diff --git a/gam.py b/gam.py index ba6084ab43..dd7e720f86 100644 --- a/gam.py +++ b/gam.py @@ -6381,7 +6381,6 @@ def doUploadAuditKey(): auditkey = sys.stdin.read() audit = getAuditObject() results = callGData(service=audit, function=u'updatePGPKey', pgpkey=auditkey) - print results def getUsersToModify(entity_type=None, entity=None, silent=False, return_uids=False, member_type=None): global domain, customerId diff --git a/gdata/Crypto/Cipher/__init__.py b/gdata/Crypto/Cipher/__init__.py deleted file mode 100644 index 3b2f855d6a..0000000000 --- a/gdata/Crypto/Cipher/__init__.py +++ /dev/null @@ -1,33 +0,0 @@ -"""Secret-key encryption algorithms. - -Secret-key encryption algorithms transform plaintext in some way that -is dependent on a key, producing ciphertext. This transformation can -easily be reversed, if (and, hopefully, only if) one knows the key. - -The encryption modules here all support the interface described in PEP -272, "API for Block Encryption Algorithms". - -If you don't know which algorithm to choose, use AES because it's -standard and has undergone a fair bit of examination. - -Crypto.Cipher.AES Advanced Encryption Standard -Crypto.Cipher.ARC2 Alleged RC2 -Crypto.Cipher.ARC4 Alleged RC4 -Crypto.Cipher.Blowfish -Crypto.Cipher.CAST -Crypto.Cipher.DES The Data Encryption Standard. Very commonly used - in the past, but today its 56-bit keys are too small. -Crypto.Cipher.DES3 Triple DES. -Crypto.Cipher.IDEA -Crypto.Cipher.RC5 -Crypto.Cipher.XOR The simple XOR cipher. -""" - -__all__ = ['AES', 'ARC2', 'ARC4', - 'Blowfish', 'CAST', 'DES', 'DES3', 'IDEA', 'RC5', - 'XOR' - ] - -__revision__ = "$Id: __init__.py,v 1.7 2003/02/28 15:28:35 akuchling Exp $" - - diff --git a/gdata/Crypto/Hash/HMAC.py b/gdata/Crypto/Hash/HMAC.py deleted file mode 100644 index eeb5782319..0000000000 --- a/gdata/Crypto/Hash/HMAC.py +++ /dev/null @@ -1,108 +0,0 @@ -"""HMAC (Keyed-Hashing for Message Authentication) Python module. - -Implements the HMAC algorithm as described by RFC 2104. - -This is just a copy of the Python 2.2 HMAC module, modified to work when -used on versions of Python before 2.2. -""" - -__revision__ = "$Id: HMAC.py,v 1.5 2002/07/25 17:19:02 z3p Exp $" - -import string - -def _strxor(s1, s2): - """Utility method. XOR the two strings s1 and s2 (must have same length). - """ - return "".join(map(lambda x, y: chr(ord(x) ^ ord(y)), s1, s2)) - -# The size of the digests returned by HMAC depends on the underlying -# hashing module used. -digest_size = None - -class HMAC: - """RFC2104 HMAC class. - - This supports the API for Cryptographic Hash Functions (PEP 247). - """ - - def __init__(self, key, msg = None, digestmod = None): - """Create a new HMAC object. - - key: key for the keyed hash object. - msg: Initial input for the hash, if provided. - digestmod: A module supporting PEP 247. Defaults to the md5 module. - """ - if digestmod == None: - import md5 - digestmod = md5 - - self.digestmod = digestmod - self.outer = digestmod.new() - self.inner = digestmod.new() - try: - self.digest_size = digestmod.digest_size - except AttributeError: - self.digest_size = len(self.outer.digest()) - - blocksize = 64 - ipad = "\x36" * blocksize - opad = "\x5C" * blocksize - - if len(key) > blocksize: - key = digestmod.new(key).digest() - - key = key + chr(0) * (blocksize - len(key)) - self.outer.update(_strxor(key, opad)) - self.inner.update(_strxor(key, ipad)) - if (msg): - self.update(msg) - -## def clear(self): -## raise NotImplementedError, "clear() method not available in HMAC." - - def update(self, msg): - """Update this hashing object with the string msg. - """ - self.inner.update(msg) - - def copy(self): - """Return a separate copy of this hashing object. - - An update to this copy won't affect the original object. - """ - other = HMAC("") - other.digestmod = self.digestmod - other.inner = self.inner.copy() - other.outer = self.outer.copy() - return other - - def digest(self): - """Return the hash value of this hashing object. - - This returns a string containing 8-bit data. The object is - not altered in any way by this function; you can continue - updating the object after calling this function. - """ - h = self.outer.copy() - h.update(self.inner.digest()) - return h.digest() - - def hexdigest(self): - """Like digest(), but returns a string of hexadecimal digits instead. - """ - return "".join([string.zfill(hex(ord(x))[2:], 2) - for x in tuple(self.digest())]) - -def new(key, msg = None, digestmod = None): - """Create a new hashing object and return it. - - key: The starting key for the hash. - msg: if available, will immediately be hashed into the object's starting - state. - - You can now feed arbitrary strings into the object using its update() - method, and can ask for the hash value at any time by calling its digest() - method. - """ - return HMAC(key, msg, digestmod) - diff --git a/gdata/Crypto/Hash/MD5.py b/gdata/Crypto/Hash/MD5.py deleted file mode 100644 index b0eba3947a..0000000000 --- a/gdata/Crypto/Hash/MD5.py +++ /dev/null @@ -1,13 +0,0 @@ - -# Just use the MD5 module from the Python standard library - -__revision__ = "$Id: MD5.py,v 1.4 2002/07/11 14:31:19 akuchling Exp $" - -from md5 import * - -import md5 -if hasattr(md5, 'digestsize'): - digest_size = digestsize - del digestsize -del md5 - diff --git a/gdata/Crypto/Hash/SHA.py b/gdata/Crypto/Hash/SHA.py deleted file mode 100644 index ea3c6a34f7..0000000000 --- a/gdata/Crypto/Hash/SHA.py +++ /dev/null @@ -1,11 +0,0 @@ - -# Just use the SHA module from the Python standard library - -__revision__ = "$Id: SHA.py,v 1.4 2002/07/11 14:31:19 akuchling Exp $" - -from sha import * -import sha -if hasattr(sha, 'digestsize'): - digest_size = digestsize - del digestsize -del sha diff --git a/gdata/Crypto/Hash/__init__.py b/gdata/Crypto/Hash/__init__.py deleted file mode 100644 index 920fe746e7..0000000000 --- a/gdata/Crypto/Hash/__init__.py +++ /dev/null @@ -1,24 +0,0 @@ -"""Hashing algorithms - -Hash functions take arbitrary strings as input, and produce an output -of fixed size that is dependent on the input; it should never be -possible to derive the input data given only the hash function's -output. Hash functions can be used simply as a checksum, or, in -association with a public-key algorithm, can be used to implement -digital signatures. - -The hashing modules here all support the interface described in PEP -247, "API for Cryptographic Hash Functions". - -Submodules: -Crypto.Hash.HMAC RFC 2104: Keyed-Hashing for Message Authentication -Crypto.Hash.MD2 -Crypto.Hash.MD4 -Crypto.Hash.MD5 -Crypto.Hash.RIPEMD -Crypto.Hash.SHA -""" - -__all__ = ['HMAC', 'MD2', 'MD4', 'MD5', 'RIPEMD', 'SHA', 'SHA256'] -__revision__ = "$Id: __init__.py,v 1.6 2003/12/19 14:24:25 akuchling Exp $" - diff --git a/gdata/Crypto/Protocol/AllOrNothing.py b/gdata/Crypto/Protocol/AllOrNothing.py deleted file mode 100644 index 6f3505d2f4..0000000000 --- a/gdata/Crypto/Protocol/AllOrNothing.py +++ /dev/null @@ -1,295 +0,0 @@ -"""This file implements all-or-nothing package transformations. - -An all-or-nothing package transformation is one in which some text is -transformed into message blocks, such that all blocks must be obtained before -the reverse transformation can be applied. Thus, if any blocks are corrupted -or lost, the original message cannot be reproduced. - -An all-or-nothing package transformation is not encryption, although a block -cipher algorithm is used. The encryption key is randomly generated and is -extractable from the message blocks. - -This class implements the All-Or-Nothing package transformation algorithm -described in: - -Ronald L. Rivest. "All-Or-Nothing Encryption and The Package Transform" -http://theory.lcs.mit.edu/~rivest/fusion.pdf - -""" - -__revision__ = "$Id: AllOrNothing.py,v 1.8 2003/02/28 15:23:20 akuchling Exp $" - -import operator -import string -from Crypto.Util.number import bytes_to_long, long_to_bytes - - - -class AllOrNothing: - """Class implementing the All-or-Nothing package transform. - - Methods for subclassing: - - _inventkey(key_size): - Returns a randomly generated key. Subclasses can use this to - implement better random key generating algorithms. The default - algorithm is probably not very cryptographically secure. - - """ - - def __init__(self, ciphermodule, mode=None, IV=None): - """AllOrNothing(ciphermodule, mode=None, IV=None) - - ciphermodule is a module implementing the cipher algorithm to - use. It must provide the PEP272 interface. - - Note that the encryption key is randomly generated - automatically when needed. Optional arguments mode and IV are - passed directly through to the ciphermodule.new() method; they - are the feedback mode and initialization vector to use. All - three arguments must be the same for the object used to create - the digest, and to undigest'ify the message blocks. - """ - - self.__ciphermodule = ciphermodule - self.__mode = mode - self.__IV = IV - self.__key_size = ciphermodule.key_size - if self.__key_size == 0: - self.__key_size = 16 - - __K0digit = chr(0x69) - - def digest(self, text): - """digest(text:string) : [string] - - Perform the All-or-Nothing package transform on the given - string. Output is a list of message blocks describing the - transformed text, where each block is a string of bit length equal - to the ciphermodule's block_size. - """ - - # generate a random session key and K0, the key used to encrypt the - # hash blocks. Rivest calls this a fixed, publically-known encryption - # key, but says nothing about the security implications of this key or - # how to choose it. - key = self._inventkey(self.__key_size) - K0 = self.__K0digit * self.__key_size - - # we need two cipher objects here, one that is used to encrypt the - # message blocks and one that is used to encrypt the hashes. The - # former uses the randomly generated key, while the latter uses the - # well-known key. - mcipher = self.__newcipher(key) - hcipher = self.__newcipher(K0) - - # Pad the text so that its length is a multiple of the cipher's - # block_size. Pad with trailing spaces, which will be eliminated in - # the undigest() step. - block_size = self.__ciphermodule.block_size - padbytes = block_size - (len(text) % block_size) - text = text + ' ' * padbytes - - # Run through the algorithm: - # s: number of message blocks (size of text / block_size) - # input sequence: m1, m2, ... ms - # random key K' (`key' in the code) - # Compute output sequence: m'1, m'2, ... m's' for s' = s + 1 - # Let m'i = mi ^ E(K', i) for i = 1, 2, 3, ..., s - # Let m's' = K' ^ h1 ^ h2 ^ ... hs - # where hi = E(K0, m'i ^ i) for i = 1, 2, ... s - # - # The one complication I add is that the last message block is hard - # coded to the number of padbytes added, so that these can be stripped - # during the undigest() step - s = len(text) / block_size - blocks = [] - hashes = [] - for i in range(1, s+1): - start = (i-1) * block_size - end = start + block_size - mi = text[start:end] - assert len(mi) == block_size - cipherblock = mcipher.encrypt(long_to_bytes(i, block_size)) - mticki = bytes_to_long(mi) ^ bytes_to_long(cipherblock) - blocks.append(mticki) - # calculate the hash block for this block - hi = hcipher.encrypt(long_to_bytes(mticki ^ i, block_size)) - hashes.append(bytes_to_long(hi)) - - # Add the padbytes length as a message block - i = i + 1 - cipherblock = mcipher.encrypt(long_to_bytes(i, block_size)) - mticki = padbytes ^ bytes_to_long(cipherblock) - blocks.append(mticki) - - # calculate this block's hash - hi = hcipher.encrypt(long_to_bytes(mticki ^ i, block_size)) - hashes.append(bytes_to_long(hi)) - - # Now calculate the last message block of the sequence 1..s'. This - # will contain the random session key XOR'd with all the hash blocks, - # so that for undigest(), once all the hash blocks are calculated, the - # session key can be trivially extracted. Calculating all the hash - # blocks requires that all the message blocks be received, thus the - # All-or-Nothing algorithm succeeds. - mtick_stick = bytes_to_long(key) ^ reduce(operator.xor, hashes) - blocks.append(mtick_stick) - - # we convert the blocks to strings since in Python, byte sequences are - # always represented as strings. This is more consistent with the - # model that encryption and hash algorithms always operate on strings. - return map(long_to_bytes, blocks) - - - def undigest(self, blocks): - """undigest(blocks : [string]) : string - - Perform the reverse package transformation on a list of message - blocks. Note that the ciphermodule used for both transformations - must be the same. blocks is a list of strings of bit length - equal to the ciphermodule's block_size. - """ - - # better have at least 2 blocks, for the padbytes package and the hash - # block accumulator - if len(blocks) < 2: - raise ValueError, "List must be at least length 2." - - # blocks is a list of strings. We need to deal with them as long - # integers - blocks = map(bytes_to_long, blocks) - - # Calculate the well-known key, to which the hash blocks are - # encrypted, and create the hash cipher. - K0 = self.__K0digit * self.__key_size - hcipher = self.__newcipher(K0) - - # Since we have all the blocks (or this method would have been called - # prematurely), we can calcualte all the hash blocks. - hashes = [] - for i in range(1, len(blocks)): - mticki = blocks[i-1] ^ i - hi = hcipher.encrypt(long_to_bytes(mticki)) - hashes.append(bytes_to_long(hi)) - - # now we can calculate K' (key). remember the last block contains - # m's' which we don't include here - key = blocks[-1] ^ reduce(operator.xor, hashes) - - # and now we can create the cipher object - mcipher = self.__newcipher(long_to_bytes(key)) - block_size = self.__ciphermodule.block_size - - # And we can now decode the original message blocks - parts = [] - for i in range(1, len(blocks)): - cipherblock = mcipher.encrypt(long_to_bytes(i, block_size)) - mi = blocks[i-1] ^ bytes_to_long(cipherblock) - parts.append(mi) - - # The last message block contains the number of pad bytes appended to - # the original text string, such that its length was an even multiple - # of the cipher's block_size. This number should be small enough that - # the conversion from long integer to integer should never overflow - padbytes = int(parts[-1]) - text = string.join(map(long_to_bytes, parts[:-1]), '') - return text[:-padbytes] - - def _inventkey(self, key_size): - # TBD: Not a very secure algorithm. Eventually, I'd like to use JHy's - # kernelrand module - import time - from Crypto.Util import randpool - # TBD: key_size * 2 to work around possible bug in RandomPool? - pool = randpool.RandomPool(key_size * 2) - while key_size > pool.entropy: - pool.add_event() - - # we now have enough entropy in the pool to get a key_size'd key - return pool.get_bytes(key_size) - - def __newcipher(self, key): - if self.__mode is None and self.__IV is None: - return self.__ciphermodule.new(key) - elif self.__IV is None: - return self.__ciphermodule.new(key, self.__mode) - else: - return self.__ciphermodule.new(key, self.__mode, self.__IV) - - - -if __name__ == '__main__': - import sys - import getopt - import base64 - - usagemsg = '''\ -Test module usage: %(program)s [-c cipher] [-l] [-h] - -Where: - --cipher module - -c module - Cipher module to use. Default: %(ciphermodule)s - - --aslong - -l - Print the encoded message blocks as long integers instead of base64 - encoded strings - - --help - -h - Print this help message -''' - - ciphermodule = 'AES' - aslong = 0 - - def usage(code, msg=None): - if msg: - print msg - print usagemsg % {'program': sys.argv[0], - 'ciphermodule': ciphermodule} - sys.exit(code) - - try: - opts, args = getopt.getopt(sys.argv[1:], - 'c:l', ['cipher=', 'aslong']) - except getopt.error, msg: - usage(1, msg) - - if args: - usage(1, 'Too many arguments') - - for opt, arg in opts: - if opt in ('-h', '--help'): - usage(0) - elif opt in ('-c', '--cipher'): - ciphermodule = arg - elif opt in ('-l', '--aslong'): - aslong = 1 - - # ugly hack to force __import__ to give us the end-path module - module = __import__('Crypto.Cipher.'+ciphermodule, None, None, ['new']) - - a = AllOrNothing(module) - print 'Original text:\n==========' - print __doc__ - print '==========' - msgblocks = a.digest(__doc__) - print 'message blocks:' - for i, blk in map(None, range(len(msgblocks)), msgblocks): - # base64 adds a trailing newline - print ' %3d' % i, - if aslong: - print bytes_to_long(blk) - else: - print base64.encodestring(blk)[:-1] - # - # get a new undigest-only object so there's no leakage - b = AllOrNothing(module) - text = b.undigest(msgblocks) - if text == __doc__: - print 'They match!' - else: - print 'They differ!' diff --git a/gdata/Crypto/Protocol/Chaffing.py b/gdata/Crypto/Protocol/Chaffing.py deleted file mode 100644 index fdfb82d0c3..0000000000 --- a/gdata/Crypto/Protocol/Chaffing.py +++ /dev/null @@ -1,229 +0,0 @@ -"""This file implements the chaffing algorithm. - -Winnowing and chaffing is a technique for enhancing privacy without requiring -strong encryption. In short, the technique takes a set of authenticated -message blocks (the wheat) and adds a number of chaff blocks which have -randomly chosen data and MAC fields. This means that to an adversary, the -chaff blocks look as valid as the wheat blocks, and so the authentication -would have to be performed on every block. By tailoring the number of chaff -blocks added to the message, the sender can make breaking the message -computationally infeasible. There are many other interesting properties of -the winnow/chaff technique. - -For example, say Alice is sending a message to Bob. She packetizes the -message and performs an all-or-nothing transformation on the packets. Then -she authenticates each packet with a message authentication code (MAC). The -MAC is a hash of the data packet, and there is a secret key which she must -share with Bob (key distribution is an exercise left to the reader). She then -adds a serial number to each packet, and sends the packets to Bob. - -Bob receives the packets, and using the shared secret authentication key, -authenticates the MACs for each packet. Those packets that have bad MACs are -simply discarded. The remainder are sorted by serial number, and passed -through the reverse all-or-nothing transform. The transform means that an -eavesdropper (say Eve) must acquire all the packets before any of the data can -be read. If even one packet is missing, the data is useless. - -There's one twist: by adding chaff packets, Alice and Bob can make Eve's job -much harder, since Eve now has to break the shared secret key, or try every -combination of wheat and chaff packet to read any of the message. The cool -thing is that Bob doesn't need to add any additional code; the chaff packets -are already filtered out because their MACs don't match (in all likelihood -- -since the data and MACs for the chaff packets are randomly chosen it is -possible, but very unlikely that a chaff MAC will match the chaff data). And -Alice need not even be the party adding the chaff! She could be completely -unaware that a third party, say Charles, is adding chaff packets to her -messages as they are transmitted. - -For more information on winnowing and chaffing see this paper: - -Ronald L. Rivest, "Chaffing and Winnowing: Confidentiality without Encryption" -http://theory.lcs.mit.edu/~rivest/chaffing.txt - -""" - -__revision__ = "$Id: Chaffing.py,v 1.7 2003/02/28 15:23:21 akuchling Exp $" - -from Crypto.Util.number import bytes_to_long - -class Chaff: - """Class implementing the chaff adding algorithm. - - Methods for subclasses: - - _randnum(size): - Returns a randomly generated number with a byte-length equal - to size. Subclasses can use this to implement better random - data and MAC generating algorithms. The default algorithm is - probably not very cryptographically secure. It is most - important that the chaff data does not contain any patterns - that can be used to discern it from wheat data without running - the MAC. - - """ - - def __init__(self, factor=1.0, blocksper=1): - """Chaff(factor:float, blocksper:int) - - factor is the number of message blocks to add chaff to, - expressed as a percentage between 0.0 and 1.0. blocksper is - the number of chaff blocks to include for each block being - chaffed. Thus the defaults add one chaff block to every - message block. By changing the defaults, you can adjust how - computationally difficult it could be for an adversary to - brute-force crack the message. The difficulty is expressed - as: - - pow(blocksper, int(factor * number-of-blocks)) - - For ease of implementation, when factor < 1.0, only the first - int(factor*number-of-blocks) message blocks are chaffed. - """ - - if not (0.0<=factor<=1.0): - raise ValueError, "'factor' must be between 0.0 and 1.0" - if blocksper < 0: - raise ValueError, "'blocksper' must be zero or more" - - self.__factor = factor - self.__blocksper = blocksper - - - def chaff(self, blocks): - """chaff( [(serial-number:int, data:string, MAC:string)] ) - : [(int, string, string)] - - Add chaff to message blocks. blocks is a list of 3-tuples of the - form (serial-number, data, MAC). - - Chaff is created by choosing a random number of the same - byte-length as data, and another random number of the same - byte-length as MAC. The message block's serial number is - placed on the chaff block and all the packet's chaff blocks - are randomly interspersed with the single wheat block. This - method then returns a list of 3-tuples of the same form. - Chaffed blocks will contain multiple instances of 3-tuples - with the same serial number, but the only way to figure out - which blocks are wheat and which are chaff is to perform the - MAC hash and compare values. - """ - - chaffedblocks = [] - - # count is the number of blocks to add chaff to. blocksper is the - # number of chaff blocks to add per message block that is being - # chaffed. - count = len(blocks) * self.__factor - blocksper = range(self.__blocksper) - for i, wheat in map(None, range(len(blocks)), blocks): - # it shouldn't matter which of the n blocks we add chaff to, so for - # ease of implementation, we'll just add them to the first count - # blocks - if i < count: - serial, data, mac = wheat - datasize = len(data) - macsize = len(mac) - addwheat = 1 - # add chaff to this block - for j in blocksper: - import sys - chaffdata = self._randnum(datasize) - chaffmac = self._randnum(macsize) - chaff = (serial, chaffdata, chaffmac) - # mix up the order, if the 5th bit is on then put the - # wheat on the list - if addwheat and bytes_to_long(self._randnum(16)) & 0x40: - chaffedblocks.append(wheat) - addwheat = 0 - chaffedblocks.append(chaff) - if addwheat: - chaffedblocks.append(wheat) - else: - # just add the wheat - chaffedblocks.append(wheat) - return chaffedblocks - - def _randnum(self, size): - # TBD: Not a very secure algorithm. - # TBD: size * 2 to work around possible bug in RandomPool - from Crypto.Util import randpool - import time - pool = randpool.RandomPool(size * 2) - while size > pool.entropy: - pass - - # we now have enough entropy in the pool to get size bytes of random - # data... well, probably - return pool.get_bytes(size) - - - -if __name__ == '__main__': - text = """\ -We hold these truths to be self-evident, that all men are created equal, that -they are endowed by their Creator with certain unalienable Rights, that among -these are Life, Liberty, and the pursuit of Happiness. That to secure these -rights, Governments are instituted among Men, deriving their just powers from -the consent of the governed. That whenever any Form of Government becomes -destructive of these ends, it is the Right of the People to alter or to -abolish it, and to institute new Government, laying its foundation on such -principles and organizing its powers in such form, as to them shall seem most -likely to effect their Safety and Happiness. -""" - print 'Original text:\n==========' - print text - print '==========' - - # first transform the text into packets - blocks = [] ; size = 40 - for i in range(0, len(text), size): - blocks.append( text[i:i+size] ) - - # now get MACs for all the text blocks. The key is obvious... - print 'Calculating MACs...' - from Crypto.Hash import HMAC, SHA - key = 'Jefferson' - macs = [HMAC.new(key, block, digestmod=SHA).digest() - for block in blocks] - - assert len(blocks) == len(macs) - - # put these into a form acceptable as input to the chaffing procedure - source = [] - m = map(None, range(len(blocks)), blocks, macs) - print m - for i, data, mac in m: - source.append((i, data, mac)) - - # now chaff these - print 'Adding chaff...' - c = Chaff(factor=0.5, blocksper=2) - chaffed = c.chaff(source) - - from base64 import encodestring - - # print the chaffed message blocks. meanwhile, separate the wheat from - # the chaff - - wheat = [] - print 'chaffed message blocks:' - for i, data, mac in chaffed: - # do the authentication - h = HMAC.new(key, data, digestmod=SHA) - pmac = h.digest() - if pmac == mac: - tag = '-->' - wheat.append(data) - else: - tag = ' ' - # base64 adds a trailing newline - print tag, '%3d' % i, \ - repr(data), encodestring(mac)[:-1] - - # now decode the message packets and check it against the original text - print 'Undigesting wheat...' - newtext = "".join(wheat) - if newtext == text: - print 'They match!' - else: - print 'They differ!' diff --git a/gdata/Crypto/Protocol/__init__.py b/gdata/Crypto/Protocol/__init__.py deleted file mode 100644 index a6d68bcf8d..0000000000 --- a/gdata/Crypto/Protocol/__init__.py +++ /dev/null @@ -1,17 +0,0 @@ - -"""Cryptographic protocols - -Implements various cryptographic protocols. (Don't expect to find -network protocols here.) - -Crypto.Protocol.AllOrNothing Transforms a message into a set of message - blocks, such that the blocks can be - recombined to get the message back. - -Crypto.Protocol.Chaffing Takes a set of authenticated message blocks - (the wheat) and adds a number of - randomly generated blocks (the chaff). -""" - -__all__ = ['AllOrNothing', 'Chaffing'] -__revision__ = "$Id: __init__.py,v 1.4 2003/02/28 15:23:21 akuchling Exp $" diff --git a/gdata/Crypto/PublicKey/DSA.py b/gdata/Crypto/PublicKey/DSA.py deleted file mode 100644 index 7947b6f5fb..0000000000 --- a/gdata/Crypto/PublicKey/DSA.py +++ /dev/null @@ -1,238 +0,0 @@ - -# -# DSA.py : Digital Signature Algorithm -# -# Part of the Python Cryptography Toolkit -# -# Distribute and use freely; there are no restrictions on further -# dissemination and usage except those imposed by the laws of your -# country of residence. This software is provided "as is" without -# warranty of fitness for use or suitability for any purpose, express -# or implied. Use at your own risk or not at all. -# - -__revision__ = "$Id: DSA.py,v 1.16 2004/05/06 12:52:54 akuchling Exp $" - -from Crypto.PublicKey.pubkey import * -from Crypto.Util import number -from Crypto.Util.number import bytes_to_long, long_to_bytes -from Crypto.Hash import SHA - -try: - from Crypto.PublicKey import _fastmath -except ImportError: - _fastmath = None - -class error (Exception): - pass - -def generateQ(randfunc): - S=randfunc(20) - hash1=SHA.new(S).digest() - hash2=SHA.new(long_to_bytes(bytes_to_long(S)+1)).digest() - q = bignum(0) - for i in range(0,20): - c=ord(hash1[i])^ord(hash2[i]) - if i==0: - c=c | 128 - if i==19: - c= c | 1 - q=q*256+c - while (not isPrime(q)): - q=q+2 - if pow(2,159L) < q < pow(2,160L): - return S, q - raise error, 'Bad q value generated' - -def generate(bits, randfunc, progress_func=None): - """generate(bits:int, randfunc:callable, progress_func:callable) - - Generate a DSA key of length 'bits', using 'randfunc' to get - random data and 'progress_func', if present, to display - the progress of the key generation. - """ - - if bits<160: - raise error, 'Key length <160 bits' - obj=DSAobj() - # Generate string S and prime q - if progress_func: - progress_func('p,q\n') - while (1): - S, obj.q = generateQ(randfunc) - n=(bits-1)/160 - C, N, V = 0, 2, {} - b=(obj.q >> 5) & 15 - powb=pow(bignum(2), b) - powL1=pow(bignum(2), bits-1) - while C<4096: - for k in range(0, n+1): - V[k]=bytes_to_long(SHA.new(S+str(N)+str(k)).digest()) - W=V[n] % powb - for k in range(n-1, -1, -1): - W=(W<<160L)+V[k] - X=W+powL1 - p=X-(X%(2*obj.q)-1) - if powL1<=p and isPrime(p): - break - C, N = C+1, N+n+1 - if C<4096: - break - if progress_func: - progress_func('4096 multiples failed\n') - - obj.p = p - power=(p-1)/obj.q - if progress_func: - progress_func('h,g\n') - while (1): - h=bytes_to_long(randfunc(bits)) % (p-1) - g=pow(h, power, p) - if 11: - break - obj.g=g - if progress_func: - progress_func('x,y\n') - while (1): - x=bytes_to_long(randfunc(20)) - if 0 < x < obj.q: - break - obj.x, obj.y = x, pow(g, x, p) - return obj - -def construct(tuple): - """construct(tuple:(long,long,long,long)|(long,long,long,long,long)):DSAobj - Construct a DSA object from a 4- or 5-tuple of numbers. - """ - obj=DSAobj() - if len(tuple) not in [4,5]: - raise error, 'argument for construct() wrong length' - for i in range(len(tuple)): - field = obj.keydata[i] - setattr(obj, field, tuple[i]) - return obj - -class DSAobj(pubkey): - keydata=['y', 'g', 'p', 'q', 'x'] - - def _encrypt(self, s, Kstr): - raise error, 'DSA algorithm cannot encrypt data' - - def _decrypt(self, s): - raise error, 'DSA algorithm cannot decrypt data' - - def _sign(self, M, K): - if (K<2 or self.q<=K): - raise error, 'K is not between 2 and q' - r=pow(self.g, K, self.p) % self.q - s=(inverse(K, self.q)*(M+self.x*r)) % self.q - return (r,s) - - def _verify(self, M, sig): - r, s = sig - if r<=0 or r>=self.q or s<=0 or s>=self.q: - return 0 - w=inverse(s, self.q) - u1, u2 = (M*w) % self.q, (r*w) % self.q - v1 = pow(self.g, u1, self.p) - v2 = pow(self.y, u2, self.p) - v = ((v1*v2) % self.p) - v = v % self.q - if v==r: - return 1 - return 0 - - def size(self): - "Return the maximum number of bits that can be handled by this key." - return number.size(self.p) - 1 - - def has_private(self): - """Return a Boolean denoting whether the object contains - private components.""" - if hasattr(self, 'x'): - return 1 - else: - return 0 - - def can_sign(self): - """Return a Boolean value recording whether this algorithm can generate signatures.""" - return 1 - - def can_encrypt(self): - """Return a Boolean value recording whether this algorithm can encrypt data.""" - return 0 - - def publickey(self): - """Return a new key object containing only the public information.""" - return construct((self.y, self.g, self.p, self.q)) - -object=DSAobj - -generate_py = generate -construct_py = construct - -class DSAobj_c(pubkey): - keydata = ['y', 'g', 'p', 'q', 'x'] - - def __init__(self, key): - self.key = key - - def __getattr__(self, attr): - if attr in self.keydata: - return getattr(self.key, attr) - else: - if self.__dict__.has_key(attr): - self.__dict__[attr] - else: - raise AttributeError, '%s instance has no attribute %s' % (self.__class__, attr) - - def __getstate__(self): - d = {} - for k in self.keydata: - if hasattr(self.key, k): - d[k]=getattr(self.key, k) - return d - - def __setstate__(self, state): - y,g,p,q = state['y'], state['g'], state['p'], state['q'] - if not state.has_key('x'): - self.key = _fastmath.dsa_construct(y,g,p,q) - else: - x = state['x'] - self.key = _fastmath.dsa_construct(y,g,p,q,x) - - def _sign(self, M, K): - return self.key._sign(M, K) - - def _verify(self, M, (r, s)): - return self.key._verify(M, r, s) - - def size(self): - return self.key.size() - - def has_private(self): - return self.key.has_private() - - def publickey(self): - return construct_c((self.key.y, self.key.g, self.key.p, self.key.q)) - - def can_sign(self): - return 1 - - def can_encrypt(self): - return 0 - -def generate_c(bits, randfunc, progress_func=None): - obj = generate_py(bits, randfunc, progress_func) - y,g,p,q,x = obj.y, obj.g, obj.p, obj.q, obj.x - return construct_c((y,g,p,q,x)) - -def construct_c(tuple): - key = apply(_fastmath.dsa_construct, tuple) - return DSAobj_c(key) - -if _fastmath: - #print "using C version of DSA" - generate = generate_c - construct = construct_c - error = _fastmath.error diff --git a/gdata/Crypto/PublicKey/ElGamal.py b/gdata/Crypto/PublicKey/ElGamal.py deleted file mode 100644 index 026881c91a..0000000000 --- a/gdata/Crypto/PublicKey/ElGamal.py +++ /dev/null @@ -1,132 +0,0 @@ -# -# ElGamal.py : ElGamal encryption/decryption and signatures -# -# Part of the Python Cryptography Toolkit -# -# Distribute and use freely; there are no restrictions on further -# dissemination and usage except those imposed by the laws of your -# country of residence. This software is provided "as is" without -# warranty of fitness for use or suitability for any purpose, express -# or implied. Use at your own risk or not at all. -# - -__revision__ = "$Id: ElGamal.py,v 1.9 2003/04/04 19:44:26 akuchling Exp $" - -from Crypto.PublicKey.pubkey import * -from Crypto.Util import number - -class error (Exception): - pass - -# Generate an ElGamal key with N bits -def generate(bits, randfunc, progress_func=None): - """generate(bits:int, randfunc:callable, progress_func:callable) - - Generate an ElGamal key of length 'bits', using 'randfunc' to get - random data and 'progress_func', if present, to display - the progress of the key generation. - """ - obj=ElGamalobj() - # Generate prime p - if progress_func: - progress_func('p\n') - obj.p=bignum(getPrime(bits, randfunc)) - # Generate random number g - if progress_func: - progress_func('g\n') - size=bits-1-(ord(randfunc(1)) & 63) # g will be from 1--64 bits smaller than p - if size<1: - size=bits-1 - while (1): - obj.g=bignum(getPrime(size, randfunc)) - if obj.g < obj.p: - break - size=(size+1) % bits - if size==0: - size=4 - # Generate random number x - if progress_func: - progress_func('x\n') - while (1): - size=bits-1-ord(randfunc(1)) # x will be from 1 to 256 bits smaller than p - if size>2: - break - while (1): - obj.x=bignum(getPrime(size, randfunc)) - if obj.x < obj.p: - break - size = (size+1) % bits - if size==0: - size=4 - if progress_func: - progress_func('y\n') - obj.y = pow(obj.g, obj.x, obj.p) - return obj - -def construct(tuple): - """construct(tuple:(long,long,long,long)|(long,long,long,long,long))) - : ElGamalobj - Construct an ElGamal key from a 3- or 4-tuple of numbers. - """ - - obj=ElGamalobj() - if len(tuple) not in [3,4]: - raise error, 'argument for construct() wrong length' - for i in range(len(tuple)): - field = obj.keydata[i] - setattr(obj, field, tuple[i]) - return obj - -class ElGamalobj(pubkey): - keydata=['p', 'g', 'y', 'x'] - - def _encrypt(self, M, K): - a=pow(self.g, K, self.p) - b=( M*pow(self.y, K, self.p) ) % self.p - return ( a,b ) - - def _decrypt(self, M): - if (not hasattr(self, 'x')): - raise error, 'Private key not available in this object' - ax=pow(M[0], self.x, self.p) - plaintext=(M[1] * inverse(ax, self.p ) ) % self.p - return plaintext - - def _sign(self, M, K): - if (not hasattr(self, 'x')): - raise error, 'Private key not available in this object' - p1=self.p-1 - if (GCD(K, p1)!=1): - raise error, 'Bad K value: GCD(K,p-1)!=1' - a=pow(self.g, K, self.p) - t=(M-self.x*a) % p1 - while t<0: t=t+p1 - b=(t*inverse(K, p1)) % p1 - return (a, b) - - def _verify(self, M, sig): - v1=pow(self.y, sig[0], self.p) - v1=(v1*pow(sig[0], sig[1], self.p)) % self.p - v2=pow(self.g, M, self.p) - if v1==v2: - return 1 - return 0 - - def size(self): - "Return the maximum number of bits that can be handled by this key." - return number.size(self.p) - 1 - - def has_private(self): - """Return a Boolean denoting whether the object contains - private components.""" - if hasattr(self, 'x'): - return 1 - else: - return 0 - - def publickey(self): - """Return a new key object containing only the public information.""" - return construct((self.p, self.g, self.y)) - - -object=ElGamalobj diff --git a/gdata/Crypto/PublicKey/RSA.py b/gdata/Crypto/PublicKey/RSA.py deleted file mode 100644 index e0e877ec16..0000000000 --- a/gdata/Crypto/PublicKey/RSA.py +++ /dev/null @@ -1,256 +0,0 @@ -# -# RSA.py : RSA encryption/decryption -# -# Part of the Python Cryptography Toolkit -# -# Distribute and use freely; there are no restrictions on further -# dissemination and usage except those imposed by the laws of your -# country of residence. This software is provided "as is" without -# warranty of fitness for use or suitability for any purpose, express -# or implied. Use at your own risk or not at all. -# - -__revision__ = "$Id: RSA.py,v 1.20 2004/05/06 12:52:54 akuchling Exp $" - -from Crypto.PublicKey import pubkey -from Crypto.Util import number - -try: - from Crypto.PublicKey import _fastmath -except ImportError: - _fastmath = None - -class error (Exception): - pass - -def generate(bits, randfunc, progress_func=None): - """generate(bits:int, randfunc:callable, progress_func:callable) - - Generate an RSA key of length 'bits', using 'randfunc' to get - random data and 'progress_func', if present, to display - the progress of the key generation. - """ - obj=RSAobj() - - # Generate the prime factors of n - if progress_func: - progress_func('p,q\n') - p = q = 1L - while number.size(p*q) < bits: - p = pubkey.getPrime(bits/2, randfunc) - q = pubkey.getPrime(bits/2, randfunc) - - # p shall be smaller than q (for calc of u) - if p > q: - (p, q)=(q, p) - obj.p = p - obj.q = q - - if progress_func: - progress_func('u\n') - obj.u = pubkey.inverse(obj.p, obj.q) - obj.n = obj.p*obj.q - - obj.e = 65537L - if progress_func: - progress_func('d\n') - obj.d=pubkey.inverse(obj.e, (obj.p-1)*(obj.q-1)) - - assert bits <= 1+obj.size(), "Generated key is too small" - - return obj - -def construct(tuple): - """construct(tuple:(long,) : RSAobj - Construct an RSA object from a 2-, 3-, 5-, or 6-tuple of numbers. - """ - - obj=RSAobj() - if len(tuple) not in [2,3,5,6]: - raise error, 'argument for construct() wrong length' - for i in range(len(tuple)): - field = obj.keydata[i] - setattr(obj, field, tuple[i]) - if len(tuple) >= 5: - # Ensure p is smaller than q - if obj.p>obj.q: - (obj.p, obj.q)=(obj.q, obj.p) - - if len(tuple) == 5: - # u not supplied, so we're going to have to compute it. - obj.u=pubkey.inverse(obj.p, obj.q) - - return obj - -class RSAobj(pubkey.pubkey): - keydata = ['n', 'e', 'd', 'p', 'q', 'u'] - def _encrypt(self, plaintext, K=''): - if self.n<=plaintext: - raise error, 'Plaintext too large' - return (pow(plaintext, self.e, self.n),) - - def _decrypt(self, ciphertext): - if (not hasattr(self, 'd')): - raise error, 'Private key not available in this object' - if self.n<=ciphertext[0]: - raise error, 'Ciphertext too large' - return pow(ciphertext[0], self.d, self.n) - - def _sign(self, M, K=''): - return (self._decrypt((M,)),) - - def _verify(self, M, sig): - m2=self._encrypt(sig[0]) - if m2[0]==M: - return 1 - else: return 0 - - def _blind(self, M, B): - tmp = pow(B, self.e, self.n) - return (M * tmp) % self.n - - def _unblind(self, M, B): - tmp = pubkey.inverse(B, self.n) - return (M * tmp) % self.n - - def can_blind (self): - """can_blind() : bool - Return a Boolean value recording whether this algorithm can - blind data. (This does not imply that this - particular key object has the private information required to - to blind a message.) - """ - return 1 - - def size(self): - """size() : int - Return the maximum number of bits that can be handled by this key. - """ - return number.size(self.n) - 1 - - def has_private(self): - """has_private() : bool - Return a Boolean denoting whether the object contains - private components. - """ - if hasattr(self, 'd'): - return 1 - else: return 0 - - def publickey(self): - """publickey(): RSAobj - Return a new key object containing only the public key information. - """ - return construct((self.n, self.e)) - -class RSAobj_c(pubkey.pubkey): - keydata = ['n', 'e', 'd', 'p', 'q', 'u'] - - def __init__(self, key): - self.key = key - - def __getattr__(self, attr): - if attr in self.keydata: - return getattr(self.key, attr) - else: - if self.__dict__.has_key(attr): - self.__dict__[attr] - else: - raise AttributeError, '%s instance has no attribute %s' % (self.__class__, attr) - - def __getstate__(self): - d = {} - for k in self.keydata: - if hasattr(self.key, k): - d[k]=getattr(self.key, k) - return d - - def __setstate__(self, state): - n,e = state['n'], state['e'] - if not state.has_key('d'): - self.key = _fastmath.rsa_construct(n,e) - else: - d = state['d'] - if not state.has_key('q'): - self.key = _fastmath.rsa_construct(n,e,d) - else: - p, q, u = state['p'], state['q'], state['u'] - self.key = _fastmath.rsa_construct(n,e,d,p,q,u) - - def _encrypt(self, plain, K): - return (self.key._encrypt(plain),) - - def _decrypt(self, cipher): - return self.key._decrypt(cipher[0]) - - def _sign(self, M, K): - return (self.key._sign(M),) - - def _verify(self, M, sig): - return self.key._verify(M, sig[0]) - - def _blind(self, M, B): - return self.key._blind(M, B) - - def _unblind(self, M, B): - return self.key._unblind(M, B) - - def can_blind (self): - return 1 - - def size(self): - return self.key.size() - - def has_private(self): - return self.key.has_private() - - def publickey(self): - return construct_c((self.key.n, self.key.e)) - -def generate_c(bits, randfunc, progress_func = None): - # Generate the prime factors of n - if progress_func: - progress_func('p,q\n') - - p = q = 1L - while number.size(p*q) < bits: - p = pubkey.getPrime(bits/2, randfunc) - q = pubkey.getPrime(bits/2, randfunc) - - # p shall be smaller than q (for calc of u) - if p > q: - (p, q)=(q, p) - if progress_func: - progress_func('u\n') - u=pubkey.inverse(p, q) - n=p*q - - e = 65537L - if progress_func: - progress_func('d\n') - d=pubkey.inverse(e, (p-1)*(q-1)) - key = _fastmath.rsa_construct(n,e,d,p,q,u) - obj = RSAobj_c(key) - -## print p -## print q -## print number.size(p), number.size(q), number.size(q*p), -## print obj.size(), bits - assert bits <= 1+obj.size(), "Generated key is too small" - return obj - - -def construct_c(tuple): - key = apply(_fastmath.rsa_construct, tuple) - return RSAobj_c(key) - -object = RSAobj - -generate_py = generate -construct_py = construct - -if _fastmath: - #print "using C version of RSA" - generate = generate_c - construct = construct_c - error = _fastmath.error diff --git a/gdata/Crypto/PublicKey/__init__.py b/gdata/Crypto/PublicKey/__init__.py deleted file mode 100644 index ad1c80ca14..0000000000 --- a/gdata/Crypto/PublicKey/__init__.py +++ /dev/null @@ -1,17 +0,0 @@ -"""Public-key encryption and signature algorithms. - -Public-key encryption uses two different keys, one for encryption and -one for decryption. The encryption key can be made public, and the -decryption key is kept private. Many public-key algorithms can also -be used to sign messages, and some can *only* be used for signatures. - -Crypto.PublicKey.DSA Digital Signature Algorithm. (Signature only) -Crypto.PublicKey.ElGamal (Signing and encryption) -Crypto.PublicKey.RSA (Signing, encryption, and blinding) -Crypto.PublicKey.qNEW (Signature only) - -""" - -__all__ = ['RSA', 'DSA', 'ElGamal', 'qNEW'] -__revision__ = "$Id: __init__.py,v 1.4 2003/04/03 20:27:13 akuchling Exp $" - diff --git a/gdata/Crypto/PublicKey/pubkey.py b/gdata/Crypto/PublicKey/pubkey.py deleted file mode 100644 index 5c75c3e3ad..0000000000 --- a/gdata/Crypto/PublicKey/pubkey.py +++ /dev/null @@ -1,172 +0,0 @@ -# -# pubkey.py : Internal functions for public key operations -# -# Part of the Python Cryptography Toolkit -# -# Distribute and use freely; there are no restrictions on further -# dissemination and usage except those imposed by the laws of your -# country of residence. This software is provided "as is" without -# warranty of fitness for use or suitability for any purpose, express -# or implied. Use at your own risk or not at all. -# - -__revision__ = "$Id: pubkey.py,v 1.11 2003/04/03 20:36:14 akuchling Exp $" - -import types, warnings -from Crypto.Util.number import * - -# Basic public key class -class pubkey: - def __init__(self): - pass - - def __getstate__(self): - """To keep key objects platform-independent, the key data is - converted to standard Python long integers before being - written out. It will then be reconverted as necessary on - restoration.""" - d=self.__dict__ - for key in self.keydata: - if d.has_key(key): d[key]=long(d[key]) - return d - - def __setstate__(self, d): - """On unpickling a key object, the key data is converted to the big -number representation being used, whether that is Python long -integers, MPZ objects, or whatever.""" - for key in self.keydata: - if d.has_key(key): self.__dict__[key]=bignum(d[key]) - - def encrypt(self, plaintext, K): - """encrypt(plaintext:string|long, K:string|long) : tuple - Encrypt the string or integer plaintext. K is a random - parameter required by some algorithms. - """ - wasString=0 - if isinstance(plaintext, types.StringType): - plaintext=bytes_to_long(plaintext) ; wasString=1 - if isinstance(K, types.StringType): - K=bytes_to_long(K) - ciphertext=self._encrypt(plaintext, K) - if wasString: return tuple(map(long_to_bytes, ciphertext)) - else: return ciphertext - - def decrypt(self, ciphertext): - """decrypt(ciphertext:tuple|string|long): string - Decrypt 'ciphertext' using this key. - """ - wasString=0 - if not isinstance(ciphertext, types.TupleType): - ciphertext=(ciphertext,) - if isinstance(ciphertext[0], types.StringType): - ciphertext=tuple(map(bytes_to_long, ciphertext)) ; wasString=1 - plaintext=self._decrypt(ciphertext) - if wasString: return long_to_bytes(plaintext) - else: return plaintext - - def sign(self, M, K): - """sign(M : string|long, K:string|long) : tuple - Return a tuple containing the signature for the message M. - K is a random parameter required by some algorithms. - """ - if (not self.has_private()): - raise error, 'Private key not available in this object' - if isinstance(M, types.StringType): M=bytes_to_long(M) - if isinstance(K, types.StringType): K=bytes_to_long(K) - return self._sign(M, K) - - def verify (self, M, signature): - """verify(M:string|long, signature:tuple) : bool - Verify that the signature is valid for the message M; - returns true if the signature checks out. - """ - if isinstance(M, types.StringType): M=bytes_to_long(M) - return self._verify(M, signature) - - # alias to compensate for the old validate() name - def validate (self, M, signature): - warnings.warn("validate() method name is obsolete; use verify()", - DeprecationWarning) - - def blind(self, M, B): - """blind(M : string|long, B : string|long) : string|long - Blind message M using blinding factor B. - """ - wasString=0 - if isinstance(M, types.StringType): - M=bytes_to_long(M) ; wasString=1 - if isinstance(B, types.StringType): B=bytes_to_long(B) - blindedmessage=self._blind(M, B) - if wasString: return long_to_bytes(blindedmessage) - else: return blindedmessage - - def unblind(self, M, B): - """unblind(M : string|long, B : string|long) : string|long - Unblind message M using blinding factor B. - """ - wasString=0 - if isinstance(M, types.StringType): - M=bytes_to_long(M) ; wasString=1 - if isinstance(B, types.StringType): B=bytes_to_long(B) - unblindedmessage=self._unblind(M, B) - if wasString: return long_to_bytes(unblindedmessage) - else: return unblindedmessage - - - # The following methods will usually be left alone, except for - # signature-only algorithms. They both return Boolean values - # recording whether this key's algorithm can sign and encrypt. - def can_sign (self): - """can_sign() : bool - Return a Boolean value recording whether this algorithm can - generate signatures. (This does not imply that this - particular key object has the private information required to - to generate a signature.) - """ - return 1 - - def can_encrypt (self): - """can_encrypt() : bool - Return a Boolean value recording whether this algorithm can - encrypt data. (This does not imply that this - particular key object has the private information required to - to decrypt a message.) - """ - return 1 - - def can_blind (self): - """can_blind() : bool - Return a Boolean value recording whether this algorithm can - blind data. (This does not imply that this - particular key object has the private information required to - to blind a message.) - """ - return 0 - - # The following methods will certainly be overridden by - # subclasses. - - def size (self): - """size() : int - Return the maximum number of bits that can be handled by this key. - """ - return 0 - - def has_private (self): - """has_private() : bool - Return a Boolean denoting whether the object contains - private components. - """ - return 0 - - def publickey (self): - """publickey(): object - Return a new key object containing only the public information. - """ - return self - - def __eq__ (self, other): - """__eq__(other): 0, 1 - Compare us to other for equality. - """ - return self.__getstate__() == other.__getstate__() diff --git a/gdata/Crypto/PublicKey/qNEW.py b/gdata/Crypto/PublicKey/qNEW.py deleted file mode 100644 index 65f8ae36b3..0000000000 --- a/gdata/Crypto/PublicKey/qNEW.py +++ /dev/null @@ -1,170 +0,0 @@ -# -# qNEW.py : The q-NEW signature algorithm. -# -# Part of the Python Cryptography Toolkit -# -# Distribute and use freely; there are no restrictions on further -# dissemination and usage except those imposed by the laws of your -# country of residence. This software is provided "as is" without -# warranty of fitness for use or suitability for any purpose, express -# or implied. Use at your own risk or not at all. -# - -__revision__ = "$Id: qNEW.py,v 1.8 2003/04/04 15:13:35 akuchling Exp $" - -from Crypto.PublicKey import pubkey -from Crypto.Util.number import * -from Crypto.Hash import SHA - -class error (Exception): - pass - -HASHBITS = 160 # Size of SHA digests - -def generate(bits, randfunc, progress_func=None): - """generate(bits:int, randfunc:callable, progress_func:callable) - - Generate a qNEW key of length 'bits', using 'randfunc' to get - random data and 'progress_func', if present, to display - the progress of the key generation. - """ - obj=qNEWobj() - - # Generate prime numbers p and q. q is a 160-bit prime - # number. p is another prime number (the modulus) whose bit - # size is chosen by the caller, and is generated so that p-1 - # is a multiple of q. - # - # Note that only a single seed is used to - # generate p and q; if someone generates a key for you, you can - # use the seed to duplicate the key generation. This can - # protect you from someone generating values of p,q that have - # some special form that's easy to break. - if progress_func: - progress_func('p,q\n') - while (1): - obj.q = getPrime(160, randfunc) - # assert pow(2, 159L)1. g is kept; h can be discarded. - if progress_func: - progress_func('h,g\n') - while (1): - h=bytes_to_long(randfunc(bits)) % (p-1) - g=pow(h, power, p) - if 11: - break - obj.g=g - - # x is the private key information, and is - # just a random number between 0 and q. - # y=g**x mod p, and is part of the public information. - if progress_func: - progress_func('x,y\n') - while (1): - x=bytes_to_long(randfunc(20)) - if 0 < x < obj.q: - break - obj.x, obj.y=x, pow(g, x, p) - - return obj - -# Construct a qNEW object -def construct(tuple): - """construct(tuple:(long,long,long,long)|(long,long,long,long,long) - Construct a qNEW object from a 4- or 5-tuple of numbers. - """ - obj=qNEWobj() - if len(tuple) not in [4,5]: - raise error, 'argument for construct() wrong length' - for i in range(len(tuple)): - field = obj.keydata[i] - setattr(obj, field, tuple[i]) - return obj - -class qNEWobj(pubkey.pubkey): - keydata=['p', 'q', 'g', 'y', 'x'] - - def _sign(self, M, K=''): - if (self.q<=K): - raise error, 'K is greater than q' - if M<0: - raise error, 'Illegal value of M (<0)' - if M>=pow(2,161L): - raise error, 'Illegal value of M (too large)' - r=pow(self.g, K, self.p) % self.q - s=(K- (r*M*self.x % self.q)) % self.q - return (r,s) - def _verify(self, M, sig): - r, s = sig - if r<=0 or r>=self.q or s<=0 or s>=self.q: - return 0 - if M<0: - raise error, 'Illegal value of M (<0)' - if M<=0 or M>=pow(2,161L): - return 0 - v1 = pow(self.g, s, self.p) - v2 = pow(self.y, M*r, self.p) - v = ((v1*v2) % self.p) - v = v % self.q - if v==r: - return 1 - return 0 - - def size(self): - "Return the maximum number of bits that can be handled by this key." - return 160 - - def has_private(self): - """Return a Boolean denoting whether the object contains - private components.""" - return hasattr(self, 'x') - - def can_sign(self): - """Return a Boolean value recording whether this algorithm can generate signatures.""" - return 1 - - def can_encrypt(self): - """Return a Boolean value recording whether this algorithm can encrypt data.""" - return 0 - - def publickey(self): - """Return a new key object containing only the public information.""" - return construct((self.p, self.q, self.g, self.y)) - -object = qNEWobj - diff --git a/gdata/Crypto/Util/RFC1751.py b/gdata/Crypto/Util/RFC1751.py deleted file mode 100644 index 0a47952495..0000000000 --- a/gdata/Crypto/Util/RFC1751.py +++ /dev/null @@ -1,342 +0,0 @@ -#!/usr/local/bin/python -# rfc1751.py : Converts between 128-bit strings and a human-readable -# sequence of words, as defined in RFC1751: "A Convention for -# Human-Readable 128-bit Keys", by Daniel L. McDonald. - -__revision__ = "$Id: RFC1751.py,v 1.6 2003/04/04 15:15:10 akuchling Exp $" - - -import string, binascii - -binary={0:'0000', 1:'0001', 2:'0010', 3:'0011', 4:'0100', 5:'0101', - 6:'0110', 7:'0111', 8:'1000', 9:'1001', 10:'1010', 11:'1011', - 12:'1100', 13:'1101', 14:'1110', 15:'1111'} - -def _key2bin(s): - "Convert a key into a string of binary digits" - kl=map(lambda x: ord(x), s) - kl=map(lambda x: binary[x/16]+binary[x&15], kl) - return ''.join(kl) - -def _extract(key, start, length): - """Extract a bitstring from a string of binary digits, and return its - numeric value.""" - k=key[start:start+length] - return reduce(lambda x,y: x*2+ord(y)-48, k, 0) - -def key_to_english (key): - """key_to_english(key:string) : string - Transform an arbitrary key into a string containing English words. - The key length must be a multiple of 8. - """ - english='' - for index in range(0, len(key), 8): # Loop over 8-byte subkeys - subkey=key[index:index+8] - # Compute the parity of the key - skbin=_key2bin(subkey) ; p=0 - for i in range(0, 64, 2): p=p+_extract(skbin, i, 2) - # Append parity bits to the subkey - skbin=_key2bin(subkey+chr((p<<6) & 255)) - for i in range(0, 64, 11): - english=english+wordlist[_extract(skbin, i, 11)]+' ' - - return english[:-1] # Remove the trailing space - -def english_to_key (str): - """english_to_key(string):string - Transform a string into a corresponding key. - The string must contain words separated by whitespace; the number - of words must be a multiple of 6. - """ - - L=string.split(string.upper(str)) ; key='' - for index in range(0, len(L), 6): - sublist=L[index:index+6] ; char=9*[0] ; bits=0 - for i in sublist: - index = wordlist.index(i) - shift = (8-(bits+11)%8) %8 - y = index << shift - cl, cc, cr = (y>>16), (y>>8)&0xff, y & 0xff - if (shift>5): - char[bits/8] = char[bits/8] | cl - char[bits/8+1] = char[bits/8+1] | cc - char[bits/8+2] = char[bits/8+2] | cr - elif shift>-3: - char[bits/8] = char[bits/8] | cc - char[bits/8+1] = char[bits/8+1] | cr - else: char[bits/8] = char[bits/8] | cr - bits=bits+11 - subkey=reduce(lambda x,y:x+chr(y), char, '') - - # Check the parity of the resulting key - skbin=_key2bin(subkey) - p=0 - for i in range(0, 64, 2): p=p+_extract(skbin, i, 2) - if (p&3) != _extract(skbin, 64, 2): - raise ValueError, "Parity error in resulting key" - key=key+subkey[0:8] - return key - -wordlist=[ "A", "ABE", "ACE", "ACT", "AD", "ADA", "ADD", - "AGO", "AID", "AIM", "AIR", "ALL", "ALP", "AM", "AMY", "AN", "ANA", - "AND", "ANN", "ANT", "ANY", "APE", "APS", "APT", "ARC", "ARE", "ARK", - "ARM", "ART", "AS", "ASH", "ASK", "AT", "ATE", "AUG", "AUK", "AVE", - "AWE", "AWK", "AWL", "AWN", "AX", "AYE", "BAD", "BAG", "BAH", "BAM", - "BAN", "BAR", "BAT", "BAY", "BE", "BED", "BEE", "BEG", "BEN", "BET", - "BEY", "BIB", "BID", "BIG", "BIN", "BIT", "BOB", "BOG", "BON", "BOO", - "BOP", "BOW", "BOY", "BUB", "BUD", "BUG", "BUM", "BUN", "BUS", "BUT", - "BUY", "BY", "BYE", "CAB", "CAL", "CAM", "CAN", "CAP", "CAR", "CAT", - "CAW", "COD", "COG", "COL", "CON", "COO", "COP", "COT", "COW", "COY", - "CRY", "CUB", "CUE", "CUP", "CUR", "CUT", "DAB", "DAD", "DAM", "DAN", - "DAR", "DAY", "DEE", "DEL", "DEN", "DES", "DEW", "DID", "DIE", "DIG", - "DIN", "DIP", "DO", "DOE", "DOG", "DON", "DOT", "DOW", "DRY", "DUB", - "DUD", "DUE", "DUG", "DUN", "EAR", "EAT", "ED", "EEL", "EGG", "EGO", - "ELI", "ELK", "ELM", "ELY", "EM", "END", "EST", "ETC", "EVA", "EVE", - "EWE", "EYE", "FAD", "FAN", "FAR", "FAT", "FAY", "FED", "FEE", "FEW", - "FIB", "FIG", "FIN", "FIR", "FIT", "FLO", "FLY", "FOE", "FOG", "FOR", - "FRY", "FUM", "FUN", "FUR", "GAB", "GAD", "GAG", "GAL", "GAM", "GAP", - "GAS", "GAY", "GEE", "GEL", "GEM", "GET", "GIG", "GIL", "GIN", "GO", - "GOT", "GUM", "GUN", "GUS", "GUT", "GUY", "GYM", "GYP", "HA", "HAD", - "HAL", "HAM", "HAN", "HAP", "HAS", "HAT", "HAW", "HAY", "HE", "HEM", - "HEN", "HER", "HEW", "HEY", "HI", "HID", "HIM", "HIP", "HIS", "HIT", - "HO", "HOB", "HOC", "HOE", "HOG", "HOP", "HOT", "HOW", "HUB", "HUE", - "HUG", "HUH", "HUM", "HUT", "I", "ICY", "IDA", "IF", "IKE", "ILL", - "INK", "INN", "IO", "ION", "IQ", "IRA", "IRE", "IRK", "IS", "IT", - "ITS", "IVY", "JAB", "JAG", "JAM", "JAN", "JAR", "JAW", "JAY", "JET", - "JIG", "JIM", "JO", "JOB", "JOE", "JOG", "JOT", "JOY", "JUG", "JUT", - "KAY", "KEG", "KEN", "KEY", "KID", "KIM", "KIN", "KIT", "LA", "LAB", - "LAC", "LAD", "LAG", "LAM", "LAP", "LAW", "LAY", "LEA", "LED", "LEE", - "LEG", "LEN", "LEO", "LET", "LEW", "LID", "LIE", "LIN", "LIP", "LIT", - "LO", "LOB", "LOG", "LOP", "LOS", "LOT", "LOU", "LOW", "LOY", "LUG", - "LYE", "MA", "MAC", "MAD", "MAE", "MAN", "MAO", "MAP", "MAT", "MAW", - "MAY", "ME", "MEG", "MEL", "MEN", "MET", "MEW", "MID", "MIN", "MIT", - "MOB", "MOD", "MOE", "MOO", "MOP", "MOS", "MOT", "MOW", "MUD", "MUG", - "MUM", "MY", "NAB", "NAG", "NAN", "NAP", "NAT", "NAY", "NE", "NED", - "NEE", "NET", "NEW", "NIB", "NIL", "NIP", "NIT", "NO", "NOB", "NOD", - "NON", "NOR", "NOT", "NOV", "NOW", "NU", "NUN", "NUT", "O", "OAF", - "OAK", "OAR", "OAT", "ODD", "ODE", "OF", "OFF", "OFT", "OH", "OIL", - "OK", "OLD", "ON", "ONE", "OR", "ORB", "ORE", "ORR", "OS", "OTT", - "OUR", "OUT", "OVA", "OW", "OWE", "OWL", "OWN", "OX", "PA", "PAD", - "PAL", "PAM", "PAN", "PAP", "PAR", "PAT", "PAW", "PAY", "PEA", "PEG", - "PEN", "PEP", "PER", "PET", "PEW", "PHI", "PI", "PIE", "PIN", "PIT", - "PLY", "PO", "POD", "POE", "POP", "POT", "POW", "PRO", "PRY", "PUB", - "PUG", "PUN", "PUP", "PUT", "QUO", "RAG", "RAM", "RAN", "RAP", "RAT", - "RAW", "RAY", "REB", "RED", "REP", "RET", "RIB", "RID", "RIG", "RIM", - "RIO", "RIP", "ROB", "ROD", "ROE", "RON", "ROT", "ROW", "ROY", "RUB", - "RUE", "RUG", "RUM", "RUN", "RYE", "SAC", "SAD", "SAG", "SAL", "SAM", - "SAN", "SAP", "SAT", "SAW", "SAY", "SEA", "SEC", "SEE", "SEN", "SET", - "SEW", "SHE", "SHY", "SIN", "SIP", "SIR", "SIS", "SIT", "SKI", "SKY", - "SLY", "SO", "SOB", "SOD", "SON", "SOP", "SOW", "SOY", "SPA", "SPY", - "SUB", "SUD", "SUE", "SUM", "SUN", "SUP", "TAB", "TAD", "TAG", "TAN", - "TAP", "TAR", "TEA", "TED", "TEE", "TEN", "THE", "THY", "TIC", "TIE", - "TIM", "TIN", "TIP", "TO", "TOE", "TOG", "TOM", "TON", "TOO", "TOP", - "TOW", "TOY", "TRY", "TUB", "TUG", "TUM", "TUN", "TWO", "UN", "UP", - "US", "USE", "VAN", "VAT", "VET", "VIE", "WAD", "WAG", "WAR", "WAS", - "WAY", "WE", "WEB", "WED", "WEE", "WET", "WHO", "WHY", "WIN", "WIT", - "WOK", "WON", "WOO", "WOW", "WRY", "WU", "YAM", "YAP", "YAW", "YE", - "YEA", "YES", "YET", "YOU", "ABED", "ABEL", "ABET", "ABLE", "ABUT", - "ACHE", "ACID", "ACME", "ACRE", "ACTA", "ACTS", "ADAM", "ADDS", - "ADEN", "AFAR", "AFRO", "AGEE", "AHEM", "AHOY", "AIDA", "AIDE", - "AIDS", "AIRY", "AJAR", "AKIN", "ALAN", "ALEC", "ALGA", "ALIA", - "ALLY", "ALMA", "ALOE", "ALSO", "ALTO", "ALUM", "ALVA", "AMEN", - "AMES", "AMID", "AMMO", "AMOK", "AMOS", "AMRA", "ANDY", "ANEW", - "ANNA", "ANNE", "ANTE", "ANTI", "AQUA", "ARAB", "ARCH", "AREA", - "ARGO", "ARID", "ARMY", "ARTS", "ARTY", "ASIA", "ASKS", "ATOM", - "AUNT", "AURA", "AUTO", "AVER", "AVID", "AVIS", "AVON", "AVOW", - "AWAY", "AWRY", "BABE", "BABY", "BACH", "BACK", "BADE", "BAIL", - "BAIT", "BAKE", "BALD", "BALE", "BALI", "BALK", "BALL", "BALM", - "BAND", "BANE", "BANG", "BANK", "BARB", "BARD", "BARE", "BARK", - "BARN", "BARR", "BASE", "BASH", "BASK", "BASS", "BATE", "BATH", - "BAWD", "BAWL", "BEAD", "BEAK", "BEAM", "BEAN", "BEAR", "BEAT", - "BEAU", "BECK", "BEEF", "BEEN", "BEER", - "BEET", "BELA", "BELL", "BELT", "BEND", "BENT", "BERG", "BERN", - "BERT", "BESS", "BEST", "BETA", "BETH", "BHOY", "BIAS", "BIDE", - "BIEN", "BILE", "BILK", "BILL", "BIND", "BING", "BIRD", "BITE", - "BITS", "BLAB", "BLAT", "BLED", "BLEW", "BLOB", "BLOC", "BLOT", - "BLOW", "BLUE", "BLUM", "BLUR", "BOAR", "BOAT", "BOCA", "BOCK", - "BODE", "BODY", "BOGY", "BOHR", "BOIL", "BOLD", "BOLO", "BOLT", - "BOMB", "BONA", "BOND", "BONE", "BONG", "BONN", "BONY", "BOOK", - "BOOM", "BOON", "BOOT", "BORE", "BORG", "BORN", "BOSE", "BOSS", - "BOTH", "BOUT", "BOWL", "BOYD", "BRAD", "BRAE", "BRAG", "BRAN", - "BRAY", "BRED", "BREW", "BRIG", "BRIM", "BROW", "BUCK", "BUDD", - "BUFF", "BULB", "BULK", "BULL", "BUNK", "BUNT", "BUOY", "BURG", - "BURL", "BURN", "BURR", "BURT", "BURY", "BUSH", "BUSS", "BUST", - "BUSY", "BYTE", "CADY", "CAFE", "CAGE", "CAIN", "CAKE", "CALF", - "CALL", "CALM", "CAME", "CANE", "CANT", "CARD", "CARE", "CARL", - "CARR", "CART", "CASE", "CASH", "CASK", "CAST", "CAVE", "CEIL", - "CELL", "CENT", "CERN", "CHAD", "CHAR", "CHAT", "CHAW", "CHEF", - "CHEN", "CHEW", "CHIC", "CHIN", "CHOU", "CHOW", "CHUB", "CHUG", - "CHUM", "CITE", "CITY", "CLAD", "CLAM", "CLAN", "CLAW", "CLAY", - "CLOD", "CLOG", "CLOT", "CLUB", "CLUE", "COAL", "COAT", "COCA", - "COCK", "COCO", "CODA", "CODE", "CODY", "COED", "COIL", "COIN", - "COKE", "COLA", "COLD", "COLT", "COMA", "COMB", "COME", "COOK", - "COOL", "COON", "COOT", "CORD", "CORE", "CORK", "CORN", "COST", - "COVE", "COWL", "CRAB", "CRAG", "CRAM", "CRAY", "CREW", "CRIB", - "CROW", "CRUD", "CUBA", "CUBE", "CUFF", "CULL", "CULT", "CUNY", - "CURB", "CURD", "CURE", "CURL", "CURT", "CUTS", "DADE", "DALE", - "DAME", "DANA", "DANE", "DANG", "DANK", "DARE", "DARK", "DARN", - "DART", "DASH", "DATA", "DATE", "DAVE", "DAVY", "DAWN", "DAYS", - "DEAD", "DEAF", "DEAL", "DEAN", "DEAR", "DEBT", "DECK", "DEED", - "DEEM", "DEER", "DEFT", "DEFY", "DELL", "DENT", "DENY", "DESK", - "DIAL", "DICE", "DIED", "DIET", "DIME", "DINE", "DING", "DINT", - "DIRE", "DIRT", "DISC", "DISH", "DISK", "DIVE", "DOCK", "DOES", - "DOLE", "DOLL", "DOLT", "DOME", "DONE", "DOOM", "DOOR", "DORA", - "DOSE", "DOTE", "DOUG", "DOUR", "DOVE", "DOWN", "DRAB", "DRAG", - "DRAM", "DRAW", "DREW", "DRUB", "DRUG", "DRUM", "DUAL", "DUCK", - "DUCT", "DUEL", "DUET", "DUKE", "DULL", "DUMB", "DUNE", "DUNK", - "DUSK", "DUST", "DUTY", "EACH", "EARL", "EARN", "EASE", "EAST", - "EASY", "EBEN", "ECHO", "EDDY", "EDEN", "EDGE", "EDGY", "EDIT", - "EDNA", "EGAN", "ELAN", "ELBA", "ELLA", "ELSE", "EMIL", "EMIT", - "EMMA", "ENDS", "ERIC", "EROS", "EVEN", "EVER", "EVIL", "EYED", - "FACE", "FACT", "FADE", "FAIL", "FAIN", "FAIR", "FAKE", "FALL", - "FAME", "FANG", "FARM", "FAST", "FATE", "FAWN", "FEAR", "FEAT", - "FEED", "FEEL", "FEET", "FELL", "FELT", "FEND", "FERN", "FEST", - "FEUD", "FIEF", "FIGS", "FILE", "FILL", "FILM", "FIND", "FINE", - "FINK", "FIRE", "FIRM", "FISH", "FISK", "FIST", "FITS", "FIVE", - "FLAG", "FLAK", "FLAM", "FLAT", "FLAW", "FLEA", "FLED", "FLEW", - "FLIT", "FLOC", "FLOG", "FLOW", "FLUB", "FLUE", "FOAL", "FOAM", - "FOGY", "FOIL", "FOLD", "FOLK", "FOND", "FONT", "FOOD", "FOOL", - "FOOT", "FORD", "FORE", "FORK", "FORM", "FORT", "FOSS", "FOUL", - "FOUR", "FOWL", "FRAU", "FRAY", "FRED", "FREE", "FRET", "FREY", - "FROG", "FROM", "FUEL", "FULL", "FUME", "FUND", "FUNK", "FURY", - "FUSE", "FUSS", "GAFF", "GAGE", "GAIL", "GAIN", "GAIT", "GALA", - "GALE", "GALL", "GALT", "GAME", "GANG", "GARB", "GARY", "GASH", - "GATE", "GAUL", "GAUR", "GAVE", "GAWK", "GEAR", "GELD", "GENE", - "GENT", "GERM", "GETS", "GIBE", "GIFT", "GILD", "GILL", "GILT", - "GINA", "GIRD", "GIRL", "GIST", "GIVE", "GLAD", "GLEE", "GLEN", - "GLIB", "GLOB", "GLOM", "GLOW", "GLUE", "GLUM", "GLUT", "GOAD", - "GOAL", "GOAT", "GOER", "GOES", "GOLD", "GOLF", "GONE", "GONG", - "GOOD", "GOOF", "GORE", "GORY", "GOSH", "GOUT", "GOWN", "GRAB", - "GRAD", "GRAY", "GREG", "GREW", "GREY", "GRID", "GRIM", "GRIN", - "GRIT", "GROW", "GRUB", "GULF", "GULL", "GUNK", "GURU", "GUSH", - "GUST", "GWEN", "GWYN", "HAAG", "HAAS", "HACK", "HAIL", "HAIR", - "HALE", "HALF", "HALL", "HALO", "HALT", "HAND", "HANG", "HANK", - "HANS", "HARD", "HARK", "HARM", "HART", "HASH", "HAST", "HATE", - "HATH", "HAUL", "HAVE", "HAWK", "HAYS", "HEAD", "HEAL", "HEAR", - "HEAT", "HEBE", "HECK", "HEED", "HEEL", "HEFT", "HELD", "HELL", - "HELM", "HERB", "HERD", "HERE", "HERO", "HERS", "HESS", "HEWN", - "HICK", "HIDE", "HIGH", "HIKE", "HILL", "HILT", "HIND", "HINT", - "HIRE", "HISS", "HIVE", "HOBO", "HOCK", "HOFF", "HOLD", "HOLE", - "HOLM", "HOLT", "HOME", "HONE", "HONK", "HOOD", "HOOF", "HOOK", - "HOOT", "HORN", "HOSE", "HOST", "HOUR", "HOVE", "HOWE", "HOWL", - "HOYT", "HUCK", "HUED", "HUFF", "HUGE", "HUGH", "HUGO", "HULK", - "HULL", "HUNK", "HUNT", "HURD", "HURL", "HURT", "HUSH", "HYDE", - "HYMN", "IBIS", "ICON", "IDEA", "IDLE", "IFFY", "INCA", "INCH", - "INTO", "IONS", "IOTA", "IOWA", "IRIS", "IRMA", "IRON", "ISLE", - "ITCH", "ITEM", "IVAN", "JACK", "JADE", "JAIL", "JAKE", "JANE", - "JAVA", "JEAN", "JEFF", "JERK", "JESS", "JEST", "JIBE", "JILL", - "JILT", "JIVE", "JOAN", "JOBS", "JOCK", "JOEL", "JOEY", "JOHN", - "JOIN", "JOKE", "JOLT", "JOVE", "JUDD", "JUDE", "JUDO", "JUDY", - "JUJU", "JUKE", "JULY", "JUNE", "JUNK", "JUNO", "JURY", "JUST", - "JUTE", "KAHN", "KALE", "KANE", "KANT", "KARL", "KATE", "KEEL", - "KEEN", "KENO", "KENT", "KERN", "KERR", "KEYS", "KICK", "KILL", - "KIND", "KING", "KIRK", "KISS", "KITE", "KLAN", "KNEE", "KNEW", - "KNIT", "KNOB", "KNOT", "KNOW", "KOCH", "KONG", "KUDO", "KURD", - "KURT", "KYLE", "LACE", "LACK", "LACY", "LADY", "LAID", "LAIN", - "LAIR", "LAKE", "LAMB", "LAME", "LAND", "LANE", "LANG", "LARD", - "LARK", "LASS", "LAST", "LATE", "LAUD", "LAVA", "LAWN", "LAWS", - "LAYS", "LEAD", "LEAF", "LEAK", "LEAN", "LEAR", "LEEK", "LEER", - "LEFT", "LEND", "LENS", "LENT", "LEON", "LESK", "LESS", "LEST", - "LETS", "LIAR", "LICE", "LICK", "LIED", "LIEN", "LIES", "LIEU", - "LIFE", "LIFT", "LIKE", "LILA", "LILT", "LILY", "LIMA", "LIMB", - "LIME", "LIND", "LINE", "LINK", "LINT", "LION", "LISA", "LIST", - "LIVE", "LOAD", "LOAF", "LOAM", "LOAN", "LOCK", "LOFT", "LOGE", - "LOIS", "LOLA", "LONE", "LONG", "LOOK", "LOON", "LOOT", "LORD", - "LORE", "LOSE", "LOSS", "LOST", "LOUD", "LOVE", "LOWE", "LUCK", - "LUCY", "LUGE", "LUKE", "LULU", "LUND", "LUNG", "LURA", "LURE", - "LURK", "LUSH", "LUST", "LYLE", "LYNN", "LYON", "LYRA", "MACE", - "MADE", "MAGI", "MAID", "MAIL", "MAIN", "MAKE", "MALE", "MALI", - "MALL", "MALT", "MANA", "MANN", "MANY", "MARC", "MARE", "MARK", - "MARS", "MART", "MARY", "MASH", "MASK", "MASS", "MAST", "MATE", - "MATH", "MAUL", "MAYO", "MEAD", "MEAL", "MEAN", "MEAT", "MEEK", - "MEET", "MELD", "MELT", "MEMO", "MEND", "MENU", "MERT", "MESH", - "MESS", "MICE", "MIKE", "MILD", "MILE", "MILK", "MILL", "MILT", - "MIMI", "MIND", "MINE", "MINI", "MINK", "MINT", "MIRE", "MISS", - "MIST", "MITE", "MITT", "MOAN", "MOAT", "MOCK", "MODE", "MOLD", - "MOLE", "MOLL", "MOLT", "MONA", "MONK", "MONT", "MOOD", "MOON", - "MOOR", "MOOT", "MORE", "MORN", "MORT", "MOSS", "MOST", "MOTH", - "MOVE", "MUCH", "MUCK", "MUDD", "MUFF", "MULE", "MULL", "MURK", - "MUSH", "MUST", "MUTE", "MUTT", "MYRA", "MYTH", "NAGY", "NAIL", - "NAIR", "NAME", "NARY", "NASH", "NAVE", "NAVY", "NEAL", "NEAR", - "NEAT", "NECK", "NEED", "NEIL", "NELL", "NEON", "NERO", "NESS", - "NEST", "NEWS", "NEWT", "NIBS", "NICE", "NICK", "NILE", "NINA", - "NINE", "NOAH", "NODE", "NOEL", "NOLL", "NONE", "NOOK", "NOON", - "NORM", "NOSE", "NOTE", "NOUN", "NOVA", "NUDE", "NULL", "NUMB", - "OATH", "OBEY", "OBOE", "ODIN", "OHIO", "OILY", "OINT", "OKAY", - "OLAF", "OLDY", "OLGA", "OLIN", "OMAN", "OMEN", "OMIT", "ONCE", - "ONES", "ONLY", "ONTO", "ONUS", "ORAL", "ORGY", "OSLO", "OTIS", - "OTTO", "OUCH", "OUST", "OUTS", "OVAL", "OVEN", "OVER", "OWLY", - "OWNS", "QUAD", "QUIT", "QUOD", "RACE", "RACK", "RACY", "RAFT", - "RAGE", "RAID", "RAIL", "RAIN", "RAKE", "RANK", "RANT", "RARE", - "RASH", "RATE", "RAVE", "RAYS", "READ", "REAL", "REAM", "REAR", - "RECK", "REED", "REEF", "REEK", "REEL", "REID", "REIN", "RENA", - "REND", "RENT", "REST", "RICE", "RICH", "RICK", "RIDE", "RIFT", - "RILL", "RIME", "RING", "RINK", "RISE", "RISK", "RITE", "ROAD", - "ROAM", "ROAR", "ROBE", "ROCK", "RODE", "ROIL", "ROLL", "ROME", - "ROOD", "ROOF", "ROOK", "ROOM", "ROOT", "ROSA", "ROSE", "ROSS", - "ROSY", "ROTH", "ROUT", "ROVE", "ROWE", "ROWS", "RUBE", "RUBY", - "RUDE", "RUDY", "RUIN", "RULE", "RUNG", "RUNS", "RUNT", "RUSE", - "RUSH", "RUSK", "RUSS", "RUST", "RUTH", "SACK", "SAFE", "SAGE", - "SAID", "SAIL", "SALE", "SALK", "SALT", "SAME", "SAND", "SANE", - "SANG", "SANK", "SARA", "SAUL", "SAVE", "SAYS", "SCAN", "SCAR", - "SCAT", "SCOT", "SEAL", "SEAM", "SEAR", "SEAT", "SEED", "SEEK", - "SEEM", "SEEN", "SEES", "SELF", "SELL", "SEND", "SENT", "SETS", - "SEWN", "SHAG", "SHAM", "SHAW", "SHAY", "SHED", "SHIM", "SHIN", - "SHOD", "SHOE", "SHOT", "SHOW", "SHUN", "SHUT", "SICK", "SIDE", - "SIFT", "SIGH", "SIGN", "SILK", "SILL", "SILO", "SILT", "SINE", - "SING", "SINK", "SIRE", "SITE", "SITS", "SITU", "SKAT", "SKEW", - "SKID", "SKIM", "SKIN", "SKIT", "SLAB", "SLAM", "SLAT", "SLAY", - "SLED", "SLEW", "SLID", "SLIM", "SLIT", "SLOB", "SLOG", "SLOT", - "SLOW", "SLUG", "SLUM", "SLUR", "SMOG", "SMUG", "SNAG", "SNOB", - "SNOW", "SNUB", "SNUG", "SOAK", "SOAR", "SOCK", "SODA", "SOFA", - "SOFT", "SOIL", "SOLD", "SOME", "SONG", "SOON", "SOOT", "SORE", - "SORT", "SOUL", "SOUR", "SOWN", "STAB", "STAG", "STAN", "STAR", - "STAY", "STEM", "STEW", "STIR", "STOW", "STUB", "STUN", "SUCH", - "SUDS", "SUIT", "SULK", "SUMS", "SUNG", "SUNK", "SURE", "SURF", - "SWAB", "SWAG", "SWAM", "SWAN", "SWAT", "SWAY", "SWIM", "SWUM", - "TACK", "TACT", "TAIL", "TAKE", "TALE", "TALK", "TALL", "TANK", - "TASK", "TATE", "TAUT", "TEAL", "TEAM", "TEAR", "TECH", "TEEM", - "TEEN", "TEET", "TELL", "TEND", "TENT", "TERM", "TERN", "TESS", - "TEST", "THAN", "THAT", "THEE", "THEM", "THEN", "THEY", "THIN", - "THIS", "THUD", "THUG", "TICK", "TIDE", "TIDY", "TIED", "TIER", - "TILE", "TILL", "TILT", "TIME", "TINA", "TINE", "TINT", "TINY", - "TIRE", "TOAD", "TOGO", "TOIL", "TOLD", "TOLL", "TONE", "TONG", - "TONY", "TOOK", "TOOL", "TOOT", "TORE", "TORN", "TOTE", "TOUR", - "TOUT", "TOWN", "TRAG", "TRAM", "TRAY", "TREE", "TREK", "TRIG", - "TRIM", "TRIO", "TROD", "TROT", "TROY", "TRUE", "TUBA", "TUBE", - "TUCK", "TUFT", "TUNA", "TUNE", "TUNG", "TURF", "TURN", "TUSK", - "TWIG", "TWIN", "TWIT", "ULAN", "UNIT", "URGE", "USED", "USER", - "USES", "UTAH", "VAIL", "VAIN", "VALE", "VARY", "VASE", "VAST", - "VEAL", "VEDA", "VEIL", "VEIN", "VEND", "VENT", "VERB", "VERY", - "VETO", "VICE", "VIEW", "VINE", "VISE", "VOID", "VOLT", "VOTE", - "WACK", "WADE", "WAGE", "WAIL", "WAIT", "WAKE", "WALE", "WALK", - "WALL", "WALT", "WAND", "WANE", "WANG", "WANT", "WARD", "WARM", - "WARN", "WART", "WASH", "WAST", "WATS", "WATT", "WAVE", "WAVY", - "WAYS", "WEAK", "WEAL", "WEAN", "WEAR", "WEED", "WEEK", "WEIR", - "WELD", "WELL", "WELT", "WENT", "WERE", "WERT", "WEST", "WHAM", - "WHAT", "WHEE", "WHEN", "WHET", "WHOA", "WHOM", "WICK", "WIFE", - "WILD", "WILL", "WIND", "WINE", "WING", "WINK", "WINO", "WIRE", - "WISE", "WISH", "WITH", "WOLF", "WONT", "WOOD", "WOOL", "WORD", - "WORE", "WORK", "WORM", "WORN", "WOVE", "WRIT", "WYNN", "YALE", - "YANG", "YANK", "YARD", "YARN", "YAWL", "YAWN", "YEAH", "YEAR", - "YELL", "YOGA", "YOKE" ] - -if __name__=='__main__': - data = [('EB33F77EE73D4053', 'TIDE ITCH SLOW REIN RULE MOT'), - ('CCAC2AED591056BE4F90FD441C534766', - 'RASH BUSH MILK LOOK BAD BRIM AVID GAFF BAIT ROT POD LOVE'), - ('EFF81F9BFBC65350920CDD7416DE8009', - 'TROD MUTE TAIL WARM CHAR KONG HAAG CITY BORE O TEAL AWL') - ] - - for key, words in data: - print 'Trying key', key - key=binascii.a2b_hex(key) - w2=key_to_english(key) - if w2!=words: - print 'key_to_english fails on key', repr(key), ', producing', str(w2) - k2=english_to_key(words) - if k2!=key: - print 'english_to_key fails on key', repr(key), ', producing', repr(k2) - - diff --git a/gdata/Crypto/Util/__init__.py b/gdata/Crypto/Util/__init__.py deleted file mode 100644 index 0d14768181..0000000000 --- a/gdata/Crypto/Util/__init__.py +++ /dev/null @@ -1,16 +0,0 @@ -"""Miscellaneous modules - -Contains useful modules that don't belong into any of the -other Crypto.* subpackages. - -Crypto.Util.number Number-theoretic functions (primality testing, etc.) -Crypto.Util.randpool Random number generation -Crypto.Util.RFC1751 Converts between 128-bit keys and human-readable - strings of words. - -""" - -__all__ = ['randpool', 'RFC1751', 'number'] - -__revision__ = "$Id: __init__.py,v 1.4 2003/02/28 15:26:00 akuchling Exp $" - diff --git a/gdata/Crypto/Util/number.py b/gdata/Crypto/Util/number.py deleted file mode 100644 index 9d50563e90..0000000000 --- a/gdata/Crypto/Util/number.py +++ /dev/null @@ -1,201 +0,0 @@ -# -# number.py : Number-theoretic functions -# -# Part of the Python Cryptography Toolkit -# -# Distribute and use freely; there are no restrictions on further -# dissemination and usage except those imposed by the laws of your -# country of residence. This software is provided "as is" without -# warranty of fitness for use or suitability for any purpose, express -# or implied. Use at your own risk or not at all. -# - -__revision__ = "$Id: number.py,v 1.13 2003/04/04 18:21:07 akuchling Exp $" - -bignum = long -try: - from Crypto.PublicKey import _fastmath -except ImportError: - _fastmath = None - -# Commented out and replaced with faster versions below -## def long2str(n): -## s='' -## while n>0: -## s=chr(n & 255)+s -## n=n>>8 -## return s - -## import types -## def str2long(s): -## if type(s)!=types.StringType: return s # Integers will be left alone -## return reduce(lambda x,y : x*256+ord(y), s, 0L) - -def size (N): - """size(N:long) : int - Returns the size of the number N in bits. - """ - bits, power = 0,1L - while N >= power: - bits += 1 - power = power << 1 - return bits - -def getRandomNumber(N, randfunc): - """getRandomNumber(N:int, randfunc:callable):long - Return an N-bit random number.""" - - S = randfunc(N/8) - odd_bits = N % 8 - if odd_bits != 0: - char = ord(randfunc(1)) >> (8-odd_bits) - S = chr(char) + S - value = bytes_to_long(S) - value |= 2L ** (N-1) # Ensure high bit is set - assert size(value) >= N - return value - -def GCD(x,y): - """GCD(x:long, y:long): long - Return the GCD of x and y. - """ - x = abs(x) ; y = abs(y) - while x > 0: - x, y = y % x, x - return y - -def inverse(u, v): - """inverse(u:long, u:long):long - Return the inverse of u mod v. - """ - u3, v3 = long(u), long(v) - u1, v1 = 1L, 0L - while v3 > 0: - q=u3 / v3 - u1, v1 = v1, u1 - v1*q - u3, v3 = v3, u3 - v3*q - while u1<0: - u1 = u1 + v - return u1 - -# Given a number of bits to generate and a random generation function, -# find a prime number of the appropriate size. - -def getPrime(N, randfunc): - """getPrime(N:int, randfunc:callable):long - Return a random N-bit prime number. - """ - - number=getRandomNumber(N, randfunc) | 1 - while (not isPrime(number)): - number=number+2 - return number - -def isPrime(N): - """isPrime(N:long):bool - Return true if N is prime. - """ - if N == 1: - return 0 - if N in sieve: - return 1 - for i in sieve: - if (N % i)==0: - return 0 - - # Use the accelerator if available - if _fastmath is not None: - return _fastmath.isPrime(N) - - # Compute the highest bit that's set in N - N1 = N - 1L - n = 1L - while (n> 1L - - # Rabin-Miller test - for c in sieve[:7]: - a=long(c) ; d=1L ; t=n - while (t): # Iterate over the bits in N1 - x=(d*d) % N - if x==1L and d!=1L and d!=N1: - return 0 # Square root of 1 found - if N1 & t: - d=(x*a) % N - else: - d=x - t = t >> 1L - if d!=1L: - return 0 - return 1 - -# Small primes used for checking primality; these are all the primes -# less than 256. This should be enough to eliminate most of the odd -# numbers before needing to do a Rabin-Miller test at all. - -sieve=[2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, - 61, 67, 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, - 131, 137, 139, 149, 151, 157, 163, 167, 173, 179, 181, 191, 193, - 197, 199, 211, 223, 227, 229, 233, 239, 241, 251] - -# Improved conversion functions contributed by Barry Warsaw, after -# careful benchmarking - -import struct - -def long_to_bytes(n, blocksize=0): - """long_to_bytes(n:long, blocksize:int) : string - Convert a long integer to a byte string. - - If optional blocksize is given and greater than zero, pad the front of the - byte string with binary zeros so that the length is a multiple of - blocksize. - """ - # after much testing, this algorithm was deemed to be the fastest - s = '' - n = long(n) - pack = struct.pack - while n > 0: - s = pack('>I', n & 0xffffffffL) + s - n = n >> 32 - # strip off leading zeros - for i in range(len(s)): - if s[i] != '\000': - break - else: - # only happens when n == 0 - s = '\000' - i = 0 - s = s[i:] - # add back some pad bytes. this could be done more efficiently w.r.t. the - # de-padding being done above, but sigh... - if blocksize > 0 and len(s) % blocksize: - s = (blocksize - len(s) % blocksize) * '\000' + s - return s - -def bytes_to_long(s): - """bytes_to_long(string) : long - Convert a byte string to a long integer. - - This is (essentially) the inverse of long_to_bytes(). - """ - acc = 0L - unpack = struct.unpack - length = len(s) - if length % 4: - extra = (4 - length % 4) - s = '\000' * extra + s - length = length + extra - for i in range(0, length, 4): - acc = (acc << 32) + unpack('>I', s[i:i+4])[0] - return acc - -# For backwards compatibility... -import warnings -def long2str(n, blocksize=0): - warnings.warn("long2str() has been replaced by long_to_bytes()") - return long_to_bytes(n, blocksize) -def str2long(s): - warnings.warn("str2long() has been replaced by bytes_to_long()") - return bytes_to_long(s) diff --git a/gdata/Crypto/Util/randpool.py b/gdata/Crypto/Util/randpool.py deleted file mode 100644 index 467501c544..0000000000 --- a/gdata/Crypto/Util/randpool.py +++ /dev/null @@ -1,421 +0,0 @@ -# -# randpool.py : Cryptographically strong random number generation -# -# Part of the Python Cryptography Toolkit -# -# Distribute and use freely; there are no restrictions on further -# dissemination and usage except those imposed by the laws of your -# country of residence. This software is provided "as is" without -# warranty of fitness for use or suitability for any purpose, express -# or implied. Use at your own risk or not at all. -# - -__revision__ = "$Id: randpool.py,v 1.14 2004/05/06 12:56:54 akuchling Exp $" - -import time, array, types, warnings, os.path -from Crypto.Util.number import long_to_bytes -try: - import Crypto.Util.winrandom as winrandom -except: - winrandom = None - -STIRNUM = 3 - -class RandomPool: - """randpool.py : Cryptographically strong random number generation. - - The implementation here is similar to the one in PGP. To be - cryptographically strong, it must be difficult to determine the RNG's - output, whether in the future or the past. This is done by using - a cryptographic hash function to "stir" the random data. - - Entropy is gathered in the same fashion as PGP; the highest-resolution - clock around is read and the data is added to the random number pool. - A conservative estimate of the entropy is then kept. - - If a cryptographically secure random source is available (/dev/urandom - on many Unixes, Windows CryptGenRandom on most Windows), then use - it. - - Instance Attributes: - bits : int - Maximum size of pool in bits - bytes : int - Maximum size of pool in bytes - entropy : int - Number of bits of entropy in this pool. - - Methods: - add_event([s]) : add some entropy to the pool - get_bytes(int) : get N bytes of random data - randomize([N]) : get N bytes of randomness from external source - """ - - - def __init__(self, numbytes = 160, cipher=None, hash=None): - if hash is None: - from Crypto.Hash import SHA as hash - - # The cipher argument is vestigial; it was removed from - # version 1.1 so RandomPool would work even in the limited - # exportable subset of the code - if cipher is not None: - warnings.warn("'cipher' parameter is no longer used") - - if isinstance(hash, types.StringType): - # ugly hack to force __import__ to give us the end-path module - hash = __import__('Crypto.Hash.'+hash, - None, None, ['new']) - warnings.warn("'hash' parameter should now be a hashing module") - - self.bytes = numbytes - self.bits = self.bytes*8 - self.entropy = 0 - self._hash = hash - - # Construct an array to hold the random pool, - # initializing it to 0. - self._randpool = array.array('B', [0]*self.bytes) - - self._event1 = self._event2 = 0 - self._addPos = 0 - self._getPos = hash.digest_size - self._lastcounter=time.time() - self.__counter = 0 - - self._measureTickSize() # Estimate timer resolution - self._randomize() - - def _updateEntropyEstimate(self, nbits): - self.entropy += nbits - if self.entropy < 0: - self.entropy = 0 - elif self.entropy > self.bits: - self.entropy = self.bits - - def _randomize(self, N = 0, devname = '/dev/urandom'): - """_randomize(N, DEVNAME:device-filepath) - collects N bits of randomness from some entropy source (e.g., - /dev/urandom on Unixes that have it, Windows CryptoAPI - CryptGenRandom, etc) - DEVNAME is optional, defaults to /dev/urandom. You can change it - to /dev/random if you want to block till you get enough - entropy. - """ - data = '' - if N <= 0: - nbytes = int((self.bits - self.entropy)/8+0.5) - else: - nbytes = int(N/8+0.5) - if winrandom: - # Windows CryptGenRandom provides random data. - data = winrandom.new().get_bytes(nbytes) - elif os.path.exists(devname): - # Many OSes support a /dev/urandom device - try: - f=open(devname) - data=f.read(nbytes) - f.close() - except IOError, (num, msg): - if num!=2: raise IOError, (num, msg) - # If the file wasn't found, ignore the error - if data: - self._addBytes(data) - # Entropy estimate: The number of bits of - # data obtained from the random source. - self._updateEntropyEstimate(8*len(data)) - self.stir_n() # Wash the random pool - - def randomize(self, N=0): - """randomize(N:int) - use the class entropy source to get some entropy data. - This is overridden by KeyboardRandomize(). - """ - return self._randomize(N) - - def stir_n(self, N = STIRNUM): - """stir_n(N) - stirs the random pool N times - """ - for i in xrange(N): - self.stir() - - def stir (self, s = ''): - """stir(s:string) - Mix up the randomness pool. This will call add_event() twice, - but out of paranoia the entropy attribute will not be - increased. The optional 's' parameter is a string that will - be hashed with the randomness pool. - """ - - entropy=self.entropy # Save inital entropy value - self.add_event() - - # Loop over the randomness pool: hash its contents - # along with a counter, and add the resulting digest - # back into the pool. - for i in range(self.bytes / self._hash.digest_size): - h = self._hash.new(self._randpool) - h.update(str(self.__counter) + str(i) + str(self._addPos) + s) - self._addBytes( h.digest() ) - self.__counter = (self.__counter + 1) & 0xFFFFffffL - - self._addPos, self._getPos = 0, self._hash.digest_size - self.add_event() - - # Restore the old value of the entropy. - self.entropy=entropy - - - def get_bytes (self, N): - """get_bytes(N:int) : string - Return N bytes of random data. - """ - - s='' - i, pool = self._getPos, self._randpool - h=self._hash.new() - dsize = self._hash.digest_size - num = N - while num > 0: - h.update( self._randpool[i:i+dsize] ) - s = s + h.digest() - num = num - dsize - i = (i + dsize) % self.bytes - if i>1, bits+1 - if bits>8: bits=8 - - self._event1, self._event2 = event, self._event1 - - self._updateEntropyEstimate(bits) - return bits - - # Private functions - def _noise(self): - # Adds a bit of noise to the random pool, by adding in the - # current time and CPU usage of this process. - # The difference from the previous call to _noise() is taken - # in an effort to estimate the entropy. - t=time.time() - delta = (t - self._lastcounter)/self._ticksize*1e6 - self._lastcounter = t - self._addBytes(long_to_bytes(long(1000*time.time()))) - self._addBytes(long_to_bytes(long(1000*time.clock()))) - self._addBytes(long_to_bytes(long(1000*time.time()))) - self._addBytes(long_to_bytes(long(delta))) - - # Reduce delta to a maximum of 8 bits so we don't add too much - # entropy as a result of this call. - delta=delta % 0xff - return int(delta) - - - def _measureTickSize(self): - # _measureTickSize() tries to estimate a rough average of the - # resolution of time that you can see from Python. It does - # this by measuring the time 100 times, computing the delay - # between measurements, and taking the median of the resulting - # list. (We also hash all the times and add them to the pool) - interval = [None] * 100 - h = self._hash.new(`(id(self),id(interval))`) - - # Compute 100 differences - t=time.time() - h.update(`t`) - i = 0 - j = 0 - while i < 100: - t2=time.time() - h.update(`(i,j,t2)`) - j += 1 - delta=int((t2-t)*1e6) - if delta: - interval[i] = delta - i += 1 - t=t2 - - # Take the median of the array of intervals - interval.sort() - self._ticksize=interval[len(interval)/2] - h.update(`(interval,self._ticksize)`) - # mix in the measurement times and wash the random pool - self.stir(h.digest()) - - def _addBytes(self, s): - "XOR the contents of the string S into the random pool" - i, pool = self._addPos, self._randpool - for j in range(0, len(s)): - pool[i]=pool[i] ^ ord(s[j]) - i=(i+1) % self.bytes - self._addPos = i - - # Deprecated method names: remove in PCT 2.1 or later. - def getBytes(self, N): - warnings.warn("getBytes() method replaced by get_bytes()", - DeprecationWarning) - return self.get_bytes(N) - - def addEvent (self, event, s=""): - warnings.warn("addEvent() method replaced by add_event()", - DeprecationWarning) - return self.add_event(s + str(event)) - -class PersistentRandomPool (RandomPool): - def __init__ (self, filename=None, *args, **kwargs): - RandomPool.__init__(self, *args, **kwargs) - self.filename = filename - if filename: - try: - # the time taken to open and read the file might have - # a little disk variability, modulo disk/kernel caching... - f=open(filename, 'rb') - self.add_event() - data = f.read() - self.add_event() - # mix in the data from the file and wash the random pool - self.stir(data) - f.close() - except IOError: - # Oh, well; the file doesn't exist or is unreadable, so - # we'll just ignore it. - pass - - def save(self): - if self.filename == "": - raise ValueError, "No filename set for this object" - # wash the random pool before save, provides some forward secrecy for - # old values of the pool. - self.stir_n() - f=open(self.filename, 'wb') - self.add_event() - f.write(self._randpool.tostring()) - f.close() - self.add_event() - # wash the pool again, provide some protection for future values - self.stir() - -# non-echoing Windows keyboard entry -_kb = 0 -if not _kb: - try: - import msvcrt - class KeyboardEntry: - def getch(self): - c = msvcrt.getch() - if c in ('\000', '\xe0'): - # function key - c += msvcrt.getch() - return c - def close(self, delay = 0): - if delay: - time.sleep(delay) - while msvcrt.kbhit(): - msvcrt.getch() - _kb = 1 - except: - pass - -# non-echoing Posix keyboard entry -if not _kb: - try: - import termios - class KeyboardEntry: - def __init__(self, fd = 0): - self._fd = fd - self._old = termios.tcgetattr(fd) - new = termios.tcgetattr(fd) - new[3]=new[3] & ~termios.ICANON & ~termios.ECHO - termios.tcsetattr(fd, termios.TCSANOW, new) - def getch(self): - termios.tcflush(0, termios.TCIFLUSH) # XXX Leave this in? - return os.read(self._fd, 1) - def close(self, delay = 0): - if delay: - time.sleep(delay) - termios.tcflush(self._fd, termios.TCIFLUSH) - termios.tcsetattr(self._fd, termios.TCSAFLUSH, self._old) - _kb = 1 - except: - pass - -class KeyboardRandomPool (PersistentRandomPool): - def __init__(self, *args, **kwargs): - PersistentRandomPool.__init__(self, *args, **kwargs) - - def randomize(self, N = 0): - "Adds N bits of entropy to random pool. If N is 0, fill up pool." - import os, string, time - if N <= 0: - bits = self.bits - self.entropy - else: - bits = N*8 - if bits == 0: - return - print bits,'bits of entropy are now required. Please type on the keyboard' - print 'until enough randomness has been accumulated.' - kb = KeyboardEntry() - s='' # We'll save the characters typed and add them to the pool. - hash = self._hash - e = 0 - try: - while e < bits: - temp=str(bits-e).rjust(6) - os.write(1, temp) - s=s+kb.getch() - e += self.add_event(s) - os.write(1, 6*chr(8)) - self.add_event(s+hash.new(s).digest() ) - finally: - kb.close() - print '\n\007 Enough. Please wait a moment.\n' - self.stir_n() # wash the random pool. - kb.close(4) - -if __name__ == '__main__': - pool = RandomPool() - print 'random pool entropy', pool.entropy, 'bits' - pool.add_event('something') - print `pool.get_bytes(100)` - import tempfile, os - fname = tempfile.mktemp() - pool = KeyboardRandomPool(filename=fname) - print 'keyboard random pool entropy', pool.entropy, 'bits' - pool.randomize() - print 'keyboard random pool entropy', pool.entropy, 'bits' - pool.randomize(128) - pool.save() - saved = open(fname, 'rb').read() - print 'saved', `saved` - print 'pool ', `pool._randpool.tostring()` - newpool = PersistentRandomPool(fname) - print 'persistent random pool entropy', pool.entropy, 'bits' - os.remove(fname) diff --git a/gdata/Crypto/Util/test.py b/gdata/Crypto/Util/test.py deleted file mode 100644 index 7b23e9f5e4..0000000000 --- a/gdata/Crypto/Util/test.py +++ /dev/null @@ -1,453 +0,0 @@ -# -# test.py : Functions used for testing the modules -# -# Part of the Python Cryptography Toolkit -# -# Distribute and use freely; there are no restrictions on further -# dissemination and usage except those imposed by the laws of your -# country of residence. This software is provided "as is" without -# warranty of fitness for use or suitability for any purpose, express -# or implied. Use at your own risk or not at all. -# - -__revision__ = "$Id: test.py,v 1.16 2004/08/13 22:24:18 akuchling Exp $" - -import binascii -import string -import testdata - -from Crypto.Cipher import * - -def die(string): - import sys - print '***ERROR: ', string -# sys.exit(0) # Will default to continuing onward... - -def print_timing (size, delta, verbose): - if verbose: - if delta == 0: - print 'Unable to measure time -- elapsed time too small' - else: - print '%.2f K/sec' % (size/delta) - -def exerciseBlockCipher(cipher, verbose): - import string, time - try: - ciph = eval(cipher) - except NameError: - print cipher, 'module not available' - return None - print cipher+ ':' - str='1' # Build 128K of test data - for i in xrange(0, 17): - str=str+str - if ciph.key_size==0: ciph.key_size=16 - password = 'password12345678Extra text for password'[0:ciph.key_size] - IV = 'Test IV Test IV Test IV Test'[0:ciph.block_size] - - if verbose: print ' ECB mode:', - obj=ciph.new(password, ciph.MODE_ECB) - if obj.block_size != ciph.block_size: - die("Module and cipher object block_size don't match") - - text='1234567812345678'[0:ciph.block_size] - c=obj.encrypt(text) - if (obj.decrypt(c)!=text): die('Error encrypting "'+text+'"') - text='KuchlingKuchling'[0:ciph.block_size] - c=obj.encrypt(text) - if (obj.decrypt(c)!=text): die('Error encrypting "'+text+'"') - text='NotTodayNotEver!'[0:ciph.block_size] - c=obj.encrypt(text) - if (obj.decrypt(c)!=text): die('Error encrypting "'+text+'"') - - start=time.time() - s=obj.encrypt(str) - s2=obj.decrypt(s) - end=time.time() - if (str!=s2): - die('Error in resulting plaintext from ECB mode') - print_timing(256, end-start, verbose) - del obj - - if verbose: print ' CFB mode:', - obj1=ciph.new(password, ciph.MODE_CFB, IV) - obj2=ciph.new(password, ciph.MODE_CFB, IV) - start=time.time() - ciphertext=obj1.encrypt(str[0:65536]) - plaintext=obj2.decrypt(ciphertext) - end=time.time() - if (plaintext!=str[0:65536]): - die('Error in resulting plaintext from CFB mode') - print_timing(64, end-start, verbose) - del obj1, obj2 - - if verbose: print ' CBC mode:', - obj1=ciph.new(password, ciph.MODE_CBC, IV) - obj2=ciph.new(password, ciph.MODE_CBC, IV) - start=time.time() - ciphertext=obj1.encrypt(str) - plaintext=obj2.decrypt(ciphertext) - end=time.time() - if (plaintext!=str): - die('Error in resulting plaintext from CBC mode') - print_timing(256, end-start, verbose) - del obj1, obj2 - - if verbose: print ' PGP mode:', - obj1=ciph.new(password, ciph.MODE_PGP, IV) - obj2=ciph.new(password, ciph.MODE_PGP, IV) - start=time.time() - ciphertext=obj1.encrypt(str) - plaintext=obj2.decrypt(ciphertext) - end=time.time() - if (plaintext!=str): - die('Error in resulting plaintext from PGP mode') - print_timing(256, end-start, verbose) - del obj1, obj2 - - if verbose: print ' OFB mode:', - obj1=ciph.new(password, ciph.MODE_OFB, IV) - obj2=ciph.new(password, ciph.MODE_OFB, IV) - start=time.time() - ciphertext=obj1.encrypt(str) - plaintext=obj2.decrypt(ciphertext) - end=time.time() - if (plaintext!=str): - die('Error in resulting plaintext from OFB mode') - print_timing(256, end-start, verbose) - del obj1, obj2 - - def counter(length=ciph.block_size): - return length * 'a' - - if verbose: print ' CTR mode:', - obj1=ciph.new(password, ciph.MODE_CTR, counter=counter) - obj2=ciph.new(password, ciph.MODE_CTR, counter=counter) - start=time.time() - ciphertext=obj1.encrypt(str) - plaintext=obj2.decrypt(ciphertext) - end=time.time() - if (plaintext!=str): - die('Error in resulting plaintext from CTR mode') - print_timing(256, end-start, verbose) - del obj1, obj2 - - # Test the IV handling - if verbose: print ' Testing IV handling' - obj1=ciph.new(password, ciph.MODE_CBC, IV) - plaintext='Test'*(ciph.block_size/4)*3 - ciphertext1=obj1.encrypt(plaintext) - obj1.IV=IV - ciphertext2=obj1.encrypt(plaintext) - if ciphertext1!=ciphertext2: - die('Error in setting IV') - - # Test keyword arguments - obj1=ciph.new(key=password) - obj1=ciph.new(password, mode=ciph.MODE_CBC) - obj1=ciph.new(mode=ciph.MODE_CBC, key=password) - obj1=ciph.new(IV=IV, mode=ciph.MODE_CBC, key=password) - - return ciph - -def exerciseStreamCipher(cipher, verbose): - import string, time - try: - ciph = eval(cipher) - except (NameError): - print cipher, 'module not available' - return None - print cipher + ':', - str='1' # Build 128K of test data - for i in xrange(0, 17): - str=str+str - key_size = ciph.key_size or 16 - password = 'password12345678Extra text for password'[0:key_size] - - obj1=ciph.new(password) - obj2=ciph.new(password) - if obj1.block_size != ciph.block_size: - die("Module and cipher object block_size don't match") - if obj1.key_size != ciph.key_size: - die("Module and cipher object key_size don't match") - - text='1234567812345678Python' - c=obj1.encrypt(text) - if (obj2.decrypt(c)!=text): die('Error encrypting "'+text+'"') - text='B1FF I2 A R3A11Y |<00L D00D!!!!!' - c=obj1.encrypt(text) - if (obj2.decrypt(c)!=text): die('Error encrypting "'+text+'"') - text='SpamSpamSpamSpamSpamSpamSpamSpamSpam' - c=obj1.encrypt(text) - if (obj2.decrypt(c)!=text): die('Error encrypting "'+text+'"') - - start=time.time() - s=obj1.encrypt(str) - str=obj2.decrypt(s) - end=time.time() - print_timing(256, end-start, verbose) - del obj1, obj2 - - return ciph - -def TestStreamModules(args=['arc4', 'XOR'], verbose=1): - import sys, string - args=map(string.lower, args) - - if 'arc4' in args: - # Test ARC4 stream cipher - arc4=exerciseStreamCipher('ARC4', verbose) - if (arc4!=None): - for entry in testdata.arc4: - key,plain,cipher=entry - key=binascii.a2b_hex(key) - plain=binascii.a2b_hex(plain) - cipher=binascii.a2b_hex(cipher) - obj=arc4.new(key) - ciphertext=obj.encrypt(plain) - if (ciphertext!=cipher): - die('ARC4 failed on entry '+`entry`) - - if 'xor' in args: - # Test XOR stream cipher - XOR=exerciseStreamCipher('XOR', verbose) - if (XOR!=None): - for entry in testdata.xor: - key,plain,cipher=entry - key=binascii.a2b_hex(key) - plain=binascii.a2b_hex(plain) - cipher=binascii.a2b_hex(cipher) - obj=XOR.new(key) - ciphertext=obj.encrypt(plain) - if (ciphertext!=cipher): - die('XOR failed on entry '+`entry`) - - -def TestBlockModules(args=['aes', 'arc2', 'des', 'blowfish', 'cast', 'des3', - 'idea', 'rc5'], - verbose=1): - import string - args=map(string.lower, args) - if 'aes' in args: - ciph=exerciseBlockCipher('AES', verbose) # AES - if (ciph!=None): - if verbose: print ' Verifying against test suite...' - for entry in testdata.aes: - key,plain,cipher=entry - key=binascii.a2b_hex(key) - plain=binascii.a2b_hex(plain) - cipher=binascii.a2b_hex(cipher) - obj=ciph.new(key, ciph.MODE_ECB) - ciphertext=obj.encrypt(plain) - if (ciphertext!=cipher): - die('AES failed on entry '+`entry`) - for i in ciphertext: - if verbose: print hex(ord(i)), - if verbose: print - - for entry in testdata.aes_modes: - mode, key, plain, cipher, kw = entry - key=binascii.a2b_hex(key) - plain=binascii.a2b_hex(plain) - cipher=binascii.a2b_hex(cipher) - obj=ciph.new(key, mode, **kw) - obj2=ciph.new(key, mode, **kw) - ciphertext=obj.encrypt(plain) - if (ciphertext!=cipher): - die('AES encrypt failed on entry '+`entry`) - for i in ciphertext: - if verbose: print hex(ord(i)), - if verbose: print - - plain2=obj2.decrypt(ciphertext) - if plain2!=plain: - die('AES decrypt failed on entry '+`entry`) - for i in plain2: - if verbose: print hex(ord(i)), - if verbose: print - - - if 'arc2' in args: - ciph=exerciseBlockCipher('ARC2', verbose) # Alleged RC2 - if (ciph!=None): - if verbose: print ' Verifying against test suite...' - for entry in testdata.arc2: - key,plain,cipher=entry - key=binascii.a2b_hex(key) - plain=binascii.a2b_hex(plain) - cipher=binascii.a2b_hex(cipher) - obj=ciph.new(key, ciph.MODE_ECB) - ciphertext=obj.encrypt(plain) - if (ciphertext!=cipher): - die('ARC2 failed on entry '+`entry`) - for i in ciphertext: - if verbose: print hex(ord(i)), - print - - if 'blowfish' in args: - ciph=exerciseBlockCipher('Blowfish',verbose)# Bruce Schneier's Blowfish cipher - if (ciph!=None): - if verbose: print ' Verifying against test suite...' - for entry in testdata.blowfish: - key,plain,cipher=entry - key=binascii.a2b_hex(key) - plain=binascii.a2b_hex(plain) - cipher=binascii.a2b_hex(cipher) - obj=ciph.new(key, ciph.MODE_ECB) - ciphertext=obj.encrypt(plain) - if (ciphertext!=cipher): - die('Blowfish failed on entry '+`entry`) - for i in ciphertext: - if verbose: print hex(ord(i)), - if verbose: print - - if 'cast' in args: - ciph=exerciseBlockCipher('CAST', verbose) # CAST-128 - if (ciph!=None): - if verbose: print ' Verifying against test suite...' - for entry in testdata.cast: - key,plain,cipher=entry - key=binascii.a2b_hex(key) - plain=binascii.a2b_hex(plain) - cipher=binascii.a2b_hex(cipher) - obj=ciph.new(key, ciph.MODE_ECB) - ciphertext=obj.encrypt(plain) - if (ciphertext!=cipher): - die('CAST failed on entry '+`entry`) - for i in ciphertext: - if verbose: print hex(ord(i)), - if verbose: print - - if 0: - # The full-maintenance test; it requires 4 million encryptions, - # and correspondingly is quite time-consuming. I've disabled - # it; it's faster to compile block/cast.c with -DTEST and run - # the resulting program. - a = b = '\x01\x23\x45\x67\x12\x34\x56\x78\x23\x45\x67\x89\x34\x56\x78\x9A' - - for i in range(0, 1000000): - obj = cast.new(b, cast.MODE_ECB) - a = obj.encrypt(a[:8]) + obj.encrypt(a[-8:]) - obj = cast.new(a, cast.MODE_ECB) - b = obj.encrypt(b[:8]) + obj.encrypt(b[-8:]) - - if a!="\xEE\xA9\xD0\xA2\x49\xFD\x3B\xA6\xB3\x43\x6F\xB8\x9D\x6D\xCA\x92": - if verbose: print 'CAST test failed: value of "a" doesn\'t match' - if b!="\xB2\xC9\x5E\xB0\x0C\x31\xAD\x71\x80\xAC\x05\xB8\xE8\x3D\x69\x6E": - if verbose: print 'CAST test failed: value of "b" doesn\'t match' - - if 'des' in args: - # Test/benchmark DES block cipher - des=exerciseBlockCipher('DES', verbose) - if (des!=None): - # Various tests taken from the DES library packaged with Kerberos V4 - obj=des.new(binascii.a2b_hex('0123456789abcdef'), des.MODE_ECB) - s=obj.encrypt('Now is t') - if (s!=binascii.a2b_hex('3fa40e8a984d4815')): - die('DES fails test 1') - obj=des.new(binascii.a2b_hex('08192a3b4c5d6e7f'), des.MODE_ECB) - s=obj.encrypt('\000\000\000\000\000\000\000\000') - if (s!=binascii.a2b_hex('25ddac3e96176467')): - die('DES fails test 2') - obj=des.new(binascii.a2b_hex('0123456789abcdef'), des.MODE_CBC, - binascii.a2b_hex('1234567890abcdef')) - s=obj.encrypt("Now is the time for all ") - if (s!=binascii.a2b_hex('e5c7cdde872bf27c43e934008c389c0f683788499a7c05f6')): - die('DES fails test 3') - obj=des.new(binascii.a2b_hex('0123456789abcdef'), des.MODE_CBC, - binascii.a2b_hex('fedcba9876543210')) - s=obj.encrypt("7654321 Now is the time for \000\000\000\000") - if (s!=binascii.a2b_hex("ccd173ffab2039f4acd8aefddfd8a1eb468e91157888ba681d269397f7fe62b4")): - die('DES fails test 4') - del obj,s - - # R. Rivest's test: see http://theory.lcs.mit.edu/~rivest/destest.txt - x=binascii.a2b_hex('9474B8E8C73BCA7D') - for i in range(0, 16): - obj=des.new(x, des.MODE_ECB) - if (i & 1): x=obj.decrypt(x) - else: x=obj.encrypt(x) - if x!=binascii.a2b_hex('1B1A2DDB4C642438'): - die("DES fails Rivest's test") - - if verbose: print ' Verifying against test suite...' - for entry in testdata.des: - key,plain,cipher=entry - key=binascii.a2b_hex(key) - plain=binascii.a2b_hex(plain) - cipher=binascii.a2b_hex(cipher) - obj=des.new(key, des.MODE_ECB) - ciphertext=obj.encrypt(plain) - if (ciphertext!=cipher): - die('DES failed on entry '+`entry`) - for entry in testdata.des_cbc: - key, iv, plain, cipher=entry - key, iv, cipher=binascii.a2b_hex(key),binascii.a2b_hex(iv),binascii.a2b_hex(cipher) - obj1=des.new(key, des.MODE_CBC, iv) - obj2=des.new(key, des.MODE_CBC, iv) - ciphertext=obj1.encrypt(plain) - if (ciphertext!=cipher): - die('DES CBC mode failed on entry '+`entry`) - - if 'des3' in args: - ciph=exerciseBlockCipher('DES3', verbose) # Triple DES - if (ciph!=None): - if verbose: print ' Verifying against test suite...' - for entry in testdata.des3: - key,plain,cipher=entry - key=binascii.a2b_hex(key) - plain=binascii.a2b_hex(plain) - cipher=binascii.a2b_hex(cipher) - obj=ciph.new(key, ciph.MODE_ECB) - ciphertext=obj.encrypt(plain) - if (ciphertext!=cipher): - die('DES3 failed on entry '+`entry`) - for i in ciphertext: - if verbose: print hex(ord(i)), - if verbose: print - for entry in testdata.des3_cbc: - key, iv, plain, cipher=entry - key, iv, cipher=binascii.a2b_hex(key),binascii.a2b_hex(iv),binascii.a2b_hex(cipher) - obj1=ciph.new(key, ciph.MODE_CBC, iv) - obj2=ciph.new(key, ciph.MODE_CBC, iv) - ciphertext=obj1.encrypt(plain) - if (ciphertext!=cipher): - die('DES3 CBC mode failed on entry '+`entry`) - - if 'idea' in args: - ciph=exerciseBlockCipher('IDEA', verbose) # IDEA block cipher - if (ciph!=None): - if verbose: print ' Verifying against test suite...' - for entry in testdata.idea: - key,plain,cipher=entry - key=binascii.a2b_hex(key) - plain=binascii.a2b_hex(plain) - cipher=binascii.a2b_hex(cipher) - obj=ciph.new(key, ciph.MODE_ECB) - ciphertext=obj.encrypt(plain) - if (ciphertext!=cipher): - die('IDEA failed on entry '+`entry`) - - if 'rc5' in args: - # Ronald Rivest's RC5 algorithm - ciph=exerciseBlockCipher('RC5', verbose) - if (ciph!=None): - if verbose: print ' Verifying against test suite...' - for entry in testdata.rc5: - key,plain,cipher=entry - key=binascii.a2b_hex(key) - plain=binascii.a2b_hex(plain) - cipher=binascii.a2b_hex(cipher) - obj=ciph.new(key[4:], ciph.MODE_ECB, - version =ord(key[0]), - word_size=ord(key[1]), - rounds =ord(key[2]) ) - ciphertext=obj.encrypt(plain) - if (ciphertext!=cipher): - die('RC5 failed on entry '+`entry`) - for i in ciphertext: - if verbose: print hex(ord(i)), - if verbose: print - - - diff --git a/gdata/Crypto/__init__.py b/gdata/Crypto/__init__.py deleted file mode 100644 index 2324ae8c37..0000000000 --- a/gdata/Crypto/__init__.py +++ /dev/null @@ -1,25 +0,0 @@ - -"""Python Cryptography Toolkit - -A collection of cryptographic modules implementing various algorithms -and protocols. - -Subpackages: -Crypto.Cipher Secret-key encryption algorithms (AES, DES, ARC4) -Crypto.Hash Hashing algorithms (MD5, SHA, HMAC) -Crypto.Protocol Cryptographic protocols (Chaffing, all-or-nothing - transform). This package does not contain any - network protocols. -Crypto.PublicKey Public-key encryption and signature algorithms - (RSA, DSA) -Crypto.Util Various useful modules and functions (long-to-string - conversion, random number generation, number - theoretic functions) -""" - -__all__ = ['Cipher', 'Hash', 'Protocol', 'PublicKey', 'Util'] - -__version__ = '2.0.1' -__revision__ = "$Id: __init__.py,v 1.12 2005/06/14 01:20:22 akuchling Exp $" - - diff --git a/gdata/Crypto/test.py b/gdata/Crypto/test.py deleted file mode 100644 index c5ed061621..0000000000 --- a/gdata/Crypto/test.py +++ /dev/null @@ -1,38 +0,0 @@ -# -# Test script for the Python Cryptography Toolkit. -# - -__revision__ = "$Id: test.py,v 1.7 2002/07/11 14:31:19 akuchling Exp $" - -import os, sys - - -# Add the build directory to the front of sys.path -from distutils.util import get_platform -s = "build/lib.%s-%.3s" % (get_platform(), sys.version) -s = os.path.join(os.getcwd(), s) -sys.path.insert(0, s) -s = os.path.join(os.getcwd(), 'test') -sys.path.insert(0, s) - -from Crypto.Util import test - -args = sys.argv[1:] -quiet = "--quiet" in args -if quiet: args.remove('--quiet') - -if not quiet: - print '\nStream Ciphers:' - print '===============' - -if args: test.TestStreamModules(args, verbose= not quiet) -else: test.TestStreamModules(verbose= not quiet) - -if not quiet: - print '\nBlock Ciphers:' - print '==============' - -if args: test.TestBlockModules(args, verbose= not quiet) -else: test.TestBlockModules(verbose= not quiet) - - diff --git a/gdata/acl/__init__.py b/gdata/acl/__init__.py deleted file mode 100644 index 22071f7a11..0000000000 --- a/gdata/acl/__init__.py +++ /dev/null @@ -1,15 +0,0 @@ -#!/usr/bin/python -# -# Copyright (C) 2009 Google 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. diff --git a/gdata/acl/data.py b/gdata/acl/data.py deleted file mode 100644 index c65359cdf8..0000000000 --- a/gdata/acl/data.py +++ /dev/null @@ -1,55 +0,0 @@ -#!/usr/bin/python -# -# Copyright (C) 2009 Google 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. - -"""Contains the data classes of the Google Access Control List (ACL) Extension""" - - -__author__ = 'j.s@google.com (Jeff Scudder)' - - -import atom.core -import atom.data -import gdata.data -import gdata.opensearch.data - - -GACL_TEMPLATE = '{http://schemas.google.com/acl/2007}%s' - - -class AclRole(atom.core.XmlElement): - """Describes the role of an entry in an access control list.""" - _qname = GACL_TEMPLATE % 'role' - value = 'value' - - -class AclScope(atom.core.XmlElement): - """Describes the scope of an entry in an access control list.""" - _qname = GACL_TEMPLATE % 'scope' - type = 'type' - value = 'value' - - -class AclEntry(gdata.data.GDEntry): - """Describes an entry in a feed of an access control list (ACL).""" - scope = AclScope - role = AclRole - - -class AclFeed(gdata.data.GDFeed): - """Describes a feed of an access control list (ACL).""" - entry = [AclEntry] - - diff --git a/gdata/analytics/Crypto/Cipher/__init__.py b/gdata/analytics/Crypto/Cipher/__init__.py deleted file mode 100644 index 3b2f855d6a..0000000000 --- a/gdata/analytics/Crypto/Cipher/__init__.py +++ /dev/null @@ -1,33 +0,0 @@ -"""Secret-key encryption algorithms. - -Secret-key encryption algorithms transform plaintext in some way that -is dependent on a key, producing ciphertext. This transformation can -easily be reversed, if (and, hopefully, only if) one knows the key. - -The encryption modules here all support the interface described in PEP -272, "API for Block Encryption Algorithms". - -If you don't know which algorithm to choose, use AES because it's -standard and has undergone a fair bit of examination. - -Crypto.Cipher.AES Advanced Encryption Standard -Crypto.Cipher.ARC2 Alleged RC2 -Crypto.Cipher.ARC4 Alleged RC4 -Crypto.Cipher.Blowfish -Crypto.Cipher.CAST -Crypto.Cipher.DES The Data Encryption Standard. Very commonly used - in the past, but today its 56-bit keys are too small. -Crypto.Cipher.DES3 Triple DES. -Crypto.Cipher.IDEA -Crypto.Cipher.RC5 -Crypto.Cipher.XOR The simple XOR cipher. -""" - -__all__ = ['AES', 'ARC2', 'ARC4', - 'Blowfish', 'CAST', 'DES', 'DES3', 'IDEA', 'RC5', - 'XOR' - ] - -__revision__ = "$Id: __init__.py,v 1.7 2003/02/28 15:28:35 akuchling Exp $" - - diff --git a/gdata/analytics/Crypto/Hash/HMAC.py b/gdata/analytics/Crypto/Hash/HMAC.py deleted file mode 100644 index eeb5782319..0000000000 --- a/gdata/analytics/Crypto/Hash/HMAC.py +++ /dev/null @@ -1,108 +0,0 @@ -"""HMAC (Keyed-Hashing for Message Authentication) Python module. - -Implements the HMAC algorithm as described by RFC 2104. - -This is just a copy of the Python 2.2 HMAC module, modified to work when -used on versions of Python before 2.2. -""" - -__revision__ = "$Id: HMAC.py,v 1.5 2002/07/25 17:19:02 z3p Exp $" - -import string - -def _strxor(s1, s2): - """Utility method. XOR the two strings s1 and s2 (must have same length). - """ - return "".join(map(lambda x, y: chr(ord(x) ^ ord(y)), s1, s2)) - -# The size of the digests returned by HMAC depends on the underlying -# hashing module used. -digest_size = None - -class HMAC: - """RFC2104 HMAC class. - - This supports the API for Cryptographic Hash Functions (PEP 247). - """ - - def __init__(self, key, msg = None, digestmod = None): - """Create a new HMAC object. - - key: key for the keyed hash object. - msg: Initial input for the hash, if provided. - digestmod: A module supporting PEP 247. Defaults to the md5 module. - """ - if digestmod == None: - import md5 - digestmod = md5 - - self.digestmod = digestmod - self.outer = digestmod.new() - self.inner = digestmod.new() - try: - self.digest_size = digestmod.digest_size - except AttributeError: - self.digest_size = len(self.outer.digest()) - - blocksize = 64 - ipad = "\x36" * blocksize - opad = "\x5C" * blocksize - - if len(key) > blocksize: - key = digestmod.new(key).digest() - - key = key + chr(0) * (blocksize - len(key)) - self.outer.update(_strxor(key, opad)) - self.inner.update(_strxor(key, ipad)) - if (msg): - self.update(msg) - -## def clear(self): -## raise NotImplementedError, "clear() method not available in HMAC." - - def update(self, msg): - """Update this hashing object with the string msg. - """ - self.inner.update(msg) - - def copy(self): - """Return a separate copy of this hashing object. - - An update to this copy won't affect the original object. - """ - other = HMAC("") - other.digestmod = self.digestmod - other.inner = self.inner.copy() - other.outer = self.outer.copy() - return other - - def digest(self): - """Return the hash value of this hashing object. - - This returns a string containing 8-bit data. The object is - not altered in any way by this function; you can continue - updating the object after calling this function. - """ - h = self.outer.copy() - h.update(self.inner.digest()) - return h.digest() - - def hexdigest(self): - """Like digest(), but returns a string of hexadecimal digits instead. - """ - return "".join([string.zfill(hex(ord(x))[2:], 2) - for x in tuple(self.digest())]) - -def new(key, msg = None, digestmod = None): - """Create a new hashing object and return it. - - key: The starting key for the hash. - msg: if available, will immediately be hashed into the object's starting - state. - - You can now feed arbitrary strings into the object using its update() - method, and can ask for the hash value at any time by calling its digest() - method. - """ - return HMAC(key, msg, digestmod) - diff --git a/gdata/analytics/Crypto/Hash/MD5.py b/gdata/analytics/Crypto/Hash/MD5.py deleted file mode 100644 index b0eba3947a..0000000000 --- a/gdata/analytics/Crypto/Hash/MD5.py +++ /dev/null @@ -1,13 +0,0 @@ - -# Just use the MD5 module from the Python standard library - -__revision__ = "$Id: MD5.py,v 1.4 2002/07/11 14:31:19 akuchling Exp $" - -from md5 import * - -import md5 -if hasattr(md5, 'digestsize'): - digest_size = digestsize - del digestsize -del md5 - diff --git a/gdata/analytics/Crypto/Hash/SHA.py b/gdata/analytics/Crypto/Hash/SHA.py deleted file mode 100644 index ea3c6a34f7..0000000000 --- a/gdata/analytics/Crypto/Hash/SHA.py +++ /dev/null @@ -1,11 +0,0 @@ - -# Just use the SHA module from the Python standard library - -__revision__ = "$Id: SHA.py,v 1.4 2002/07/11 14:31:19 akuchling Exp $" - -from sha import * -import sha -if hasattr(sha, 'digestsize'): - digest_size = digestsize - del digestsize -del sha diff --git a/gdata/analytics/Crypto/Hash/__init__.py b/gdata/analytics/Crypto/Hash/__init__.py deleted file mode 100644 index 920fe746e7..0000000000 --- a/gdata/analytics/Crypto/Hash/__init__.py +++ /dev/null @@ -1,24 +0,0 @@ -"""Hashing algorithms - -Hash functions take arbitrary strings as input, and produce an output -of fixed size that is dependent on the input; it should never be -possible to derive the input data given only the hash function's -output. Hash functions can be used simply as a checksum, or, in -association with a public-key algorithm, can be used to implement -digital signatures. - -The hashing modules here all support the interface described in PEP -247, "API for Cryptographic Hash Functions". - -Submodules: -Crypto.Hash.HMAC RFC 2104: Keyed-Hashing for Message Authentication -Crypto.Hash.MD2 -Crypto.Hash.MD4 -Crypto.Hash.MD5 -Crypto.Hash.RIPEMD -Crypto.Hash.SHA -""" - -__all__ = ['HMAC', 'MD2', 'MD4', 'MD5', 'RIPEMD', 'SHA', 'SHA256'] -__revision__ = "$Id: __init__.py,v 1.6 2003/12/19 14:24:25 akuchling Exp $" - diff --git a/gdata/analytics/Crypto/Protocol/AllOrNothing.py b/gdata/analytics/Crypto/Protocol/AllOrNothing.py deleted file mode 100644 index 6f3505d2f4..0000000000 --- a/gdata/analytics/Crypto/Protocol/AllOrNothing.py +++ /dev/null @@ -1,295 +0,0 @@ -"""This file implements all-or-nothing package transformations. - -An all-or-nothing package transformation is one in which some text is -transformed into message blocks, such that all blocks must be obtained before -the reverse transformation can be applied. Thus, if any blocks are corrupted -or lost, the original message cannot be reproduced. - -An all-or-nothing package transformation is not encryption, although a block -cipher algorithm is used. The encryption key is randomly generated and is -extractable from the message blocks. - -This class implements the All-Or-Nothing package transformation algorithm -described in: - -Ronald L. Rivest. "All-Or-Nothing Encryption and The Package Transform" -http://theory.lcs.mit.edu/~rivest/fusion.pdf - -""" - -__revision__ = "$Id: AllOrNothing.py,v 1.8 2003/02/28 15:23:20 akuchling Exp $" - -import operator -import string -from Crypto.Util.number import bytes_to_long, long_to_bytes - - - -class AllOrNothing: - """Class implementing the All-or-Nothing package transform. - - Methods for subclassing: - - _inventkey(key_size): - Returns a randomly generated key. Subclasses can use this to - implement better random key generating algorithms. The default - algorithm is probably not very cryptographically secure. - - """ - - def __init__(self, ciphermodule, mode=None, IV=None): - """AllOrNothing(ciphermodule, mode=None, IV=None) - - ciphermodule is a module implementing the cipher algorithm to - use. It must provide the PEP272 interface. - - Note that the encryption key is randomly generated - automatically when needed. Optional arguments mode and IV are - passed directly through to the ciphermodule.new() method; they - are the feedback mode and initialization vector to use. All - three arguments must be the same for the object used to create - the digest, and to undigest'ify the message blocks. - """ - - self.__ciphermodule = ciphermodule - self.__mode = mode - self.__IV = IV - self.__key_size = ciphermodule.key_size - if self.__key_size == 0: - self.__key_size = 16 - - __K0digit = chr(0x69) - - def digest(self, text): - """digest(text:string) : [string] - - Perform the All-or-Nothing package transform on the given - string. Output is a list of message blocks describing the - transformed text, where each block is a string of bit length equal - to the ciphermodule's block_size. - """ - - # generate a random session key and K0, the key used to encrypt the - # hash blocks. Rivest calls this a fixed, publically-known encryption - # key, but says nothing about the security implications of this key or - # how to choose it. - key = self._inventkey(self.__key_size) - K0 = self.__K0digit * self.__key_size - - # we need two cipher objects here, one that is used to encrypt the - # message blocks and one that is used to encrypt the hashes. The - # former uses the randomly generated key, while the latter uses the - # well-known key. - mcipher = self.__newcipher(key) - hcipher = self.__newcipher(K0) - - # Pad the text so that its length is a multiple of the cipher's - # block_size. Pad with trailing spaces, which will be eliminated in - # the undigest() step. - block_size = self.__ciphermodule.block_size - padbytes = block_size - (len(text) % block_size) - text = text + ' ' * padbytes - - # Run through the algorithm: - # s: number of message blocks (size of text / block_size) - # input sequence: m1, m2, ... ms - # random key K' (`key' in the code) - # Compute output sequence: m'1, m'2, ... m's' for s' = s + 1 - # Let m'i = mi ^ E(K', i) for i = 1, 2, 3, ..., s - # Let m's' = K' ^ h1 ^ h2 ^ ... hs - # where hi = E(K0, m'i ^ i) for i = 1, 2, ... s - # - # The one complication I add is that the last message block is hard - # coded to the number of padbytes added, so that these can be stripped - # during the undigest() step - s = len(text) / block_size - blocks = [] - hashes = [] - for i in range(1, s+1): - start = (i-1) * block_size - end = start + block_size - mi = text[start:end] - assert len(mi) == block_size - cipherblock = mcipher.encrypt(long_to_bytes(i, block_size)) - mticki = bytes_to_long(mi) ^ bytes_to_long(cipherblock) - blocks.append(mticki) - # calculate the hash block for this block - hi = hcipher.encrypt(long_to_bytes(mticki ^ i, block_size)) - hashes.append(bytes_to_long(hi)) - - # Add the padbytes length as a message block - i = i + 1 - cipherblock = mcipher.encrypt(long_to_bytes(i, block_size)) - mticki = padbytes ^ bytes_to_long(cipherblock) - blocks.append(mticki) - - # calculate this block's hash - hi = hcipher.encrypt(long_to_bytes(mticki ^ i, block_size)) - hashes.append(bytes_to_long(hi)) - - # Now calculate the last message block of the sequence 1..s'. This - # will contain the random session key XOR'd with all the hash blocks, - # so that for undigest(), once all the hash blocks are calculated, the - # session key can be trivially extracted. Calculating all the hash - # blocks requires that all the message blocks be received, thus the - # All-or-Nothing algorithm succeeds. - mtick_stick = bytes_to_long(key) ^ reduce(operator.xor, hashes) - blocks.append(mtick_stick) - - # we convert the blocks to strings since in Python, byte sequences are - # always represented as strings. This is more consistent with the - # model that encryption and hash algorithms always operate on strings. - return map(long_to_bytes, blocks) - - - def undigest(self, blocks): - """undigest(blocks : [string]) : string - - Perform the reverse package transformation on a list of message - blocks. Note that the ciphermodule used for both transformations - must be the same. blocks is a list of strings of bit length - equal to the ciphermodule's block_size. - """ - - # better have at least 2 blocks, for the padbytes package and the hash - # block accumulator - if len(blocks) < 2: - raise ValueError, "List must be at least length 2." - - # blocks is a list of strings. We need to deal with them as long - # integers - blocks = map(bytes_to_long, blocks) - - # Calculate the well-known key, to which the hash blocks are - # encrypted, and create the hash cipher. - K0 = self.__K0digit * self.__key_size - hcipher = self.__newcipher(K0) - - # Since we have all the blocks (or this method would have been called - # prematurely), we can calcualte all the hash blocks. - hashes = [] - for i in range(1, len(blocks)): - mticki = blocks[i-1] ^ i - hi = hcipher.encrypt(long_to_bytes(mticki)) - hashes.append(bytes_to_long(hi)) - - # now we can calculate K' (key). remember the last block contains - # m's' which we don't include here - key = blocks[-1] ^ reduce(operator.xor, hashes) - - # and now we can create the cipher object - mcipher = self.__newcipher(long_to_bytes(key)) - block_size = self.__ciphermodule.block_size - - # And we can now decode the original message blocks - parts = [] - for i in range(1, len(blocks)): - cipherblock = mcipher.encrypt(long_to_bytes(i, block_size)) - mi = blocks[i-1] ^ bytes_to_long(cipherblock) - parts.append(mi) - - # The last message block contains the number of pad bytes appended to - # the original text string, such that its length was an even multiple - # of the cipher's block_size. This number should be small enough that - # the conversion from long integer to integer should never overflow - padbytes = int(parts[-1]) - text = string.join(map(long_to_bytes, parts[:-1]), '') - return text[:-padbytes] - - def _inventkey(self, key_size): - # TBD: Not a very secure algorithm. Eventually, I'd like to use JHy's - # kernelrand module - import time - from Crypto.Util import randpool - # TBD: key_size * 2 to work around possible bug in RandomPool? - pool = randpool.RandomPool(key_size * 2) - while key_size > pool.entropy: - pool.add_event() - - # we now have enough entropy in the pool to get a key_size'd key - return pool.get_bytes(key_size) - - def __newcipher(self, key): - if self.__mode is None and self.__IV is None: - return self.__ciphermodule.new(key) - elif self.__IV is None: - return self.__ciphermodule.new(key, self.__mode) - else: - return self.__ciphermodule.new(key, self.__mode, self.__IV) - - - -if __name__ == '__main__': - import sys - import getopt - import base64 - - usagemsg = '''\ -Test module usage: %(program)s [-c cipher] [-l] [-h] - -Where: - --cipher module - -c module - Cipher module to use. Default: %(ciphermodule)s - - --aslong - -l - Print the encoded message blocks as long integers instead of base64 - encoded strings - - --help - -h - Print this help message -''' - - ciphermodule = 'AES' - aslong = 0 - - def usage(code, msg=None): - if msg: - print msg - print usagemsg % {'program': sys.argv[0], - 'ciphermodule': ciphermodule} - sys.exit(code) - - try: - opts, args = getopt.getopt(sys.argv[1:], - 'c:l', ['cipher=', 'aslong']) - except getopt.error, msg: - usage(1, msg) - - if args: - usage(1, 'Too many arguments') - - for opt, arg in opts: - if opt in ('-h', '--help'): - usage(0) - elif opt in ('-c', '--cipher'): - ciphermodule = arg - elif opt in ('-l', '--aslong'): - aslong = 1 - - # ugly hack to force __import__ to give us the end-path module - module = __import__('Crypto.Cipher.'+ciphermodule, None, None, ['new']) - - a = AllOrNothing(module) - print 'Original text:\n==========' - print __doc__ - print '==========' - msgblocks = a.digest(__doc__) - print 'message blocks:' - for i, blk in map(None, range(len(msgblocks)), msgblocks): - # base64 adds a trailing newline - print ' %3d' % i, - if aslong: - print bytes_to_long(blk) - else: - print base64.encodestring(blk)[:-1] - # - # get a new undigest-only object so there's no leakage - b = AllOrNothing(module) - text = b.undigest(msgblocks) - if text == __doc__: - print 'They match!' - else: - print 'They differ!' diff --git a/gdata/analytics/Crypto/Protocol/Chaffing.py b/gdata/analytics/Crypto/Protocol/Chaffing.py deleted file mode 100644 index fdfb82d0c3..0000000000 --- a/gdata/analytics/Crypto/Protocol/Chaffing.py +++ /dev/null @@ -1,229 +0,0 @@ -"""This file implements the chaffing algorithm. - -Winnowing and chaffing is a technique for enhancing privacy without requiring -strong encryption. In short, the technique takes a set of authenticated -message blocks (the wheat) and adds a number of chaff blocks which have -randomly chosen data and MAC fields. This means that to an adversary, the -chaff blocks look as valid as the wheat blocks, and so the authentication -would have to be performed on every block. By tailoring the number of chaff -blocks added to the message, the sender can make breaking the message -computationally infeasible. There are many other interesting properties of -the winnow/chaff technique. - -For example, say Alice is sending a message to Bob. She packetizes the -message and performs an all-or-nothing transformation on the packets. Then -she authenticates each packet with a message authentication code (MAC). The -MAC is a hash of the data packet, and there is a secret key which she must -share with Bob (key distribution is an exercise left to the reader). She then -adds a serial number to each packet, and sends the packets to Bob. - -Bob receives the packets, and using the shared secret authentication key, -authenticates the MACs for each packet. Those packets that have bad MACs are -simply discarded. The remainder are sorted by serial number, and passed -through the reverse all-or-nothing transform. The transform means that an -eavesdropper (say Eve) must acquire all the packets before any of the data can -be read. If even one packet is missing, the data is useless. - -There's one twist: by adding chaff packets, Alice and Bob can make Eve's job -much harder, since Eve now has to break the shared secret key, or try every -combination of wheat and chaff packet to read any of the message. The cool -thing is that Bob doesn't need to add any additional code; the chaff packets -are already filtered out because their MACs don't match (in all likelihood -- -since the data and MACs for the chaff packets are randomly chosen it is -possible, but very unlikely that a chaff MAC will match the chaff data). And -Alice need not even be the party adding the chaff! She could be completely -unaware that a third party, say Charles, is adding chaff packets to her -messages as they are transmitted. - -For more information on winnowing and chaffing see this paper: - -Ronald L. Rivest, "Chaffing and Winnowing: Confidentiality without Encryption" -http://theory.lcs.mit.edu/~rivest/chaffing.txt - -""" - -__revision__ = "$Id: Chaffing.py,v 1.7 2003/02/28 15:23:21 akuchling Exp $" - -from Crypto.Util.number import bytes_to_long - -class Chaff: - """Class implementing the chaff adding algorithm. - - Methods for subclasses: - - _randnum(size): - Returns a randomly generated number with a byte-length equal - to size. Subclasses can use this to implement better random - data and MAC generating algorithms. The default algorithm is - probably not very cryptographically secure. It is most - important that the chaff data does not contain any patterns - that can be used to discern it from wheat data without running - the MAC. - - """ - - def __init__(self, factor=1.0, blocksper=1): - """Chaff(factor:float, blocksper:int) - - factor is the number of message blocks to add chaff to, - expressed as a percentage between 0.0 and 1.0. blocksper is - the number of chaff blocks to include for each block being - chaffed. Thus the defaults add one chaff block to every - message block. By changing the defaults, you can adjust how - computationally difficult it could be for an adversary to - brute-force crack the message. The difficulty is expressed - as: - - pow(blocksper, int(factor * number-of-blocks)) - - For ease of implementation, when factor < 1.0, only the first - int(factor*number-of-blocks) message blocks are chaffed. - """ - - if not (0.0<=factor<=1.0): - raise ValueError, "'factor' must be between 0.0 and 1.0" - if blocksper < 0: - raise ValueError, "'blocksper' must be zero or more" - - self.__factor = factor - self.__blocksper = blocksper - - - def chaff(self, blocks): - """chaff( [(serial-number:int, data:string, MAC:string)] ) - : [(int, string, string)] - - Add chaff to message blocks. blocks is a list of 3-tuples of the - form (serial-number, data, MAC). - - Chaff is created by choosing a random number of the same - byte-length as data, and another random number of the same - byte-length as MAC. The message block's serial number is - placed on the chaff block and all the packet's chaff blocks - are randomly interspersed with the single wheat block. This - method then returns a list of 3-tuples of the same form. - Chaffed blocks will contain multiple instances of 3-tuples - with the same serial number, but the only way to figure out - which blocks are wheat and which are chaff is to perform the - MAC hash and compare values. - """ - - chaffedblocks = [] - - # count is the number of blocks to add chaff to. blocksper is the - # number of chaff blocks to add per message block that is being - # chaffed. - count = len(blocks) * self.__factor - blocksper = range(self.__blocksper) - for i, wheat in map(None, range(len(blocks)), blocks): - # it shouldn't matter which of the n blocks we add chaff to, so for - # ease of implementation, we'll just add them to the first count - # blocks - if i < count: - serial, data, mac = wheat - datasize = len(data) - macsize = len(mac) - addwheat = 1 - # add chaff to this block - for j in blocksper: - import sys - chaffdata = self._randnum(datasize) - chaffmac = self._randnum(macsize) - chaff = (serial, chaffdata, chaffmac) - # mix up the order, if the 5th bit is on then put the - # wheat on the list - if addwheat and bytes_to_long(self._randnum(16)) & 0x40: - chaffedblocks.append(wheat) - addwheat = 0 - chaffedblocks.append(chaff) - if addwheat: - chaffedblocks.append(wheat) - else: - # just add the wheat - chaffedblocks.append(wheat) - return chaffedblocks - - def _randnum(self, size): - # TBD: Not a very secure algorithm. - # TBD: size * 2 to work around possible bug in RandomPool - from Crypto.Util import randpool - import time - pool = randpool.RandomPool(size * 2) - while size > pool.entropy: - pass - - # we now have enough entropy in the pool to get size bytes of random - # data... well, probably - return pool.get_bytes(size) - - - -if __name__ == '__main__': - text = """\ -We hold these truths to be self-evident, that all men are created equal, that -they are endowed by their Creator with certain unalienable Rights, that among -these are Life, Liberty, and the pursuit of Happiness. That to secure these -rights, Governments are instituted among Men, deriving their just powers from -the consent of the governed. That whenever any Form of Government becomes -destructive of these ends, it is the Right of the People to alter or to -abolish it, and to institute new Government, laying its foundation on such -principles and organizing its powers in such form, as to them shall seem most -likely to effect their Safety and Happiness. -""" - print 'Original text:\n==========' - print text - print '==========' - - # first transform the text into packets - blocks = [] ; size = 40 - for i in range(0, len(text), size): - blocks.append( text[i:i+size] ) - - # now get MACs for all the text blocks. The key is obvious... - print 'Calculating MACs...' - from Crypto.Hash import HMAC, SHA - key = 'Jefferson' - macs = [HMAC.new(key, block, digestmod=SHA).digest() - for block in blocks] - - assert len(blocks) == len(macs) - - # put these into a form acceptable as input to the chaffing procedure - source = [] - m = map(None, range(len(blocks)), blocks, macs) - print m - for i, data, mac in m: - source.append((i, data, mac)) - - # now chaff these - print 'Adding chaff...' - c = Chaff(factor=0.5, blocksper=2) - chaffed = c.chaff(source) - - from base64 import encodestring - - # print the chaffed message blocks. meanwhile, separate the wheat from - # the chaff - - wheat = [] - print 'chaffed message blocks:' - for i, data, mac in chaffed: - # do the authentication - h = HMAC.new(key, data, digestmod=SHA) - pmac = h.digest() - if pmac == mac: - tag = '-->' - wheat.append(data) - else: - tag = ' ' - # base64 adds a trailing newline - print tag, '%3d' % i, \ - repr(data), encodestring(mac)[:-1] - - # now decode the message packets and check it against the original text - print 'Undigesting wheat...' - newtext = "".join(wheat) - if newtext == text: - print 'They match!' - else: - print 'They differ!' diff --git a/gdata/analytics/Crypto/Protocol/__init__.py b/gdata/analytics/Crypto/Protocol/__init__.py deleted file mode 100644 index a6d68bcf8d..0000000000 --- a/gdata/analytics/Crypto/Protocol/__init__.py +++ /dev/null @@ -1,17 +0,0 @@ - -"""Cryptographic protocols - -Implements various cryptographic protocols. (Don't expect to find -network protocols here.) - -Crypto.Protocol.AllOrNothing Transforms a message into a set of message - blocks, such that the blocks can be - recombined to get the message back. - -Crypto.Protocol.Chaffing Takes a set of authenticated message blocks - (the wheat) and adds a number of - randomly generated blocks (the chaff). -""" - -__all__ = ['AllOrNothing', 'Chaffing'] -__revision__ = "$Id: __init__.py,v 1.4 2003/02/28 15:23:21 akuchling Exp $" diff --git a/gdata/analytics/Crypto/PublicKey/DSA.py b/gdata/analytics/Crypto/PublicKey/DSA.py deleted file mode 100644 index 7947b6f5fb..0000000000 --- a/gdata/analytics/Crypto/PublicKey/DSA.py +++ /dev/null @@ -1,238 +0,0 @@ - -# -# DSA.py : Digital Signature Algorithm -# -# Part of the Python Cryptography Toolkit -# -# Distribute and use freely; there are no restrictions on further -# dissemination and usage except those imposed by the laws of your -# country of residence. This software is provided "as is" without -# warranty of fitness for use or suitability for any purpose, express -# or implied. Use at your own risk or not at all. -# - -__revision__ = "$Id: DSA.py,v 1.16 2004/05/06 12:52:54 akuchling Exp $" - -from Crypto.PublicKey.pubkey import * -from Crypto.Util import number -from Crypto.Util.number import bytes_to_long, long_to_bytes -from Crypto.Hash import SHA - -try: - from Crypto.PublicKey import _fastmath -except ImportError: - _fastmath = None - -class error (Exception): - pass - -def generateQ(randfunc): - S=randfunc(20) - hash1=SHA.new(S).digest() - hash2=SHA.new(long_to_bytes(bytes_to_long(S)+1)).digest() - q = bignum(0) - for i in range(0,20): - c=ord(hash1[i])^ord(hash2[i]) - if i==0: - c=c | 128 - if i==19: - c= c | 1 - q=q*256+c - while (not isPrime(q)): - q=q+2 - if pow(2,159L) < q < pow(2,160L): - return S, q - raise error, 'Bad q value generated' - -def generate(bits, randfunc, progress_func=None): - """generate(bits:int, randfunc:callable, progress_func:callable) - - Generate a DSA key of length 'bits', using 'randfunc' to get - random data and 'progress_func', if present, to display - the progress of the key generation. - """ - - if bits<160: - raise error, 'Key length <160 bits' - obj=DSAobj() - # Generate string S and prime q - if progress_func: - progress_func('p,q\n') - while (1): - S, obj.q = generateQ(randfunc) - n=(bits-1)/160 - C, N, V = 0, 2, {} - b=(obj.q >> 5) & 15 - powb=pow(bignum(2), b) - powL1=pow(bignum(2), bits-1) - while C<4096: - for k in range(0, n+1): - V[k]=bytes_to_long(SHA.new(S+str(N)+str(k)).digest()) - W=V[n] % powb - for k in range(n-1, -1, -1): - W=(W<<160L)+V[k] - X=W+powL1 - p=X-(X%(2*obj.q)-1) - if powL1<=p and isPrime(p): - break - C, N = C+1, N+n+1 - if C<4096: - break - if progress_func: - progress_func('4096 multiples failed\n') - - obj.p = p - power=(p-1)/obj.q - if progress_func: - progress_func('h,g\n') - while (1): - h=bytes_to_long(randfunc(bits)) % (p-1) - g=pow(h, power, p) - if 11: - break - obj.g=g - if progress_func: - progress_func('x,y\n') - while (1): - x=bytes_to_long(randfunc(20)) - if 0 < x < obj.q: - break - obj.x, obj.y = x, pow(g, x, p) - return obj - -def construct(tuple): - """construct(tuple:(long,long,long,long)|(long,long,long,long,long)):DSAobj - Construct a DSA object from a 4- or 5-tuple of numbers. - """ - obj=DSAobj() - if len(tuple) not in [4,5]: - raise error, 'argument for construct() wrong length' - for i in range(len(tuple)): - field = obj.keydata[i] - setattr(obj, field, tuple[i]) - return obj - -class DSAobj(pubkey): - keydata=['y', 'g', 'p', 'q', 'x'] - - def _encrypt(self, s, Kstr): - raise error, 'DSA algorithm cannot encrypt data' - - def _decrypt(self, s): - raise error, 'DSA algorithm cannot decrypt data' - - def _sign(self, M, K): - if (K<2 or self.q<=K): - raise error, 'K is not between 2 and q' - r=pow(self.g, K, self.p) % self.q - s=(inverse(K, self.q)*(M+self.x*r)) % self.q - return (r,s) - - def _verify(self, M, sig): - r, s = sig - if r<=0 or r>=self.q or s<=0 or s>=self.q: - return 0 - w=inverse(s, self.q) - u1, u2 = (M*w) % self.q, (r*w) % self.q - v1 = pow(self.g, u1, self.p) - v2 = pow(self.y, u2, self.p) - v = ((v1*v2) % self.p) - v = v % self.q - if v==r: - return 1 - return 0 - - def size(self): - "Return the maximum number of bits that can be handled by this key." - return number.size(self.p) - 1 - - def has_private(self): - """Return a Boolean denoting whether the object contains - private components.""" - if hasattr(self, 'x'): - return 1 - else: - return 0 - - def can_sign(self): - """Return a Boolean value recording whether this algorithm can generate signatures.""" - return 1 - - def can_encrypt(self): - """Return a Boolean value recording whether this algorithm can encrypt data.""" - return 0 - - def publickey(self): - """Return a new key object containing only the public information.""" - return construct((self.y, self.g, self.p, self.q)) - -object=DSAobj - -generate_py = generate -construct_py = construct - -class DSAobj_c(pubkey): - keydata = ['y', 'g', 'p', 'q', 'x'] - - def __init__(self, key): - self.key = key - - def __getattr__(self, attr): - if attr in self.keydata: - return getattr(self.key, attr) - else: - if self.__dict__.has_key(attr): - self.__dict__[attr] - else: - raise AttributeError, '%s instance has no attribute %s' % (self.__class__, attr) - - def __getstate__(self): - d = {} - for k in self.keydata: - if hasattr(self.key, k): - d[k]=getattr(self.key, k) - return d - - def __setstate__(self, state): - y,g,p,q = state['y'], state['g'], state['p'], state['q'] - if not state.has_key('x'): - self.key = _fastmath.dsa_construct(y,g,p,q) - else: - x = state['x'] - self.key = _fastmath.dsa_construct(y,g,p,q,x) - - def _sign(self, M, K): - return self.key._sign(M, K) - - def _verify(self, M, (r, s)): - return self.key._verify(M, r, s) - - def size(self): - return self.key.size() - - def has_private(self): - return self.key.has_private() - - def publickey(self): - return construct_c((self.key.y, self.key.g, self.key.p, self.key.q)) - - def can_sign(self): - return 1 - - def can_encrypt(self): - return 0 - -def generate_c(bits, randfunc, progress_func=None): - obj = generate_py(bits, randfunc, progress_func) - y,g,p,q,x = obj.y, obj.g, obj.p, obj.q, obj.x - return construct_c((y,g,p,q,x)) - -def construct_c(tuple): - key = apply(_fastmath.dsa_construct, tuple) - return DSAobj_c(key) - -if _fastmath: - #print "using C version of DSA" - generate = generate_c - construct = construct_c - error = _fastmath.error diff --git a/gdata/analytics/Crypto/PublicKey/ElGamal.py b/gdata/analytics/Crypto/PublicKey/ElGamal.py deleted file mode 100644 index 026881c91a..0000000000 --- a/gdata/analytics/Crypto/PublicKey/ElGamal.py +++ /dev/null @@ -1,132 +0,0 @@ -# -# ElGamal.py : ElGamal encryption/decryption and signatures -# -# Part of the Python Cryptography Toolkit -# -# Distribute and use freely; there are no restrictions on further -# dissemination and usage except those imposed by the laws of your -# country of residence. This software is provided "as is" without -# warranty of fitness for use or suitability for any purpose, express -# or implied. Use at your own risk or not at all. -# - -__revision__ = "$Id: ElGamal.py,v 1.9 2003/04/04 19:44:26 akuchling Exp $" - -from Crypto.PublicKey.pubkey import * -from Crypto.Util import number - -class error (Exception): - pass - -# Generate an ElGamal key with N bits -def generate(bits, randfunc, progress_func=None): - """generate(bits:int, randfunc:callable, progress_func:callable) - - Generate an ElGamal key of length 'bits', using 'randfunc' to get - random data and 'progress_func', if present, to display - the progress of the key generation. - """ - obj=ElGamalobj() - # Generate prime p - if progress_func: - progress_func('p\n') - obj.p=bignum(getPrime(bits, randfunc)) - # Generate random number g - if progress_func: - progress_func('g\n') - size=bits-1-(ord(randfunc(1)) & 63) # g will be from 1--64 bits smaller than p - if size<1: - size=bits-1 - while (1): - obj.g=bignum(getPrime(size, randfunc)) - if obj.g < obj.p: - break - size=(size+1) % bits - if size==0: - size=4 - # Generate random number x - if progress_func: - progress_func('x\n') - while (1): - size=bits-1-ord(randfunc(1)) # x will be from 1 to 256 bits smaller than p - if size>2: - break - while (1): - obj.x=bignum(getPrime(size, randfunc)) - if obj.x < obj.p: - break - size = (size+1) % bits - if size==0: - size=4 - if progress_func: - progress_func('y\n') - obj.y = pow(obj.g, obj.x, obj.p) - return obj - -def construct(tuple): - """construct(tuple:(long,long,long,long)|(long,long,long,long,long))) - : ElGamalobj - Construct an ElGamal key from a 3- or 4-tuple of numbers. - """ - - obj=ElGamalobj() - if len(tuple) not in [3,4]: - raise error, 'argument for construct() wrong length' - for i in range(len(tuple)): - field = obj.keydata[i] - setattr(obj, field, tuple[i]) - return obj - -class ElGamalobj(pubkey): - keydata=['p', 'g', 'y', 'x'] - - def _encrypt(self, M, K): - a=pow(self.g, K, self.p) - b=( M*pow(self.y, K, self.p) ) % self.p - return ( a,b ) - - def _decrypt(self, M): - if (not hasattr(self, 'x')): - raise error, 'Private key not available in this object' - ax=pow(M[0], self.x, self.p) - plaintext=(M[1] * inverse(ax, self.p ) ) % self.p - return plaintext - - def _sign(self, M, K): - if (not hasattr(self, 'x')): - raise error, 'Private key not available in this object' - p1=self.p-1 - if (GCD(K, p1)!=1): - raise error, 'Bad K value: GCD(K,p-1)!=1' - a=pow(self.g, K, self.p) - t=(M-self.x*a) % p1 - while t<0: t=t+p1 - b=(t*inverse(K, p1)) % p1 - return (a, b) - - def _verify(self, M, sig): - v1=pow(self.y, sig[0], self.p) - v1=(v1*pow(sig[0], sig[1], self.p)) % self.p - v2=pow(self.g, M, self.p) - if v1==v2: - return 1 - return 0 - - def size(self): - "Return the maximum number of bits that can be handled by this key." - return number.size(self.p) - 1 - - def has_private(self): - """Return a Boolean denoting whether the object contains - private components.""" - if hasattr(self, 'x'): - return 1 - else: - return 0 - - def publickey(self): - """Return a new key object containing only the public information.""" - return construct((self.p, self.g, self.y)) - - -object=ElGamalobj diff --git a/gdata/analytics/Crypto/PublicKey/RSA.py b/gdata/analytics/Crypto/PublicKey/RSA.py deleted file mode 100644 index e0e877ec16..0000000000 --- a/gdata/analytics/Crypto/PublicKey/RSA.py +++ /dev/null @@ -1,256 +0,0 @@ -# -# RSA.py : RSA encryption/decryption -# -# Part of the Python Cryptography Toolkit -# -# Distribute and use freely; there are no restrictions on further -# dissemination and usage except those imposed by the laws of your -# country of residence. This software is provided "as is" without -# warranty of fitness for use or suitability for any purpose, express -# or implied. Use at your own risk or not at all. -# - -__revision__ = "$Id: RSA.py,v 1.20 2004/05/06 12:52:54 akuchling Exp $" - -from Crypto.PublicKey import pubkey -from Crypto.Util import number - -try: - from Crypto.PublicKey import _fastmath -except ImportError: - _fastmath = None - -class error (Exception): - pass - -def generate(bits, randfunc, progress_func=None): - """generate(bits:int, randfunc:callable, progress_func:callable) - - Generate an RSA key of length 'bits', using 'randfunc' to get - random data and 'progress_func', if present, to display - the progress of the key generation. - """ - obj=RSAobj() - - # Generate the prime factors of n - if progress_func: - progress_func('p,q\n') - p = q = 1L - while number.size(p*q) < bits: - p = pubkey.getPrime(bits/2, randfunc) - q = pubkey.getPrime(bits/2, randfunc) - - # p shall be smaller than q (for calc of u) - if p > q: - (p, q)=(q, p) - obj.p = p - obj.q = q - - if progress_func: - progress_func('u\n') - obj.u = pubkey.inverse(obj.p, obj.q) - obj.n = obj.p*obj.q - - obj.e = 65537L - if progress_func: - progress_func('d\n') - obj.d=pubkey.inverse(obj.e, (obj.p-1)*(obj.q-1)) - - assert bits <= 1+obj.size(), "Generated key is too small" - - return obj - -def construct(tuple): - """construct(tuple:(long,) : RSAobj - Construct an RSA object from a 2-, 3-, 5-, or 6-tuple of numbers. - """ - - obj=RSAobj() - if len(tuple) not in [2,3,5,6]: - raise error, 'argument for construct() wrong length' - for i in range(len(tuple)): - field = obj.keydata[i] - setattr(obj, field, tuple[i]) - if len(tuple) >= 5: - # Ensure p is smaller than q - if obj.p>obj.q: - (obj.p, obj.q)=(obj.q, obj.p) - - if len(tuple) == 5: - # u not supplied, so we're going to have to compute it. - obj.u=pubkey.inverse(obj.p, obj.q) - - return obj - -class RSAobj(pubkey.pubkey): - keydata = ['n', 'e', 'd', 'p', 'q', 'u'] - def _encrypt(self, plaintext, K=''): - if self.n<=plaintext: - raise error, 'Plaintext too large' - return (pow(plaintext, self.e, self.n),) - - def _decrypt(self, ciphertext): - if (not hasattr(self, 'd')): - raise error, 'Private key not available in this object' - if self.n<=ciphertext[0]: - raise error, 'Ciphertext too large' - return pow(ciphertext[0], self.d, self.n) - - def _sign(self, M, K=''): - return (self._decrypt((M,)),) - - def _verify(self, M, sig): - m2=self._encrypt(sig[0]) - if m2[0]==M: - return 1 - else: return 0 - - def _blind(self, M, B): - tmp = pow(B, self.e, self.n) - return (M * tmp) % self.n - - def _unblind(self, M, B): - tmp = pubkey.inverse(B, self.n) - return (M * tmp) % self.n - - def can_blind (self): - """can_blind() : bool - Return a Boolean value recording whether this algorithm can - blind data. (This does not imply that this - particular key object has the private information required to - to blind a message.) - """ - return 1 - - def size(self): - """size() : int - Return the maximum number of bits that can be handled by this key. - """ - return number.size(self.n) - 1 - - def has_private(self): - """has_private() : bool - Return a Boolean denoting whether the object contains - private components. - """ - if hasattr(self, 'd'): - return 1 - else: return 0 - - def publickey(self): - """publickey(): RSAobj - Return a new key object containing only the public key information. - """ - return construct((self.n, self.e)) - -class RSAobj_c(pubkey.pubkey): - keydata = ['n', 'e', 'd', 'p', 'q', 'u'] - - def __init__(self, key): - self.key = key - - def __getattr__(self, attr): - if attr in self.keydata: - return getattr(self.key, attr) - else: - if self.__dict__.has_key(attr): - self.__dict__[attr] - else: - raise AttributeError, '%s instance has no attribute %s' % (self.__class__, attr) - - def __getstate__(self): - d = {} - for k in self.keydata: - if hasattr(self.key, k): - d[k]=getattr(self.key, k) - return d - - def __setstate__(self, state): - n,e = state['n'], state['e'] - if not state.has_key('d'): - self.key = _fastmath.rsa_construct(n,e) - else: - d = state['d'] - if not state.has_key('q'): - self.key = _fastmath.rsa_construct(n,e,d) - else: - p, q, u = state['p'], state['q'], state['u'] - self.key = _fastmath.rsa_construct(n,e,d,p,q,u) - - def _encrypt(self, plain, K): - return (self.key._encrypt(plain),) - - def _decrypt(self, cipher): - return self.key._decrypt(cipher[0]) - - def _sign(self, M, K): - return (self.key._sign(M),) - - def _verify(self, M, sig): - return self.key._verify(M, sig[0]) - - def _blind(self, M, B): - return self.key._blind(M, B) - - def _unblind(self, M, B): - return self.key._unblind(M, B) - - def can_blind (self): - return 1 - - def size(self): - return self.key.size() - - def has_private(self): - return self.key.has_private() - - def publickey(self): - return construct_c((self.key.n, self.key.e)) - -def generate_c(bits, randfunc, progress_func = None): - # Generate the prime factors of n - if progress_func: - progress_func('p,q\n') - - p = q = 1L - while number.size(p*q) < bits: - p = pubkey.getPrime(bits/2, randfunc) - q = pubkey.getPrime(bits/2, randfunc) - - # p shall be smaller than q (for calc of u) - if p > q: - (p, q)=(q, p) - if progress_func: - progress_func('u\n') - u=pubkey.inverse(p, q) - n=p*q - - e = 65537L - if progress_func: - progress_func('d\n') - d=pubkey.inverse(e, (p-1)*(q-1)) - key = _fastmath.rsa_construct(n,e,d,p,q,u) - obj = RSAobj_c(key) - -## print p -## print q -## print number.size(p), number.size(q), number.size(q*p), -## print obj.size(), bits - assert bits <= 1+obj.size(), "Generated key is too small" - return obj - - -def construct_c(tuple): - key = apply(_fastmath.rsa_construct, tuple) - return RSAobj_c(key) - -object = RSAobj - -generate_py = generate -construct_py = construct - -if _fastmath: - #print "using C version of RSA" - generate = generate_c - construct = construct_c - error = _fastmath.error diff --git a/gdata/analytics/Crypto/PublicKey/__init__.py b/gdata/analytics/Crypto/PublicKey/__init__.py deleted file mode 100644 index ad1c80ca14..0000000000 --- a/gdata/analytics/Crypto/PublicKey/__init__.py +++ /dev/null @@ -1,17 +0,0 @@ -"""Public-key encryption and signature algorithms. - -Public-key encryption uses two different keys, one for encryption and -one for decryption. The encryption key can be made public, and the -decryption key is kept private. Many public-key algorithms can also -be used to sign messages, and some can *only* be used for signatures. - -Crypto.PublicKey.DSA Digital Signature Algorithm. (Signature only) -Crypto.PublicKey.ElGamal (Signing and encryption) -Crypto.PublicKey.RSA (Signing, encryption, and blinding) -Crypto.PublicKey.qNEW (Signature only) - -""" - -__all__ = ['RSA', 'DSA', 'ElGamal', 'qNEW'] -__revision__ = "$Id: __init__.py,v 1.4 2003/04/03 20:27:13 akuchling Exp $" - diff --git a/gdata/analytics/Crypto/PublicKey/pubkey.py b/gdata/analytics/Crypto/PublicKey/pubkey.py deleted file mode 100644 index 5c75c3e3ad..0000000000 --- a/gdata/analytics/Crypto/PublicKey/pubkey.py +++ /dev/null @@ -1,172 +0,0 @@ -# -# pubkey.py : Internal functions for public key operations -# -# Part of the Python Cryptography Toolkit -# -# Distribute and use freely; there are no restrictions on further -# dissemination and usage except those imposed by the laws of your -# country of residence. This software is provided "as is" without -# warranty of fitness for use or suitability for any purpose, express -# or implied. Use at your own risk or not at all. -# - -__revision__ = "$Id: pubkey.py,v 1.11 2003/04/03 20:36:14 akuchling Exp $" - -import types, warnings -from Crypto.Util.number import * - -# Basic public key class -class pubkey: - def __init__(self): - pass - - def __getstate__(self): - """To keep key objects platform-independent, the key data is - converted to standard Python long integers before being - written out. It will then be reconverted as necessary on - restoration.""" - d=self.__dict__ - for key in self.keydata: - if d.has_key(key): d[key]=long(d[key]) - return d - - def __setstate__(self, d): - """On unpickling a key object, the key data is converted to the big -number representation being used, whether that is Python long -integers, MPZ objects, or whatever.""" - for key in self.keydata: - if d.has_key(key): self.__dict__[key]=bignum(d[key]) - - def encrypt(self, plaintext, K): - """encrypt(plaintext:string|long, K:string|long) : tuple - Encrypt the string or integer plaintext. K is a random - parameter required by some algorithms. - """ - wasString=0 - if isinstance(plaintext, types.StringType): - plaintext=bytes_to_long(plaintext) ; wasString=1 - if isinstance(K, types.StringType): - K=bytes_to_long(K) - ciphertext=self._encrypt(plaintext, K) - if wasString: return tuple(map(long_to_bytes, ciphertext)) - else: return ciphertext - - def decrypt(self, ciphertext): - """decrypt(ciphertext:tuple|string|long): string - Decrypt 'ciphertext' using this key. - """ - wasString=0 - if not isinstance(ciphertext, types.TupleType): - ciphertext=(ciphertext,) - if isinstance(ciphertext[0], types.StringType): - ciphertext=tuple(map(bytes_to_long, ciphertext)) ; wasString=1 - plaintext=self._decrypt(ciphertext) - if wasString: return long_to_bytes(plaintext) - else: return plaintext - - def sign(self, M, K): - """sign(M : string|long, K:string|long) : tuple - Return a tuple containing the signature for the message M. - K is a random parameter required by some algorithms. - """ - if (not self.has_private()): - raise error, 'Private key not available in this object' - if isinstance(M, types.StringType): M=bytes_to_long(M) - if isinstance(K, types.StringType): K=bytes_to_long(K) - return self._sign(M, K) - - def verify (self, M, signature): - """verify(M:string|long, signature:tuple) : bool - Verify that the signature is valid for the message M; - returns true if the signature checks out. - """ - if isinstance(M, types.StringType): M=bytes_to_long(M) - return self._verify(M, signature) - - # alias to compensate for the old validate() name - def validate (self, M, signature): - warnings.warn("validate() method name is obsolete; use verify()", - DeprecationWarning) - - def blind(self, M, B): - """blind(M : string|long, B : string|long) : string|long - Blind message M using blinding factor B. - """ - wasString=0 - if isinstance(M, types.StringType): - M=bytes_to_long(M) ; wasString=1 - if isinstance(B, types.StringType): B=bytes_to_long(B) - blindedmessage=self._blind(M, B) - if wasString: return long_to_bytes(blindedmessage) - else: return blindedmessage - - def unblind(self, M, B): - """unblind(M : string|long, B : string|long) : string|long - Unblind message M using blinding factor B. - """ - wasString=0 - if isinstance(M, types.StringType): - M=bytes_to_long(M) ; wasString=1 - if isinstance(B, types.StringType): B=bytes_to_long(B) - unblindedmessage=self._unblind(M, B) - if wasString: return long_to_bytes(unblindedmessage) - else: return unblindedmessage - - - # The following methods will usually be left alone, except for - # signature-only algorithms. They both return Boolean values - # recording whether this key's algorithm can sign and encrypt. - def can_sign (self): - """can_sign() : bool - Return a Boolean value recording whether this algorithm can - generate signatures. (This does not imply that this - particular key object has the private information required to - to generate a signature.) - """ - return 1 - - def can_encrypt (self): - """can_encrypt() : bool - Return a Boolean value recording whether this algorithm can - encrypt data. (This does not imply that this - particular key object has the private information required to - to decrypt a message.) - """ - return 1 - - def can_blind (self): - """can_blind() : bool - Return a Boolean value recording whether this algorithm can - blind data. (This does not imply that this - particular key object has the private information required to - to blind a message.) - """ - return 0 - - # The following methods will certainly be overridden by - # subclasses. - - def size (self): - """size() : int - Return the maximum number of bits that can be handled by this key. - """ - return 0 - - def has_private (self): - """has_private() : bool - Return a Boolean denoting whether the object contains - private components. - """ - return 0 - - def publickey (self): - """publickey(): object - Return a new key object containing only the public information. - """ - return self - - def __eq__ (self, other): - """__eq__(other): 0, 1 - Compare us to other for equality. - """ - return self.__getstate__() == other.__getstate__() diff --git a/gdata/analytics/Crypto/PublicKey/qNEW.py b/gdata/analytics/Crypto/PublicKey/qNEW.py deleted file mode 100644 index 65f8ae36b3..0000000000 --- a/gdata/analytics/Crypto/PublicKey/qNEW.py +++ /dev/null @@ -1,170 +0,0 @@ -# -# qNEW.py : The q-NEW signature algorithm. -# -# Part of the Python Cryptography Toolkit -# -# Distribute and use freely; there are no restrictions on further -# dissemination and usage except those imposed by the laws of your -# country of residence. This software is provided "as is" without -# warranty of fitness for use or suitability for any purpose, express -# or implied. Use at your own risk or not at all. -# - -__revision__ = "$Id: qNEW.py,v 1.8 2003/04/04 15:13:35 akuchling Exp $" - -from Crypto.PublicKey import pubkey -from Crypto.Util.number import * -from Crypto.Hash import SHA - -class error (Exception): - pass - -HASHBITS = 160 # Size of SHA digests - -def generate(bits, randfunc, progress_func=None): - """generate(bits:int, randfunc:callable, progress_func:callable) - - Generate a qNEW key of length 'bits', using 'randfunc' to get - random data and 'progress_func', if present, to display - the progress of the key generation. - """ - obj=qNEWobj() - - # Generate prime numbers p and q. q is a 160-bit prime - # number. p is another prime number (the modulus) whose bit - # size is chosen by the caller, and is generated so that p-1 - # is a multiple of q. - # - # Note that only a single seed is used to - # generate p and q; if someone generates a key for you, you can - # use the seed to duplicate the key generation. This can - # protect you from someone generating values of p,q that have - # some special form that's easy to break. - if progress_func: - progress_func('p,q\n') - while (1): - obj.q = getPrime(160, randfunc) - # assert pow(2, 159L)1. g is kept; h can be discarded. - if progress_func: - progress_func('h,g\n') - while (1): - h=bytes_to_long(randfunc(bits)) % (p-1) - g=pow(h, power, p) - if 11: - break - obj.g=g - - # x is the private key information, and is - # just a random number between 0 and q. - # y=g**x mod p, and is part of the public information. - if progress_func: - progress_func('x,y\n') - while (1): - x=bytes_to_long(randfunc(20)) - if 0 < x < obj.q: - break - obj.x, obj.y=x, pow(g, x, p) - - return obj - -# Construct a qNEW object -def construct(tuple): - """construct(tuple:(long,long,long,long)|(long,long,long,long,long) - Construct a qNEW object from a 4- or 5-tuple of numbers. - """ - obj=qNEWobj() - if len(tuple) not in [4,5]: - raise error, 'argument for construct() wrong length' - for i in range(len(tuple)): - field = obj.keydata[i] - setattr(obj, field, tuple[i]) - return obj - -class qNEWobj(pubkey.pubkey): - keydata=['p', 'q', 'g', 'y', 'x'] - - def _sign(self, M, K=''): - if (self.q<=K): - raise error, 'K is greater than q' - if M<0: - raise error, 'Illegal value of M (<0)' - if M>=pow(2,161L): - raise error, 'Illegal value of M (too large)' - r=pow(self.g, K, self.p) % self.q - s=(K- (r*M*self.x % self.q)) % self.q - return (r,s) - def _verify(self, M, sig): - r, s = sig - if r<=0 or r>=self.q or s<=0 or s>=self.q: - return 0 - if M<0: - raise error, 'Illegal value of M (<0)' - if M<=0 or M>=pow(2,161L): - return 0 - v1 = pow(self.g, s, self.p) - v2 = pow(self.y, M*r, self.p) - v = ((v1*v2) % self.p) - v = v % self.q - if v==r: - return 1 - return 0 - - def size(self): - "Return the maximum number of bits that can be handled by this key." - return 160 - - def has_private(self): - """Return a Boolean denoting whether the object contains - private components.""" - return hasattr(self, 'x') - - def can_sign(self): - """Return a Boolean value recording whether this algorithm can generate signatures.""" - return 1 - - def can_encrypt(self): - """Return a Boolean value recording whether this algorithm can encrypt data.""" - return 0 - - def publickey(self): - """Return a new key object containing only the public information.""" - return construct((self.p, self.q, self.g, self.y)) - -object = qNEWobj - diff --git a/gdata/analytics/Crypto/Util/RFC1751.py b/gdata/analytics/Crypto/Util/RFC1751.py deleted file mode 100644 index 0a47952495..0000000000 --- a/gdata/analytics/Crypto/Util/RFC1751.py +++ /dev/null @@ -1,342 +0,0 @@ -#!/usr/local/bin/python -# rfc1751.py : Converts between 128-bit strings and a human-readable -# sequence of words, as defined in RFC1751: "A Convention for -# Human-Readable 128-bit Keys", by Daniel L. McDonald. - -__revision__ = "$Id: RFC1751.py,v 1.6 2003/04/04 15:15:10 akuchling Exp $" - - -import string, binascii - -binary={0:'0000', 1:'0001', 2:'0010', 3:'0011', 4:'0100', 5:'0101', - 6:'0110', 7:'0111', 8:'1000', 9:'1001', 10:'1010', 11:'1011', - 12:'1100', 13:'1101', 14:'1110', 15:'1111'} - -def _key2bin(s): - "Convert a key into a string of binary digits" - kl=map(lambda x: ord(x), s) - kl=map(lambda x: binary[x/16]+binary[x&15], kl) - return ''.join(kl) - -def _extract(key, start, length): - """Extract a bitstring from a string of binary digits, and return its - numeric value.""" - k=key[start:start+length] - return reduce(lambda x,y: x*2+ord(y)-48, k, 0) - -def key_to_english (key): - """key_to_english(key:string) : string - Transform an arbitrary key into a string containing English words. - The key length must be a multiple of 8. - """ - english='' - for index in range(0, len(key), 8): # Loop over 8-byte subkeys - subkey=key[index:index+8] - # Compute the parity of the key - skbin=_key2bin(subkey) ; p=0 - for i in range(0, 64, 2): p=p+_extract(skbin, i, 2) - # Append parity bits to the subkey - skbin=_key2bin(subkey+chr((p<<6) & 255)) - for i in range(0, 64, 11): - english=english+wordlist[_extract(skbin, i, 11)]+' ' - - return english[:-1] # Remove the trailing space - -def english_to_key (str): - """english_to_key(string):string - Transform a string into a corresponding key. - The string must contain words separated by whitespace; the number - of words must be a multiple of 6. - """ - - L=string.split(string.upper(str)) ; key='' - for index in range(0, len(L), 6): - sublist=L[index:index+6] ; char=9*[0] ; bits=0 - for i in sublist: - index = wordlist.index(i) - shift = (8-(bits+11)%8) %8 - y = index << shift - cl, cc, cr = (y>>16), (y>>8)&0xff, y & 0xff - if (shift>5): - char[bits/8] = char[bits/8] | cl - char[bits/8+1] = char[bits/8+1] | cc - char[bits/8+2] = char[bits/8+2] | cr - elif shift>-3: - char[bits/8] = char[bits/8] | cc - char[bits/8+1] = char[bits/8+1] | cr - else: char[bits/8] = char[bits/8] | cr - bits=bits+11 - subkey=reduce(lambda x,y:x+chr(y), char, '') - - # Check the parity of the resulting key - skbin=_key2bin(subkey) - p=0 - for i in range(0, 64, 2): p=p+_extract(skbin, i, 2) - if (p&3) != _extract(skbin, 64, 2): - raise ValueError, "Parity error in resulting key" - key=key+subkey[0:8] - return key - -wordlist=[ "A", "ABE", "ACE", "ACT", "AD", "ADA", "ADD", - "AGO", "AID", "AIM", "AIR", "ALL", "ALP", "AM", "AMY", "AN", "ANA", - "AND", "ANN", "ANT", "ANY", "APE", "APS", "APT", "ARC", "ARE", "ARK", - "ARM", "ART", "AS", "ASH", "ASK", "AT", "ATE", "AUG", "AUK", "AVE", - "AWE", "AWK", "AWL", "AWN", "AX", "AYE", "BAD", "BAG", "BAH", "BAM", - "BAN", "BAR", "BAT", "BAY", "BE", "BED", "BEE", "BEG", "BEN", "BET", - "BEY", "BIB", "BID", "BIG", "BIN", "BIT", "BOB", "BOG", "BON", "BOO", - "BOP", "BOW", "BOY", "BUB", "BUD", "BUG", "BUM", "BUN", "BUS", "BUT", - "BUY", "BY", "BYE", "CAB", "CAL", "CAM", "CAN", "CAP", "CAR", "CAT", - "CAW", "COD", "COG", "COL", "CON", "COO", "COP", "COT", "COW", "COY", - "CRY", "CUB", "CUE", "CUP", "CUR", "CUT", "DAB", "DAD", "DAM", "DAN", - "DAR", "DAY", "DEE", "DEL", "DEN", "DES", "DEW", "DID", "DIE", "DIG", - "DIN", "DIP", "DO", "DOE", "DOG", "DON", "DOT", "DOW", "DRY", "DUB", - "DUD", "DUE", "DUG", "DUN", "EAR", "EAT", "ED", "EEL", "EGG", "EGO", - "ELI", "ELK", "ELM", "ELY", "EM", "END", "EST", "ETC", "EVA", "EVE", - "EWE", "EYE", "FAD", "FAN", "FAR", "FAT", "FAY", "FED", "FEE", "FEW", - "FIB", "FIG", "FIN", "FIR", "FIT", "FLO", "FLY", "FOE", "FOG", "FOR", - "FRY", "FUM", "FUN", "FUR", "GAB", "GAD", "GAG", "GAL", "GAM", "GAP", - "GAS", "GAY", "GEE", "GEL", "GEM", "GET", "GIG", "GIL", "GIN", "GO", - "GOT", "GUM", "GUN", "GUS", "GUT", "GUY", "GYM", "GYP", "HA", "HAD", - "HAL", "HAM", "HAN", "HAP", "HAS", "HAT", "HAW", "HAY", "HE", "HEM", - "HEN", "HER", "HEW", "HEY", "HI", "HID", "HIM", "HIP", "HIS", "HIT", - "HO", "HOB", "HOC", "HOE", "HOG", "HOP", "HOT", "HOW", "HUB", "HUE", - "HUG", "HUH", "HUM", "HUT", "I", "ICY", "IDA", "IF", "IKE", "ILL", - "INK", "INN", "IO", "ION", "IQ", "IRA", "IRE", "IRK", "IS", "IT", - "ITS", "IVY", "JAB", "JAG", "JAM", "JAN", "JAR", "JAW", "JAY", "JET", - "JIG", "JIM", "JO", "JOB", "JOE", "JOG", "JOT", "JOY", "JUG", "JUT", - "KAY", "KEG", "KEN", "KEY", "KID", "KIM", "KIN", "KIT", "LA", "LAB", - "LAC", "LAD", "LAG", "LAM", "LAP", "LAW", "LAY", "LEA", "LED", "LEE", - "LEG", "LEN", "LEO", "LET", "LEW", "LID", "LIE", "LIN", "LIP", "LIT", - "LO", "LOB", "LOG", "LOP", "LOS", "LOT", "LOU", "LOW", "LOY", "LUG", - "LYE", "MA", "MAC", "MAD", "MAE", "MAN", "MAO", "MAP", "MAT", "MAW", - "MAY", "ME", "MEG", "MEL", "MEN", "MET", "MEW", "MID", "MIN", "MIT", - "MOB", "MOD", "MOE", "MOO", "MOP", "MOS", "MOT", "MOW", "MUD", "MUG", - "MUM", "MY", "NAB", "NAG", "NAN", "NAP", "NAT", "NAY", "NE", "NED", - "NEE", "NET", "NEW", "NIB", "NIL", "NIP", "NIT", "NO", "NOB", "NOD", - "NON", "NOR", "NOT", "NOV", "NOW", "NU", "NUN", "NUT", "O", "OAF", - "OAK", "OAR", "OAT", "ODD", "ODE", "OF", "OFF", "OFT", "OH", "OIL", - "OK", "OLD", "ON", "ONE", "OR", "ORB", "ORE", "ORR", "OS", "OTT", - "OUR", "OUT", "OVA", "OW", "OWE", "OWL", "OWN", "OX", "PA", "PAD", - "PAL", "PAM", "PAN", "PAP", "PAR", "PAT", "PAW", "PAY", "PEA", "PEG", - "PEN", "PEP", "PER", "PET", "PEW", "PHI", "PI", "PIE", "PIN", "PIT", - "PLY", "PO", "POD", "POE", "POP", "POT", "POW", "PRO", "PRY", "PUB", - "PUG", "PUN", "PUP", "PUT", "QUO", "RAG", "RAM", "RAN", "RAP", "RAT", - "RAW", "RAY", "REB", "RED", "REP", "RET", "RIB", "RID", "RIG", "RIM", - "RIO", "RIP", "ROB", "ROD", "ROE", "RON", "ROT", "ROW", "ROY", "RUB", - "RUE", "RUG", "RUM", "RUN", "RYE", "SAC", "SAD", "SAG", "SAL", "SAM", - "SAN", "SAP", "SAT", "SAW", "SAY", "SEA", "SEC", "SEE", "SEN", "SET", - "SEW", "SHE", "SHY", "SIN", "SIP", "SIR", "SIS", "SIT", "SKI", "SKY", - "SLY", "SO", "SOB", "SOD", "SON", "SOP", "SOW", "SOY", "SPA", "SPY", - "SUB", "SUD", "SUE", "SUM", "SUN", "SUP", "TAB", "TAD", "TAG", "TAN", - "TAP", "TAR", "TEA", "TED", "TEE", "TEN", "THE", "THY", "TIC", "TIE", - "TIM", "TIN", "TIP", "TO", "TOE", "TOG", "TOM", "TON", "TOO", "TOP", - "TOW", "TOY", "TRY", "TUB", "TUG", "TUM", "TUN", "TWO", "UN", "UP", - "US", "USE", "VAN", "VAT", "VET", "VIE", "WAD", "WAG", "WAR", "WAS", - "WAY", "WE", "WEB", "WED", "WEE", "WET", "WHO", "WHY", "WIN", "WIT", - "WOK", "WON", "WOO", "WOW", "WRY", "WU", "YAM", "YAP", "YAW", "YE", - "YEA", "YES", "YET", "YOU", "ABED", "ABEL", "ABET", "ABLE", "ABUT", - "ACHE", "ACID", "ACME", "ACRE", "ACTA", "ACTS", "ADAM", "ADDS", - "ADEN", "AFAR", "AFRO", "AGEE", "AHEM", "AHOY", "AIDA", "AIDE", - "AIDS", "AIRY", "AJAR", "AKIN", "ALAN", "ALEC", "ALGA", "ALIA", - "ALLY", "ALMA", "ALOE", "ALSO", "ALTO", "ALUM", "ALVA", "AMEN", - "AMES", "AMID", "AMMO", "AMOK", "AMOS", "AMRA", "ANDY", "ANEW", - "ANNA", "ANNE", "ANTE", "ANTI", "AQUA", "ARAB", "ARCH", "AREA", - "ARGO", "ARID", "ARMY", "ARTS", "ARTY", "ASIA", "ASKS", "ATOM", - "AUNT", "AURA", "AUTO", "AVER", "AVID", "AVIS", "AVON", "AVOW", - "AWAY", "AWRY", "BABE", "BABY", "BACH", "BACK", "BADE", "BAIL", - "BAIT", "BAKE", "BALD", "BALE", "BALI", "BALK", "BALL", "BALM", - "BAND", "BANE", "BANG", "BANK", "BARB", "BARD", "BARE", "BARK", - "BARN", "BARR", "BASE", "BASH", "BASK", "BASS", "BATE", "BATH", - "BAWD", "BAWL", "BEAD", "BEAK", "BEAM", "BEAN", "BEAR", "BEAT", - "BEAU", "BECK", "BEEF", "BEEN", "BEER", - "BEET", "BELA", "BELL", "BELT", "BEND", "BENT", "BERG", "BERN", - "BERT", "BESS", "BEST", "BETA", "BETH", "BHOY", "BIAS", "BIDE", - "BIEN", "BILE", "BILK", "BILL", "BIND", "BING", "BIRD", "BITE", - "BITS", "BLAB", "BLAT", "BLED", "BLEW", "BLOB", "BLOC", "BLOT", - "BLOW", "BLUE", "BLUM", "BLUR", "BOAR", "BOAT", "BOCA", "BOCK", - "BODE", "BODY", "BOGY", "BOHR", "BOIL", "BOLD", "BOLO", "BOLT", - "BOMB", "BONA", "BOND", "BONE", "BONG", "BONN", "BONY", "BOOK", - "BOOM", "BOON", "BOOT", "BORE", "BORG", "BORN", "BOSE", "BOSS", - "BOTH", "BOUT", "BOWL", "BOYD", "BRAD", "BRAE", "BRAG", "BRAN", - "BRAY", "BRED", "BREW", "BRIG", "BRIM", "BROW", "BUCK", "BUDD", - "BUFF", "BULB", "BULK", "BULL", "BUNK", "BUNT", "BUOY", "BURG", - "BURL", "BURN", "BURR", "BURT", "BURY", "BUSH", "BUSS", "BUST", - "BUSY", "BYTE", "CADY", "CAFE", "CAGE", "CAIN", "CAKE", "CALF", - "CALL", "CALM", "CAME", "CANE", "CANT", "CARD", "CARE", "CARL", - "CARR", "CART", "CASE", "CASH", "CASK", "CAST", "CAVE", "CEIL", - "CELL", "CENT", "CERN", "CHAD", "CHAR", "CHAT", "CHAW", "CHEF", - "CHEN", "CHEW", "CHIC", "CHIN", "CHOU", "CHOW", "CHUB", "CHUG", - "CHUM", "CITE", "CITY", "CLAD", "CLAM", "CLAN", "CLAW", "CLAY", - "CLOD", "CLOG", "CLOT", "CLUB", "CLUE", "COAL", "COAT", "COCA", - "COCK", "COCO", "CODA", "CODE", "CODY", "COED", "COIL", "COIN", - "COKE", "COLA", "COLD", "COLT", "COMA", "COMB", "COME", "COOK", - "COOL", "COON", "COOT", "CORD", "CORE", "CORK", "CORN", "COST", - "COVE", "COWL", "CRAB", "CRAG", "CRAM", "CRAY", "CREW", "CRIB", - "CROW", "CRUD", "CUBA", "CUBE", "CUFF", "CULL", "CULT", "CUNY", - "CURB", "CURD", "CURE", "CURL", "CURT", "CUTS", "DADE", "DALE", - "DAME", "DANA", "DANE", "DANG", "DANK", "DARE", "DARK", "DARN", - "DART", "DASH", "DATA", "DATE", "DAVE", "DAVY", "DAWN", "DAYS", - "DEAD", "DEAF", "DEAL", "DEAN", "DEAR", "DEBT", "DECK", "DEED", - "DEEM", "DEER", "DEFT", "DEFY", "DELL", "DENT", "DENY", "DESK", - "DIAL", "DICE", "DIED", "DIET", "DIME", "DINE", "DING", "DINT", - "DIRE", "DIRT", "DISC", "DISH", "DISK", "DIVE", "DOCK", "DOES", - "DOLE", "DOLL", "DOLT", "DOME", "DONE", "DOOM", "DOOR", "DORA", - "DOSE", "DOTE", "DOUG", "DOUR", "DOVE", "DOWN", "DRAB", "DRAG", - "DRAM", "DRAW", "DREW", "DRUB", "DRUG", "DRUM", "DUAL", "DUCK", - "DUCT", "DUEL", "DUET", "DUKE", "DULL", "DUMB", "DUNE", "DUNK", - "DUSK", "DUST", "DUTY", "EACH", "EARL", "EARN", "EASE", "EAST", - "EASY", "EBEN", "ECHO", "EDDY", "EDEN", "EDGE", "EDGY", "EDIT", - "EDNA", "EGAN", "ELAN", "ELBA", "ELLA", "ELSE", "EMIL", "EMIT", - "EMMA", "ENDS", "ERIC", "EROS", "EVEN", "EVER", "EVIL", "EYED", - "FACE", "FACT", "FADE", "FAIL", "FAIN", "FAIR", "FAKE", "FALL", - "FAME", "FANG", "FARM", "FAST", "FATE", "FAWN", "FEAR", "FEAT", - "FEED", "FEEL", "FEET", "FELL", "FELT", "FEND", "FERN", "FEST", - "FEUD", "FIEF", "FIGS", "FILE", "FILL", "FILM", "FIND", "FINE", - "FINK", "FIRE", "FIRM", "FISH", "FISK", "FIST", "FITS", "FIVE", - "FLAG", "FLAK", "FLAM", "FLAT", "FLAW", "FLEA", "FLED", "FLEW", - "FLIT", "FLOC", "FLOG", "FLOW", "FLUB", "FLUE", "FOAL", "FOAM", - "FOGY", "FOIL", "FOLD", "FOLK", "FOND", "FONT", "FOOD", "FOOL", - "FOOT", "FORD", "FORE", "FORK", "FORM", "FORT", "FOSS", "FOUL", - "FOUR", "FOWL", "FRAU", "FRAY", "FRED", "FREE", "FRET", "FREY", - "FROG", "FROM", "FUEL", "FULL", "FUME", "FUND", "FUNK", "FURY", - "FUSE", "FUSS", "GAFF", "GAGE", "GAIL", "GAIN", "GAIT", "GALA", - "GALE", "GALL", "GALT", "GAME", "GANG", "GARB", "GARY", "GASH", - "GATE", "GAUL", "GAUR", "GAVE", "GAWK", "GEAR", "GELD", "GENE", - "GENT", "GERM", "GETS", "GIBE", "GIFT", "GILD", "GILL", "GILT", - "GINA", "GIRD", "GIRL", "GIST", "GIVE", "GLAD", "GLEE", "GLEN", - "GLIB", "GLOB", "GLOM", "GLOW", "GLUE", "GLUM", "GLUT", "GOAD", - "GOAL", "GOAT", "GOER", "GOES", "GOLD", "GOLF", "GONE", "GONG", - "GOOD", "GOOF", "GORE", "GORY", "GOSH", "GOUT", "GOWN", "GRAB", - "GRAD", "GRAY", "GREG", "GREW", "GREY", "GRID", "GRIM", "GRIN", - "GRIT", "GROW", "GRUB", "GULF", "GULL", "GUNK", "GURU", "GUSH", - "GUST", "GWEN", "GWYN", "HAAG", "HAAS", "HACK", "HAIL", "HAIR", - "HALE", "HALF", "HALL", "HALO", "HALT", "HAND", "HANG", "HANK", - "HANS", "HARD", "HARK", "HARM", "HART", "HASH", "HAST", "HATE", - "HATH", "HAUL", "HAVE", "HAWK", "HAYS", "HEAD", "HEAL", "HEAR", - "HEAT", "HEBE", "HECK", "HEED", "HEEL", "HEFT", "HELD", "HELL", - "HELM", "HERB", "HERD", "HERE", "HERO", "HERS", "HESS", "HEWN", - "HICK", "HIDE", "HIGH", "HIKE", "HILL", "HILT", "HIND", "HINT", - "HIRE", "HISS", "HIVE", "HOBO", "HOCK", "HOFF", "HOLD", "HOLE", - "HOLM", "HOLT", "HOME", "HONE", "HONK", "HOOD", "HOOF", "HOOK", - "HOOT", "HORN", "HOSE", "HOST", "HOUR", "HOVE", "HOWE", "HOWL", - "HOYT", "HUCK", "HUED", "HUFF", "HUGE", "HUGH", "HUGO", "HULK", - "HULL", "HUNK", "HUNT", "HURD", "HURL", "HURT", "HUSH", "HYDE", - "HYMN", "IBIS", "ICON", "IDEA", "IDLE", "IFFY", "INCA", "INCH", - "INTO", "IONS", "IOTA", "IOWA", "IRIS", "IRMA", "IRON", "ISLE", - "ITCH", "ITEM", "IVAN", "JACK", "JADE", "JAIL", "JAKE", "JANE", - "JAVA", "JEAN", "JEFF", "JERK", "JESS", "JEST", "JIBE", "JILL", - "JILT", "JIVE", "JOAN", "JOBS", "JOCK", "JOEL", "JOEY", "JOHN", - "JOIN", "JOKE", "JOLT", "JOVE", "JUDD", "JUDE", "JUDO", "JUDY", - "JUJU", "JUKE", "JULY", "JUNE", "JUNK", "JUNO", "JURY", "JUST", - "JUTE", "KAHN", "KALE", "KANE", "KANT", "KARL", "KATE", "KEEL", - "KEEN", "KENO", "KENT", "KERN", "KERR", "KEYS", "KICK", "KILL", - "KIND", "KING", "KIRK", "KISS", "KITE", "KLAN", "KNEE", "KNEW", - "KNIT", "KNOB", "KNOT", "KNOW", "KOCH", "KONG", "KUDO", "KURD", - "KURT", "KYLE", "LACE", "LACK", "LACY", "LADY", "LAID", "LAIN", - "LAIR", "LAKE", "LAMB", "LAME", "LAND", "LANE", "LANG", "LARD", - "LARK", "LASS", "LAST", "LATE", "LAUD", "LAVA", "LAWN", "LAWS", - "LAYS", "LEAD", "LEAF", "LEAK", "LEAN", "LEAR", "LEEK", "LEER", - "LEFT", "LEND", "LENS", "LENT", "LEON", "LESK", "LESS", "LEST", - "LETS", "LIAR", "LICE", "LICK", "LIED", "LIEN", "LIES", "LIEU", - "LIFE", "LIFT", "LIKE", "LILA", "LILT", "LILY", "LIMA", "LIMB", - "LIME", "LIND", "LINE", "LINK", "LINT", "LION", "LISA", "LIST", - "LIVE", "LOAD", "LOAF", "LOAM", "LOAN", "LOCK", "LOFT", "LOGE", - "LOIS", "LOLA", "LONE", "LONG", "LOOK", "LOON", "LOOT", "LORD", - "LORE", "LOSE", "LOSS", "LOST", "LOUD", "LOVE", "LOWE", "LUCK", - "LUCY", "LUGE", "LUKE", "LULU", "LUND", "LUNG", "LURA", "LURE", - "LURK", "LUSH", "LUST", "LYLE", "LYNN", "LYON", "LYRA", "MACE", - "MADE", "MAGI", "MAID", "MAIL", "MAIN", "MAKE", "MALE", "MALI", - "MALL", "MALT", "MANA", "MANN", "MANY", "MARC", "MARE", "MARK", - "MARS", "MART", "MARY", "MASH", "MASK", "MASS", "MAST", "MATE", - "MATH", "MAUL", "MAYO", "MEAD", "MEAL", "MEAN", "MEAT", "MEEK", - "MEET", "MELD", "MELT", "MEMO", "MEND", "MENU", "MERT", "MESH", - "MESS", "MICE", "MIKE", "MILD", "MILE", "MILK", "MILL", "MILT", - "MIMI", "MIND", "MINE", "MINI", "MINK", "MINT", "MIRE", "MISS", - "MIST", "MITE", "MITT", "MOAN", "MOAT", "MOCK", "MODE", "MOLD", - "MOLE", "MOLL", "MOLT", "MONA", "MONK", "MONT", "MOOD", "MOON", - "MOOR", "MOOT", "MORE", "MORN", "MORT", "MOSS", "MOST", "MOTH", - "MOVE", "MUCH", "MUCK", "MUDD", "MUFF", "MULE", "MULL", "MURK", - "MUSH", "MUST", "MUTE", "MUTT", "MYRA", "MYTH", "NAGY", "NAIL", - "NAIR", "NAME", "NARY", "NASH", "NAVE", "NAVY", "NEAL", "NEAR", - "NEAT", "NECK", "NEED", "NEIL", "NELL", "NEON", "NERO", "NESS", - "NEST", "NEWS", "NEWT", "NIBS", "NICE", "NICK", "NILE", "NINA", - "NINE", "NOAH", "NODE", "NOEL", "NOLL", "NONE", "NOOK", "NOON", - "NORM", "NOSE", "NOTE", "NOUN", "NOVA", "NUDE", "NULL", "NUMB", - "OATH", "OBEY", "OBOE", "ODIN", "OHIO", "OILY", "OINT", "OKAY", - "OLAF", "OLDY", "OLGA", "OLIN", "OMAN", "OMEN", "OMIT", "ONCE", - "ONES", "ONLY", "ONTO", "ONUS", "ORAL", "ORGY", "OSLO", "OTIS", - "OTTO", "OUCH", "OUST", "OUTS", "OVAL", "OVEN", "OVER", "OWLY", - "OWNS", "QUAD", "QUIT", "QUOD", "RACE", "RACK", "RACY", "RAFT", - "RAGE", "RAID", "RAIL", "RAIN", "RAKE", "RANK", "RANT", "RARE", - "RASH", "RATE", "RAVE", "RAYS", "READ", "REAL", "REAM", "REAR", - "RECK", "REED", "REEF", "REEK", "REEL", "REID", "REIN", "RENA", - "REND", "RENT", "REST", "RICE", "RICH", "RICK", "RIDE", "RIFT", - "RILL", "RIME", "RING", "RINK", "RISE", "RISK", "RITE", "ROAD", - "ROAM", "ROAR", "ROBE", "ROCK", "RODE", "ROIL", "ROLL", "ROME", - "ROOD", "ROOF", "ROOK", "ROOM", "ROOT", "ROSA", "ROSE", "ROSS", - "ROSY", "ROTH", "ROUT", "ROVE", "ROWE", "ROWS", "RUBE", "RUBY", - "RUDE", "RUDY", "RUIN", "RULE", "RUNG", "RUNS", "RUNT", "RUSE", - "RUSH", "RUSK", "RUSS", "RUST", "RUTH", "SACK", "SAFE", "SAGE", - "SAID", "SAIL", "SALE", "SALK", "SALT", "SAME", "SAND", "SANE", - "SANG", "SANK", "SARA", "SAUL", "SAVE", "SAYS", "SCAN", "SCAR", - "SCAT", "SCOT", "SEAL", "SEAM", "SEAR", "SEAT", "SEED", "SEEK", - "SEEM", "SEEN", "SEES", "SELF", "SELL", "SEND", "SENT", "SETS", - "SEWN", "SHAG", "SHAM", "SHAW", "SHAY", "SHED", "SHIM", "SHIN", - "SHOD", "SHOE", "SHOT", "SHOW", "SHUN", "SHUT", "SICK", "SIDE", - "SIFT", "SIGH", "SIGN", "SILK", "SILL", "SILO", "SILT", "SINE", - "SING", "SINK", "SIRE", "SITE", "SITS", "SITU", "SKAT", "SKEW", - "SKID", "SKIM", "SKIN", "SKIT", "SLAB", "SLAM", "SLAT", "SLAY", - "SLED", "SLEW", "SLID", "SLIM", "SLIT", "SLOB", "SLOG", "SLOT", - "SLOW", "SLUG", "SLUM", "SLUR", "SMOG", "SMUG", "SNAG", "SNOB", - "SNOW", "SNUB", "SNUG", "SOAK", "SOAR", "SOCK", "SODA", "SOFA", - "SOFT", "SOIL", "SOLD", "SOME", "SONG", "SOON", "SOOT", "SORE", - "SORT", "SOUL", "SOUR", "SOWN", "STAB", "STAG", "STAN", "STAR", - "STAY", "STEM", "STEW", "STIR", "STOW", "STUB", "STUN", "SUCH", - "SUDS", "SUIT", "SULK", "SUMS", "SUNG", "SUNK", "SURE", "SURF", - "SWAB", "SWAG", "SWAM", "SWAN", "SWAT", "SWAY", "SWIM", "SWUM", - "TACK", "TACT", "TAIL", "TAKE", "TALE", "TALK", "TALL", "TANK", - "TASK", "TATE", "TAUT", "TEAL", "TEAM", "TEAR", "TECH", "TEEM", - "TEEN", "TEET", "TELL", "TEND", "TENT", "TERM", "TERN", "TESS", - "TEST", "THAN", "THAT", "THEE", "THEM", "THEN", "THEY", "THIN", - "THIS", "THUD", "THUG", "TICK", "TIDE", "TIDY", "TIED", "TIER", - "TILE", "TILL", "TILT", "TIME", "TINA", "TINE", "TINT", "TINY", - "TIRE", "TOAD", "TOGO", "TOIL", "TOLD", "TOLL", "TONE", "TONG", - "TONY", "TOOK", "TOOL", "TOOT", "TORE", "TORN", "TOTE", "TOUR", - "TOUT", "TOWN", "TRAG", "TRAM", "TRAY", "TREE", "TREK", "TRIG", - "TRIM", "TRIO", "TROD", "TROT", "TROY", "TRUE", "TUBA", "TUBE", - "TUCK", "TUFT", "TUNA", "TUNE", "TUNG", "TURF", "TURN", "TUSK", - "TWIG", "TWIN", "TWIT", "ULAN", "UNIT", "URGE", "USED", "USER", - "USES", "UTAH", "VAIL", "VAIN", "VALE", "VARY", "VASE", "VAST", - "VEAL", "VEDA", "VEIL", "VEIN", "VEND", "VENT", "VERB", "VERY", - "VETO", "VICE", "VIEW", "VINE", "VISE", "VOID", "VOLT", "VOTE", - "WACK", "WADE", "WAGE", "WAIL", "WAIT", "WAKE", "WALE", "WALK", - "WALL", "WALT", "WAND", "WANE", "WANG", "WANT", "WARD", "WARM", - "WARN", "WART", "WASH", "WAST", "WATS", "WATT", "WAVE", "WAVY", - "WAYS", "WEAK", "WEAL", "WEAN", "WEAR", "WEED", "WEEK", "WEIR", - "WELD", "WELL", "WELT", "WENT", "WERE", "WERT", "WEST", "WHAM", - "WHAT", "WHEE", "WHEN", "WHET", "WHOA", "WHOM", "WICK", "WIFE", - "WILD", "WILL", "WIND", "WINE", "WING", "WINK", "WINO", "WIRE", - "WISE", "WISH", "WITH", "WOLF", "WONT", "WOOD", "WOOL", "WORD", - "WORE", "WORK", "WORM", "WORN", "WOVE", "WRIT", "WYNN", "YALE", - "YANG", "YANK", "YARD", "YARN", "YAWL", "YAWN", "YEAH", "YEAR", - "YELL", "YOGA", "YOKE" ] - -if __name__=='__main__': - data = [('EB33F77EE73D4053', 'TIDE ITCH SLOW REIN RULE MOT'), - ('CCAC2AED591056BE4F90FD441C534766', - 'RASH BUSH MILK LOOK BAD BRIM AVID GAFF BAIT ROT POD LOVE'), - ('EFF81F9BFBC65350920CDD7416DE8009', - 'TROD MUTE TAIL WARM CHAR KONG HAAG CITY BORE O TEAL AWL') - ] - - for key, words in data: - print 'Trying key', key - key=binascii.a2b_hex(key) - w2=key_to_english(key) - if w2!=words: - print 'key_to_english fails on key', repr(key), ', producing', str(w2) - k2=english_to_key(words) - if k2!=key: - print 'english_to_key fails on key', repr(key), ', producing', repr(k2) - - diff --git a/gdata/analytics/Crypto/Util/__init__.py b/gdata/analytics/Crypto/Util/__init__.py deleted file mode 100644 index 0d14768181..0000000000 --- a/gdata/analytics/Crypto/Util/__init__.py +++ /dev/null @@ -1,16 +0,0 @@ -"""Miscellaneous modules - -Contains useful modules that don't belong into any of the -other Crypto.* subpackages. - -Crypto.Util.number Number-theoretic functions (primality testing, etc.) -Crypto.Util.randpool Random number generation -Crypto.Util.RFC1751 Converts between 128-bit keys and human-readable - strings of words. - -""" - -__all__ = ['randpool', 'RFC1751', 'number'] - -__revision__ = "$Id: __init__.py,v 1.4 2003/02/28 15:26:00 akuchling Exp $" - diff --git a/gdata/analytics/Crypto/Util/number.py b/gdata/analytics/Crypto/Util/number.py deleted file mode 100644 index 9d50563e90..0000000000 --- a/gdata/analytics/Crypto/Util/number.py +++ /dev/null @@ -1,201 +0,0 @@ -# -# number.py : Number-theoretic functions -# -# Part of the Python Cryptography Toolkit -# -# Distribute and use freely; there are no restrictions on further -# dissemination and usage except those imposed by the laws of your -# country of residence. This software is provided "as is" without -# warranty of fitness for use or suitability for any purpose, express -# or implied. Use at your own risk or not at all. -# - -__revision__ = "$Id: number.py,v 1.13 2003/04/04 18:21:07 akuchling Exp $" - -bignum = long -try: - from Crypto.PublicKey import _fastmath -except ImportError: - _fastmath = None - -# Commented out and replaced with faster versions below -## def long2str(n): -## s='' -## while n>0: -## s=chr(n & 255)+s -## n=n>>8 -## return s - -## import types -## def str2long(s): -## if type(s)!=types.StringType: return s # Integers will be left alone -## return reduce(lambda x,y : x*256+ord(y), s, 0L) - -def size (N): - """size(N:long) : int - Returns the size of the number N in bits. - """ - bits, power = 0,1L - while N >= power: - bits += 1 - power = power << 1 - return bits - -def getRandomNumber(N, randfunc): - """getRandomNumber(N:int, randfunc:callable):long - Return an N-bit random number.""" - - S = randfunc(N/8) - odd_bits = N % 8 - if odd_bits != 0: - char = ord(randfunc(1)) >> (8-odd_bits) - S = chr(char) + S - value = bytes_to_long(S) - value |= 2L ** (N-1) # Ensure high bit is set - assert size(value) >= N - return value - -def GCD(x,y): - """GCD(x:long, y:long): long - Return the GCD of x and y. - """ - x = abs(x) ; y = abs(y) - while x > 0: - x, y = y % x, x - return y - -def inverse(u, v): - """inverse(u:long, u:long):long - Return the inverse of u mod v. - """ - u3, v3 = long(u), long(v) - u1, v1 = 1L, 0L - while v3 > 0: - q=u3 / v3 - u1, v1 = v1, u1 - v1*q - u3, v3 = v3, u3 - v3*q - while u1<0: - u1 = u1 + v - return u1 - -# Given a number of bits to generate and a random generation function, -# find a prime number of the appropriate size. - -def getPrime(N, randfunc): - """getPrime(N:int, randfunc:callable):long - Return a random N-bit prime number. - """ - - number=getRandomNumber(N, randfunc) | 1 - while (not isPrime(number)): - number=number+2 - return number - -def isPrime(N): - """isPrime(N:long):bool - Return true if N is prime. - """ - if N == 1: - return 0 - if N in sieve: - return 1 - for i in sieve: - if (N % i)==0: - return 0 - - # Use the accelerator if available - if _fastmath is not None: - return _fastmath.isPrime(N) - - # Compute the highest bit that's set in N - N1 = N - 1L - n = 1L - while (n> 1L - - # Rabin-Miller test - for c in sieve[:7]: - a=long(c) ; d=1L ; t=n - while (t): # Iterate over the bits in N1 - x=(d*d) % N - if x==1L and d!=1L and d!=N1: - return 0 # Square root of 1 found - if N1 & t: - d=(x*a) % N - else: - d=x - t = t >> 1L - if d!=1L: - return 0 - return 1 - -# Small primes used for checking primality; these are all the primes -# less than 256. This should be enough to eliminate most of the odd -# numbers before needing to do a Rabin-Miller test at all. - -sieve=[2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, - 61, 67, 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, - 131, 137, 139, 149, 151, 157, 163, 167, 173, 179, 181, 191, 193, - 197, 199, 211, 223, 227, 229, 233, 239, 241, 251] - -# Improved conversion functions contributed by Barry Warsaw, after -# careful benchmarking - -import struct - -def long_to_bytes(n, blocksize=0): - """long_to_bytes(n:long, blocksize:int) : string - Convert a long integer to a byte string. - - If optional blocksize is given and greater than zero, pad the front of the - byte string with binary zeros so that the length is a multiple of - blocksize. - """ - # after much testing, this algorithm was deemed to be the fastest - s = '' - n = long(n) - pack = struct.pack - while n > 0: - s = pack('>I', n & 0xffffffffL) + s - n = n >> 32 - # strip off leading zeros - for i in range(len(s)): - if s[i] != '\000': - break - else: - # only happens when n == 0 - s = '\000' - i = 0 - s = s[i:] - # add back some pad bytes. this could be done more efficiently w.r.t. the - # de-padding being done above, but sigh... - if blocksize > 0 and len(s) % blocksize: - s = (blocksize - len(s) % blocksize) * '\000' + s - return s - -def bytes_to_long(s): - """bytes_to_long(string) : long - Convert a byte string to a long integer. - - This is (essentially) the inverse of long_to_bytes(). - """ - acc = 0L - unpack = struct.unpack - length = len(s) - if length % 4: - extra = (4 - length % 4) - s = '\000' * extra + s - length = length + extra - for i in range(0, length, 4): - acc = (acc << 32) + unpack('>I', s[i:i+4])[0] - return acc - -# For backwards compatibility... -import warnings -def long2str(n, blocksize=0): - warnings.warn("long2str() has been replaced by long_to_bytes()") - return long_to_bytes(n, blocksize) -def str2long(s): - warnings.warn("str2long() has been replaced by bytes_to_long()") - return bytes_to_long(s) diff --git a/gdata/analytics/Crypto/Util/randpool.py b/gdata/analytics/Crypto/Util/randpool.py deleted file mode 100644 index 467501c544..0000000000 --- a/gdata/analytics/Crypto/Util/randpool.py +++ /dev/null @@ -1,421 +0,0 @@ -# -# randpool.py : Cryptographically strong random number generation -# -# Part of the Python Cryptography Toolkit -# -# Distribute and use freely; there are no restrictions on further -# dissemination and usage except those imposed by the laws of your -# country of residence. This software is provided "as is" without -# warranty of fitness for use or suitability for any purpose, express -# or implied. Use at your own risk or not at all. -# - -__revision__ = "$Id: randpool.py,v 1.14 2004/05/06 12:56:54 akuchling Exp $" - -import time, array, types, warnings, os.path -from Crypto.Util.number import long_to_bytes -try: - import Crypto.Util.winrandom as winrandom -except: - winrandom = None - -STIRNUM = 3 - -class RandomPool: - """randpool.py : Cryptographically strong random number generation. - - The implementation here is similar to the one in PGP. To be - cryptographically strong, it must be difficult to determine the RNG's - output, whether in the future or the past. This is done by using - a cryptographic hash function to "stir" the random data. - - Entropy is gathered in the same fashion as PGP; the highest-resolution - clock around is read and the data is added to the random number pool. - A conservative estimate of the entropy is then kept. - - If a cryptographically secure random source is available (/dev/urandom - on many Unixes, Windows CryptGenRandom on most Windows), then use - it. - - Instance Attributes: - bits : int - Maximum size of pool in bits - bytes : int - Maximum size of pool in bytes - entropy : int - Number of bits of entropy in this pool. - - Methods: - add_event([s]) : add some entropy to the pool - get_bytes(int) : get N bytes of random data - randomize([N]) : get N bytes of randomness from external source - """ - - - def __init__(self, numbytes = 160, cipher=None, hash=None): - if hash is None: - from Crypto.Hash import SHA as hash - - # The cipher argument is vestigial; it was removed from - # version 1.1 so RandomPool would work even in the limited - # exportable subset of the code - if cipher is not None: - warnings.warn("'cipher' parameter is no longer used") - - if isinstance(hash, types.StringType): - # ugly hack to force __import__ to give us the end-path module - hash = __import__('Crypto.Hash.'+hash, - None, None, ['new']) - warnings.warn("'hash' parameter should now be a hashing module") - - self.bytes = numbytes - self.bits = self.bytes*8 - self.entropy = 0 - self._hash = hash - - # Construct an array to hold the random pool, - # initializing it to 0. - self._randpool = array.array('B', [0]*self.bytes) - - self._event1 = self._event2 = 0 - self._addPos = 0 - self._getPos = hash.digest_size - self._lastcounter=time.time() - self.__counter = 0 - - self._measureTickSize() # Estimate timer resolution - self._randomize() - - def _updateEntropyEstimate(self, nbits): - self.entropy += nbits - if self.entropy < 0: - self.entropy = 0 - elif self.entropy > self.bits: - self.entropy = self.bits - - def _randomize(self, N = 0, devname = '/dev/urandom'): - """_randomize(N, DEVNAME:device-filepath) - collects N bits of randomness from some entropy source (e.g., - /dev/urandom on Unixes that have it, Windows CryptoAPI - CryptGenRandom, etc) - DEVNAME is optional, defaults to /dev/urandom. You can change it - to /dev/random if you want to block till you get enough - entropy. - """ - data = '' - if N <= 0: - nbytes = int((self.bits - self.entropy)/8+0.5) - else: - nbytes = int(N/8+0.5) - if winrandom: - # Windows CryptGenRandom provides random data. - data = winrandom.new().get_bytes(nbytes) - elif os.path.exists(devname): - # Many OSes support a /dev/urandom device - try: - f=open(devname) - data=f.read(nbytes) - f.close() - except IOError, (num, msg): - if num!=2: raise IOError, (num, msg) - # If the file wasn't found, ignore the error - if data: - self._addBytes(data) - # Entropy estimate: The number of bits of - # data obtained from the random source. - self._updateEntropyEstimate(8*len(data)) - self.stir_n() # Wash the random pool - - def randomize(self, N=0): - """randomize(N:int) - use the class entropy source to get some entropy data. - This is overridden by KeyboardRandomize(). - """ - return self._randomize(N) - - def stir_n(self, N = STIRNUM): - """stir_n(N) - stirs the random pool N times - """ - for i in xrange(N): - self.stir() - - def stir (self, s = ''): - """stir(s:string) - Mix up the randomness pool. This will call add_event() twice, - but out of paranoia the entropy attribute will not be - increased. The optional 's' parameter is a string that will - be hashed with the randomness pool. - """ - - entropy=self.entropy # Save inital entropy value - self.add_event() - - # Loop over the randomness pool: hash its contents - # along with a counter, and add the resulting digest - # back into the pool. - for i in range(self.bytes / self._hash.digest_size): - h = self._hash.new(self._randpool) - h.update(str(self.__counter) + str(i) + str(self._addPos) + s) - self._addBytes( h.digest() ) - self.__counter = (self.__counter + 1) & 0xFFFFffffL - - self._addPos, self._getPos = 0, self._hash.digest_size - self.add_event() - - # Restore the old value of the entropy. - self.entropy=entropy - - - def get_bytes (self, N): - """get_bytes(N:int) : string - Return N bytes of random data. - """ - - s='' - i, pool = self._getPos, self._randpool - h=self._hash.new() - dsize = self._hash.digest_size - num = N - while num > 0: - h.update( self._randpool[i:i+dsize] ) - s = s + h.digest() - num = num - dsize - i = (i + dsize) % self.bytes - if i>1, bits+1 - if bits>8: bits=8 - - self._event1, self._event2 = event, self._event1 - - self._updateEntropyEstimate(bits) - return bits - - # Private functions - def _noise(self): - # Adds a bit of noise to the random pool, by adding in the - # current time and CPU usage of this process. - # The difference from the previous call to _noise() is taken - # in an effort to estimate the entropy. - t=time.time() - delta = (t - self._lastcounter)/self._ticksize*1e6 - self._lastcounter = t - self._addBytes(long_to_bytes(long(1000*time.time()))) - self._addBytes(long_to_bytes(long(1000*time.clock()))) - self._addBytes(long_to_bytes(long(1000*time.time()))) - self._addBytes(long_to_bytes(long(delta))) - - # Reduce delta to a maximum of 8 bits so we don't add too much - # entropy as a result of this call. - delta=delta % 0xff - return int(delta) - - - def _measureTickSize(self): - # _measureTickSize() tries to estimate a rough average of the - # resolution of time that you can see from Python. It does - # this by measuring the time 100 times, computing the delay - # between measurements, and taking the median of the resulting - # list. (We also hash all the times and add them to the pool) - interval = [None] * 100 - h = self._hash.new(`(id(self),id(interval))`) - - # Compute 100 differences - t=time.time() - h.update(`t`) - i = 0 - j = 0 - while i < 100: - t2=time.time() - h.update(`(i,j,t2)`) - j += 1 - delta=int((t2-t)*1e6) - if delta: - interval[i] = delta - i += 1 - t=t2 - - # Take the median of the array of intervals - interval.sort() - self._ticksize=interval[len(interval)/2] - h.update(`(interval,self._ticksize)`) - # mix in the measurement times and wash the random pool - self.stir(h.digest()) - - def _addBytes(self, s): - "XOR the contents of the string S into the random pool" - i, pool = self._addPos, self._randpool - for j in range(0, len(s)): - pool[i]=pool[i] ^ ord(s[j]) - i=(i+1) % self.bytes - self._addPos = i - - # Deprecated method names: remove in PCT 2.1 or later. - def getBytes(self, N): - warnings.warn("getBytes() method replaced by get_bytes()", - DeprecationWarning) - return self.get_bytes(N) - - def addEvent (self, event, s=""): - warnings.warn("addEvent() method replaced by add_event()", - DeprecationWarning) - return self.add_event(s + str(event)) - -class PersistentRandomPool (RandomPool): - def __init__ (self, filename=None, *args, **kwargs): - RandomPool.__init__(self, *args, **kwargs) - self.filename = filename - if filename: - try: - # the time taken to open and read the file might have - # a little disk variability, modulo disk/kernel caching... - f=open(filename, 'rb') - self.add_event() - data = f.read() - self.add_event() - # mix in the data from the file and wash the random pool - self.stir(data) - f.close() - except IOError: - # Oh, well; the file doesn't exist or is unreadable, so - # we'll just ignore it. - pass - - def save(self): - if self.filename == "": - raise ValueError, "No filename set for this object" - # wash the random pool before save, provides some forward secrecy for - # old values of the pool. - self.stir_n() - f=open(self.filename, 'wb') - self.add_event() - f.write(self._randpool.tostring()) - f.close() - self.add_event() - # wash the pool again, provide some protection for future values - self.stir() - -# non-echoing Windows keyboard entry -_kb = 0 -if not _kb: - try: - import msvcrt - class KeyboardEntry: - def getch(self): - c = msvcrt.getch() - if c in ('\000', '\xe0'): - # function key - c += msvcrt.getch() - return c - def close(self, delay = 0): - if delay: - time.sleep(delay) - while msvcrt.kbhit(): - msvcrt.getch() - _kb = 1 - except: - pass - -# non-echoing Posix keyboard entry -if not _kb: - try: - import termios - class KeyboardEntry: - def __init__(self, fd = 0): - self._fd = fd - self._old = termios.tcgetattr(fd) - new = termios.tcgetattr(fd) - new[3]=new[3] & ~termios.ICANON & ~termios.ECHO - termios.tcsetattr(fd, termios.TCSANOW, new) - def getch(self): - termios.tcflush(0, termios.TCIFLUSH) # XXX Leave this in? - return os.read(self._fd, 1) - def close(self, delay = 0): - if delay: - time.sleep(delay) - termios.tcflush(self._fd, termios.TCIFLUSH) - termios.tcsetattr(self._fd, termios.TCSAFLUSH, self._old) - _kb = 1 - except: - pass - -class KeyboardRandomPool (PersistentRandomPool): - def __init__(self, *args, **kwargs): - PersistentRandomPool.__init__(self, *args, **kwargs) - - def randomize(self, N = 0): - "Adds N bits of entropy to random pool. If N is 0, fill up pool." - import os, string, time - if N <= 0: - bits = self.bits - self.entropy - else: - bits = N*8 - if bits == 0: - return - print bits,'bits of entropy are now required. Please type on the keyboard' - print 'until enough randomness has been accumulated.' - kb = KeyboardEntry() - s='' # We'll save the characters typed and add them to the pool. - hash = self._hash - e = 0 - try: - while e < bits: - temp=str(bits-e).rjust(6) - os.write(1, temp) - s=s+kb.getch() - e += self.add_event(s) - os.write(1, 6*chr(8)) - self.add_event(s+hash.new(s).digest() ) - finally: - kb.close() - print '\n\007 Enough. Please wait a moment.\n' - self.stir_n() # wash the random pool. - kb.close(4) - -if __name__ == '__main__': - pool = RandomPool() - print 'random pool entropy', pool.entropy, 'bits' - pool.add_event('something') - print `pool.get_bytes(100)` - import tempfile, os - fname = tempfile.mktemp() - pool = KeyboardRandomPool(filename=fname) - print 'keyboard random pool entropy', pool.entropy, 'bits' - pool.randomize() - print 'keyboard random pool entropy', pool.entropy, 'bits' - pool.randomize(128) - pool.save() - saved = open(fname, 'rb').read() - print 'saved', `saved` - print 'pool ', `pool._randpool.tostring()` - newpool = PersistentRandomPool(fname) - print 'persistent random pool entropy', pool.entropy, 'bits' - os.remove(fname) diff --git a/gdata/analytics/Crypto/Util/test.py b/gdata/analytics/Crypto/Util/test.py deleted file mode 100644 index 7b23e9f5e4..0000000000 --- a/gdata/analytics/Crypto/Util/test.py +++ /dev/null @@ -1,453 +0,0 @@ -# -# test.py : Functions used for testing the modules -# -# Part of the Python Cryptography Toolkit -# -# Distribute and use freely; there are no restrictions on further -# dissemination and usage except those imposed by the laws of your -# country of residence. This software is provided "as is" without -# warranty of fitness for use or suitability for any purpose, express -# or implied. Use at your own risk or not at all. -# - -__revision__ = "$Id: test.py,v 1.16 2004/08/13 22:24:18 akuchling Exp $" - -import binascii -import string -import testdata - -from Crypto.Cipher import * - -def die(string): - import sys - print '***ERROR: ', string -# sys.exit(0) # Will default to continuing onward... - -def print_timing (size, delta, verbose): - if verbose: - if delta == 0: - print 'Unable to measure time -- elapsed time too small' - else: - print '%.2f K/sec' % (size/delta) - -def exerciseBlockCipher(cipher, verbose): - import string, time - try: - ciph = eval(cipher) - except NameError: - print cipher, 'module not available' - return None - print cipher+ ':' - str='1' # Build 128K of test data - for i in xrange(0, 17): - str=str+str - if ciph.key_size==0: ciph.key_size=16 - password = 'password12345678Extra text for password'[0:ciph.key_size] - IV = 'Test IV Test IV Test IV Test'[0:ciph.block_size] - - if verbose: print ' ECB mode:', - obj=ciph.new(password, ciph.MODE_ECB) - if obj.block_size != ciph.block_size: - die("Module and cipher object block_size don't match") - - text='1234567812345678'[0:ciph.block_size] - c=obj.encrypt(text) - if (obj.decrypt(c)!=text): die('Error encrypting "'+text+'"') - text='KuchlingKuchling'[0:ciph.block_size] - c=obj.encrypt(text) - if (obj.decrypt(c)!=text): die('Error encrypting "'+text+'"') - text='NotTodayNotEver!'[0:ciph.block_size] - c=obj.encrypt(text) - if (obj.decrypt(c)!=text): die('Error encrypting "'+text+'"') - - start=time.time() - s=obj.encrypt(str) - s2=obj.decrypt(s) - end=time.time() - if (str!=s2): - die('Error in resulting plaintext from ECB mode') - print_timing(256, end-start, verbose) - del obj - - if verbose: print ' CFB mode:', - obj1=ciph.new(password, ciph.MODE_CFB, IV) - obj2=ciph.new(password, ciph.MODE_CFB, IV) - start=time.time() - ciphertext=obj1.encrypt(str[0:65536]) - plaintext=obj2.decrypt(ciphertext) - end=time.time() - if (plaintext!=str[0:65536]): - die('Error in resulting plaintext from CFB mode') - print_timing(64, end-start, verbose) - del obj1, obj2 - - if verbose: print ' CBC mode:', - obj1=ciph.new(password, ciph.MODE_CBC, IV) - obj2=ciph.new(password, ciph.MODE_CBC, IV) - start=time.time() - ciphertext=obj1.encrypt(str) - plaintext=obj2.decrypt(ciphertext) - end=time.time() - if (plaintext!=str): - die('Error in resulting plaintext from CBC mode') - print_timing(256, end-start, verbose) - del obj1, obj2 - - if verbose: print ' PGP mode:', - obj1=ciph.new(password, ciph.MODE_PGP, IV) - obj2=ciph.new(password, ciph.MODE_PGP, IV) - start=time.time() - ciphertext=obj1.encrypt(str) - plaintext=obj2.decrypt(ciphertext) - end=time.time() - if (plaintext!=str): - die('Error in resulting plaintext from PGP mode') - print_timing(256, end-start, verbose) - del obj1, obj2 - - if verbose: print ' OFB mode:', - obj1=ciph.new(password, ciph.MODE_OFB, IV) - obj2=ciph.new(password, ciph.MODE_OFB, IV) - start=time.time() - ciphertext=obj1.encrypt(str) - plaintext=obj2.decrypt(ciphertext) - end=time.time() - if (plaintext!=str): - die('Error in resulting plaintext from OFB mode') - print_timing(256, end-start, verbose) - del obj1, obj2 - - def counter(length=ciph.block_size): - return length * 'a' - - if verbose: print ' CTR mode:', - obj1=ciph.new(password, ciph.MODE_CTR, counter=counter) - obj2=ciph.new(password, ciph.MODE_CTR, counter=counter) - start=time.time() - ciphertext=obj1.encrypt(str) - plaintext=obj2.decrypt(ciphertext) - end=time.time() - if (plaintext!=str): - die('Error in resulting plaintext from CTR mode') - print_timing(256, end-start, verbose) - del obj1, obj2 - - # Test the IV handling - if verbose: print ' Testing IV handling' - obj1=ciph.new(password, ciph.MODE_CBC, IV) - plaintext='Test'*(ciph.block_size/4)*3 - ciphertext1=obj1.encrypt(plaintext) - obj1.IV=IV - ciphertext2=obj1.encrypt(plaintext) - if ciphertext1!=ciphertext2: - die('Error in setting IV') - - # Test keyword arguments - obj1=ciph.new(key=password) - obj1=ciph.new(password, mode=ciph.MODE_CBC) - obj1=ciph.new(mode=ciph.MODE_CBC, key=password) - obj1=ciph.new(IV=IV, mode=ciph.MODE_CBC, key=password) - - return ciph - -def exerciseStreamCipher(cipher, verbose): - import string, time - try: - ciph = eval(cipher) - except (NameError): - print cipher, 'module not available' - return None - print cipher + ':', - str='1' # Build 128K of test data - for i in xrange(0, 17): - str=str+str - key_size = ciph.key_size or 16 - password = 'password12345678Extra text for password'[0:key_size] - - obj1=ciph.new(password) - obj2=ciph.new(password) - if obj1.block_size != ciph.block_size: - die("Module and cipher object block_size don't match") - if obj1.key_size != ciph.key_size: - die("Module and cipher object key_size don't match") - - text='1234567812345678Python' - c=obj1.encrypt(text) - if (obj2.decrypt(c)!=text): die('Error encrypting "'+text+'"') - text='B1FF I2 A R3A11Y |<00L D00D!!!!!' - c=obj1.encrypt(text) - if (obj2.decrypt(c)!=text): die('Error encrypting "'+text+'"') - text='SpamSpamSpamSpamSpamSpamSpamSpamSpam' - c=obj1.encrypt(text) - if (obj2.decrypt(c)!=text): die('Error encrypting "'+text+'"') - - start=time.time() - s=obj1.encrypt(str) - str=obj2.decrypt(s) - end=time.time() - print_timing(256, end-start, verbose) - del obj1, obj2 - - return ciph - -def TestStreamModules(args=['arc4', 'XOR'], verbose=1): - import sys, string - args=map(string.lower, args) - - if 'arc4' in args: - # Test ARC4 stream cipher - arc4=exerciseStreamCipher('ARC4', verbose) - if (arc4!=None): - for entry in testdata.arc4: - key,plain,cipher=entry - key=binascii.a2b_hex(key) - plain=binascii.a2b_hex(plain) - cipher=binascii.a2b_hex(cipher) - obj=arc4.new(key) - ciphertext=obj.encrypt(plain) - if (ciphertext!=cipher): - die('ARC4 failed on entry '+`entry`) - - if 'xor' in args: - # Test XOR stream cipher - XOR=exerciseStreamCipher('XOR', verbose) - if (XOR!=None): - for entry in testdata.xor: - key,plain,cipher=entry - key=binascii.a2b_hex(key) - plain=binascii.a2b_hex(plain) - cipher=binascii.a2b_hex(cipher) - obj=XOR.new(key) - ciphertext=obj.encrypt(plain) - if (ciphertext!=cipher): - die('XOR failed on entry '+`entry`) - - -def TestBlockModules(args=['aes', 'arc2', 'des', 'blowfish', 'cast', 'des3', - 'idea', 'rc5'], - verbose=1): - import string - args=map(string.lower, args) - if 'aes' in args: - ciph=exerciseBlockCipher('AES', verbose) # AES - if (ciph!=None): - if verbose: print ' Verifying against test suite...' - for entry in testdata.aes: - key,plain,cipher=entry - key=binascii.a2b_hex(key) - plain=binascii.a2b_hex(plain) - cipher=binascii.a2b_hex(cipher) - obj=ciph.new(key, ciph.MODE_ECB) - ciphertext=obj.encrypt(plain) - if (ciphertext!=cipher): - die('AES failed on entry '+`entry`) - for i in ciphertext: - if verbose: print hex(ord(i)), - if verbose: print - - for entry in testdata.aes_modes: - mode, key, plain, cipher, kw = entry - key=binascii.a2b_hex(key) - plain=binascii.a2b_hex(plain) - cipher=binascii.a2b_hex(cipher) - obj=ciph.new(key, mode, **kw) - obj2=ciph.new(key, mode, **kw) - ciphertext=obj.encrypt(plain) - if (ciphertext!=cipher): - die('AES encrypt failed on entry '+`entry`) - for i in ciphertext: - if verbose: print hex(ord(i)), - if verbose: print - - plain2=obj2.decrypt(ciphertext) - if plain2!=plain: - die('AES decrypt failed on entry '+`entry`) - for i in plain2: - if verbose: print hex(ord(i)), - if verbose: print - - - if 'arc2' in args: - ciph=exerciseBlockCipher('ARC2', verbose) # Alleged RC2 - if (ciph!=None): - if verbose: print ' Verifying against test suite...' - for entry in testdata.arc2: - key,plain,cipher=entry - key=binascii.a2b_hex(key) - plain=binascii.a2b_hex(plain) - cipher=binascii.a2b_hex(cipher) - obj=ciph.new(key, ciph.MODE_ECB) - ciphertext=obj.encrypt(plain) - if (ciphertext!=cipher): - die('ARC2 failed on entry '+`entry`) - for i in ciphertext: - if verbose: print hex(ord(i)), - print - - if 'blowfish' in args: - ciph=exerciseBlockCipher('Blowfish',verbose)# Bruce Schneier's Blowfish cipher - if (ciph!=None): - if verbose: print ' Verifying against test suite...' - for entry in testdata.blowfish: - key,plain,cipher=entry - key=binascii.a2b_hex(key) - plain=binascii.a2b_hex(plain) - cipher=binascii.a2b_hex(cipher) - obj=ciph.new(key, ciph.MODE_ECB) - ciphertext=obj.encrypt(plain) - if (ciphertext!=cipher): - die('Blowfish failed on entry '+`entry`) - for i in ciphertext: - if verbose: print hex(ord(i)), - if verbose: print - - if 'cast' in args: - ciph=exerciseBlockCipher('CAST', verbose) # CAST-128 - if (ciph!=None): - if verbose: print ' Verifying against test suite...' - for entry in testdata.cast: - key,plain,cipher=entry - key=binascii.a2b_hex(key) - plain=binascii.a2b_hex(plain) - cipher=binascii.a2b_hex(cipher) - obj=ciph.new(key, ciph.MODE_ECB) - ciphertext=obj.encrypt(plain) - if (ciphertext!=cipher): - die('CAST failed on entry '+`entry`) - for i in ciphertext: - if verbose: print hex(ord(i)), - if verbose: print - - if 0: - # The full-maintenance test; it requires 4 million encryptions, - # and correspondingly is quite time-consuming. I've disabled - # it; it's faster to compile block/cast.c with -DTEST and run - # the resulting program. - a = b = '\x01\x23\x45\x67\x12\x34\x56\x78\x23\x45\x67\x89\x34\x56\x78\x9A' - - for i in range(0, 1000000): - obj = cast.new(b, cast.MODE_ECB) - a = obj.encrypt(a[:8]) + obj.encrypt(a[-8:]) - obj = cast.new(a, cast.MODE_ECB) - b = obj.encrypt(b[:8]) + obj.encrypt(b[-8:]) - - if a!="\xEE\xA9\xD0\xA2\x49\xFD\x3B\xA6\xB3\x43\x6F\xB8\x9D\x6D\xCA\x92": - if verbose: print 'CAST test failed: value of "a" doesn\'t match' - if b!="\xB2\xC9\x5E\xB0\x0C\x31\xAD\x71\x80\xAC\x05\xB8\xE8\x3D\x69\x6E": - if verbose: print 'CAST test failed: value of "b" doesn\'t match' - - if 'des' in args: - # Test/benchmark DES block cipher - des=exerciseBlockCipher('DES', verbose) - if (des!=None): - # Various tests taken from the DES library packaged with Kerberos V4 - obj=des.new(binascii.a2b_hex('0123456789abcdef'), des.MODE_ECB) - s=obj.encrypt('Now is t') - if (s!=binascii.a2b_hex('3fa40e8a984d4815')): - die('DES fails test 1') - obj=des.new(binascii.a2b_hex('08192a3b4c5d6e7f'), des.MODE_ECB) - s=obj.encrypt('\000\000\000\000\000\000\000\000') - if (s!=binascii.a2b_hex('25ddac3e96176467')): - die('DES fails test 2') - obj=des.new(binascii.a2b_hex('0123456789abcdef'), des.MODE_CBC, - binascii.a2b_hex('1234567890abcdef')) - s=obj.encrypt("Now is the time for all ") - if (s!=binascii.a2b_hex('e5c7cdde872bf27c43e934008c389c0f683788499a7c05f6')): - die('DES fails test 3') - obj=des.new(binascii.a2b_hex('0123456789abcdef'), des.MODE_CBC, - binascii.a2b_hex('fedcba9876543210')) - s=obj.encrypt("7654321 Now is the time for \000\000\000\000") - if (s!=binascii.a2b_hex("ccd173ffab2039f4acd8aefddfd8a1eb468e91157888ba681d269397f7fe62b4")): - die('DES fails test 4') - del obj,s - - # R. Rivest's test: see http://theory.lcs.mit.edu/~rivest/destest.txt - x=binascii.a2b_hex('9474B8E8C73BCA7D') - for i in range(0, 16): - obj=des.new(x, des.MODE_ECB) - if (i & 1): x=obj.decrypt(x) - else: x=obj.encrypt(x) - if x!=binascii.a2b_hex('1B1A2DDB4C642438'): - die("DES fails Rivest's test") - - if verbose: print ' Verifying against test suite...' - for entry in testdata.des: - key,plain,cipher=entry - key=binascii.a2b_hex(key) - plain=binascii.a2b_hex(plain) - cipher=binascii.a2b_hex(cipher) - obj=des.new(key, des.MODE_ECB) - ciphertext=obj.encrypt(plain) - if (ciphertext!=cipher): - die('DES failed on entry '+`entry`) - for entry in testdata.des_cbc: - key, iv, plain, cipher=entry - key, iv, cipher=binascii.a2b_hex(key),binascii.a2b_hex(iv),binascii.a2b_hex(cipher) - obj1=des.new(key, des.MODE_CBC, iv) - obj2=des.new(key, des.MODE_CBC, iv) - ciphertext=obj1.encrypt(plain) - if (ciphertext!=cipher): - die('DES CBC mode failed on entry '+`entry`) - - if 'des3' in args: - ciph=exerciseBlockCipher('DES3', verbose) # Triple DES - if (ciph!=None): - if verbose: print ' Verifying against test suite...' - for entry in testdata.des3: - key,plain,cipher=entry - key=binascii.a2b_hex(key) - plain=binascii.a2b_hex(plain) - cipher=binascii.a2b_hex(cipher) - obj=ciph.new(key, ciph.MODE_ECB) - ciphertext=obj.encrypt(plain) - if (ciphertext!=cipher): - die('DES3 failed on entry '+`entry`) - for i in ciphertext: - if verbose: print hex(ord(i)), - if verbose: print - for entry in testdata.des3_cbc: - key, iv, plain, cipher=entry - key, iv, cipher=binascii.a2b_hex(key),binascii.a2b_hex(iv),binascii.a2b_hex(cipher) - obj1=ciph.new(key, ciph.MODE_CBC, iv) - obj2=ciph.new(key, ciph.MODE_CBC, iv) - ciphertext=obj1.encrypt(plain) - if (ciphertext!=cipher): - die('DES3 CBC mode failed on entry '+`entry`) - - if 'idea' in args: - ciph=exerciseBlockCipher('IDEA', verbose) # IDEA block cipher - if (ciph!=None): - if verbose: print ' Verifying against test suite...' - for entry in testdata.idea: - key,plain,cipher=entry - key=binascii.a2b_hex(key) - plain=binascii.a2b_hex(plain) - cipher=binascii.a2b_hex(cipher) - obj=ciph.new(key, ciph.MODE_ECB) - ciphertext=obj.encrypt(plain) - if (ciphertext!=cipher): - die('IDEA failed on entry '+`entry`) - - if 'rc5' in args: - # Ronald Rivest's RC5 algorithm - ciph=exerciseBlockCipher('RC5', verbose) - if (ciph!=None): - if verbose: print ' Verifying against test suite...' - for entry in testdata.rc5: - key,plain,cipher=entry - key=binascii.a2b_hex(key) - plain=binascii.a2b_hex(plain) - cipher=binascii.a2b_hex(cipher) - obj=ciph.new(key[4:], ciph.MODE_ECB, - version =ord(key[0]), - word_size=ord(key[1]), - rounds =ord(key[2]) ) - ciphertext=obj.encrypt(plain) - if (ciphertext!=cipher): - die('RC5 failed on entry '+`entry`) - for i in ciphertext: - if verbose: print hex(ord(i)), - if verbose: print - - - diff --git a/gdata/analytics/Crypto/__init__.py b/gdata/analytics/Crypto/__init__.py deleted file mode 100644 index 2324ae8c37..0000000000 --- a/gdata/analytics/Crypto/__init__.py +++ /dev/null @@ -1,25 +0,0 @@ - -"""Python Cryptography Toolkit - -A collection of cryptographic modules implementing various algorithms -and protocols. - -Subpackages: -Crypto.Cipher Secret-key encryption algorithms (AES, DES, ARC4) -Crypto.Hash Hashing algorithms (MD5, SHA, HMAC) -Crypto.Protocol Cryptographic protocols (Chaffing, all-or-nothing - transform). This package does not contain any - network protocols. -Crypto.PublicKey Public-key encryption and signature algorithms - (RSA, DSA) -Crypto.Util Various useful modules and functions (long-to-string - conversion, random number generation, number - theoretic functions) -""" - -__all__ = ['Cipher', 'Hash', 'Protocol', 'PublicKey', 'Util'] - -__version__ = '2.0.1' -__revision__ = "$Id: __init__.py,v 1.12 2005/06/14 01:20:22 akuchling Exp $" - - diff --git a/gdata/analytics/Crypto/test.py b/gdata/analytics/Crypto/test.py deleted file mode 100644 index c5ed061621..0000000000 --- a/gdata/analytics/Crypto/test.py +++ /dev/null @@ -1,38 +0,0 @@ -# -# Test script for the Python Cryptography Toolkit. -# - -__revision__ = "$Id: test.py,v 1.7 2002/07/11 14:31:19 akuchling Exp $" - -import os, sys - - -# Add the build directory to the front of sys.path -from distutils.util import get_platform -s = "build/lib.%s-%.3s" % (get_platform(), sys.version) -s = os.path.join(os.getcwd(), s) -sys.path.insert(0, s) -s = os.path.join(os.getcwd(), 'test') -sys.path.insert(0, s) - -from Crypto.Util import test - -args = sys.argv[1:] -quiet = "--quiet" in args -if quiet: args.remove('--quiet') - -if not quiet: - print '\nStream Ciphers:' - print '===============' - -if args: test.TestStreamModules(args, verbose= not quiet) -else: test.TestStreamModules(verbose= not quiet) - -if not quiet: - print '\nBlock Ciphers:' - print '==============' - -if args: test.TestBlockModules(args, verbose= not quiet) -else: test.TestBlockModules(verbose= not quiet) - - diff --git a/gdata/analytics/__init__.py b/gdata/analytics/__init__.py deleted file mode 100644 index 634889b060..0000000000 --- a/gdata/analytics/__init__.py +++ /dev/null @@ -1,835 +0,0 @@ -#!/usr/bin/python -# -# Copyright (C) 2006 Google 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. - - -"""Contains classes representing Google Data elements. - - Extends Atom classes to add Google Data specific elements. -""" - - -__author__ = 'j.s@google.com (Jeffrey Scudder)' - -import os -import atom -try: - from xml.etree import cElementTree as ElementTree -except ImportError: - try: - import cElementTree as ElementTree - except ImportError: - try: - from xml.etree import ElementTree - except ImportError: - from elementtree import ElementTree - - -# XML namespaces which are often used in GData entities. -GDATA_NAMESPACE = 'http://schemas.google.com/g/2005' -GDATA_TEMPLATE = '{http://schemas.google.com/g/2005}%s' -OPENSEARCH_NAMESPACE = 'http://a9.com/-/spec/opensearchrss/1.0/' -OPENSEARCH_TEMPLATE = '{http://a9.com/-/spec/opensearchrss/1.0/}%s' -BATCH_NAMESPACE = 'http://schemas.google.com/gdata/batch' -GACL_NAMESPACE = 'http://schemas.google.com/acl/2007' -GACL_TEMPLATE = '{http://schemas.google.com/acl/2007}%s' - - -# Labels used in batch request entries to specify the desired CRUD operation. -BATCH_INSERT = 'insert' -BATCH_UPDATE = 'update' -BATCH_DELETE = 'delete' -BATCH_QUERY = 'query' - -class Error(Exception): - pass - - -class MissingRequiredParameters(Error): - pass - - -class MediaSource(object): - """GData Entries can refer to media sources, so this class provides a - place to store references to these objects along with some metadata. - """ - - def __init__(self, file_handle=None, content_type=None, content_length=None, - file_path=None, file_name=None): - """Creates an object of type MediaSource. - - Args: - file_handle: A file handle pointing to the file to be encapsulated in the - MediaSource - content_type: string The MIME type of the file. Required if a file_handle - is given. - content_length: int The size of the file. Required if a file_handle is - given. - file_path: string (optional) A full path name to the file. Used in - place of a file_handle. - file_name: string The name of the file without any path information. - Required if a file_handle is given. - """ - self.file_handle = file_handle - self.content_type = content_type - self.content_length = content_length - self.file_name = file_name - - if (file_handle is None and content_type is not None and - file_path is not None): - self.setFile(file_path, content_type) - - def setFile(self, file_name, content_type): - """A helper function which can create a file handle from a given filename - and set the content type and length all at once. - - Args: - file_name: string The path and file name to the file containing the media - content_type: string A MIME type representing the type of the media - """ - - self.file_handle = open(file_name, 'rb') - self.content_type = content_type - self.content_length = os.path.getsize(file_name) - self.file_name = os.path.basename(file_name) - - -class LinkFinder(atom.LinkFinder): - """An "interface" providing methods to find link elements - - GData Entry elements often contain multiple links which differ in the rel - attribute or content type. Often, developers are interested in a specific - type of link so this class provides methods to find specific classes of - links. - - This class is used as a mixin in GData entries. - """ - - def GetSelfLink(self): - """Find the first link with rel set to 'self' - - Returns: - An atom.Link or none if none of the links had rel equal to 'self' - """ - - for a_link in self.link: - if a_link.rel == 'self': - return a_link - return None - - def GetEditLink(self): - for a_link in self.link: - if a_link.rel == 'edit': - return a_link - return None - - def GetEditMediaLink(self): - """The Picasa API mistakenly returns media-edit rather than edit-media, but - this may change soon. - """ - for a_link in self.link: - if a_link.rel == 'edit-media': - return a_link - if a_link.rel == 'media-edit': - return a_link - return None - - def GetHtmlLink(self): - """Find the first link with rel of alternate and type of text/html - - Returns: - An atom.Link or None if no links matched - """ - for a_link in self.link: - if a_link.rel == 'alternate' and a_link.type == 'text/html': - return a_link - return None - - def GetPostLink(self): - """Get a link containing the POST target URL. - - The POST target URL is used to insert new entries. - - Returns: - A link object with a rel matching the POST type. - """ - for a_link in self.link: - if a_link.rel == 'http://schemas.google.com/g/2005#post': - return a_link - return None - - def GetAclLink(self): - for a_link in self.link: - if a_link.rel == 'http://schemas.google.com/acl/2007#accessControlList': - return a_link - return None - - def GetFeedLink(self): - for a_link in self.link: - if a_link.rel == 'http://schemas.google.com/g/2005#feed': - return a_link - return None - - def GetNextLink(self): - for a_link in self.link: - if a_link.rel == 'next': - return a_link - return None - - def GetPrevLink(self): - for a_link in self.link: - if a_link.rel == 'previous': - return a_link - return None - - -class TotalResults(atom.AtomBase): - """opensearch:TotalResults for a GData feed""" - - _tag = 'totalResults' - _namespace = OPENSEARCH_NAMESPACE - _children = atom.AtomBase._children.copy() - _attributes = atom.AtomBase._attributes.copy() - - def __init__(self, extension_elements=None, - extension_attributes=None, text=None): - self.text = text - self.extension_elements = extension_elements or [] - self.extension_attributes = extension_attributes or {} - - -def TotalResultsFromString(xml_string): - return atom.CreateClassFromXMLString(TotalResults, xml_string) - - -class StartIndex(atom.AtomBase): - """The opensearch:startIndex element in GData feed""" - - _tag = 'startIndex' - _namespace = OPENSEARCH_NAMESPACE - _children = atom.AtomBase._children.copy() - _attributes = atom.AtomBase._attributes.copy() - - def __init__(self, extension_elements=None, - extension_attributes=None, text=None): - self.text = text - self.extension_elements = extension_elements or [] - self.extension_attributes = extension_attributes or {} - - -def StartIndexFromString(xml_string): - return atom.CreateClassFromXMLString(StartIndex, xml_string) - - -class ItemsPerPage(atom.AtomBase): - """The opensearch:itemsPerPage element in GData feed""" - - _tag = 'itemsPerPage' - _namespace = OPENSEARCH_NAMESPACE - _children = atom.AtomBase._children.copy() - _attributes = atom.AtomBase._attributes.copy() - - def __init__(self, extension_elements=None, - extension_attributes=None, text=None): - self.text = text - self.extension_elements = extension_elements or [] - self.extension_attributes = extension_attributes or {} - - -def ItemsPerPageFromString(xml_string): - return atom.CreateClassFromXMLString(ItemsPerPage, xml_string) - - -class ExtendedProperty(atom.AtomBase): - """The Google Data extendedProperty element. - - Used to store arbitrary key-value information specific to your - application. The value can either be a text string stored as an XML - attribute (.value), or an XML node (XmlBlob) as a child element. - - This element is used in the Google Calendar data API and the Google - Contacts data API. - """ - - _tag = 'extendedProperty' - _namespace = GDATA_NAMESPACE - _children = atom.AtomBase._children.copy() - _attributes = atom.AtomBase._attributes.copy() - _attributes['name'] = 'name' - _attributes['value'] = 'value' - - def __init__(self, name=None, value=None, extension_elements=None, - extension_attributes=None, text=None): - self.name = name - self.value = value - self.text = text - self.extension_elements = extension_elements or [] - self.extension_attributes = extension_attributes or {} - - def GetXmlBlobExtensionElement(self): - """Returns the XML blob as an atom.ExtensionElement. - - Returns: - An atom.ExtensionElement representing the blob's XML, or None if no - blob was set. - """ - if len(self.extension_elements) < 1: - return None - else: - return self.extension_elements[0] - - def GetXmlBlobString(self): - """Returns the XML blob as a string. - - Returns: - A string containing the blob's XML, or None if no blob was set. - """ - blob = self.GetXmlBlobExtensionElement() - if blob: - return blob.ToString() - return None - - def SetXmlBlob(self, blob): - """Sets the contents of the extendedProperty to XML as a child node. - - Since the extendedProperty is only allowed one child element as an XML - blob, setting the XML blob will erase any preexisting extension elements - in this object. - - Args: - blob: str, ElementTree Element or atom.ExtensionElement representing - the XML blob stored in the extendedProperty. - """ - # Erase any existing extension_elements, clears the child nodes from the - # extendedProperty. - self.extension_elements = [] - if isinstance(blob, atom.ExtensionElement): - self.extension_elements.append(blob) - elif ElementTree.iselement(blob): - self.extension_elements.append(atom._ExtensionElementFromElementTree( - blob)) - else: - self.extension_elements.append(atom.ExtensionElementFromString(blob)) - - -def ExtendedPropertyFromString(xml_string): - return atom.CreateClassFromXMLString(ExtendedProperty, xml_string) - - -class GDataEntry(atom.Entry, LinkFinder): - """Extends Atom Entry to provide data processing""" - - _tag = atom.Entry._tag - _namespace = atom.Entry._namespace - _children = atom.Entry._children.copy() - _attributes = atom.Entry._attributes.copy() - - def __GetId(self): - return self.__id - - # This method was created to strip the unwanted whitespace from the id's - # text node. - def __SetId(self, id): - self.__id = id - if id is not None and id.text is not None: - self.__id.text = id.text.strip() - - id = property(__GetId, __SetId) - - def IsMedia(self): - """Determines whether or not an entry is a GData Media entry. - """ - if (self.GetEditMediaLink()): - return True - else: - return False - - def GetMediaURL(self): - """Returns the URL to the media content, if the entry is a media entry. - Otherwise returns None. - """ - if not self.IsMedia(): - return None - else: - return self.content.src - - -def GDataEntryFromString(xml_string): - """Creates a new GDataEntry instance given a string of XML.""" - return atom.CreateClassFromXMLString(GDataEntry, xml_string) - - -class GDataFeed(atom.Feed, LinkFinder): - """A Feed from a GData service""" - - _tag = 'feed' - _namespace = atom.ATOM_NAMESPACE - _children = atom.Feed._children.copy() - _attributes = atom.Feed._attributes.copy() - _children['{%s}totalResults' % OPENSEARCH_NAMESPACE] = ('total_results', - TotalResults) - _children['{%s}startIndex' % OPENSEARCH_NAMESPACE] = ('start_index', - StartIndex) - _children['{%s}itemsPerPage' % OPENSEARCH_NAMESPACE] = ('items_per_page', - ItemsPerPage) - # Add a conversion rule for atom:entry to make it into a GData - # Entry. - _children['{%s}entry' % atom.ATOM_NAMESPACE] = ('entry', [GDataEntry]) - - def __GetId(self): - return self.__id - - def __SetId(self, id): - self.__id = id - if id is not None and id.text is not None: - self.__id.text = id.text.strip() - - id = property(__GetId, __SetId) - - def __GetGenerator(self): - return self.__generator - - def __SetGenerator(self, generator): - self.__generator = generator - if generator is not None: - self.__generator.text = generator.text.strip() - - generator = property(__GetGenerator, __SetGenerator) - - def __init__(self, author=None, category=None, contributor=None, - generator=None, icon=None, atom_id=None, link=None, logo=None, - rights=None, subtitle=None, title=None, updated=None, entry=None, - total_results=None, start_index=None, items_per_page=None, - extension_elements=None, extension_attributes=None, text=None): - """Constructor for Source - - Args: - author: list (optional) A list of Author instances which belong to this - class. - category: list (optional) A list of Category instances - contributor: list (optional) A list on Contributor instances - generator: Generator (optional) - icon: Icon (optional) - id: Id (optional) The entry's Id element - link: list (optional) A list of Link instances - logo: Logo (optional) - rights: Rights (optional) The entry's Rights element - subtitle: Subtitle (optional) The entry's subtitle element - title: Title (optional) the entry's title element - updated: Updated (optional) the entry's updated element - entry: list (optional) A list of the Entry instances contained in the - feed. - text: String (optional) The text contents of the element. This is the - contents of the Entry's XML text node. - (Example: This is the text) - extension_elements: list (optional) A list of ExtensionElement instances - which are children of this element. - extension_attributes: dict (optional) A dictionary of strings which are - the values for additional XML attributes of this element. - """ - - self.author = author or [] - self.category = category or [] - self.contributor = contributor or [] - self.generator = generator - self.icon = icon - self.id = atom_id - self.link = link or [] - self.logo = logo - self.rights = rights - self.subtitle = subtitle - self.title = title - self.updated = updated - self.entry = entry or [] - self.total_results = total_results - self.start_index = start_index - self.items_per_page = items_per_page - self.text = text - self.extension_elements = extension_elements or [] - self.extension_attributes = extension_attributes or {} - - -def GDataFeedFromString(xml_string): - return atom.CreateClassFromXMLString(GDataFeed, xml_string) - - -class BatchId(atom.AtomBase): - _tag = 'id' - _namespace = BATCH_NAMESPACE - _children = atom.AtomBase._children.copy() - _attributes = atom.AtomBase._attributes.copy() - - -def BatchIdFromString(xml_string): - return atom.CreateClassFromXMLString(BatchId, xml_string) - - -class BatchOperation(atom.AtomBase): - _tag = 'operation' - _namespace = BATCH_NAMESPACE - _children = atom.AtomBase._children.copy() - _attributes = atom.AtomBase._attributes.copy() - _attributes['type'] = 'type' - - def __init__(self, op_type=None, extension_elements=None, - extension_attributes=None, - text=None): - self.type = op_type - atom.AtomBase.__init__(self, - extension_elements=extension_elements, - extension_attributes=extension_attributes, - text=text) - - -def BatchOperationFromString(xml_string): - return atom.CreateClassFromXMLString(BatchOperation, xml_string) - - -class BatchStatus(atom.AtomBase): - """The batch:status element present in a batch response entry. - - A status element contains the code (HTTP response code) and - reason as elements. In a single request these fields would - be part of the HTTP response, but in a batch request each - Entry operation has a corresponding Entry in the response - feed which includes status information. - - See http://code.google.com/apis/gdata/batch.html#Handling_Errors - """ - - _tag = 'status' - _namespace = BATCH_NAMESPACE - _children = atom.AtomBase._children.copy() - _attributes = atom.AtomBase._attributes.copy() - _attributes['code'] = 'code' - _attributes['reason'] = 'reason' - _attributes['content-type'] = 'content_type' - - def __init__(self, code=None, reason=None, content_type=None, - extension_elements=None, extension_attributes=None, text=None): - self.code = code - self.reason = reason - self.content_type = content_type - atom.AtomBase.__init__(self, extension_elements=extension_elements, - extension_attributes=extension_attributes, - text=text) - - -def BatchStatusFromString(xml_string): - return atom.CreateClassFromXMLString(BatchStatus, xml_string) - - -class BatchEntry(GDataEntry): - """An atom:entry for use in batch requests. - - The BatchEntry contains additional members to specify the operation to be - performed on this entry and a batch ID so that the server can reference - individual operations in the response feed. For more information, see: - http://code.google.com/apis/gdata/batch.html - """ - - _tag = GDataEntry._tag - _namespace = GDataEntry._namespace - _children = GDataEntry._children.copy() - _children['{%s}operation' % BATCH_NAMESPACE] = ('batch_operation', BatchOperation) - _children['{%s}id' % BATCH_NAMESPACE] = ('batch_id', BatchId) - _children['{%s}status' % BATCH_NAMESPACE] = ('batch_status', BatchStatus) - _attributes = GDataEntry._attributes.copy() - - def __init__(self, author=None, category=None, content=None, - contributor=None, atom_id=None, link=None, published=None, rights=None, - source=None, summary=None, control=None, title=None, updated=None, - batch_operation=None, batch_id=None, batch_status=None, - extension_elements=None, extension_attributes=None, text=None): - self.batch_operation = batch_operation - self.batch_id = batch_id - self.batch_status = batch_status - GDataEntry.__init__(self, author=author, category=category, - content=content, contributor=contributor, atom_id=atom_id, link=link, - published=published, rights=rights, source=source, summary=summary, - control=control, title=title, updated=updated, - extension_elements=extension_elements, - extension_attributes=extension_attributes, text=text) - - -def BatchEntryFromString(xml_string): - return atom.CreateClassFromXMLString(BatchEntry, xml_string) - - -class BatchInterrupted(atom.AtomBase): - """The batch:interrupted element sent if batch request was interrupted. - - Only appears in a feed if some of the batch entries could not be processed. - See: http://code.google.com/apis/gdata/batch.html#Handling_Errors - """ - - _tag = 'interrupted' - _namespace = BATCH_NAMESPACE - _children = atom.AtomBase._children.copy() - _attributes = atom.AtomBase._attributes.copy() - _attributes['reason'] = 'reason' - _attributes['success'] = 'success' - _attributes['failures'] = 'failures' - _attributes['parsed'] = 'parsed' - - def __init__(self, reason=None, success=None, failures=None, parsed=None, - extension_elements=None, extension_attributes=None, text=None): - self.reason = reason - self.success = success - self.failures = failures - self.parsed = parsed - atom.AtomBase.__init__(self, extension_elements=extension_elements, - extension_attributes=extension_attributes, - text=text) - - -def BatchInterruptedFromString(xml_string): - return atom.CreateClassFromXMLString(BatchInterrupted, xml_string) - - -class BatchFeed(GDataFeed): - """A feed containing a list of batch request entries.""" - - _tag = GDataFeed._tag - _namespace = GDataFeed._namespace - _children = GDataFeed._children.copy() - _attributes = GDataFeed._attributes.copy() - _children['{%s}entry' % atom.ATOM_NAMESPACE] = ('entry', [BatchEntry]) - _children['{%s}interrupted' % BATCH_NAMESPACE] = ('interrupted', BatchInterrupted) - - def __init__(self, author=None, category=None, contributor=None, - generator=None, icon=None, atom_id=None, link=None, logo=None, - rights=None, subtitle=None, title=None, updated=None, entry=None, - total_results=None, start_index=None, items_per_page=None, - interrupted=None, - extension_elements=None, extension_attributes=None, text=None): - self.interrupted = interrupted - GDataFeed.__init__(self, author=author, category=category, - contributor=contributor, generator=generator, - icon=icon, atom_id=atom_id, link=link, - logo=logo, rights=rights, subtitle=subtitle, - title=title, updated=updated, entry=entry, - total_results=total_results, start_index=start_index, - items_per_page=items_per_page, - extension_elements=extension_elements, - extension_attributes=extension_attributes, - text=text) - - def AddBatchEntry(self, entry=None, id_url_string=None, - batch_id_string=None, operation_string=None): - """Logic for populating members of a BatchEntry and adding to the feed. - - - If the entry is not a BatchEntry, it is converted to a BatchEntry so - that the batch specific members will be present. - - The id_url_string can be used in place of an entry if the batch operation - applies to a URL. For example query and delete operations require just - the URL of an entry, no body is sent in the HTTP request. If an - id_url_string is sent instead of an entry, a BatchEntry is created and - added to the feed. - - This method also assigns the desired batch id to the entry so that it - can be referenced in the server's response. If the batch_id_string is - None, this method will assign a batch_id to be the index at which this - entry will be in the feed's entry list. - - Args: - entry: BatchEntry, atom.Entry, or another Entry flavor (optional) The - entry which will be sent to the server as part of the batch request. - The item must have a valid atom id so that the server knows which - entry this request references. - id_url_string: str (optional) The URL of the entry to be acted on. You - can find this URL in the text member of the atom id for an entry. - If an entry is not sent, this id will be used to construct a new - BatchEntry which will be added to the request feed. - batch_id_string: str (optional) The batch ID to be used to reference - this batch operation in the results feed. If this parameter is None, - the current length of the feed's entry array will be used as a - count. Note that batch_ids should either always be specified or - never, mixing could potentially result in duplicate batch ids. - operation_string: str (optional) The desired batch operation which will - set the batch_operation.type member of the entry. Options are - 'insert', 'update', 'delete', and 'query' - - Raises: - MissingRequiredParameters: Raised if neither an id_ url_string nor an - entry are provided in the request. - - Returns: - The added entry. - """ - if entry is None and id_url_string is None: - raise MissingRequiredParameters('supply either an entry or URL string') - if entry is None and id_url_string is not None: - entry = BatchEntry(atom_id=atom.Id(text=id_url_string)) - # TODO: handle cases in which the entry lacks batch_... members. - #if not isinstance(entry, BatchEntry): - # Convert the entry to a batch entry. - if batch_id_string is not None: - entry.batch_id = BatchId(text=batch_id_string) - elif entry.batch_id is None or entry.batch_id.text is None: - entry.batch_id = BatchId(text=str(len(self.entry))) - if operation_string is not None: - entry.batch_operation = BatchOperation(op_type=operation_string) - self.entry.append(entry) - return entry - - def AddInsert(self, entry, batch_id_string=None): - """Add an insert request to the operations in this batch request feed. - - If the entry doesn't yet have an operation or a batch id, these will - be set to the insert operation and a batch_id specified as a parameter. - - Args: - entry: BatchEntry The entry which will be sent in the batch feed as an - insert request. - batch_id_string: str (optional) The batch ID to be used to reference - this batch operation in the results feed. If this parameter is None, - the current length of the feed's entry array will be used as a - count. Note that batch_ids should either always be specified or - never, mixing could potentially result in duplicate batch ids. - """ - entry = self.AddBatchEntry(entry=entry, batch_id_string=batch_id_string, - operation_string=BATCH_INSERT) - - def AddUpdate(self, entry, batch_id_string=None): - """Add an update request to the list of batch operations in this feed. - - Sets the operation type of the entry to insert if it is not already set - and assigns the desired batch id to the entry so that it can be - referenced in the server's response. - - Args: - entry: BatchEntry The entry which will be sent to the server as an - update (HTTP PUT) request. The item must have a valid atom id - so that the server knows which entry to replace. - batch_id_string: str (optional) The batch ID to be used to reference - this batch operation in the results feed. If this parameter is None, - the current length of the feed's entry array will be used as a - count. See also comments for AddInsert. - """ - entry = self.AddBatchEntry(entry=entry, batch_id_string=batch_id_string, - operation_string=BATCH_UPDATE) - - def AddDelete(self, url_string=None, entry=None, batch_id_string=None): - """Adds a delete request to the batch request feed. - - This method takes either the url_string which is the atom id of the item - to be deleted, or the entry itself. The atom id of the entry must be - present so that the server knows which entry should be deleted. - - Args: - url_string: str (optional) The URL of the entry to be deleted. You can - find this URL in the text member of the atom id for an entry. - entry: BatchEntry (optional) The entry to be deleted. - batch_id_string: str (optional) - - Raises: - MissingRequiredParameters: Raised if neither a url_string nor an entry - are provided in the request. - """ - entry = self.AddBatchEntry(entry=entry, id_url_string=url_string, - batch_id_string=batch_id_string, - operation_string=BATCH_DELETE) - - def AddQuery(self, url_string=None, entry=None, batch_id_string=None): - """Adds a query request to the batch request feed. - - This method takes either the url_string which is the query URL - whose results will be added to the result feed. The query URL will - be encapsulated in a BatchEntry, and you may pass in the BatchEntry - with a query URL instead of sending a url_string. - - Args: - url_string: str (optional) - entry: BatchEntry (optional) - batch_id_string: str (optional) - - Raises: - MissingRequiredParameters - """ - entry = self.AddBatchEntry(entry=entry, id_url_string=url_string, - batch_id_string=batch_id_string, - operation_string=BATCH_QUERY) - - def GetBatchLink(self): - for link in self.link: - if link.rel == 'http://schemas.google.com/g/2005#batch': - return link - return None - - -def BatchFeedFromString(xml_string): - return atom.CreateClassFromXMLString(BatchFeed, xml_string) - - -class EntryLink(atom.AtomBase): - """The gd:entryLink element""" - - _tag = 'entryLink' - _namespace = GDATA_NAMESPACE - _children = atom.AtomBase._children.copy() - _attributes = atom.AtomBase._attributes.copy() - # The entry used to be an atom.Entry, now it is a GDataEntry. - _children['{%s}entry' % atom.ATOM_NAMESPACE] = ('entry', GDataEntry) - _attributes['rel'] = 'rel' - _attributes['readOnly'] = 'read_only' - _attributes['href'] = 'href' - - def __init__(self, href=None, read_only=None, rel=None, - entry=None, extension_elements=None, - extension_attributes=None, text=None): - self.href = href - self.read_only = read_only - self.rel = rel - self.entry = entry - self.text = text - self.extension_elements = extension_elements or [] - self.extension_attributes = extension_attributes or {} - - -def EntryLinkFromString(xml_string): - return atom.CreateClassFromXMLString(EntryLink, xml_string) - - -class FeedLink(atom.AtomBase): - """The gd:feedLink element""" - - _tag = 'feedLink' - _namespace = GDATA_NAMESPACE - _children = atom.AtomBase._children.copy() - _attributes = atom.AtomBase._attributes.copy() - _children['{%s}feed' % atom.ATOM_NAMESPACE] = ('feed', GDataFeed) - _attributes['rel'] = 'rel' - _attributes['readOnly'] = 'read_only' - _attributes['countHint'] = 'count_hint' - _attributes['href'] = 'href' - - def __init__(self, count_hint=None, href=None, read_only=None, rel=None, - feed=None, extension_elements=None, extension_attributes=None, - text=None): - self.count_hint = count_hint - self.href = href - self.read_only = read_only - self.rel = rel - self.feed = feed - self.text = text - self.extension_elements = extension_elements or [] - self.extension_attributes = extension_attributes or {} - - -def FeedLinkFromString(xml_string): - return atom.CreateClassFromXMLString(FeedLink, xml_string) diff --git a/gdata/analytics/acl/__init__.py b/gdata/analytics/acl/__init__.py deleted file mode 100644 index 22071f7a11..0000000000 --- a/gdata/analytics/acl/__init__.py +++ /dev/null @@ -1,15 +0,0 @@ -#!/usr/bin/python -# -# Copyright (C) 2009 Google 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. diff --git a/gdata/analytics/acl/data.py b/gdata/analytics/acl/data.py deleted file mode 100644 index 17d6c15048..0000000000 --- a/gdata/analytics/acl/data.py +++ /dev/null @@ -1,63 +0,0 @@ -#!/usr/bin/python -# -# Copyright (C) 2009 Google 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. - -"""Contains the data classes of the Google Access Control List (ACL) Extension""" - - -__author__ = 'j.s@google.com (Jeff Scudder)' - - -import atom.core -import atom.data -import gdata.data -import gdata.opensearch.data - - -GACL_TEMPLATE = '{http://schemas.google.com/acl/2007}%s' - - -class AclRole(atom.core.XmlElement): - """Describes the role of an entry in an access control list.""" - _qname = GACL_TEMPLATE % 'role' - value = 'value' - - -class AclScope(atom.core.XmlElement): - """Describes the scope of an entry in an access control list.""" - _qname = GACL_TEMPLATE % 'scope' - type = 'type' - value = 'value' - - -class AclWithKey(atom.core.XmlElement): - """Describes a key that can be used to access a document.""" - _qname = GACL_TEMPLATE % 'withKey' - key = 'key' - role = AclRole - - -class AclEntry(gdata.data.GDEntry): - """Describes an entry in a feed of an access control list (ACL).""" - scope = AclScope - role = AclRole - with_key = AclWithKey - - -class AclFeed(gdata.data.GDFeed): - """Describes a feed of an access control list (ACL).""" - entry = [AclEntry] - - diff --git a/gdata/analytics/alt/__init__.py b/gdata/analytics/alt/__init__.py deleted file mode 100644 index 742980e819..0000000000 --- a/gdata/analytics/alt/__init__.py +++ /dev/null @@ -1,20 +0,0 @@ -#!/usr/bin/python -# -# Copyright (C) 2008 Google 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. -"""This package's modules adapt the gdata library to run in other environments - -The first example is the appengine module which contains functions and -classes which modify a GDataService object to run on Google App Engine. -""" diff --git a/gdata/analytics/alt/app_engine.py b/gdata/analytics/alt/app_engine.py deleted file mode 100644 index afa412d5bc..0000000000 --- a/gdata/analytics/alt/app_engine.py +++ /dev/null @@ -1,101 +0,0 @@ -#!/usr/bin/python -# -# Copyright (C) 2009 Google 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. - - -"""Provides functions to persist serialized auth tokens in the datastore. - -The get_token and set_token functions should be used in conjunction with -gdata.gauth's token_from_blob and token_to_blob to allow auth token objects -to be reused across requests. It is up to your own code to ensure that the -token key's are unique. -""" - -__author__ = 'j.s@google.com (Jeff Scudder)' - - -from google.appengine.ext import db -from google.appengine.api import memcache - - -class Token(db.Model): - """Datastore Model which stores a serialized auth token.""" - t = db.BlobProperty() - - -def get_token(unique_key): - """Searches for a stored token with the desired key. - - Checks memcache and then the datastore if required. - - Args: - unique_key: str which uniquely identifies the desired auth token. - - Returns: - A string encoding the auth token data. Use gdata.gauth.token_from_blob to - convert back into a usable token object. None if the token was not found - in memcache or the datastore. - """ - token_string = memcache.get(unique_key) - if token_string is None: - # The token wasn't in memcache, so look in the datastore. - token = Token.get_by_key_name(unique_key) - if token is None: - return None - return token.t - return token_string - - -def set_token(unique_key, token_str): - """Saves the serialized auth token in the datastore. - - The token is also stored in memcache to speed up retrieval on a cache hit. - - Args: - unique_key: The unique name for this token as a string. It is up to your - code to ensure that this token value is unique in your application. - Previous values will be silently overwitten. - token_str: A serialized auth token as a string. I expect that this string - will be generated by gdata.gauth.token_to_blob. - - Returns: - True if the token was stored sucessfully, False if the token could not be - safely cached (if an old value could not be cleared). If the token was - set in memcache, but not in the datastore, this function will return None. - However, in that situation an exception will likely be raised. - - Raises: - Datastore exceptions may be raised from the App Engine SDK in the event of - failure. - """ - # First try to save in memcache. - result = memcache.set(unique_key, token_str) - # If memcache fails to save the value, clear the cached value. - if not result: - result = memcache.delete(unique_key) - # If we could not clear the cached value for this token, refuse to save. - if result == 0: - return False - # Save to the datastore. - if Token(key_name=unique_key, t=token_str).put(): - return True - return None - - -def delete_token(unique_key): - # Clear from memcache. - memcache.delete(unique_key) - # Clear from the datastore. - Token(key_name=unique_key).delete() diff --git a/gdata/analytics/alt/appengine.py b/gdata/analytics/alt/appengine.py deleted file mode 100644 index 22516214e1..0000000000 --- a/gdata/analytics/alt/appengine.py +++ /dev/null @@ -1,321 +0,0 @@ -#!/usr/bin/python -# -# Copyright (C) 2008 Google 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. - - -"""Provides HTTP functions for gdata.service to use on Google App Engine - -AppEngineHttpClient: Provides an HTTP request method which uses App Engine's - urlfetch API. Set the http_client member of a GDataService object to an - instance of an AppEngineHttpClient to allow the gdata library to run on - Google App Engine. - -run_on_appengine: Function which will modify an existing GDataService object - to allow it to run on App Engine. It works by creating a new instance of - the AppEngineHttpClient and replacing the GDataService object's - http_client. -""" - - -__author__ = 'api.jscudder (Jeff Scudder)' - - -import StringIO -import pickle -import atom.http_interface -import atom.token_store -from google.appengine.api import urlfetch -from google.appengine.ext import db -from google.appengine.api import users -from google.appengine.api import memcache - - -def run_on_appengine(gdata_service, store_tokens=True, - single_user_mode=False, deadline=None): - """Modifies a GDataService object to allow it to run on App Engine. - - Args: - gdata_service: An instance of AtomService, GDataService, or any - of their subclasses which has an http_client member and a - token_store member. - store_tokens: Boolean, defaults to True. If True, the gdata_service - will attempt to add each token to it's token_store when - SetClientLoginToken or SetAuthSubToken is called. If False - the tokens will not automatically be added to the - token_store. - single_user_mode: Boolean, defaults to False. If True, the current_token - member of gdata_service will be set when - SetClientLoginToken or SetAuthTubToken is called. If set - to True, the current_token is set in the gdata_service - and anyone who accesses the object will use the same - token. - - Note: If store_tokens is set to False and - single_user_mode is set to False, all tokens will be - ignored, since the library assumes: the tokens should not - be stored in the datastore and they should not be stored - in the gdata_service object. This will make it - impossible to make requests which require authorization. - deadline: int (optional) The number of seconds to wait for a response - before timing out on the HTTP request. If no deadline is - specified, the deafault deadline for HTTP requests from App - Engine is used. The maximum is currently 10 (for 10 seconds). - The default deadline for App Engine is 5 seconds. - """ - gdata_service.http_client = AppEngineHttpClient(deadline=deadline) - gdata_service.token_store = AppEngineTokenStore() - gdata_service.auto_store_tokens = store_tokens - gdata_service.auto_set_current_token = single_user_mode - return gdata_service - - -class AppEngineHttpClient(atom.http_interface.GenericHttpClient): - def __init__(self, headers=None, deadline=None): - self.debug = False - self.headers = headers or {} - self.deadline = deadline - - def request(self, operation, url, data=None, headers=None): - """Performs an HTTP call to the server, supports GET, POST, PUT, and - DELETE. - - Usage example, perform and HTTP GET on http://www.google.com/: - import atom.http - client = atom.http.HttpClient() - http_response = client.request('GET', 'http://www.google.com/') - - Args: - operation: str The HTTP operation to be performed. This is usually one - of 'GET', 'POST', 'PUT', or 'DELETE' - data: filestream, list of parts, or other object which can be converted - to a string. Should be set to None when performing a GET or DELETE. - If data is a file-like object which can be read, this method will - read a chunk of 100K bytes at a time and send them. - If the data is a list of parts to be sent, each part will be - evaluated and sent. - url: The full URL to which the request should be sent. Can be a string - or atom.url.Url. - headers: dict of strings. HTTP headers which should be sent - in the request. - """ - all_headers = self.headers.copy() - if headers: - all_headers.update(headers) - - # Construct the full payload. - # Assume that data is None or a string. - data_str = data - if data: - if isinstance(data, list): - # If data is a list of different objects, convert them all to strings - # and join them together. - converted_parts = [_convert_data_part(x) for x in data] - data_str = ''.join(converted_parts) - else: - data_str = _convert_data_part(data) - - # If the list of headers does not include a Content-Length, attempt to - # calculate it based on the data object. - if data and 'Content-Length' not in all_headers: - all_headers['Content-Length'] = str(len(data_str)) - - # Set the content type to the default value if none was set. - if 'Content-Type' not in all_headers: - all_headers['Content-Type'] = 'application/atom+xml' - - # Lookup the urlfetch operation which corresponds to the desired HTTP verb. - if operation == 'GET': - method = urlfetch.GET - elif operation == 'POST': - method = urlfetch.POST - elif operation == 'PUT': - method = urlfetch.PUT - elif operation == 'DELETE': - method = urlfetch.DELETE - else: - method = None - if self.deadline is None: - return HttpResponse(urlfetch.Fetch(url=str(url), payload=data_str, - method=method, headers=all_headers, follow_redirects=False)) - return HttpResponse(urlfetch.Fetch(url=str(url), payload=data_str, - method=method, headers=all_headers, follow_redirects=False, - deadline=self.deadline)) - - -def _convert_data_part(data): - if not data or isinstance(data, str): - return data - elif hasattr(data, 'read'): - # data is a file like object, so read it completely. - return data.read() - # The data object was not a file. - # Try to convert to a string and send the data. - return str(data) - - -class HttpResponse(object): - """Translates a urlfetch resoinse to look like an hhtplib resoinse. - - Used to allow the resoinse from HttpRequest to be usable by gdata.service - methods. - """ - - def __init__(self, urlfetch_response): - self.body = StringIO.StringIO(urlfetch_response.content) - self.headers = urlfetch_response.headers - self.status = urlfetch_response.status_code - self.reason = '' - - def read(self, length=None): - if not length: - return self.body.read() - else: - return self.body.read(length) - - def getheader(self, name): - if not self.headers.has_key(name): - return self.headers[name.lower()] - return self.headers[name] - - -class TokenCollection(db.Model): - """Datastore Model which associates auth tokens with the current user.""" - user = db.UserProperty() - pickled_tokens = db.BlobProperty() - - -class AppEngineTokenStore(atom.token_store.TokenStore): - """Stores the user's auth tokens in the App Engine datastore. - - Tokens are only written to the datastore if a user is signed in (if - users.get_current_user() returns a user object). - """ - def __init__(self): - self.user = None - - def add_token(self, token): - """Associates the token with the current user and stores it. - - If there is no current user, the token will not be stored. - - Returns: - False if the token was not stored. - """ - tokens = load_auth_tokens(self.user) - if not hasattr(token, 'scopes') or not token.scopes: - return False - for scope in token.scopes: - tokens[str(scope)] = token - key = save_auth_tokens(tokens, self.user) - if key: - return True - return False - - def find_token(self, url): - """Searches the current user's collection of token for a token which can - be used for a request to the url. - - Returns: - The stored token which belongs to the current user and is valid for the - desired URL. If there is no current user, or there is no valid user - token in the datastore, a atom.http_interface.GenericToken is returned. - """ - if url is None: - return None - if isinstance(url, (str, unicode)): - url = atom.url.parse_url(url) - tokens = load_auth_tokens(self.user) - if url in tokens: - token = tokens[url] - if token.valid_for_scope(url): - return token - else: - del tokens[url] - save_auth_tokens(tokens, self.user) - for scope, token in tokens.iteritems(): - if token.valid_for_scope(url): - return token - return atom.http_interface.GenericToken() - - def remove_token(self, token): - """Removes the token from the current user's collection in the datastore. - - Returns: - False if the token was not removed, this could be because the token was - not in the datastore, or because there is no current user. - """ - token_found = False - scopes_to_delete = [] - tokens = load_auth_tokens(self.user) - for scope, stored_token in tokens.iteritems(): - if stored_token == token: - scopes_to_delete.append(scope) - token_found = True - for scope in scopes_to_delete: - del tokens[scope] - if token_found: - save_auth_tokens(tokens, self.user) - return token_found - - def remove_all_tokens(self): - """Removes all of the current user's tokens from the datastore.""" - save_auth_tokens({}, self.user) - - -def save_auth_tokens(token_dict, user=None): - """Associates the tokens with the current user and writes to the datastore. - - If there us no current user, the tokens are not written and this function - returns None. - - Returns: - The key of the datastore entity containing the user's tokens, or None if - there was no current user. - """ - if user is None: - user = users.get_current_user() - if user is None: - return None - memcache.set('gdata_pickled_tokens:%s' % user, pickle.dumps(token_dict)) - user_tokens = TokenCollection.all().filter('user =', user).get() - if user_tokens: - user_tokens.pickled_tokens = pickle.dumps(token_dict) - return user_tokens.put() - else: - user_tokens = TokenCollection( - user=user, - pickled_tokens=pickle.dumps(token_dict)) - return user_tokens.put() - - -def load_auth_tokens(user=None): - """Reads a dictionary of the current user's tokens from the datastore. - - If there is no current user (a user is not signed in to the app) or the user - does not have any tokens, an empty dictionary is returned. - """ - if user is None: - user = users.get_current_user() - if user is None: - return {} - pickled_tokens = memcache.get('gdata_pickled_tokens:%s' % user) - if pickled_tokens: - return pickle.loads(pickled_tokens) - user_tokens = TokenCollection.all().filter('user =', user).get() - if user_tokens: - memcache.set('gdata_pickled_tokens:%s' % user, user_tokens.pickled_tokens) - return pickle.loads(user_tokens.pickled_tokens) - return {} - diff --git a/gdata/analytics/analytics/__init__.py b/gdata/analytics/analytics/__init__.py deleted file mode 100644 index 8dfa20ba98..0000000000 --- a/gdata/analytics/analytics/__init__.py +++ /dev/null @@ -1,223 +0,0 @@ -#!/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 diff --git a/gdata/analytics/analytics/client.py b/gdata/analytics/analytics/client.py deleted file mode 100644 index dbfc5f7831..0000000000 --- a/gdata/analytics/analytics/client.py +++ /dev/null @@ -1,313 +0,0 @@ -#!/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' - ssl = True - - 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) diff --git a/gdata/analytics/analytics/data.py b/gdata/analytics/analytics/data.py deleted file mode 100644 index 6b17ef6e27..0000000000 --- a/gdata/analytics/analytics/data.py +++ /dev/null @@ -1,379 +0,0 @@ -#!/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 element to retrieve. - - Returns: - A property object corresponding to the matching 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 element to retrieve. - - Returns: - A property object corresponding to the matching 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 element to retrieve. - - Returns: - A dimension object corresponding to the matching 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 """ - _qname = DXP_NS % 'startDate' - - -class EndDate(atom.core.XmlElement): - """Analytics Feed """ - _qname = DXP_NS % 'endDate' - - -class Metric(atom.core.XmlElement): - """Analytics Feed """ - _qname = DXP_NS % 'metric' - name = 'name' - type = 'type' - value = 'value' - confidence_interval = 'confidenceInterval' - - -class Aggregates(atom.core.XmlElement, GetMetric): - """Analytics Data Feed """ - _qname = DXP_NS % 'aggregates' - metric = [Metric] - - -class ContainsSampledData(atom.core.XmlElement): - """Analytics Data Feed """ - _qname = DXP_NS % 'containsSampledData' - - -class TableId(atom.core.XmlElement): - """Analytics Feed """ - _qname = DXP_NS % 'tableId' - - -class TableName(atom.core.XmlElement): - """Analytics Feed """ - _qname = DXP_NS % 'tableName' - - -class Property(atom.core.XmlElement): - """Analytics Feed """ - _qname = DXP_NS % 'property' - name = 'name' - value = 'value' - - -class Definition(atom.core.XmlElement): - """Analytics Feed """ - _qname = DXP_NS % 'definition' - - -class Segment(atom.core.XmlElement): - """Analytics Feed """ - _qname = DXP_NS % 'segment' - id = 'id' - name = 'name' - definition = Definition - - -class Engagement(atom.core.XmlElement): - """Analytics Feed """ - _qname = GA_NS % 'engagement' - type = 'type' - comparison = 'comparison' - threshold_value = 'thresholdValue' - - -class Step(atom.core.XmlElement): - """Analytics Feed """ - _qname = GA_NS % 'step' - number = 'number' - name = 'name' - path = 'path' - - -class Destination(atom.core.XmlElement): - """Analytics Feed """ - _qname = GA_NS % 'destination' - step = [Step] - expression = 'expression' - case_sensitive = 'caseSensitive' - match_type = 'matchType' - step1_required = 'step1Required' - - -class Goal(atom.core.XmlElement): - """Analytics Feed """ - _qname = GA_NS % 'goal' - destination = Destination - engagement = Engagement - number = 'number' - name = 'name' - value = 'value' - active = 'active' - - -class CustomVariable(atom.core.XmlElement): - """Analytics Data Feed """ - _qname = GA_NS % 'customVariable' - index = 'index' - name = 'name' - scope = 'scope' - - -class DataSource(atom.core.XmlElement, GetProperty): - """Analytics Data Feed """ - _qname = DXP_NS % 'dataSource' - table_id = TableId - table_name = TableName - property = [Property] - - -class Dimension(atom.core.XmlElement): - """Analytics Feed """ - _qname = DXP_NS % 'dimension' - name = 'name' - value = 'value' - - -class AnalyticsLink(atom.data.Link): - """Subclass of 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 """ - _qname = atom.data.ATOM_TEMPLATE % 'entry' - table_id = TableId - property = [Property] - goal = [Goal] - custom_variable = [CustomVariable] - - -class AccountFeed(gdata.data.GDFeed): - """Analytics Account Feed """ - _qname = atom.data.ATOM_TEMPLATE % 'feed' - segment = [Segment] - entry = [AccountEntry] - - -# Data Feed. -class DataEntry(gdata.data.GDEntry, GetMetric, GetDimension): - """Analytics Data Feed """ - _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 . - - 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 - contains_sampled_data = ContainsSampledData - data_source = [DataSource] - entry = [DataEntry] - segment = Segment - - def has_sampled_data(self): - """Returns whether this feed has sampled data.""" - if (self.contains_sampled_data.text == 'true'): - return True - return False - - HasSampledData = has_sampled_data - - -# Management Feed. -class ManagementEntry(gdata.data.GDEntry, GetProperty, GaLinkFinder): - """Analytics Managememt 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 . - - 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' diff --git a/gdata/analytics/analytics/service.py b/gdata/analytics/analytics/service.py deleted file mode 100644 index 0638b48b45..0000000000 --- a/gdata/analytics/analytics/service.py +++ /dev/null @@ -1,331 +0,0 @@ -#!/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 diff --git a/gdata/analytics/apps/__init__.py b/gdata/analytics/apps/__init__.py deleted file mode 100644 index ebdf98ec9a..0000000000 --- a/gdata/analytics/apps/__init__.py +++ /dev/null @@ -1,526 +0,0 @@ -#!/usr/bin/python -# -# Copyright (C) 2007 SIOS Technology, 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. - -"""Contains objects used with Google Apps.""" - -__author__ = 'tmatsuo@sios.com (Takashi MATSUO)' - - -import atom -import gdata - - -# XML namespaces which are often used in Google Apps entity. -APPS_NAMESPACE = 'http://schemas.google.com/apps/2006' -APPS_TEMPLATE = '{http://schemas.google.com/apps/2006}%s' - - -class EmailList(atom.AtomBase): - """The Google Apps EmailList element""" - - _tag = 'emailList' - _namespace = APPS_NAMESPACE - _children = atom.AtomBase._children.copy() - _attributes = atom.AtomBase._attributes.copy() - _attributes['name'] = 'name' - - def __init__(self, name=None, extension_elements=None, - extension_attributes=None, text=None): - self.name = name - self.text = text - self.extension_elements = extension_elements or [] - self.extension_attributes = extension_attributes or {} - -def EmailListFromString(xml_string): - return atom.CreateClassFromXMLString(EmailList, xml_string) - - -class Who(atom.AtomBase): - """The Google Apps Who element""" - - _tag = 'who' - _namespace = gdata.GDATA_NAMESPACE - _children = atom.AtomBase._children.copy() - _attributes = atom.AtomBase._attributes.copy() - _attributes['rel'] = 'rel' - _attributes['email'] = 'email' - - def __init__(self, rel=None, email=None, extension_elements=None, - extension_attributes=None, text=None): - self.rel = rel - self.email = email - self.text = text - self.extension_elements = extension_elements or [] - self.extension_attributes = extension_attributes or {} - -def WhoFromString(xml_string): - return atom.CreateClassFromXMLString(Who, xml_string) - - -class Login(atom.AtomBase): - """The Google Apps Login element""" - - _tag = 'login' - _namespace = APPS_NAMESPACE - _children = atom.AtomBase._children.copy() - _attributes = atom.AtomBase._attributes.copy() - _attributes['userName'] = 'user_name' - _attributes['password'] = 'password' - _attributes['suspended'] = 'suspended' - _attributes['admin'] = 'admin' - _attributes['changePasswordAtNextLogin'] = 'change_password' - _attributes['agreedToTerms'] = 'agreed_to_terms' - _attributes['ipWhitelisted'] = 'ip_whitelisted' - _attributes['hashFunctionName'] = 'hash_function_name' - - def __init__(self, user_name=None, password=None, suspended=None, - ip_whitelisted=None, hash_function_name=None, - admin=None, change_password=None, agreed_to_terms=None, - extension_elements=None, extension_attributes=None, - text=None): - self.user_name = user_name - self.password = password - self.suspended = suspended - self.admin = admin - self.change_password = change_password - self.agreed_to_terms = agreed_to_terms - self.ip_whitelisted = ip_whitelisted - self.hash_function_name = hash_function_name - self.text = text - self.extension_elements = extension_elements or [] - self.extension_attributes = extension_attributes or {} - - -def LoginFromString(xml_string): - return atom.CreateClassFromXMLString(Login, xml_string) - - -class Quota(atom.AtomBase): - """The Google Apps Quota element""" - - _tag = 'quota' - _namespace = APPS_NAMESPACE - _children = atom.AtomBase._children.copy() - _attributes = atom.AtomBase._attributes.copy() - _attributes['limit'] = 'limit' - - def __init__(self, limit=None, extension_elements=None, - extension_attributes=None, text=None): - self.limit = limit - self.text = text - self.extension_elements = extension_elements or [] - self.extension_attributes = extension_attributes or {} - - -def QuotaFromString(xml_string): - return atom.CreateClassFromXMLString(Quota, xml_string) - - -class Name(atom.AtomBase): - """The Google Apps Name element""" - - _tag = 'name' - _namespace = APPS_NAMESPACE - _children = atom.AtomBase._children.copy() - _attributes = atom.AtomBase._attributes.copy() - _attributes['familyName'] = 'family_name' - _attributes['givenName'] = 'given_name' - - def __init__(self, family_name=None, given_name=None, - extension_elements=None, extension_attributes=None, text=None): - self.family_name = family_name - self.given_name = given_name - self.text = text - self.extension_elements = extension_elements or [] - self.extension_attributes = extension_attributes or {} - - -def NameFromString(xml_string): - return atom.CreateClassFromXMLString(Name, xml_string) - - -class Nickname(atom.AtomBase): - """The Google Apps Nickname element""" - - _tag = 'nickname' - _namespace = APPS_NAMESPACE - _children = atom.AtomBase._children.copy() - _attributes = atom.AtomBase._attributes.copy() - _attributes['name'] = 'name' - - def __init__(self, name=None, - extension_elements=None, extension_attributes=None, text=None): - self.name = name - self.text = text - self.extension_elements = extension_elements or [] - self.extension_attributes = extension_attributes or {} - - -def NicknameFromString(xml_string): - return atom.CreateClassFromXMLString(Nickname, xml_string) - - -class NicknameEntry(gdata.GDataEntry): - """A Google Apps flavor of an Atom Entry for Nickname""" - - _tag = 'entry' - _namespace = atom.ATOM_NAMESPACE - _children = gdata.GDataEntry._children.copy() - _attributes = gdata.GDataEntry._attributes.copy() - _children['{%s}login' % APPS_NAMESPACE] = ('login', Login) - _children['{%s}nickname' % APPS_NAMESPACE] = ('nickname', Nickname) - - def __init__(self, author=None, category=None, content=None, - atom_id=None, link=None, published=None, - title=None, updated=None, - login=None, nickname=None, - extended_property=None, - extension_elements=None, extension_attributes=None, text=None): - - gdata.GDataEntry.__init__(self, author=author, category=category, - content=content, - atom_id=atom_id, link=link, published=published, - title=title, updated=updated) - self.login = login - self.nickname = nickname - self.extended_property = extended_property or [] - self.text = text - self.extension_elements = extension_elements or [] - self.extension_attributes = extension_attributes or {} - - -def NicknameEntryFromString(xml_string): - return atom.CreateClassFromXMLString(NicknameEntry, xml_string) - - -class NicknameFeed(gdata.GDataFeed, gdata.LinkFinder): - """A Google Apps Nickname feed flavor of an Atom Feed""" - - _tag = 'feed' - _namespace = atom.ATOM_NAMESPACE - _children = gdata.GDataFeed._children.copy() - _attributes = gdata.GDataFeed._attributes.copy() - _children['{%s}entry' % atom.ATOM_NAMESPACE] = ('entry', [NicknameEntry]) - - def __init__(self, author=None, category=None, contributor=None, - generator=None, icon=None, atom_id=None, link=None, logo=None, - rights=None, subtitle=None, title=None, updated=None, - entry=None, total_results=None, start_index=None, - items_per_page=None, extension_elements=None, - extension_attributes=None, text=None): - gdata.GDataFeed.__init__(self, author=author, category=category, - contributor=contributor, generator=generator, - icon=icon, atom_id=atom_id, link=link, - logo=logo, rights=rights, subtitle=subtitle, - title=title, updated=updated, entry=entry, - total_results=total_results, - start_index=start_index, - items_per_page=items_per_page, - extension_elements=extension_elements, - extension_attributes=extension_attributes, - text=text) - - -def NicknameFeedFromString(xml_string): - return atom.CreateClassFromXMLString(NicknameFeed, xml_string) - - -class UserEntry(gdata.GDataEntry): - """A Google Apps flavor of an Atom Entry""" - - _tag = 'entry' - _namespace = atom.ATOM_NAMESPACE - _children = gdata.GDataEntry._children.copy() - _attributes = gdata.GDataEntry._attributes.copy() - _children['{%s}login' % APPS_NAMESPACE] = ('login', Login) - _children['{%s}name' % APPS_NAMESPACE] = ('name', Name) - _children['{%s}quota' % APPS_NAMESPACE] = ('quota', Quota) - # This child may already be defined in GDataEntry, confirm before removing. - _children['{%s}feedLink' % gdata.GDATA_NAMESPACE] = ('feed_link', - [gdata.FeedLink]) - _children['{%s}who' % gdata.GDATA_NAMESPACE] = ('who', Who) - - def __init__(self, author=None, category=None, content=None, - atom_id=None, link=None, published=None, - title=None, updated=None, - login=None, name=None, quota=None, who=None, feed_link=None, - extended_property=None, - extension_elements=None, extension_attributes=None, text=None): - - gdata.GDataEntry.__init__(self, author=author, category=category, - content=content, - atom_id=atom_id, link=link, published=published, - title=title, updated=updated) - self.login = login - self.name = name - self.quota = quota - self.who = who - self.feed_link = feed_link or [] - self.extended_property = extended_property or [] - self.text = text - self.extension_elements = extension_elements or [] - self.extension_attributes = extension_attributes or {} - - -def UserEntryFromString(xml_string): - return atom.CreateClassFromXMLString(UserEntry, xml_string) - - -class UserFeed(gdata.GDataFeed, gdata.LinkFinder): - """A Google Apps User feed flavor of an Atom Feed""" - - _tag = 'feed' - _namespace = atom.ATOM_NAMESPACE - _children = gdata.GDataFeed._children.copy() - _attributes = gdata.GDataFeed._attributes.copy() - _children['{%s}entry' % atom.ATOM_NAMESPACE] = ('entry', [UserEntry]) - - def __init__(self, author=None, category=None, contributor=None, - generator=None, icon=None, atom_id=None, link=None, logo=None, - rights=None, subtitle=None, title=None, updated=None, - entry=None, total_results=None, start_index=None, - items_per_page=None, extension_elements=None, - extension_attributes=None, text=None): - gdata.GDataFeed.__init__(self, author=author, category=category, - contributor=contributor, generator=generator, - icon=icon, atom_id=atom_id, link=link, - logo=logo, rights=rights, subtitle=subtitle, - title=title, updated=updated, entry=entry, - total_results=total_results, - start_index=start_index, - items_per_page=items_per_page, - extension_elements=extension_elements, - extension_attributes=extension_attributes, - text=text) - - -def UserFeedFromString(xml_string): - return atom.CreateClassFromXMLString(UserFeed, xml_string) - - -class EmailListEntry(gdata.GDataEntry): - """A Google Apps EmailList flavor of an Atom Entry""" - - _tag = 'entry' - _namespace = atom.ATOM_NAMESPACE - _children = gdata.GDataEntry._children.copy() - _attributes = gdata.GDataEntry._attributes.copy() - _children['{%s}emailList' % APPS_NAMESPACE] = ('email_list', EmailList) - # Might be able to remove this _children entry. - _children['{%s}feedLink' % gdata.GDATA_NAMESPACE] = ('feed_link', - [gdata.FeedLink]) - - def __init__(self, author=None, category=None, content=None, - atom_id=None, link=None, published=None, - title=None, updated=None, - email_list=None, feed_link=None, - extended_property=None, - extension_elements=None, extension_attributes=None, text=None): - - gdata.GDataEntry.__init__(self, author=author, category=category, - content=content, - atom_id=atom_id, link=link, published=published, - title=title, updated=updated) - self.email_list = email_list - self.feed_link = feed_link or [] - self.extended_property = extended_property or [] - self.text = text - self.extension_elements = extension_elements or [] - self.extension_attributes = extension_attributes or {} - - -def EmailListEntryFromString(xml_string): - return atom.CreateClassFromXMLString(EmailListEntry, xml_string) - - -class EmailListFeed(gdata.GDataFeed, gdata.LinkFinder): - """A Google Apps EmailList feed flavor of an Atom Feed""" - - _tag = 'feed' - _namespace = atom.ATOM_NAMESPACE - _children = gdata.GDataFeed._children.copy() - _attributes = gdata.GDataFeed._attributes.copy() - _children['{%s}entry' % atom.ATOM_NAMESPACE] = ('entry', [EmailListEntry]) - - def __init__(self, author=None, category=None, contributor=None, - generator=None, icon=None, atom_id=None, link=None, logo=None, - rights=None, subtitle=None, title=None, updated=None, - entry=None, total_results=None, start_index=None, - items_per_page=None, extension_elements=None, - extension_attributes=None, text=None): - gdata.GDataFeed.__init__(self, author=author, category=category, - contributor=contributor, generator=generator, - icon=icon, atom_id=atom_id, link=link, - logo=logo, rights=rights, subtitle=subtitle, - title=title, updated=updated, entry=entry, - total_results=total_results, - start_index=start_index, - items_per_page=items_per_page, - extension_elements=extension_elements, - extension_attributes=extension_attributes, - text=text) - - -def EmailListFeedFromString(xml_string): - return atom.CreateClassFromXMLString(EmailListFeed, xml_string) - - -class EmailListRecipientEntry(gdata.GDataEntry): - """A Google Apps EmailListRecipient flavor of an Atom Entry""" - - _tag = 'entry' - _namespace = atom.ATOM_NAMESPACE - _children = gdata.GDataEntry._children.copy() - _attributes = gdata.GDataEntry._attributes.copy() - _children['{%s}who' % gdata.GDATA_NAMESPACE] = ('who', Who) - - def __init__(self, author=None, category=None, content=None, - atom_id=None, link=None, published=None, - title=None, updated=None, - who=None, - extended_property=None, - extension_elements=None, extension_attributes=None, text=None): - - gdata.GDataEntry.__init__(self, author=author, category=category, - content=content, - atom_id=atom_id, link=link, published=published, - title=title, updated=updated) - self.who = who - self.extended_property = extended_property or [] - self.text = text - self.extension_elements = extension_elements or [] - self.extension_attributes = extension_attributes or {} - - -def EmailListRecipientEntryFromString(xml_string): - return atom.CreateClassFromXMLString(EmailListRecipientEntry, xml_string) - - -class EmailListRecipientFeed(gdata.GDataFeed, gdata.LinkFinder): - """A Google Apps EmailListRecipient feed flavor of an Atom Feed""" - - _tag = 'feed' - _namespace = atom.ATOM_NAMESPACE - _children = gdata.GDataFeed._children.copy() - _attributes = gdata.GDataFeed._attributes.copy() - _children['{%s}entry' % atom.ATOM_NAMESPACE] = ('entry', - [EmailListRecipientEntry]) - - def __init__(self, author=None, category=None, contributor=None, - generator=None, icon=None, atom_id=None, link=None, logo=None, - rights=None, subtitle=None, title=None, updated=None, - entry=None, total_results=None, start_index=None, - items_per_page=None, extension_elements=None, - extension_attributes=None, text=None): - gdata.GDataFeed.__init__(self, author=author, category=category, - contributor=contributor, generator=generator, - icon=icon, atom_id=atom_id, link=link, - logo=logo, rights=rights, subtitle=subtitle, - title=title, updated=updated, entry=entry, - total_results=total_results, - start_index=start_index, - items_per_page=items_per_page, - extension_elements=extension_elements, - extension_attributes=extension_attributes, - text=text) - - -def EmailListRecipientFeedFromString(xml_string): - return atom.CreateClassFromXMLString(EmailListRecipientFeed, xml_string) - - -class Property(atom.AtomBase): - """The Google Apps Property element""" - - _tag = 'property' - _namespace = APPS_NAMESPACE - _children = atom.AtomBase._children.copy() - _attributes = atom.AtomBase._attributes.copy() - _attributes['name'] = 'name' - _attributes['value'] = 'value' - - def __init__(self, name=None, value=None, extension_elements=None, - extension_attributes=None, text=None): - self.name = name - self.value = value - self.text = text - self.extension_elements = extension_elements or [] - self.extension_attributes = extension_attributes or {} - - -def PropertyFromString(xml_string): - return atom.CreateClassFromXMLString(Property, xml_string) - - -class PropertyEntry(gdata.GDataEntry): - """A Google Apps Property flavor of an Atom Entry""" - - _tag = 'entry' - _namespace = atom.ATOM_NAMESPACE - _children = gdata.GDataEntry._children.copy() - _attributes = gdata.GDataEntry._attributes.copy() - _children['{%s}property' % APPS_NAMESPACE] = ('property', [Property]) - - def __init__(self, author=None, category=None, content=None, - atom_id=None, link=None, published=None, - title=None, updated=None, - property=None, - extended_property=None, - extension_elements=None, extension_attributes=None, text=None): - - gdata.GDataEntry.__init__(self, author=author, category=category, - content=content, - atom_id=atom_id, link=link, published=published, - title=title, updated=updated) - self.property = property - self.extended_property = extended_property or [] - self.text = text - self.extension_elements = extension_elements or [] - self.extension_attributes = extension_attributes or {} - - -def PropertyEntryFromString(xml_string): - return atom.CreateClassFromXMLString(PropertyEntry, xml_string) - -class PropertyFeed(gdata.GDataFeed, gdata.LinkFinder): - """A Google Apps Property feed flavor of an Atom Feed""" - - _tag = 'feed' - _namespace = atom.ATOM_NAMESPACE - _children = gdata.GDataFeed._children.copy() - _attributes = gdata.GDataFeed._attributes.copy() - _children['{%s}entry' % atom.ATOM_NAMESPACE] = ('entry', [PropertyEntry]) - - def __init__(self, author=None, category=None, contributor=None, - generator=None, icon=None, atom_id=None, link=None, logo=None, - rights=None, subtitle=None, title=None, updated=None, - entry=None, total_results=None, start_index=None, - items_per_page=None, extension_elements=None, - extension_attributes=None, text=None): - gdata.GDataFeed.__init__(self, author=author, category=category, - contributor=contributor, generator=generator, - icon=icon, atom_id=atom_id, link=link, - logo=logo, rights=rights, subtitle=subtitle, - title=title, updated=updated, entry=entry, - total_results=total_results, - start_index=start_index, - items_per_page=items_per_page, - extension_elements=extension_elements, - extension_attributes=extension_attributes, - text=text) - -def PropertyFeedFromString(xml_string): - return atom.CreateClassFromXMLString(PropertyFeed, xml_string) diff --git a/gdata/analytics/apps/adminsettings/__init__.py b/gdata/analytics/apps/adminsettings/__init__.py deleted file mode 100644 index d284c7cee3..0000000000 --- a/gdata/analytics/apps/adminsettings/__init__.py +++ /dev/null @@ -1,16 +0,0 @@ -#!/usr/bin/python -# -# Copyright (C) 2008 Google -# -# 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. - diff --git a/gdata/analytics/apps/adminsettings/service.py b/gdata/analytics/apps/adminsettings/service.py deleted file mode 100644 index c69fa36bc9..0000000000 --- a/gdata/analytics/apps/adminsettings/service.py +++ /dev/null @@ -1,471 +0,0 @@ -#!/usr/bin/python -# -# Copyright (C) 2008 Google, 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. - -"""Allow Google Apps domain administrators to set domain admin settings. - - AdminSettingsService: Set admin settings.""" - -__author__ = 'jlee@pbu.edu' - - -import gdata.apps -import gdata.apps.service -import gdata.service - - -API_VER='2.0' - -class AdminSettingsService(gdata.apps.service.PropertyService): - """Client for the Google Apps Admin Settings service.""" - - def _serviceUrl(self, setting_id, domain=None): - if domain is None: - domain = self.domain - return '/a/feeds/domain/%s/%s/%s' % (API_VER, domain, setting_id) - - def genericGet(self, location): - """Generic HTTP Get Wrapper - - Args: - location: relative uri to Get - - Returns: - A dict containing the result of the get operation.""" - - uri = self._serviceUrl(location) - try: - return self._GetProperties(uri) - except gdata.service.RequestError, e: - raise AppsForYourDomainException(e.args[0]) - - def GetDefaultLanguage(self): - """Gets Domain Default Language - - Args: - None - - Returns: - Default Language as a string. All possible values are listed at: - http://code.google.com/apis/apps/email_settings/developers_guide_protocol.html#GA_email_language_tags""" - - result = self.genericGet('general/defaultLanguage') - return result['defaultLanguage'] - - def UpdateDefaultLanguage(self, defaultLanguage): - """Updates Domain Default Language - - Args: - defaultLanguage: Domain Language to set - possible values are at: - http://code.google.com/apis/apps/email_settings/developers_guide_protocol.html#GA_email_language_tags - - Returns: - A dict containing the result of the put operation""" - - uri = self._serviceUrl('general/defaultLanguage') - properties = {'defaultLanguage': defaultLanguage} - return self._PutProperties(uri, properties) - - def GetOrganizationName(self): - """Gets Domain Default Language - - Args: - None - - Returns: - Organization Name as a string.""" - - result = self.genericGet('general/organizationName') - return result['organizationName'] - - - def UpdateOrganizationName(self, organizationName): - """Updates Organization Name - - Args: - organizationName: Name of organization - - Returns: - A dict containing the result of the put operation""" - - uri = self._serviceUrl('general/organizationName') - properties = {'organizationName': organizationName} - return self._PutProperties(uri, properties) - - def GetMaximumNumberOfUsers(self): - """Gets Maximum Number of Users Allowed - - Args: - None - - Returns: An integer, the maximum number of users""" - - result = self.genericGet('general/maximumNumberOfUsers') - return int(result['maximumNumberOfUsers']) - - def GetCurrentNumberOfUsers(self): - """Gets Current Number of Users - - Args: - None - - Returns: An integer, the current number of users""" - - result = self.genericGet('general/currentNumberOfUsers') - return int(result['currentNumberOfUsers']) - - def IsDomainVerified(self): - """Is the domain verified - - Args: - None - - Returns: Boolean, is domain verified""" - - result = self.genericGet('accountInformation/isVerified') - if result['isVerified'] == 'true': - return True - else: - return False - - def GetSupportPIN(self): - """Gets Support PIN - - Args: - None - - Returns: A string, the Support PIN""" - - result = self.genericGet('accountInformation/supportPIN') - return result['supportPIN'] - - def GetEdition(self): - """Gets Google Apps Domain Edition - - Args: - None - - Returns: A string, the domain's edition (premier, education, partner)""" - - result = self.genericGet('accountInformation/edition') - return result['edition'] - - def GetCustomerPIN(self): - """Gets Customer PIN - - Args: - None - - Returns: A string, the customer PIN""" - - result = self.genericGet('accountInformation/customerPIN') - return result['customerPIN'] - - def GetCreationTime(self): - """Gets Domain Creation Time - - Args: - None - - Returns: A string, the domain's creation time""" - - result = self.genericGet('accountInformation/creationTime') - return result['creationTime'] - - def GetCountryCode(self): - """Gets Domain Country Code - - Args: - None - - Returns: A string, the domain's country code. Possible values at: - http://www.iso.org/iso/country_codes/iso_3166_code_lists/english_country_names_and_code_elements.htm""" - - result = self.genericGet('accountInformation/countryCode') - return result['countryCode'] - - def GetAdminSecondaryEmail(self): - """Gets Domain Admin Secondary Email Address - - Args: - None - - Returns: A string, the secondary email address for domain admin""" - - result = self.genericGet('accountInformation/adminSecondaryEmail') - return result['adminSecondaryEmail'] - - def UpdateAdminSecondaryEmail(self, adminSecondaryEmail): - """Gets Domain Creation Time - - Args: - adminSecondaryEmail: string, secondary email address of admin - - Returns: A dict containing the result of the put operation""" - - uri = self._serviceUrl('accountInformation/adminSecondaryEmail') - properties = {'adminSecondaryEmail': adminSecondaryEmail} - return self._PutProperties(uri, properties) - - def GetDomainLogo(self): - """Gets Domain Logo - - This function does not make use of the Google Apps Admin Settings API, - it does an HTTP Get of a url specific to the Google Apps domain. It is - included for completeness sake. - - Args: - None - - Returns: binary image file""" - - import urllib - url = 'http://www.google.com/a/cpanel/'+self.domain+'/images/logo.gif' - response = urllib.urlopen(url) - return response.read() - - def UpdateDomainLogo(self, logoImage): - """Update Domain's Custom Logo - - Args: - logoImage: binary image data - - Returns: A dict containing the result of the put operation""" - - from base64 import base64encode - uri = self._serviceUrl('appearance/customLogo') - properties = {'logoImage': base64encode(logoImage)} - return self._PutProperties(uri, properties) - - def GetCNAMEVerificationStatus(self): - """Gets Domain CNAME Verification Status - - Args: - None - - Returns: A dict {recordName, verified, verifiedMethod}""" - - return self.genericGet('verification/cname') - - def UpdateCNAMEVerificationStatus(self, verified): - """Updates CNAME Verification Status - - Args: - verified: boolean, True will retry verification process - - Returns: A dict containing the result of the put operation""" - - uri = self._serviceUrl('verification/cname') - properties = self.GetCNAMEVerificationStatus() - properties['verified'] = verified - return self._PutProperties(uri, properties) - - def GetMXVerificationStatus(self): - """Gets Domain MX Verification Status - - Args: - None - - Returns: A dict {verified, verifiedMethod}""" - - return self.genericGet('verification/mx') - - def UpdateMXVerificationStatus(self, verified): - """Updates MX Verification Status - - Args: - verified: boolean, True will retry verification process - - Returns: A dict containing the result of the put operation""" - - uri = self._serviceUrl('verification/mx') - properties = self.GetMXVerificationStatus() - properties['verified'] = verified - return self._PutProperties(uri, properties) - - def GetSSOSettings(self): - """Gets Domain Single Sign-On Settings - - Args: - None - - Returns: A dict {samlSignonUri, samlLogoutUri, changePasswordUri, enableSSO, ssoWhitelist, useDomainSpecificIssuer}""" - - return self.genericGet('sso/general') - - def UpdateSSOSettings(self, enableSSO=None, samlSignonUri=None, - samlLogoutUri=None, changePasswordUri=None, - ssoWhitelist=None, useDomainSpecificIssuer=None): - """Update SSO Settings. - - Args: - enableSSO: boolean, SSO Master on/off switch - samlSignonUri: string, SSO Login Page - samlLogoutUri: string, SSO Logout Page - samlPasswordUri: string, SSO Password Change Page - ssoWhitelist: string, Range of IP Addresses which will see SSO - useDomainSpecificIssuer: boolean, Include Google Apps Domain in Issuer - - Returns: - A dict containing the result of the update operation. - """ - uri = self._serviceUrl('sso/general') - - #Get current settings, replace Nones with '' - properties = self.GetSSOSettings() - if properties['samlSignonUri'] == None: - properties['samlSignonUri'] = '' - if properties['samlLogoutUri'] == None: - properties['samlLogoutUri'] = '' - if properties['changePasswordUri'] == None: - properties['changePasswordUri'] = '' - if properties['ssoWhitelist'] == None: - properties['ssoWhitelist'] = '' - - #update only the values we were passed - if enableSSO != None: - properties['enableSSO'] = gdata.apps.service._bool2str(enableSSO) - if samlSignonUri != None: - properties['samlSignonUri'] = samlSignonUri - if samlLogoutUri != None: - properties['samlLogoutUri'] = samlLogoutUri - if changePasswordUri != None: - properties['changePasswordUri'] = changePasswordUri - if ssoWhitelist != None: - properties['ssoWhitelist'] = ssoWhitelist - if useDomainSpecificIssuer != None: - properties['useDomainSpecificIssuer'] = gdata.apps.service._bool2str(useDomainSpecificIssuer) - - return self._PutProperties(uri, properties) - - def GetSSOKey(self): - """Gets Domain Single Sign-On Signing Key - - Args: - None - - Returns: A dict {modulus, exponent, algorithm, format}""" - - return self.genericGet('sso/signingkey') - - def UpdateSSOKey(self, signingKey): - """Update SSO Settings. - - Args: - signingKey: string, public key to be uploaded - - Returns: - A dict containing the result of the update operation.""" - - uri = self._serviceUrl('sso/signingkey') - properties = {'signingKey': signingKey} - return self._PutProperties(uri, properties) - - def IsUserMigrationEnabled(self): - """Is User Migration Enabled - - Args: - None - - Returns: - boolean, is user migration enabled""" - - result = self.genericGet('email/migration') - if result['enableUserMigration'] == 'true': - return True - else: - return False - - def UpdateUserMigrationStatus(self, enableUserMigration): - """Update User Migration Status - - Args: - enableUserMigration: boolean, user migration enable/disable - - Returns: - A dict containing the result of the update operation.""" - - uri = self._serviceUrl('email/migration') - properties = {'enableUserMigration': enableUserMigration} - return self._PutProperties(uri, properties) - - def GetOutboundGatewaySettings(self): - """Get Outbound Gateway Settings - - Args: - None - - Returns: - A dict {smartHost, smtpMode}""" - - uri = self._serviceUrl('email/gateway') - try: - return self._GetProperties(uri) - except gdata.service.RequestError, e: - raise AppsForYourDomainException(e.args[0]) - except TypeError: - #if no outbound gateway is set, we get a TypeError, - #catch it and return nothing... - return {'smartHost': None, 'smtpMode': None} - - def UpdateOutboundGatewaySettings(self, smartHost=None, smtpMode=None): - """Update Outbound Gateway Settings - - Args: - smartHost: string, ip address or hostname of outbound gateway - smtpMode: string, SMTP or SMTP_TLS - - Returns: - A dict containing the result of the update operation.""" - - uri = self._serviceUrl('email/gateway') - - #Get current settings, replace Nones with '' - properties = GetOutboundGatewaySettings() - if properties['smartHost'] == None: - properties['smartHost'] = '' - if properties['smtpMode'] == None: - properties['smtpMode'] = '' - - #If we were passed new values for smartHost or smtpMode, update them - if smartHost != None: - properties['smartHost'] = smartHost - if smtpMode != None: - properties['smtpMode'] = smtpMode - - return self._PutProperties(uri, properties) - - def AddEmailRoute(self, routeDestination, routeRewriteTo, routeEnabled, bounceNotifications, accountHandling): - """Adds Domain Email Route - - Args: - routeDestination: string, destination ip address or hostname - routeRewriteTo: boolean, rewrite smtp envelop To: - routeEnabled: boolean, enable disable email routing - bounceNotifications: boolean, send bound notificiations to sender - accountHandling: string, which to route, "allAccounts", "provisionedAccounts", "unknownAccounts" - - Returns: - A dict containing the result of the update operation.""" - - uri = self._serviceUrl('emailrouting') - properties = {} - properties['routeDestination'] = routeDestination - properties['routeRewriteTo'] = gdata.apps.service._bool2str(routeRewriteTo) - properties['routeEnabled'] = gdata.apps.service._bool2str(routeEnabled) - properties['bounceNotifications'] = gdata.apps.service._bool2str(bounceNotifications) - properties['accountHandling'] = accountHandling - return self._PostProperties(uri, properties) diff --git a/gdata/analytics/apps/audit/__init__.py b/gdata/analytics/apps/audit/__init__.py deleted file mode 100644 index 8b13789179..0000000000 --- a/gdata/analytics/apps/audit/__init__.py +++ /dev/null @@ -1 +0,0 @@ - diff --git a/gdata/analytics/apps/audit/service.py b/gdata/analytics/apps/audit/service.py deleted file mode 100644 index d8cf72cfca..0000000000 --- a/gdata/analytics/apps/audit/service.py +++ /dev/null @@ -1,277 +0,0 @@ -# Copyright (C) 2008 Google, 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. - -"""Allow Google Apps domain administrators to audit user data. - - AuditService: Set auditing.""" - -__author__ = 'jlee@pbu.edu' - -from base64 import b64encode - -import gdata.apps -import gdata.apps.service -import gdata.service - -class AuditService(gdata.apps.service.PropertyService): - """Client for the Google Apps Audit service.""" - - def _serviceUrl(self, setting_id, domain=None, user=None): - if domain is None: - domain = self.domain - if user is None: - return '/a/feeds/compliance/audit/%s/%s' % (setting_id, domain) - else: - return '/a/feeds/compliance/audit/%s/%s/%s' % (setting_id, domain, user) - - def updatePGPKey(self, pgpkey): - """Updates Public PGP Key Google uses to encrypt audit data - - Args: - pgpkey: string, ASCII text of PGP Public Key to be used - - Returns: - A dict containing the result of the POST operation.""" - - uri = self._serviceUrl('publickey') - b64pgpkey = b64encode(pgpkey) - properties = {} - properties['publicKey'] = b64pgpkey - return self._PostProperties(uri, properties) - - def createEmailMonitor(self, source_user, destination_user, end_date, - begin_date=None, incoming_headers_only=False, - outgoing_headers_only=False, drafts=False, - drafts_headers_only=False, chats=False, - chats_headers_only=False): - """Creates a email monitor, forwarding the source_users emails/chats - - Args: - source_user: string, the user whose email will be audited - destination_user: string, the user to receive the audited email - end_date: string, the date the audit will end in - "yyyy-MM-dd HH:mm" format, required - begin_date: string, the date the audit will start in - "yyyy-MM-dd HH:mm" format, leave blank to use current time - incoming_headers_only: boolean, whether to audit only the headers of - mail delivered to source user - outgoing_headers_only: boolean, whether to audit only the headers of - mail sent from the source user - drafts: boolean, whether to audit draft messages of the source user - drafts_headers_only: boolean, whether to audit only the headers of - mail drafts saved by the user - chats: boolean, whether to audit archived chats of the source user - chats_headers_only: boolean, whether to audit only the headers of - archived chats of the source user - - Returns: - A dict containing the result of the POST operation.""" - - uri = self._serviceUrl('mail/monitor', user=source_user) - properties = {} - properties['destUserName'] = destination_user - if begin_date is not None: - properties['beginDate'] = begin_date - properties['endDate'] = end_date - if incoming_headers_only: - properties['incomingEmailMonitorLevel'] = 'HEADER_ONLY' - else: - properties['incomingEmailMonitorLevel'] = 'FULL_MESSAGE' - if outgoing_headers_only: - properties['outgoingEmailMonitorLevel'] = 'HEADER_ONLY' - else: - properties['outgoingEmailMonitorLevel'] = 'FULL_MESSAGE' - if drafts: - if drafts_headers_only: - properties['draftMonitorLevel'] = 'HEADER_ONLY' - else: - properties['draftMonitorLevel'] = 'FULL_MESSAGE' - if chats: - if chats_headers_only: - properties['chatMonitorLevel'] = 'HEADER_ONLY' - else: - properties['chatMonitorLevel'] = 'FULL_MESSAGE' - return self._PostProperties(uri, properties) - - def getEmailMonitors(self, user): - """"Gets the email monitors for the given user - - Args: - user: string, the user to retrieve email monitors for - - Returns: - list results of the POST operation - - """ - uri = self._serviceUrl('mail/monitor', user=user) - return self._GetPropertiesList(uri) - - def deleteEmailMonitor(self, source_user, destination_user): - """Deletes the email monitor for the given user - - Args: - source_user: string, the user who is being monitored - destination_user: string, theuser who recieves the monitored emails - - Returns: - Nothing - """ - - uri = self._serviceUrl('mail/monitor', user=source_user+'/'+destination_user) - try: - return self._DeleteProperties(uri) - except gdata.service.RequestError, e: - raise AppsForYourDomainException(e.args[0]) - - def createAccountInformationRequest(self, user): - """Creates a request for account auditing details - - Args: - user: string, the user to request account information for - - Returns: - A dict containing the result of the post operation.""" - - uri = self._serviceUrl('account', user=user) - properties = {} - #XML Body is left empty - try: - return self._PostProperties(uri, properties) - except gdata.service.RequestError, e: - raise AppsForYourDomainException(e.args[0]) - - def getAccountInformationRequestStatus(self, user, request_id): - """Gets the status of an account auditing request - - Args: - user: string, the user whose account auditing details were requested - request_id: string, the request_id - - Returns: - A dict containing the result of the get operation.""" - - uri = self._serviceUrl('account', user=user+'/'+request_id) - try: - return self._GetProperties(uri) - except gdata.service.RequestError, e: - raise AppsForYourDomainException(e.args[0]) - - def getAllAccountInformationRequestsStatus(self): - """Gets the status of all account auditing requests for the domain - - Args: - None - - Returns: - list results of the POST operation - """ - - uri = self._serviceUrl('account') - return self._GetPropertiesList(uri) - - - def deleteAccountInformationRequest(self, user, request_id): - """Deletes the request for account auditing information - - Args: - user: string, the user whose account auditing details were requested - request_id: string, the request_id - - Returns: - Nothing - """ - - uri = self._serviceUrl('account', user=user+'/'+request_id) - try: - return self._DeleteProperties(uri) - except gdata.service.RequestError, e: - raise AppsForYourDomainException(e.args[0]) - - def createMailboxExportRequest(self, user, begin_date=None, end_date=None, include_deleted=False, search_query=None, headers_only=False): - """Creates a mailbox export request - - Args: - user: string, the user whose mailbox export is being requested - begin_date: string, date of earliest emails to export, optional, defaults to date of account creation - format is 'yyyy-MM-dd HH:mm' - end_date: string, date of latest emails to export, optional, defaults to current date - format is 'yyyy-MM-dd HH:mm' - include_deleted: boolean, whether to include deleted emails in export, mutually exclusive with search_query - search_query: string, gmail style search query, matched emails will be exported, mutually exclusive with include_deleted - - Returns: - A dict containing the result of the post operation.""" - - uri = self._serviceUrl('mail/export', user=user) - properties = {} - if begin_date is not None: - properties['beginDate'] = begin_date - if end_date is not None: - properties['endDate'] = end_date - if include_deleted is not None: - properties['includeDeleted'] = gdata.apps.service._bool2str(include_deleted) - if search_query is not None: - properties['searchQuery'] = search_query - if headers_only is True: - properties['packageContent'] = 'HEADER_ONLY' - else: - properties['packageContent'] = 'FULL_MESSAGE' - return self._PostProperties(uri, properties) - - def getMailboxExportRequestStatus(self, user, request_id): - """Gets the status of an mailbox export request - - Args: - user: string, the user whose mailbox were requested - request_id: string, the request_id - - Returns: - A dict containing the result of the get operation.""" - - uri = self._serviceUrl('mail/export', user=user+'/'+request_id) - try: - return self._GetProperties(uri) - except gdata.service.RequestError, e: - raise AppsForYourDomainException(e.args[0]) - - def getAllMailboxExportRequestsStatus(self): - """Gets the status of all mailbox export requests for the domain - - Args: - None - - Returns: - list results of the POST operation - """ - - uri = self._serviceUrl('mail/export') - return self._GetPropertiesList(uri) - - - def deleteMailboxExportRequest(self, user, request_id): - """Deletes the request for mailbox export - - Args: - user: string, the user whose mailbox were requested - request_id: string, the request_id - - Returns: - Nothing - """ - - uri = self._serviceUrl('mail/export', user=user+'/'+request_id) - try: - return self._DeleteProperties(uri) - except gdata.service.RequestError, e: - raise AppsForYourDomainException(e.args[0]) diff --git a/gdata/analytics/apps/emailsettings/__init__.py b/gdata/analytics/apps/emailsettings/__init__.py deleted file mode 100644 index 275c6a0b21..0000000000 --- a/gdata/analytics/apps/emailsettings/__init__.py +++ /dev/null @@ -1,15 +0,0 @@ -#!/usr/bin/python -# -# Copyright (C) 2008 Google -# -# 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. diff --git a/gdata/analytics/apps/emailsettings/client.py b/gdata/analytics/apps/emailsettings/client.py deleted file mode 100644 index 97294380b9..0000000000 --- a/gdata/analytics/apps/emailsettings/client.py +++ /dev/null @@ -1,557 +0,0 @@ -#!/usr/bin/python2.4 -# -# 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. - -"""EmailSettingsClient simplifies Email Settings API calls. - -EmailSettingsClient extends gdata.client.GDClient to ease interaction with -the Google Apps Email Settings API. These interactions include the ability -to create labels, filters, aliases, and update web-clip, forwarding, POP, -IMAP, vacation-responder, signature, language, and general settings, and -retrieve labels, send-as, forwarding, pop, imap, vacation and signature -settings. -""" - - -__author__ = 'Claudio Cherubino ' - - -import gdata.apps.emailsettings.data -import gdata.client - - -# Email Settings URI template -# The strings in this template are eventually replaced with the API version, -# Google Apps domain name, username, and settingID, respectively. -EMAIL_SETTINGS_URI_TEMPLATE = '/a/feeds/emailsettings/%s/%s/%s/%s' - - -# The settingID value for the label requests -SETTING_ID_LABEL = 'label' -# The settingID value for the filter requests -SETTING_ID_FILTER = 'filter' -# The settingID value for the send-as requests -SETTING_ID_SENDAS = 'sendas' -# The settingID value for the webclip requests -SETTING_ID_WEBCLIP = 'webclip' -# The settingID value for the forwarding requests -SETTING_ID_FORWARDING = 'forwarding' -# The settingID value for the POP requests -SETTING_ID_POP = 'pop' -# The settingID value for the IMAP requests -SETTING_ID_IMAP = 'imap' -# The settingID value for the vacation responder requests -SETTING_ID_VACATION_RESPONDER = 'vacation' -# The settingID value for the signature requests -SETTING_ID_SIGNATURE = 'signature' -# The settingID value for the language requests -SETTING_ID_LANGUAGE = 'language' -# The settingID value for the general requests -SETTING_ID_GENERAL = 'general' -# The settingID value for the delegation requests -SETTING_ID_DELEGATION = 'delegation' - -# The KEEP action for the email settings -ACTION_KEEP = 'KEEP' -# The ARCHIVE action for the email settings -ACTION_ARCHIVE = 'ARCHIVE' -# The DELETE action for the email settings -ACTION_DELETE = 'DELETE' - -# The ALL_MAIL setting for POP enable_for property -POP_ENABLE_FOR_ALL_MAIL = 'ALL_MAIL' -# The MAIL_FROM_NOW_ON setting for POP enable_for property -POP_ENABLE_FOR_MAIL_FROM_NOW_ON = 'MAIL_FROM_NOW_ON' - - -class EmailSettingsClient(gdata.client.GDClient): - """Client extension for the Google Email Settings API service. - - Attributes: - host: string The hostname for the Email Settings API service. - api_version: string The version of the Email Settings API. - """ - - host = 'apps-apis.google.com' - api_version = '2.0' - auth_service = 'apps' - auth_scopes = gdata.gauth.AUTH_SCOPES['apps'] - ssl = True - - def __init__(self, domain, auth_token=None, **kwargs): - """Constructs a new client for the Email Settings API. - - Args: - domain: string The Google Apps domain with Email Settings. - auth_token: (optional) gdata.gauth.ClientLoginToken, AuthSubToken, or - OAuthToken which authorizes this client to edit the email settings. - kwargs: The other parameters to pass to the gdata.client.GDClient - constructor. - """ - gdata.client.GDClient.__init__(self, auth_token=auth_token, **kwargs) - self.domain = domain - - def make_email_settings_uri(self, username, setting_id): - """Creates the URI for the Email Settings API call. - - Using this client's Google Apps domain, create the URI to setup - email settings for the given user in that domain. If params are provided, - append them as GET params. - - Args: - username: string The name of the user affected by this setting. - setting_id: string The key of the setting to be configured. - - Returns: - A string giving the URI for Email Settings API calls for this client's - Google Apps domain. - """ - if '@' in username: - username, domain = username.split('@', 1) - else: - domain = self.domain - uri = EMAIL_SETTINGS_URI_TEMPLATE % (self.api_version, domain, - username, setting_id) - return uri - - MakeEmailSettingsUri = make_email_settings_uri - - def create_label(self, username, name, **kwargs): - """Creates a label with the given properties. - - Args: - username: string The name of the user. - name: string The name of the label. - kwargs: The other parameters to pass to gdata.client.GDClient.post(). - - Returns: - gdata.apps.emailsettings.data.EmailSettingsLabel of the new resource. - """ - uri = self.MakeEmailSettingsUri(username=username, - setting_id=SETTING_ID_LABEL) - new_label = gdata.apps.emailsettings.data.EmailSettingsLabel( - uri=uri, name=name) - return self.post(new_label, uri, **kwargs) - - CreateLabel = create_label - - def retrieve_labels(self, username, **kwargs): - """Retrieves email labels for the specified username - - Args: - username: string The name of the user to get the labels for - - Returns: - A gdata.data.GDFeed of the user's email labels - """ - uri = self.MakeEmailSettingsUri(username=username, - setting_id=SETTING_ID_LABEL) - return self.GetFeed(uri, auth_token=None, query=None, **kwargs) - - RetrieveLabels = retrieve_labels - - def create_filter(self, username, from_address=None, - to_address=None, subject=None, has_the_word=None, - does_not_have_the_word=None, has_attachments=None, - label=None, mark_as_read=None, archive=None, **kwargs): - """Creates a filter with the given properties. - - Args: - username: string The name of the user. - from_address: string The source email address for the filter. - to_address: string (optional) The destination email address for - the filter. - subject: string (optional) The value the email must have in its - subject to be filtered. - has_the_word: string (optional) The value the email must have - in its subject or body to be filtered. - does_not_have_the_word: string (optional) The value the email - cannot have in its subject or body to be filtered. - has_attachments: string (optional) A boolean string representing - whether the email must have an attachment to be filtered. - label: string (optional) The name of the label to apply to - messages matching the filter criteria. - mark_as_read: Boolean (optional) Whether or not to mark - messages matching the filter criteria as read. - archive: Boolean (optional) Whether or not to move messages - matching to Archived state. - kwargs: The other parameters to pass to gdata.client.GDClient.post(). - - Returns: - gdata.apps.emailsettings.data.EmailSettingsFilter of the new resource. - """ - uri = self.MakeEmailSettingsUri(username=username, - setting_id=SETTING_ID_FILTER) - new_filter = gdata.apps.emailsettings.data.EmailSettingsFilter( - uri=uri, from_address=from_address, - to_address=to_address, subject=subject, - has_the_word=has_the_word, - does_not_have_the_word=does_not_have_the_word, - has_attachments=has_attachments, label=label, - mark_as_read=mark_as_read, archive=archive) - return self.post(new_filter, uri, **kwargs) - - CreateFilter = create_filter - - def create_send_as(self, username, name, address, reply_to=None, - make_default=None, **kwargs): - """Creates a send-as alias with the given properties. - - Args: - username: string The name of the user. - name: string The name that will appear in the "From" field. - address: string The email address that appears as the - origination address for emails sent by this user. - reply_to: string (optional) The address to be used as the reply-to - address in email sent using the alias. - make_default: Boolean (optional) Whether or not this alias should - become the default alias for this user. - kwargs: The other parameters to pass to gdata.client.GDClient.post(). - - Returns: - gdata.apps.emailsettings.data.EmailSettingsSendAsAlias of the - new resource. - """ - uri = self.MakeEmailSettingsUri(username=username, - setting_id=SETTING_ID_SENDAS) - new_alias = gdata.apps.emailsettings.data.EmailSettingsSendAsAlias( - uri=uri, name=name, address=address, - reply_to=reply_to, make_default=make_default) - return self.post(new_alias, uri, **kwargs) - - CreateSendAs = create_send_as - - def retrieve_send_as(self, username, **kwargs): - """Retrieves send-as aliases for the specified username - - Args: - username: string The name of the user to get the send-as for - - Returns: - A gdata.data.GDFeed of the user's send-as alias settings - """ - uri = self.MakeEmailSettingsUri(username=username, - setting_id=SETTING_ID_SENDAS) - return self.GetFeed(uri, auth_token=None, query=None, **kwargs) - - RetrieveSendAs = retrieve_send_as - - def update_webclip(self, username, enable, **kwargs): - """Enable/Disable Google Mail web clip. - - Args: - username: string The name of the user. - enable: Boolean Whether to enable showing Web clips. - kwargs: The other parameters to pass to the update method. - - Returns: - gdata.apps.emailsettings.data.EmailSettingsWebClip of the - updated resource. - """ - uri = self.MakeEmailSettingsUri(username=username, - setting_id=SETTING_ID_WEBCLIP) - new_webclip = gdata.apps.emailsettings.data.EmailSettingsWebClip( - uri=uri, enable=enable) - return self.update(new_webclip, **kwargs) - - UpdateWebclip = update_webclip - - def update_forwarding(self, username, enable, forward_to=None, - action=None, **kwargs): - """Update Google Mail Forwarding settings. - - Args: - username: string The name of the user. - enable: Boolean Whether to enable incoming email forwarding. - forward_to: (optional) string The address email will be forwarded to. - action: string (optional) The action to perform after forwarding - an email (ACTION_KEEP, ACTION_ARCHIVE, ACTION_DELETE). - kwargs: The other parameters to pass to the update method. - - Returns: - gdata.apps.emailsettings.data.EmailSettingsForwarding of the - updated resource - """ - uri = self.MakeEmailSettingsUri(username=username, - setting_id=SETTING_ID_FORWARDING) - new_forwarding = gdata.apps.emailsettings.data.EmailSettingsForwarding( - uri=uri, enable=enable, forward_to=forward_to, action=action) - return self.update(new_forwarding, **kwargs) - - UpdateForwarding = update_forwarding - - def retrieve_forwarding(self, username, **kwargs): - """Retrieves forwarding settings for the specified username - - Args: - username: string The name of the user to get the forwarding settings for - - Returns: - A gdata.data.GDEntry of the user's email forwarding settings - """ - uri = self.MakeEmailSettingsUri(username=username, - setting_id=SETTING_ID_FORWARDING) - return self.GetEntry(uri, auth_token=None, query=None, **kwargs) - - RetrieveForwarding = retrieve_forwarding - - def update_pop(self, username, enable, enable_for=None, action=None, - **kwargs): - """Update Google Mail POP settings. - - Args: - username: string The name of the user. - enable: Boolean Whether to enable incoming POP3 access. - enable_for: string (optional) Whether to enable POP3 for all mail - (POP_ENABLE_FOR_ALL_MAIL), or mail from now on - (POP_ENABLE_FOR_MAIL_FROM_NOW_ON). - action: string (optional) What Google Mail should do with its copy - of the email after it is retrieved using POP (ACTION_KEEP, - ACTION_ARCHIVE, ACTION_DELETE). - kwargs: The other parameters to pass to the update method. - - Returns: - gdata.apps.emailsettings.data.EmailSettingsPop of the updated resource. - """ - uri = self.MakeEmailSettingsUri(username=username, - setting_id=SETTING_ID_POP) - new_pop = gdata.apps.emailsettings.data.EmailSettingsPop( - uri=uri, enable=enable, - enable_for=enable_for, action=action) - return self.update(new_pop, **kwargs) - - UpdatePop = update_pop - - def retrieve_pop(self, username, **kwargs): - """Retrieves POP settings for the specified username - - Args: - username: string The name of the user to get the POP settings for - - Returns: - A gdata.data.GDEntry of the user's POP settings - """ - uri = self.MakeEmailSettingsUri(username=username, - setting_id=SETTING_ID_POP) - return self.GetEntry(uri, auth_token=None, query=None, **kwargs) - - RetrievePop = retrieve_pop - - def update_imap(self, username, enable, **kwargs): - """Update Google Mail IMAP settings. - - Args: - username: string The name of the user. - enable: Boolean Whether to enable IMAP access.language - kwargs: The other parameters to pass to the update method. - - Returns: - gdata.apps.emailsettings.data.EmailSettingsImap of the updated resource. - """ - uri = self.MakeEmailSettingsUri(username=username, - setting_id=SETTING_ID_IMAP) - new_imap = gdata.apps.emailsettings.data.EmailSettingsImap( - uri=uri, enable=enable) - return self.update(new_imap, **kwargs) - - UpdateImap = update_imap - - def retrieve_imap(self, username, **kwargs): - """Retrieves imap settings for the specified username - - Args: - username: string The name of the user to get the imap settings for - - Returns: - A gdata.data.GDEntry of the user's IMAP settings - """ - uri = self.MakeEmailSettingsUri(username=username, - setting_id=SETTING_ID_IMAP) - return self.GetEntry(uri, auth_token=None, query=None, **kwargs) - - RetrieveImap = retrieve_imap - - def update_vacation(self, username, enable, subject=None, message=None, - contacts_only=None, **kwargs): - """Update Google Mail vacation-responder settings. - - Args: - username: string The name of the user. - enable: Boolean Whether to enable the vacation responder. - subject: string (optional) The subject line of the vacation responder - autoresponse. - message: string (optional) The message body of the vacation responder - autoresponse. - contacts_only: Boolean (optional) Whether to only send autoresponses - to known contacts. - kwargs: The other parameters to pass to the update method. - - Returns: - gdata.apps.emailsettings.data.EmailSettingsVacationResponder of the - updated resource. - """ - uri = self.MakeEmailSettingsUri(username=username, - setting_id=SETTING_ID_VACATION_RESPONDER) - new_vacation = gdata.apps.emailsettings.data.EmailSettingsVacationResponder( - uri=uri, enable=enable, subject=subject, - message=message, contacts_only=contacts_only) - return self.update(new_vacation, **kwargs) - - UpdateVacation = update_vacation - - def retrieve_vacation(self, username, **kwargs): - """Retrieves vacation settings for the specified username - - Args: - username: string The name of the user to get the vacation settings for - - Returns: - A gdata.data.GDEntry of the user's vacation auto-responder settings - """ - uri = self.MakeEmailSettingsUri(username=username, - setting_id=SETTING_ID_VACATION_RESPONDER) - return self.GetEntry(uri, auth_token=None, query=None, **kwargs) - - RetrieveVacation = retrieve_vacation - - def update_signature(self, username, signature, **kwargs): - """Update Google Mail signature. - - Args: - username: string The name of the user. - signature: string The signature to be appended to outgoing messages. - kwargs: The other parameters to pass to the update method. - - Returns: - gdata.apps.emailsettings.data.EmailSettingsSignature of the - updated resource. - """ - uri = self.MakeEmailSettingsUri(username=username, - setting_id=SETTING_ID_SIGNATURE) - new_signature = gdata.apps.emailsettings.data.EmailSettingsSignature( - uri=uri, signature=signature) - return self.update(new_signature, **kwargs) - - UpdateSignature = update_signature - - def retrieve_signature(self, username, **kwargs): - """Retrieves signature settings for the specified username - - Args: - username: string The name of the user to get the signature settings for - - Returns: - A gdata.data.GDEntry of the user's signature settings - """ - uri = self.MakeEmailSettingsUri(username=username, - setting_id=SETTING_ID_SIGNATURE) - return self.GetEntry(uri, auth_token=None, query=None, **kwargs) - - RetrieveSignature = retrieve_signature - - def update_language(self, username, language, **kwargs): - """Update Google Mail language settings. - - Args: - username: string The name of the user. - language: string The language tag for Google Mail's display language. - kwargs: The other parameters to pass to the update method. - - Returns: - gdata.apps.emailsettings.data.EmailSettingsLanguage of the - updated resource. - """ - uri = self.MakeEmailSettingsUri(username=username, - setting_id=SETTING_ID_LANGUAGE) - new_language = gdata.apps.emailsettings.data.EmailSettingsLanguage( - uri=uri, language=language) - return self.update(new_language, **kwargs) - - UpdateLanguage = update_language - - def update_general_settings(self, username, page_size=None, shortcuts=None, - arrows=None, snippets=None, use_unicode=None, - **kwargs): - """Update Google Mail general settings. - - Args: - username: string The name of the user. - page_size: int (optional) The number of conversations to be shown per - page. - shortcuts: Boolean (optional) Whether to enable keyboard shortcuts. - arrows: Boolean (optional) Whether to display arrow-shaped personal - indicators next to email sent specifically to the user. - snippets: Boolean (optional) Whether to display snippets of the messages - in the inbox and when searching. - use_unicode: Boolean (optional) Whether to use UTF-8 (unicode) encoding - for all outgoing messages. - kwargs: The other parameters to pass to the update method. - - Returns: - gdata.apps.emailsettings.data.EmailSettingsGeneral of the - updated resource. - """ - uri = self.MakeEmailSettingsUri(username=username, - setting_id=SETTING_ID_GENERAL) - new_general = gdata.apps.emailsettings.data.EmailSettingsGeneral( - uri=uri, page_size=page_size, shortcuts=shortcuts, - arrows=arrows, snippets=snippets, use_unicode=use_unicode) - return self.update(new_general, **kwargs) - - UpdateGeneralSettings = update_general_settings - - def add_email_delegate(self, username, address, **kwargs): - """Add an email delegate to the mail account - - Args: - username: string The name of the user - address: string The email address of the delegated account - """ - uri = self.MakeEmailSettingsUri(username=username, - setting_id=SETTING_ID_DELEGATION) - new_delegation = gdata.apps.emailsettings.data.EmailSettingsDelegation( - uri=uri, address=address) - return self.post(new_delegation, uri, **kwargs) - - AddEmailDelegate = add_email_delegate - - def retrieve_email_delegates(self, username, **kwargs): - """Retrieve a feed of the email delegates for the specified username - - Args: - username: string The name of the user to get the email delegates for - - Returns: - A gdata.data.GDFeed of the user's email delegates - """ - uri = self.MakeEmailSettingsUri(username=username, - setting_id=SETTING_ID_DELEGATION) - return self.GetFeed(uri, auth_token=None, query=None, **kwargs) - - RetrieveEmailDelegates = retrieve_email_delegates - - def delete_email_delegate(self, username, address, **kwargs): - """Delete an email delegate from the specified account - - Args: - username: string The name of the user - address: string The email address of the delegated account - """ - uri = self.MakeEmailSettingsUri(username=username, - setting_id=SETTING_ID_DELEGATION) - uri = uri + '/' + address - return self.delete(uri, **kwargs) - - DeleteEmailDelegate = delete_email_delegate diff --git a/gdata/analytics/apps/emailsettings/data.py b/gdata/analytics/apps/emailsettings/data.py deleted file mode 100644 index ab908de71c..0000000000 --- a/gdata/analytics/apps/emailsettings/data.py +++ /dev/null @@ -1,1174 +0,0 @@ -#!/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 the Email Settings API.""" - - -__author__ = 'Claudio Cherubino ' - - -import atom.data -import gdata.apps -import gdata.apps_property -import gdata.data - - -# This is required to work around a naming conflict between the Google -# Spreadsheets API and Python's built-in property function -pyproperty = property - - -# The apps:property label of the label property -LABEL_NAME = 'label' - -# The apps:property from of the filter property -FILTER_FROM_NAME = 'from' -# The apps:property to of the filter property -FILTER_TO_NAME = 'to' -# The apps:property subject of the filter property -FILTER_SUBJECT_NAME = 'subject' -# The apps:property hasTheWord of the filter property -FILTER_HAS_THE_WORD_NAME = 'hasTheWord' -# The apps:property doesNotHaveTheWord of the filter property -FILTER_DOES_NOT_HAVE_THE_WORD_NAME = 'doesNotHaveTheWord' -# The apps:property hasAttachment of the filter property -FILTER_HAS_ATTACHMENTS_NAME = 'hasAttachment' -# The apps:property label of the filter action property -FILTER_LABEL = 'label' -# The apps:property shouldMarkAsRead of the filter action property -FILTER_MARK_AS_READ = 'shouldMarkAsRead' -# The apps:property shouldArchive of the filter action propertylabel -FILTER_ARCHIVE = 'shouldArchive' - -# The apps:property name of the send-as alias property -SENDAS_ALIAS_NAME = 'name' -# The apps:property address of theAPPS_TEMPLATE send-as alias property -SENDAS_ALIAS_ADDRESS = 'address' -# The apps:property replyTo of the send-as alias property -SENDAS_ALIAS_REPLY_TO = 'replyTo' -# The apps:property makeDefault of the send-as alias property -SENDAS_ALIAS_MAKE_DEFAULT = 'makeDefault' - -# The apps:property enable of the webclip property -WEBCLIP_ENABLE = 'enable' - -# The apps:property enable of the forwarding property -FORWARDING_ENABLE = 'enable' -# The apps:property forwardTo of the forwarding property -FORWARDING_TO = 'forwardTo' -# The apps:property action of the forwarding property -FORWARDING_ACTION = 'action' - -# The apps:property enable of the POP property -POP_ENABLE = 'enable' -# The apps:property enableFor of the POP propertyACTION -POP_ENABLE_FOR = 'enableFor' -# The apps:property action of the POP property -POP_ACTION = 'action' - -# The apps:property enable of the IMAP property -IMAP_ENABLE = 'enable' - -# The apps:property enable of the vacation responder property -VACATION_RESPONDER_ENABLE = 'enable' -# The apps:property subject of the vacation responder property -VACATION_RESPONDER_SUBJECT = 'subject' -# The apps:property message of the vacation responder property -VACATION_RESPONDER_MESSAGE = 'message' -# The apps:property contactsOnly of the vacation responder property -VACATION_RESPONDER_CONTACTS_ONLY = 'contactsOnly' - -# The apps:property signature of the signature property -SIGNATURE_VALUE = 'signature' - -# The apps:property language of the language property -LANGUAGE_TAG = 'language' - -# The apps:property pageSize of the general settings property -GENERAL_PAGE_SIZE = 'pageSize' -# The apps:property shortcuts of the general settings property -GENERAL_SHORTCUTS = 'shortcuts' -# The apps:property arrows of the general settings property -GENERAL_ARROWS = 'arrows' -# The apps:prgdata.appsoperty snippets of the general settings property -GENERAL_SNIPPETS = 'snippets' -# The apps:property uniAppsProcode of the general settings property -GENERAL_UNICODE = 'unicode' - -# The apps:property delegationId of the email delegation property -DELEGATION_ID = "delegationId" -# The apps:property address of the email delegation property -DELEGATION_ADDRESS = 'address' -# The apps:property delegate of the email delegation property -DELEGATION_DELEGATE = "delegate" -# The apps:property status of the email delegation property -DELEGATION_STATUS = "status" - -class EmailSettingsEntry(gdata.data.GDEntry): - """Represents an Email Settings entry in object form.""" - - property = [gdata.apps_property.AppsProperty] - - def _GetProperty(self, name): - """Get the apps:property value with the given name. - - Args: - name: string Name of the apps:property value to get. - - Returns: - The apps:property value with the given name, or None if the name was - invalid. - """ - - value = None - for p in self.property: - if p.name == name: - value = p.value - break - return value - - def _SetProperty(self, name, value): - """Set the apps:property value with the given name to the given value. - - Args: - name: string Name of the apps:property value to set. - value: string Value to give the apps:property value with the given name. - """ - found = False - for i in range(len(self.property)): - if self.property[i].name == name: - self.property[i].value = value - found = True - break - if not found: - self.property.append(gdata.apps_property.AppsProperty(name=name, value=value)) - - def find_edit_link(self): - return self.uri - - -class EmailSettingsLabel(EmailSettingsEntry): - """Represents a Label in object form.""" - - def GetName(self): - """Get the name of the Label object. - - Returns: - The name of this Label object as a string or None. - """ - - return self._GetProperty(LABEL_NAME) - - def SetName(self, value): - """Set the name of this Label object. - - Args: - value: string The new label name to give this object. - """ - - self._SetProperty(LABEL_NAME, value) - - name = pyproperty(GetName, SetName) - - def __init__(self, uri=None, name=None, *args, **kwargs): - """Constructs a new EmailSettingsLabel object with the given arguments. - - Args: - uri: string (optional) The uri of of this object for HTTP requests. - name: string (optional) The name to give this new object. - args: The other parameters to pass to gdata.entry.GDEntry constructor. - kwargs: The other parameters to pass to gdata.entry.GDEntry constructor. - """ - super(EmailSettingsLabel, self).__init__(*args, **kwargs) - if uri: - self.uri = uri - if name: - self.name = name - - -class EmailSettingsFilter(EmailSettingsEntry): - """Represents an Email Settings Filter in object form.""" - - def GetFrom(self): - """Get the From value of the Filter object. - - Returns: - The From value of this Filter object as a string or None. - """ - - return self._GetProperty(FILTER_FROM_NAME) - - def SetFrom(self, value): - """Set the From value of this Filter object. - - Args: - value: string The new From value to give this object. - """ - - self._SetProperty(FILTER_FROM_NAME, value) - - from_address = pyproperty(GetFrom, SetFrom) - - def GetTo(self): - """Get the To value of the Filter object. - - Returns: - The To value of this Filter object as a string or None. - """ - - return self._GetProperty(FILTER_TO_NAME) - - def SetTo(self, value): - """Set the To value of this Filter object. - - Args: - value: string The new To value to give this object. - """ - - self._SetProperty(FILTER_TO_NAME, value) - - to_address = pyproperty(GetTo, SetTo) - - def GetSubject(self): - """Get the Subject value of the Filter object. - - Returns: - The Subject value of this Filter object as a string or None. - """ - - return self._GetProperty(FILTER_SUBJECT_NAME) - - def SetSubject(self, value): - """Set the Subject value of this Filter object. - - Args: - value: string The new Subject value to give this object. - """ - - self._SetProperty(FILTER_SUBJECT_NAME, value) - - subject = pyproperty(GetSubject, SetSubject) - - def GetHasTheWord(self): - """Get the HasTheWord value of the Filter object. - - Returns: - The HasTheWord value of this Filter object as a string or None. - """ - - return self._GetProperty(FILTER_HAS_THE_WORD_NAME) - - def SetHasTheWord(self, value): - """Set the HasTheWord value of this Filter object. - - Args: - value: string The new HasTheWord value to give this object. - """ - - self._SetProperty(FILTER_HAS_THE_WORD_NAME, value) - - has_the_word = pyproperty(GetHasTheWord, SetHasTheWord) - - def GetDoesNotHaveTheWord(self): - """Get the DoesNotHaveTheWord value of the Filter object. - - Returns: - The DoesNotHaveTheWord value of this Filter object as a string or None. - """ - - return self._GetProperty(FILTER_DOES_NOT_HAVE_THE_WORD_NAME) - - def SetDoesNotHaveTheWord(self, value): - """Set the DoesNotHaveTheWord value of this Filter object. - - Args: - value: string The new DoesNotHaveTheWord value to give this object. - """ - - self._SetProperty(FILTER_DOES_NOT_HAVE_THE_WORD_NAME, value) - - does_not_have_the_word = pyproperty(GetDoesNotHaveTheWord, - SetDoesNotHaveTheWord) - - def GetHasAttachments(self): - """Get the HasAttachments value of the Filter object. - - Returns: - The HasAttachments value of this Filter object as a string or None. - """ - - return self._GetProperty(FILTER_HAS_ATTACHMENTS_NAME) - - def SetHasAttachments(self, value): - """Set the HasAttachments value of this Filter object. - - Args: - value: string The new HasAttachments value to give this object. - """ - - self._SetProperty(FILTER_HAS_ATTACHMENTS_NAME, value) - - has_attachments = pyproperty(GetHasAttachments, - SetHasAttachments) - - def GetLabel(self): - """Get the Label value of the Filter object. - - Returns: - The Label value of this Filter object as a string or None. - """ - - return self._GetProperty(FILTER_LABEL) - - def SetLabel(self, value): - """Set the Label value of this Filter object. - - Args: - value: string The new Label value to give this object. - """ - - self._SetProperty(FILTER_LABEL, value) - - label = pyproperty(GetLabel, SetLabel) - - def GetMarkAsRead(self): - """Get the MarkAsRead value of the Filter object. - - Returns: - The MarkAsRead value of this Filter object as a string or None. - """ - - return self._GetProperty(FILTER_MARK_AS_READ) - - def SetMarkAsRead(self, value): - """Set the MarkAsRead value of this Filter object. - - Args: - value: string The new MarkAsRead value to give this object. - """ - - self._SetProperty(FILTER_MARK_AS_READ, value) - - mark_as_read = pyproperty(GetMarkAsRead, SetMarkAsRead) - - def GetArchive(self): - """Get the Archive value of the Filter object. - - Returns: - The Archive value of this Filter object as a string or None. - """ - - return self._GetProperty(FILTER_ARCHIVE) - - def SetArchive(self, value): - """Set the Archive value of this Filter object. - - Args: - value: string The new Archive value to give this object. - """ - - self._SetProperty(FILTER_ARCHIVE, value) - - archive = pyproperty(GetArchive, SetArchive) - - def __init__(self, uri=None, from_address=None, to_address=None, - subject=None, has_the_word=None, does_not_have_the_word=None, - has_attachments=None, label=None, mark_as_read=None, - archive=None, *args, **kwargs): - """Constructs a new EmailSettingsFilter object with the given arguments. - - Args: - uri: string (optional) The uri of of this object for HTTP requests. - from_address: string (optional) The source email address for the filter. - to_address: string (optional) The destination email address for - the filter. - subject: string (optional) The value the email must have in its - subject to be filtered. - has_the_word: string (optional) The value the email must have in its - subject or body to be filtered. - does_not_have_the_word: string (optional) The value the email cannot - have in its subject or body to be filtered. - has_attachments: Boolean (optional) Whether or not the email must - have an attachment to be filtered. - label: string (optional) The name of the label to apply to - messages matching the filter criteria. - mark_as_read: Boolean (optional) Whether or not to mark messages - matching the filter criteria as read. - archive: Boolean (optional) Whether or not to move messages - matching to Archived state. - args: The other parameters to pass to gdata.entry.GDEntry constructor. - kwargs: The other parameters to pass to gdata.entry.GDEntry constructor. - """ - super(EmailSettingsFilter, self).__init__(*args, **kwargs) - if uri: - self.uri = uri - if from_address: - self.from_address = from_address - if to_address: - self.to_address = to_address - if subject: - self.subject = subject - if has_the_word: - self.has_the_word = has_the_word - if does_not_have_the_word: - self.does_not_have_the_word = does_not_have_the_word - if has_attachments is not None: - self.has_attachments = str(has_attachments) - if label: - self.label = label - if mark_as_read is not None: - self.mark_as_read = str(mark_as_read) - if archive is not None: - self.archive = str(archive) - - -class EmailSettingsSendAsAlias(EmailSettingsEntry): - """Represents an Email Settings send-as Alias in object form.""" - - def GetName(self): - """Get the Name of the send-as Alias object. - - Returns: - The Name of this send-as Alias object as a string or None. - """ - - return self._GetProperty(SENDAS_ALIAS_NAME) - - def SetName(self, value): - """Set the Name of this send-as Alias object. - - Args: - value: string The new Name to give this object. - """ - - self._SetProperty(SENDAS_ALIAS_NAME, value) - - name = pyproperty(GetName, SetName) - - def GetAddress(self): - """Get the Address of the send-as Alias object. - - Returns: - The Address of this send-as Alias object as a string or None. - """ - - return self._GetProperty(SENDAS_ALIAS_ADDRESS) - - def SetAddress(self, value): - """Set the Address of this send-as Alias object. - - Args: - value: string The new Address to give this object. - """ - - self._SetProperty(SENDAS_ALIAS_ADDRESS, value) - - address = pyproperty(GetAddress, SetAddress) - - def GetReplyTo(self): - """Get the ReplyTo address of the send-as Alias object. - - Returns: - The ReplyTo address of this send-as Alias object as a string or None. - """ - - return self._GetProperty(SENDAS_ALIAS_REPLY_TO) - - def SetReplyTo(self, value): - """Set the ReplyTo address of this send-as Alias object. - - Args: - value: string The new ReplyTo address to give this object. - """ - - self._SetProperty(SENDAS_ALIAS_REPLY_TO, value) - - reply_to = pyproperty(GetReplyTo, SetReplyTo) - - def GetMakeDefault(self): - """Get the MakeDefault value of the send-as Alias object. - - Returns: - The MakeDefault value of this send-as Alias object as a string or None. - """ - - return self._GetProperty(SENDAS_ALIAS_MAKE_DEFAULT) - - def SetMakeDefault(self, value): - """Set the MakeDefault value of this send-as Alias object. - - Args: - value: string The new MakeDefault valueto give this object.WebClip - """ - - self._SetProperty(SENDAS_ALIAS_MAKE_DEFAULT, value) - - make_default = pyproperty(GetMakeDefault, SetMakeDefault) - - def __init__(self, uri=None, name=None, address=None, reply_to=None, - make_default=None, *args, **kwargs): - """Constructs a new EmailSettingsSendAsAlias object with the given - arguments. - - Args: - uri: string (optional) The uri of of this object for HTTP requests. - name: string (optional) The name that will appear in the "From" field - for this user. - address: string (optional) The email address that appears as the - origination address for emails sent by this user. - reply_to: string (optional) The address to be used as the reply-to - address in email sent using the alias. - make_default: Boolean (optional) Whether or not this alias should - become the default alias for this user. - args: The other parameters to pass to gdata.entry.GDEntry constructor. - kwargs: The other parameters to pass to gdata.entry.GDEntry constructor. - """ - super(EmailSettingsSendAsAlias, self).__init__(*args, **kwargs) - if uri: - self.uri = uri - if name: - self.name = name - if address: - self.address = address - if reply_to: - self.reply_to = reply_to - if make_default is not None: - self.make_default = str(make_default) - - -class EmailSettingsWebClip(EmailSettingsEntry): - """Represents a WebClip in object form.""" - - def GetEnable(self): - """Get the Enable value of the WebClip object. - - Returns: - The Enable value of this WebClip object as a string or None. - """ - - return self._GetProperty(WEBCLIP_ENABLE) - - def SetEnable(self, value): - """Set the Enable value of this WebClip object. - - Args: - value: string The new Enable value to give this object. - """ - - self._SetProperty(WEBCLIP_ENABLE, value) - - enable = pyproperty(GetEnable, SetEnable) - - def __init__(self, uri=None, enable=None, *args, **kwargs): - """Constructs a new EmailSettingsWebClip object with the given arguments. - - Args: - uri: string (optional) The uri of of this object for HTTP requests. - enable: Boolean (optional) Whether to enable showing Web clips. - args: The other parameters to pass to gdata.entry.GDEntry constructor. - kwargs: The other parameters to pass to gdata.entry.GDEntry constructor. - """ - super(EmailSettingsWebClip, self).__init__(*args, **kwargs) - if uri: - self.uri = uri - if enable is not None: - self.enable = str(enable) - - -class EmailSettingsForwarding(EmailSettingsEntry): - """Represents Forwarding settings in object form.""" - - def GetEnable(self): - """Get the Enable value of the Forwarding object. - - Returns: - The Enable value of this Forwarding object as a string or None. - """ - - return self._GetProperty(FORWARDING_ENABLE) - - def SetEnable(self, value): - """Set the Enable value of this Forwarding object. - - Args: - value: string The new Enable value to give this object. - """ - - self._SetProperty(FORWARDING_ENABLE, value) - - enable = pyproperty(GetEnable, SetEnable) - - def GetForwardTo(self): - """Get the ForwardTo value of the Forwarding object. - - Returns: - The ForwardTo value of this Forwarding object as a string or None. - """ - - return self._GetProperty(FORWARDING_TO) - - def SetForwardTo(self, value): - """Set the ForwardTo value of this Forwarding object. - - Args: - value: string The new ForwardTo value to give this object. - """ - - self._SetProperty(FORWARDING_TO, value) - - forward_to = pyproperty(GetForwardTo, SetForwardTo) - - def GetAction(self): - """Get the Action value of the Forwarding object. - - Returns: - The Action value of this Forwarding object as a string or None. - """ - - return self._GetProperty(FORWARDING_ACTION) - - def SetAction(self, value): - """Set the Action value of this Forwarding object. - - Args: - value: string The new Action value to give this object. - """ - - self._SetProperty(FORWARDING_ACTION, value) - - action = pyproperty(GetAction, SetAction) - - def __init__(self, uri=None, enable=None, forward_to=None, action=None, - *args, **kwargs): - """Constructs a new EmailSettingsForwarding object with the given arguments. - - Args: - uri: string (optional) The uri of of this object for HTTP requests. - enable: Boolean (optional) Whether to enable incoming email forwarding. - forward_to: string (optional) The address email will be forwarded to. - action: string (optional) The action to perform after forwarding an - email ("KEEP", "ARCHIVE", "DELETE"). - args: The other parameters to pass to gdata.entry.GDEntry constructor. - kwargs: The other parameters to pass to gdata.entry.GDEntry constructor. - """ - super(EmailSettingsForwarding, self).__init__(*args, **kwargs) - if uri: - self.uri = uri - if enable is not None: - self.enable = str(enable) - if forward_to: - self.forward_to = forward_to - if action: - self.action = action - - -class EmailSettingsPop(EmailSettingsEntry): - """Represents POP settings in object form.""" - - def GetEnable(self): - """Get the Enable value of the POP object. - - Returns: - The Enable value of this POP object as a string or None. - """ - - return self._GetProperty(POP_ENABLE) - - def SetEnable(self, value): - """Set the Enable value of this POP object. - - Args: - value: string The new Enable value to give this object. - """ - - self._SetProperty(POP_ENABLE, value) - - enable = pyproperty(GetEnable, SetEnable) - - def GetEnableFor(self): - """Get the EnableFor value of the POP object. - - Returns: - The EnableFor value of this POP object as a string or None. - """ - - return self._GetProperty(POP_ENABLE_FOR) - - def SetEnableFor(self, value): - """Set the EnableFor value of this POP object. - - Args: - value: string The new EnableFor value to give this object. - """ - - self._SetProperty(POP_ENABLE_FOR, value) - - enable_for = pyproperty(GetEnableFor, SetEnableFor) - - def GetPopAction(self): - """Get the Action value of the POP object. - - Returns: - The Action value of this POP object as a string or None. - """ - - return self._GetProperty(POP_ACTION) - - def SetPopAction(self, value): - """Set the Action value of this POP object. - - Args: - value: string The new Action value to give this object. - """ - - self._SetProperty(POP_ACTION, value) - - action = pyproperty(GetPopAction, SetPopAction) - - def __init__(self, uri=None, enable=None, enable_for=None, - action=None, *args, **kwargs): - """Constructs a new EmailSettingsPOP object with the given arguments. - - Args: - uri: string (optional) The uri of of this object for HTTP requests. - enable: Boolean (optional) Whether to enable incoming POP3 access. - enable_for: string (optional) Whether to enable POP3 for all mail - ("ALL_MAIL"), or mail from now on ("MAIL_FROM_NOW_ON"). - action: string (optional) What Google Mail should do with its copy - of the email after it is retrieved using POP - ("KEEP", "ARCHIVE", or "DELETE"). - args: The other parameters to pass to gdata.entry.GDEntry constructor. - kwargs: The other parameters to pass to gdata.entry.GDEntry constructor. - """ - super(EmailSettingsPop, self).__init__(*args, **kwargs) - if uri: - self.uri = uri - if enable is not None: - self.enable = str(enable) - if enable_for: - self.enable_for = enable_for - if action: - self.action = action - - -class EmailSettingsImap(EmailSettingsEntry): - """Represents IMAP settings in object form.""" - - def GetEnable(self): - """Get the Enable value of the IMAP object. - - Returns: - The Enable value of this IMAP object as a string or None. - """ - - return self._GetProperty(IMAP_ENABLE) - - def SetEnable(self, value): - """Set the Enable value of this IMAP object. - - Args: - value: string The new Enable value to give this object. - """ - - self._SetProperty(IMAP_ENABLE, value) - - enable = pyproperty(GetEnable, SetEnable) - - def __init__(self, uri=None, enable=None, *args, **kwargs): - """Constructs a new EmailSettingsImap object with the given arguments. - - Args: - uri: string (optional) The uri of of this object for HTTP requests. - enable: Boolean (optional) Whether to enable IMAP access. - args: The other parameters to pass to gdata.entry.GDEntry constructor. - kwargs: The other parameters to pass to gdata.entry.GDEntry constructor. - """ - super(EmailSettingsImap, self).__init__(*args, **kwargs) - if uri: - self.uri = uri - if enable is not None: - self.enable = str(enable) - - -class EmailSettingsVacationResponder(EmailSettingsEntry): - """Represents Vacation Responder settings in object form.""" - - def GetEnable(self): - """Get the Enable value of the Vacation Responder object. - - Returns: - The Enable value of this Vacation Responder object as a string or None. - """ - - return self._GetProperty(VACATION_RESPONDER_ENABLE) - - def SetEnable(self, value): - """Set the Enable value of this Vacation Responder object. - - Args: - value: string The new Enable value to give this object. - """ - - self._SetProperty(VACATION_RESPONDER_ENABLE, value) - - enable = pyproperty(GetEnable, SetEnable) - - def GetSubject(self): - """Get the Subject value of the Vacation Responder object. - - Returns: - The Subject value of this Vacation Responder object as a string or None. - """ - - return self._GetProperty(VACATION_RESPONDER_SUBJECT) - - def SetSubject(self, value): - """Set the Subject value of this Vacation Responder object. - - Args: - value: string The new Subject value to give this object. - """ - - self._SetProperty(VACATION_RESPONDER_SUBJECT, value) - - subject = pyproperty(GetSubject, SetSubject) - - def GetMessage(self): - """Get the Message value of the Vacation Responder object. - - Returns: - The Message value of this Vacation Responder object as a string or None. - """ - - return self._GetProperty(VACATION_RESPONDER_MESSAGE) - - def SetMessage(self, value): - """Set the Message value of this Vacation Responder object. - - Args: - value: string The new Message value to give this object. - """ - - self._SetProperty(VACATION_RESPONDER_MESSAGE, value) - - message = pyproperty(GetMessage, SetMessage) - - def GetContactsOnly(self): - """Get the ContactsOnly value of the Vacation Responder object. - - Returns: - The ContactsOnly value of this Vacation Responder object as a - string or None. - """ - - return self._GetProperty(VACATION_RESPONDER_CONTACTS_ONLY) - - def SetContactsOnly(self, value): - """Set the ContactsOnly value of this Vacation Responder object. - - Args: - value: string The new ContactsOnly value to give this object. - """ - - self._SetProperty(VACATION_RESPONDER_CONTACTS_ONLY, value) - - contacts_only = pyproperty(GetContactsOnly, SetContactsOnly) - - def __init__(self, uri=None, enable=None, subject=None, - message=None, contacts_only=None, *args, **kwargs): - """Constructs a new EmailSettingsVacationResponder object with the - given arguments. - - Args: - uri: string (optional) The uri of of this object for HTTP requests. - enable: Boolean (optional) Whether to enable the vacation responder. - subject: string (optional) The subject line of the vacation responder - autoresponse. - message: string (optional) The message body of the vacation responder - autoresponse. - contacts_only: Boolean (optional) Whether to only send autoresponses - to known contacts. - args: The other parameters to pass to gdata.entry.GDEntry constructor. - kwargs: The other parameters to pass to gdata.entry.GDEntry constructor. - """ - super(EmailSettingsVacationResponder, self).__init__(*args, **kwargs) - if uri: - self.uri = uri - if enable is not None: - self.enable = str(enable) - if subject: - self.subject = subject - if message: - self.message = message - if contacts_only is not None: - self.contacts_only = str(contacts_only) - - -class EmailSettingsSignature(EmailSettingsEntry): - """Represents a Signature in object form.""" - - def GetValue(self): - """Get the value of the Signature object. - - Returns: - The value of this Signature object as a string or None. - """ - - value = self._GetProperty(SIGNATURE_VALUE) - if value == ' ': # hack to support empty signature - return '' - else: - return value - - def SetValue(self, value): - """Set the name of this Signature object. - - Args: - value: string The new signature value to give this object. - """ - - if value == '': # hack to support empty signature - value = ' ' - self._SetProperty(SIGNATURE_VALUE, value) - - signature_value = pyproperty(GetValue, SetValue) - - def __init__(self, uri=None, signature=None, *args, **kwargs): - """Constructs a new EmailSettingsSignature object with the given arguments. - - Args: - uri: string (optional) The uri of of this object for HTTP requests. - signature: string (optional) The signature to be appended to outgoing - messages. - args: The other parameters to pass to gdata.entry.GDEntry constructor. - kwargs: The other parameters to pass to gdata.entry.GDEntry constructor. - """ - super(EmailSettingsSignature, self).__init__(*args, **kwargs) - if uri: - self.uri = uri - if signature is not None: - self.signature_value = signature - - -class EmailSettingsLanguage(EmailSettingsEntry): - """Represents Language Settings in object form.""" - - def GetLanguage(self): - """Get the tag of the Language object. - - Returns: - The tag of this Language object as a string or None. - """ - - return self._GetProperty(LANGUAGE_TAG) - - def SetLanguage(self, value): - """Set the tag of this Language object. - - Args: - value: string The new tag value to give this object. - """ - - self._SetProperty(LANGUAGE_TAG, value) - - language_tag = pyproperty(GetLanguage, SetLanguage) - - def __init__(self, uri=None, language=None, *args, **kwargs): - """Constructs a new EmailSettingsLanguage object with the given arguments. - - Args: - uri: string (optional) The uri of of this object for HTTP requests. - language: string (optional) The language tag for Google Mail's display - language. - args: The other parameters to pass to gdata.entry.GDEntry constructor. - kwargs: The other parameters to pass to gdata.entry.GDEntry constructor. - """ - super(EmailSettingsLanguage, self).__init__(*args, **kwargs) - if uri: - self.uri = uri - if language: - self.language_tag = language - - -class EmailSettingsGeneral(EmailSettingsEntry): - """Represents General Settings in object form.""" - - def GetPageSize(self): - """Get the Page Size value of the General Settings object. - - Returns: - The Page Size value of this General Settings object as a string or None. - """ - - return self._GetProperty(GENERAL_PAGE_SIZE) - - def SetPageSize(self, value): - """Set the Page Size value of this General Settings object. - - Args: - value: string The new Page Size value to give this object. - """ - - self._SetProperty(GENERAL_PAGE_SIZE, value) - - page_size = pyproperty(GetPageSize, SetPageSize) - - def GetShortcuts(self): - """Get the Shortcuts value of the General Settings object. - - Returns: - The Shortcuts value of this General Settings object as a string or None. - """ - - return self._GetProperty(GENERAL_SHORTCUTS) - - def SetShortcuts(self, value): - """Set the Shortcuts value of this General Settings object. - - Args: - value: string The new Shortcuts value to give this object. - """ - - self._SetProperty(GENERAL_SHORTCUTS, value) - - shortcuts = pyproperty(GetShortcuts, SetShortcuts) - - def GetArrows(self): - """Get the Arrows value of the General Settings object. - - Returns: - The Arrows value of this General Settings object as a string or None. - """ - - return self._GetProperty(GENERAL_ARROWS) - - def SetArrows(self, value): - """Set the Arrows value of this General Settings object. - - Args: - value: string The new Arrows value to give this object. - """ - - self._SetProperty(GENERAL_ARROWS, value) - - arrows = pyproperty(GetArrows, SetArrows) - - def GetSnippets(self): - """Get the Snippets value of the General Settings object. - - Returns: - The Snippets value of this General Settings object as a string or None. - """ - - return self._GetProperty(GENERAL_SNIPPETS) - - def SetSnippets(self, value): - """Set the Snippets value of this General Settings object. - - Args: - value: string The new Snippets value to give this object. - """ - - self._SetProperty(GENERAL_SNIPPETS, value) - - snippets = pyproperty(GetSnippets, SetSnippets) - - def GetUnicode(self): - """Get the Unicode value of the General Settings object. - - Returns: - The Unicode value of this General Settings object as a string or None. - """ - - return self._GetProperty(GENERAL_UNICODE) - - def SetUnicode(self, value): - """Set the Unicode value of this General Settings object. - - Args: - value: string The new Unicode value to give this object. - """ - - self._SetProperty(GENERAL_UNICODE, value) - - use_unicode = pyproperty(GetUnicode, SetUnicode) - - def __init__(self, uri=None, page_size=None, shortcuts=None, - arrows=None, snippets=None, use_unicode=None, *args, **kwargs): - """Constructs a new EmailSettingsGeneral object with the given arguments. - - Args: - uri: string (optional) The uri of of this object for HTTP requests. - page_size: int (optional) The number of conversations to be shown per page. - shortcuts: Boolean (optional) Whether to enable keyboard shortcuts. - arrows: Boolean (optional) Whether to display arrow-shaped personal - indicators next to email sent specifically to the user. - snippets: Boolean (optional) Whether to display snippets of the messages - in the inbox and when searching. - use_unicode: Boolean (optional) Whether to use UTF-8 (unicode) encoding - for all outgoing messages. - args: The other parameters to pass to gdata.entry.GDEntry constructor. - kwargs: The other parameters to pass to gdata.entry.GDEntry constructor. - """ - super(EmailSettingsGeneral, self).__init__(*args, **kwargs) - if uri: - self.uri = uri - if page_size is not None: - self.page_size = str(page_size) - if shortcuts is not None: - self.shortcuts = str(shortcuts) - if arrows is not None: - self.arrows = str(arrows) - if snippets is not None: - self.snippets = str(snippets) - if use_unicode is not None: - self.use_unicode = str(use_unicode) - -class EmailSettingsDelegation(EmailSettingsEntry): - """Represents an Email Settings delegation entry in object form.""" - - def GetAddress(self): - """Get the email address of the delegated user. - - Returns: - The email address of the delegated user as a string or None. - """ - return self._GetProperty(DELEGATION_ADDRESS) - - def SetAddress(self, value): - """Set the email address of of the delegated user. - - Args: - value: string The email address of another user on the same domain - """ - self._SetProperty(DELEGATION_ADDRESS, value) - - address = pyproperty(GetAddress, SetAddress) - - def __init__(self, uri=None, address=None, *args, **kwargs): - """Constructs a new EmailSettingsDelegation object with the given - arguments. - - Args: - uri: string (optional) The uri of of this object for HTTP requests. - address: string The email address of the delegated user. - """ - super(EmailSettingsDelegation, self).__init__(*args, **kwargs) - if uri: - self.uri = uri - if address: - self.address = address - \ No newline at end of file diff --git a/gdata/analytics/apps/emailsettings/service.py b/gdata/analytics/apps/emailsettings/service.py deleted file mode 100644 index cab61eac58..0000000000 --- a/gdata/analytics/apps/emailsettings/service.py +++ /dev/null @@ -1,264 +0,0 @@ -#!/usr/bin/python -# -# Copyright (C) 2008 Google, 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. - -"""Allow Google Apps domain administrators to set users' email settings. - - EmailSettingsService: Set various email settings. -""" - -__author__ = 'google-apps-apis@googlegroups.com' - - -import gdata.apps -import gdata.apps.service -import gdata.service - - -API_VER='2.0' -# Forwarding and POP3 options -KEEP='KEEP' -ARCHIVE='ARCHIVE' -DELETE='DELETE' -ALL_MAIL='ALL_MAIL' -MAIL_FROM_NOW_ON='MAIL_FROM_NOW_ON' - - -class EmailSettingsService(gdata.apps.service.PropertyService): - """Client for the Google Apps Email Settings service.""" - - def _serviceUrl(self, setting_id, username, domain=None): - if domain is None: - domain = self.domain - return '/a/feeds/emailsettings/%s/%s/%s/%s' % (API_VER, domain, username, - setting_id) - - def CreateLabel(self, username, label): - """Create a label. - - Args: - username: User to create label for. - label: Label to create. - - Returns: - A dict containing the result of the create operation. - """ - uri = self._serviceUrl('label', username) - properties = {'label': label} - return self._PostProperties(uri, properties) - - def CreateFilter(self, username, from_=None, to=None, subject=None, - has_the_word=None, does_not_have_the_word=None, - has_attachment=None, label=None, should_mark_as_read=None, - should_archive=None): - """Create a filter. - - Args: - username: User to create filter for. - from_: Filter from string. - to: Filter to string. - subject: Filter subject. - has_the_word: Words to filter in. - does_not_have_the_word: Words to filter out. - has_attachment: Boolean for message having attachment. - label: Label to apply. - should_mark_as_read: Boolean for marking message as read. - should_archive: Boolean for archiving message. - - Returns: - A dict containing the result of the create operation. - """ - uri = self._serviceUrl('filter', username) - properties = {} - properties['from'] = from_ - properties['to'] = to - properties['subject'] = subject - properties['hasTheWord'] = has_the_word - properties['doesNotHaveTheWord'] = does_not_have_the_word - properties['hasAttachment'] = gdata.apps.service._bool2str(has_attachment) - properties['label'] = label - properties['shouldMarkAsRead'] = gdata.apps.service._bool2str(should_mark_as_read) - properties['shouldArchive'] = gdata.apps.service._bool2str(should_archive) - return self._PostProperties(uri, properties) - - def CreateSendAsAlias(self, username, name, address, reply_to=None, - make_default=None): - """Create alias to send mail as. - - Args: - username: User to create alias for. - name: Name of alias. - address: Email address to send from. - reply_to: Email address to reply to. - make_default: Boolean for whether this is the new default sending alias. - - Returns: - A dict containing the result of the create operation. - """ - uri = self._serviceUrl('sendas', username) - properties = {} - properties['name'] = name - properties['address'] = address - properties['replyTo'] = reply_to - properties['makeDefault'] = gdata.apps.service._bool2str(make_default) - return self._PostProperties(uri, properties) - - def UpdateWebClipSettings(self, username, enable): - """Update WebClip Settings - - Args: - username: User to update forwarding for. - enable: Boolean whether to enable Web Clip. - Returns: - A dict containing the result of the update operation. - """ - uri = self._serviceUrl('webclip', username) - properties = {} - properties['enable'] = gdata.apps.service._bool2str(enable) - return self._PutProperties(uri, properties) - - def UpdateForwarding(self, username, enable, forward_to=None, action=None): - """Update forwarding settings. - - Args: - username: User to update forwarding for. - enable: Boolean whether to enable this forwarding rule. - forward_to: Email address to forward to. - action: Action to take after forwarding. - - Returns: - A dict containing the result of the update operation. - """ - uri = self._serviceUrl('forwarding', username) - properties = {} - properties['enable'] = gdata.apps.service._bool2str(enable) - if enable is True: - properties['forwardTo'] = forward_to - properties['action'] = action - return self._PutProperties(uri, properties) - - def UpdatePop(self, username, enable, enable_for=None, action=None): - """Update POP3 settings. - - Args: - username: User to update POP3 settings for. - enable: Boolean whether to enable POP3. - enable_for: Which messages to make available via POP3. - action: Action to take after user retrieves email via POP3. - - Returns: - A dict containing the result of the update operation. - """ - uri = self._serviceUrl('pop', username) - properties = {} - properties['enable'] = gdata.apps.service._bool2str(enable) - if enable is True: - properties['enableFor'] = enable_for - properties['action'] = action - return self._PutProperties(uri, properties) - - def UpdateImap(self, username, enable): - """Update IMAP settings. - - Args: - username: User to update IMAP settings for. - enable: Boolean whether to enable IMAP. - - Returns: - A dict containing the result of the update operation. - """ - uri = self._serviceUrl('imap', username) - properties = {'enable': gdata.apps.service._bool2str(enable)} - return self._PutProperties(uri, properties) - - def UpdateVacation(self, username, enable, subject=None, message=None, - contacts_only=None): - """Update vacation settings. - - Args: - username: User to update vacation settings for. - enable: Boolean whether to enable vacation responses. - subject: Vacation message subject. - message: Vacation message body. - contacts_only: Boolean whether to send message only to contacts. - - Returns: - A dict containing the result of the update operation. - """ - uri = self._serviceUrl('vacation', username) - properties = {} - properties['enable'] = gdata.apps.service._bool2str(enable) - if enable is True: - properties['subject'] = subject - properties['message'] = message - properties['contactsOnly'] = gdata.apps.service._bool2str(contacts_only) - return self._PutProperties(uri, properties) - - def UpdateSignature(self, username, signature): - """Update signature. - - Args: - username: User to update signature for. - signature: Signature string. - - Returns: - A dict containing the result of the update operation. - """ - uri = self._serviceUrl('signature', username) - properties = {'signature': signature} - return self._PutProperties(uri, properties) - - def UpdateLanguage(self, username, language): - """Update user interface language. - - Args: - username: User to update language for. - language: Language code. - - Returns: - A dict containing the result of the update operation. - """ - uri = self._serviceUrl('language', username) - properties = {'language': language} - return self._PutProperties(uri, properties) - - def UpdateGeneral(self, username, page_size=None, shortcuts=None, arrows=None, - snippets=None, unicode=None): - """Update general settings. - - Args: - username: User to update general settings for. - page_size: Number of messages to show. - shortcuts: Boolean whether shortcuts are enabled. - arrows: Boolean whether arrows are enabled. - snippets: Boolean whether snippets are enabled. - unicode: Wheter unicode is enabled. - - Returns: - A dict containing the result of the update operation. - """ - uri = self._serviceUrl('general', username) - properties = {} - if page_size != None: - properties['pageSize'] = str(page_size) - if shortcuts != None: - properties['shortcuts'] = gdata.apps.service._bool2str(shortcuts) - if arrows != None: - properties['arrows'] = gdata.apps.service._bool2str(arrows) - if snippets != None: - properties['snippets'] = gdata.apps.service._bool2str(snippets) - if unicode != None: - properties['unicode'] = gdata.apps.service._bool2str(unicode) - return self._PutProperties(uri, properties) diff --git a/gdata/analytics/apps/groups/__init__.py b/gdata/analytics/apps/groups/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/gdata/analytics/apps/groups/service.py b/gdata/analytics/apps/groups/service.py deleted file mode 100644 index 80df41773b..0000000000 --- a/gdata/analytics/apps/groups/service.py +++ /dev/null @@ -1,387 +0,0 @@ -#!/usr/bin/python -# -# Copyright (C) 2008 Google, 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. - -"""Allow Google Apps domain administrators to manage groups, group members and group owners. - - GroupsService: Provides methods to manage groups, members and owners. -""" - -__author__ = 'google-apps-apis@googlegroups.com' - - -import urllib -import gdata.apps -import gdata.apps.service -import gdata.service - - -API_VER = '2.0' -BASE_URL = '/a/feeds/group/' + API_VER + '/%s' -GROUP_MEMBER_URL = BASE_URL + '?member=%s' -GROUP_MEMBER_DIRECT_URL = GROUP_MEMBER_URL + '&directOnly=%s' -GROUP_ID_URL = BASE_URL + '/%s' -MEMBER_URL = BASE_URL + '/%s/member' -MEMBER_WITH_SUSPENDED_URL = MEMBER_URL + '?includeSuspendedUsers=%s' -MEMBER_ID_URL = MEMBER_URL + '/%s' -OWNER_URL = BASE_URL + '/%s/owner' -OWNER_WITH_SUSPENDED_URL = OWNER_URL + '?includeSuspendedUsers=%s' -OWNER_ID_URL = OWNER_URL + '/%s' - -PERMISSION_OWNER = 'Owner' -PERMISSION_MEMBER = 'Member' -PERMISSION_DOMAIN = 'Domain' -PERMISSION_ANYONE = 'Anyone' - - -class GroupsService(gdata.apps.service.PropertyService): - """Client for the Google Apps Groups service.""" - - def _ServiceUrl(self, service_type, is_existed, group_id, member_id, owner_email, - direct_only=False, domain=None, suspended_users=False): - if domain is None: - domain = self.domain - - if service_type == 'group': - if group_id != '' and is_existed: - return GROUP_ID_URL % (domain, group_id) - elif member_id != '': - if direct_only: - return GROUP_MEMBER_DIRECT_URL % (domain, urllib.quote_plus(member_id), - self._Bool2Str(direct_only)) - else: - return GROUP_MEMBER_URL % (domain, urllib.quote_plus(member_id)) - else: - return BASE_URL % (domain) - - if service_type == 'member': - if member_id != '' and is_existed: - return MEMBER_ID_URL % (domain, group_id, urllib.quote_plus(member_id)) - elif suspended_users: - return MEMBER_WITH_SUSPENDED_URL % (domain, group_id, - self._Bool2Str(suspended_users)) - else: - return MEMBER_URL % (domain, group_id) - - if service_type == 'owner': - if owner_email != '' and is_existed: - return OWNER_ID_URL % (domain, group_id, urllib.quote_plus(owner_email)) - elif suspended_users: - return OWNER_WITH_SUSPENDED_URL % (domain, group_id, - self._Bool2Str(suspended_users)) - else: - return OWNER_URL % (domain, group_id) - - def _Bool2Str(self, b): - if b is None: - return None - return str(b is True).lower() - - def _IsExisted(self, uri): - try: - self._GetProperties(uri) - return True - except gdata.apps.service.AppsForYourDomainException, e: - if e.error_code == gdata.apps.service.ENTITY_DOES_NOT_EXIST: - return False - else: - raise e - - def CreateGroup(self, group_id, group_name, description, email_permission): - """Create a group. - - Args: - group_id: The ID of the group (e.g. us-sales). - group_name: The name of the group. - description: A description of the group - email_permission: The subscription permission of the group. - - Returns: - A dict containing the result of the create operation. - """ - uri = self._ServiceUrl('group', False, group_id, '', '') - properties = {} - properties['groupId'] = group_id - properties['groupName'] = group_name - properties['description'] = description - properties['emailPermission'] = email_permission - return self._PostProperties(uri, properties) - - def UpdateGroup(self, group_id, group_name, description, email_permission): - """Update a group's name, description and/or permission. - - Args: - group_id: The ID of the group (e.g. us-sales). - group_name: The name of the group. - description: A description of the group - email_permission: The subscription permission of the group. - - Returns: - A dict containing the result of the update operation. - """ - uri = self._ServiceUrl('group', True, group_id, '', '') - properties = {} - properties['groupId'] = group_id - properties['groupName'] = group_name - properties['description'] = description - properties['emailPermission'] = email_permission - return self._PutProperties(uri, properties) - - def RetrieveGroup(self, group_id): - """Retrieve a group based on its ID. - - Args: - group_id: The ID of the group (e.g. us-sales). - - Returns: - A dict containing the result of the retrieve operation. - """ - uri = self._ServiceUrl('group', True, group_id, '', '') - return self._GetProperties(uri) - - def RetrieveAllGroups(self): - """Retrieve all groups in the domain. - - Args: - None - - Returns: - A list containing the result of the retrieve operation. - """ - uri = self._ServiceUrl('group', True, '', '', '') - return self._GetPropertiesList(uri) - - def RetrievePageOfGroups(self, start_group=None): - """Retrieve one page of groups in the domain. - - Args: - start_group: The key to continue for pagination through all groups. - - Returns: - A feed object containing the result of the retrieve operation. - """ - uri = self._ServiceUrl('group', True, '', '', '') - if start_group is not None: - uri += "?start="+start_group - property_feed = self._GetPropertyFeed(uri) - return property_feed - - def RetrieveGroups(self, member_id, direct_only=False): - """Retrieve all groups that belong to the given member_id. - - Args: - member_id: The member's email address (e.g. member@example.com). - direct_only: Boolean whether only return groups that this member directly belongs to. - - Returns: - A list containing the result of the retrieve operation. - """ - uri = self._ServiceUrl('group', True, '', member_id, '', direct_only=direct_only) - return self._GetPropertiesList(uri) - - def DeleteGroup(self, group_id): - """Delete a group based on its ID. - - Args: - group_id: The ID of the group (e.g. us-sales). - - Returns: - A dict containing the result of the delete operation. - """ - uri = self._ServiceUrl('group', True, group_id, '', '') - return self._DeleteProperties(uri) - - def AddMemberToGroup(self, member_id, group_id): - """Add a member to a group. - - Args: - member_id: The member's email address (e.g. member@example.com). - group_id: The ID of the group (e.g. us-sales). - - Returns: - A dict containing the result of the add operation. - """ - uri = self._ServiceUrl('member', False, group_id, member_id, '') - properties = {} - properties['memberId'] = member_id - return self._PostProperties(uri, properties) - - def IsMember(self, member_id, group_id): - """Check whether the given member already exists in the given group. - - Args: - member_id: The member's email address (e.g. member@example.com). - group_id: The ID of the group (e.g. us-sales). - - Returns: - True if the member exists in the group. False otherwise. - """ - uri = self._ServiceUrl('member', True, group_id, member_id, '') - return self._IsExisted(uri) - - def RetrieveMember(self, member_id, group_id): - """Retrieve the given member in the given group. - - Args: - member_id: The member's email address (e.g. member@example.com). - group_id: The ID of the group (e.g. us-sales). - - Returns: - A dict containing the result of the retrieve operation. - """ - uri = self._ServiceUrl('member', True, group_id, member_id, '') - return self._GetProperties(uri) - - def RetrieveAllMembers(self, group_id, suspended_users=False): - """Retrieve all members in the given group. - - Args: - group_id: The ID of the group (e.g. us-sales). - suspended_users: A boolean; should we include any suspended users in - the membership list returned? - - Returns: - A list containing the result of the retrieve operation. - """ - uri = self._ServiceUrl('member', True, group_id, '', '', - suspended_users=suspended_users) - return self._GetPropertiesList(uri) - - def RetrievePageOfMembers(self, group_id, suspended_users=False, start=None): - """Retrieve one page of members of a given group. - - Args: - group_id: The ID of the group (e.g. us-sales). - suspended_users: A boolean; should we include any suspended users in - the membership list returned? - start: The key to continue for pagination through all members. - - Returns: - A feed object containing the result of the retrieve operation. - """ - - uri = self._ServiceUrl('member', True, group_id, '', '', - suspended_users=suspended_users) - if start is not None: - if suspended_users: - uri += "&start="+start - else: - uri += "?start="+start - property_feed = self._GetPropertyFeed(uri) - return property_feed - - def RemoveMemberFromGroup(self, member_id, group_id): - """Remove the given member from the given group. - - Args: - member_id: The member's email address (e.g. member@example.com). - group_id: The ID of the group (e.g. us-sales). - - Returns: - A dict containing the result of the remove operation. - """ - uri = self._ServiceUrl('member', True, group_id, member_id, '') - return self._DeleteProperties(uri) - - def AddOwnerToGroup(self, owner_email, group_id): - """Add an owner to a group. - - Args: - owner_email: The email address of a group owner. - group_id: The ID of the group (e.g. us-sales). - - Returns: - A dict containing the result of the add operation. - """ - uri = self._ServiceUrl('owner', False, group_id, '', owner_email) - properties = {} - properties['email'] = owner_email - return self._PostProperties(uri, properties) - - def IsOwner(self, owner_email, group_id): - """Check whether the given member an owner of the given group. - - Args: - owner_email: The email address of a group owner. - group_id: The ID of the group (e.g. us-sales). - - Returns: - True if the member is an owner of the given group. False otherwise. - """ - uri = self._ServiceUrl('owner', True, group_id, '', owner_email) - return self._IsExisted(uri) - - def RetrieveOwner(self, owner_email, group_id): - """Retrieve the given owner in the given group. - - Args: - owner_email: The email address of a group owner. - group_id: The ID of the group (e.g. us-sales). - - Returns: - A dict containing the result of the retrieve operation. - """ - uri = self._ServiceUrl('owner', True, group_id, '', owner_email) - return self._GetProperties(uri) - - def RetrieveAllOwners(self, group_id, suspended_users=False): - """Retrieve all owners of the given group. - - Args: - group_id: The ID of the group (e.g. us-sales). - suspended_users: A boolean; should we include any suspended users in - the ownership list returned? - - Returns: - A list containing the result of the retrieve operation. - """ - uri = self._ServiceUrl('owner', True, group_id, '', '', - suspended_users=suspended_users) - return self._GetPropertiesList(uri) - - def RetrievePageOfOwners(self, group_id, suspended_users=False, start=None): - """Retrieve one page of owners of the given group. - - Args: - group_id: The ID of the group (e.g. us-sales). - suspended_users: A boolean; should we include any suspended users in - the ownership list returned? - start: The key to continue for pagination through all owners. - - Returns: - A feed object containing the result of the retrieve operation. - """ - uri = self._ServiceUrl('owner', True, group_id, '', '', - suspended_users=suspended_users) - if start is not None: - if suspended_users: - uri += "&start="+start - else: - uri += "?start="+start - property_feed = self._GetPropertyFeed(uri) - return property_feed - - def RemoveOwnerFromGroup(self, owner_email, group_id): - """Remove the given owner from the given group. - - Args: - owner_email: The email address of a group owner. - group_id: The ID of the group (e.g. us-sales). - - Returns: - A dict containing the result of the remove operation. - """ - uri = self._ServiceUrl('owner', True, group_id, '', owner_email) - return self._DeleteProperties(uri) diff --git a/gdata/analytics/apps/migration/__init__.py b/gdata/analytics/apps/migration/__init__.py deleted file mode 100644 index 0a7df6c1f1..0000000000 --- a/gdata/analytics/apps/migration/__init__.py +++ /dev/null @@ -1,223 +0,0 @@ -#!/usr/bin/python2.4 -# -# Copyright 2008 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. - -"""Contains objects used with Google Apps.""" - -__author__ = 'google-apps-apis@googlegroups.com' - - -import atom -import gdata - - -# XML namespaces which are often used in Google Apps entity. -APPS_NAMESPACE = 'http://schemas.google.com/apps/2006' -APPS_TEMPLATE = '{http://schemas.google.com/apps/2006}%s' - - -class Rfc822Msg(atom.AtomBase): - """The Migration rfc822Msg element.""" - - _tag = 'rfc822Msg' - _namespace = APPS_NAMESPACE - _children = atom.AtomBase._children.copy() - _attributes = atom.AtomBase._attributes.copy() - _attributes['encoding'] = 'encoding' - - def __init__(self, extension_elements=None, - extension_attributes=None, text=None): - self.text = text - self.encoding = 'base64' - self.extension_elements = extension_elements or [] - self.extension_attributes = extension_attributes or {} - - -def Rfc822MsgFromString(xml_string): - """Parse in the Rrc822 message from the XML definition.""" - - return atom.CreateClassFromXMLString(Rfc822Msg, xml_string) - - -class MailItemProperty(atom.AtomBase): - """The Migration mailItemProperty element.""" - - _tag = 'mailItemProperty' - _namespace = APPS_NAMESPACE - _children = atom.AtomBase._children.copy() - _attributes = atom.AtomBase._attributes.copy() - _attributes['value'] = 'value' - - def __init__(self, value=None, extension_elements=None, - extension_attributes=None, text=None): - self.value = value - self.text = text - self.extension_elements = extension_elements or [] - self.extension_attributes = extension_attributes or {} - - -def MailItemPropertyFromString(xml_string): - """Parse in the MailItemProperiy from the XML definition.""" - - return atom.CreateClassFromXMLString(MailItemProperty, xml_string) - - -class Label(atom.AtomBase): - """The Migration label element.""" - - _tag = 'label' - _namespace = APPS_NAMESPACE - _children = atom.AtomBase._children.copy() - _attributes = atom.AtomBase._attributes.copy() - _attributes['labelName'] = 'label_name' - - def __init__(self, label_name=None, - extension_elements=None, extension_attributes=None, - text=None): - self.label_name = label_name - self.text = text - self.extension_elements = extension_elements or [] - self.extension_attributes = extension_attributes or {} - - -def LabelFromString(xml_string): - """Parse in the mailItemProperty from the XML definition.""" - - return atom.CreateClassFromXMLString(Label, xml_string) - - -class MailEntry(gdata.GDataEntry): - """A Google Migration flavor of an Atom Entry.""" - - _tag = 'entry' - _namespace = atom.ATOM_NAMESPACE - _children = gdata.GDataEntry._children.copy() - _attributes = gdata.GDataEntry._attributes.copy() - _children['{%s}rfc822Msg' % APPS_NAMESPACE] = ('rfc822_msg', Rfc822Msg) - _children['{%s}mailItemProperty' % APPS_NAMESPACE] = ('mail_item_property', - [MailItemProperty]) - _children['{%s}label' % APPS_NAMESPACE] = ('label', [Label]) - - def __init__(self, author=None, category=None, content=None, - atom_id=None, link=None, published=None, - title=None, updated=None, - rfc822_msg=None, mail_item_property=None, label=None, - extended_property=None, - extension_elements=None, extension_attributes=None, text=None): - - gdata.GDataEntry.__init__(self, author=author, category=category, - content=content, - atom_id=atom_id, link=link, published=published, - title=title, updated=updated) - self.rfc822_msg = rfc822_msg - self.mail_item_property = mail_item_property - self.label = label - self.extended_property = extended_property or [] - self.text = text - self.extension_elements = extension_elements or [] - self.extension_attributes = extension_attributes or {} - - -def MailEntryFromString(xml_string): - """Parse in the MailEntry from the XML definition.""" - - return atom.CreateClassFromXMLString(MailEntry, xml_string) - - -class BatchMailEntry(gdata.BatchEntry): - """A Google Migration flavor of an Atom Entry.""" - - _tag = gdata.BatchEntry._tag - _namespace = gdata.BatchEntry._namespace - _children = gdata.BatchEntry._children.copy() - _attributes = gdata.BatchEntry._attributes.copy() - _children['{%s}rfc822Msg' % APPS_NAMESPACE] = ('rfc822_msg', Rfc822Msg) - _children['{%s}mailItemProperty' % APPS_NAMESPACE] = ('mail_item_property', - [MailItemProperty]) - _children['{%s}label' % APPS_NAMESPACE] = ('label', [Label]) - - def __init__(self, author=None, category=None, content=None, - atom_id=None, link=None, published=None, - title=None, updated=None, - rfc822_msg=None, mail_item_property=None, label=None, - batch_operation=None, batch_id=None, batch_status=None, - extended_property=None, - extension_elements=None, extension_attributes=None, text=None): - - gdata.BatchEntry.__init__(self, author=author, category=category, - content=content, - atom_id=atom_id, link=link, published=published, - batch_operation=batch_operation, - batch_id=batch_id, batch_status=batch_status, - title=title, updated=updated) - self.rfc822_msg = rfc822_msg or None - self.mail_item_property = mail_item_property or [] - self.label = label or [] - self.extended_property = extended_property or [] - self.text = text - self.extension_elements = extension_elements or [] - self.extension_attributes = extension_attributes or {} - - -def BatchMailEntryFromString(xml_string): - """Parse in the BatchMailEntry from the XML definition.""" - - return atom.CreateClassFromXMLString(BatchMailEntry, xml_string) - - -class BatchMailEventFeed(gdata.BatchFeed): - """A Migration event feed flavor of an Atom Feed.""" - - _tag = gdata.BatchFeed._tag - _namespace = gdata.BatchFeed._namespace - _children = gdata.BatchFeed._children.copy() - _attributes = gdata.BatchFeed._attributes.copy() - _children['{%s}entry' % atom.ATOM_NAMESPACE] = ('entry', [BatchMailEntry]) - - def __init__(self, author=None, category=None, contributor=None, - generator=None, icon=None, atom_id=None, link=None, logo=None, - rights=None, subtitle=None, title=None, updated=None, - entry=None, total_results=None, start_index=None, - items_per_page=None, interrupted=None, extension_elements=None, - extension_attributes=None, text=None): - gdata.BatchFeed.__init__(self, author=author, category=category, - contributor=contributor, generator=generator, - icon=icon, atom_id=atom_id, link=link, - logo=logo, rights=rights, subtitle=subtitle, - title=title, updated=updated, entry=entry, - total_results=total_results, - start_index=start_index, - items_per_page=items_per_page, - interrupted=interrupted, - extension_elements=extension_elements, - extension_attributes=extension_attributes, - text=text) - - -class MailEntryProperties(object): - """Represents a mail message and its attributes.""" - - def __init__(self, mail_message=None, mail_item_properties=None, - mail_labels=None, identifier=None): - self.mail_message = mail_message - self.mail_item_properties = mail_item_properties or [] - self.mail_labels = mail_labels or [] - self.identifier = identifier - - -def BatchMailEventFeedFromString(xml_string): - """Parse in the BatchMailEventFeed from the XML definition.""" - - return atom.CreateClassFromXMLString(BatchMailEventFeed, xml_string) diff --git a/gdata/analytics/apps/migration/service.py b/gdata/analytics/apps/migration/service.py deleted file mode 100644 index 5a89effba6..0000000000 --- a/gdata/analytics/apps/migration/service.py +++ /dev/null @@ -1,218 +0,0 @@ -#!/usr/bin/python2.4 -# -# Copyright 2008 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. - -"""Contains the methods to import mail via Google Apps Email Migration API. - - MigrationService: Provides methods to import mail. -""" - -__author__ = ('google-apps-apis@googlegroups.com', - 'pti@google.com (Prashant Tiwari)') - - -import base64 -import threading -import time -from atom.service import deprecation -from gdata.apps import migration -from gdata.apps.migration import MailEntryProperties -import gdata.apps.service -import gdata.service - - -API_VER = '2.0' - - -class MigrationService(gdata.apps.service.AppsService): - """Client for the EMAPI migration service. Use either ImportMail to import - one message at a time, or AddMailEntry and ImportMultipleMails to import a - bunch of messages at a time. - """ - - def __init__(self, email=None, password=None, domain=None, source=None, - server='apps-apis.google.com', additional_headers=None): - gdata.apps.service.AppsService.__init__( - self, email=email, password=password, domain=domain, source=source, - server=server, additional_headers=additional_headers) - self.mail_batch = migration.BatchMailEventFeed() - self.mail_entries = [] - self.exceptions = 0 - - def _BaseURL(self): - return '/a/feeds/migration/%s/%s' % (API_VER, self.domain) - - def ImportMail(self, user_name, mail_message, mail_item_properties, - mail_labels): - """Imports a single mail message. - - Args: - user_name: The username to import messages to. - mail_message: An RFC822 format email message. - mail_item_properties: A list of Gmail properties to apply to the message. - mail_labels: A list of labels to apply to the message. - - Returns: - A MailEntry representing the successfully imported message. - - Raises: - AppsForYourDomainException: An error occurred importing the message. - """ - uri = '%s/%s/mail' % (self._BaseURL(), user_name) - - mail_entry = migration.MailEntry() - mail_entry.rfc822_msg = migration.Rfc822Msg(text=(base64.b64encode( - mail_message))) - mail_entry.rfc822_msg.encoding = 'base64' - mail_entry.mail_item_property = map( - lambda x: migration.MailItemProperty(value=x), mail_item_properties) - mail_entry.label = map(lambda x: migration.Label(label_name=x), - mail_labels) - - try: - return migration.MailEntryFromString(str(self.Post(mail_entry, uri))) - except gdata.service.RequestError, e: - # Store the number of failed imports when importing several at a time - self.exceptions += 1 - raise gdata.apps.service.AppsForYourDomainException(e.args[0]) - - def AddBatchEntry(self, mail_message, mail_item_properties, - mail_labels): - """Adds a message to the current batch that you later will submit. - - Deprecated, use AddMailEntry instead - - Args: - mail_message: An RFC822 format email message. - mail_item_properties: A list of Gmail properties to apply to the message. - mail_labels: A list of labels to apply to the message. - - Returns: - The length of the MailEntry representing the message. - """ - deprecation("calling deprecated method AddBatchEntry") - mail_entry = migration.BatchMailEntry() - mail_entry.rfc822_msg = migration.Rfc822Msg(text=(base64.b64encode( - mail_message))) - mail_entry.rfc822_msg.encoding = 'base64' - mail_entry.mail_item_property = map( - lambda x: migration.MailItemProperty(value=x), mail_item_properties) - mail_entry.label = map(lambda x: migration.Label(label_name=x), - mail_labels) - - self.mail_batch.AddBatchEntry(mail_entry) - - return len(str(mail_entry)) - - def SubmitBatch(self, user_name): - """Sends all the mail items you have added to the batch to the server. - - Deprecated, use ImportMultipleMails instead - - Args: - user_name: The username to import messages to. - - Returns: - An HTTPResponse from the web service call. - - Raises: - AppsForYourDomainException: An error occurred importing the batch. - """ - deprecation("calling deprecated method SubmitBatch") - uri = '%s/%s/mail/batch' % (self._BaseURL(), user_name) - - try: - self.result = self.Post(self.mail_batch, uri, - converter=migration.BatchMailEventFeedFromString) - except gdata.service.RequestError, e: - raise gdata.apps.service.AppsForYourDomainException(e.args[0]) - - self.mail_batch = migration.BatchMailEventFeed() - - return self.result - - def AddMailEntry(self, mail_message, mail_item_properties=None, - mail_labels=None, identifier=None): - """Prepares a list of mail messages to import using ImportMultipleMails. - - Args: - mail_message: An RFC822 format email message as a string. - mail_item_properties: List of Gmail properties to apply to the - message. - mail_labels: List of Gmail labels to apply to the message. - identifier: The optional file identifier string - - Returns: - The number of email messages to be imported. - """ - mail_entry_properties = MailEntryProperties( - mail_message=mail_message, - mail_item_properties=mail_item_properties, - mail_labels=mail_labels, - identifier=identifier) - - self.mail_entries.append(mail_entry_properties) - return len(self.mail_entries) - - def ImportMultipleMails(self, user_name, threads_per_batch=20): - """Launches separate threads to import every message added by AddMailEntry. - - Args: - user_name: The user account name to import messages to. - threads_per_batch: Number of messages to import at a time. - - Returns: - The number of email messages that were successfully migrated. - - Raises: - Exception: An error occurred while importing mails. - """ - num_entries = len(self.mail_entries) - - if not num_entries: - return 0 - - threads = [] - for mail_entry_properties in self.mail_entries: - t = threading.Thread(name=mail_entry_properties.identifier, - target=self.ImportMail, - args=(user_name, mail_entry_properties.mail_message, - mail_entry_properties.mail_item_properties, - mail_entry_properties.mail_labels)) - threads.append(t) - try: - # Determine the number of batches needed with threads_per_batch in each - batches = num_entries / threads_per_batch + ( - 0 if num_entries % threads_per_batch == 0 else 1) - batch_min = 0 - # Start the threads, one batch at a time - for batch in range(batches): - batch_max = ((batch + 1) * threads_per_batch - if (batch + 1) * threads_per_batch < num_entries - else num_entries) - for i in range(batch_min, batch_max): - threads[i].start() - time.sleep(1) - - for i in range(batch_min, batch_max): - threads[i].join() - - batch_min = batch_max - - self.mail_entries = [] - except Exception, e: - raise Exception(e.args[0]) - else: - return num_entries - self.exceptions diff --git a/gdata/analytics/apps/multidomain/__init__.py b/gdata/analytics/apps/multidomain/__init__.py deleted file mode 100644 index d284c7cee3..0000000000 --- a/gdata/analytics/apps/multidomain/__init__.py +++ /dev/null @@ -1,16 +0,0 @@ -#!/usr/bin/python -# -# Copyright (C) 2008 Google -# -# 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. - diff --git a/gdata/analytics/apps/multidomain/client.py b/gdata/analytics/apps/multidomain/client.py deleted file mode 100644 index 316aa30f16..0000000000 --- a/gdata/analytics/apps/multidomain/client.py +++ /dev/null @@ -1,336 +0,0 @@ -#!/usr/bin/python2.4 -# -# Copyright 2011 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. - -"""MultiDomainProvisioningClient simplifies Multidomain Provisioning API calls. - -MultiDomainProvisioningClient extends gdata.client.GDClient to ease interaction -with the Google Multidomain Provisioning API. These interactions include the -ability to create, retrieve, update and delete users and aliases in multiple -domains. -""" - - -__author__ = 'Claudio Cherubino ' - - -import urllib -import gdata.apps.multidomain.data -import gdata.client - - -# Multidomain URI templates -# The strings in this template are eventually replaced with the feed type -# (user/alias), API version and Google Apps domain name, respectively. -MULTIDOMAIN_URI_TEMPLATE = '/a/feeds/%s/%s/%s' -# The strings in this template are eventually replaced with the API version, -# Google Apps domain name and old email address, respectively. -MULTIDOMAIN_USER_RENAME_URI_TEMPLATE = '/a/feeds/user/userEmail/%s/%s/%s' - -# The value for user requests -MULTIDOMAIN_USER_FEED = 'user' -# The value for alias requests -MULTIDOMAIN_ALIAS_FEED = 'alias' - - -class MultiDomainProvisioningClient(gdata.client.GDClient): - """Client extension for the Google MultiDomain Provisioning API service. - - Attributes: - host: string The hostname for the MultiDomain Provisioning API service. - api_version: string The version of the MultiDomain Provisioning API. - """ - - host = 'apps-apis.google.com' - api_version = '2.0' - auth_service = 'apps' - auth_scopes = gdata.gauth.AUTH_SCOPES['apps'] - ssl = True - - def __init__(self, domain, auth_token=None, **kwargs): - """Constructs a new client for the MultiDomain Provisioning API. - - Args: - domain: string The Google Apps domain with MultiDomain Provisioning. - auth_token: (optional) gdata.gauth.ClientLoginToken, AuthSubToken, or - OAuthToken which authorizes this client to edit the email settings. - kwargs: The other parameters to pass to the gdata.client.GDClient - constructor. - """ - gdata.client.GDClient.__init__(self, auth_token=auth_token, **kwargs) - self.domain = domain - - def make_multidomain_provisioning_uri( - self, feed_type, email=None, params=None): - - """Creates a resource feed URI for the MultiDomain Provisioning API. - - Using this client's Google Apps domain, create a feed URI for multidomain - provisioning in that domain. If an email address is provided, return a - URI for that specific resource. If params are provided, append them as GET - params. - - Args: - feed_type: string The type of feed (user/alias) - email: string (optional) The email address of multidomain resource for - which to make a feed URI. - params: dict (optional) key -> value params to append as GET vars to the - URI. Example: params={'start': 'my-resource-id'} - - Returns: - A string giving the URI for multidomain provisioning for this client's - Google Apps domain. - """ - uri = MULTIDOMAIN_URI_TEMPLATE % (feed_type, self.api_version, self.domain) - if email: - uri += '/' + email - if params: - uri += '?' + urllib.urlencode(params) - return uri - - MakeMultidomainProvisioningUri = make_multidomain_provisioning_uri - - def make_multidomain_user_provisioning_uri(self, email=None, params=None): - """Creates a resource feed URI for the MultiDomain User Provisioning API. - - Using this client's Google Apps domain, create a feed URI for multidomain - user provisioning in that domain. If an email address is provided, return a - URI for that specific resource. If params are provided, append them as GET - params. - - Args: - email: string (optional) The email address of multidomain user for which - to make a feed URI. - params: dict (optional) key -> value params to append as GET vars to the - URI. Example: params={'start': 'my-resource-id'} - - Returns: - A string giving the URI for multidomain user provisioning for thisis that - client's Google Apps domain. - """ - return self.make_multidomain_provisioning_uri( - MULTIDOMAIN_USER_FEED, email, params) - - MakeMultidomainUserProvisioningUri = make_multidomain_user_provisioning_uri - - def make_multidomain_alias_provisioning_uri(self, email=None, params=None): - """Creates a resource feed URI for the MultiDomain Alias Provisioning API. - - Using this client's Google Apps domain, create a feed URI for multidomain - alias provisioning in that domain. If an email address is provided, return a - URI for that specific resource. If params are provided, append them as GET - params. - - Args: - email: string (optional) The email address of multidomain alias for which - to make a feed URI. - params: dict (optional) key -> value params to append as GET vars to the - URI. Example: params={'start': 'my-resource-id'} - - Returns: - A string giving the URI for multidomain alias provisioning for this - client's Google Apps domain. - """ - return self.make_multidomain_provisioning_uri( - MULTIDOMAIN_ALIAS_FEED, email, params) - - MakeMultidomainAliasProvisioningUri = make_multidomain_alias_provisioning_uri - - def retrieve_all_users(self, **kwargs): - """Retrieves all users in all domains. - - Args: - kwargs: The other parameters to pass to gdata.client.GDClient.GetFeed() - - Returns: - A gdata.data.GDFeed of the domain users - """ - uri = self.MakeMultidomainUserProvisioningUri() - return self.GetFeed( - uri, - desired_class=gdata.apps.multidomain.data.UserFeed, - **kwargs) - - RetrieveAllUsers = retrieve_all_users - - def retrieve_user(self, email, **kwargs): - """Retrieves a single user in the domain. - - Args: - email: string The email address of the user to be retrieved - kwargs: The other parameters to pass to gdata.client.GDClient.GetEntry() - - Returns: - A gdata.apps.multidomain.data.UserEntry representing the user - """ - uri = self.MakeMultidomainUserProvisioningUri(email=email) - return self.GetEntry( - uri, - desired_class=gdata.apps.multidomain.data.UserEntry, - **kwargs) - - RetrieveUser = retrieve_user - - def create_user(self, email, first_name, last_name, password, is_admin, - hash_function=None, suspended=None, change_password=None, - ip_whitelisted=None, quota=None, **kwargs): - """Creates an user in the domain with the given properties. - - Args: - email: string The email address of the user. - first_name: string The first name of the user. - last_name: string The last name of the user. - password: string The password of the user. - is_admin: Boolean Whether or not the user has administrator privileges. - hash_function: string (optional) The name of the function used to hash the - password. - suspended: Boolean (optional) Whether or not the user is suspended. - change_password: Boolean (optional) Whether or not the user must change - password at first login. - ip_whitelisted: Boolean (optional) Whether or not the user's ip is - whitelisted. - quota: string (optional) The value (in GB) of the user's quota. - kwargs: The other parameters to pass to gdata.client.GDClient.post(). - - Returns: - A gdata.apps.multidomain.data.UserEntry of the new user - """ - new_user = gdata.apps.multidomain.data.UserEntry( - email=email, first_name=first_name, last_name=last_name, - password=password, is_admin=is_admin, hash_function=hash_function, - suspended=suspended, change_password=change_password, - ip_whitelisted=ip_whitelisted, quota=quota) - return self.post(new_user, self.MakeMultidomainUserProvisioningUri(), - **kwargs) - - CreateUser = create_user - - def update_user(self, email, user_entry, **kwargs): - """Deletes the user with the given email address. - - Args: - email: string The email address of the user to be updated. - user_entry: UserEntry The user entry with updated values. - kwargs: The other parameters to pass to gdata.client.GDClient.put() - - Returns: - A gdata.apps.multidomain.data.UserEntry representing the user - """ - return self.update(user_entry, - uri=self.MakeMultidomainUserProvisioningUri(email), - **kwargs) - - UpdateUser = update_user - - def delete_user(self, email, **kwargs): - """Deletes the user with the given email address. - - Args: - email: string The email address of the user to delete. - kwargs: The other parameters to pass to gdata.client.GDClient.delete() - - Returns: - An HTTP response object. See gdata.client.request(). - """ - return self.delete(self.MakeMultidomainUserProvisioningUri(email), **kwargs) - - DeleteUser = delete_user - - def rename_user(self, old_email, new_email, **kwargs): - """Renames an user's account to a different domain. - - Args: - old_email: string The old email address of the user to rename. - new_email: string The new email address for the user to be renamed. - kwargs: The other parameters to pass to gdata.client.GDClient.put() - - Returns: - A gdata.apps.multidomain.data.UserRenameRequest representing the request. - """ - rename_uri = MULTIDOMAIN_USER_RENAME_URI_TEMPLATE % (self.api_version, - self.domain, - old_email) - entry = gdata.apps.multidomain.data.UserRenameRequest(new_email) - return self.update(entry, uri=rename_uri, **kwargs) - - RenameUser = rename_user - - def retrieve_all_aliases(self, **kwargs): - """Retrieves all aliases in the domain. - - Args: - kwargs: The other parameters to pass to gdata.client.GDClient.GetFeed() - - Returns: - A gdata.data.GDFeed of the domain aliases - """ - uri = self.MakeMultidomainAliasProvisioningUri() - return self.GetFeed( - uri, - desired_class=gdata.apps.multidomain.data.AliasFeed, - **kwargs) - - RetrieveAllAliases = retrieve_all_aliases - - def retrieve_alias(self, email, **kwargs): - """Retrieves a single alias in the domain. - - Args: - email: string The email address of the alias to be retrieved - kwargs: The other parameters to pass to gdata.client.GDClient.GetEntry() - - Returns: - A gdata.apps.multidomain.data.AliasEntry representing the alias - """ - uri = self.MakeMultidomainAliasProvisioningUri(email=email) - return self.GetEntry( - uri, - desired_class=gdata.apps.multidomain.data.AliasEntry, - **kwargs) - - RetrieveAlias = retrieve_alias - - def create_alias(self, user_email, alias_email, **kwargs): - """Creates an alias in the domain with the given properties. - - Args: - user_email: string The email address of the user. - alias_email: string The first name of the user. - kwargs: The other parameters to pass to gdata.client.GDClient.post(). - - Returns: - A gdata.apps.multidomain.data.AliasEntry of the new alias - """ - new_alias = gdata.apps.multidomain.data.AliasEntry( - user_email=user_email, alias_email=alias_email) - return self.post(new_alias, self.MakeMultidomainAliasProvisioningUri(), - **kwargs) - - CreateAlias = create_alias - - def delete_alias(self, email, **kwargs): - """Deletes the alias with the given email address. - - Args: - email: string The email address of the alias to delete. - kwargs: The other parameters to pass to gdata.client.GDClient.delete() - - Returns: - An HTTP response object. See gdata.client.request(). - """ - return self.delete(self.MakeMultidomainAliasProvisioningUri(email), - **kwargs) - - DeleteAlias = delete_alias diff --git a/gdata/analytics/apps/multidomain/data.py b/gdata/analytics/apps/multidomain/data.py deleted file mode 100644 index c75c5fa2b6..0000000000 --- a/gdata/analytics/apps/multidomain/data.py +++ /dev/null @@ -1,453 +0,0 @@ -#!/usr/bin/python2.4 -# -# Copyright 2011 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 the Multidomain Provisioning API.""" - - -__author__ = 'Claudio Cherubino ' - - -import gdata.apps -import gdata.apps_property -import gdata.data - - -# This is required to work around a naming conflict between the Google -# Spreadsheets API and Python's built-in property function -pyproperty = property - - -# The apps:property firstName of a user entry -USER_FIRST_NAME = 'firstName' -# The apps:property lastName of a user entry -USER_LAST_NAME = 'lastName' -# The apps:property userEmail of a user entry -USER_EMAIL = 'userEmail' -# The apps:property password of a user entry -USER_PASSWORD = 'password' -# The apps:property hashFunction of a user entry -USER_HASH_FUNCTION = 'hashFunction' -# The apps:property isChangePasswordAtNextLogin of a user entry -USER_CHANGE_PASSWORD = 'isChangePasswordAtNextLogin' -# The apps:property agreedToTerms of a user entry -USER_AGREED_TO_TERMS = 'agreedToTerms' -# The apps:property isSuspended of a user entry -USER_SUSPENDED = 'isSuspended' -# The apps:property isAdmin of a user entry -USER_ADMIN = 'isAdmin' -# The apps:property ipWhitelisted of a user entry -USER_IP_WHITELISTED = 'ipWhitelisted' -# The apps:property quotaInGb of a user entry -USER_QUOTA = 'quotaInGb' - -# The apps:property newEmail of a user rename request entry -USER_NEW_EMAIL = 'newEmail' - -# The apps:property aliasEmail of an alias entry -ALIAS_EMAIL = 'aliasEmail' - - -class MultidomainProvisioningEntry(gdata.data.GDEntry): - """Represents a Multidomain Provisioning entry in object form.""" - - property = [gdata.apps_property.AppsProperty] - - def _GetProperty(self, name): - """Get the apps:property value with the given name. - - Args: - name: string Name of the apps:property value to get. - - Returns: - The apps:property value with the given name, or None if the name was - invalid. - """ - value = None - for p in self.property: - if p.name == name: - value = p.value - break - return value - - def _SetProperty(self, name, value): - """Set the apps:property value with the given name to the given value. - - Args: - name: string Name of the apps:property value to set. - value: string Value to give the apps:property value with the given name. - """ - found = False - for i in range(len(self.property)): - if self.property[i].name == name: - self.property[i].value = value - found = True - break - if not found: - self.property.append( - gdata.apps_property.AppsProperty(name=name, value=value)) - - -class UserEntry(MultidomainProvisioningEntry): - """Represents an User in object form.""" - - def GetFirstName(self): - """Get the first name of the User object. - - Returns: - The first name of this User object as a string or None. - """ - return self._GetProperty(USER_FIRST_NAME) - - def SetFirstName(self, value): - """Set the first name of this User object. - - Args: - value: string The new first name to give this object. - """ - self._SetProperty(USER_FIRST_NAME, value) - - first_name = pyproperty(GetFirstName, SetFirstName) - - def GetLastName(self): - """Get the last name of the User object. - - Returns: - The last name of this User object as a string or None. - """ - return self._GetProperty(USER_LAST_NAME) - - def SetLastName(self, value): - """Set the last name of this User object. - - Args: - value: string The new last name to give this object. - """ - self._SetProperty(USER_LAST_NAME, value) - - last_name = pyproperty(GetLastName, SetLastName) - - def GetEmail(self): - """Get the email address of the User object. - - Returns: - The email address of this User object as a string or None. - """ - return self._GetProperty(USER_EMAIL) - - def SetEmail(self, value): - """Set the email address of this User object. - - Args: - value: string The new email address to give this object. - """ - self._SetProperty(USER_EMAIL, value) - - email = pyproperty(GetEmail, SetEmail) - - def GetPassword(self): - """Get the password of the User object. - - Returns: - The password of this User object as a string or None. - """ - return self._GetProperty(USER_PASSWORD) - - def SetPassword(self, value): - """Set the password of this User object. - - Args: - value: string The new password to give this object. - """ - self._SetProperty(USER_PASSWORD, value) - - password = pyproperty(GetPassword, SetPassword) - - def GetHashFunction(self): - """Get the hash function of the User object. - - Returns: - The hash function of this User object as a string or None. - """ - return self._GetProperty(USER_HASH_FUNCTION) - - def SetHashFunction(self, value): - """Set the hash function of this User object. - - Args: - value: string The new hash function to give this object. - """ - self._SetProperty(USER_HASH_FUNCTION, value) - - hash_function = pyproperty(GetHashFunction, SetHashFunction) - - def GetChangePasswordAtNextLogin(self): - """Get the change password at next login flag of the User object. - - Returns: - The change password at next login flag of this User object as a string or - None. - """ - return self._GetProperty(USER_CHANGE_PASSWORD) - - def SetChangePasswordAtNextLogin(self, value): - """Set the change password at next login flag of this User object. - - Args: - value: string The new change password at next login flag to give this - object. - """ - self._SetProperty(USER_CHANGE_PASSWORD, value) - - change_password_at_next_login = pyproperty(GetChangePasswordAtNextLogin, - SetChangePasswordAtNextLogin) - - def GetAgreedToTerms(self): - """Get the agreed to terms flag of the User object. - - Returns: - The agreed to terms flag of this User object as a string or None. - """ - return self._GetProperty(USER_AGREED_TO_TERMS) - - agreed_to_terms = pyproperty(GetAgreedToTerms) - - def GetSuspended(self): - """Get the suspended flag of the User object. - - Returns: - The suspended flag of this User object as a string or None. - """ - return self._GetProperty(USER_SUSPENDED) - - def SetSuspended(self, value): - """Set the suspended flag of this User object. - - Args: - value: string The new suspended flag to give this object. - """ - self._SetProperty(USER_SUSPENDED, value) - - suspended = pyproperty(GetSuspended, SetSuspended) - - def GetIsAdmin(self): - """Get the isAdmin flag of the User object. - - Returns: - The isAdmin flag of this User object as a string or None. - """ - return self._GetProperty(USER_ADMIN) - - def SetIsAdmin(self, value): - """Set the isAdmin flag of this User object. - - Args: - value: string The new isAdmin flag to give this object. - """ - self._SetProperty(USER_ADMIN, value) - - is_admin = pyproperty(GetIsAdmin, SetIsAdmin) - - def GetIpWhitelisted(self): - """Get the ipWhitelisted flag of the User object. - - Returns: - The ipWhitelisted flag of this User object as a string or None. - """ - return self._GetProperty(USER_IP_WHITELISTED) - - def SetIpWhitelisted(self, value): - """Set the ipWhitelisted flag of this User object. - - Args: - value: string The new ipWhitelisted flag to give this object. - """ - self._SetProperty(USER_IP_WHITELISTED, value) - - ip_whitelisted = pyproperty(GetIpWhitelisted, SetIpWhitelisted) - - def GetQuota(self): - """Get the quota of the User object. - - Returns: - The quota of this User object as a string or None. - """ - return self._GetProperty(USER_QUOTA) - - def SetQuota(self, value): - """Set the quota of this User object. - - Args: - value: string The new quota to give this object. - """ - self._SetProperty(USER_QUOTA, value) - - quota = pyproperty(GetQuota, GetQuota) - - def __init__(self, uri=None, email=None, first_name=None, last_name=None, - password=None, hash_function=None, change_password=None, - agreed_to_terms=None, suspended=None, is_admin=None, - ip_whitelisted=None, quota=None, *args, **kwargs): - """Constructs a new UserEntry object with the given arguments. - - Args: - uri: string (optional) The uri of of this object for HTTP requests. - email: string (optional) The email address of the user. - first_name: string (optional) The first name of the user. - last_name: string (optional) The last name of the user. - password: string (optional) The password of the user. - hash_function: string (optional) The name of the function used to hash the - password. - change_password: Boolean (optional) Whether or not the user must change - password at first login. - agreed_to_terms: Boolean (optional) Whether or not the user has agreed to - the Terms of Service. - suspended: Boolean (optional) Whether or not the user is suspended. - is_admin: Boolean (optional) Whether or not the user has administrator - privileges. - ip_whitelisted: Boolean (optional) Whether or not the user's ip is - whitelisted. - quota: string (optional) The value (in GB) of the user's quota. - args: The other parameters to pass to gdata.entry.GDEntry constructor. - kwargs: The other parameters to pass to gdata.entry.GDEntry constructor. - """ - super(UserEntry, self).__init__(*args, **kwargs) - if uri: - self.uri = uri - if email: - self.email = email - if first_name: - self.first_name = first_name - if last_name: - self.last_name = last_name - if password: - self.password = password - if hash_function: - self.hash_function = hash_function - if change_password is not None: - self.change_password = str(change_password) - if agreed_to_terms is not None: - self.agreed_to_terms = str(agreed_to_terms) - if suspended is not None: - self.suspended = str(suspended) - if is_admin is not None: - self.is_admin = str(is_admin) - if ip_whitelisted is not None: - self.ip_whitelisted = str(ip_whitelisted) - if quota: - self.quota = quota - - -class UserFeed(gdata.data.GDFeed): - """Represents a feed of UserEntry objects.""" - - # Override entry so that this feed knows how to type its list of entries. - entry = [UserEntry] - - -class UserRenameRequest(MultidomainProvisioningEntry): - """Represents an User rename request in object form.""" - - def GetNewEmail(self): - """Get the new email address for the User object. - - Returns: - The new email address for the User object as a string or None. - """ - return self._GetProperty(USER_NEW_EMAIL) - - def SetNewEmail(self, value): - """Set the new email address for the User object. - - Args: - value: string The new email address to give this object. - """ - self._SetProperty(USER_NEW_EMAIL, value) - - new_email = pyproperty(GetNewEmail, SetNewEmail) - - def __init__(self, new_email=None, *args, **kwargs): - """Constructs a new UserRenameRequest object with the given arguments. - - Args: - new_email: string (optional) The new email address for the target user. - args: The other parameters to pass to gdata.entry.GDEntry constructor. - kwargs: The other parameters to pass to gdata.entry.GDEntry constructor. - """ - super(UserRenameRequest, self).__init__(*args, **kwargs) - if new_email: - self.new_email = new_email - - -class AliasEntry(MultidomainProvisioningEntry): - """Represents an Alias in object form.""" - - def GetUserEmail(self): - """Get the user email address of the Alias object. - - Returns: - The user email address of this Alias object as a string or None. - """ - return self._GetProperty(USER_EMAIL) - - def SetUserEmail(self, value): - """Set the user email address of this Alias object. - - Args: - value: string The new user email address to give this object. - """ - self._SetProperty(USER_EMAIL, value) - - user_email = pyproperty(GetUserEmail, SetUserEmail) - - def GetAliasEmail(self): - """Get the alias email address of the Alias object. - - Returns: - The alias email address of this Alias object as a string or None. - """ - return self._GetProperty(ALIAS_EMAIL) - - def SetAliasEmail(self, value): - """Set the alias email address of this Alias object. - - Args: - value: string The new alias email address to give this object. - """ - self._SetProperty(ALIAS_EMAIL, value) - - alias_email = pyproperty(GetAliasEmail, SetAliasEmail) - - def __init__(self, user_email=None, alias_email=None, *args, **kwargs): - """Constructs a new AliasEntry object with the given arguments. - - Args: - user_email: string (optional) The user email address for the object. - alias_email: string (optional) The alias email address for the object. - args: The other parameters to pass to gdata.entry.GDEntry constructor. - kwargs: The other parameters to pass to gdata.entry.GDEntry constructor. - """ - super(AliasEntry, self).__init__(*args, **kwargs) - if user_email: - self.user_email = user_email - if alias_email: - self.alias_email = alias_email - - -class AliasFeed(gdata.data.GDFeed): - """Represents a feed of AliasEntry objects.""" - - # Override entry so that this feed knows how to type its list of entries. - entry = [AliasEntry] diff --git a/gdata/analytics/apps/multidomain/service.py b/gdata/analytics/apps/multidomain/service.py deleted file mode 100644 index f404160147..0000000000 --- a/gdata/analytics/apps/multidomain/service.py +++ /dev/null @@ -1,105 +0,0 @@ -#!/usr/bin/python -# -# Copyright (C) 2008 Google, 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. - -"""Extended Multi Domain Support. - - MultiDomainService: Multi Domain Support.""" - -__author__ = 'jlee@pbu.edu' - - -import gdata.apps -import gdata.apps.service -import gdata.service - - -API_VER='2.0' - -class MultiDomainService(gdata.apps.service.PropertyService): - """Extended functions for Google Apps Multi-Domain Support.""" - - def _serviceUrl(self, setting_id, domain=None): - if domain is None: - domain = self.domain - return '/a/feeds/%s/%s/%s' % (setting_id, API_VER, domain) - - def CreateUser(self, user_email, password, first_name, last_name, user_domain=None, is_admin=False): - - uri = self._serviceUrl('user', user_domain) - properties = {} - properties['userEmail'] = user_email - properties['password'] = password - properties['firstName'] = first_name - properties['lastName'] = last_name - properties['isAdmin'] = is_admin - return self._PostProperties(uri, properties) - - def UpdateUser(self, user_email, user_domain=None, password=None, first_name=None, last_name=None, is_admin=None): - - uri = self._serviceUrl('user', user_domain) - properties = RetrieveUser(user_domain, user_email) - if password != None: - properties['password'] = password - if first_name != None: - properties['firstName'] = first_name - if last_name != None: - properties['lastName'] = last_name - if is_admin != None: - properties['isAdmin'] = gdata.apps.service._bool2str(is_admin) - return self._PutProperties(uri, properties) - - def RenameUser(self, old_email, new_email): - - old_domain = old_email[old_email.find('@')+1:] - uri = self._serviceUrl('user/userEmail', old_domain+'/'+old_email) - properties = {} - properties['newEmail'] = new_email - return self._PutProperties(uri, properties) - - def CreateAlias(self, user_email, alias_email): - - if alias_email.find('@') > 0: - domain = alias_email[alias_email.find('@')+1:] - else: - domain = self.domain - uri = self._serviceUrl('alias', domain) - properties = {} - properties['userEmail'] = user_email - properties['aliasEmail'] = alias_email - return self._PostProperties(uri, properties) - - def RetrieveAlias(self, alias_email): - - alias_domain = alias_email[alias_email.find('@')+1:] - uri = self._serviceUrl('alias', alias_domain+'/'+alias_email) - return self._GetProperties(uri) - - def RetrieveAllAliases(self): - - uri = self._serviceUrl('alias', self.domain) - return self._GetPropertiesList(uri) - - def DeleteAlias(self, alias_email): - - alias_domain = alias_email[alias_email.find('@')+1:] - uri = self._serviceUrl('alias', alias_domain+'/'+alias_email) - return self._DeleteProperties(uri) - - def GetUserAliases(self, user_email): - - user_domain = user_email[user_email.find('@')+1:] - uri = self._serviceUrl('alias', user_domain+'?userEmail='+user_email) - return self._GetPropertiesList(uri) \ No newline at end of file diff --git a/gdata/analytics/apps/organization/__init__.py b/gdata/analytics/apps/organization/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/gdata/analytics/apps/organization/service.py b/gdata/analytics/apps/organization/service.py deleted file mode 100644 index 763a6bcf1f..0000000000 --- a/gdata/analytics/apps/organization/service.py +++ /dev/null @@ -1,297 +0,0 @@ -#!/usr/bin/python -# -# Copyright (C) 2008 Google, 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. - -"""Allow Google Apps domain administrators to manage organization unit and organization user. - - OrganizationService: Provides methods to manage organization unit and organization user. -""" - -__author__ = 'Alexandre Vivien (alex@simplecode.fr)' - - -import gdata.apps -import gdata.apps.service -import gdata.service - - -API_VER = '2.0' -CUSTOMER_BASE_URL = '/a/feeds/customer/2.0/customerId' -BASE_UNIT_URL = '/a/feeds/orgunit/' + API_VER + '/%s' -UNIT_URL = BASE_UNIT_URL + '/%s' -UNIT_ALL_URL = BASE_UNIT_URL + '?get=all' -UNIT_CHILD_URL = BASE_UNIT_URL + '?get=children&orgUnitPath=%s' -BASE_USER_URL = '/a/feeds/orguser/' + API_VER + '/%s' -USER_URL = BASE_USER_URL + '/%s' -USER_ALL_URL = BASE_USER_URL + '?get=all' -USER_CHILD_URL = BASE_USER_URL + '?get=children&orgUnitPath=%s' - - -class OrganizationService(gdata.apps.service.PropertyService): - """Client for the Google Apps Organizations service.""" - - def _Bool2Str(self, b): - if b is None: - return None - return str(b is True).lower() - - def RetrieveCustomerId(self): - """Retrieve the Customer ID for the account of the authenticated administrator making this request. - - Args: - None. - - Returns: - A dict containing the result of the retrieve operation. - """ - - uri = CUSTOMER_BASE_URL - return self._GetProperties(uri) - - def CreateOrgUnit(self, customer_id, name, parent_org_unit_path='/', description='', block_inheritance=False): - """Create a Organization Unit. - - Args: - customer_id: The ID of the Google Apps customer. - name: The simple organization unit text name, not the full path name. - parent_org_unit_path: The full path of the parental tree to this organization unit (default: '/'). - Note: Each element of the path MUST be URL encoded (example: finance%2Forganization/suborganization) - description: The human readable text description of the organization unit (optional). - block_inheritance: This parameter blocks policy setting inheritance - from organization units higher in the organization tree (default: False). - - Returns: - A dict containing the result of the create operation. - """ - - uri = BASE_UNIT_URL % (customer_id) - properties = {} - properties['name'] = name - properties['parentOrgUnitPath'] = parent_org_unit_path - properties['description'] = description - properties['blockInheritance'] = self._Bool2Str(block_inheritance) - return self._PostProperties(uri, properties) - - def UpdateOrgUnit(self, customer_id, org_unit_path, name=None, parent_org_unit_path=None, - description=None, block_inheritance=None): - """Update a Organization Unit. - - Args: - customer_id: The ID of the Google Apps customer. - org_unit_path: The organization's full path name. - Note: Each element of the path MUST be URL encoded (example: finance%2Forganization/suborganization) - name: The simple organization unit text name, not the full path name. - parent_org_unit_path: The full path of the parental tree to this organization unit. - Note: Each element of the path MUST be URL encoded (example: finance%2Forganization/suborganization) - description: The human readable text description of the organization unit. - block_inheritance: This parameter blocks policy setting inheritance - from organization units higher in the organization tree. - - Returns: - A dict containing the result of the update operation. - """ - - uri = UNIT_URL % (customer_id, org_unit_path) - properties = {} - if name: - properties['name'] = name - if parent_org_unit_path: - properties['parentOrgUnitPath'] = parent_org_unit_path - if description: - properties['description'] = description - if block_inheritance: - properties['blockInheritance'] = self._Bool2Str(block_inheritance) - return self._PutProperties(uri, properties) - - def MoveUserToOrgUnit(self, customer_id, org_unit_path, users_to_move): - """Move a user to an Organization Unit. - - Args: - customer_id: The ID of the Google Apps customer. - org_unit_path: The organization's full path name. - Note: Each element of the path MUST be URL encoded (example: finance%2Forganization/suborganization) - users_to_move: Email addresses list of users to move. Note: You can move a maximum of 25 users at one time. - - Returns: - A dict containing the result of the update operation. - """ - - uri = UNIT_URL % (customer_id, org_unit_path) - properties = {} - if users_to_move and isinstance(users_to_move, list): - properties['usersToMove'] = ', '.join(users_to_move) - return self._PutProperties(uri, properties) - - def RetrieveOrgUnit(self, customer_id, org_unit_path): - """Retrieve a Orgunit based on its path. - - Args: - customer_id: The ID of the Google Apps customer. - org_unit_path: The organization's full path name. - Note: Each element of the path MUST be URL encoded (example: finance%2Forganization/suborganization) - - Returns: - A dict containing the result of the retrieve operation. - """ - uri = UNIT_URL % (customer_id, org_unit_path) - return self._GetProperties(uri) - - def DeleteOrgUnit(self, customer_id, org_unit_path): - """Delete a Orgunit based on its path. - - Args: - customer_id: The ID of the Google Apps customer. - org_unit_path: The organization's full path name. - Note: Each element of the path MUST be URL encoded (example: finance%2Forganization/suborganization) - - Returns: - A dict containing the result of the delete operation. - """ - uri = UNIT_URL % (customer_id, org_unit_path) - return self._DeleteProperties(uri) - - def RetrieveAllOrgUnits(self, customer_id): - """Retrieve all OrgUnits in the customer's domain. - - Args: - customer_id: The ID of the Google Apps customer. - - Returns: - A list containing the result of the retrieve operation. - """ - uri = UNIT_ALL_URL % (customer_id) - return self._GetPropertiesList(uri) - - def RetrievePageOfOrgUnits(self, customer_id, startKey=None): - """Retrieve one page of OrgUnits in the customer's domain. - - Args: - customer_id: The ID of the Google Apps customer. - startKey: The key to continue for pagination through all OrgUnits. - - Returns: - A feed object containing the result of the retrieve operation. - """ - uri = UNIT_ALL_URL % (customer_id) - if startKey is not None: - uri += "&startKey=" + startKey - property_feed = self._GetPropertyFeed(uri) - return property_feed - - def RetrieveSubOrgUnits(self, customer_id, org_unit_path): - """Retrieve all Sub-OrgUnits of the provided OrgUnit. - - Args: - customer_id: The ID of the Google Apps customer. - org_unit_path: The organization's full path name. - Note: Each element of the path MUST be URL encoded (example: finance%2Forganization/suborganization) - - Returns: - A list containing the result of the retrieve operation. - """ - uri = UNIT_CHILD_URL % (customer_id, org_unit_path) - return self._GetPropertiesList(uri) - - def RetrieveOrgUser(self, customer_id, user_email): - """Retrieve the OrgUnit of the user. - - Args: - customer_id: The ID of the Google Apps customer. - user_email: The email address of the user. - - Returns: - A dict containing the result of the retrieve operation. - """ - uri = USER_URL % (customer_id, user_email) - return self._GetProperties(uri) - - def UpdateOrgUser(self, customer_id, user_email, org_unit_path): - """Update the OrgUnit of a OrgUser. - - Args: - customer_id: The ID of the Google Apps customer. - user_email: The email address of the user. - org_unit_path: The new organization's full path name. - Note: Each element of the path MUST be URL encoded (example: finance%2Forganization/suborganization) - - Returns: - A dict containing the result of the update operation. - """ - - uri = USER_URL % (customer_id, user_email) - properties = {} - if org_unit_path: - properties['orgUnitPath'] = org_unit_path - return self._PutProperties(uri, properties) - - def RetrieveAllOrgUsers(self, customer_id): - """Retrieve all OrgUsers in the customer's domain. - - Args: - customer_id: The ID of the Google Apps customer. - - Returns: - A list containing the result of the retrieve operation. - """ - uri = USER_ALL_URL % (customer_id) - return self._GetPropertiesList(uri) - - def RetrievePageOfOrgUsers(self, customer_id, startKey=None): - """Retrieve one page of OrgUsers in the customer's domain. - - Args: - customer_id: The ID of the Google Apps customer. - startKey: The key to continue for pagination through all OrgUnits. - - Returns: - A feed object containing the result of the retrieve operation. - """ - uri = USER_ALL_URL % (customer_id) - if startKey is not None: - uri += "&startKey=" + startKey - property_feed = self._GetPropertyFeed(uri) - return property_feed - - def RetrieveOrgUnitUsers(self, customer_id, org_unit_path): - """Retrieve all OrgUsers of the provided OrgUnit. - - Args: - customer_id: The ID of the Google Apps customer. - org_unit_path: The organization's full path name. - Note: Each element of the path MUST be URL encoded (example: finance%2Forganization/suborganization) - - Returns: - A list containing the result of the retrieve operation. - """ - uri = USER_CHILD_URL % (customer_id, org_unit_path) - return self._GetPropertiesList(uri) - - def RetrieveOrgUnitPageOfUsers(self, customer_id, org_unit_path, startKey=None): - """Retrieve one page of OrgUsers of the provided OrgUnit. - - Args: - customer_id: The ID of the Google Apps customer. - org_unit_path: The organization's full path name. - Note: Each element of the path MUST be URL encoded (example: finance%2Forganization/suborganization) - startKey: The key to continue for pagination through all OrgUsers. - - Returns: - A feed object containing the result of the retrieve operation. - """ - uri = USER_CHILD_URL % (customer_id, org_unit_path) - if startKey is not None: - uri += "&startKey=" + startKey - property_feed = self._GetPropertyFeed(uri) - return property_feed diff --git a/gdata/analytics/apps/orgs/__init__.py b/gdata/analytics/apps/orgs/__init__.py deleted file mode 100644 index d284c7cee3..0000000000 --- a/gdata/analytics/apps/orgs/__init__.py +++ /dev/null @@ -1,16 +0,0 @@ -#!/usr/bin/python -# -# Copyright (C) 2008 Google -# -# 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. - diff --git a/gdata/analytics/apps/orgs/service.py b/gdata/analytics/apps/orgs/service.py deleted file mode 100644 index 073909ca48..0000000000 --- a/gdata/analytics/apps/orgs/service.py +++ /dev/null @@ -1,159 +0,0 @@ -#!/usr/bin/python -# -# Copyright (C) 2008 Google, 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. - -"""Organization Support. - - OrganizationService: Organization Support.""" - -__author__ = 'jlee@pbu.edu' - - -import urllib -import gdata.apps -import gdata.apps.service -import gdata.service - - -API_VER='2.0' - -class OrganizationService(gdata.apps.service.PropertyService): - """Extended functions for Google Apps Organization Support.""" - - def _serviceUrl(self, setting_id, domain=None): - if domain is None: - domain = self.domain - return '/a/feeds/%s/%s/%s' % (setting_id, API_VER, domain) - - def RetrieveCustomerId(self): - - uri = '/a/feeds/customer/2.0/customerId' - return self._GetProperties(uri) - - def CreateOrganizationUnit(self, name, description, parent_org_unit_path='/', block_inheritance=False): - - customer_id = self.RetrieveCustomerId()['customerId'] - uri = '/a/feeds/orgunit/2.0/%s' % customer_id - properties = {} - properties['name'] = name - properties['description'] = description - properties['parentOrgUnitPath'] = urllib.quote_plus(parent_org_unit_path, safe='/') - properties['blockInheritance'] = gdata.apps.service._bool2str(block_inheritance) - return self._PostProperties(uri, properties) - - def UpdateOrganizationUnit(self, old_name, new_name=None, description=None, parent_org_unit_path=None, block_inheritance=None, users_to_move=None): - - customer_id = self.RetrieveCustomerId()['customerId'] - old_name = urllib.quote_plus(old_name, safe='/') - uri = '/a/feeds/orgunit/2.0/%s/%s' % (customer_id, old_name) - properties = {} - if new_name != None: - properties['name'] = new_name - if description != None: - properties['description'] = description - if parent_org_unit_path != None: - properties['parentOrgUnitPath'] = urllib.quote_plus(parent_org_unit_path, safe='/') - if block_inheritance != None: - properties['blockInheritance'] = gdata.apps.service._bool2str(block_inheritance) - if users_to_move != None: - properties['usersToMove'] = '' - for user in users_to_move: - if user.find('@') < 0: - user = user+'@'+self.domain - properties['usersToMove'] += user+', ' - return self._PutProperties(uri, properties) - - def RetrieveOrganizationUnit(self, name): - - customer_id = self.RetrieveCustomerId()['customerId'] - name = urllib.quote_plus(name, safe='/') - uri = '/a/feeds/orgunit/2.0/%s/%s' % (customer_id, name) - org = self._GetProperties(uri) - try: - org['orgUnitPath'] = urllib.unquote_plus(org['orgUnitPath']) - org['parentOrgUnitPath'] = urllib.unquote_plus(org['parentOrgUnitPath']) - except AttributeError: - pass - return org - - def RetrieveAllOrganizationUnits(self): - - customer_id = self.RetrieveCustomerId()['customerId'] - uri = '/a/feeds/orgunit/2.0/%s?get=all' % customer_id - all_orgs = self._GetPropertiesList(uri) - for org in all_orgs: - try: - org['orgUnitPath'] = urllib.unquote_plus(org['orgUnitPath']) - org['parentOrgUnitPath'] = urllib.unquote_plus(org['parentOrgUnitPath']) - except AttributeError: - pass - return all_orgs - - def RetrieveSubOrganizationUnits(self, name): - - customer_id = self.RetrieveCustomerId()['customerId'] - uri = '/a/feeds/orgunit/2.0/%s?get=children&orgUnitPath=%s' % (customer_id, urllib.quote_plus(name, safe='/')) - sub_orgs = self._GetPropertiesList(uri) - for org in sub_orgs: - try: - org['orgUnitPath'] = urllib.unquote_plus(org['orgUnitPath']) - org['parentOrgUnitPath'] = urllib.unquote_plus(org['parentOrgUnitPath']) - except AttributeError: - pass - return sub_orgs - - def DeleteOrganizationUnit(self, name): - - customer_id = self.RetrieveCustomerId()['customerId'] - name = urllib.quote_plus(name, safe='/') - uri = '/a/feeds/orgunit/2.0/%s/%s' % (customer_id, name) - return self._DeleteProperties(uri) - - def RetrieveUserOrganization(self, user): - - customer_id = self.RetrieveCustomerId()['customerId'] - if user.find('@') < 0: - user = user+'@'+self.domain - uri = '/a/feeds/orguser/2.0/%s/%s' % (customer_id, urllib.quote_plus(user)) - org = self._GetProperties(uri) - try: - org['orgUnitPath'] = urllib.unquote_plus(org['orgUnitPath']) - except AttributeError: - pass - return org - - def RetrieveAllOrganizationUsers(self): - - customer_id = self.RetrieveCustomerId()['customerId'] - uri = '/a/feeds/orguser/2.0/%s?get=all' % customer_id - all_users = self._GetPropertiesList(uri) - for user in all_users: - try: - user['orgUnitPath'] = urllib.unquote_plus(user['orgUnitPath']) - except AttributeError: - pass - return all_users - - def RetrieveAllOrganizationUnitUsers(self, name): - - customer_id = self.RetrieveCustomerId()['customerId'] - uri = '/a/feeds/orguser/2.0/%s?get=children&orgUnitPath=%s' % (customer_id, urllib.quote_plus(name)) - all_users = self._GetPropertiesList(uri) - for user in all_users: - try: - user['orgUnitPath'] = urllib.unquote_plus(user['orgUnitPath']) - except AttributeError: - pass - return all_users \ No newline at end of file diff --git a/gdata/analytics/apps/res_cal/__init__.py b/gdata/analytics/apps/res_cal/__init__.py deleted file mode 100644 index 8b13789179..0000000000 --- a/gdata/analytics/apps/res_cal/__init__.py +++ /dev/null @@ -1 +0,0 @@ - diff --git a/gdata/analytics/apps/res_cal/service.py b/gdata/analytics/apps/res_cal/service.py deleted file mode 100644 index bb173bbce5..0000000000 --- a/gdata/analytics/apps/res_cal/service.py +++ /dev/null @@ -1,71 +0,0 @@ -# Copyright (C) 2008 Google, 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. - -"""Allow Google Apps domain administrators to create/modify/delete resource calendars. - - ResCalService: Interact with Resource Calendars.""" - -__author__ = 'jlee@pbu.edu' - -import gdata.apps -import gdata.apps.service -import gdata.service - -class ResCalService(gdata.apps.service.PropertyService): - """Client for the Google Apps Resource Calendar service.""" - - def _serviceUrl(self, domain=None): - if domain is None: - domain = self.domain - return '/a/feeds/calendar/resource/2.0/%s' % domain - - def CreateResourceCalendar(self, id, common_name, description=None, type=None): - - uri = self._serviceUrl() - properties = {} - properties['resourceId'] = id - properties['resourceCommonName'] = common_name - if description != None: - properties['resourceDescription'] = description - if type != None: - properties['resourceType'] = type - return self._PostProperties(uri, properties) - - def RetrieveResourceCalendar(self, id): - - uri = self._serviceUrl()+'/'+id - return self._GetProperties(uri) - - def RetrieveAllResourceCalendars(self): - - uri = self._serviceUrl()+'/' - return self._GetPropertiesList(uri) - - def UpdateResourceCalendar(self, id, common_name=None, description=None, type=None): - - uri = self._serviceUrl()+'/'+id - properties = {} - properties['resourceId'] = id - if common_name != None: - properties['resourceCommonName'] = common_name - if description != None: - properties['resourceDescription'] = description - if type != None: - properties['resourceType'] = type - return self._PutProperties(uri, properties) - - def DeleteResourceCalendar(self, id): - - uri = self._serviceUrl()+'/'+id - return self._DeleteProperties(uri) \ No newline at end of file diff --git a/gdata/analytics/apps/service.py b/gdata/analytics/apps/service.py deleted file mode 100644 index bc97484989..0000000000 --- a/gdata/analytics/apps/service.py +++ /dev/null @@ -1,552 +0,0 @@ -#!/usr/bin/python -# -# Copyright (C) 2007 SIOS Technology, 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. - -__author__ = 'tmatsuo@sios.com (Takashi MATSUO)' - -try: - from xml.etree import cElementTree as ElementTree -except ImportError: - try: - import cElementTree as ElementTree - except ImportError: - try: - from xml.etree import ElementTree - except ImportError: - from elementtree import ElementTree -import urllib -import gdata -import atom.service -import gdata.service -import gdata.apps -import atom - -API_VER="2.0" -HTTP_OK=200 - -UNKOWN_ERROR=1000 -USER_DELETED_RECENTLY=1100 -USER_SUSPENDED=1101 -DOMAIN_USER_LIMIT_EXCEEDED=1200 -DOMAIN_ALIAS_LIMIT_EXCEEDED=1201 -DOMAIN_SUSPENDED=1202 -DOMAIN_FEATURE_UNAVAILABLE=1203 -ENTITY_EXISTS=1300 -ENTITY_DOES_NOT_EXIST=1301 -ENTITY_NAME_IS_RESERVED=1302 -ENTITY_NAME_NOT_VALID=1303 -INVALID_GIVEN_NAME=1400 -INVALID_FAMILY_NAME=1401 -INVALID_PASSWORD=1402 -INVALID_USERNAME=1403 -INVALID_HASH_FUNCTION_NAME=1404 -INVALID_HASH_DIGGEST_LENGTH=1405 -INVALID_EMAIL_ADDRESS=1406 -INVALID_QUERY_PARAMETER_VALUE=1407 -TOO_MANY_RECIPIENTS_ON_EMAIL_LIST=1500 - -DEFAULT_QUOTA_LIMIT='2048' - - -class Error(Exception): - pass - - -class AppsForYourDomainException(Error): - - def __init__(self, response): - - Error.__init__(self, response) - try: - self.element_tree = ElementTree.fromstring(response['body']) - self.error_code = int(self.element_tree[0].attrib['errorCode']) - self.reason = self.element_tree[0].attrib['reason'] - self.invalidInput = self.element_tree[0].attrib['invalidInput'] - except: - self.error_code = UNKOWN_ERROR - - -class AppsService(gdata.service.GDataService): - """Client for the Google Apps Provisioning service.""" - - def __init__(self, email=None, password=None, domain=None, source=None, - server='apps-apis.google.com', additional_headers=None, - **kwargs): - """Creates a client for the Google Apps Provisioning service. - - Args: - email: string (optional) The user's email address, used for - authentication. - password: string (optional) The user's password. - domain: string (optional) The Google Apps domain name. - 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: 'apps-apis.google.com'. - **kwargs: The other parameters to pass to gdata.service.GDataService - constructor. - """ - gdata.service.GDataService.__init__( - self, email=email, password=password, service='apps', source=source, - server=server, additional_headers=additional_headers, **kwargs) - self.ssl = True - self.port = 443 - self.domain = domain - - def _baseURL(self): - return "/a/feeds/%s" % self.domain - - def AddAllElementsFromAllPages(self, link_finder, func): - """retrieve all pages and add all elements""" - next = link_finder.GetNextLink() - while next is not None: - next_feed = self.Get(next.href, converter=func) - for a_entry in next_feed.entry: - link_finder.entry.append(a_entry) - next = next_feed.GetNextLink() - return link_finder - - def RetrievePageOfEmailLists(self, start_email_list_name=None, - num_retries=gdata.service.DEFAULT_NUM_RETRIES, - delay=gdata.service.DEFAULT_DELAY, - backoff=gdata.service.DEFAULT_BACKOFF): - """Retrieve one page of email list""" - uri = "%s/emailList/%s" % (self._baseURL(), API_VER) - if start_email_list_name is not None: - uri += "?startEmailListName=%s" % start_email_list_name - try: - return gdata.apps.EmailListFeedFromString(str(self.GetWithRetries( - uri, num_retries=num_retries, delay=delay, backoff=backoff))) - except gdata.service.RequestError, e: - raise AppsForYourDomainException(e.args[0]) - - def GetGeneratorForAllEmailLists( - self, num_retries=gdata.service.DEFAULT_NUM_RETRIES, - delay=gdata.service.DEFAULT_DELAY, backoff=gdata.service.DEFAULT_BACKOFF): - """Retrieve a generator for all emaillists in this domain.""" - first_page = self.RetrievePageOfEmailLists(num_retries=num_retries, - delay=delay, - backoff=backoff) - return self.GetGeneratorFromLinkFinder( - first_page, gdata.apps.EmailListRecipientFeedFromString, - num_retries=num_retries, delay=delay, backoff=backoff) - - def RetrieveAllEmailLists(self): - """Retrieve all email list of a domain.""" - - ret = self.RetrievePageOfEmailLists() - # pagination - return self.AddAllElementsFromAllPages( - ret, gdata.apps.EmailListFeedFromString) - - def RetrieveEmailList(self, list_name): - """Retreive a single email list by the list's name.""" - - uri = "%s/emailList/%s/%s" % ( - self._baseURL(), API_VER, list_name) - try: - return self.Get(uri, converter=gdata.apps.EmailListEntryFromString) - except gdata.service.RequestError, e: - raise AppsForYourDomainException(e.args[0]) - - def RetrieveEmailLists(self, recipient): - """Retrieve All Email List Subscriptions for an Email Address.""" - - uri = "%s/emailList/%s?recipient=%s" % ( - self._baseURL(), API_VER, recipient) - try: - ret = gdata.apps.EmailListFeedFromString(str(self.Get(uri))) - except gdata.service.RequestError, e: - raise AppsForYourDomainException(e.args[0]) - - # pagination - return self.AddAllElementsFromAllPages( - ret, gdata.apps.EmailListFeedFromString) - - def RemoveRecipientFromEmailList(self, recipient, list_name): - """Remove recipient from email list.""" - - uri = "%s/emailList/%s/%s/recipient/%s" % ( - self._baseURL(), API_VER, list_name, recipient) - try: - self.Delete(uri) - except gdata.service.RequestError, e: - raise AppsForYourDomainException(e.args[0]) - - def RetrievePageOfRecipients(self, list_name, start_recipient=None, - num_retries=gdata.service.DEFAULT_NUM_RETRIES, - delay=gdata.service.DEFAULT_DELAY, - backoff=gdata.service.DEFAULT_BACKOFF): - """Retrieve one page of recipient of an email list. """ - - uri = "%s/emailList/%s/%s/recipient" % ( - self._baseURL(), API_VER, list_name) - - if start_recipient is not None: - uri += "?startRecipient=%s" % start_recipient - try: - return gdata.apps.EmailListRecipientFeedFromString(str( - self.GetWithRetries( - uri, num_retries=num_retries, delay=delay, backoff=backoff))) - except gdata.service.RequestError, e: - raise AppsForYourDomainException(e.args[0]) - - def GetGeneratorForAllRecipients( - self, list_name, num_retries=gdata.service.DEFAULT_NUM_RETRIES, - delay=gdata.service.DEFAULT_DELAY, backoff=gdata.service.DEFAULT_BACKOFF): - """Retrieve a generator for all recipients of a particular emaillist.""" - first_page = self.RetrievePageOfRecipients(list_name, - num_retries=num_retries, - delay=delay, - backoff=backoff) - return self.GetGeneratorFromLinkFinder( - first_page, gdata.apps.EmailListRecipientFeedFromString, - num_retries=num_retries, delay=delay, backoff=backoff) - - def RetrieveAllRecipients(self, list_name): - """Retrieve all recipient of an email list.""" - - ret = self.RetrievePageOfRecipients(list_name) - # pagination - return self.AddAllElementsFromAllPages( - ret, gdata.apps.EmailListRecipientFeedFromString) - - def AddRecipientToEmailList(self, recipient, list_name): - """Add a recipient to a email list.""" - - uri = "%s/emailList/%s/%s/recipient" % ( - self._baseURL(), API_VER, list_name) - recipient_entry = gdata.apps.EmailListRecipientEntry() - recipient_entry.who = gdata.apps.Who(email=recipient) - - try: - return gdata.apps.EmailListRecipientEntryFromString( - str(self.Post(recipient_entry, uri))) - except gdata.service.RequestError, e: - raise AppsForYourDomainException(e.args[0]) - - def DeleteEmailList(self, list_name): - """Delete a email list""" - - uri = "%s/emailList/%s/%s" % (self._baseURL(), API_VER, list_name) - try: - self.Delete(uri) - except gdata.service.RequestError, e: - raise AppsForYourDomainException(e.args[0]) - - def CreateEmailList(self, list_name): - """Create a email list. """ - - uri = "%s/emailList/%s" % (self._baseURL(), API_VER) - email_list_entry = gdata.apps.EmailListEntry() - email_list_entry.email_list = gdata.apps.EmailList(name=list_name) - try: - return gdata.apps.EmailListEntryFromString( - str(self.Post(email_list_entry, uri))) - except gdata.service.RequestError, e: - raise AppsForYourDomainException(e.args[0]) - - def DeleteNickname(self, nickname): - """Delete a nickname""" - - uri = "%s/nickname/%s/%s" % (self._baseURL(), API_VER, nickname) - try: - self.Delete(uri) - except gdata.service.RequestError, e: - raise AppsForYourDomainException(e.args[0]) - - def RetrievePageOfNicknames(self, start_nickname=None, - num_retries=gdata.service.DEFAULT_NUM_RETRIES, - delay=gdata.service.DEFAULT_DELAY, - backoff=gdata.service.DEFAULT_BACKOFF): - """Retrieve one page of nicknames in the domain""" - - uri = "%s/nickname/%s" % (self._baseURL(), API_VER) - if start_nickname is not None: - uri += "?startNickname=%s" % start_nickname - try: - return gdata.apps.NicknameFeedFromString(str(self.GetWithRetries( - uri, num_retries=num_retries, delay=delay, backoff=backoff))) - except gdata.service.RequestError, e: - raise AppsForYourDomainException(e.args[0]) - - def GetGeneratorForAllNicknames( - self, num_retries=gdata.service.DEFAULT_NUM_RETRIES, - delay=gdata.service.DEFAULT_DELAY, backoff=gdata.service.DEFAULT_BACKOFF): - """Retrieve a generator for all nicknames in this domain.""" - first_page = self.RetrievePageOfNicknames(num_retries=num_retries, - delay=delay, - backoff=backoff) - return self.GetGeneratorFromLinkFinder( - first_page, gdata.apps.NicknameFeedFromString, num_retries=num_retries, - delay=delay, backoff=backoff) - - def RetrieveAllNicknames(self): - """Retrieve all nicknames in the domain""" - - ret = self.RetrievePageOfNicknames() - # pagination - return self.AddAllElementsFromAllPages( - ret, gdata.apps.NicknameFeedFromString) - - def GetGeneratorForAllNicknamesOfAUser( - self, user_name, num_retries=gdata.service.DEFAULT_NUM_RETRIES, - delay=gdata.service.DEFAULT_DELAY, backoff=gdata.service.DEFAULT_BACKOFF): - """Retrieve a generator for all nicknames of a particular user.""" - uri = "%s/nickname/%s?username=%s" % (self._baseURL(), API_VER, user_name) - try: - first_page = gdata.apps.NicknameFeedFromString(str(self.GetWithRetries( - uri, num_retries=num_retries, delay=delay, backoff=backoff))) - except gdata.service.RequestError, e: - raise AppsForYourDomainException(e.args[0]) - return self.GetGeneratorFromLinkFinder( - first_page, gdata.apps.NicknameFeedFromString, num_retries=num_retries, - delay=delay, backoff=backoff) - - def RetrieveNicknames(self, user_name): - """Retrieve nicknames of the user""" - - uri = "%s/nickname/%s?username=%s" % (self._baseURL(), API_VER, user_name) - try: - ret = gdata.apps.NicknameFeedFromString(str(self.Get(uri))) - except gdata.service.RequestError, e: - raise AppsForYourDomainException(e.args[0]) - - # pagination - return self.AddAllElementsFromAllPages( - ret, gdata.apps.NicknameFeedFromString) - - def RetrieveNickname(self, nickname): - """Retrieve a nickname. - - Args: - nickname: string The nickname to retrieve - - Returns: - gdata.apps.NicknameEntry - """ - - uri = "%s/nickname/%s/%s" % (self._baseURL(), API_VER, nickname) - try: - return gdata.apps.NicknameEntryFromString(str(self.Get(uri))) - except gdata.service.RequestError, e: - raise AppsForYourDomainException(e.args[0]) - - def CreateNickname(self, user_name, nickname): - """Create a nickname""" - - uri = "%s/nickname/%s" % (self._baseURL(), API_VER) - nickname_entry = gdata.apps.NicknameEntry() - nickname_entry.login = gdata.apps.Login(user_name=user_name) - nickname_entry.nickname = gdata.apps.Nickname(name=nickname) - - try: - return gdata.apps.NicknameEntryFromString( - str(self.Post(nickname_entry, uri))) - except gdata.service.RequestError, e: - raise AppsForYourDomainException(e.args[0]) - - def DeleteUser(self, user_name): - """Delete a user account""" - - uri = "%s/user/%s/%s" % (self._baseURL(), API_VER, user_name) - try: - return self.Delete(uri) - except gdata.service.RequestError, e: - raise AppsForYourDomainException(e.args[0]) - - def UpdateUser(self, user_name, user_entry): - """Update a user account.""" - - uri = "%s/user/%s/%s" % (self._baseURL(), API_VER, user_name) - try: - return gdata.apps.UserEntryFromString(str(self.Put(user_entry, uri))) - except gdata.service.RequestError, e: - raise AppsForYourDomainException(e.args[0]) - - def CreateUser(self, user_name, family_name, given_name, password, - suspended='false', quota_limit=None, - password_hash_function=None, - change_password=None): - """Create a user account. """ - - uri = "%s/user/%s" % (self._baseURL(), API_VER) - user_entry = gdata.apps.UserEntry() - user_entry.login = gdata.apps.Login( - user_name=user_name, password=password, suspended=suspended, - hash_function_name=password_hash_function, - change_password=change_password) - user_entry.name = gdata.apps.Name(family_name=family_name, - given_name=given_name) - if quota_limit is not None: - user_entry.quota = gdata.apps.Quota(limit=str(quota_limit)) - - try: - return gdata.apps.UserEntryFromString(str(self.Post(user_entry, uri))) - except gdata.service.RequestError, e: - raise AppsForYourDomainException(e.args[0]) - - def SuspendUser(self, user_name): - user_entry = self.RetrieveUser(user_name) - if user_entry.login.suspended != 'true': - user_entry.login.suspended = 'true' - user_entry = self.UpdateUser(user_name, user_entry) - return user_entry - - def RestoreUser(self, user_name): - user_entry = self.RetrieveUser(user_name) - if user_entry.login.suspended != 'false': - user_entry.login.suspended = 'false' - user_entry = self.UpdateUser(user_name, user_entry) - return user_entry - - def RetrieveUser(self, user_name): - """Retrieve an user account. - - Args: - user_name: string The user name to retrieve - - Returns: - gdata.apps.UserEntry - """ - - uri = "%s/user/%s/%s" % (self._baseURL(), API_VER, user_name) - try: - return gdata.apps.UserEntryFromString(str(self.Get(uri))) - except gdata.service.RequestError, e: - raise AppsForYourDomainException(e.args[0]) - - def RetrievePageOfUsers(self, start_username=None, - num_retries=gdata.service.DEFAULT_NUM_RETRIES, - delay=gdata.service.DEFAULT_DELAY, - backoff=gdata.service.DEFAULT_BACKOFF): - """Retrieve one page of users in this domain.""" - - uri = "%s/user/%s" % (self._baseURL(), API_VER) - if start_username is not None: - uri += "?startUsername=%s" % start_username - try: - return gdata.apps.UserFeedFromString(str(self.GetWithRetries( - uri, num_retries=num_retries, delay=delay, backoff=backoff))) - except gdata.service.RequestError, e: - raise AppsForYourDomainException(e.args[0]) - - def GetGeneratorForAllUsers(self, - num_retries=gdata.service.DEFAULT_NUM_RETRIES, - delay=gdata.service.DEFAULT_DELAY, - backoff=gdata.service.DEFAULT_BACKOFF): - """Retrieve a generator for all users in this domain.""" - first_page = self.RetrievePageOfUsers(num_retries=num_retries, delay=delay, - backoff=backoff) - return self.GetGeneratorFromLinkFinder( - first_page, gdata.apps.UserFeedFromString, num_retries=num_retries, - delay=delay, backoff=backoff) - - def RetrieveAllUsers(self): - """Retrieve all users in this domain. OBSOLETE""" - - ret = self.RetrievePageOfUsers() - # pagination - return self.AddAllElementsFromAllPages( - ret, gdata.apps.UserFeedFromString) - - -class PropertyService(gdata.service.GDataService): - """Client for the Google Apps Property service.""" - - def __init__(self, email=None, password=None, domain=None, source=None, - server='apps-apis.google.com', additional_headers=None): - gdata.service.GDataService.__init__(self, email=email, password=password, - service='apps', source=source, - server=server, - additional_headers=additional_headers) - self.ssl = True - self.port = 443 - self.domain = domain - - def AddAllElementsFromAllPages(self, link_finder, func): - """retrieve all pages and add all elements""" - next = link_finder.GetNextLink() - while next is not None: - next_feed = self.Get(next.href, converter=func) - for a_entry in next_feed.entry: - link_finder.entry.append(a_entry) - next = next_feed.GetNextLink() - return link_finder - - def _GetPropertyEntry(self, properties): - property_entry = gdata.apps.PropertyEntry() - property = [] - for name, value in properties.iteritems(): - if name is not None and value is not None: - property.append(gdata.apps.Property(name=name, value=value)) - property_entry.property = property - return property_entry - - def _PropertyEntry2Dict(self, property_entry): - properties = {} - for i, property in enumerate(property_entry.property): - properties[property.name] = property.value - return properties - - def _GetPropertyFeed(self, uri): - try: - return gdata.apps.PropertyFeedFromString(str(self.Get(uri))) - except gdata.service.RequestError, e: - raise gdata.apps.service.AppsForYourDomainException(e.args[0]) - - def _GetPropertiesList(self, uri): - property_feed = self._GetPropertyFeed(uri) - # pagination - property_feed = self.AddAllElementsFromAllPages( - property_feed, gdata.apps.PropertyFeedFromString) - properties_list = [] - for property_entry in property_feed.entry: - properties_list.append(self._PropertyEntry2Dict(property_entry)) - return properties_list - - def _GetProperties(self, uri): - try: - return self._PropertyEntry2Dict(gdata.apps.PropertyEntryFromString( - str(self.Get(uri)))) - except gdata.service.RequestError, e: - raise gdata.apps.service.AppsForYourDomainException(e.args[0]) - - def _PostProperties(self, uri, properties): - property_entry = self._GetPropertyEntry(properties) - try: - return self._PropertyEntry2Dict(gdata.apps.PropertyEntryFromString( - str(self.Post(property_entry, uri)))) - except gdata.service.RequestError, e: - raise gdata.apps.service.AppsForYourDomainException(e.args[0]) - - def _PutProperties(self, uri, properties): - property_entry = self._GetPropertyEntry(properties) - try: - return self._PropertyEntry2Dict(gdata.apps.PropertyEntryFromString( - str(self.Put(property_entry, uri)))) - except gdata.service.RequestError, e: - raise gdata.apps.service.AppsForYourDomainException(e.args[0]) - - def _DeleteProperties(self, uri): - try: - self.Delete(uri) - except gdata.service.RequestError, e: - raise gdata.apps.service.AppsForYourDomainException(e.args[0]) - - -def _bool2str(b): - if b is None: - return None - return str(b is True).lower() diff --git a/gdata/analytics/apps_property.py b/gdata/analytics/apps_property.py deleted file mode 100644 index 5afa1f38b9..0000000000 --- a/gdata/analytics/apps_property.py +++ /dev/null @@ -1,39 +0,0 @@ -#!/usr/bin/env python -# -# Copyright (C) 2010 Google 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. - - -# This module is used for version 2 of the Google Data APIs. - - -"""Provides a base class to represent property elements in feeds. - -This module is used for version 2 of the Google Data APIs. The primary class -in this module is AppsProperty. -""" - - -__author__ = 'Vic Fryzel ' - - -import atom.core -import gdata.apps - - -class AppsProperty(atom.core.XmlElement): - """Represents an element in a feed.""" - _qname = gdata.apps.APPS_TEMPLATE % 'property' - name = 'name' - value = 'value' diff --git a/gdata/analytics/auth.py b/gdata/analytics/auth.py deleted file mode 100644 index 139c6cd016..0000000000 --- a/gdata/analytics/auth.py +++ /dev/null @@ -1,952 +0,0 @@ -#!/usr/bin/python -# -# Copyright (C) 2007 - 2009 Google 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. - - -import cgi -import math -import random -import re -import time -import types -import urllib -import atom.http_interface -import atom.token_store -import atom.url -import gdata.oauth as oauth -import gdata.oauth.rsa as oauth_rsa -import gdata.tlslite.utils.keyfactory as keyfactory -import gdata.tlslite.utils.cryptomath as cryptomath - -import gdata.gauth - -__author__ = 'api.jscudder (Jeff Scudder)' - - -PROGRAMMATIC_AUTH_LABEL = 'GoogleLogin auth=' -AUTHSUB_AUTH_LABEL = 'AuthSub token=' - - -"""This module provides functions and objects used with Google authentication. - -Details on Google authorization mechanisms used with the Google Data APIs can -be found here: -http://code.google.com/apis/gdata/auth.html -http://code.google.com/apis/accounts/ - -The essential functions are the following. -Related to ClientLogin: - generate_client_login_request_body: Constructs the body of an HTTP request to - obtain a ClientLogin token for a specific - service. - extract_client_login_token: Creates a ClientLoginToken with the token from a - success response to a ClientLogin request. - get_captcha_challenge: If the server responded to the ClientLogin request - with a CAPTCHA challenge, this method extracts the - CAPTCHA URL and identifying CAPTCHA token. - -Related to AuthSub: - generate_auth_sub_url: Constructs a full URL for a AuthSub request. The - user's browser must be sent to this Google Accounts - URL and redirected back to the app to obtain the - AuthSub token. - extract_auth_sub_token_from_url: Once the user's browser has been - redirected back to the web app, use this - function to create an AuthSubToken with - the correct authorization token and scope. - token_from_http_body: Extracts the AuthSubToken value string from the - server's response to an AuthSub session token upgrade - request. -""" - -def generate_client_login_request_body(email, password, service, source, - account_type='HOSTED_OR_GOOGLE', captcha_token=None, - captcha_response=None): - """Creates the body of the autentication request - - See http://code.google.com/apis/accounts/AuthForInstalledApps.html#Request - for more details. - - Args: - email: str - password: str - service: str - source: str - account_type: str (optional) Defaul is 'HOSTED_OR_GOOGLE', other valid - values are 'GOOGLE' and 'HOSTED' - captcha_token: str (optional) - captcha_response: str (optional) - - Returns: - The HTTP body to send in a request for a client login token. - """ - return gdata.gauth.generate_client_login_request_body(email, password, - service, source, account_type, captcha_token, captcha_response) - - -GenerateClientLoginRequestBody = generate_client_login_request_body - - -def GenerateClientLoginAuthToken(http_body): - """Returns the token value to use in Authorization headers. - - Reads the token from the server's response to a Client Login request and - creates header value to use in requests. - - Args: - http_body: str The body of the server's HTTP response to a Client Login - request - - Returns: - The value half of an Authorization header. - """ - token = get_client_login_token(http_body) - if token: - return 'GoogleLogin auth=%s' % token - return None - - -def get_client_login_token(http_body): - """Returns the token value for a ClientLoginToken. - - Reads the token from the server's response to a Client Login request and - creates the token value string to use in requests. - - Args: - http_body: str The body of the server's HTTP response to a Client Login - request - - Returns: - The token value string for a ClientLoginToken. - """ - return gdata.gauth.get_client_login_token_string(http_body) - - -def extract_client_login_token(http_body, scopes): - """Parses the server's response and returns a ClientLoginToken. - - Args: - http_body: str The body of the server's HTTP response to a Client Login - request. It is assumed that the login request was successful. - scopes: list containing atom.url.Urls or strs. The scopes list contains - all of the partial URLs under which the client login token is - valid. For example, if scopes contains ['http://example.com/foo'] - then the client login token would be valid for - http://example.com/foo/bar/baz - - Returns: - A ClientLoginToken which is valid for the specified scopes. - """ - token_string = get_client_login_token(http_body) - token = ClientLoginToken(scopes=scopes) - token.set_token_string(token_string) - return token - - -def get_captcha_challenge(http_body, - captcha_base_url='http://www.google.com/accounts/'): - """Returns the URL and token for a CAPTCHA challenge issued by the server. - - Args: - http_body: str The body of the HTTP response from the server which - contains the CAPTCHA challenge. - captcha_base_url: str This function returns a full URL for viewing the - challenge image which is built from the server's response. This - base_url is used as the beginning of the URL because the server - only provides the end of the URL. For example the server provides - 'Captcha?ctoken=Hi...N' and the URL for the image is - 'http://www.google.com/accounts/Captcha?ctoken=Hi...N' - - Returns: - A dictionary containing the information needed to repond to the CAPTCHA - challenge, the image URL and the ID token of the challenge. The - dictionary is in the form: - {'token': string identifying the CAPTCHA image, - 'url': string containing the URL of the image} - Returns None if there was no CAPTCHA challenge in the response. - """ - return gdata.gauth.get_captcha_challenge(http_body, captcha_base_url) - - -GetCaptchaChallenge = get_captcha_challenge - - -def GenerateOAuthRequestTokenUrl( - oauth_input_params, scopes, - request_token_url='https://www.google.com/accounts/OAuthGetRequestToken', - extra_parameters=None): - """Generate a URL at which a request for OAuth request token is to be sent. - - Args: - oauth_input_params: OAuthInputParams OAuth input parameters. - scopes: list of strings The URLs of the services to be accessed. - request_token_url: string The beginning of the request token URL. This is - normally 'https://www.google.com/accounts/OAuthGetRequestToken' or - '/accounts/OAuthGetRequestToken' - extra_parameters: dict (optional) key-value pairs as any additional - parameters to be included in the URL and signature while making a - request for fetching an OAuth request token. All the OAuth parameters - are added by default. But if provided through this argument, any - default parameters will be overwritten. For e.g. a default parameter - oauth_version 1.0 can be overwritten if - extra_parameters = {'oauth_version': '2.0'} - - Returns: - atom.url.Url OAuth request token URL. - """ - scopes_string = ' '.join([str(scope) for scope in scopes]) - parameters = {'scope': scopes_string} - if extra_parameters: - parameters.update(extra_parameters) - oauth_request = oauth.OAuthRequest.from_consumer_and_token( - oauth_input_params.GetConsumer(), http_url=request_token_url, - parameters=parameters) - oauth_request.sign_request(oauth_input_params.GetSignatureMethod(), - oauth_input_params.GetConsumer(), None) - return atom.url.parse_url(oauth_request.to_url()) - - -def GenerateOAuthAuthorizationUrl( - request_token, - authorization_url='https://www.google.com/accounts/OAuthAuthorizeToken', - callback_url=None, extra_params=None, - include_scopes_in_callback=False, scopes_param_prefix='oauth_token_scope'): - """Generates URL at which user will login to authorize the request token. - - Args: - request_token: gdata.auth.OAuthToken OAuth request token. - authorization_url: string The beginning of the authorization URL. This is - normally 'https://www.google.com/accounts/OAuthAuthorizeToken' or - '/accounts/OAuthAuthorizeToken' - callback_url: string (optional) The URL user will be sent to after - logging in and granting access. - extra_params: dict (optional) Additional parameters to be sent. - include_scopes_in_callback: Boolean (default=False) if set to True, and - if 'callback_url' is present, the 'callback_url' will be modified to - include the scope(s) from the request token as a URL parameter. The - key for the 'callback' URL's scope parameter will be - OAUTH_SCOPE_URL_PARAM_NAME. The benefit of including the scope URL as - a parameter to the 'callback' URL, is that the page which receives - the OAuth token will be able to tell which URLs the token grants - access to. - scopes_param_prefix: string (default='oauth_token_scope') The URL - parameter key which maps to the list of valid scopes for the token. - This URL parameter will be included in the callback URL along with - the scopes of the token as value if include_scopes_in_callback=True. - - Returns: - atom.url.Url OAuth authorization URL. - """ - scopes = request_token.scopes - if isinstance(scopes, list): - scopes = ' '.join(scopes) - if include_scopes_in_callback and callback_url: - if callback_url.find('?') > -1: - callback_url += '&' - else: - callback_url += '?' - callback_url += urllib.urlencode({scopes_param_prefix:scopes}) - oauth_token = oauth.OAuthToken(request_token.key, request_token.secret) - oauth_request = oauth.OAuthRequest.from_token_and_callback( - token=oauth_token, callback=callback_url, - http_url=authorization_url, parameters=extra_params) - return atom.url.parse_url(oauth_request.to_url()) - - -def GenerateOAuthAccessTokenUrl( - authorized_request_token, - oauth_input_params, - access_token_url='https://www.google.com/accounts/OAuthGetAccessToken', - oauth_version='1.0', - oauth_verifier=None): - """Generates URL at which user will login to authorize the request token. - - Args: - authorized_request_token: gdata.auth.OAuthToken OAuth authorized request - token. - oauth_input_params: OAuthInputParams OAuth input parameters. - access_token_url: string The beginning of the authorization URL. This is - normally 'https://www.google.com/accounts/OAuthGetAccessToken' or - '/accounts/OAuthGetAccessToken' - oauth_version: str (default='1.0') oauth_version parameter. - oauth_verifier: str (optional) If present, it is assumed that the client - will use the OAuth v1.0a protocol which includes passing the - oauth_verifier (as returned by the SP) in the access token step. - - Returns: - atom.url.Url OAuth access token URL. - """ - oauth_token = oauth.OAuthToken(authorized_request_token.key, - authorized_request_token.secret) - parameters = {'oauth_version': oauth_version} - if oauth_verifier is not None: - parameters['oauth_verifier'] = oauth_verifier - oauth_request = oauth.OAuthRequest.from_consumer_and_token( - oauth_input_params.GetConsumer(), token=oauth_token, - http_url=access_token_url, parameters=parameters) - oauth_request.sign_request(oauth_input_params.GetSignatureMethod(), - oauth_input_params.GetConsumer(), oauth_token) - return atom.url.parse_url(oauth_request.to_url()) - - -def GenerateAuthSubUrl(next, scope, secure=False, session=True, - request_url='https://www.google.com/accounts/AuthSubRequest', - domain='default'): - """Generate a URL at which the user will login and be redirected back. - - Users enter their credentials on a Google login page and a token is sent - to the URL specified in next. See documentation for AuthSub login at: - http://code.google.com/apis/accounts/AuthForWebApps.html - - Args: - request_url: str The beginning of the request URL. This is normally - 'http://www.google.com/accounts/AuthSubRequest' or - '/accounts/AuthSubRequest' - next: string The URL user will be sent to after logging in. - scope: string The URL of the service to be accessed. - secure: boolean (optional) Determines whether or not the issued token - is a secure token. - session: boolean (optional) Determines whether or not the issued token - can be upgraded to a session token. - domain: str (optional) The Google Apps domain for this account. If this - is not a Google Apps account, use 'default' which is the default - value. - """ - # Translate True/False values for parameters into numeric values acceoted - # by the AuthSub service. - if secure: - secure = 1 - else: - secure = 0 - - if session: - session = 1 - else: - session = 0 - - request_params = urllib.urlencode({'next': next, 'scope': scope, - 'secure': secure, 'session': session, - 'hd': domain}) - if request_url.find('?') == -1: - return '%s?%s' % (request_url, request_params) - else: - # The request URL already contained url parameters so we should add - # the parameters using the & seperator - return '%s&%s' % (request_url, request_params) - - -def generate_auth_sub_url(next, scopes, secure=False, session=True, - request_url='https://www.google.com/accounts/AuthSubRequest', - domain='default', scopes_param_prefix='auth_sub_scopes'): - """Constructs a URL string for requesting a multiscope AuthSub token. - - The generated token will contain a URL parameter to pass along the - requested scopes to the next URL. When the Google Accounts page - redirects the broswser to the 'next' URL, it appends the single use - AuthSub token value to the URL as a URL parameter with the key 'token'. - However, the information about which scopes were requested is not - included by Google Accounts. This method adds the scopes to the next - URL before making the request so that the redirect will be sent to - a page, and both the token value and the list of scopes can be - extracted from the request URL. - - Args: - next: atom.url.URL or string The URL user will be sent to after - authorizing this web application to access their data. - scopes: list containint strings The URLs of the services to be accessed. - secure: boolean (optional) Determines whether or not the issued token - is a secure token. - session: boolean (optional) Determines whether or not the issued token - can be upgraded to a session token. - request_url: atom.url.Url or str The beginning of the request URL. This - is normally 'http://www.google.com/accounts/AuthSubRequest' or - '/accounts/AuthSubRequest' - domain: The domain which the account is part of. This is used for Google - Apps accounts, the default value is 'default' which means that the - requested account is a Google Account (@gmail.com for example) - scopes_param_prefix: str (optional) The requested scopes are added as a - URL parameter to the next URL so that the page at the 'next' URL can - extract the token value and the valid scopes from the URL. The key - for the URL parameter defaults to 'auth_sub_scopes' - - Returns: - An atom.url.Url which the user's browser should be directed to in order - to authorize this application to access their information. - """ - if isinstance(next, (str, unicode)): - next = atom.url.parse_url(next) - scopes_string = ' '.join([str(scope) for scope in scopes]) - next.params[scopes_param_prefix] = scopes_string - - if isinstance(request_url, (str, unicode)): - request_url = atom.url.parse_url(request_url) - request_url.params['next'] = str(next) - request_url.params['scope'] = scopes_string - if session: - request_url.params['session'] = 1 - else: - request_url.params['session'] = 0 - if secure: - request_url.params['secure'] = 1 - else: - request_url.params['secure'] = 0 - request_url.params['hd'] = domain - return request_url - - -def AuthSubTokenFromUrl(url): - """Extracts the AuthSub token from the URL. - - Used after the AuthSub redirect has sent the user to the 'next' page and - appended the token to the URL. This function returns the value to be used - in the Authorization header. - - Args: - url: str The URL of the current page which contains the AuthSub token as - a URL parameter. - """ - token = TokenFromUrl(url) - if token: - return 'AuthSub token=%s' % token - return None - - -def TokenFromUrl(url): - """Extracts the AuthSub token from the URL. - - Returns the raw token value. - - Args: - url: str The URL or the query portion of the URL string (after the ?) of - the current page which contains the AuthSub token as a URL parameter. - """ - if url.find('?') > -1: - query_params = url.split('?')[1] - else: - query_params = url - for pair in query_params.split('&'): - if pair.startswith('token='): - return pair[6:] - return None - - -def extract_auth_sub_token_from_url(url, - scopes_param_prefix='auth_sub_scopes', rsa_key=None): - """Creates an AuthSubToken and sets the token value and scopes from the URL. - - After the Google Accounts AuthSub pages redirect the user's broswer back to - the web application (using the 'next' URL from the request) the web app must - extract the token from the current page's URL. The token is provided as a - URL parameter named 'token' and if generate_auth_sub_url was used to create - the request, the token's valid scopes are included in a URL parameter whose - name is specified in scopes_param_prefix. - - Args: - url: atom.url.Url or str representing the current URL. The token value - and valid scopes should be included as URL parameters. - scopes_param_prefix: str (optional) The URL parameter key which maps to - the list of valid scopes for the token. - - Returns: - An AuthSubToken with the token value from the URL and set to be valid for - the scopes passed in on the URL. If no scopes were included in the URL, - the AuthSubToken defaults to being valid for no scopes. If there was no - 'token' parameter in the URL, this function returns None. - """ - if isinstance(url, (str, unicode)): - url = atom.url.parse_url(url) - if 'token' not in url.params: - return None - scopes = [] - if scopes_param_prefix in url.params: - scopes = url.params[scopes_param_prefix].split(' ') - token_value = url.params['token'] - if rsa_key: - token = SecureAuthSubToken(rsa_key, scopes=scopes) - else: - token = AuthSubToken(scopes=scopes) - token.set_token_string(token_value) - return token - - -def AuthSubTokenFromHttpBody(http_body): - """Extracts the AuthSub token from an HTTP body string. - - Used to find the new session token after making a request to upgrade a - single use AuthSub token. - - Args: - http_body: str The repsonse from the server which contains the AuthSub - key. For example, this function would find the new session token - from the server's response to an upgrade token request. - - Returns: - The header value to use for Authorization which contains the AuthSub - token. - """ - token_value = token_from_http_body(http_body) - if token_value: - return '%s%s' % (AUTHSUB_AUTH_LABEL, token_value) - return None - - -def token_from_http_body(http_body): - """Extracts the AuthSub token from an HTTP body string. - - Used to find the new session token after making a request to upgrade a - single use AuthSub token. - - Args: - http_body: str The repsonse from the server which contains the AuthSub - key. For example, this function would find the new session token - from the server's response to an upgrade token request. - - Returns: - The raw token value to use in an AuthSubToken object. - """ - for response_line in http_body.splitlines(): - if response_line.startswith('Token='): - # Strip off Token= and return the token value string. - return response_line[6:] - return None - - -TokenFromHttpBody = token_from_http_body - - -def OAuthTokenFromUrl(url, scopes_param_prefix='oauth_token_scope'): - """Creates an OAuthToken and sets token key and scopes (if present) from URL. - - After the Google Accounts OAuth pages redirect the user's broswer back to - the web application (using the 'callback' URL from the request) the web app - can extract the token from the current page's URL. The token is same as the - request token, but it is either authorized (if user grants access) or - unauthorized (if user denies access). The token is provided as a - URL parameter named 'oauth_token' and if it was chosen to use - GenerateOAuthAuthorizationUrl with include_scopes_in_param=True, the token's - valid scopes are included in a URL parameter whose name is specified in - scopes_param_prefix. - - Args: - url: atom.url.Url or str representing the current URL. The token value - and valid scopes should be included as URL parameters. - scopes_param_prefix: str (optional) The URL parameter key which maps to - the list of valid scopes for the token. - - Returns: - An OAuthToken with the token key from the URL and set to be valid for - the scopes passed in on the URL. If no scopes were included in the URL, - the OAuthToken defaults to being valid for no scopes. If there was no - 'oauth_token' parameter in the URL, this function returns None. - """ - if isinstance(url, (str, unicode)): - url = atom.url.parse_url(url) - if 'oauth_token' not in url.params: - return None - scopes = [] - if scopes_param_prefix in url.params: - scopes = url.params[scopes_param_prefix].split(' ') - token_key = url.params['oauth_token'] - token = OAuthToken(key=token_key, scopes=scopes) - return token - - -def OAuthTokenFromHttpBody(http_body): - """Parses the HTTP response body and returns an OAuth token. - - The returned OAuth token will just have key and secret parameters set. - It won't have any knowledge about the scopes or oauth_input_params. It is - your responsibility to make it aware of the remaining parameters. - - Returns: - OAuthToken OAuth token. - """ - token = oauth.OAuthToken.from_string(http_body) - oauth_token = OAuthToken(key=token.key, secret=token.secret) - return oauth_token - - -class OAuthSignatureMethod(object): - """Holds valid OAuth signature methods. - - RSA_SHA1: Class to build signature according to RSA-SHA1 algorithm. - HMAC_SHA1: Class to build signature according to HMAC-SHA1 algorithm. - """ - - HMAC_SHA1 = oauth.OAuthSignatureMethod_HMAC_SHA1 - - class RSA_SHA1(oauth_rsa.OAuthSignatureMethod_RSA_SHA1): - """Provides implementation for abstract methods to return RSA certs.""" - - def __init__(self, private_key, public_cert): - self.private_key = private_key - self.public_cert = public_cert - - def _fetch_public_cert(self, unused_oauth_request): - return self.public_cert - - def _fetch_private_cert(self, unused_oauth_request): - return self.private_key - - -class OAuthInputParams(object): - """Stores OAuth input parameters. - - This class is a store for OAuth input parameters viz. consumer key and secret, - signature method and RSA key. - """ - - def __init__(self, signature_method, consumer_key, consumer_secret=None, - rsa_key=None, requestor_id=None): - """Initializes object with parameters required for using OAuth mechanism. - - NOTE: Though consumer_secret and rsa_key are optional, either of the two - is required depending on the value of the signature_method. - - Args: - signature_method: class which provides implementation for strategy class - oauth.oauth.OAuthSignatureMethod. Signature method to be used for - signing each request. Valid implementations are provided as the - constants defined by gdata.auth.OAuthSignatureMethod. Currently - they are gdata.auth.OAuthSignatureMethod.RSA_SHA1 and - gdata.auth.OAuthSignatureMethod.HMAC_SHA1. Instead of passing in - the strategy class, you may pass in a string for 'RSA_SHA1' or - 'HMAC_SHA1'. If you plan to use OAuth on App Engine (or another - WSGI environment) I recommend specifying signature method using a - string (the only options are 'RSA_SHA1' and 'HMAC_SHA1'). In these - environments there are sometimes issues with pickling an object in - which a member references a class or function. Storing a string to - refer to the signature method mitigates complications when - pickling. - consumer_key: string Domain identifying third_party web application. - consumer_secret: string (optional) Secret generated during registration. - Required only for HMAC_SHA1 signature method. - rsa_key: string (optional) Private key required for RSA_SHA1 signature - method. - requestor_id: string (optional) User email adress to make requests on - their behalf. This parameter should only be set when performing - 2 legged OAuth requests. - """ - if (signature_method == OAuthSignatureMethod.RSA_SHA1 - or signature_method == 'RSA_SHA1'): - self.__signature_strategy = 'RSA_SHA1' - elif (signature_method == OAuthSignatureMethod.HMAC_SHA1 - or signature_method == 'HMAC_SHA1'): - self.__signature_strategy = 'HMAC_SHA1' - else: - self.__signature_strategy = signature_method - self.rsa_key = rsa_key - self._consumer = oauth.OAuthConsumer(consumer_key, consumer_secret) - self.requestor_id = requestor_id - - def __get_signature_method(self): - if self.__signature_strategy == 'RSA_SHA1': - return OAuthSignatureMethod.RSA_SHA1(self.rsa_key, None) - elif self.__signature_strategy == 'HMAC_SHA1': - return OAuthSignatureMethod.HMAC_SHA1() - else: - return self.__signature_strategy() - - def __set_signature_method(self, signature_method): - if (signature_method == OAuthSignatureMethod.RSA_SHA1 - or signature_method == 'RSA_SHA1'): - self.__signature_strategy = 'RSA_SHA1' - elif (signature_method == OAuthSignatureMethod.HMAC_SHA1 - or signature_method == 'HMAC_SHA1'): - self.__signature_strategy = 'HMAC_SHA1' - else: - self.__signature_strategy = signature_method - - _signature_method = property(__get_signature_method, __set_signature_method, - doc="""Returns object capable of signing the request using RSA of HMAC. - - Replaces the _signature_method member to avoid pickle errors.""") - - def GetSignatureMethod(self): - """Gets the OAuth signature method. - - Returns: - object of supertype - """ - return self._signature_method - - def GetConsumer(self): - """Gets the OAuth consumer. - - Returns: - object of type - """ - return self._consumer - - -class ClientLoginToken(atom.http_interface.GenericToken): - """Stores the Authorization header in auth_header and adds to requests. - - This token will add it's Authorization header to an HTTP request - as it is made. Ths token class is simple but - some Token classes must calculate portions of the Authorization header - based on the request being made, which is why the token is responsible - for making requests via an http_client parameter. - - Args: - auth_header: str The value for the Authorization header. - scopes: list of str or atom.url.Url specifying the beginnings of URLs - for which this token can be used. For example, if scopes contains - 'http://example.com/foo', then this token can be used for a request to - 'http://example.com/foo/bar' but it cannot be used for a request to - 'http://example.com/baz' - """ - def __init__(self, auth_header=None, scopes=None): - self.auth_header = auth_header - self.scopes = scopes or [] - - def __str__(self): - return self.auth_header - - def perform_request(self, http_client, operation, url, data=None, - headers=None): - """Sets the Authorization header and makes the HTTP request.""" - if headers is None: - headers = {'Authorization':self.auth_header} - else: - headers['Authorization'] = self.auth_header - return http_client.request(operation, url, data=data, headers=headers) - - def get_token_string(self): - """Removes PROGRAMMATIC_AUTH_LABEL to give just the token value.""" - return self.auth_header[len(PROGRAMMATIC_AUTH_LABEL):] - - def set_token_string(self, token_string): - self.auth_header = '%s%s' % (PROGRAMMATIC_AUTH_LABEL, token_string) - - def valid_for_scope(self, url): - """Tells the caller if the token authorizes access to the desired URL. - """ - if isinstance(url, (str, unicode)): - url = atom.url.parse_url(url) - for scope in self.scopes: - if scope == atom.token_store.SCOPE_ALL: - return True - if isinstance(scope, (str, unicode)): - scope = atom.url.parse_url(scope) - if scope == url: - return True - # Check the host and the path, but ignore the port and protocol. - elif scope.host == url.host and not scope.path: - return True - elif scope.host == url.host and scope.path and not url.path: - continue - elif scope.host == url.host and url.path.startswith(scope.path): - return True - return False - - -class AuthSubToken(ClientLoginToken): - def get_token_string(self): - """Removes AUTHSUB_AUTH_LABEL to give just the token value.""" - return self.auth_header[len(AUTHSUB_AUTH_LABEL):] - - def set_token_string(self, token_string): - self.auth_header = '%s%s' % (AUTHSUB_AUTH_LABEL, token_string) - - -class OAuthToken(atom.http_interface.GenericToken): - """Stores the token key, token secret and scopes for which token is valid. - - This token adds the authorization header to each request made. It - re-calculates authorization header for every request since the OAuth - signature to be added to the authorization header is dependent on the - request parameters. - - Attributes: - key: str The value for the OAuth token i.e. token key. - secret: str The value for the OAuth token secret. - scopes: list of str or atom.url.Url specifying the beginnings of URLs - for which this token can be used. For example, if scopes contains - 'http://example.com/foo', then this token can be used for a request to - 'http://example.com/foo/bar' but it cannot be used for a request to - 'http://example.com/baz' - oauth_input_params: OAuthInputParams OAuth input parameters. - """ - - def __init__(self, key=None, secret=None, scopes=None, - oauth_input_params=None): - self.key = key - self.secret = secret - self.scopes = scopes or [] - self.oauth_input_params = oauth_input_params - - def __str__(self): - return self.get_token_string() - - def get_token_string(self): - """Returns the token string. - - The token string returned is of format - oauth_token=[0]&oauth_token_secret=[1], where [0] and [1] are some strings. - - Returns: - A token string of format oauth_token=[0]&oauth_token_secret=[1], - where [0] and [1] are some strings. If self.secret is absent, it just - returns oauth_token=[0]. If self.key is absent, it just returns - oauth_token_secret=[1]. If both are absent, it returns None. - """ - if self.key and self.secret: - return urllib.urlencode({'oauth_token': self.key, - 'oauth_token_secret': self.secret}) - elif self.key: - return 'oauth_token=%s' % self.key - elif self.secret: - return 'oauth_token_secret=%s' % self.secret - else: - return None - - def set_token_string(self, token_string): - """Sets the token key and secret from the token string. - - Args: - token_string: str Token string of form - oauth_token=[0]&oauth_token_secret=[1]. If oauth_token is not present, - self.key will be None. If oauth_token_secret is not present, - self.secret will be None. - """ - token_params = cgi.parse_qs(token_string, keep_blank_values=False) - if 'oauth_token' in token_params: - self.key = token_params['oauth_token'][0] - if 'oauth_token_secret' in token_params: - self.secret = token_params['oauth_token_secret'][0] - - def GetAuthHeader(self, http_method, http_url, realm=''): - """Get the authentication header. - - Args: - http_method: string HTTP method i.e. operation e.g. GET, POST, PUT, etc. - http_url: string or atom.url.Url HTTP URL to which request is made. - realm: string (default='') realm parameter to be included in the - authorization header. - - Returns: - dict Header to be sent with every subsequent request after - authentication. - """ - if isinstance(http_url, types.StringTypes): - http_url = atom.url.parse_url(http_url) - header = None - token = None - if self.key or self.secret: - token = oauth.OAuthToken(self.key, self.secret) - oauth_request = oauth.OAuthRequest.from_consumer_and_token( - self.oauth_input_params.GetConsumer(), token=token, - http_url=str(http_url), http_method=http_method, - parameters=http_url.params) - oauth_request.sign_request(self.oauth_input_params.GetSignatureMethod(), - self.oauth_input_params.GetConsumer(), token) - header = oauth_request.to_header(realm=realm) - header['Authorization'] = header['Authorization'].replace('+', '%2B') - return header - - def perform_request(self, http_client, operation, url, data=None, - headers=None): - """Sets the Authorization header and makes the HTTP request.""" - if not headers: - headers = {} - if self.oauth_input_params.requestor_id: - url.params['xoauth_requestor_id'] = self.oauth_input_params.requestor_id - headers.update(self.GetAuthHeader(operation, url)) - return http_client.request(operation, url, data=data, headers=headers) - - def valid_for_scope(self, url): - if isinstance(url, (str, unicode)): - url = atom.url.parse_url(url) - for scope in self.scopes: - if scope == atom.token_store.SCOPE_ALL: - return True - if isinstance(scope, (str, unicode)): - scope = atom.url.parse_url(scope) - if scope == url: - return True - # Check the host and the path, but ignore the port and protocol. - elif scope.host == url.host and not scope.path: - return True - elif scope.host == url.host and scope.path and not url.path: - continue - elif scope.host == url.host and url.path.startswith(scope.path): - return True - return False - - -class SecureAuthSubToken(AuthSubToken): - """Stores the rsa private key, token, and scopes for the secure AuthSub token. - - This token adds the authorization header to each request made. It - re-calculates authorization header for every request since the secure AuthSub - signature to be added to the authorization header is dependent on the - request parameters. - - Attributes: - rsa_key: string The RSA private key in PEM format that the token will - use to sign requests - token_string: string (optional) The value for the AuthSub token. - scopes: list of str or atom.url.Url specifying the beginnings of URLs - for which this token can be used. For example, if scopes contains - 'http://example.com/foo', then this token can be used for a request to - 'http://example.com/foo/bar' but it cannot be used for a request to - 'http://example.com/baz' - """ - - def __init__(self, rsa_key, token_string=None, scopes=None): - self.rsa_key = keyfactory.parsePEMKey(rsa_key) - self.token_string = token_string or '' - self.scopes = scopes or [] - - def __str__(self): - return self.get_token_string() - - def get_token_string(self): - return str(self.token_string) - - def set_token_string(self, token_string): - self.token_string = token_string - - def GetAuthHeader(self, http_method, http_url): - """Generates the Authorization header. - - The form of the secure AuthSub Authorization header is - Authorization: AuthSub token="token" sigalg="sigalg" data="data" sig="sig" - and data represents a string in the form - data = http_method http_url timestamp nonce - - Args: - http_method: string HTTP method i.e. operation e.g. GET, POST, PUT, etc. - http_url: string or atom.url.Url HTTP URL to which request is made. - - Returns: - dict Header to be sent with every subsequent request after authentication. - """ - timestamp = int(math.floor(time.time())) - nonce = '%lu' % random.randrange(1, 2**64) - data = '%s %s %d %s' % (http_method, str(http_url), timestamp, nonce) - sig = cryptomath.bytesToBase64(self.rsa_key.hashAndSign(data)) - header = {'Authorization': '%s"%s" data="%s" sig="%s" sigalg="rsa-sha1"' % - (AUTHSUB_AUTH_LABEL, self.token_string, data, sig)} - return header - - def perform_request(self, http_client, operation, url, data=None, - headers=None): - """Sets the Authorization header and makes the HTTP request.""" - if not headers: - headers = {} - headers.update(self.GetAuthHeader(operation, url)) - return http_client.request(operation, url, data=data, headers=headers) diff --git a/gdata/analytics/blogger/__init__.py b/gdata/analytics/blogger/__init__.py deleted file mode 100644 index 156f25c563..0000000000 --- a/gdata/analytics/blogger/__init__.py +++ /dev/null @@ -1,202 +0,0 @@ -#!/usr/bin/python -# -# Copyright (C) 2007, 2008 Google 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. - - -"""Contains extensions to Atom objects used with Blogger.""" - - -__author__ = 'api.jscudder (Jeffrey Scudder)' - - -import atom -import gdata -import re - - -LABEL_SCHEME = 'http://www.blogger.com/atom/ns#' -THR_NAMESPACE = 'http://purl.org/syndication/thread/1.0' - - -class BloggerEntry(gdata.GDataEntry): - """Adds convenience methods inherited by all Blogger entries.""" - - blog_name_pattern = re.compile('(http://)(\w*)') - blog_id_pattern = re.compile('(tag:blogger.com,1999:blog-)(\w*)') - blog_id2_pattern = re.compile('tag:blogger.com,1999:user-(\d+)\.blog-(\d+)') - - def GetBlogId(self): - """Extracts the Blogger id of this blog. - This method is useful when contructing URLs by hand. The blog id is - often used in blogger operation URLs. This should not be confused with - the id member of a BloggerBlog. The id element is the Atom id XML element. - The blog id which this method returns is a part of the Atom id. - - Returns: - The blog's unique id as a string. - """ - if self.id.text: - match = self.blog_id_pattern.match(self.id.text) - if match: - return match.group(2) - else: - return self.blog_id2_pattern.match(self.id.text).group(2) - return None - - def GetBlogName(self): - """Finds the name of this blog as used in the 'alternate' URL. - An alternate URL is in the form 'http://blogName.blogspot.com/'. For an - entry representing the above example, this method would return 'blogName'. - - Returns: - The blog's URL name component as a string. - """ - for link in self.link: - if link.rel == 'alternate': - return self.blog_name_pattern.match(link.href).group(2) - return None - - -class BlogEntry(BloggerEntry): - """Describes a blog entry in the feed listing a user's blogs.""" - - -def BlogEntryFromString(xml_string): - return atom.CreateClassFromXMLString(BlogEntry, xml_string) - - -class BlogFeed(gdata.GDataFeed): - """Describes a feed of a user's blogs.""" - - _children = gdata.GDataFeed._children.copy() - _children['{%s}entry' % atom.ATOM_NAMESPACE] = ('entry', [BlogEntry]) - - -def BlogFeedFromString(xml_string): - return atom.CreateClassFromXMLString(BlogFeed, xml_string) - - -class BlogPostEntry(BloggerEntry): - """Describes a blog post entry in the feed of a blog's posts.""" - - post_id_pattern = re.compile('(tag:blogger.com,1999:blog-)(\w*)(.post-)(\w*)') - - def AddLabel(self, label): - """Adds a label to the blog post. - - The label is represented by an Atom category element, so this method - is shorthand for appending a new atom.Category object. - - Args: - label: str - """ - self.category.append(atom.Category(scheme=LABEL_SCHEME, term=label)) - - def GetPostId(self): - """Extracts the postID string from the entry's Atom id. - - Returns: A string of digits which identify this post within the blog. - """ - if self.id.text: - return self.post_id_pattern.match(self.id.text).group(4) - return None - - -def BlogPostEntryFromString(xml_string): - return atom.CreateClassFromXMLString(BlogPostEntry, xml_string) - - -class BlogPostFeed(gdata.GDataFeed): - """Describes a feed of a blog's posts.""" - - _children = gdata.GDataFeed._children.copy() - _children['{%s}entry' % atom.ATOM_NAMESPACE] = ('entry', [BlogPostEntry]) - - -def BlogPostFeedFromString(xml_string): - return atom.CreateClassFromXMLString(BlogPostFeed, xml_string) - - -class InReplyTo(atom.AtomBase): - _tag = 'in-reply-to' - _namespace = THR_NAMESPACE - _attributes = atom.AtomBase._attributes.copy() - _attributes['href'] = 'href' - _attributes['ref'] = 'ref' - _attributes['source'] = 'source' - _attributes['type'] = 'type' - - def __init__(self, href=None, ref=None, source=None, type=None, - extension_elements=None, extension_attributes=None, text=None): - self.href = href - self.ref = ref - self.source = source - self.type = type - self.extension_elements = extension_elements or [] - self.extension_attributes = extension_attributes or {} - self.text = text - - -def InReplyToFromString(xml_string): - return atom.CreateClassFromXMLString(InReplyTo, xml_string) - - -class CommentEntry(BloggerEntry): - """Describes a blog post comment entry in the feed of a blog post's - comments.""" - - _children = BloggerEntry._children.copy() - _children['{%s}in-reply-to' % THR_NAMESPACE] = ('in_reply_to', InReplyTo) - - comment_id_pattern = re.compile('.*-(\w*)$') - - def __init__(self, author=None, category=None, content=None, - contributor=None, atom_id=None, link=None, published=None, rights=None, - source=None, summary=None, control=None, title=None, updated=None, - in_reply_to=None, extension_elements=None, extension_attributes=None, - text=None): - BloggerEntry.__init__(self, author=author, category=category, - content=content, contributor=contributor, atom_id=atom_id, link=link, - published=published, rights=rights, source=source, summary=summary, - control=control, title=title, updated=updated, - extension_elements=extension_elements, - extension_attributes=extension_attributes, text=text) - self.in_reply_to = in_reply_to - - def GetCommentId(self): - """Extracts the commentID string from the entry's Atom id. - - Returns: A string of digits which identify this post within the blog. - """ - if self.id.text: - return self.comment_id_pattern.match(self.id.text).group(1) - return None - - -def CommentEntryFromString(xml_string): - return atom.CreateClassFromXMLString(CommentEntry, xml_string) - - -class CommentFeed(gdata.GDataFeed): - """Describes a feed of a blog post's comments.""" - - _children = gdata.GDataFeed._children.copy() - _children['{%s}entry' % atom.ATOM_NAMESPACE] = ('entry', [CommentEntry]) - - -def CommentFeedFromString(xml_string): - return atom.CreateClassFromXMLString(CommentFeed, xml_string) - - diff --git a/gdata/analytics/blogger/client.py b/gdata/analytics/blogger/client.py deleted file mode 100644 index a0bad63687..0000000000 --- a/gdata/analytics/blogger/client.py +++ /dev/null @@ -1,175 +0,0 @@ -#!/usr/bin/env python -# -# Copyright (C) 2009 Google 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. - - -"""Contains a client to communicate with the Blogger servers. - -For documentation on the Blogger API, see: -http://code.google.com/apis/blogger/ -""" - - -__author__ = 'j.s@google.com (Jeff Scudder)' - - -import gdata.client -import gdata.gauth -import gdata.blogger.data -import atom.data -import atom.http_core - - -# List user's blogs, takes a user ID, or 'default'. -BLOGS_URL = 'http://www.blogger.com/feeds/%s/blogs' -# Takes a blog ID. -BLOG_POST_URL = 'http://www.blogger.com/feeds/%s/posts/default' -# Takes a blog ID. -BLOG_PAGE_URL = 'http://www.blogger.com/feeds/%s/pages/default' -# Takes a blog ID and post ID. -BLOG_POST_COMMENTS_URL = 'http://www.blogger.com/feeds/%s/%s/comments/default' -# Takes a blog ID. -BLOG_COMMENTS_URL = 'http://www.blogger.com/feeds/%s/comments/default' -# Takes a blog ID. -BLOG_ARCHIVE_URL = 'http://www.blogger.com/feeds/%s/archive/full' - - -class BloggerClient(gdata.client.GDClient): - api_version = '2' - auth_service = 'blogger' - auth_scopes = gdata.gauth.AUTH_SCOPES['blogger'] - - def get_blogs(self, user_id='default', auth_token=None, - desired_class=gdata.blogger.data.BlogFeed, **kwargs): - return self.get_feed(BLOGS_URL % user_id, auth_token=auth_token, - desired_class=desired_class, **kwargs) - - GetBlogs = get_blogs - - def get_posts(self, blog_id, auth_token=None, - desired_class=gdata.blogger.data.BlogPostFeed, query=None, - **kwargs): - return self.get_feed(BLOG_POST_URL % blog_id, auth_token=auth_token, - desired_class=desired_class, query=query, **kwargs) - - GetPosts = get_posts - - def get_pages(self, blog_id, auth_token=None, - desired_class=gdata.blogger.data.BlogPageFeed, query=None, - **kwargs): - return self.get_feed(BLOG_PAGE_URL % blog_id, auth_token=auth_token, - desired_class=desired_class, query=query, **kwargs) - - GetPages = get_pages - - def get_post_comments(self, blog_id, post_id, auth_token=None, - desired_class=gdata.blogger.data.CommentFeed, - query=None, **kwargs): - return self.get_feed(BLOG_POST_COMMENTS_URL % (blog_id, post_id), - auth_token=auth_token, desired_class=desired_class, - query=query, **kwargs) - - GetPostComments = get_post_comments - - def get_blog_comments(self, blog_id, auth_token=None, - desired_class=gdata.blogger.data.CommentFeed, - query=None, **kwargs): - return self.get_feed(BLOG_COMMENTS_URL % blog_id, auth_token=auth_token, - desired_class=desired_class, query=query, **kwargs) - - GetBlogComments = get_blog_comments - - def get_blog_archive(self, blog_id, auth_token=None, **kwargs): - return self.get_feed(BLOG_ARCHIVE_URL % blog_id, auth_token=auth_token, - **kwargs) - - GetBlogArchive = get_blog_archive - - def add_post(self, blog_id, title, body, labels=None, draft=False, - auth_token=None, title_type='text', body_type='html', **kwargs): - # Construct an atom Entry for the blog post to be sent to the server. - new_entry = gdata.blogger.data.BlogPost( - title=atom.data.Title(text=title, type=title_type), - content=atom.data.Content(text=body, type=body_type)) - if labels: - for label in labels: - new_entry.add_label(label) - if draft: - new_entry.control = atom.data.Control(draft=atom.data.Draft(text='yes')) - return self.post(new_entry, BLOG_POST_URL % blog_id, auth_token=auth_token, **kwargs) - - AddPost = add_post - - def add_page(self, blog_id, title, body, draft=False, auth_token=None, - title_type='text', body_type='html', **kwargs): - new_entry = gdata.blogger.data.BlogPage( - title=atom.data.Title(text=title, type=title_type), - content=atom.data.Content(text=body, type=body_type)) - if draft: - new_entry.control = atom.data.Control(draft=atom.data.Draft(text='yes')) - return self.post(new_entry, BLOG_PAGE_URL % blog_id, auth_token=auth_token, **kwargs) - - AddPage = add_page - - def add_comment(self, blog_id, post_id, body, auth_token=None, - title_type='text', body_type='html', **kwargs): - new_entry = gdata.blogger.data.Comment( - content=atom.data.Content(text=body, type=body_type)) - return self.post(new_entry, BLOG_POST_COMMENTS_URL % (blog_id, post_id), - auth_token=auth_token, **kwargs) - - AddComment = add_comment - - def update(self, entry, auth_token=None, **kwargs): - # The Blogger API does not currently support ETags, so for now remove - # the ETag before performing an update. - old_etag = entry.etag - entry.etag = None - response = gdata.client.GDClient.update(self, entry, - auth_token=auth_token, **kwargs) - entry.etag = old_etag - return response - - Update = update - - def delete(self, entry_or_uri, auth_token=None, **kwargs): - if isinstance(entry_or_uri, (str, unicode, atom.http_core.Uri)): - return gdata.client.GDClient.delete(self, entry_or_uri, - auth_token=auth_token, **kwargs) - # The Blogger API does not currently support ETags, so for now remove - # the ETag before performing a delete. - old_etag = entry_or_uri.etag - entry_or_uri.etag = None - response = gdata.client.GDClient.delete(self, entry_or_uri, - auth_token=auth_token, **kwargs) - # TODO: if GDClient.delete raises and exception, the entry's etag may be - # left as None. Should revisit this logic. - entry_or_uri.etag = old_etag - return response - - Delete = delete - - -class Query(gdata.client.Query): - - def __init__(self, order_by=None, **kwargs): - gdata.client.Query.__init__(self, **kwargs) - self.order_by = order_by - - def modify_request(self, http_request): - gdata.client._add_query_param('orderby', self.order_by, http_request) - gdata.client.Query.modify_request(self, http_request) - - ModifyRequest = modify_request diff --git a/gdata/analytics/blogger/data.py b/gdata/analytics/blogger/data.py deleted file mode 100644 index 3cdaa734d5..0000000000 --- a/gdata/analytics/blogger/data.py +++ /dev/null @@ -1,168 +0,0 @@ -#!/usr/bin/env python -# -# Copyright (C) 2009 Google 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. - - -"""Data model classes for parsing and generating XML for the Blogger API.""" - - -__author__ = 'j.s@google.com (Jeff Scudder)' - - -import re -import urlparse -import atom.core -import gdata.data - - -LABEL_SCHEME = 'http://www.blogger.com/atom/ns#' -THR_TEMPLATE = '{http://purl.org/syndication/thread/1.0}%s' - -BLOG_NAME_PATTERN = re.compile('(http://)(\w*)') -BLOG_ID_PATTERN = re.compile('(tag:blogger.com,1999:blog-)(\w*)') -BLOG_ID2_PATTERN = re.compile('tag:blogger.com,1999:user-(\d+)\.blog-(\d+)') -POST_ID_PATTERN = re.compile( - '(tag:blogger.com,1999:blog-)(\w*)(.post-)(\w*)') -PAGE_ID_PATTERN = re.compile( - '(tag:blogger.com,1999:blog-)(\w*)(.page-)(\w*)') -COMMENT_ID_PATTERN = re.compile('.*-(\w*)$') - - -class BloggerEntry(gdata.data.GDEntry): - """Adds convenience methods inherited by all Blogger entries.""" - - def get_blog_id(self): - """Extracts the Blogger id of this blog. - - This method is useful when contructing URLs by hand. The blog id is - often used in blogger operation URLs. This should not be confused with - the id member of a BloggerBlog. The id element is the Atom id XML element. - The blog id which this method returns is a part of the Atom id. - - Returns: - The blog's unique id as a string. - """ - if self.id.text: - match = BLOG_ID_PATTERN.match(self.id.text) - if match: - return match.group(2) - else: - return BLOG_ID2_PATTERN.match(self.id.text).group(2) - return None - - GetBlogId = get_blog_id - - def get_blog_name(self): - """Finds the name of this blog as used in the 'alternate' URL. - - An alternate URL is in the form 'http://blogName.blogspot.com/'. For an - entry representing the above example, this method would return 'blogName'. - - Returns: - The blog's URL name component as a string. - """ - for link in self.link: - if link.rel == 'alternate': - return urlparse.urlparse(link.href)[1].split(".", 1)[0] - return None - - GetBlogName = get_blog_name - - -class Blog(BloggerEntry): - """Represents a blog which belongs to the user.""" - - -class BlogFeed(gdata.data.GDFeed): - entry = [Blog] - - -class BlogPost(BloggerEntry): - """Represents a single post on a blog.""" - - def add_label(self, label): - """Adds a label to the blog post. - - The label is represented by an Atom category element, so this method - is shorthand for appending a new atom.Category object. - - Args: - label: str - """ - self.category.append(atom.data.Category(scheme=LABEL_SCHEME, term=label)) - - AddLabel = add_label - - def get_post_id(self): - """Extracts the postID string from the entry's Atom id. - - Returns: A string of digits which identify this post within the blog. - """ - if self.id.text: - return POST_ID_PATTERN.match(self.id.text).group(4) - return None - - GetPostId = get_post_id - - -class BlogPostFeed(gdata.data.GDFeed): - entry = [BlogPost] - - -class BlogPage(BloggerEntry): - """Represents a single page on a blog.""" - - def get_page_id(self): - """Extracts the pageID string from entry's Atom id. - - Returns: A string of digits which identify this post within the blog. - """ - if self.id.text: - return PAGE_ID_PATTERN.match(self.id.text).group(4) - return None - - GetPageId = get_page_id - - -class BlogPageFeed(gdata.data.GDFeed): - entry = [BlogPage] - - -class InReplyTo(atom.core.XmlElement): - _qname = THR_TEMPLATE % 'in-reply-to' - href = 'href' - ref = 'ref' - source = 'source' - type = 'type' - - -class Comment(BloggerEntry): - """Blog post comment entry in a feed listing comments on a post or blog.""" - in_reply_to = InReplyTo - - def get_comment_id(self): - """Extracts the commentID string from the entry's Atom id. - - Returns: A string of digits which identify this post within the blog. - """ - if self.id.text: - return COMMENT_ID_PATTERN.match(self.id.text).group(1) - return None - - GetCommentId = get_comment_id - - -class CommentFeed(gdata.data.GDFeed): - entry = [Comment] diff --git a/gdata/analytics/blogger/service.py b/gdata/analytics/blogger/service.py deleted file mode 100644 index ad74d6329c..0000000000 --- a/gdata/analytics/blogger/service.py +++ /dev/null @@ -1,142 +0,0 @@ -#!/usr/bin/python -# -# Copyright (C) 2007 Google 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. - -"""Classes to interact with the Blogger server.""" - -__author__ = 'api.jscudder (Jeffrey Scudder)' - -import gdata.service -import gdata.blogger - - -class BloggerService(gdata.service.GDataService): - - def __init__(self, email=None, password=None, source=None, - server='www.blogger.com', **kwargs): - """Creates a client for the Blogger 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: 'www.blogger.com'. - **kwargs: The other parameters to pass to gdata.service.GDataService - constructor. - """ - gdata.service.GDataService.__init__( - self, email=email, password=password, service='blogger', source=source, - server=server, **kwargs) - - def GetBlogFeed(self, uri=None): - """Retrieve a list of the blogs to which the current user may manage.""" - if not uri: - uri = '/feeds/default/blogs' - return self.Get(uri, converter=gdata.blogger.BlogFeedFromString) - - def GetBlogCommentFeed(self, blog_id=None, uri=None): - """Retrieve a list of the comments for this blog.""" - if blog_id: - uri = '/feeds/%s/comments/default' % blog_id - return self.Get(uri, converter=gdata.blogger.CommentFeedFromString) - - def GetBlogPostFeed(self, blog_id=None, uri=None): - if blog_id: - uri = '/feeds/%s/posts/default' % blog_id - return self.Get(uri, converter=gdata.blogger.BlogPostFeedFromString) - - def GetPostCommentFeed(self, blog_id=None, post_id=None, uri=None): - """Retrieve a list of the comments for this particular blog post.""" - if blog_id and post_id: - uri = '/feeds/%s/%s/comments/default' % (blog_id, post_id) - return self.Get(uri, converter=gdata.blogger.CommentFeedFromString) - - def AddPost(self, entry, blog_id=None, uri=None): - if blog_id: - uri = '/feeds/%s/posts/default' % blog_id - return self.Post(entry, uri, - converter=gdata.blogger.BlogPostEntryFromString) - - def UpdatePost(self, entry, uri=None): - if not uri: - uri = entry.GetEditLink().href - return self.Put(entry, uri, - converter=gdata.blogger.BlogPostEntryFromString) - - def DeletePost(self, entry=None, uri=None): - if not uri: - uri = entry.GetEditLink().href - return self.Delete(uri) - - def AddComment(self, comment_entry, blog_id=None, post_id=None, uri=None): - """Adds a new comment to the specified blog post.""" - if blog_id and post_id: - uri = '/feeds/%s/%s/comments/default' % (blog_id, post_id) - return self.Post(comment_entry, uri, - converter=gdata.blogger.CommentEntryFromString) - - def DeleteComment(self, entry=None, uri=None): - if not uri: - uri = entry.GetEditLink().href - return self.Delete(uri) - - -class BlogQuery(gdata.service.Query): - - def __init__(self, feed=None, params=None, categories=None, blog_id=None): - """Constructs a query object for the list of a user's Blogger blogs. - - Args: - feed: str (optional) The beginning of the URL to be queried. If the - feed is not set, and there is no blog_id passed in, the default - value is used ('/feeds/default/blogs'). - params: dict (optional) - categories: list (optional) - blog_id: str (optional) - """ - if not feed and blog_id: - feed = '/feeds/default/blogs/%s' % blog_id - elif not feed: - feed = '/feeds/default/blogs' - gdata.service.Query.__init__(self, feed=feed, params=params, - categories=categories) - - -class BlogPostQuery(gdata.service.Query): - - def __init__(self, feed=None, params=None, categories=None, blog_id=None, - post_id=None): - if not feed and blog_id and post_id: - feed = '/feeds/%s/posts/default/%s' % (blog_id, post_id) - elif not feed and blog_id: - feed = '/feeds/%s/posts/default' % blog_id - gdata.service.Query.__init__(self, feed=feed, params=params, - categories=categories) - - -class BlogCommentQuery(gdata.service.Query): - - def __init__(self, feed=None, params=None, categories=None, blog_id=None, - post_id=None, comment_id=None): - if not feed and blog_id and comment_id: - feed = '/feeds/%s/comments/default/%s' % (blog_id, comment_id) - elif not feed and blog_id and post_id: - feed = '/feeds/%s/%s/comments/default' % (blog_id, post_id) - elif not feed and blog_id: - feed = '/feeds/%s/comments/default' % blog_id - gdata.service.Query.__init__(self, feed=feed, params=params, - categories=categories) diff --git a/gdata/analytics/books/__init__.py b/gdata/analytics/books/__init__.py deleted file mode 100644 index 1a961ab7d2..0000000000 --- a/gdata/analytics/books/__init__.py +++ /dev/null @@ -1,473 +0,0 @@ -#!/usr/bin/python - -""" - Data Models for books.service - - All classes can be instantiated from an xml string using their FromString - class method. - - Notes: - * Book.title displays the first dc:title because the returned XML - repeats that datum as atom:title. - There is an undocumented gbs:openAccess element that is not parsed. -""" - -__author__ = "James Sams " -__copyright__ = "Apache License v2.0" - -import atom -import gdata - - -BOOK_SEARCH_NAMESPACE = 'http://schemas.google.com/books/2008' -DC_NAMESPACE = 'http://purl.org/dc/terms' -ANNOTATION_REL = "http://schemas.google.com/books/2008/annotation" -INFO_REL = "http://schemas.google.com/books/2008/info" -LABEL_SCHEME = "http://schemas.google.com/books/2008/labels" -PREVIEW_REL = "http://schemas.google.com/books/2008/preview" -THUMBNAIL_REL = "http://schemas.google.com/books/2008/thumbnail" -FULL_VIEW = "http://schemas.google.com/books/2008#view_all_pages" -PARTIAL_VIEW = "http://schemas.google.com/books/2008#view_partial" -NO_VIEW = "http://schemas.google.com/books/2008#view_no_pages" -UNKNOWN_VIEW = "http://schemas.google.com/books/2008#view_unknown" -EMBEDDABLE = "http://schemas.google.com/books/2008#embeddable" -NOT_EMBEDDABLE = "http://schemas.google.com/books/2008#not_embeddable" - - - -class _AtomFromString(atom.AtomBase): - - #@classmethod - def FromString(cls, s): - return atom.CreateClassFromXMLString(cls, s) - - FromString = classmethod(FromString) - - -class Creator(_AtomFromString): - """ - The element identifies an author-or more generally, an entity - responsible for creating the volume in question. Examples of a creator - include a person, an organization, or a service. In the case of - anthologies, proceedings, or other edited works, this field may be used to - indicate editors or other entities responsible for collecting the volume's - contents. - - This element appears as a child of . If there are multiple authors or - contributors to the book, there may be multiple elements in the - volume entry (one for each creator or contributor). - """ - - _tag = 'creator' - _namespace = DC_NAMESPACE - - -class Date(_AtomFromString): #iso 8601 / W3CDTF profile - """ - The element indicates the publication date of the specific volume - in question. If the book is a reprint, this is the reprint date, not the - original publication date. The date is encoded according to the ISO-8601 - standard (and more specifically, the W3CDTF profile). - - The element can appear only as a child of . - - Usually only the year or the year and the month are given. - - YYYY-MM-DDThh:mm:ssTZD TZD = -hh:mm or +hh:mm - """ - - _tag = 'date' - _namespace = DC_NAMESPACE - - -class Description(_AtomFromString): - """ - The element includes text that describes a book or book - result. In a search result feed, this may be a search result "snippet" that - contains the words around the user's search term. For a single volume feed, - this element may contain a synopsis of the book. - - The element can appear only as a child of - """ - - _tag = 'description' - _namespace = DC_NAMESPACE - - -class Format(_AtomFromString): - """ - The element describes the physical properties of the volume. - Currently, it indicates the number of pages in the book, but more - information may be added to this field in the future. - - This element can appear only as a child of . - """ - - _tag = 'format' - _namespace = DC_NAMESPACE - - -class Identifier(_AtomFromString): - """ - The element provides an unambiguous reference to a - particular book. - * Every contains at least one child. - * The first identifier is always the unique string Book Search has assigned - to the volume (such as s1gVAAAAYAAJ). This is the ID that appears in the - book's URL in the Book Search GUI, as well as in the URL of that book's - single item feed. - * Many books contain additional elements. These provide - alternate, external identifiers to the volume. Such identifiers may - include the ISBNs, ISSNs, Library of Congress Control Numbers (LCCNs), - and OCLC numbers; they are prepended with a corresponding namespace - prefix (such as "ISBN:"). - * Any can be passed to the Dynamic Links, used to - instantiate an Embedded Viewer, or even used to construct static links to - Book Search. - The element can appear only as a child of . - """ - - _tag = 'identifier' - _namespace = DC_NAMESPACE - - -class Publisher(_AtomFromString): - """ - The element contains the name of the entity responsible for - producing and distributing the volume (usually the specific edition of this - book). Examples of a publisher include a person, an organization, or a - service. - - This element can appear only as a child of . If there is more than - one publisher, multiple elements may appear. - """ - - _tag = 'publisher' - _namespace = DC_NAMESPACE - - -class Subject(_AtomFromString): - """ - The element identifies the topic of the book. Usually this is - a Library of Congress Subject Heading (LCSH) or Book Industry Standards - and Communications Subject Heading (BISAC). - - The element can appear only as a child of . There may - be multiple elements per entry. - """ - - _tag = 'subject' - _namespace = DC_NAMESPACE - - -class Title(_AtomFromString): - """ - The element contains the title of a book as it was published. If - a book has a subtitle, it appears as a second element in the book - result's . - """ - - _tag = 'title' - _namespace = DC_NAMESPACE - - -class Viewability(_AtomFromString): - """ - Google Book Search respects the user's local copyright restrictions. As a - result, previews or full views of some books are not available in all - locations. The element indicates whether a book is fully - viewable, can be previewed, or only has "about the book" information. These - three "viewability modes" are the same ones returned by the Dynamic Links - API. - - The element can appear only as a child of . - - The value attribute will take the form of the following URIs to represent - the relevant viewing capability: - - Full View: http://schemas.google.com/books/2008#view_all_pages - Limited Preview: http://schemas.google.com/books/2008#view_partial - Snippet View/No Preview: http://schemas.google.com/books/2008#view_no_pages - Unknown view: http://schemas.google.com/books/2008#view_unknown - """ - - _tag = 'viewability' - _namespace = BOOK_SEARCH_NAMESPACE - _attributes = atom.AtomBase._attributes.copy() - _attributes['value'] = 'value' - - def __init__(self, value=None, text=None, - extension_elements=None, extension_attributes=None): - self.value = value - _AtomFromString.__init__(self, extension_elements=extension_elements, - extension_attributes=extension_attributes, text=text) - - -class Embeddability(_AtomFromString): - """ - Many of the books found on Google Book Search can be embedded on third-party - sites using the Embedded Viewer. The element indicates - whether a particular book result is available for embedding. By definition, - a book that cannot be previewed on Book Search cannot be embedded on third- - party sites. - - The element can appear only as a child of . - - The value attribute will take on one of the following URIs: - embeddable: http://schemas.google.com/books/2008#embeddable - not embeddable: http://schemas.google.com/books/2008#not_embeddable - """ - - _tag = 'embeddability' - _namespace = BOOK_SEARCH_NAMESPACE - _attributes = atom.AtomBase._attributes.copy() - _attributes['value'] = 'value' - - def __init__(self, value=None, text=None, extension_elements=None, - extension_attributes=None): - self.value = value - _AtomFromString.__init__(self, extension_elements=extension_elements, - extension_attributes=extension_attributes, text=text) - - -class Review(_AtomFromString): - """ - When present, the element contains a user-generated review for - a given book. This element currently appears only in the user library and - user annotation feeds, as a child of . - - type: text, html, xhtml - xml:lang: id of the language, a guess, (always two letters?) - """ - - _tag = 'review' - _namespace = BOOK_SEARCH_NAMESPACE - _attributes = atom.AtomBase._attributes.copy() - _attributes['type'] = 'type' - _attributes['{http://www.w3.org/XML/1998/namespace}lang'] = 'lang' - - def __init__(self, type=None, lang=None, text=None, - extension_elements=None, extension_attributes=None): - self.type = type - self.lang = lang - _AtomFromString.__init__(self, extension_elements=extension_elements, - extension_attributes=extension_attributes, text=text) - - -class Rating(_AtomFromString): - """All attributes must take an integral string between 1 and 5. - The min, max, and average attributes represent 'community' ratings. The - value attribute is the user's (of the feed from which the item is fetched, - not necessarily the authenticated user) rating of the book. - """ - - _tag = 'rating' - _namespace = gdata.GDATA_NAMESPACE - _attributes = atom.AtomBase._attributes.copy() - _attributes['min'] = 'min' - _attributes['max'] = 'max' - _attributes['average'] = 'average' - _attributes['value'] = 'value' - - def __init__(self, min=None, max=None, average=None, value=None, text=None, - extension_elements=None, extension_attributes=None): - self.min = min - self.max = max - self.average = average - self.value = value - _AtomFromString.__init__(self, extension_elements=extension_elements, - extension_attributes=extension_attributes, text=text) - - -class Book(_AtomFromString, gdata.GDataEntry): - """ - Represents an from either a search, annotation, library, or single - item feed. Note that dc_title attribute is the proper title of the volume, - title is an atom element and may not represent the full title. - """ - - _tag = 'entry' - _namespace = atom.ATOM_NAMESPACE - _children = gdata.GDataEntry._children.copy() - for i in (Creator, Identifier, Publisher, Subject,): - _children['{%s}%s' % (i._namespace, i._tag)] = (i._tag, [i]) - for i in (Date, Description, Format, Viewability, Embeddability, - Review, Rating): # Review, Rating maybe only in anno/lib entrys - _children['{%s}%s' % (i._namespace, i._tag)] = (i._tag, i) - # there is an atom title as well, should we clobber that? - del(i) - _children['{%s}%s' % (Title._namespace, Title._tag)] = ('dc_title', [Title]) - - def to_dict(self): - """Returns a dictionary of the book's available metadata. If the data - cannot be discovered, it is not included as a key in the returned dict. - The possible keys are: authors, embeddability, date, description, - format, identifiers, publishers, rating, review, subjects, title, and - viewability. - - Notes: - * Plural keys will be lists - * Singular keys will be strings - * Title, despite usually being a list, joins the title and subtitle - with a space as a single string. - * embeddability and viewability only return the portion of the URI - after # - * identifiers is a list of tuples, where the first item of each tuple - is the type of identifier and the second item is the identifying - string. Note that while doing dict() on this tuple may be possible, - some items may have multiple of the same identifier and converting - to a dict may resulted in collisions/dropped data. - * Rating returns only the user's rating. See Rating class for precise - definition. - """ - d = {} - if self.GetAnnotationLink(): - d['annotation'] = self.GetAnnotationLink().href - if self.creator: - d['authors'] = [x.text for x in self.creator] - if self.embeddability: - d['embeddability'] = self.embeddability.value.split('#')[-1] - if self.date: - d['date'] = self.date.text - if self.description: - d['description'] = self.description.text - if self.format: - d['format'] = self.format.text - if self.identifier: - d['identifiers'] = [('google_id', self.identifier[0].text)] - for x in self.identifier[1:]: - l = x.text.split(':') # should we lower the case of the ids? - d['identifiers'].append((l[0], ':'.join(l[1:]))) - if self.GetInfoLink(): - d['info'] = self.GetInfoLink().href - if self.GetPreviewLink(): - d['preview'] = self.GetPreviewLink().href - if self.publisher: - d['publishers'] = [x.text for x in self.publisher] - if self.rating: - d['rating'] = self.rating.value - if self.review: - d['review'] = self.review.text - if self.subject: - d['subjects'] = [x.text for x in self.subject] - if self.GetThumbnailLink(): - d['thumbnail'] = self.GetThumbnailLink().href - if self.dc_title: - d['title'] = ' '.join([x.text for x in self.dc_title]) - if self.viewability: - d['viewability'] = self.viewability.value.split('#')[-1] - return d - - def __init__(self, creator=None, date=None, - description=None, format=None, author=None, identifier=None, - publisher=None, subject=None, dc_title=None, viewability=None, - embeddability=None, review=None, rating=None, category=None, - content=None, contributor=None, atom_id=None, link=None, - published=None, rights=None, source=None, summary=None, - title=None, control=None, updated=None, text=None, - extension_elements=None, extension_attributes=None): - self.creator = creator - self.date = date - self.description = description - self.format = format - self.identifier = identifier - self.publisher = publisher - self.subject = subject - self.dc_title = dc_title or [] - self.viewability = viewability - self.embeddability = embeddability - self.review = review - self.rating = rating - gdata.GDataEntry.__init__(self, author=author, category=category, - content=content, contributor=contributor, atom_id=atom_id, - link=link, published=published, rights=rights, source=source, - summary=summary, title=title, control=control, updated=updated, - text=text, extension_elements=extension_elements, - extension_attributes=extension_attributes) - - def GetThumbnailLink(self): - """Returns the atom.Link object representing the thumbnail URI.""" - for i in self.link: - if i.rel == THUMBNAIL_REL: - return i - - def GetInfoLink(self): - """ - Returns the atom.Link object representing the human-readable info URI. - """ - for i in self.link: - if i.rel == INFO_REL: - return i - - def GetPreviewLink(self): - """Returns the atom.Link object representing the preview URI.""" - for i in self.link: - if i.rel == PREVIEW_REL: - return i - - def GetAnnotationLink(self): - """ - Returns the atom.Link object representing the Annotation URI. - Note that the use of www.books in the href of this link seems to make - this information useless. Using books.service.ANNOTATION_FEED and - BOOK_SERVER to construct your URI seems to work better. - """ - for i in self.link: - if i.rel == ANNOTATION_REL: - return i - - def set_rating(self, value): - """Set user's rating. Must be an integral string between 1 nad 5""" - assert (value in ('1','2','3','4','5')) - if not isinstance(self.rating, Rating): - self.rating = Rating() - self.rating.value = value - - def set_review(self, text, type='text', lang='en'): - """Set user's review text""" - self.review = Review(text=text, type=type, lang=lang) - - def get_label(self): - """Get users label for the item as a string""" - for i in self.category: - if i.scheme == LABEL_SCHEME: - return i.term - - def set_label(self, term): - """Clear pre-existing label for the item and set term as the label.""" - self.remove_label() - self.category.append(atom.Category(term=term, scheme=LABEL_SCHEME)) - - def remove_label(self): - """Clear the user's label for the item""" - ln = len(self.category) - for i, j in enumerate(self.category[::-1]): - if j.scheme == LABEL_SCHEME: - del(self.category[ln-1-i]) - - def clean_annotations(self): - """Clear all annotations from an item. Useful for taking an item from - another user's library/annotation feed and adding it to the - authenticated user's library without adopting annotations.""" - self.remove_label() - self.review = None - self.rating = None - - - def get_google_id(self): - """Get Google's ID of the item.""" - return self.id.text.split('/')[-1] - - -class BookFeed(_AtomFromString, gdata.GDataFeed): - """Represents a feed of entries from a search.""" - - _tag = 'feed' - _namespace = atom.ATOM_NAMESPACE - _children = gdata.GDataFeed._children.copy() - _children['{%s}%s' % (Book._namespace, Book._tag)] = (Book._tag, [Book]) - - -if __name__ == '__main__': - import doctest - doctest.testfile('datamodels.txt') diff --git a/gdata/analytics/books/data.py b/gdata/analytics/books/data.py deleted file mode 100644 index 3f7f978b34..0000000000 --- a/gdata/analytics/books/data.py +++ /dev/null @@ -1,90 +0,0 @@ -#!/usr/bin/python -# -# Copyright (C) 2009 Google 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. - -"""Contains the data classes of the Google Book Search Data API""" - - -__author__ = 'j.s@google.com (Jeff Scudder)' - - -import atom.core -import atom.data -import gdata.data -import gdata.dublincore.data -import gdata.opensearch.data - - -GBS_TEMPLATE = '{http://schemas.google.com/books/2008/}%s' - - -class CollectionEntry(gdata.data.GDEntry): - """Describes an entry in a feed of collections.""" - - -class CollectionFeed(gdata.data.BatchFeed): - """Describes a Book Search collection feed.""" - entry = [CollectionEntry] - - -class Embeddability(atom.core.XmlElement): - """Describes an embeddability.""" - _qname = GBS_TEMPLATE % 'embeddability' - value = 'value' - - -class OpenAccess(atom.core.XmlElement): - """Describes an open access.""" - _qname = GBS_TEMPLATE % 'openAccess' - value = 'value' - - -class Review(atom.core.XmlElement): - """User-provided review.""" - _qname = GBS_TEMPLATE % 'review' - lang = 'lang' - type = 'type' - - -class Viewability(atom.core.XmlElement): - """Describes a viewability.""" - _qname = GBS_TEMPLATE % 'viewability' - value = 'value' - - -class VolumeEntry(gdata.data.GDEntry): - """Describes an entry in a feed of Book Search volumes.""" - comments = gdata.data.Comments - language = [gdata.dublincore.data.Language] - open_access = OpenAccess - format = [gdata.dublincore.data.Format] - dc_title = [gdata.dublincore.data.Title] - viewability = Viewability - embeddability = Embeddability - creator = [gdata.dublincore.data.Creator] - rating = gdata.data.Rating - description = [gdata.dublincore.data.Description] - publisher = [gdata.dublincore.data.Publisher] - date = [gdata.dublincore.data.Date] - subject = [gdata.dublincore.data.Subject] - identifier = [gdata.dublincore.data.Identifier] - review = Review - - -class VolumeFeed(gdata.data.BatchFeed): - """Describes a Book Search volume feed.""" - entry = [VolumeEntry] - - diff --git a/gdata/analytics/books/service.py b/gdata/analytics/books/service.py deleted file mode 100644 index cbb846fcd4..0000000000 --- a/gdata/analytics/books/service.py +++ /dev/null @@ -1,266 +0,0 @@ -#!/usr/bin/python - -""" - Extend gdata.service.GDataService to support authenticated CRUD ops on - Books API - - http://code.google.com/apis/books/docs/getting-started.html - http://code.google.com/apis/books/docs/gdata/developers_guide_protocol.html - - TODO: (here and __init__) - * search based on label, review, or other annotations (possible?) - * edit (specifically, Put requests) seem to fail effect a change - - Problems With API: - * Adding a book with a review to the library adds a note, not a review. - This does not get included in the returned item. You see this by - looking at My Library through the website. - * Editing a review never edits a review (unless it is freshly added, but - see above). More generally, - * a Put request with changed annotations (label/rating/review) does NOT - change the data. Note: Put requests only work on the href from - GetEditLink (as per the spec). Do not try to PUT to the annotate or - library feeds, this will cause a 400 Invalid URI Bad Request response. - Attempting to Post to one of the feeds with the updated annotations - does not update them. See the following for (hopefully) a follow up: - google.com/support/forum/p/booksearch-apis/thread?tid=27fd7f68de438fc8 - * Attempts to workaround the edit problem continue to fail. For example, - removing the item, editing the data, readding the item, gives us only - our originally added data (annotations). This occurs even if we - completely shut python down, refetch the book from the public feed, - and re-add it. There is some kind of persistence going on that I - cannot change. This is likely due to the annotations being cached in - the annotation feed and the inability to edit (see Put, above) - * GetAnnotationLink has www.books.... as the server, but hitting www... - results in a bad URI error. - * Spec indicates there may be multiple labels, but there does not seem - to be a way to get the server to accept multiple labels, nor does the - web interface have an obvious way to have multiple labels. Multiple - labels are never returned. -""" - -__author__ = "James Sams " -__copyright__ = "Apache License v2.0" - -from shlex import split - -import gdata.service -try: - import books -except ImportError: - import gdata.books as books - - -BOOK_SERVER = "books.google.com" -GENERAL_FEED = "/books/feeds/volumes" -ITEM_FEED = "/books/feeds/volumes/" -LIBRARY_FEED = "/books/feeds/users/%s/collections/library/volumes" -ANNOTATION_FEED = "/books/feeds/users/%s/volumes" -PARTNER_FEED = "/books/feeds/p/%s/volumes" -BOOK_SERVICE = "print" -ACCOUNT_TYPE = "HOSTED_OR_GOOGLE" - - -class BookService(gdata.service.GDataService): - - def __init__(self, email=None, password=None, source=None, - server=BOOK_SERVER, account_type=ACCOUNT_TYPE, - exception_handlers=tuple(), **kwargs): - """source should be of form 'ProgramCompany - ProgramName - Version'""" - - gdata.service.GDataService.__init__(self, email=email, - password=password, service=BOOK_SERVICE, source=source, - server=server, **kwargs) - self.exception_handlers = exception_handlers - - def search(self, q, start_index="1", max_results="10", - min_viewability="none", feed=GENERAL_FEED, - converter=books.BookFeed.FromString): - """ - Query the Public search feed. q is either a search string or a - gdata.service.Query instance with a query set. - - min_viewability must be "none", "partial", or "full". - - If you change the feed to a single item feed, note that you will - probably need to change the converter to be Book.FromString - """ - - if not isinstance(q, gdata.service.Query): - q = gdata.service.Query(text_query=q) - if feed: - q.feed = feed - q['start-index'] = start_index - q['max-results'] = max_results - q['min-viewability'] = min_viewability - return self.Get(uri=q.ToUri(),converter=converter) - - def search_by_keyword(self, q='', feed=GENERAL_FEED, start_index="1", - max_results="10", min_viewability="none", **kwargs): - """ - Query the Public Search Feed by keyword. Non-keyword strings can be - set in q. This is quite fragile. Is there a function somewhere in - the Google library that will parse a query the same way that Google - does? - - Legal Identifiers are listed below and correspond to their meaning - at http://books.google.com/advanced_book_search: - all_words - exact_phrase - at_least_one - without_words - title - author - publisher - subject - isbn - lccn - oclc - seemingly unsupported: - publication_date: a sequence of two, two tuples: - ((min_month,min_year),(max_month,max_year)) - where month is one/two digit month, year is 4 digit, eg: - (('1','2000'),('10','2003')). Lower bound is inclusive, - upper bound is exclusive - """ - - for k, v in kwargs.items(): - if not v: - continue - k = k.lower() - if k == 'all_words': - q = "%s %s" % (q, v) - elif k == 'exact_phrase': - q = '%s "%s"' % (q, v.strip('"')) - elif k == 'at_least_one': - q = '%s %s' % (q, ' '.join(['OR "%s"' % x for x in split(v)])) - elif k == 'without_words': - q = '%s %s' % (q, ' '.join(['-"%s"' % x for x in split(v)])) - elif k in ('author','title', 'publisher'): - q = '%s %s' % (q, ' '.join(['in%s:"%s"'%(k,x) for x in split(v)])) - elif k == 'subject': - q = '%s %s' % (q, ' '.join(['%s:"%s"' % (k,x) for x in split(v)])) - elif k == 'isbn': - q = '%s ISBN%s' % (q, v) - elif k == 'issn': - q = '%s ISSN%s' % (q,v) - elif k == 'oclc': - q = '%s OCLC%s' % (q,v) - else: - raise ValueError("Unsupported search keyword") - return self.search(q.strip(),start_index=start_index, feed=feed, - max_results=max_results, - min_viewability=min_viewability) - - def search_library(self, q, id='me', **kwargs): - """Like search, but in a library feed. Default is the authenticated - user's feed. Change by setting id.""" - - if 'feed' in kwargs: - raise ValueError("kwarg 'feed' conflicts with library_id") - feed = LIBRARY_FEED % id - return self.search(q, feed=feed, **kwargs) - - def search_library_by_keyword(self, id='me', **kwargs): - """Hybrid of search_by_keyword and search_library - """ - - if 'feed' in kwargs: - raise ValueError("kwarg 'feed' conflicts with library_id") - feed = LIBRARY_FEED % id - return self.search_by_keyword(feed=feed,**kwargs) - - def search_annotations(self, q, id='me', **kwargs): - """Like search, but in an annotation feed. Default is the authenticated - user's feed. Change by setting id.""" - - if 'feed' in kwargs: - raise ValueError("kwarg 'feed' conflicts with library_id") - feed = ANNOTATION_FEED % id - return self.search(q, feed=feed, **kwargs) - - def search_annotations_by_keyword(self, id='me', **kwargs): - """Hybrid of search_by_keyword and search_annotations - """ - - if 'feed' in kwargs: - raise ValueError("kwarg 'feed' conflicts with library_id") - feed = ANNOTATION_FEED % id - return self.search_by_keyword(feed=feed,**kwargs) - - def add_item_to_library(self, item): - """Add the item, either an XML string or books.Book instance, to the - user's library feed""" - - feed = LIBRARY_FEED % 'me' - return self.Post(data=item, uri=feed, converter=books.Book.FromString) - - def remove_item_from_library(self, item): - """ - Remove the item, a books.Book instance, from the authenticated user's - library feed. Using an item retrieved from a public search will fail. - """ - - return self.Delete(item.GetEditLink().href) - - def add_annotation(self, item): - """ - Add the item, either an XML string or books.Book instance, to the - user's annotation feed. - """ - # do not use GetAnnotationLink, results in 400 Bad URI due to www - return self.Post(data=item, uri=ANNOTATION_FEED % 'me', - converter=books.Book.FromString) - - def edit_annotation(self, item): - """ - Send an edited item, a books.Book instance, to the user's annotation - feed. Note that whereas extra annotations in add_annotations, minus - ratings which are immutable once set, are simply added to the item in - the annotation feed, if an annotation has been removed from the item, - sending an edit request will remove that annotation. This should not - happen with add_annotation. - """ - - return self.Put(data=item, uri=item.GetEditLink().href, - converter=books.Book.FromString) - - def get_by_google_id(self, id): - return self.Get(ITEM_FEED + id, converter=books.Book.FromString) - - def get_library(self, id='me',feed=LIBRARY_FEED, start_index="1", - max_results="100", min_viewability="none", - converter=books.BookFeed.FromString): - """ - Return a generator object that will return gbook.Book instances until - the search feed no longer returns an item from the GetNextLink method. - Thus max_results is not the maximum number of items that will be - returned, but rather the number of items per page of searches. This has - been set high to reduce the required number of network requests. - """ - - q = gdata.service.Query() - q.feed = feed % id - q['start-index'] = start_index - q['max-results'] = max_results - q['min-viewability'] = min_viewability - x = self.Get(uri=q.ToUri(), converter=converter) - while 1: - for entry in x.entry: - yield entry - else: - l = x.GetNextLink() - if l: # hope the server preserves our preferences - x = self.Get(uri=l.href, converter=converter) - else: - break - - def get_annotations(self, id='me', start_index="1", max_results="100", - min_viewability="none", converter=books.BookFeed.FromString): - """ - Like get_library, but for the annotation feed - """ - - return self.get_library(id=id, feed=ANNOTATION_FEED, - max_results=max_results, min_viewability = min_viewability, - converter=converter) diff --git a/gdata/analytics/calendar/__init__.py b/gdata/analytics/calendar/__init__.py deleted file mode 100644 index 06c041075a..0000000000 --- a/gdata/analytics/calendar/__init__.py +++ /dev/null @@ -1,1044 +0,0 @@ -#!/usr/bin/python -# -# Copyright (C) 2006 Google 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. - - -"""Contains extensions to ElementWrapper objects used with Google Calendar.""" - - -__author__ = 'api.vli (Vivian Li), api.rboyd (Ryan Boyd)' - - -try: - from xml.etree import cElementTree as ElementTree -except ImportError: - try: - import cElementTree as ElementTree - except ImportError: - try: - from xml.etree import ElementTree - except ImportError: - from elementtree import ElementTree -import atom -import gdata - - -# XML namespaces which are often used in Google Calendar entities. -GCAL_NAMESPACE = 'http://schemas.google.com/gCal/2005' -GCAL_TEMPLATE = '{http://schemas.google.com/gCal/2005}%s' -WEB_CONTENT_LINK_REL = '%s/%s' % (GCAL_NAMESPACE, 'webContent') -GACL_NAMESPACE = gdata.GACL_NAMESPACE -GACL_TEMPLATE = gdata.GACL_TEMPLATE - - - -class ValueAttributeContainer(atom.AtomBase): - """A parent class for all Calendar classes which have a value attribute. - - Children include Color, AccessLevel, Hidden - """ - - _children = atom.AtomBase._children.copy() - _attributes = atom.AtomBase._attributes.copy() - _attributes['value'] = 'value' - - def __init__(self, value=None, extension_elements=None, - extension_attributes=None, text=None): - self.value = value - self.text = text - self.extension_elements = extension_elements or [] - self.extension_attributes = extension_attributes or {} - -class Color(ValueAttributeContainer): - """The Google Calendar color element""" - - _tag = 'color' - _namespace = GCAL_NAMESPACE - _children = ValueAttributeContainer._children.copy() - _attributes = ValueAttributeContainer._attributes.copy() - - - -class AccessLevel(ValueAttributeContainer): - """The Google Calendar accesslevel element""" - - _tag = 'accesslevel' - _namespace = GCAL_NAMESPACE - _children = ValueAttributeContainer._children.copy() - _attributes = ValueAttributeContainer._attributes.copy() - - -class Hidden(ValueAttributeContainer): - """The Google Calendar hidden element""" - - _tag = 'hidden' - _namespace = GCAL_NAMESPACE - _children = ValueAttributeContainer._children.copy() - _attributes = ValueAttributeContainer._attributes.copy() - - -class Selected(ValueAttributeContainer): - """The Google Calendar selected element""" - - _tag = 'selected' - _namespace = GCAL_NAMESPACE - _children = ValueAttributeContainer._children.copy() - _attributes = ValueAttributeContainer._attributes.copy() - - -class Timezone(ValueAttributeContainer): - """The Google Calendar timezone element""" - - _tag = 'timezone' - _namespace = GCAL_NAMESPACE - _children = ValueAttributeContainer._children.copy() - _attributes = ValueAttributeContainer._attributes.copy() - - -class Where(atom.AtomBase): - """The Google Calendar Where element""" - - _tag = 'where' - _namespace = gdata.GDATA_NAMESPACE - _children = atom.AtomBase._children.copy() - _attributes = atom.AtomBase._attributes.copy() - _attributes['valueString'] = 'value_string' - - def __init__(self, value_string=None, extension_elements=None, - extension_attributes=None, text=None): - self.value_string = value_string - self.text = text - self.extension_elements = extension_elements or [] - self.extension_attributes = extension_attributes or {} - - -class CalendarListEntry(gdata.GDataEntry, gdata.LinkFinder): - """A Google Calendar meta Entry flavor of an Atom Entry """ - - _tag = gdata.GDataEntry._tag - _namespace = gdata.GDataEntry._namespace - _children = gdata.GDataEntry._children.copy() - _attributes = gdata.GDataEntry._attributes.copy() - _children['{%s}color' % GCAL_NAMESPACE] = ('color', Color) - _children['{%s}accesslevel' % GCAL_NAMESPACE] = ('access_level', - AccessLevel) - _children['{%s}hidden' % GCAL_NAMESPACE] = ('hidden', Hidden) - _children['{%s}selected' % GCAL_NAMESPACE] = ('selected', Selected) - _children['{%s}timezone' % GCAL_NAMESPACE] = ('timezone', Timezone) - _children['{%s}where' % gdata.GDATA_NAMESPACE] = ('where', Where) - - def __init__(self, author=None, category=None, content=None, - atom_id=None, link=None, published=None, - title=None, updated=None, - color=None, access_level=None, hidden=None, timezone=None, - selected=None, - where=None, - extension_elements=None, extension_attributes=None, text=None): - gdata.GDataEntry.__init__(self, author=author, category=category, - content=content, atom_id=atom_id, link=link, - published=published, title=title, - updated=updated, text=None) - - self.color = color - self.access_level = access_level - self.hidden = hidden - self.selected = selected - self.timezone = timezone - self.where = where - - -class CalendarListFeed(gdata.GDataFeed, gdata.LinkFinder): - """A Google Calendar meta feed flavor of an Atom Feed""" - - _tag = gdata.GDataFeed._tag - _namespace = gdata.GDataFeed._namespace - _children = gdata.GDataFeed._children.copy() - _attributes = gdata.GDataFeed._attributes.copy() - _children['{%s}entry' % atom.ATOM_NAMESPACE] = ('entry', [CalendarListEntry]) - - -class Scope(atom.AtomBase): - """The Google ACL scope element""" - - _tag = 'scope' - _namespace = GACL_NAMESPACE - _children = atom.AtomBase._children.copy() - _attributes = atom.AtomBase._attributes.copy() - _attributes['value'] = 'value' - _attributes['type'] = 'type' - - def __init__(self, extension_elements=None, value=None, scope_type=None, - extension_attributes=None, text=None): - self.value = value - self.type = scope_type - self.text = text - self.extension_elements = extension_elements or [] - self.extension_attributes = extension_attributes or {} - - -class Role(ValueAttributeContainer): - """The Google Calendar timezone element""" - - _tag = 'role' - _namespace = GACL_NAMESPACE - _children = ValueAttributeContainer._children.copy() - _attributes = ValueAttributeContainer._attributes.copy() - - -class CalendarAclEntry(gdata.GDataEntry, gdata.LinkFinder): - """A Google Calendar ACL Entry flavor of an Atom Entry """ - - _tag = gdata.GDataEntry._tag - _namespace = gdata.GDataEntry._namespace - _children = gdata.GDataEntry._children.copy() - _attributes = gdata.GDataEntry._attributes.copy() - _children['{%s}scope' % GACL_NAMESPACE] = ('scope', Scope) - _children['{%s}role' % GACL_NAMESPACE] = ('role', Role) - - def __init__(self, author=None, category=None, content=None, - atom_id=None, link=None, published=None, - title=None, updated=None, - scope=None, role=None, - extension_elements=None, extension_attributes=None, text=None): - gdata.GDataEntry.__init__(self, author=author, category=category, - content=content, atom_id=atom_id, link=link, - published=published, title=title, - updated=updated, text=None) - self.scope = scope - self.role = role - - -class CalendarAclFeed(gdata.GDataFeed, gdata.LinkFinder): - """A Google Calendar ACL feed flavor of an Atom Feed""" - - _tag = gdata.GDataFeed._tag - _namespace = gdata.GDataFeed._namespace - _children = gdata.GDataFeed._children.copy() - _attributes = gdata.GDataFeed._attributes.copy() - _children['{%s}entry' % atom.ATOM_NAMESPACE] = ('entry', [CalendarAclEntry]) - - -class CalendarEventCommentEntry(gdata.GDataEntry, gdata.LinkFinder): - """A Google Calendar event comments entry flavor of an Atom Entry""" - - _tag = gdata.GDataEntry._tag - _namespace = gdata.GDataEntry._namespace - _children = gdata.GDataEntry._children.copy() - _attributes = gdata.GDataEntry._attributes.copy() - - -class CalendarEventCommentFeed(gdata.GDataFeed, gdata.LinkFinder): - """A Google Calendar event comments feed flavor of an Atom Feed""" - - _tag = gdata.GDataFeed._tag - _namespace = gdata.GDataFeed._namespace - _children = gdata.GDataFeed._children.copy() - _attributes = gdata.GDataFeed._attributes.copy() - _children['{%s}entry' % atom.ATOM_NAMESPACE] = ('entry', - [CalendarEventCommentEntry]) - - -class ExtendedProperty(gdata.ExtendedProperty): - """A transparent subclass of gdata.ExtendedProperty added to this module - for backwards compatibility.""" - - -class Reminder(atom.AtomBase): - """The Google Calendar reminder element""" - - _tag = 'reminder' - _namespace = gdata.GDATA_NAMESPACE - _children = atom.AtomBase._children.copy() - _attributes = atom.AtomBase._attributes.copy() - _attributes['absoluteTime'] = 'absolute_time' - _attributes['days'] = 'days' - _attributes['hours'] = 'hours' - _attributes['minutes'] = 'minutes' - _attributes['method'] = 'method' - - def __init__(self, absolute_time=None, - days=None, hours=None, minutes=None, method=None, - extension_elements=None, - extension_attributes=None, text=None): - self.absolute_time = absolute_time - if days is not None: - self.days = str(days) - else: - self.days = None - if hours is not None: - self.hours = str(hours) - else: - self.hours = None - if minutes is not None: - self.minutes = str(minutes) - else: - self.minutes = None - self.method = method - self.text = text - self.extension_elements = extension_elements or [] - self.extension_attributes = extension_attributes or {} - - -class When(atom.AtomBase): - """The Google Calendar When element""" - - _tag = 'when' - _namespace = gdata.GDATA_NAMESPACE - _children = atom.AtomBase._children.copy() - _attributes = atom.AtomBase._attributes.copy() - _children['{%s}reminder' % gdata.GDATA_NAMESPACE] = ('reminder', [Reminder]) - _attributes['startTime'] = 'start_time' - _attributes['endTime'] = 'end_time' - - def __init__(self, start_time=None, end_time=None, reminder=None, - extension_elements=None, extension_attributes=None, text=None): - self.start_time = start_time - self.end_time = end_time - self.reminder = reminder or [] - self.text = text - self.extension_elements = extension_elements or [] - self.extension_attributes = extension_attributes or {} - - -class Recurrence(atom.AtomBase): - """The Google Calendar Recurrence element""" - - _tag = 'recurrence' - _namespace = gdata.GDATA_NAMESPACE - _children = atom.AtomBase._children.copy() - _attributes = atom.AtomBase._attributes.copy() - - -class UriEnumElement(atom.AtomBase): - - _children = atom.AtomBase._children.copy() - _attributes = atom.AtomBase._attributes.copy() - - def __init__(self, tag, enum_map, attrib_name='value', - extension_elements=None, extension_attributes=None, text=None): - self.tag=tag - self.enum_map=enum_map - self.attrib_name=attrib_name - self.value=None - self.text=text - self.extension_elements = extension_elements or [] - self.extension_attributes = extension_attributes or {} - - def findKey(self, value): - res=[item[0] for item in self.enum_map.items() if item[1] == value] - if res is None or len(res) == 0: - return None - return res[0] - - def _ConvertElementAttributeToMember(self, attribute, value): - # Special logic to use the enum_map to set the value of the object's value member. - if attribute == self.attrib_name and value != '': - self.value = self.enum_map[value] - return - # Find the attribute in this class's list of attributes. - if self.__class__._attributes.has_key(attribute): - # Find the member of this class which corresponds to the XML attribute - # (lookup in current_class._attributes) and set this member to the - # desired value (using self.__dict__). - setattr(self, self.__class__._attributes[attribute], value) - else: - # The current class doesn't map this attribute, so try to parent class. - atom.ExtensionContainer._ConvertElementAttributeToMember(self, - attribute, - value) - - def _AddMembersToElementTree(self, tree): - # Convert the members of this class which are XML child nodes. - # This uses the class's _children dictionary to find the members which - # should become XML child nodes. - member_node_names = [values[0] for tag, values in - self.__class__._children.iteritems()] - for member_name in member_node_names: - member = getattr(self, member_name) - if member is None: - pass - elif isinstance(member, list): - for instance in member: - instance._BecomeChildElement(tree) - else: - member._BecomeChildElement(tree) - # Special logic to set the desired XML attribute. - key = self.findKey(self.value) - if key is not None: - tree.attrib[self.attrib_name]=key - # Convert the members of this class which are XML attributes. - for xml_attribute, member_name in self.__class__._attributes.iteritems(): - member = getattr(self, member_name) - if member is not None: - tree.attrib[xml_attribute] = member - # Lastly, call the parent's _AddMembersToElementTree to get any - # extension elements. - atom.ExtensionContainer._AddMembersToElementTree(self, tree) - - - -class AttendeeStatus(UriEnumElement): - """The Google Calendar attendeeStatus element""" - - _tag = 'attendeeStatus' - _namespace = gdata.GDATA_NAMESPACE - _children = UriEnumElement._children.copy() - _attributes = UriEnumElement._attributes.copy() - - attendee_enum = { - 'http://schemas.google.com/g/2005#event.accepted' : 'ACCEPTED', - 'http://schemas.google.com/g/2005#event.declined' : 'DECLINED', - 'http://schemas.google.com/g/2005#event.invited' : 'INVITED', - 'http://schemas.google.com/g/2005#event.tentative' : 'TENTATIVE'} - - def __init__(self, extension_elements=None, - extension_attributes=None, text=None): - UriEnumElement.__init__(self, 'attendeeStatus', AttendeeStatus.attendee_enum, - extension_elements=extension_elements, - extension_attributes=extension_attributes, - text=text) - - -class AttendeeType(UriEnumElement): - """The Google Calendar attendeeType element""" - - _tag = 'attendeeType' - _namespace = gdata.GDATA_NAMESPACE - _children = UriEnumElement._children.copy() - _attributes = UriEnumElement._attributes.copy() - - attendee_type_enum = { - 'http://schemas.google.com/g/2005#event.optional' : 'OPTIONAL', - 'http://schemas.google.com/g/2005#event.required' : 'REQUIRED' } - - def __init__(self, extension_elements=None, - extension_attributes=None, text=None): - UriEnumElement.__init__(self, 'attendeeType', - AttendeeType.attendee_type_enum, - extension_elements=extension_elements, - extension_attributes=extension_attributes,text=text) - - -class Visibility(UriEnumElement): - """The Google Calendar Visibility element""" - - _tag = 'visibility' - _namespace = gdata.GDATA_NAMESPACE - _children = UriEnumElement._children.copy() - _attributes = UriEnumElement._attributes.copy() - - visibility_enum = { - 'http://schemas.google.com/g/2005#event.confidential' : 'CONFIDENTIAL', - 'http://schemas.google.com/g/2005#event.default' : 'DEFAULT', - 'http://schemas.google.com/g/2005#event.private' : 'PRIVATE', - 'http://schemas.google.com/g/2005#event.public' : 'PUBLIC' } - - def __init__(self, extension_elements=None, - extension_attributes=None, text=None): - UriEnumElement.__init__(self, 'visibility', Visibility.visibility_enum, - extension_elements=extension_elements, - extension_attributes=extension_attributes, - text=text) - - -class Transparency(UriEnumElement): - """The Google Calendar Transparency element""" - - _tag = 'transparency' - _namespace = gdata.GDATA_NAMESPACE - _children = UriEnumElement._children.copy() - _attributes = UriEnumElement._attributes.copy() - - transparency_enum = { - 'http://schemas.google.com/g/2005#event.opaque' : 'OPAQUE', - 'http://schemas.google.com/g/2005#event.transparent' : 'TRANSPARENT' } - - def __init__(self, extension_elements=None, - extension_attributes=None, text=None): - UriEnumElement.__init__(self, tag='transparency', - enum_map=Transparency.transparency_enum, - extension_elements=extension_elements, - extension_attributes=extension_attributes, - text=text) - - -class Comments(atom.AtomBase): - """The Google Calendar comments element""" - - _tag = 'comments' - _namespace = gdata.GDATA_NAMESPACE - _children = atom.AtomBase._children.copy() - _attributes = atom.AtomBase._attributes.copy() - _children['{%s}feedLink' % gdata.GDATA_NAMESPACE] = ('feed_link', - gdata.FeedLink) - _attributes['rel'] = 'rel' - - def __init__(self, rel=None, feed_link=None, extension_elements=None, - extension_attributes=None, text=None): - self.rel = rel - self.feed_link = feed_link - self.text = text - self.extension_elements = extension_elements or [] - self.extension_attributes = extension_attributes or {} - - -class EventStatus(UriEnumElement): - """The Google Calendar eventStatus element""" - - _tag = 'eventStatus' - _namespace = gdata.GDATA_NAMESPACE - _children = UriEnumElement._children.copy() - _attributes = UriEnumElement._attributes.copy() - - status_enum = { 'http://schemas.google.com/g/2005#event.canceled' : 'CANCELED', - 'http://schemas.google.com/g/2005#event.confirmed' : 'CONFIRMED', - 'http://schemas.google.com/g/2005#event.tentative' : 'TENTATIVE'} - - def __init__(self, extension_elements=None, - extension_attributes=None, text=None): - UriEnumElement.__init__(self, tag='eventStatus', - enum_map=EventStatus.status_enum, - extension_elements=extension_elements, - extension_attributes=extension_attributes, - text=text) - - -class Who(UriEnumElement): - """The Google Calendar Who element""" - - _tag = 'who' - _namespace = gdata.GDATA_NAMESPACE - _children = UriEnumElement._children.copy() - _attributes = UriEnumElement._attributes.copy() - _children['{%s}attendeeStatus' % gdata.GDATA_NAMESPACE] = ( - 'attendee_status', AttendeeStatus) - _children['{%s}attendeeType' % gdata.GDATA_NAMESPACE] = ('attendee_type', - AttendeeType) - _attributes['valueString'] = 'name' - _attributes['email'] = 'email' - - relEnum = { 'http://schemas.google.com/g/2005#event.attendee' : 'ATTENDEE', - 'http://schemas.google.com/g/2005#event.organizer' : 'ORGANIZER', - 'http://schemas.google.com/g/2005#event.performer' : 'PERFORMER', - 'http://schemas.google.com/g/2005#event.speaker' : 'SPEAKER', - 'http://schemas.google.com/g/2005#message.bcc' : 'BCC', - 'http://schemas.google.com/g/2005#message.cc' : 'CC', - 'http://schemas.google.com/g/2005#message.from' : 'FROM', - 'http://schemas.google.com/g/2005#message.reply-to' : 'REPLY_TO', - 'http://schemas.google.com/g/2005#message.to' : 'TO' } - - def __init__(self, name=None, email=None, attendee_status=None, - attendee_type=None, rel=None, extension_elements=None, - extension_attributes=None, text=None): - UriEnumElement.__init__(self, 'who', Who.relEnum, attrib_name='rel', - extension_elements=extension_elements, - extension_attributes=extension_attributes, - text=text) - self.name = name - self.email = email - self.attendee_status = attendee_status - self.attendee_type = attendee_type - self.rel = rel - - -class OriginalEvent(atom.AtomBase): - """The Google Calendar OriginalEvent element""" - - _tag = 'originalEvent' - _namespace = gdata.GDATA_NAMESPACE - _children = atom.AtomBase._children.copy() - _attributes = atom.AtomBase._attributes.copy() - # TODO: The when tag used to map to a EntryLink, make sure it should really be a When. - _children['{%s}when' % gdata.GDATA_NAMESPACE] = ('when', When) - _attributes['id'] = 'id' - _attributes['href'] = 'href' - - def __init__(self, id=None, href=None, when=None, - extension_elements=None, extension_attributes=None, text=None): - self.id = id - self.href = href - self.when = when - self.text = text - self.extension_elements = extension_elements or [] - self.extension_attributes = extension_attributes or {} - - -def GetCalendarEventEntryClass(): - return CalendarEventEntry - - -# This class is not completely defined here, because of a circular reference -# in which CalendarEventEntryLink and CalendarEventEntry refer to one another. -class CalendarEventEntryLink(gdata.EntryLink): - """An entryLink which contains a calendar event entry - - Within an event's recurranceExceptions, an entry link - points to a calendar event entry. This class exists - to capture the calendar specific extensions in the entry. - """ - - _tag = 'entryLink' - _namespace = gdata.GDATA_NAMESPACE - _children = gdata.EntryLink._children.copy() - _attributes = gdata.EntryLink._attributes.copy() - # The CalendarEventEntryLink should like CalendarEventEntry as a child but - # that class hasn't been defined yet, so we will wait until after defining - # CalendarEventEntry to list it in _children. - - -class RecurrenceException(atom.AtomBase): - """The Google Calendar RecurrenceException element""" - - _tag = 'recurrenceException' - _namespace = gdata.GDATA_NAMESPACE - _children = atom.AtomBase._children.copy() - _attributes = atom.AtomBase._attributes.copy() - _children['{%s}entryLink' % gdata.GDATA_NAMESPACE] = ('entry_link', - CalendarEventEntryLink) - _children['{%s}originalEvent' % gdata.GDATA_NAMESPACE] = ('original_event', - OriginalEvent) - _attributes['specialized'] = 'specialized' - - def __init__(self, specialized=None, entry_link=None, - original_event=None, extension_elements=None, - extension_attributes=None, text=None): - self.specialized = specialized - self.entry_link = entry_link - self.original_event = original_event - self.text = text - self.extension_elements = extension_elements or [] - self.extension_attributes = extension_attributes or {} - - -class SendEventNotifications(atom.AtomBase): - """The Google Calendar sendEventNotifications element""" - - _tag = 'sendEventNotifications' - _namespace = GCAL_NAMESPACE - _children = atom.AtomBase._children.copy() - _attributes = atom.AtomBase._attributes.copy() - _attributes['value'] = 'value' - - def __init__(self, extension_elements=None, - value=None, extension_attributes=None, text=None): - self.value = value - self.text = text - self.extension_elements = extension_elements or [] - self.extension_attributes = extension_attributes or {} - - -class QuickAdd(atom.AtomBase): - """The Google Calendar quickadd element""" - - _tag = 'quickadd' - _namespace = GCAL_NAMESPACE - _children = atom.AtomBase._children.copy() - _attributes = atom.AtomBase._attributes.copy() - _attributes['value'] = 'value' - - def __init__(self, extension_elements=None, - value=None, extension_attributes=None, text=None): - self.value = value - self.text = text - self.extension_elements = extension_elements or [] - self.extension_attributes = extension_attributes or {} - - def _TransferToElementTree(self, element_tree): - if self.value: - element_tree.attrib['value'] = self.value - element_tree.tag = GCAL_TEMPLATE % 'quickadd' - atom.AtomBase._TransferToElementTree(self, element_tree) - return element_tree - - def _TakeAttributeFromElementTree(self, attribute, element_tree): - if attribute == 'value': - self.value = element_tree.attrib[attribute] - del element_tree.attrib[attribute] - else: - atom.AtomBase._TakeAttributeFromElementTree(self, attribute, - element_tree) - - -class SyncEvent(atom.AtomBase): - _tag = 'syncEvent' - _namespace = GCAL_NAMESPACE - _children = atom.AtomBase._children.copy() - _attributes = atom.AtomBase._attributes.copy() - _attributes['value'] = 'value' - - def __init__(self, value='false', extension_elements=None, - extension_attributes=None, text=None): - self.value = value - self.text = text - self.extension_elements = extension_elements or [] - self.extension_attributes = extension_attributes or {} - - -class UID(atom.AtomBase): - _tag = 'uid' - _namespace = GCAL_NAMESPACE - _children = atom.AtomBase._children.copy() - _attributes = atom.AtomBase._attributes.copy() - _attributes['value'] = 'value' - - def __init__(self, value=None, extension_elements=None, - extension_attributes=None, text=None): - self.value = value - self.text = text - self.extension_elements = extension_elements or [] - self.extension_attributes = extension_attributes or {} - - -class Sequence(atom.AtomBase): - _tag = 'sequence' - _namespace = GCAL_NAMESPACE - _children = atom.AtomBase._children.copy() - _attributes = atom.AtomBase._attributes.copy() - _attributes['value'] = 'value' - - def __init__(self, value=None, extension_elements=None, - extension_attributes=None, text=None): - self.value = value - self.text = text - self.extension_elements = extension_elements or [] - self.extension_attributes = extension_attributes or {} - - -class WebContentGadgetPref(atom.AtomBase): - - _tag = 'webContentGadgetPref' - _namespace = GCAL_NAMESPACE - _children = atom.AtomBase._children.copy() - _attributes = atom.AtomBase._attributes.copy() - _attributes['name'] = 'name' - _attributes['value'] = 'value' - - """The Google Calendar Web Content Gadget Preferences element""" - - def __init__(self, name=None, value=None, extension_elements=None, - extension_attributes=None, text=None): - self.name = name - self.value = value - self.text = text - self.extension_elements = extension_elements or [] - self.extension_attributes = extension_attributes or {} - - -class WebContent(atom.AtomBase): - - _tag = 'webContent' - _namespace = GCAL_NAMESPACE - _children = atom.AtomBase._children.copy() - _attributes = atom.AtomBase._attributes.copy() - _children['{%s}webContentGadgetPref' % GCAL_NAMESPACE] = ('gadget_pref', - [WebContentGadgetPref]) - _attributes['url'] = 'url' - _attributes['width'] = 'width' - _attributes['height'] = 'height' - - def __init__(self, url=None, width=None, height=None, text=None, - gadget_pref=None, extension_elements=None, extension_attributes=None): - self.url = url - self.width = width - self.height = height - self.text = text - self.gadget_pref = gadget_pref or [] - self.extension_elements = extension_elements or [] - self.extension_attributes = extension_attributes or {} - - -class WebContentLink(atom.Link): - - _tag = 'link' - _namespace = atom.ATOM_NAMESPACE - _children = atom.Link._children.copy() - _attributes = atom.Link._attributes.copy() - _children['{%s}webContent' % GCAL_NAMESPACE] = ('web_content', WebContent) - - def __init__(self, title=None, href=None, link_type=None, - web_content=None): - atom.Link.__init__(self, rel=WEB_CONTENT_LINK_REL, title=title, href=href, - link_type=link_type) - self.web_content = web_content - - -class GuestsCanInviteOthers(atom.AtomBase): - """Indicates whether event attendees may invite others to the event. - - This element may only be changed by the organizer of the event. If not - included as part of the event entry, this element will default to true - during a POST request, and will inherit its previous value during a PUT - request. - """ - _tag = 'guestsCanInviteOthers' - _namespace = GCAL_NAMESPACE - _attributes = atom.AtomBase._attributes.copy() - _attributes['value'] = 'value' - - def __init__(self, value='true', *args, **kwargs): - atom.AtomBase.__init__(self, *args, **kwargs) - self.value = value - - -class GuestsCanSeeGuests(atom.AtomBase): - """Indicates whether attendees can see other people invited to the event. - - The organizer always sees all attendees. Guests always see themselves. This - property affects what attendees see in the event's guest list via both the - Calendar UI and API feeds. - - This element may only be changed by the organizer of the event. - - If not included as part of the event entry, this element will default to - true during a POST request, and will inherit its previous value during a - PUT request. - """ - _tag = 'guestsCanSeeGuests' - _namespace = GCAL_NAMESPACE - _attributes = atom.AtomBase._attributes.copy() - _attributes['value'] = 'value' - - def __init__(self, value='true', *args, **kwargs): - atom.AtomBase.__init__(self, *args, **kwargs) - self.value = value - - -class GuestsCanModify(atom.AtomBase): - """Indicates whether event attendees may modify the original event. - - If yes, changes are visible to organizer and other attendees. Otherwise, - any changes made by attendees will be restricted to that attendee's - calendar. - - This element may only be changed by the organizer of the event, and may - be set to 'true' only if both gCal:guestsCanInviteOthers and - gCal:guestsCanSeeGuests are set to true in the same PUT/POST request. - Otherwise, request fails with HTTP error code 400 (Bad Request). - - If not included as part of the event entry, this element will default to - false during a POST request, and will inherit its previous value during a - PUT request.""" - _tag = 'guestsCanModify' - _namespace = GCAL_NAMESPACE - _attributes = atom.AtomBase._attributes.copy() - _attributes['value'] = 'value' - - def __init__(self, value='false', *args, **kwargs): - atom.AtomBase.__init__(self, *args, **kwargs) - self.value = value - - - -class CalendarEventEntry(gdata.BatchEntry): - """A Google Calendar flavor of an Atom Entry """ - - _tag = gdata.BatchEntry._tag - _namespace = gdata.BatchEntry._namespace - _children = gdata.BatchEntry._children.copy() - _attributes = gdata.BatchEntry._attributes.copy() - # This class also contains WebContentLinks but converting those members - # is handled in a special version of _ConvertElementTreeToMember. - _children['{%s}where' % gdata.GDATA_NAMESPACE] = ('where', [Where]) - _children['{%s}when' % gdata.GDATA_NAMESPACE] = ('when', [When]) - _children['{%s}who' % gdata.GDATA_NAMESPACE] = ('who', [Who]) - _children['{%s}extendedProperty' % gdata.GDATA_NAMESPACE] = ( - 'extended_property', [ExtendedProperty]) - _children['{%s}visibility' % gdata.GDATA_NAMESPACE] = ('visibility', - Visibility) - _children['{%s}transparency' % gdata.GDATA_NAMESPACE] = ('transparency', - Transparency) - _children['{%s}eventStatus' % gdata.GDATA_NAMESPACE] = ('event_status', - EventStatus) - _children['{%s}recurrence' % gdata.GDATA_NAMESPACE] = ('recurrence', - Recurrence) - _children['{%s}recurrenceException' % gdata.GDATA_NAMESPACE] = ( - 'recurrence_exception', [RecurrenceException]) - _children['{%s}sendEventNotifications' % GCAL_NAMESPACE] = ( - 'send_event_notifications', SendEventNotifications) - _children['{%s}quickadd' % GCAL_NAMESPACE] = ('quick_add', QuickAdd) - _children['{%s}comments' % gdata.GDATA_NAMESPACE] = ('comments', Comments) - _children['{%s}originalEvent' % gdata.GDATA_NAMESPACE] = ('original_event', - OriginalEvent) - _children['{%s}sequence' % GCAL_NAMESPACE] = ('sequence', Sequence) - _children['{%s}reminder' % gdata.GDATA_NAMESPACE] = ('reminder', [Reminder]) - _children['{%s}syncEvent' % GCAL_NAMESPACE] = ('sync_event', SyncEvent) - _children['{%s}uid' % GCAL_NAMESPACE] = ('uid', UID) - _children['{%s}guestsCanInviteOthers' % GCAL_NAMESPACE] = ( - 'guests_can_invite_others', GuestsCanInviteOthers) - _children['{%s}guestsCanModify' % GCAL_NAMESPACE] = ( - 'guests_can_modify', GuestsCanModify) - _children['{%s}guestsCanSeeGuests' % GCAL_NAMESPACE] = ( - 'guests_can_see_guests', GuestsCanSeeGuests) - - def __init__(self, author=None, category=None, content=None, - atom_id=None, link=None, published=None, - title=None, updated=None, - transparency=None, comments=None, event_status=None, - send_event_notifications=None, visibility=None, - recurrence=None, recurrence_exception=None, - where=None, when=None, who=None, quick_add=None, - extended_property=None, original_event=None, - batch_operation=None, batch_id=None, batch_status=None, - sequence=None, reminder=None, sync_event=None, uid=None, - guests_can_invite_others=None, guests_can_modify=None, - guests_can_see_guests=None, - extension_elements=None, extension_attributes=None, text=None): - - gdata.BatchEntry.__init__(self, author=author, category=category, - content=content, - atom_id=atom_id, link=link, published=published, - batch_operation=batch_operation, batch_id=batch_id, - batch_status=batch_status, - title=title, updated=updated) - - self.transparency = transparency - self.comments = comments - self.event_status = event_status - self.send_event_notifications = send_event_notifications - self.visibility = visibility - self.recurrence = recurrence - self.recurrence_exception = recurrence_exception or [] - self.where = where or [] - self.when = when or [] - self.who = who or [] - self.quick_add = quick_add - self.extended_property = extended_property or [] - self.original_event = original_event - self.sequence = sequence - self.reminder = reminder or [] - self.sync_event = sync_event - self.uid = uid - self.text = text - self.guests_can_invite_others = guests_can_invite_others - self.guests_can_modify = guests_can_modify - self.guests_can_see_guests = guests_can_see_guests - self.extension_elements = extension_elements or [] - self.extension_attributes = extension_attributes or {} - - # We needed to add special logic to _ConvertElementTreeToMember because we - # want to make links with a rel of WEB_CONTENT_LINK_REL into a - # WebContentLink - def _ConvertElementTreeToMember(self, child_tree): - # Special logic to handle Web Content links - if (child_tree.tag == '{%s}link' % atom.ATOM_NAMESPACE and - child_tree.attrib['rel'] == WEB_CONTENT_LINK_REL): - if self.link is None: - self.link = [] - self.link.append(atom._CreateClassFromElementTree(WebContentLink, - child_tree)) - return - # Find the element's tag in this class's list of child members - if self.__class__._children.has_key(child_tree.tag): - member_name = self.__class__._children[child_tree.tag][0] - member_class = self.__class__._children[child_tree.tag][1] - # If the class member is supposed to contain a list, make sure the - # matching member is set to a list, then append the new member - # instance to the list. - if isinstance(member_class, list): - if getattr(self, member_name) is None: - setattr(self, member_name, []) - getattr(self, member_name).append(atom._CreateClassFromElementTree( - member_class[0], child_tree)) - else: - setattr(self, member_name, - atom._CreateClassFromElementTree(member_class, child_tree)) - else: - atom.ExtensionContainer._ConvertElementTreeToMember(self, child_tree) - - - def GetWebContentLink(self): - """Finds the first link with rel set to WEB_CONTENT_REL - - Returns: - A gdata.calendar.WebContentLink or none if none of the links had rel - equal to WEB_CONTENT_REL - """ - - for a_link in self.link: - if a_link.rel == WEB_CONTENT_LINK_REL: - return a_link - return None - - -def CalendarEventEntryFromString(xml_string): - return atom.CreateClassFromXMLString(CalendarEventEntry, xml_string) - - -def CalendarEventCommentEntryFromString(xml_string): - return atom.CreateClassFromXMLString(CalendarEventCommentEntry, xml_string) - - -CalendarEventEntryLink._children = {'{%s}entry' % atom.ATOM_NAMESPACE: - ('entry', CalendarEventEntry)} - - -def CalendarEventEntryLinkFromString(xml_string): - return atom.CreateClassFromXMLString(CalendarEventEntryLink, xml_string) - - -class CalendarEventFeed(gdata.BatchFeed, gdata.LinkFinder): - """A Google Calendar event feed flavor of an Atom Feed""" - - _tag = gdata.BatchFeed._tag - _namespace = gdata.BatchFeed._namespace - _children = gdata.BatchFeed._children.copy() - _attributes = gdata.BatchFeed._attributes.copy() - _children['{%s}entry' % atom.ATOM_NAMESPACE] = ('entry', - [CalendarEventEntry]) - _children['{%s}timezone' % GCAL_NAMESPACE] = ('timezone', Timezone) - - def __init__(self, author=None, category=None, contributor=None, - generator=None, icon=None, atom_id=None, link=None, logo=None, - rights=None, subtitle=None, title=None, updated=None, entry=None, - total_results=None, start_index=None, items_per_page=None, - interrupted=None, timezone=None, - extension_elements=None, extension_attributes=None, text=None): - gdata.BatchFeed.__init__(self, author=author, category=category, - contributor=contributor, generator=generator, - icon=icon, atom_id=atom_id, link=link, - logo=logo, rights=rights, subtitle=subtitle, - title=title, updated=updated, entry=entry, - total_results=total_results, - start_index=start_index, - items_per_page=items_per_page, - interrupted=interrupted, - extension_elements=extension_elements, - extension_attributes=extension_attributes, - text=text) - self.timezone = timezone - - -def CalendarListEntryFromString(xml_string): - return atom.CreateClassFromXMLString(CalendarListEntry, xml_string) - - -def CalendarAclEntryFromString(xml_string): - return atom.CreateClassFromXMLString(CalendarAclEntry, xml_string) - - -def CalendarListFeedFromString(xml_string): - return atom.CreateClassFromXMLString(CalendarListFeed, xml_string) - - -def CalendarAclFeedFromString(xml_string): - return atom.CreateClassFromXMLString(CalendarAclFeed, xml_string) - - -def CalendarEventFeedFromString(xml_string): - return atom.CreateClassFromXMLString(CalendarEventFeed, xml_string) - - -def CalendarEventCommentFeedFromString(xml_string): - return atom.CreateClassFromXMLString(CalendarEventCommentFeed, xml_string) diff --git a/gdata/analytics/calendar/client.py b/gdata/analytics/calendar/client.py deleted file mode 100644 index 414338d499..0000000000 --- a/gdata/analytics/calendar/client.py +++ /dev/null @@ -1,538 +0,0 @@ -#!/usr/bin/python -# -# Copyright (C) 2011 Google 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. - -"""CalendarClient extends the GDataService to streamline Google Calendar operations. - - CalendarService: Provides methods to query feeds and manipulate items. Extends - GDataService. - - DictionaryToParamList: Function which converts a dictionary into a list of - URL arguments (represented as strings). This is a - utility function used in CRUD operations. -""" - - -__author__ = 'alainv (Alain Vongsouvanh)' - - -import urllib -import gdata.client -import gdata.calendar.data -import atom.data -import atom.http_core -import gdata.gauth - - -DEFAULT_BATCH_URL = ('https://www.google.com/calendar/feeds/default/private' - '/full/batch') - - -class CalendarClient(gdata.client.GDClient): - """Client for the Google Calendar service.""" - api_version = '2' - auth_service = 'cl' - server = "www.google.com" - contact_list = "default" - auth_scopes = gdata.gauth.AUTH_SCOPES['cl'] - - def __init__(self, domain=None, auth_token=None, **kwargs): - """Constructs a new client for the Calendar API. - - Args: - domain: string The Google Apps domain (if any). - kwargs: The other parameters to pass to the gdata.client.GDClient - constructor. - """ - gdata.client.GDClient.__init__(self, auth_token=auth_token, **kwargs) - self.domain = domain - - def get_calendar_feed_uri(self, feed='', projection='full', scheme="https"): - """Builds a feed URI. - - Args: - projection: The projection to apply to the feed contents, for example - 'full', 'base', 'base/12345', 'full/batch'. Default value: 'full'. - scheme: The URL scheme such as 'http' or 'https', None to return a - relative URI without hostname. - - Returns: - A feed URI using the given scheme and projection. - Example: '/calendar/feeds/default/owncalendars/full'. - """ - prefix = scheme and '%s://%s' % (scheme, self.server) or '' - suffix = feed and '/%s/%s' % (feed, projection) or '' - return '%s/calendar/feeds/default%s' % (prefix, suffix) - - GetCalendarFeedUri = get_calendar_feed_uri - - def get_calendar_event_feed_uri(self, calendar='default', visibility='private', - projection='full', scheme="https"): - """Builds a feed URI. - - Args: - projection: The projection to apply to the feed contents, for example - 'full', 'base', 'base/12345', 'full/batch'. Default value: 'full'. - scheme: The URL scheme such as 'http' or 'https', None to return a - relative URI without hostname. - - Returns: - A feed URI using the given scheme and projection. - Example: '/calendar/feeds/default/private/full'. - """ - prefix = scheme and '%s://%s' % (scheme, self.server) or '' - return '%s/calendar/feeds/%s/%s/%s' % (prefix, calendar, - visibility, projection) - - GetCalendarEventFeedUri = get_calendar_event_feed_uri - - def get_calendars_feed(self, uri, - desired_class=gdata.calendar.data.CalendarFeed, - auth_token=None, **kwargs): - """Obtains a calendar feed. - - Args: - uri: The uri of the calendar feed to request. - desired_class: class descended from atom.core.XmlElement to which a - successful response should be converted. If there is no - converter function specified (desired_class=None) then the - desired_class will be used in calling the - atom.core.parse function. If neither - the desired_class nor the converter is specified, an - HTTP reponse object will be returned. Defaults to - gdata.calendar.data.CalendarFeed. - auth_token: An object which sets the Authorization HTTP header in its - modify_request method. Recommended classes include - gdata.gauth.ClientLoginToken and gdata.gauth.AuthSubToken - among others. Represents the current user. Defaults to None - and if None, this method will look for a value in the - auth_token member of SpreadsheetsClient. - """ - return self.get_feed(uri, auth_token=auth_token, - desired_class=desired_class, **kwargs) - - GetCalendarsFeed = get_calendars_feed - - def get_own_calendars_feed(self, - desired_class=gdata.calendar.data.CalendarFeed, - auth_token=None, **kwargs): - """Obtains a feed containing the calendars owned by the current user. - - Args: - desired_class: class descended from atom.core.XmlElement to which a - successful response should be converted. If there is no - converter function specified (desired_class=None) then the - desired_class will be used in calling the - atom.core.parse function. If neither - the desired_class nor the converter is specified, an - HTTP reponse object will be returned. Defaults to - gdata.calendar.data.CalendarFeed. - auth_token: An object which sets the Authorization HTTP header in its - modify_request method. Recommended classes include - gdata.gauth.ClientLoginToken and gdata.gauth.AuthSubToken - among others. Represents the current user. Defaults to None - and if None, this method will look for a value in the - auth_token member of SpreadsheetsClient. - """ - return self.GetCalendarsFeed(uri=self.GetCalendarFeedUri(feed='owncalendars'), - desired_class=desired_class, auth_token=auth_token, - **kwargs) - - GetOwnCalendarsFeed = get_own_calendars_feed - - def get_all_calendars_feed(self, desired_class=gdata.calendar.data.CalendarFeed, - auth_token=None, **kwargs): - """Obtains a feed containing all the ccalendars the current user has access to. - - Args: - desired_class: class descended from atom.core.XmlElement to which a - successful response should be converted. If there is no - converter function specified (desired_class=None) then the - desired_class will be used in calling the - atom.core.parse function. If neither - the desired_class nor the converter is specified, an - HTTP reponse object will be returned. Defaults to - gdata.calendar.data.CalendarFeed. - auth_token: An object which sets the Authorization HTTP header in its - modify_request method. Recommended classes include - gdata.gauth.ClientLoginToken and gdata.gauth.AuthSubToken - among others. Represents the current user. Defaults to None - and if None, this method will look for a value in the - auth_token member of SpreadsheetsClient. - """ - return self.GetCalendarsFeed(uri=self.GetCalendarFeedUri(feed='allcalendars'), - desired_class=desired_class, auth_token=auth_token, - **kwargs) - - GetAllCalendarsFeed = get_all_calendars_feed - - def get_calendar_entry(self, uri, desired_class=gdata.calendar.data.CalendarEntry, - auth_token=None, **kwargs): - """Obtains a single calendar entry. - - Args: - uri: The uri of the desired calendar entry. - desired_class: class descended from atom.core.XmlElement to which a - successful response should be converted. If there is no - converter function specified (desired_class=None) then the - desired_class will be used in calling the - atom.core.parse function. If neither - the desired_class nor the converter is specified, an - HTTP reponse object will be returned. Defaults to - gdata.calendar.data.CalendarEntry. - auth_token: An object which sets the Authorization HTTP header in its - modify_request method. Recommended classes include - gdata.gauth.ClientLoginToken and gdata.gauth.AuthSubToken - among others. Represents the current user. Defaults to None - and if None, this method will look for a value in the - auth_token member of SpreadsheetsClient. - """ - return self.get_entry(uri, auth_token=auth_token, desired_class=desired_class, - **kwargs) - - GetCalendarEntry = get_calendar_entry - - def get_calendar_event_feed(self, uri=None, - desired_class=gdata.calendar.data.CalendarEventFeed, - auth_token=None, **kwargs): - """Obtains a feed of events for the desired calendar. - - Args: - uri: The uri of the desired calendar entry. - Defaults to https://www.google.com/calendar/feeds/default/private/full. - desired_class: class descended from atom.core.XmlElement to which a - successful response should be converted. If there is no - converter function specified (desired_class=None) then the - desired_class will be used in calling the - atom.core.parse function. If neither - the desired_class nor the converter is specified, an - HTTP reponse object will be returned. Defaults to - gdata.calendar.data.CalendarEventFeed. - auth_token: An object which sets the Authorization HTTP header in its - modify_request method. Recommended classes include - gdata.gauth.ClientLoginToken and gdata.gauth.AuthSubToken - among others. Represents the current user. Defaults to None - and if None, this method will look for a value in the - auth_token member of SpreadsheetsClient. - """ - uri = uri or self.GetCalendarEventFeedUri() - return self.get_feed(uri, auth_token=auth_token, - desired_class=desired_class, **kwargs) - - GetCalendarEventFeed = get_calendar_event_feed - - def get_event_entry(self, uri, desired_class=gdata.calendar.data.CalendarEventEntry, - auth_token=None, **kwargs): - """Obtains a single event entry. - - Args: - uri: The uri of the desired calendar event entry. - desired_class: class descended from atom.core.XmlElement to which a - successful response should be converted. If there is no - converter function specified (desired_class=None) then the - desired_class will be used in calling the - atom.core.parse function. If neither - the desired_class nor the converter is specified, an - HTTP reponse object will be returned. Defaults to - gdata.calendar.data.CalendarEventEntry. - auth_token: An object which sets the Authorization HTTP header in its - modify_request method. Recommended classes include - gdata.gauth.ClientLoginToken and gdata.gauth.AuthSubToken - among others. Represents the current user. Defaults to None - and if None, this method will look for a value in the - auth_token member of SpreadsheetsClient. - """ - return self.get_entry(uri, auth_token=auth_token, desired_class=desired_class, - **kwargs) - - GetEventEntry = get_event_entry - - def get_calendar_acl_feed(self, uri='https://www.google.com/calendar/feeds/default/acl/full', - desired_class=gdata.calendar.data.CalendarAclFeed, - auth_token=None, **kwargs): - """Obtains an Access Control List feed. - - Args: - uri: The uri of the desired Acl feed. - Defaults to https://www.google.com/calendar/feeds/default/acl/full. - desired_class: class descended from atom.core.XmlElement to which a - successful response should be converted. If there is no - converter function specified (desired_class=None) then the - desired_class will be used in calling the - atom.core.parse function. If neither - the desired_class nor the converter is specified, an - HTTP reponse object will be returned. Defaults to - gdata.calendar.data.CalendarAclFeed. - auth_token: An object which sets the Authorization HTTP header in its - modify_request method. Recommended classes include - gdata.gauth.ClientLoginToken and gdata.gauth.AuthSubToken - among others. Represents the current user. Defaults to None - and if None, this method will look for a value in the - auth_token member of SpreadsheetsClient. - """ - return self.get_feed(uri, auth_token=auth_token, desired_class=desired_class, - **kwargs) - - GetCalendarAclFeed = get_calendar_acl_feed - - def get_calendar_acl_entry(self, uri, desired_class=gdata.calendar.data.CalendarAclEntry, - auth_token=None, **kwargs): - """Obtains a single Access Control List entry. - - Args: - uri: The uri of the desired Acl feed. - desired_class: class descended from atom.core.XmlElement to which a - successful response should be converted. If there is no - converter function specified (desired_class=None) then the - desired_class will be used in calling the - atom.core.parse function. If neither - the desired_class nor the converter is specified, an - HTTP reponse object will be returned. Defaults to - gdata.calendar.data.CalendarAclEntry. - auth_token: An object which sets the Authorization HTTP header in its - modify_request method. Recommended classes include - gdata.gauth.ClientLoginToken and gdata.gauth.AuthSubToken - among others. Represents the current user. Defaults to None - and if None, this method will look for a value in the - auth_token member of SpreadsheetsClient. - """ - return self.get_entry(uri, auth_token=auth_token, desired_class=desired_class, - **kwargs) - - GetCalendarAclEntry = get_calendar_acl_entry - - def insert_calendar(self, new_calendar, insert_uri=None, auth_token=None, **kwargs): - """Adds an new calendar to Google Calendar. - - Args: - new_calendar: atom.Entry or subclass A new calendar which is to be added to - Google Calendar. - insert_uri: the URL to post new contacts to the feed - url_params: dict (optional) Additional URL parameters to be included - in the insertion request. - escape_params: boolean (optional) If true, the url_parameters will be - escaped before they are included in the request. - - Returns: - On successful insert, an entry containing the contact created - On failure, a RequestError is raised of the form: - {'status': HTTP status code from server, - 'reason': HTTP reason from the server, - 'body': HTTP body of the server's response} - """ - insert_uri = insert_uri or self.GetCalendarFeedUri(feed='owncalendars') - return self.Post(new_calendar, insert_uri, - auth_token=auth_token, **kwargs) - - InsertCalendar = insert_calendar - - def insert_calendar_subscription(self, calendar, insert_uri=None, - auth_token=None, **kwargs): - """Subscribes the authenticated user to the provided calendar. - - Args: - calendar: The calendar to which the user should be subscribed. - url_params: dict (optional) Additional URL parameters to be included - in the insertion request. - escape_params: boolean (optional) If true, the url_parameters will be - escaped before they are included in the request. - - Returns: - On successful insert, an entry containing the subscription created - On failure, a RequestError is raised of the form: - {'status': HTTP status code from server, - 'reason': HTTP reason from the server, - 'body': HTTP body of the server's response} - """ - insert_uri = insert_uri or self.GetCalendarFeedUri(feed='allcalendars') - return self.Post(calendar, insert_uri, auth_token=auth_token, **kwargs) - - InsertCalendarSubscription = insert_calendar_subscription - - def insert_event(self, new_event, insert_uri=None, auth_token=None, **kwargs): - """Adds an new event to Google Calendar. - - Args: - new_event: atom.Entry or subclass A new event which is to be added to - Google Calendar. - insert_uri: the URL to post new contacts to the feed - url_params: dict (optional) Additional URL parameters to be included - in the insertion request. - escape_params: boolean (optional) If true, the url_parameters will be - escaped before they are included in the request. - - Returns: - On successful insert, an entry containing the contact created - On failure, a RequestError is raised of the form: - {'status': HTTP status code from server, - 'reason': HTTP reason from the server, - 'body': HTTP body of the server's response} - """ - insert_uri = insert_uri or self.GetCalendarEventFeedUri() - return self.Post(new_event, insert_uri, - auth_token=auth_token, **kwargs) - - - InsertEvent = insert_event - - def insert_acl_entry(self, new_acl_entry, - insert_uri = 'https://www.google.com/calendar/feeds/default/acl/full', - auth_token=None, **kwargs): - """Adds an new Acl entry to Google Calendar. - - Args: - new_acl_event: atom.Entry or subclass A new acl which is to be added to - Google Calendar. - insert_uri: the URL to post new contacts to the feed - url_params: dict (optional) Additional URL parameters to be included - in the insertion request. - escape_params: boolean (optional) If true, the url_parameters will be - escaped before they are included in the request. - - Returns: - On successful insert, an entry containing the contact created - On failure, a RequestError is raised of the form: - {'status': HTTP status code from server, - 'reason': HTTP reason from the server, - 'body': HTTP body of the server's response} - """ - return self.Post(new_acl_entry, insert_uri, auth_token=auth_token, **kwargs) - - InsertAclEntry = insert_acl_entry - - def execute_batch(self, batch_feed, url, desired_class=None): - """Sends a batch request feed to the server. - - Args: - batch_feed: gdata.contacts.CalendarEventFeed A feed containing batch - request entries. Each entry contains the operation to be performed - on the data contained in the entry. For example an entry with an - operation type of insert will be used as if the individual entry - had been inserted. - url: str The batch URL to which these operations should be applied. - converter: Function (optional) The function used to convert the server's - response to an object. - - Returns: - The results of the batch request's execution on the server. If the - default converter is used, this is stored in a ContactsFeed. - """ - return self.Post(batch_feed, url, desired_class=desired_class) - - ExecuteBatch = execute_batch - - def update(self, entry, auth_token=None, **kwargs): - """Edits the entry on the server by sending the XML for this entry. - - Performs a PUT and converts the response to a new entry object with a - matching class to the entry passed in. - - Args: - entry: - auth_token: - - Returns: - A new Entry object of a matching type to the entry which was passed in. - """ - return gdata.client.GDClient.Update(self, entry, auth_token=auth_token, - force=True, **kwargs) - - Update = update - - -class CalendarEventQuery(gdata.client.Query): - """ - Create a custom Calendar Query - - Full specs can be found at: U{Calendar query parameters reference - } - """ - - def __init__(self, feed=None, ctz=None, fields=None, futureevents=None, - max_attendees=None, orderby=None, recurrence_expansion_start=None, - recurrence_expansion_end=None, singleevents=None, showdeleted=None, - showhidden=None, sortorder=None, start_min=None, start_max=None, - updated_min=None, **kwargs): - """ - @param max_results: The maximum number of entries to return. If you want - to receive all of the contacts, rather than only the default maximum, you - can specify a very large number for max-results. - @param start-index: The 1-based index of the first result to be retrieved. - @param updated-min: The lower bound on entry update dates. - @param group: Constrains the results to only the contacts belonging to the - group specified. Value of this parameter specifies group ID - @param orderby: Sorting criterion. The only supported value is - lastmodified. - @param showdeleted: Include deleted contacts in the returned contacts feed - @pram sortorder: Sorting order direction. Can be either ascending or - descending. - @param requirealldeleted: Only relevant if showdeleted and updated-min - are also provided. It dictates the behavior of the server in case it - detects that placeholders of some entries deleted since the point in - time specified as updated-min may have been lost. - """ - gdata.client.Query.__init__(self, **kwargs) - self.ctz = ctz - self.fields = fields - self.futureevents = futureevents - self.max_attendees = max_attendees - self.orderby = orderby - self.recurrence_expansion_start = recurrence_expansion_start - self.recurrence_expansion_end = recurrence_expansion_end - self.singleevents = singleevents - self.showdeleted = showdeleted - self.showhidden = showhidden - self.sortorder = sortorder - self.start_min = start_min - self.start_max = start_max - self.updated_min = updated_min - - def modify_request(self, http_request): - if self.ctz: - gdata.client._add_query_param('ctz', self.ctz, http_request) - if self.fields: - gdata.client._add_query_param('fields', self.fields, http_request) - if self.futureevents: - gdata.client._add_query_param('futureevents', self.futureevents, http_request) - if self.max_attendees: - gdata.client._add_query_param('max-attendees', self.max_attendees, http_request) - if self.orderby: - gdata.client._add_query_param('orderby', self.orderby, http_request) - if self.recurrence_expansion_start: - gdata.client._add_query_param('recurrence-expansion-start', - self.recurrence_expansion_start, http_request) - if self.recurrence_expansion_end: - gdata.client._add_query_param('recurrence-expansion-end', - self.recurrence_expansion_end, http_request) - if self.singleevents: - gdata.client._add_query_param('singleevents', self.singleevents, http_request) - if self.showdeleted: - gdata.client._add_query_param('showdeleted', self.showdeleted, http_request) - if self.showhidden: - gdata.client._add_query_param('showhidden', self.showhidden, http_request) - if self.sortorder: - gdata.client._add_query_param('sortorder', self.sortorder, http_request) - if self.start_min: - gdata.client._add_query_param('start-min', self.start_min, http_request) - if self.start_max: - gdata.client._add_query_param('start-max', self.start_max, http_request) - if self.updated_min: - gdata.client._add_query_param('updated-min', self.updated_min, http_request) - gdata.client.Query.modify_request(self, http_request) - - ModifyRequest = modify_request - - - diff --git a/gdata/analytics/calendar/data.py b/gdata/analytics/calendar/data.py deleted file mode 100644 index 0a0235ef4a..0000000000 --- a/gdata/analytics/calendar/data.py +++ /dev/null @@ -1,327 +0,0 @@ -#!/usr/bin/python -# -# Copyright (C) 2009 Google 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. - - -"""Contains the data classes of the Google Calendar Data API""" - - -__author__ = 'j.s@google.com (Jeff Scudder)' - - -import atom.core -import atom.data -import gdata.acl.data -import gdata.data -import gdata.geo.data -import gdata.opensearch.data - - -GCAL_NAMESPACE = 'http://schemas.google.com/gCal/2005' -GCAL_TEMPLATE = '{%s}%%s' % GCAL_NAMESPACE -WEB_CONTENT_LINK_REL = '%s/%s' % (GCAL_NAMESPACE, 'webContent') - - -class AccessLevelProperty(atom.core.XmlElement): - """Describes how much a given user may do with an event or calendar""" - _qname = GCAL_TEMPLATE % 'accesslevel' - value = 'value' - - -class AllowGSync2Property(atom.core.XmlElement): - """Whether the user is permitted to run Google Apps Sync""" - _qname = GCAL_TEMPLATE % 'allowGSync2' - value = 'value' - - -class AllowGSyncProperty(atom.core.XmlElement): - """Whether the user is permitted to run Google Apps Sync""" - _qname = GCAL_TEMPLATE % 'allowGSync' - value = 'value' - - -class AnyoneCanAddSelfProperty(atom.core.XmlElement): - """Whether anyone can add self as attendee""" - _qname = GCAL_TEMPLATE % 'anyoneCanAddSelf' - value = 'value' - - -class CalendarAclRole(gdata.acl.data.AclRole): - """Describes the Calendar roles of an entry in the Calendar access control list""" - _qname = gdata.acl.data.GACL_TEMPLATE % 'role' - - -class CalendarCommentEntry(gdata.data.GDEntry): - """Describes an entry in a feed of a Calendar event's comments""" - - -class CalendarCommentFeed(gdata.data.GDFeed): - """Describes feed of a Calendar event's comments""" - entry = [CalendarCommentEntry] - - -class CalendarComments(gdata.data.Comments): - """Describes a container of a feed link for Calendar comment entries""" - _qname = gdata.data.GD_TEMPLATE % 'comments' - - -class CalendarExtendedProperty(gdata.data.ExtendedProperty): - """Defines a value for the realm attribute that is used only in the calendar API""" - _qname = gdata.data.GD_TEMPLATE % 'extendedProperty' - - -class CalendarWhere(gdata.data.Where): - """Extends the base Where class with Calendar extensions""" - _qname = gdata.data.GD_TEMPLATE % 'where' - - -class ColorProperty(atom.core.XmlElement): - """Describes the color of a calendar""" - _qname = GCAL_TEMPLATE % 'color' - value = 'value' - - -class GuestsCanInviteOthersProperty(atom.core.XmlElement): - """Whether guests can invite others to the event""" - _qname = GCAL_TEMPLATE % 'guestsCanInviteOthers' - value = 'value' - - -class GuestsCanModifyProperty(atom.core.XmlElement): - """Whether guests can modify event""" - _qname = GCAL_TEMPLATE % 'guestsCanModify' - value = 'value' - - -class GuestsCanSeeGuestsProperty(atom.core.XmlElement): - """Whether guests can see other attendees""" - _qname = GCAL_TEMPLATE % 'guestsCanSeeGuests' - value = 'value' - - -class HiddenProperty(atom.core.XmlElement): - """Describes whether a calendar is hidden""" - _qname = GCAL_TEMPLATE % 'hidden' - value = 'value' - - -class IcalUIDProperty(atom.core.XmlElement): - """Describes the UID in the ical export of the event""" - _qname = GCAL_TEMPLATE % 'uid' - value = 'value' - - -class OverrideNameProperty(atom.core.XmlElement): - """Describes the override name property of a calendar""" - _qname = GCAL_TEMPLATE % 'overridename' - value = 'value' - - -class PrivateCopyProperty(atom.core.XmlElement): - """Indicates whether this is a private copy of the event, changes to which should not be sent to other calendars""" - _qname = GCAL_TEMPLATE % 'privateCopy' - value = 'value' - - -class QuickAddProperty(atom.core.XmlElement): - """Describes whether gd:content is for quick-add processing""" - _qname = GCAL_TEMPLATE % 'quickadd' - value = 'value' - - -class ResourceProperty(atom.core.XmlElement): - """Describes whether gd:who is a resource such as a conference room""" - _qname = GCAL_TEMPLATE % 'resource' - value = 'value' - id = 'id' - - -class EventWho(gdata.data.Who): - """Extends the base Who class with Calendar extensions""" - _qname = gdata.data.GD_TEMPLATE % 'who' - resource = ResourceProperty - - -class SelectedProperty(atom.core.XmlElement): - """Describes whether a calendar is selected""" - _qname = GCAL_TEMPLATE % 'selected' - value = 'value' - - -class SendAclNotificationsProperty(atom.core.XmlElement): - """Describes whether to send ACL notifications to grantees""" - _qname = GCAL_TEMPLATE % 'sendAclNotifications' - value = 'value' - - -class CalendarAclEntry(gdata.acl.data.AclEntry): - """Describes an entry in a feed of a Calendar access control list (ACL)""" - send_acl_notifications = SendAclNotificationsProperty - - -class CalendarAclFeed(gdata.data.GDFeed): - """Describes a Calendar access contorl list (ACL) feed""" - entry = [CalendarAclEntry] - - -class SendEventNotificationsProperty(atom.core.XmlElement): - """Describes whether to send event notifications to other participants of the event""" - _qname = GCAL_TEMPLATE % 'sendEventNotifications' - value = 'value' - - -class SequenceNumberProperty(atom.core.XmlElement): - """Describes sequence number of an event""" - _qname = GCAL_TEMPLATE % 'sequence' - value = 'value' - - -class CalendarRecurrenceExceptionEntry(gdata.data.GDEntry): - """Describes an entry used by a Calendar recurrence exception entry link""" - uid = IcalUIDProperty - sequence = SequenceNumberProperty - - -class CalendarRecurrenceException(gdata.data.RecurrenceException): - """Describes an exception to a recurring Calendar event""" - _qname = gdata.data.GD_TEMPLATE % 'recurrenceException' - - -class SettingsProperty(atom.core.XmlElement): - """User preference name-value pair""" - _qname = GCAL_TEMPLATE % 'settingsProperty' - name = 'name' - value = 'value' - - -class SettingsEntry(gdata.data.GDEntry): - """Describes a Calendar Settings property entry""" - settings_property = SettingsProperty - - -class CalendarSettingsFeed(gdata.data.GDFeed): - """Personal settings for Calendar application""" - entry = [SettingsEntry] - - -class SuppressReplyNotificationsProperty(atom.core.XmlElement): - """Lists notification methods to be suppressed for this reply""" - _qname = GCAL_TEMPLATE % 'suppressReplyNotifications' - methods = 'methods' - - -class SyncEventProperty(atom.core.XmlElement): - """Describes whether this is a sync scenario where the Ical UID and Sequence number are honored during inserts and updates""" - _qname = GCAL_TEMPLATE % 'syncEvent' - value = 'value' - - -class When(gdata.data.When): - """Extends the gd:when element to add reminders""" - reminder = [gdata.data.Reminder] - - -class CalendarEventEntry(gdata.data.BatchEntry): - """Describes a Calendar event entry""" - quick_add = QuickAddProperty - send_event_notifications = SendEventNotificationsProperty - sync_event = SyncEventProperty - anyone_can_add_self = AnyoneCanAddSelfProperty - extended_property = [CalendarExtendedProperty] - sequence = SequenceNumberProperty - guests_can_invite_others = GuestsCanInviteOthersProperty - guests_can_modify = GuestsCanModifyProperty - guests_can_see_guests = GuestsCanSeeGuestsProperty - georss_where = gdata.geo.data.GeoRssWhere - private_copy = PrivateCopyProperty - suppress_reply_notifications = SuppressReplyNotificationsProperty - uid = IcalUIDProperty - where = [gdata.data.Where] - when = [When] - who = [gdata.data.Who] - transparency = gdata.data.Transparency - comments = gdata.data.Comments - event_status = gdata.data.EventStatus - visibility = gdata.data.Visibility - recurrence = gdata.data.Recurrence - recurrence_exception = [gdata.data.RecurrenceException] - original_event = gdata.data.OriginalEvent - reminder = [gdata.data.Reminder] - - -class TimeZoneProperty(atom.core.XmlElement): - """Describes the time zone of a calendar""" - _qname = GCAL_TEMPLATE % 'timezone' - value = 'value' - - -class TimesCleanedProperty(atom.core.XmlElement): - """Describes how many times calendar was cleaned via Manage Calendars""" - _qname = GCAL_TEMPLATE % 'timesCleaned' - value = 'value' - - -class CalendarEntry(gdata.data.GDEntry): - """Describes a Calendar entry in the feed of a user's calendars""" - timezone = TimeZoneProperty - overridename = OverrideNameProperty - hidden = HiddenProperty - selected = SelectedProperty - times_cleaned = TimesCleanedProperty - color = ColorProperty - where = [CalendarWhere] - accesslevel = AccessLevelProperty - - -class CalendarEventFeed(gdata.data.BatchFeed): - """Describes a Calendar event feed""" - allow_g_sync2 = AllowGSync2Property - timezone = TimeZoneProperty - entry = [CalendarEventEntry] - times_cleaned = TimesCleanedProperty - allow_g_sync = AllowGSyncProperty - - -class CalendarFeed(gdata.data.GDFeed): - """Describes a feed of Calendars""" - entry = [CalendarEntry] - - -class WebContentGadgetPref(atom.core.XmlElement): - """Describes a single web content gadget preference""" - _qname = GCAL_TEMPLATE % 'webContentGadgetPref' - name = 'name' - value = 'value' - - -class WebContent(atom.core.XmlElement): - """Describes a "web content" extension""" - _qname = GCAL_TEMPLATE % 'webContent' - height = 'height' - width = 'width' - web_content_gadget_pref = [WebContentGadgetPref] - url = 'url' - display = 'display' - - -class WebContentLink(atom.data.Link): - """Describes a "web content" link""" - def __init__(self, title=None, href=None, link_type=None, - web_content=None): - atom.data.Link.__init__(self, rel=WEB_CONTENT_LINK_REL, title=title, href=href, - link_type=link_type) - - web_content = WebContent - diff --git a/gdata/analytics/calendar/service.py b/gdata/analytics/calendar/service.py deleted file mode 100644 index 53a94e31f8..0000000000 --- a/gdata/analytics/calendar/service.py +++ /dev/null @@ -1,595 +0,0 @@ -#!/usr/bin/python -# -# Copyright (C) 2006 Google 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. - -"""CalendarService extends the GDataService to streamline Google Calendar operations. - - CalendarService: Provides methods to query feeds and manipulate items. Extends - GDataService. - - DictionaryToParamList: Function which converts a dictionary into a list of - URL arguments (represented as strings). This is a - utility function used in CRUD operations. -""" - - -__author__ = 'api.vli (Vivian Li)' - - -import urllib -import gdata -import atom.service -import gdata.service -import gdata.calendar -import atom - - -DEFAULT_BATCH_URL = ('http://www.google.com/calendar/feeds/default/private' - '/full/batch') - - -class Error(Exception): - pass - - -class RequestError(Error): - pass - - -class CalendarService(gdata.service.GDataService): - """Client for the Google Calendar service.""" - - def __init__(self, email=None, password=None, source=None, - server='www.google.com', additional_headers=None, **kwargs): - """Creates a client for the Google Calendar 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: 'www.google.com'. - **kwargs: The other parameters to pass to gdata.service.GDataService - constructor. - """ - gdata.service.GDataService.__init__( - self, email=email, password=password, service='cl', source=source, - server=server, additional_headers=additional_headers, **kwargs) - - def GetCalendarEventFeed(self, uri='/calendar/feeds/default/private/full'): - return self.Get(uri, converter=gdata.calendar.CalendarEventFeedFromString) - - def GetCalendarEventEntry(self, uri): - return self.Get(uri, converter=gdata.calendar.CalendarEventEntryFromString) - - def GetCalendarListFeed(self, uri='/calendar/feeds/default/allcalendars/full'): - return self.Get(uri, converter=gdata.calendar.CalendarListFeedFromString) - - def GetAllCalendarsFeed(self, uri='/calendar/feeds/default/allcalendars/full'): - return self.Get(uri, converter=gdata.calendar.CalendarListFeedFromString) - - def GetOwnCalendarsFeed(self, uri='/calendar/feeds/default/owncalendars/full'): - return self.Get(uri, converter=gdata.calendar.CalendarListFeedFromString) - - def GetCalendarListEntry(self, uri): - return self.Get(uri, converter=gdata.calendar.CalendarListEntryFromString) - - def GetCalendarAclFeed(self, uri='/calendar/feeds/default/acl/full'): - return self.Get(uri, converter=gdata.calendar.CalendarAclFeedFromString) - - def GetCalendarAclEntry(self, uri): - return self.Get(uri, converter=gdata.calendar.CalendarAclEntryFromString) - - def GetCalendarEventCommentFeed(self, uri): - return self.Get(uri, converter=gdata.calendar.CalendarEventCommentFeedFromString) - - def GetCalendarEventCommentEntry(self, uri): - return self.Get(uri, converter=gdata.calendar.CalendarEventCommentEntryFromString) - - def Query(self, uri, converter=None): - """Performs a query and returns a resulting feed or entry. - - Args: - feed: string The feed which is to be queried - - Returns: - On success, a GDataFeed or Entry depending on which is sent from the - server. - On failure, a RequestError is raised of the form: - {'status': HTTP status code from server, - 'reason': HTTP reason from the server, - 'body': HTTP body of the server's response} - """ - - if converter: - result = self.Get(uri, converter=converter) - else: - result = self.Get(uri) - return result - - def CalendarQuery(self, query): - if isinstance(query, CalendarEventQuery): - return self.Query(query.ToUri(), - converter=gdata.calendar.CalendarEventFeedFromString) - elif isinstance(query, CalendarListQuery): - return self.Query(query.ToUri(), - converter=gdata.calendar.CalendarListFeedFromString) - elif isinstance(query, CalendarEventCommentQuery): - return self.Query(query.ToUri(), - converter=gdata.calendar.CalendarEventCommentFeedFromString) - else: - return self.Query(query.ToUri()) - - def InsertEvent(self, new_event, insert_uri, url_params=None, - escape_params=True): - """Adds an event to Google Calendar. - - Args: - new_event: atom.Entry or subclass A new event which is to be added to - Google Calendar. - insert_uri: the URL to post new events to the feed - url_params: dict (optional) Additional URL parameters to be included - in the insertion request. - escape_params: boolean (optional) If true, the url_parameters will be - escaped before they are included in the request. - - Returns: - On successful insert, an entry containing the event created - On failure, a RequestError is raised of the form: - {'status': HTTP status code from server, - 'reason': HTTP reason from the server, - 'body': HTTP body of the server's response} - """ - - return self.Post(new_event, insert_uri, url_params=url_params, - escape_params=escape_params, - converter=gdata.calendar.CalendarEventEntryFromString) - - def InsertCalendarSubscription(self, calendar, url_params=None, - escape_params=True): - """Subscribes the authenticated user to the provided calendar. - - Args: - calendar: The calendar to which the user should be subscribed. - url_params: dict (optional) Additional URL parameters to be included - in the insertion request. - escape_params: boolean (optional) If true, the url_parameters will be - escaped before they are included in the request. - - Returns: - On successful insert, an entry containing the subscription created - On failure, a RequestError is raised of the form: - {'status': HTTP status code from server, - 'reason': HTTP reason from the server, - 'body': HTTP body of the server's response} - """ - - insert_uri = '/calendar/feeds/default/allcalendars/full' - return self.Post(calendar, insert_uri, url_params=url_params, - escape_params=escape_params, - converter=gdata.calendar.CalendarListEntryFromString) - - def InsertCalendar(self, new_calendar, url_params=None, - escape_params=True): - """Creates a new calendar. - - Args: - new_calendar: The calendar to be created - url_params: dict (optional) Additional URL parameters to be included - in the insertion request. - escape_params: boolean (optional) If true, the url_parameters will be - escaped before they are included in the request. - - Returns: - On successful insert, an entry containing the calendar created - On failure, a RequestError is raised of the form: - {'status': HTTP status code from server, - 'reason': HTTP reason from the server, - 'body': HTTP body of the server's response} - """ - - insert_uri = '/calendar/feeds/default/owncalendars/full' - response = self.Post(new_calendar, insert_uri, url_params=url_params, - escape_params=escape_params, - converter=gdata.calendar.CalendarListEntryFromString) - return response - - def UpdateCalendar(self, calendar, url_params=None, - escape_params=True): - """Updates a calendar. - - Args: - calendar: The calendar which should be updated - url_params: dict (optional) Additional URL parameters to be included - in the insertion request. - escape_params: boolean (optional) If true, the url_parameters will be - escaped before they are included in the request. - - Returns: - On successful insert, an entry containing the calendar created - On failure, a RequestError is raised of the form: - {'status': HTTP status code from server, - 'reason': HTTP reason from the server, - 'body': HTTP body of the server's response} - """ - - update_uri = calendar.GetEditLink().href - response = self.Put(data=calendar, uri=update_uri, url_params=url_params, - escape_params=escape_params, - converter=gdata.calendar.CalendarListEntryFromString) - return response - - def InsertAclEntry(self, new_entry, insert_uri, url_params=None, - escape_params=True): - """Adds an ACL entry (rule) to Google Calendar. - - Args: - new_entry: atom.Entry or subclass A new ACL entry which is to be added to - Google Calendar. - insert_uri: the URL to post new entries to the ACL feed - url_params: dict (optional) Additional URL parameters to be included - in the insertion request. - escape_params: boolean (optional) If true, the url_parameters will be - escaped before they are included in the request. - - Returns: - On successful insert, an entry containing the ACL entry created - On failure, a RequestError is raised of the form: - {'status': HTTP status code from server, - 'reason': HTTP reason from the server, - 'body': HTTP body of the server's response} - """ - - return self.Post(new_entry, insert_uri, url_params=url_params, - escape_params=escape_params, - converter=gdata.calendar.CalendarAclEntryFromString) - - def InsertEventComment(self, new_entry, insert_uri, url_params=None, - escape_params=True): - """Adds an entry to Google Calendar. - - Args: - new_entry: atom.Entry or subclass A new entry which is to be added to - Google Calendar. - insert_uri: the URL to post new entrys to the feed - url_params: dict (optional) Additional URL parameters to be included - in the insertion request. - escape_params: boolean (optional) If true, the url_parameters will be - escaped before they are included in the request. - - Returns: - On successful insert, an entry containing the comment created - On failure, a RequestError is raised of the form: - {'status': HTTP status code from server, - 'reason': HTTP reason from the server, - 'body': HTTP body of the server's response} - """ - - return self.Post(new_entry, insert_uri, url_params=url_params, - escape_params=escape_params, - converter=gdata.calendar.CalendarEventCommentEntryFromString) - - def _RemoveStandardUrlPrefix(self, url): - url_prefix = 'http://%s/' % self.server - if url.startswith(url_prefix): - return url[len(url_prefix) - 1:] - return url - - def DeleteEvent(self, edit_uri, extra_headers=None, - url_params=None, escape_params=True): - """Removes an event with the specified ID from Google Calendar. - - Args: - edit_uri: string The edit URL of the entry to be deleted. Example: - 'http://www.google.com/calendar/feeds/default/private/full/abx' - url_params: dict (optional) Additional URL parameters to be included - in the deletion request. - escape_params: boolean (optional) If true, the url_parameters will be - escaped before they are included in the request. - - Returns: - On successful delete, a httplib.HTTPResponse containing the server's - response to the DELETE request. - On failure, a RequestError is raised of the form: - {'status': HTTP status code from server, - 'reason': HTTP reason from the server, - 'body': HTTP body of the server's response} - """ - - edit_uri = self._RemoveStandardUrlPrefix(edit_uri) - return self.Delete('%s' % edit_uri, - url_params=url_params, escape_params=escape_params) - - def DeleteAclEntry(self, edit_uri, extra_headers=None, - url_params=None, escape_params=True): - """Removes an ACL entry at the given edit_uri from Google Calendar. - - Args: - edit_uri: string The edit URL of the entry to be deleted. Example: - 'http://www.google.com/calendar/feeds/liz%40gmail.com/acl/full/default' - url_params: dict (optional) Additional URL parameters to be included - in the deletion request. - escape_params: boolean (optional) If true, the url_parameters will be - escaped before they are included in the request. - - Returns: - On successful delete, a httplib.HTTPResponse containing the server's - response to the DELETE request. - On failure, a RequestError is raised of the form: - {'status': HTTP status code from server, - 'reason': HTTP reason from the server, - 'body': HTTP body of the server's response} - """ - - edit_uri = self._RemoveStandardUrlPrefix(edit_uri) - return self.Delete('%s' % edit_uri, - url_params=url_params, escape_params=escape_params) - - def DeleteCalendarEntry(self, edit_uri, extra_headers=None, - url_params=None, escape_params=True): - """Removes a calendar entry at the given edit_uri from Google Calendar. - - Args: - edit_uri: string The edit URL of the entry to be deleted. Example: - 'http://www.google.com/calendar/feeds/default/allcalendars/abcdef@group.calendar.google.com' - url_params: dict (optional) Additional URL parameters to be included - in the deletion request. - escape_params: boolean (optional) If true, the url_parameters will be - escaped before they are included in the request. - - Returns: - On successful delete, True is returned - On failure, a RequestError is raised of the form: - {'status': HTTP status code from server, - 'reason': HTTP reason from the server, - 'body': HTTP body of the server's response} - """ - - return self.Delete(edit_uri, url_params=url_params, - escape_params=escape_params) - - def UpdateEvent(self, edit_uri, updated_event, url_params=None, - escape_params=True): - """Updates an existing event. - - Args: - edit_uri: string The edit link URI for the element being updated - updated_event: string, atom.Entry, or subclass containing - the Atom Entry which will replace the event which is - stored at the edit_url - url_params: dict (optional) Additional URL parameters to be included - in the update request. - escape_params: boolean (optional) If true, the url_parameters will be - escaped before they are included in the request. - - Returns: - On successful update, a httplib.HTTPResponse containing the server's - response to the PUT request. - On failure, a RequestError is raised of the form: - {'status': HTTP status code from server, - 'reason': HTTP reason from the server, - 'body': HTTP body of the server's response} - """ - - edit_uri = self._RemoveStandardUrlPrefix(edit_uri) - return self.Put(updated_event, '%s' % edit_uri, - url_params=url_params, - escape_params=escape_params, - converter=gdata.calendar.CalendarEventEntryFromString) - - def UpdateAclEntry(self, edit_uri, updated_rule, url_params=None, - escape_params=True): - """Updates an existing ACL rule. - - Args: - edit_uri: string The edit link URI for the element being updated - updated_rule: string, atom.Entry, or subclass containing - the Atom Entry which will replace the event which is - stored at the edit_url - url_params: dict (optional) Additional URL parameters to be included - in the update request. - escape_params: boolean (optional) If true, the url_parameters will be - escaped before they are included in the request. - - Returns: - On successful update, a httplib.HTTPResponse containing the server's - response to the PUT request. - On failure, a RequestError is raised of the form: - {'status': HTTP status code from server, - 'reason': HTTP reason from the server, - 'body': HTTP body of the server's response} - """ - - edit_uri = self._RemoveStandardUrlPrefix(edit_uri) - return self.Put(updated_rule, '%s' % edit_uri, - url_params=url_params, - escape_params=escape_params, - converter=gdata.calendar.CalendarAclEntryFromString) - - def ExecuteBatch(self, batch_feed, url, - converter=gdata.calendar.CalendarEventFeedFromString): - """Sends a batch request feed to the server. - - The batch request needs to be sent to the batch URL for a particular - calendar. You can find the URL by calling GetBatchLink().href on the - CalendarEventFeed. - - Args: - batch_feed: gdata.calendar.CalendarEventFeed A feed containing batch - request entries. Each entry contains the operation to be performed - on the data contained in the entry. For example an entry with an - operation type of insert will be used as if the individual entry - had been inserted. - url: str The batch URL for the Calendar to which these operations should - be applied. - converter: Function (optional) The function used to convert the server's - response to an object. The default value is - CalendarEventFeedFromString. - - Returns: - The results of the batch request's execution on the server. If the - default converter is used, this is stored in a CalendarEventFeed. - """ - return self.Post(batch_feed, url, converter=converter) - - -class CalendarEventQuery(gdata.service.Query): - - def __init__(self, user='default', visibility='private', projection='full', - text_query=None, params=None, categories=None): - gdata.service.Query.__init__(self, - feed='http://www.google.com/calendar/feeds/%s/%s/%s' % ( - urllib.quote(user), - urllib.quote(visibility), - urllib.quote(projection)), - text_query=text_query, params=params, categories=categories) - - def _GetStartMin(self): - if 'start-min' in self.keys(): - return self['start-min'] - else: - return None - - def _SetStartMin(self, val): - self['start-min'] = val - - start_min = property(_GetStartMin, _SetStartMin, - doc="""The start-min query parameter""") - - def _GetStartMax(self): - if 'start-max' in self.keys(): - return self['start-max'] - else: - return None - - def _SetStartMax(self, val): - self['start-max'] = val - - start_max = property(_GetStartMax, _SetStartMax, - doc="""The start-max query parameter""") - - def _GetOrderBy(self): - if 'orderby' in self.keys(): - return self['orderby'] - else: - return None - - def _SetOrderBy(self, val): - if val is not 'lastmodified' and val is not 'starttime': - raise Error, "Order By must be either 'lastmodified' or 'starttime'" - self['orderby'] = val - - orderby = property(_GetOrderBy, _SetOrderBy, - doc="""The orderby query parameter""") - - def _GetSortOrder(self): - if 'sortorder' in self.keys(): - return self['sortorder'] - else: - return None - - def _SetSortOrder(self, val): - if (val is not 'ascending' and val is not 'descending' - and val is not 'a' and val is not 'd' and val is not 'ascend' - and val is not 'descend'): - raise Error, "Sort order must be either ascending, ascend, " + ( - "a or descending, descend, or d") - self['sortorder'] = val - - sortorder = property(_GetSortOrder, _SetSortOrder, - doc="""The sortorder query parameter""") - - def _GetSingleEvents(self): - if 'singleevents' in self.keys(): - return self['singleevents'] - else: - return None - - def _SetSingleEvents(self, val): - self['singleevents'] = val - - singleevents = property(_GetSingleEvents, _SetSingleEvents, - doc="""The singleevents query parameter""") - - def _GetFutureEvents(self): - if 'futureevents' in self.keys(): - return self['futureevents'] - else: - return None - - def _SetFutureEvents(self, val): - self['futureevents'] = val - - futureevents = property(_GetFutureEvents, _SetFutureEvents, - doc="""The futureevents query parameter""") - - def _GetRecurrenceExpansionStart(self): - if 'recurrence-expansion-start' in self.keys(): - return self['recurrence-expansion-start'] - else: - return None - - def _SetRecurrenceExpansionStart(self, val): - self['recurrence-expansion-start'] = val - - recurrence_expansion_start = property(_GetRecurrenceExpansionStart, - _SetRecurrenceExpansionStart, - doc="""The recurrence-expansion-start query parameter""") - - def _GetRecurrenceExpansionEnd(self): - if 'recurrence-expansion-end' in self.keys(): - return self['recurrence-expansion-end'] - else: - return None - - def _SetRecurrenceExpansionEnd(self, val): - self['recurrence-expansion-end'] = val - - recurrence_expansion_end = property(_GetRecurrenceExpansionEnd, - _SetRecurrenceExpansionEnd, - doc="""The recurrence-expansion-end query parameter""") - - def _SetTimezone(self, val): - self['ctz'] = val - - def _GetTimezone(self): - if 'ctz' in self.keys(): - return self['ctz'] - else: - return None - - ctz = property(_GetTimezone, _SetTimezone, - doc="""The ctz query parameter which sets report time on the server.""") - - -class CalendarListQuery(gdata.service.Query): - """Queries the Google Calendar meta feed""" - - def __init__(self, userId=None, text_query=None, - params=None, categories=None): - if userId is None: - userId = 'default' - - gdata.service.Query.__init__(self, feed='http://www.google.com/calendar/feeds/' - +userId, - text_query=text_query, params=params, - categories=categories) - -class CalendarEventCommentQuery(gdata.service.Query): - """Queries the Google Calendar event comments feed""" - - def __init__(self, feed=None): - gdata.service.Query.__init__(self, feed=feed) diff --git a/gdata/analytics/calendar_resource/__init__.py b/gdata/analytics/calendar_resource/__init__.py deleted file mode 100644 index 8b13789179..0000000000 --- a/gdata/analytics/calendar_resource/__init__.py +++ /dev/null @@ -1 +0,0 @@ - diff --git a/gdata/analytics/calendar_resource/client.py b/gdata/analytics/calendar_resource/client.py deleted file mode 100644 index 73ddff805f..0000000000 --- a/gdata/analytics/calendar_resource/client.py +++ /dev/null @@ -1,199 +0,0 @@ -#!/usr/bin/python -# -# Copyright 2009 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. - -"""CalendarResourceClient simplifies Calendar Resources API calls. - -CalendarResourceClient extends gdata.client.GDClient to ease interaction with -the Google Apps Calendar Resources API. These interactions include the ability -to create, retrieve, update, and delete calendar resources in a Google Apps -domain. -""" - - -__author__ = 'Vic Fryzel ' - - -import gdata.calendar_resource.data -import gdata.client -import urllib - - -# Feed URI template. This must end with a / -# The strings in this template are eventually replaced with the API version -# and Google Apps domain name, respectively. -RESOURCE_FEED_TEMPLATE = '/a/feeds/calendar/resource/%s/%s/' - - -class CalendarResourceClient(gdata.client.GDClient): - """Client extension for the Google Calendar Resource API service. - - Attributes: - host: string The hostname for the Calendar Resouce API service. - api_version: string The version of the Calendar Resource API. - """ - - host = 'apps-apis.google.com' - api_version = '2.0' - auth_service = 'apps' - auth_scopes = gdata.gauth.AUTH_SCOPES['apps'] - ssl = True - - def __init__(self, domain, auth_token=None, **kwargs): - """Constructs a new client for the Calendar Resource API. - - Args: - domain: string The Google Apps domain with Calendar Resources. - auth_token: (optional) gdata.gauth.ClientLoginToken, AuthSubToken, or - OAuthToken which authorizes this client to edit the calendar resource - data. - kwargs: The other parameters to pass to the gdata.client.GDClient - constructor. - """ - gdata.client.GDClient.__init__(self, auth_token=auth_token, **kwargs) - self.domain = domain - - def make_resource_feed_uri(self, resource_id=None, params=None): - """Creates a resource feed URI for the Calendar Resource API. - - Using this client's Google Apps domain, create a feed URI for calendar - resources in that domain. If a resource_id is provided, return a URI - for that specific resource. If params are provided, append them as GET - params. - - Args: - resource_id: string (optional) The ID of the calendar resource for which - to make a feed URI. - params: dict (optional) key -> value params to append as GET vars to the - URI. Example: params={'start': 'my-resource-id'} - Returns: - A string giving the URI for calendar resources for this client's Google - Apps domain. - """ - uri = RESOURCE_FEED_TEMPLATE % (self.api_version, self.domain) - if resource_id: - uri += resource_id - if params: - uri += '?' + urllib.urlencode(params) - return uri - - MakeResourceFeedUri = make_resource_feed_uri - - def get_resource_feed(self, uri=None, **kwargs): - """Fetches a ResourceFeed of calendar resources at the given URI. - - Args: - uri: string The URI of the feed to pull. - kwargs: The other parameters to pass to gdata.client.GDClient.get_feed(). - - Returns: - A ResourceFeed object representing the feed at the given URI. - """ - - if uri is None: - uri = self.MakeResourceFeedUri() - return self.get_feed( - uri, - desired_class=gdata.calendar_resource.data.CalendarResourceFeed, - **kwargs) - - GetResourceFeed = get_resource_feed - - def get_resource(self, uri=None, resource_id=None, **kwargs): - """Fetches a single calendar resource by resource ID. - - Args: - uri: string The base URI of the feed from which to fetch the resource. - resource_id: string The string ID of the Resource to fetch. - kwargs: The other parameters to pass to gdata.client.GDClient.get_entry(). - - Returns: - A Resource object representing the calendar resource with the given - base URI and resource ID. - """ - - if uri is None: - uri = self.MakeResourceFeedUri(resource_id) - return self.get_entry( - uri, - desired_class=gdata.calendar_resource.data.CalendarResourceEntry, - **kwargs) - - GetResource = get_resource - - def create_resource(self, resource_id, resource_common_name=None, - resource_description=None, resource_type=None, **kwargs): - """Creates a calendar resource with the given properties. - - Args: - resource_id: string The resource ID of the calendar resource. - resource_common_name: string (optional) The common name of the resource. - resource_description: string (optional) The description of the resource. - resource_type: string (optional) The type of the resource. - kwargs: The other parameters to pass to gdata.client.GDClient.post(). - - Returns: - gdata.calendar_resource.data.CalendarResourceEntry of the new resource. - """ - new_resource = gdata.calendar_resource.data.CalendarResourceEntry( - resource_id=resource_id, - resource_common_name=resource_common_name, - resource_description=resource_description, - resource_type=resource_type) - return self.post(new_resource, self.MakeResourceFeedUri(), **kwargs) - - CreateResource = create_resource - - def update_resource(self, resource_id, resource_common_name=None, - resource_description=None, resource_type=None, **kwargs): - """Updates the calendar resource with the given resource ID. - - Args: - resource_id: string The resource ID of the calendar resource to update. - resource_common_name: string (optional) The common name to give the - resource. - resource_description: string (optional) The description to give the - resource. - resource_type: string (optional) The type to give the resource. - kwargs: The other parameters to pass to gdata.client.GDClient.update(). - - Returns: - gdata.calendar_resource.data.CalendarResourceEntry of the updated - resource. - """ - new_resource = gdata.calendar_resource.data.CalendarResourceEntry( - resource_id=resource_id, - resource_common_name=resource_common_name, - resource_description=resource_description, - resource_type=resource_type) - return self.update(new_resource, uri=self.MakeResourceFeedUri(resource_id), - **kwargs) - - UpdateResource = update_resource - - def delete_resource(self, resource_id, **kwargs): - """Deletes the calendar resource with the given resource ID. - - Args: - resource_id: string The resource ID of the calendar resource to delete. - kwargs: The other parameters to pass to gdata.client.GDClient.delete() - - Returns: - An HTTP response object. See gdata.client.request(). - """ - - return self.delete(self.MakeResourceFeedUri(resource_id), **kwargs) - - DeleteResource = delete_resource diff --git a/gdata/analytics/calendar_resource/data.py b/gdata/analytics/calendar_resource/data.py deleted file mode 100644 index 82c152a6c6..0000000000 --- a/gdata/analytics/calendar_resource/data.py +++ /dev/null @@ -1,206 +0,0 @@ -#!/usr/bin/python -# -# Copyright 2009 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 for parsing and generating XML for the Calendar Resource API.""" - - -__author__ = 'Vic Fryzel ' - - -import atom.core -import atom.data -import gdata.apps -import gdata.apps_property -import gdata.data - - -# This is required to work around a naming conflict between the Google -# Spreadsheets API and Python's built-in property function -pyproperty = property - - -# The apps:property name of the resourceId property -RESOURCE_ID_NAME = 'resourceId' -# The apps:property name of the resourceCommonName property -RESOURCE_COMMON_NAME_NAME = 'resourceCommonName' -# The apps:property name of the resourceDescription property -RESOURCE_DESCRIPTION_NAME = 'resourceDescription' -# The apps:property name of the resourceType property -RESOURCE_TYPE_NAME = 'resourceType' -# The apps:property name of the resourceEmail property -RESOURCE_EMAIL_NAME = 'resourceEmail' - - -class CalendarResourceEntry(gdata.data.GDEntry): - """Represents a Calendar Resource entry in object form.""" - - property = [gdata.apps_property.AppsProperty] - - def _GetProperty(self, name): - """Get the apps:property value with the given name. - - Args: - name: string Name of the apps:property value to get. - - Returns: - The apps:property value with the given name, or None if the name was - invalid. - """ - - for p in self.property: - if p.name == name: - return p.value - return None - - def _SetProperty(self, name, value): - """Set the apps:property value with the given name to the given value. - - Args: - name: string Name of the apps:property value to set. - value: string Value to give the apps:property value with the given name. - """ - - for i in range(len(self.property)): - if self.property[i].name == name: - self.property[i].value = value - return - self.property.append(gdata.apps_property.AppsProperty(name=name, value=value)) - - def GetResourceId(self): - """Get the resource ID of this Calendar Resource object. - - Returns: - The resource ID of this Calendar Resource object as a string or None. - """ - - return self._GetProperty(RESOURCE_ID_NAME) - - def SetResourceId(self, value): - """Set the resource ID of this Calendar Resource object. - - Args: - value: string The new resource ID value to give this object. - """ - - self._SetProperty(RESOURCE_ID_NAME, value) - - resource_id = pyproperty(GetResourceId, SetResourceId) - - def GetResourceCommonName(self): - """Get the common name of this Calendar Resource object. - - Returns: - The common name of this Calendar Resource object as a string or None. - """ - - return self._GetProperty(RESOURCE_COMMON_NAME_NAME) - - def SetResourceCommonName(self, value): - """Set the common name of this Calendar Resource object. - - Args: - value: string The new common name value to give this object. - """ - - self._SetProperty(RESOURCE_COMMON_NAME_NAME, value) - - resource_common_name = pyproperty( - GetResourceCommonName, - SetResourceCommonName) - - def GetResourceDescription(self): - """Get the description of this Calendar Resource object. - - Returns: - The description of this Calendar Resource object as a string or None. - """ - - return self._GetProperty(RESOURCE_DESCRIPTION_NAME) - - def SetResourceDescription(self, value): - """Set the description of this Calendar Resource object. - - Args: - value: string The new description value to give this object. - """ - - self._SetProperty(RESOURCE_DESCRIPTION_NAME, value) - - resource_description = pyproperty( - GetResourceDescription, - SetResourceDescription) - - def GetResourceType(self): - """Get the type of this Calendar Resource object. - - Returns: - The type of this Calendar Resource object as a string or None. - """ - - return self._GetProperty(RESOURCE_TYPE_NAME) - - def SetResourceType(self, value): - """Set the type value of this Calendar Resource object. - - Args: - value: string The new type value to give this object. - """ - - self._SetProperty(RESOURCE_TYPE_NAME, value) - - resource_type = pyproperty(GetResourceType, SetResourceType) - - def GetResourceEmail(self): - """Get the email of this Calendar Resource object. - - Returns: - The email of this Calendar Resource object as a string or None. - """ - - return self._GetProperty(RESOURCE_EMAIL_NAME) - - resource_email = pyproperty(GetResourceEmail) - - def __init__(self, resource_id=None, resource_common_name=None, - resource_description=None, resource_type=None, *args, **kwargs): - """Constructs a new CalendarResourceEntry object with the given arguments. - - Args: - resource_id: string (optional) The resource ID to give this new object. - resource_common_name: string (optional) The common name to give this new - object. - resource_description: string (optional) The description to give this new - object. - resource_type: string (optional) The type to give this new object. - args: The other parameters to pass to gdata.entry.GDEntry constructor. - kwargs: The other parameters to pass to gdata.entry.GDEntry constructor. - """ - super(CalendarResourceEntry, self).__init__(*args, **kwargs) - if resource_id: - self.resource_id = resource_id - if resource_common_name: - self.resource_common_name = resource_common_name - if resource_description: - self.resource_description = resource_description - if resource_type: - self.resource_type = resource_type - - -class CalendarResourceFeed(gdata.data.GDFeed): - """Represents a feed of CalendarResourceEntry objects.""" - - # Override entry so that this feed knows how to type its list of entries. - entry = [CalendarResourceEntry] diff --git a/gdata/analytics/client.py b/gdata/analytics/client.py deleted file mode 100644 index 17ec850536..0000000000 --- a/gdata/analytics/client.py +++ /dev/null @@ -1,1163 +0,0 @@ -#!/usr/bin/env python -# -# Copyright (C) 2008, 2009 Google 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. - - -# This module is used for version 2 of the Google Data APIs. - - -"""Provides a client to interact with Google Data API servers. - -This module is used for version 2 of the Google Data APIs. The primary class -in this module is GDClient. - - GDClient: handles auth and CRUD operations when communicating with servers. - GDataClient: deprecated client for version one services. Will be removed. -""" - - -__author__ = 'j.s@google.com (Jeff Scudder)' - - -import re -import atom.client -import atom.core -import atom.http_core -import gdata.gauth -import gdata.data - - -class Error(Exception): - pass - - -class RequestError(Error): - status = None - reason = None - body = None - headers = None - - -class RedirectError(RequestError): - pass - - -class CaptchaChallenge(RequestError): - captcha_url = None - captcha_token = None - - -class ClientLoginTokenMissing(Error): - pass - - -class MissingOAuthParameters(Error): - pass - - -class ClientLoginFailed(RequestError): - pass - - -class UnableToUpgradeToken(RequestError): - pass - - -class Unauthorized(Error): - pass - - -class BadAuthenticationServiceURL(RedirectError): - pass - - -class BadAuthentication(RequestError): - pass - - -class NotModified(RequestError): - pass - -class NotImplemented(RequestError): - pass - - -def error_from_response(message, http_response, error_class, - response_body=None): - - """Creates a new exception and sets the HTTP information in the error. - - Args: - message: str human readable message to be displayed if the exception is - not caught. - http_response: The response from the server, contains error information. - error_class: The exception to be instantiated and populated with - information from the http_response - response_body: str (optional) specify if the response has already been read - from the http_response object. - """ - if response_body is None: - body = http_response.read() - else: - body = response_body - error = error_class('%s: %i, %s' % (message, http_response.status, body)) - error.status = http_response.status - error.reason = http_response.reason - error.body = body - error.headers = atom.http_core.get_headers(http_response) - return error - - -def get_xml_version(version): - """Determines which XML schema to use based on the client API version. - - Args: - version: string which is converted to an int. The version string is in - the form 'Major.Minor.x.y.z' and only the major version number - is considered. If None is provided assume version 1. - """ - if version is None: - return 1 - return int(version.split('.')[0]) - - -class GDClient(atom.client.AtomPubClient): - """Communicates with Google Data servers to perform CRUD operations. - - This class is currently experimental and may change in backwards - incompatible ways. - - This class exists to simplify the following three areas involved in using - the Google Data APIs. - - CRUD Operations: - - The client provides a generic 'request' method for making HTTP requests. - There are a number of convenience methods which are built on top of - request, which include get_feed, get_entry, get_next, post, update, and - delete. These methods contact the Google Data servers. - - Auth: - - Reading user-specific private data requires authorization from the user as - do any changes to user data. An auth_token object can be passed into any - of the HTTP requests to set the Authorization header in the request. - - You may also want to set the auth_token member to a an object which can - use modify_request to set the Authorization header in the HTTP request. - - If you are authenticating using the email address and password, you can - use the client_login method to obtain an auth token and set the - auth_token member. - - If you are using browser redirects, specifically AuthSub, you will want - to use gdata.gauth.AuthSubToken.from_url to obtain the token after the - redirect, and you will probably want to updgrade this since use token - to a multiple use (session) token using the upgrade_token method. - - API Versions: - - This client is multi-version capable and can be used with Google Data API - version 1 and version 2. The version should be specified by setting the - api_version member to a string, either '1' or '2'. - """ - - # The gsessionid is used by Google Calendar to prevent redirects. - __gsessionid = None - api_version = None - # Name of the Google Data service when making a ClientLogin request. - auth_service = None - # URL prefixes which should be requested for AuthSub and OAuth. - auth_scopes = None - # Name of alternate auth service to use in certain cases - alt_auth_service = None - - def request(self, method=None, uri=None, auth_token=None, - http_request=None, converter=None, desired_class=None, - redirects_remaining=4, **kwargs): - """Make an HTTP request to the server. - - See also documentation for atom.client.AtomPubClient.request. - - If a 302 redirect is sent from the server to the client, this client - assumes that the redirect is in the form used by the Google Calendar API. - The same request URI and method will be used as in the original request, - but a gsessionid URL parameter will be added to the request URI with - the value provided in the server's 302 redirect response. If the 302 - redirect is not in the format specified by the Google Calendar API, a - RedirectError will be raised containing the body of the server's - response. - - The method calls the client's modify_request method to make any changes - required by the client before the request is made. For example, a - version 2 client could add a GData-Version: 2 header to the request in - its modify_request method. - - Args: - method: str The HTTP verb for this request, usually 'GET', 'POST', - 'PUT', or 'DELETE' - uri: atom.http_core.Uri, str, or unicode The URL being requested. - auth_token: An object which sets the Authorization HTTP header in its - modify_request method. Recommended classes include - gdata.gauth.ClientLoginToken and gdata.gauth.AuthSubToken - among others. - http_request: (optional) atom.http_core.HttpRequest - converter: function which takes the body of the response as it's only - argument and returns the desired object. - desired_class: class descended from atom.core.XmlElement to which a - successful response should be converted. If there is no - converter function specified (converter=None) then the - desired_class will be used in calling the - atom.core.parse function. If neither - the desired_class nor the converter is specified, an - HTTP reponse object will be returned. - redirects_remaining: (optional) int, if this number is 0 and the - server sends a 302 redirect, the request method - will raise an exception. This parameter is used in - recursive request calls to avoid an infinite loop. - - Any additional arguments are passed through to - atom.client.AtomPubClient.request. - - Returns: - An HTTP response object (see atom.http_core.HttpResponse for a - description of the object's interface) if no converter was - specified and no desired_class was specified. If a converter function - was provided, the results of calling the converter are returned. If no - converter was specified but a desired_class was provided, the response - body will be converted to the class using - atom.core.parse. - """ - if isinstance(uri, (str, unicode)): - uri = atom.http_core.Uri.parse_uri(uri) - - # Add the gsession ID to the URL to prevent further redirects. - # TODO: If different sessions are using the same client, there will be a - # multitude of redirects and session ID shuffling. - # If the gsession ID is in the URL, adopt it as the standard location. - if uri is not None and uri.query is not None and 'gsessionid' in uri.query: - self.__gsessionid = uri.query['gsessionid'] - # The gsession ID could also be in the HTTP request. - elif (http_request is not None and http_request.uri is not None - and http_request.uri.query is not None - and 'gsessionid' in http_request.uri.query): - self.__gsessionid = http_request.uri.query['gsessionid'] - # If the gsession ID is stored in the client, and was not present in the - # URI then add it to the URI. - elif self.__gsessionid is not None: - uri.query['gsessionid'] = self.__gsessionid - - # The AtomPubClient should call this class' modify_request before - # performing the HTTP request. - #http_request = self.modify_request(http_request) - - response = atom.client.AtomPubClient.request(self, method=method, - uri=uri, auth_token=auth_token, http_request=http_request, **kwargs) - # On success, convert the response body using the desired converter - # function if present. - if response is None: - return None - if response.status == 200 or response.status == 201: - if converter is not None: - return converter(response) - elif desired_class is not None: - if self.api_version is not None: - return atom.core.parse(response.read(), desired_class, - version=get_xml_version(self.api_version)) - else: - # No API version was specified, so allow parse to - # use the default version. - return atom.core.parse(response.read(), desired_class) - else: - return response - # TODO: move the redirect logic into the Google Calendar client once it - # exists since the redirects are only used in the calendar API. - elif response.status == 302: - if redirects_remaining > 0: - location = (response.getheader('Location') - or response.getheader('location')) - if location is not None: - # Make a recursive call with the gsession ID in the URI to follow - # the redirect. - return self.request(method=method, uri=location, - auth_token=auth_token, http_request=http_request, - converter=converter, desired_class=desired_class, - redirects_remaining=redirects_remaining-1, - **kwargs) - else: - raise error_from_response('302 received without Location header', - response, RedirectError) - else: - raise error_from_response('Too many redirects from server', - response, RedirectError) - elif response.status == 401: - raise error_from_response('Unauthorized - Server responded with', - response, Unauthorized) - elif response.status == 304: - raise error_from_response('Entry Not Modified - Server responded with', - response, NotModified) - elif response.status == 501: - raise error_from_response( - 'This API operation is not implemented. - Server responded with', - response, NotImplemented) - # If the server's response was not a 200, 201, 302, 304, 401, or 501, raise - # an exception. - else: - raise error_from_response('Server responded with', response, - RequestError) - - Request = request - - def request_client_login_token( - self, email, password, source, service=None, - account_type='HOSTED_OR_GOOGLE', - auth_url=atom.http_core.Uri.parse_uri( - 'https://www.google.com/accounts/ClientLogin'), - captcha_token=None, captcha_response=None): - service = service or self.auth_service - # Set the target URL. - http_request = atom.http_core.HttpRequest(uri=auth_url, method='POST') - http_request.add_body_part( - gdata.gauth.generate_client_login_request_body(email=email, - password=password, service=service, source=source, - account_type=account_type, captcha_token=captcha_token, - captcha_response=captcha_response), - 'application/x-www-form-urlencoded') - - # Use the underlying http_client to make the request. - response = self.http_client.request(http_request) - - response_body = response.read() - if response.status == 200: - token_string = gdata.gauth.get_client_login_token_string(response_body) - if token_string is not None: - return gdata.gauth.ClientLoginToken(token_string) - else: - raise ClientLoginTokenMissing( - 'Recieved a 200 response to client login request,' - ' but no token was present. %s' % (response_body,)) - elif response.status == 403: - captcha_challenge = gdata.gauth.get_captcha_challenge(response_body) - if captcha_challenge: - challenge = CaptchaChallenge('CAPTCHA required') - challenge.captcha_url = captcha_challenge['url'] - challenge.captcha_token = captcha_challenge['token'] - raise challenge - elif response_body.splitlines()[0] == 'Error=BadAuthentication': - raise BadAuthentication('Incorrect username or password') - else: - raise error_from_response('Server responded with a 403 code', - response, RequestError, response_body) - elif response.status == 302: - # Google tries to redirect all bad URLs back to - # http://www.google.. If a redirect - # attempt is made, assume the user has supplied an incorrect - # authentication URL - raise error_from_response('Server responded with a redirect', - response, BadAuthenticationServiceURL, - response_body) - else: - raise error_from_response('Server responded to ClientLogin request', - response, ClientLoginFailed, response_body) - - RequestClientLoginToken = request_client_login_token - - def client_login(self, email, password, source, service=None, - account_type='HOSTED_OR_GOOGLE', - auth_url=atom.http_core.Uri.parse_uri( - 'https://www.google.com/accounts/ClientLogin'), - captcha_token=None, captcha_response=None): - """Performs an auth request using the user's email address and password. - - In order to modify user specific data and read user private data, your - application must be authorized by the user. One way to demonstrage - authorization is by including a Client Login token in the Authorization - HTTP header of all requests. This method requests the Client Login token - by sending the user's email address, password, the name of the - application, and the service code for the service which will be accessed - by the application. If the username and password are correct, the server - will respond with the client login code and a new ClientLoginToken - object will be set in the client's auth_token member. With the auth_token - set, future requests from this client will include the Client Login - token. - - For a list of service names, see - http://code.google.com/apis/gdata/faq.html#clientlogin - For more information on Client Login, see: - http://code.google.com/apis/accounts/docs/AuthForInstalledApps.html - - Args: - email: str The user's email address or username. - password: str The password for the user's account. - source: str The name of your application. This can be anything you - like but should should give some indication of which app is - making the request. - service: str The service code for the service you would like to access. - For example, 'cp' for contacts, 'cl' for calendar. For a full - list see - http://code.google.com/apis/gdata/faq.html#clientlogin - If you are using a subclass of the gdata.client.GDClient, the - service will usually be filled in for you so you do not need - to specify it. For example see BloggerClient, - SpreadsheetsClient, etc. - account_type: str (optional) The type of account which is being - authenticated. This can be either 'GOOGLE' for a Google - Account, 'HOSTED' for a Google Apps Account, or the - default 'HOSTED_OR_GOOGLE' which will select the Google - Apps Account if the same email address is used for both - a Google Account and a Google Apps Account. - auth_url: str (optional) The URL to which the login request should be - sent. - captcha_token: str (optional) If a previous login attempt was reponded - to with a CAPTCHA challenge, this is the token which - identifies the challenge (from the CAPTCHA's URL). - captcha_response: str (optional) If a previous login attempt was - reponded to with a CAPTCHA challenge, this is the - response text which was contained in the challenge. - - Returns: - Generated token, which is also stored in this object. - - Raises: - A RequestError or one of its suclasses: BadAuthentication, - BadAuthenticationServiceURL, ClientLoginFailed, - ClientLoginTokenMissing, or CaptchaChallenge - """ - service = service or self.auth_service - self.auth_token = self.request_client_login_token(email, password, - source, service=service, account_type=account_type, auth_url=auth_url, - captcha_token=captcha_token, captcha_response=captcha_response) - if self.alt_auth_service is not None: - self.alt_auth_token = self.request_client_login_token( - email, password, source, service=self.alt_auth_service, - account_type=account_type, auth_url=auth_url, - captcha_token=captcha_token, captcha_response=captcha_response) - return self.auth_token - - ClientLogin = client_login - - def upgrade_token(self, token=None, url=atom.http_core.Uri.parse_uri( - 'https://www.google.com/accounts/AuthSubSessionToken')): - """Asks the Google auth server for a multi-use AuthSub token. - - For details on AuthSub, see: - http://code.google.com/apis/accounts/docs/AuthSub.html - - Args: - token: gdata.gauth.AuthSubToken or gdata.gauth.SecureAuthSubToken - (optional) If no token is passed in, the client's auth_token member - is used to request the new token. The token object will be modified - to contain the new session token string. - url: str or atom.http_core.Uri (optional) The URL to which the token - upgrade request should be sent. Defaults to: - https://www.google.com/accounts/AuthSubSessionToken - - Returns: - The upgraded gdata.gauth.AuthSubToken object. - """ - # Default to using the auth_token member if no token is provided. - if token is None: - token = self.auth_token - # We cannot upgrade a None token. - if token is None: - raise UnableToUpgradeToken('No token was provided.') - if not isinstance(token, gdata.gauth.AuthSubToken): - raise UnableToUpgradeToken( - 'Cannot upgrade the token because it is not an AuthSubToken object.') - http_request = atom.http_core.HttpRequest(uri=url, method='GET') - token.modify_request(http_request) - # Use the lower level HttpClient to make the request. - response = self.http_client.request(http_request) - if response.status == 200: - token._upgrade_token(response.read()) - return token - else: - raise UnableToUpgradeToken( - 'Server responded to token upgrade request with %s: %s' % ( - response.status, response.read())) - - UpgradeToken = upgrade_token - - def revoke_token(self, token=None, url=atom.http_core.Uri.parse_uri( - 'https://www.google.com/accounts/AuthSubRevokeToken')): - """Requests that the token be invalidated. - - This method can be used for both AuthSub and OAuth tokens (to invalidate - a ClientLogin token, the user must change their password). - - Returns: - True if the server responded with a 200. - - Raises: - A RequestError if the server responds with a non-200 status. - """ - # Default to using the auth_token member if no token is provided. - if token is None: - token = self.auth_token - - http_request = atom.http_core.HttpRequest(uri=url, method='GET') - token.modify_request(http_request) - response = self.http_client.request(http_request) - if response.status != 200: - raise error_from_response('Server sent non-200 to revoke token', - response, RequestError, response.read()) - - return True - - RevokeToken = revoke_token - - def get_oauth_token(self, scopes, next, consumer_key, consumer_secret=None, - rsa_private_key=None, - url=gdata.gauth.REQUEST_TOKEN_URL): - """Obtains an OAuth request token to allow the user to authorize this app. - - Once this client has a request token, the user can authorize the request - token by visiting the authorization URL in their browser. After being - redirected back to this app at the 'next' URL, this app can then exchange - the authorized request token for an access token. - - For more information see the documentation on Google Accounts with OAuth: - http://code.google.com/apis/accounts/docs/OAuth.html#AuthProcess - - Args: - scopes: list of strings or atom.http_core.Uri objects which specify the - URL prefixes which this app will be accessing. For example, to access - the Google Calendar API, you would want to use scopes: - ['https://www.google.com/calendar/feeds/', - 'http://www.google.com/calendar/feeds/'] - next: str or atom.http_core.Uri object, The URL which the user's browser - should be sent to after they authorize access to their data. This - should be a URL in your application which will read the token - information from the URL and upgrade the request token to an access - token. - consumer_key: str This is the identifier for this application which you - should have received when you registered your application with Google - to use OAuth. - consumer_secret: str (optional) The shared secret between your app and - Google which provides evidence that this request is coming from you - application and not another app. If present, this libraries assumes - you want to use an HMAC signature to verify requests. Keep this data - a secret. - rsa_private_key: str (optional) The RSA private key which is used to - generate a digital signature which is checked by Google's server. If - present, this library assumes that you want to use an RSA signature - to verify requests. Keep this data a secret. - url: The URL to which a request for a token should be made. The default - is Google's OAuth request token provider. - """ - http_request = None - if rsa_private_key is not None: - http_request = gdata.gauth.generate_request_for_request_token( - consumer_key, gdata.gauth.RSA_SHA1, scopes, - rsa_key=rsa_private_key, auth_server_url=url, next=next) - elif consumer_secret is not None: - http_request = gdata.gauth.generate_request_for_request_token( - consumer_key, gdata.gauth.HMAC_SHA1, scopes, - consumer_secret=consumer_secret, auth_server_url=url, next=next) - else: - raise MissingOAuthParameters( - 'To request an OAuth token, you must provide your consumer secret' - ' or your private RSA key.') - - response = self.http_client.request(http_request) - response_body = response.read() - - if response.status != 200: - raise error_from_response('Unable to obtain OAuth request token', - response, RequestError, response_body) - - if rsa_private_key is not None: - return gdata.gauth.rsa_token_from_body(response_body, consumer_key, - rsa_private_key, - gdata.gauth.REQUEST_TOKEN) - elif consumer_secret is not None: - return gdata.gauth.hmac_token_from_body(response_body, consumer_key, - consumer_secret, - gdata.gauth.REQUEST_TOKEN) - - GetOAuthToken = get_oauth_token - - def get_access_token(self, request_token, - url=gdata.gauth.ACCESS_TOKEN_URL): - """Exchanges an authorized OAuth request token for an access token. - - Contacts the Google OAuth server to upgrade a previously authorized - request token. Once the request token is upgraded to an access token, - the access token may be used to access the user's data. - - For more details, see the Google Accounts OAuth documentation: - http://code.google.com/apis/accounts/docs/OAuth.html#AccessToken - - Args: - request_token: An OAuth token which has been authorized by the user. - url: (optional) The URL to which the upgrade request should be sent. - Defaults to: https://www.google.com/accounts/OAuthAuthorizeToken - """ - http_request = gdata.gauth.generate_request_for_access_token( - request_token, auth_server_url=url) - response = self.http_client.request(http_request) - response_body = response.read() - if response.status != 200: - raise error_from_response( - 'Unable to upgrade OAuth request token to access token', - response, RequestError, response_body) - - return gdata.gauth.upgrade_to_access_token(request_token, response_body) - - GetAccessToken = get_access_token - - def modify_request(self, http_request): - """Adds or changes request before making the HTTP request. - - This client will add the API version if it is specified. - Subclasses may override this method to add their own request - modifications before the request is made. - """ - http_request = atom.client.AtomPubClient.modify_request(self, - http_request) - if self.api_version is not None: - http_request.headers['GData-Version'] = self.api_version - return http_request - - ModifyRequest = modify_request - - def get_feed(self, uri, auth_token=None, converter=None, - desired_class=gdata.data.GDFeed, **kwargs): - return self.request(method='GET', uri=uri, auth_token=auth_token, - converter=converter, desired_class=desired_class, - **kwargs) - - GetFeed = get_feed - - def get_entry(self, uri, auth_token=None, converter=None, - desired_class=gdata.data.GDEntry, etag=None, **kwargs): - http_request = atom.http_core.HttpRequest() - # Conditional retrieval - if etag is not None: - http_request.headers['If-None-Match'] = etag - return self.request(method='GET', uri=uri, auth_token=auth_token, - http_request=http_request, converter=converter, - desired_class=desired_class, **kwargs) - - GetEntry = get_entry - - def get_next(self, feed, auth_token=None, converter=None, - desired_class=None, **kwargs): - """Fetches the next set of results from the feed. - - When requesting a feed, the number of entries returned is capped at a - service specific default limit (often 25 entries). You can specify your - own entry-count cap using the max-results URL query parameter. If there - are more results than could fit under max-results, the feed will contain - a next link. This method performs a GET against this next results URL. - - Returns: - A new feed object containing the next set of entries in this feed. - """ - if converter is None and desired_class is None: - desired_class = feed.__class__ - return self.get_feed(feed.find_next_link(), auth_token=auth_token, - converter=converter, desired_class=desired_class, - **kwargs) - - GetNext = get_next - - # TODO: add a refresh method to re-fetch the entry/feed from the server - # if it has been updated. - - def post(self, entry, uri, auth_token=None, converter=None, - desired_class=None, **kwargs): - if converter is None and desired_class is None: - desired_class = entry.__class__ - http_request = atom.http_core.HttpRequest() - http_request.add_body_part( - entry.to_string(get_xml_version(self.api_version)), - 'application/atom+xml') - return self.request(method='POST', uri=uri, auth_token=auth_token, - http_request=http_request, converter=converter, - desired_class=desired_class, **kwargs) - - Post = post - - def update(self, entry, auth_token=None, force=False, uri=None, **kwargs): - """Edits the entry on the server by sending the XML for this entry. - - Performs a PUT and converts the response to a new entry object with a - matching class to the entry passed in. - - Args: - entry: - auth_token: - force: boolean stating whether an update should be forced. Defaults to - False. Normally, if a change has been made since the passed in - entry was obtained, the server will not overwrite the entry since - the changes were based on an obsolete version of the entry. - Setting force to True will cause the update to silently - overwrite whatever version is present. - uri: The uri to put to. If provided, this uri is PUT to rather than the - inferred uri from the entry's edit link. - - Returns: - A new Entry object of a matching type to the entry which was passed in. - """ - http_request = atom.http_core.HttpRequest() - http_request.add_body_part( - entry.to_string(get_xml_version(self.api_version)), - 'application/atom+xml') - # Include the ETag in the request if present. - if force: - http_request.headers['If-Match'] = '*' - elif hasattr(entry, 'etag') and entry.etag: - http_request.headers['If-Match'] = entry.etag - - if uri is None: - uri = entry.find_edit_link() - - return self.request(method='PUT', uri=uri, auth_token=auth_token, - http_request=http_request, - desired_class=entry.__class__, **kwargs) - - Update = update - - def delete(self, entry_or_uri, auth_token=None, force=False, **kwargs): - http_request = atom.http_core.HttpRequest() - - # Include the ETag in the request if present. - if force: - http_request.headers['If-Match'] = '*' - elif hasattr(entry_or_uri, 'etag') and entry_or_uri.etag: - http_request.headers['If-Match'] = entry_or_uri.etag - - # If the user passes in a URL, just delete directly, may not work as - # the service might require an ETag. - if isinstance(entry_or_uri, (str, unicode, atom.http_core.Uri)): - return self.request(method='DELETE', uri=entry_or_uri, - http_request=http_request, auth_token=auth_token, - **kwargs) - - return self.request(method='DELETE', uri=entry_or_uri.find_edit_link(), - http_request=http_request, auth_token=auth_token, - **kwargs) - - Delete = delete - - #TODO: implement batch requests. - #def batch(feed, uri, auth_token=None, converter=None, **kwargs): - # pass - - # TODO: add a refresh method to request a conditional update to an entry - # or feed. - - -def _add_query_param(param_string, value, http_request): - if value: - http_request.uri.query[param_string] = value - - -class Query(object): - - def __init__(self, text_query=None, categories=None, author=None, alt=None, - updated_min=None, updated_max=None, pretty_print=False, - published_min=None, published_max=None, start_index=None, - max_results=None, strict=False, **custom_parameters): - """Constructs a Google Data Query to filter feed contents serverside. - - Args: - text_query: Full text search str (optional) - categories: list of strings (optional). Each string is a required - category. To include an 'or' query, put a | in the string between - terms. For example, to find everything in the Fitz category and - the Laurie or Jane category (Fitz and (Laurie or Jane)) you would - set categories to ['Fitz', 'Laurie|Jane']. - author: str (optional) The service returns entries where the author - name and/or email address match your query string. - alt: str (optional) for the Alternative representation type you'd like - the feed in. If you don't specify an alt parameter, the service - returns an Atom feed. This is equivalent to alt='atom'. - alt='rss' returns an RSS 2.0 result feed. - alt='json' returns a JSON representation of the feed. - alt='json-in-script' Requests a response that wraps JSON in a script - tag. - alt='atom-in-script' Requests an Atom response that wraps an XML - string in a script tag. - alt='rss-in-script' Requests an RSS response that wraps an XML - string in a script tag. - updated_min: str (optional), RFC 3339 timestamp format, lower bounds. - For example: 2005-08-09T10:57:00-08:00 - updated_max: str (optional) updated time must be earlier than timestamp. - pretty_print: boolean (optional) If True the server's XML response will - be indented to make it more human readable. Defaults to False. - published_min: str (optional), Similar to updated_min but for published - time. - published_max: str (optional), Similar to updated_max but for published - time. - start_index: int or str (optional) 1-based index of the first result to - be retrieved. Note that this isn't a general cursoring mechanism. - If you first send a query with ?start-index=1&max-results=10 and - then send another query with ?start-index=11&max-results=10, the - service cannot guarantee that the results are equivalent to - ?start-index=1&max-results=20, because insertions and deletions - could have taken place in between the two queries. - max_results: int or str (optional) Maximum number of results to be - retrieved. Each service has a default max (usually 25) which can - vary from service to service. There is also a service-specific - limit to the max_results you can fetch in a request. - strict: boolean (optional) If True, the server will return an error if - the server does not recognize any of the parameters in the request - URL. Defaults to False. - custom_parameters: other query parameters that are not explicitly defined. - """ - self.text_query = text_query - self.categories = categories or [] - self.author = author - self.alt = alt - self.updated_min = updated_min - self.updated_max = updated_max - self.pretty_print = pretty_print - self.published_min = published_min - self.published_max = published_max - self.start_index = start_index - self.max_results = max_results - self.strict = strict - self.custom_parameters = custom_parameters - - def add_custom_parameter(self, key, value): - self.custom_parameters[key] = value - - AddCustomParameter = add_custom_parameter - - def modify_request(self, http_request): - _add_query_param('q', self.text_query, http_request) - if self.categories: - http_request.uri.query['category'] = ','.join(self.categories) - _add_query_param('author', self.author, http_request) - _add_query_param('alt', self.alt, http_request) - _add_query_param('updated-min', self.updated_min, http_request) - _add_query_param('updated-max', self.updated_max, http_request) - if self.pretty_print: - http_request.uri.query['prettyprint'] = 'true' - _add_query_param('published-min', self.published_min, http_request) - _add_query_param('published-max', self.published_max, http_request) - if self.start_index is not None: - http_request.uri.query['start-index'] = str(self.start_index) - if self.max_results is not None: - http_request.uri.query['max-results'] = str(self.max_results) - if self.strict: - http_request.uri.query['strict'] = 'true' - http_request.uri.query.update(self.custom_parameters) - - ModifyRequest = modify_request - - -class GDQuery(atom.http_core.Uri): - - def _get_text_query(self): - return self.query['q'] - - def _set_text_query(self, value): - self.query['q'] = value - - text_query = property(_get_text_query, _set_text_query, - doc='The q parameter for searching for an exact text match on content') - - -class ResumableUploader(object): - """Resumable upload helper for the Google Data protocol.""" - - DEFAULT_CHUNK_SIZE = 5242880 # 5MB - # Initial chunks which are smaller than 256KB might be dropped. The last - # chunk for a file can be smaller tan this. - MIN_CHUNK_SIZE = 262144 # 256KB - - def __init__(self, client, file_handle, content_type, total_file_size, - chunk_size=None, desired_class=None): - """Starts a resumable upload to a service that supports the protocol. - - Args: - client: gdata.client.GDClient A Google Data API service. - file_handle: object A file-like object containing the file to upload. - content_type: str The mimetype of the file to upload. - total_file_size: int The file's total size in bytes. - chunk_size: int The size of each upload chunk. If None, the - DEFAULT_CHUNK_SIZE will be used. - desired_class: object (optional) The type of gdata.data.GDEntry to parse - the completed entry as. This should be specific to the API. - """ - self.client = client - self.file_handle = file_handle - self.content_type = content_type - self.total_file_size = total_file_size - self.chunk_size = chunk_size or self.DEFAULT_CHUNK_SIZE - if self.chunk_size < self.MIN_CHUNK_SIZE: - self.chunk_size = self.MIN_CHUNK_SIZE - self.desired_class = desired_class or gdata.data.GDEntry - self.upload_uri = None - - # Send the entire file if the chunk size is less than fize's total size. - if self.total_file_size <= self.chunk_size: - self.chunk_size = total_file_size - - def _init_session(self, resumable_media_link, entry=None, headers=None, - auth_token=None, method='POST'): - """Starts a new resumable upload to a service that supports the protocol. - - The method makes a request to initiate a new upload session. The unique - upload uri returned by the server (and set in this method) should be used - to send upload chunks to the server. - - Args: - resumable_media_link: str The full URL for the #resumable-create-media or - #resumable-edit-media link for starting a resumable upload request or - updating media using a resumable PUT. - entry: A (optional) gdata.data.GDEntry containging metadata to create the - upload from. - headers: dict (optional) Additional headers to send in the initial request - to create the resumable upload request. These headers will override - any default headers sent in the request. For example: - headers={'Slug': 'MyTitle'}. - auth_token: (optional) An object which sets the Authorization HTTP header - in its modify_request method. Recommended classes include - gdata.gauth.ClientLoginToken and gdata.gauth.AuthSubToken - among others. - method: (optional) Type of HTTP request to start the session with. - Defaults to 'POST', but may also be 'PUT'. - - Returns: - Result of HTTP request to intialize the session. See atom.client.request. - - Raises: - RequestError if the unique upload uri is not set or the - server returns something other than an HTTP 308 when the upload is - incomplete. - """ - http_request = atom.http_core.HttpRequest() - - # Send empty body if Atom XML wasn't specified. - if entry is None: - http_request.add_body_part('', self.content_type, size=0) - else: - http_request.add_body_part(str(entry), 'application/atom+xml', - size=len(str(entry))) - http_request.headers['X-Upload-Content-Type'] = self.content_type - http_request.headers['X-Upload-Content-Length'] = self.total_file_size - - if headers is not None: - http_request.headers.update(headers) - - response = self.client.request(method=method, - uri=resumable_media_link, - auth_token=auth_token, - http_request=http_request) - - self.upload_uri = (response.getheader('location') or - response.getheader('Location')) - - return response - - _InitSession = _init_session - - def upload_chunk(self, start_byte, content_bytes): - """Uploads a byte range (chunk) to the resumable upload server. - - Args: - start_byte: int The byte offset of the total file where the byte range - passed in lives. - content_bytes: str The file contents of this chunk. - - Returns: - The final Atom entry created on the server. The entry object's type will - be the class specified in self.desired_class. - - Raises: - RequestError if the unique upload uri is not set or the - server returns something other than an HTTP 308 when the upload is - incomplete. - """ - if self.upload_uri is None: - raise RequestError('Resumable upload request not initialized.') - - # Adjustment if last byte range is less than defined chunk size. - chunk_size = self.chunk_size - if len(content_bytes) <= chunk_size: - chunk_size = len(content_bytes) - - http_request = atom.http_core.HttpRequest() - http_request.add_body_part(content_bytes, self.content_type, - size=len(content_bytes)) - http_request.headers['Content-Range'] = ('bytes %s-%s/%s' - % (start_byte, - start_byte + chunk_size - 1, - self.total_file_size)) - - try: - response = self.client.request(method='PUT', uri=self.upload_uri, - http_request=http_request, - desired_class=self.desired_class) - return response - except RequestError, error: - if error.status == 308: - return None - else: - raise error - - UploadChunk = upload_chunk - - def upload_file(self, resumable_media_link, entry=None, headers=None, - auth_token=None, **kwargs): - """Uploads an entire file in chunks using the resumable upload protocol. - - If you are interested in pausing an upload or controlling the chunking - yourself, use the upload_chunk() method instead. - - Args: - resumable_media_link: str The full URL for the #resumable-create-media for - starting a resumable upload request. - entry: A (optional) gdata.data.GDEntry containging metadata to create the - upload from. - headers: dict Additional headers to send in the initial request to create - the resumable upload request. These headers will override any default - headers sent in the request. For example: headers={'Slug': 'MyTitle'}. - auth_token: (optional) An object which sets the Authorization HTTP header - in its modify_request method. Recommended classes include - gdata.gauth.ClientLoginToken and gdata.gauth.AuthSubToken - among others. - kwargs: (optional) Other args to pass to self._init_session. - - Returns: - The final Atom entry created on the server. The entry object's type will - be the class specified in self.desired_class. - - Raises: - RequestError if anything other than a HTTP 308 is returned - when the request raises an exception. - """ - self._init_session(resumable_media_link, headers=headers, - auth_token=auth_token, entry=entry, **kwargs) - - start_byte = 0 - entry = None - - while not entry: - entry = self.upload_chunk( - start_byte, self.file_handle.read(self.chunk_size)) - start_byte += self.chunk_size - - return entry - - UploadFile = upload_file - - def update_file(self, entry_or_resumable_edit_link, headers=None, force=False, - auth_token=None, update_metadata=False, uri_params=None): - """Updates the contents of an existing file using the resumable protocol. - - If you are interested in pausing an upload or controlling the chunking - yourself, use the upload_chunk() method instead. - - Args: - entry_or_resumable_edit_link: object or string A gdata.data.GDEntry for - the entry/file to update or the full uri of the link with rel - #resumable-edit-media. - headers: dict Additional headers to send in the initial request to create - the resumable upload request. These headers will override any default - headers sent in the request. For example: headers={'Slug': 'MyTitle'}. - force boolean (optional) True to force an update and set the If-Match - header to '*'. If False and entry_or_resumable_edit_link is a - gdata.data.GDEntry object, its etag value is used. Otherwise this - parameter should be set to True to force the update. - auth_token: (optional) An object which sets the Authorization HTTP header - in its modify_request method. Recommended classes include - gdata.gauth.ClientLoginToken and gdata.gauth.AuthSubToken - among others. - update_metadata: (optional) True to also update the entry's metadata - with that in the given GDEntry object in entry_or_resumable_edit_link. - uri_params: (optional) Dict of additional parameters to attach to the URI. - Some non-dict types are valid here, too, like list of tuple pairs. - - Returns: - The final Atom entry created on the server. The entry object's type will - be the class specified in self.desired_class. - - Raises: - RequestError if anything other than a HTTP 308 is returned when the - request raises an exception. - """ - - custom_headers = {} - if headers is not None: - custom_headers.update(headers) - - uri = None - entry = None - if isinstance(entry_or_resumable_edit_link, gdata.data.GDEntry): - uri = entry_or_resumable_edit_link.find_url( - 'http://schemas.google.com/g/2005#resumable-edit-media') - custom_headers['If-Match'] = entry_or_resumable_edit_link.etag - if update_metadata: - entry = entry_or_resumable_edit_link - else: - uri = entry_or_resumable_edit_link - - uri = atom.http_core.parse_uri(uri) - if uri_params is not None: - uri.query.update(uri_params) - - if force: - custom_headers['If-Match'] = '*' - - return self.upload_file(str(uri), entry=entry, headers=custom_headers, - auth_token=auth_token, method='PUT') - - UpdateFile = update_file - - def query_upload_status(self, uri=None): - """Queries the current status of a resumable upload request. - - Args: - uri: str (optional) A resumable upload uri to query and override the one - that is set in this object. - - Returns: - An integer representing the file position (byte) to resume the upload from - or True if the upload is complete. - - Raises: - RequestError if anything other than a HTTP 308 is returned - when the request raises an exception. - """ - # Override object's unique upload uri. - if uri is None: - uri = self.upload_uri - - http_request = atom.http_core.HttpRequest() - http_request.headers['Content-Length'] = '0' - http_request.headers['Content-Range'] = 'bytes */%s' % self.total_file_size - - try: - response = self.client.request( - method='POST', uri=uri, http_request=http_request) - if response.status == 201: - return True - else: - raise error_from_response( - '%s returned by server' % response.status, response, RequestError) - except RequestError, error: - if error.status == 308: - for pair in error.headers: - if pair[0].capitalize() == 'Range': - return int(pair[1].split('-')[1]) + 1 - else: - raise error - - QueryUploadStatus = query_upload_status diff --git a/gdata/analytics/codesearch/__init__.py b/gdata/analytics/codesearch/__init__.py deleted file mode 100644 index fa23ef021d..0000000000 --- a/gdata/analytics/codesearch/__init__.py +++ /dev/null @@ -1,136 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright (c) 2007 Benoit Chesneau -# -# Permission to use, copy, modify, and distribute this software for any -# purpose with or without fee is hereby granted, provided that the above -# copyright notice and this permission notice appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF -# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - - -"""Contains extensions to Atom objects used by Google Codesearch""" - -__author__ = 'Benoit Chesneau' - - -import atom -import gdata - - -CODESEARCH_NAMESPACE='http://schemas.google.com/codesearch/2006' -CODESEARCH_TEMPLATE='{http://shema.google.com/codesearch/2006}%s' - - -class Match(atom.AtomBase): - """ The Google Codesearch match element """ - _tag = 'match' - _namespace = CODESEARCH_NAMESPACE - _children = atom.AtomBase._children.copy() - _attributes = atom.AtomBase._attributes.copy() - _attributes['lineNumber'] = 'line_number' - _attributes['type'] = 'type' - - def __init__(self, line_number=None, type=None, extension_elements=None, - extension_attributes=None, text=None): - self.text = text - self.type = type - self.line_number = line_number - self.extension_elements = extension_elements or [] - self.extension_attributes = extension_attributes or {} - - -class File(atom.AtomBase): - """ The Google Codesearch file element""" - _tag = 'file' - _namespace = CODESEARCH_NAMESPACE - _children = atom.AtomBase._children.copy() - _attributes = atom.AtomBase._attributes.copy() - _attributes['name'] = 'name' - - def __init__(self, name=None, extension_elements=None, - extension_attributes=None, text=None): - self.text = text - self.name = name - self.extension_elements = extension_elements or [] - self.extension_attributes = extension_attributes or {} - - -class Package(atom.AtomBase): - """ The Google Codesearch package element""" - _tag = 'package' - _namespace = CODESEARCH_NAMESPACE - _children = atom.AtomBase._children.copy() - _attributes = atom.AtomBase._attributes.copy() - _attributes['name'] = 'name' - _attributes['uri'] = 'uri' - - def __init__(self, name=None, uri=None, extension_elements=None, - extension_attributes=None, text=None): - self.text = text - self.name = name - self.uri = uri - self.extension_elements = extension_elements or [] - self.extension_attributes = extension_attributes or {} - - -class CodesearchEntry(gdata.GDataEntry): - """ Google codesearch atom entry""" - _tag = gdata.GDataEntry._tag - _namespace = gdata.GDataEntry._namespace - _children = gdata.GDataEntry._children.copy() - _attributes = gdata.GDataEntry._attributes.copy() - - _children['{%s}file' % CODESEARCH_NAMESPACE] = ('file', File) - _children['{%s}package' % CODESEARCH_NAMESPACE] = ('package', Package) - _children['{%s}match' % CODESEARCH_NAMESPACE] = ('match', [Match]) - - def __init__(self, author=None, category=None, content=None, - atom_id=None, link=None, published=None, - title=None, updated=None, - match=None, - extension_elements=None, extension_attributes=None, text=None): - - gdata.GDataEntry.__init__(self, author=author, category=category, - content=content, atom_id=atom_id, link=link, - published=published, title=title, - updated=updated, text=None) - - self.match = match or [] - - -def CodesearchEntryFromString(xml_string): - """Converts an XML string into a CodesearchEntry object. - - Args: - xml_string: string The XML describing a Codesearch feed entry. - - Returns: - A CodesearchEntry object corresponding to the given XML. - """ - return atom.CreateClassFromXMLString(CodesearchEntry, xml_string) - - -class CodesearchFeed(gdata.GDataFeed): - """feed containing list of Google codesearch Items""" - _tag = gdata.GDataFeed._tag - _namespace = gdata.GDataFeed._namespace - _children = gdata.GDataFeed._children.copy() - _attributes = gdata.GDataFeed._attributes.copy() - _children['{%s}entry' % atom.ATOM_NAMESPACE] = ('entry', [CodesearchEntry]) - - -def CodesearchFeedFromString(xml_string): - """Converts an XML string into a CodesearchFeed object. - Args: - xml_string: string The XML describing a Codesearch feed. - Returns: - A CodeseartchFeed object corresponding to the given XML. - """ - return atom.CreateClassFromXMLString(CodesearchFeed, xml_string) diff --git a/gdata/analytics/codesearch/service.py b/gdata/analytics/codesearch/service.py deleted file mode 100644 index 1243d614ee..0000000000 --- a/gdata/analytics/codesearch/service.py +++ /dev/null @@ -1,110 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright (c) 2007 Benoit Chesneau -# -# Permission to use, copy, modify, and distribute this software for any -# purpose with or without fee is hereby granted, provided that the above -# copyright notice and this permission notice appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF -# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - - -"""CodesearchService extends GDataService to streamline Google Codesearch -operations""" - - -__author__ = 'Benoit Chesneau' - - -import atom -import gdata.service -import gdata.codesearch - - -class CodesearchService(gdata.service.GDataService): - """Client extension for Google codesearch service""" - ssl = True - - def __init__(self, email=None, password=None, source=None, - server='www.google.com', additional_headers=None, **kwargs): - """Creates a client for the Google codesearch 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: 'www.google.com'. - **kwargs: The other parameters to pass to gdata.service.GDataService - constructor. - """ - gdata.service.GDataService.__init__( - self, email=email, password=password, service='codesearch', - source=source, server=server, additional_headers=additional_headers, - **kwargs) - - def Query(self, uri, converter=gdata.codesearch.CodesearchFeedFromString): - """Queries the Codesearch feed and returns the resulting feed of - entries. - - Args: - uri: string The full URI to be queried. This can contain query - parameters, a hostname, or simply the relative path to a Document - List feed. The DocumentQuery object is useful when constructing - query parameters. - converter: func (optional) A function which will be executed on the - retrieved item, generally to render it into a Python object. - By default the CodesearchFeedFromString function is used to - return a CodesearchFeed object. This is because most feed - queries will result in a feed and not a single entry. - - Returns : - A CodesearchFeed objects representing the feed returned by the server - """ - return self.Get(uri, converter=converter) - - def GetSnippetsFeed(self, text_query=None): - """Retrieve Codesearch feed for a keyword - - Args: - text_query : string (optional) The contents of the q query parameter. This - string is URL escaped upon conversion to a URI. - Returns: - A CodesearchFeed objects representing the feed returned by the server - """ - - query=gdata.codesearch.service.CodesearchQuery(text_query=text_query) - feed = self.Query(query.ToUri()) - return feed - - -class CodesearchQuery(gdata.service.Query): - """Object used to construct the query to the Google Codesearch feed. here only as a shorcut""" - - def __init__(self, feed='/codesearch/feeds/search', text_query=None, - params=None, categories=None): - """Constructor for Codesearch Query. - - Args: - feed: string (optional) The path for the feed. (e.g. '/codesearch/feeds/search') - 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. - - Yelds: - A CodesearchQuery object to construct a URI based on Codesearch feed - """ - - gdata.service.Query.__init__(self, feed, text_query, params, categories) diff --git a/gdata/analytics/contacts/__init__.py b/gdata/analytics/contacts/__init__.py deleted file mode 100644 index 41e7c31e4f..0000000000 --- a/gdata/analytics/contacts/__init__.py +++ /dev/null @@ -1,740 +0,0 @@ -#!/usr/bin/env python -# -# Copyright 2009 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. - -"""Contains extensions to ElementWrapper objects used with Google Contacts.""" - -__author__ = 'dbrattli (Dag Brattli)' - - -import atom -import gdata - - -## Constants from http://code.google.com/apis/gdata/elements.html ## -REL_HOME = 'http://schemas.google.com/g/2005#home' -REL_WORK = 'http://schemas.google.com/g/2005#work' -REL_OTHER = 'http://schemas.google.com/g/2005#other' - -# AOL Instant Messenger protocol -IM_AIM = 'http://schemas.google.com/g/2005#AIM' -IM_MSN = 'http://schemas.google.com/g/2005#MSN' # MSN Messenger protocol -IM_YAHOO = 'http://schemas.google.com/g/2005#YAHOO' # Yahoo Messenger protocol -IM_SKYPE = 'http://schemas.google.com/g/2005#SKYPE' # Skype protocol -IM_QQ = 'http://schemas.google.com/g/2005#QQ' # QQ protocol -# Google Talk protocol -IM_GOOGLE_TALK = 'http://schemas.google.com/g/2005#GOOGLE_TALK' -IM_ICQ = 'http://schemas.google.com/g/2005#ICQ' # ICQ protocol -IM_JABBER = 'http://schemas.google.com/g/2005#JABBER' # Jabber protocol -IM_NETMEETING = 'http://schemas.google.com/g/2005#netmeeting' # NetMeeting - -PHOTO_LINK_REL = 'http://schemas.google.com/contacts/2008/rel#photo' -PHOTO_EDIT_LINK_REL = 'http://schemas.google.com/contacts/2008/rel#edit-photo' - -# Different phone types, for more info see: -# http://code.google.com/apis/gdata/docs/2.0/elements.html#gdPhoneNumber -PHONE_CAR = 'http://schemas.google.com/g/2005#car' -PHONE_FAX = 'http://schemas.google.com/g/2005#fax' -PHONE_GENERAL = 'http://schemas.google.com/g/2005#general' -PHONE_HOME = REL_HOME -PHONE_HOME_FAX = 'http://schemas.google.com/g/2005#home_fax' -PHONE_INTERNAL = 'http://schemas.google.com/g/2005#internal-extension' -PHONE_MOBILE = 'http://schemas.google.com/g/2005#mobile' -PHONE_OTHER = REL_OTHER -PHONE_PAGER = 'http://schemas.google.com/g/2005#pager' -PHONE_SATELLITE = 'http://schemas.google.com/g/2005#satellite' -PHONE_VOIP = 'http://schemas.google.com/g/2005#voip' -PHONE_WORK = REL_WORK -PHONE_WORK_FAX = 'http://schemas.google.com/g/2005#work_fax' -PHONE_WORK_MOBILE = 'http://schemas.google.com/g/2005#work_mobile' -PHONE_WORK_PAGER = 'http://schemas.google.com/g/2005#work_pager' -PHONE_MAIN = 'http://schemas.google.com/g/2005#main' -PHONE_ASSISTANT = 'http://schemas.google.com/g/2005#assistant' -PHONE_CALLBACK = 'http://schemas.google.com/g/2005#callback' -PHONE_COMPANY_MAIN = 'http://schemas.google.com/g/2005#company_main' -PHONE_ISDN = 'http://schemas.google.com/g/2005#isdn' -PHONE_OTHER_FAX = 'http://schemas.google.com/g/2005#other_fax' -PHONE_RADIO = 'http://schemas.google.com/g/2005#radio' -PHONE_TELEX = 'http://schemas.google.com/g/2005#telex' -PHONE_TTY_TDD = 'http://schemas.google.com/g/2005#tty_tdd' - -EXTERNAL_ID_ORGANIZATION = 'organization' - -RELATION_MANAGER = 'manager' - -CONTACTS_NAMESPACE = 'http://schemas.google.com/contact/2008' - - -class GDataBase(atom.AtomBase): - """The Google Contacts intermediate class from atom.AtomBase.""" - - _namespace = gdata.GDATA_NAMESPACE - _children = atom.AtomBase._children.copy() - _attributes = atom.AtomBase._attributes.copy() - - def __init__(self, text=None, - extension_elements=None, extension_attributes=None): - self.text = text - self.extension_elements = extension_elements or [] - self.extension_attributes = extension_attributes or {} - - -class ContactsBase(GDataBase): - """The Google Contacts intermediate class for Contacts namespace.""" - - _namespace = CONTACTS_NAMESPACE - - -class OrgName(GDataBase): - """The Google Contacts OrgName element.""" - - _tag = 'orgName' - - -class OrgTitle(GDataBase): - """The Google Contacts OrgTitle element.""" - - _tag = 'orgTitle' - - -class OrgDepartment(GDataBase): - """The Google Contacts OrgDepartment element.""" - - _tag = 'orgDepartment' - - -class OrgJobDescription(GDataBase): - """The Google Contacts OrgJobDescription element.""" - - _tag = 'orgJobDescription' - - -class Where(GDataBase): - """The Google Contacts Where element.""" - - _tag = 'where' - _children = GDataBase._children.copy() - _attributes = GDataBase._attributes.copy() - _attributes['rel'] = 'rel' - _attributes['label'] = 'label' - _attributes['valueString'] = 'value_string' - - def __init__(self, value_string=None, rel=None, label=None, - text=None, extension_elements=None, extension_attributes=None): - GDataBase.__init__(self, text=text, extension_elements=extension_elements, - extension_attributes=extension_attributes) - self.rel = rel - self.label = label - self.value_string = value_string - - -class When(GDataBase): - """The Google Contacts When element.""" - - _tag = 'when' - _children = GDataBase._children.copy() - _attributes = GDataBase._attributes.copy() - _attributes['startTime'] = 'start_time' - _attributes['endTime'] = 'end_time' - _attributes['label'] = 'label' - - def __init__(self, start_time=None, end_time=None, label=None, - text=None, extension_elements=None, extension_attributes=None): - GDataBase.__init__(self, text=text, extension_elements=extension_elements, - extension_attributes=extension_attributes) - self.start_time = start_time - self.end_time = end_time - self.label = label - - -class Organization(GDataBase): - """The Google Contacts Organization element.""" - - _tag = 'organization' - _children = GDataBase._children.copy() - _attributes = GDataBase._attributes.copy() - _attributes['label'] = 'label' - _attributes['rel'] = 'rel' - _attributes['primary'] = 'primary' - _children['{%s}orgName' % GDataBase._namespace] = ( - 'org_name', OrgName) - _children['{%s}orgTitle' % GDataBase._namespace] = ( - 'org_title', OrgTitle) - _children['{%s}orgDepartment' % GDataBase._namespace] = ( - 'org_department', OrgDepartment) - _children['{%s}orgJobDescription' % GDataBase._namespace] = ( - 'org_job_description', OrgJobDescription) - #_children['{%s}where' % GDataBase._namespace] = ('where', Where) - - def __init__(self, label=None, rel=None, primary='false', org_name=None, - org_title=None, org_department=None, org_job_description=None, - where=None, text=None, - extension_elements=None, extension_attributes=None): - GDataBase.__init__(self, text=text, extension_elements=extension_elements, - extension_attributes=extension_attributes) - self.label = label - self.rel = rel or REL_OTHER - self.primary = primary - self.org_name = org_name - self.org_title = org_title - self.org_department = org_department - self.org_job_description = org_job_description - self.where = where - - -class PostalAddress(GDataBase): - """The Google Contacts PostalAddress element.""" - - _tag = 'postalAddress' - _children = GDataBase._children.copy() - _attributes = GDataBase._attributes.copy() - _attributes['rel'] = 'rel' - _attributes['primary'] = 'primary' - - def __init__(self, primary=None, rel=None, text=None, - extension_elements=None, extension_attributes=None): - GDataBase.__init__(self, text=text, extension_elements=extension_elements, - extension_attributes=extension_attributes) - self.rel = rel or REL_OTHER - self.primary = primary - - -class FormattedAddress(GDataBase): - """The Google Contacts FormattedAddress element.""" - - _tag = 'formattedAddress' - - -class StructuredPostalAddress(GDataBase): - """The Google Contacts StructuredPostalAddress element.""" - - _tag = 'structuredPostalAddress' - _children = GDataBase._children.copy() - _attributes = GDataBase._attributes.copy() - _attributes['rel'] = 'rel' - _attributes['primary'] = 'primary' - _children['{%s}formattedAddress' % GDataBase._namespace] = ( - 'formatted_address', FormattedAddress) - - def __init__(self, rel=None, primary=None, - formatted_address=None, text=None, - extension_elements=None, extension_attributes=None): - GDataBase.__init__(self, text=text, extension_elements=extension_elements, - extension_attributes=extension_attributes) - self.rel = rel or REL_OTHER - self.primary = primary - self.formatted_address = formatted_address - - -class IM(GDataBase): - """The Google Contacts IM element.""" - - _tag = 'im' - _children = GDataBase._children.copy() - _attributes = GDataBase._attributes.copy() - _attributes['address'] = 'address' - _attributes['primary'] = 'primary' - _attributes['protocol'] = 'protocol' - _attributes['label'] = 'label' - _attributes['rel'] = 'rel' - - def __init__(self, primary='false', rel=None, address=None, protocol=None, - label=None, text=None, - extension_elements=None, extension_attributes=None): - GDataBase.__init__(self, text=text, extension_elements=extension_elements, - extension_attributes=extension_attributes) - self.protocol = protocol - self.address = address - self.primary = primary - self.rel = rel or REL_OTHER - self.label = label - - -class Email(GDataBase): - """The Google Contacts Email element.""" - - _tag = 'email' - _children = GDataBase._children.copy() - _attributes = GDataBase._attributes.copy() - _attributes['address'] = 'address' - _attributes['primary'] = 'primary' - _attributes['rel'] = 'rel' - _attributes['label'] = 'label' - - def __init__(self, label=None, rel=None, address=None, primary='false', - text=None, extension_elements=None, extension_attributes=None): - GDataBase.__init__(self, text=text, extension_elements=extension_elements, - extension_attributes=extension_attributes) - self.label = label - self.rel = rel or REL_OTHER - self.address = address - self.primary = primary - - -class PhoneNumber(GDataBase): - """The Google Contacts PhoneNumber element.""" - - _tag = 'phoneNumber' - _children = GDataBase._children.copy() - _attributes = GDataBase._attributes.copy() - _attributes['label'] = 'label' - _attributes['rel'] = 'rel' - _attributes['uri'] = 'uri' - _attributes['primary'] = 'primary' - - def __init__(self, label=None, rel=None, uri=None, primary='false', - text=None, extension_elements=None, extension_attributes=None): - GDataBase.__init__(self, text=text, extension_elements=extension_elements, - extension_attributes=extension_attributes) - self.label = label - self.rel = rel or REL_OTHER - self.uri = uri - self.primary = primary - - -class Nickname(ContactsBase): - """The Google Contacts Nickname element.""" - - _tag = 'nickname' - - -class Occupation(ContactsBase): - """The Google Contacts Occupation element.""" - - _tag = 'occupation' - - -class Gender(ContactsBase): - """The Google Contacts Gender element.""" - - _tag = 'gender' - _children = ContactsBase._children.copy() - _attributes = ContactsBase._attributes.copy() - _attributes['value'] = 'value' - - def __init__(self, value=None, - text=None, extension_elements=None, extension_attributes=None): - ContactsBase.__init__(self, text=text, - extension_elements=extension_elements, - extension_attributes=extension_attributes) - self.value = value - - -class Birthday(ContactsBase): - """The Google Contacts Birthday element.""" - - _tag = 'birthday' - _children = ContactsBase._children.copy() - _attributes = ContactsBase._attributes.copy() - _attributes['when'] = 'when' - - def __init__(self, when=None, - text=None, extension_elements=None, extension_attributes=None): - ContactsBase.__init__(self, text=text, - extension_elements=extension_elements, - extension_attributes=extension_attributes) - self.when = when - - -class Relation(ContactsBase): - """The Google Contacts Relation element.""" - - _tag = 'relation' - _children = ContactsBase._children.copy() - _attributes = ContactsBase._attributes.copy() - _attributes['label'] = 'label' - _attributes['rel'] = 'rel' - - def __init__(self, label=None, rel=None, - text=None, extension_elements=None, extension_attributes=None): - ContactsBase.__init__(self, text=text, - extension_elements=extension_elements, - extension_attributes=extension_attributes) - self.label = label - self.rel = rel - - -def RelationFromString(xml_string): - return atom.CreateClassFromXMLString(Relation, xml_string) - - -class UserDefinedField(ContactsBase): - """The Google Contacts UserDefinedField element.""" - - _tag = 'userDefinedField' - _children = ContactsBase._children.copy() - _attributes = ContactsBase._attributes.copy() - _attributes['key'] = 'key' - _attributes['value'] = 'value' - - def __init__(self, key=None, value=None, - text=None, extension_elements=None, extension_attributes=None): - ContactsBase.__init__(self, text=text, - extension_elements=extension_elements, - extension_attributes=extension_attributes) - self.key = key - self.value = value - - -def UserDefinedFieldFromString(xml_string): - return atom.CreateClassFromXMLString(UserDefinedField, xml_string) - - -class Website(ContactsBase): - """The Google Contacts Website element.""" - - _tag = 'website' - _children = ContactsBase._children.copy() - _attributes = ContactsBase._attributes.copy() - _attributes['href'] = 'href' - _attributes['label'] = 'label' - _attributes['primary'] = 'primary' - _attributes['rel'] = 'rel' - - def __init__(self, href=None, label=None, primary='false', rel=None, - text=None, extension_elements=None, extension_attributes=None): - ContactsBase.__init__(self, text=text, - extension_elements=extension_elements, - extension_attributes=extension_attributes) - self.href = href - self.label = label - self.primary = primary - self.rel = rel - - -def WebsiteFromString(xml_string): - return atom.CreateClassFromXMLString(Website, xml_string) - - -class ExternalId(ContactsBase): - """The Google Contacts ExternalId element.""" - - _tag = 'externalId' - _children = ContactsBase._children.copy() - _attributes = ContactsBase._attributes.copy() - _attributes['label'] = 'label' - _attributes['rel'] = 'rel' - _attributes['value'] = 'value' - - def __init__(self, label=None, rel=None, value=None, - text=None, extension_elements=None, extension_attributes=None): - ContactsBase.__init__(self, text=text, - extension_elements=extension_elements, - extension_attributes=extension_attributes) - self.label = label - self.rel = rel - self.value = value - - -def ExternalIdFromString(xml_string): - return atom.CreateClassFromXMLString(ExternalId, xml_string) - - -class Event(ContactsBase): - """The Google Contacts Event element.""" - - _tag = 'event' - _children = ContactsBase._children.copy() - _attributes = ContactsBase._attributes.copy() - _attributes['label'] = 'label' - _attributes['rel'] = 'rel' - _children['{%s}when' % ContactsBase._namespace] = ('when', When) - - def __init__(self, label=None, rel=None, when=None, - text=None, extension_elements=None, extension_attributes=None): - ContactsBase.__init__(self, text=text, - extension_elements=extension_elements, - extension_attributes=extension_attributes) - self.label = label - self.rel = rel - self.when = when - - -def EventFromString(xml_string): - return atom.CreateClassFromXMLString(Event, xml_string) - - -class Deleted(GDataBase): - """The Google Contacts Deleted element.""" - - _tag = 'deleted' - - -class GroupMembershipInfo(ContactsBase): - """The Google Contacts GroupMembershipInfo element.""" - - _tag = 'groupMembershipInfo' - - _children = ContactsBase._children.copy() - _attributes = ContactsBase._attributes.copy() - _attributes['deleted'] = 'deleted' - _attributes['href'] = 'href' - - def __init__(self, deleted=None, href=None, text=None, - extension_elements=None, extension_attributes=None): - ContactsBase.__init__(self, text=text, - extension_elements=extension_elements, - extension_attributes=extension_attributes) - self.deleted = deleted - self.href = href - - -class PersonEntry(gdata.BatchEntry): - """Base class for ContactEntry and ProfileEntry.""" - - _children = gdata.BatchEntry._children.copy() - _children['{%s}organization' % gdata.GDATA_NAMESPACE] = ( - 'organization', [Organization]) - _children['{%s}phoneNumber' % gdata.GDATA_NAMESPACE] = ( - 'phone_number', [PhoneNumber]) - _children['{%s}nickname' % CONTACTS_NAMESPACE] = ('nickname', Nickname) - _children['{%s}occupation' % CONTACTS_NAMESPACE] = ('occupation', Occupation) - _children['{%s}gender' % CONTACTS_NAMESPACE] = ('gender', Gender) - _children['{%s}birthday' % CONTACTS_NAMESPACE] = ('birthday', Birthday) - _children['{%s}postalAddress' % gdata.GDATA_NAMESPACE] = ('postal_address', - [PostalAddress]) - _children['{%s}structuredPostalAddress' % gdata.GDATA_NAMESPACE] = ( - 'structured_postal_address', [StructuredPostalAddress]) - _children['{%s}email' % gdata.GDATA_NAMESPACE] = ('email', [Email]) - _children['{%s}im' % gdata.GDATA_NAMESPACE] = ('im', [IM]) - _children['{%s}relation' % CONTACTS_NAMESPACE] = ('relation', [Relation]) - _children['{%s}userDefinedField' % CONTACTS_NAMESPACE] = ( - 'user_defined_field', [UserDefinedField]) - _children['{%s}website' % CONTACTS_NAMESPACE] = ('website', [Website]) - _children['{%s}externalId' % CONTACTS_NAMESPACE] = ( - 'external_id', [ExternalId]) - _children['{%s}event' % CONTACTS_NAMESPACE] = ('event', [Event]) - # The following line should be removed once the Python support - # for GData 2.0 is mature. - _attributes = gdata.BatchEntry._attributes.copy() - _attributes['{%s}etag' % gdata.GDATA_NAMESPACE] = 'etag' - - def __init__(self, author=None, category=None, content=None, - atom_id=None, link=None, published=None, - title=None, updated=None, organization=None, phone_number=None, - nickname=None, occupation=None, gender=None, birthday=None, - postal_address=None, structured_postal_address=None, email=None, - im=None, relation=None, user_defined_field=None, website=None, - external_id=None, event=None, batch_operation=None, - batch_id=None, batch_status=None, text=None, - extension_elements=None, extension_attributes=None, etag=None): - gdata.BatchEntry.__init__(self, author=author, category=category, - content=content, atom_id=atom_id, link=link, - published=published, - batch_operation=batch_operation, - batch_id=batch_id, batch_status=batch_status, - title=title, updated=updated) - self.organization = organization or [] - self.phone_number = phone_number or [] - self.nickname = nickname - self.occupation = occupation - self.gender = gender - self.birthday = birthday - self.postal_address = postal_address or [] - self.structured_postal_address = structured_postal_address or [] - self.email = email or [] - self.im = im or [] - self.relation = relation or [] - self.user_defined_field = user_defined_field or [] - self.website = website or [] - self.external_id = external_id or [] - self.event = event or [] - self.text = text - self.extension_elements = extension_elements or [] - self.extension_attributes = extension_attributes or {} - # The following line should be removed once the Python support - # for GData 2.0 is mature. - self.etag = etag - - -class ContactEntry(PersonEntry): - """A Google Contact flavor of an Atom Entry.""" - - _children = PersonEntry._children.copy() - - _children['{%s}deleted' % gdata.GDATA_NAMESPACE] = ('deleted', Deleted) - _children['{%s}groupMembershipInfo' % CONTACTS_NAMESPACE] = ( - 'group_membership_info', [GroupMembershipInfo]) - _children['{%s}extendedProperty' % gdata.GDATA_NAMESPACE] = ( - 'extended_property', [gdata.ExtendedProperty]) - # Overwrite the organization rule in PersonEntry so that a ContactEntry - # may only contain one element. - _children['{%s}organization' % gdata.GDATA_NAMESPACE] = ( - 'organization', Organization) - - def __init__(self, author=None, category=None, content=None, - atom_id=None, link=None, published=None, - title=None, updated=None, organization=None, phone_number=None, - nickname=None, occupation=None, gender=None, birthday=None, - postal_address=None, structured_postal_address=None, email=None, - im=None, relation=None, user_defined_field=None, website=None, - external_id=None, event=None, batch_operation=None, - batch_id=None, batch_status=None, text=None, - extension_elements=None, extension_attributes=None, etag=None, - deleted=None, extended_property=None, - group_membership_info=None): - PersonEntry.__init__(self, author=author, category=category, - content=content, atom_id=atom_id, link=link, - published=published, title=title, updated=updated, - organization=organization, phone_number=phone_number, - nickname=nickname, occupation=occupation, - gender=gender, birthday=birthday, - postal_address=postal_address, - structured_postal_address=structured_postal_address, - email=email, im=im, relation=relation, - user_defined_field=user_defined_field, - website=website, external_id=external_id, event=event, - batch_operation=batch_operation, batch_id=batch_id, - batch_status=batch_status, text=text, - extension_elements=extension_elements, - extension_attributes=extension_attributes, etag=etag) - self.deleted = deleted - self.extended_property = extended_property or [] - self.group_membership_info = group_membership_info or [] - - def GetPhotoLink(self): - for a_link in self.link: - if a_link.rel == PHOTO_LINK_REL: - return a_link - return None - - def GetPhotoEditLink(self): - for a_link in self.link: - if a_link.rel == PHOTO_EDIT_LINK_REL: - return a_link - return None - - -def ContactEntryFromString(xml_string): - return atom.CreateClassFromXMLString(ContactEntry, xml_string) - - -class ContactsFeed(gdata.BatchFeed, gdata.LinkFinder): - """A Google Contacts feed flavor of an Atom Feed.""" - - _children = gdata.BatchFeed._children.copy() - - _children['{%s}entry' % atom.ATOM_NAMESPACE] = ('entry', [ContactEntry]) - - def __init__(self, author=None, category=None, contributor=None, - generator=None, icon=None, atom_id=None, link=None, logo=None, - rights=None, subtitle=None, title=None, updated=None, - entry=None, total_results=None, start_index=None, - items_per_page=None, extension_elements=None, - extension_attributes=None, text=None): - gdata.BatchFeed.__init__(self, author=author, category=category, - contributor=contributor, generator=generator, - icon=icon, atom_id=atom_id, link=link, - logo=logo, rights=rights, subtitle=subtitle, - title=title, updated=updated, entry=entry, - total_results=total_results, - start_index=start_index, - items_per_page=items_per_page, - extension_elements=extension_elements, - extension_attributes=extension_attributes, - text=text) - - -def ContactsFeedFromString(xml_string): - return atom.CreateClassFromXMLString(ContactsFeed, xml_string) - - -class GroupEntry(gdata.BatchEntry): - """Represents a contact group.""" - _children = gdata.BatchEntry._children.copy() - _children['{%s}extendedProperty' % gdata.GDATA_NAMESPACE] = ( - 'extended_property', [gdata.ExtendedProperty]) - - def __init__(self, author=None, category=None, content=None, - contributor=None, atom_id=None, link=None, published=None, - rights=None, source=None, summary=None, control=None, - title=None, updated=None, - extended_property=None, batch_operation=None, batch_id=None, - batch_status=None, - extension_elements=None, extension_attributes=None, text=None): - gdata.BatchEntry.__init__(self, author=author, category=category, - content=content, - atom_id=atom_id, link=link, published=published, - batch_operation=batch_operation, - batch_id=batch_id, batch_status=batch_status, - title=title, updated=updated) - self.extended_property = extended_property or [] - - -def GroupEntryFromString(xml_string): - return atom.CreateClassFromXMLString(GroupEntry, xml_string) - - -class GroupsFeed(gdata.BatchFeed): - """A Google contact groups feed flavor of an Atom Feed.""" - _children = gdata.BatchFeed._children.copy() - _children['{%s}entry' % atom.ATOM_NAMESPACE] = ('entry', [GroupEntry]) - - -def GroupsFeedFromString(xml_string): - return atom.CreateClassFromXMLString(GroupsFeed, xml_string) - - -class ProfileEntry(PersonEntry): - """A Google Profiles flavor of an Atom Entry.""" - - -def ProfileEntryFromString(xml_string): - """Converts an XML string into a ProfileEntry object. - - Args: - xml_string: string The XML describing a Profile entry. - - Returns: - A ProfileEntry object corresponding to the given XML. - """ - return atom.CreateClassFromXMLString(ProfileEntry, xml_string) - - -class ProfilesFeed(gdata.BatchFeed, gdata.LinkFinder): - """A Google Profiles feed flavor of an Atom Feed.""" - - _children = gdata.BatchFeed._children.copy() - _children['{%s}entry' % atom.ATOM_NAMESPACE] = ('entry', [ProfileEntry]) - - def __init__(self, author=None, category=None, contributor=None, - generator=None, icon=None, atom_id=None, link=None, logo=None, - rights=None, subtitle=None, title=None, updated=None, - entry=None, total_results=None, start_index=None, - items_per_page=None, extension_elements=None, - extension_attributes=None, text=None): - gdata.BatchFeed.__init__(self, author=author, category=category, - contributor=contributor, generator=generator, - icon=icon, atom_id=atom_id, link=link, - logo=logo, rights=rights, subtitle=subtitle, - title=title, updated=updated, entry=entry, - total_results=total_results, - start_index=start_index, - items_per_page=items_per_page, - extension_elements=extension_elements, - extension_attributes=extension_attributes, - text=text) - - -def ProfilesFeedFromString(xml_string): - """Converts an XML string into a ProfilesFeed object. - - Args: - xml_string: string The XML describing a Profiles feed. - - Returns: - A ProfilesFeed object corresponding to the given XML. - """ - return atom.CreateClassFromXMLString(ProfilesFeed, xml_string) diff --git a/gdata/analytics/contacts/client.py b/gdata/analytics/contacts/client.py deleted file mode 100644 index 9ae9cd770e..0000000000 --- a/gdata/analytics/contacts/client.py +++ /dev/null @@ -1,547 +0,0 @@ -#!/usr/bin/env python -# -# Copyright (C) 2009 Google 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. -from types import ListType, DictionaryType - - -"""Contains a client to communicate with the Contacts servers. - -For documentation on the Contacts API, see: -http://code.google.com/apis/contatcs/ -""" - -__author__ = 'vinces1979@gmail.com (Vince Spicer)' - - -import gdata.client -import gdata.contacts.data -import atom.client -import atom.data -import atom.http_core -import gdata.gauth - -DEFAULT_BATCH_URL = ('https://www.google.com/m8/feeds/contacts/default/full' - '/batch') -DEFAULT_PROFILES_BATCH_URL = ('https://www.google.com/m8/feeds/profiles/domain/' - '%s/full/batch') - -class ContactsClient(gdata.client.GDClient): - api_version = '3' - auth_service = 'cp' - server = "www.google.com" - contact_list = "default" - auth_scopes = gdata.gauth.AUTH_SCOPES['cp'] - ssl = True - - - def __init__(self, domain=None, auth_token=None, **kwargs): - """Constructs a new client for the Email Settings API. - - Args: - domain: string The Google Apps domain (if any). - kwargs: The other parameters to pass to the gdata.client.GDClient - constructor. - """ - gdata.client.GDClient.__init__(self, auth_token=auth_token, **kwargs) - self.domain = domain - - def get_feed_uri(self, kind='contacts', contact_list=None, projection='full', - scheme="https"): - """Builds a feed URI. - - Args: - kind: The type of feed to return, typically 'groups' or 'contacts'. - Default value: 'contacts'. - contact_list: The contact list to return a feed for. - Default value: self.contact_list. - projection: The projection to apply to the feed contents, for example - 'full', 'base', 'base/12345', 'full/batch'. Default value: 'full'. - scheme: The URL scheme such as 'http' or 'https', None to return a - relative URI without hostname. - - Returns: - A feed URI using the given kind, contact list, and projection. - Example: '/m8/feeds/contacts/default/full'. - """ - contact_list = contact_list or self.contact_list - if kind == 'profiles': - contact_list = 'domain/%s' % self.domain - prefix = scheme and '%s://%s' % (scheme, self.server) or '' - return '%s/m8/feeds/%s/%s/%s' % (prefix, kind, contact_list, projection) - - GetFeedUri = get_feed_uri - - def get_contact(self, uri, desired_class=gdata.contacts.data.ContactEntry, - auth_token=None, **kwargs): - return self.get_entry(uri, auth_token=auth_token, - desired_class=desired_class, **kwargs) - - - GetContact = get_contact - - - def create_contact(self, new_contact, insert_uri=None, auth_token=None, **kwargs): - """Adds an new contact to Google Contacts. - - Args: - new_contact: atom.Entry or subclass A new contact which is to be added to - Google Contacts. - insert_uri: the URL to post new contacts to the feed - url_params: dict (optional) Additional URL parameters to be included - in the insertion request. - escape_params: boolean (optional) If true, the url_parameters will be - escaped before they are included in the request. - - Returns: - On successful insert, an entry containing the contact created - On failure, a RequestError is raised of the form: - {'status': HTTP status code from server, - 'reason': HTTP reason from the server, - 'body': HTTP body of the server's response} - """ - insert_uri = insert_uri or self.GetFeedUri() - return self.Post(new_contact, insert_uri, - auth_token=auth_token, **kwargs) - - CreateContact = create_contact - - def add_contact(self, new_contact, insert_uri=None, auth_token=None, - billing_information=None, birthday=None, calendar_link=None, **kwargs): - """Adds an new contact to Google Contacts. - - Args: - new_contact: atom.Entry or subclass A new contact which is to be added to - Google Contacts. - insert_uri: the URL to post new contacts to the feed - url_params: dict (optional) Additional URL parameters to be included - in the insertion request. - escape_params: boolean (optional) If true, the url_parameters will be - escaped before they are included in the request. - - Returns: - On successful insert, an entry containing the contact created - On failure, a RequestError is raised of the form: - {'status': HTTP status code from server, - 'reason': HTTP reason from the server, - 'body': HTTP body of the server's response} - """ - - contact = gdata.contacts.data.ContactEntry() - - if billing_information is not None: - if not isinstance(billing_information, gdata.contacts.data.BillingInformation): - billing_information = gdata.contacts.data.BillingInformation(text=billing_information) - - contact.billing_information = billing_information - - if birthday is not None: - if not isinstance(birthday, gdata.contacts.data.Birthday): - birthday = gdata.contacts.data.Birthday(when=birthday) - - contact.birthday = birthday - - if calendar_link is not None: - if type(calendar_link) is not ListType: - calendar_link = [calendar_link] - - for link in calendar_link: - if not isinstance(link, gdata.contacts.data.CalendarLink): - if type(link) is not DictionaryType: - raise TypeError, "calendar_link Requires dictionary not %s" % type(link) - - link = gdata.contacts.data.CalendarLink( - rel=link.get("rel", None), - label=link.get("label", None), - primary=link.get("primary", None), - href=link.get("href", None), - ) - - contact.calendar_link.append(link) - - insert_uri = insert_uri or self.GetFeedUri() - return self.Post(contact, insert_uri, - auth_token=auth_token, **kwargs) - - AddContact = add_contact - - def get_contacts(self, uri=None, desired_class=gdata.contacts.data.ContactsFeed, - auth_token=None, **kwargs): - """Obtains a feed with the contacts belonging to the current user. - - Args: - auth_token: An object which sets the Authorization HTTP header in its - modify_request method. Recommended classes include - gdata.gauth.ClientLoginToken and gdata.gauth.AuthSubToken - among others. Represents the current user. Defaults to None - and if None, this method will look for a value in the - auth_token member of SpreadsheetsClient. - desired_class: class descended from atom.core.XmlElement to which a - successful response should be converted. If there is no - converter function specified (desired_class=None) then the - desired_class will be used in calling the - atom.core.parse function. If neither - the desired_class nor the converter is specified, an - HTTP reponse object will be returned. Defaults to - gdata.spreadsheets.data.SpreadsheetsFeed. - """ - uri = uri or self.GetFeedUri() - return self.get_feed(uri, auth_token=auth_token, - desired_class=desired_class, **kwargs) - - GetContacts = get_contacts - - def get_group(self, uri=None, desired_class=gdata.contacts.data.GroupEntry, - auth_token=None, **kwargs): - """ Get a single groups details - Args: - uri: the group uri or id - """ - return self.get_entry(uri, desired_class=desired_class, auth_token=auth_token, **kwargs) - - GetGroup = get_group - - def get_groups(self, uri=None, desired_class=gdata.contacts.data.GroupsFeed, - auth_token=None, **kwargs): - uri = uri or self.GetFeedUri('groups') - return self.get_feed(uri, desired_class=desired_class, auth_token=auth_token, **kwargs) - - GetGroups = get_groups - - def create_group(self, new_group, insert_uri=None, url_params=None, - desired_class=None, **kwargs): - insert_uri = insert_uri or self.GetFeedUri('groups') - return self.Post(new_group, insert_uri, url_params=url_params, - desired_class=desired_class, **kwargs) - - CreateGroup = create_group - - def update_group(self, edit_uri, updated_group, url_params=None, - escape_params=True, desired_class=None, auth_token=None, **kwargs): - return self.Put(updated_group, self._CleanUri(edit_uri), - url_params=url_params, - escape_params=escape_params, - desired_class=desired_class, - auth_token=auth_token, **kwargs) - - UpdateGroup = update_group - - def delete_group(self, group_object, auth_token=None, force=False, **kws): - return self.Delete(group_object, auth_token=auth_token, force=force, **kws) - - DeleteGroup = delete_group - - def change_photo(self, media, contact_entry_or_url, content_type=None, - content_length=None, auth_token=None, **kwargs): - """Change the photo for the contact by uploading a new photo. - - Performs a PUT against the photo edit URL to send the binary data for the - photo. - - Args: - media: filename, file-like-object, or a gdata.data.MediaSource object to send. - contact_entry_or_url: ContactEntry or str If it is a ContactEntry, this - method will search for an edit photo link URL and - perform a PUT to the URL. - content_type: str (optional) the mime type for the photo data. This is - necessary if media is a file or file name, but if media - is a MediaSource object then the media object can contain - the mime type. If media_type is set, it will override the - mime type in the media object. - content_length: int or str (optional) Specifying the content length is - only required if media is a file-like object. If media - is a filename, the length is determined using - os.path.getsize. If media is a MediaSource object, it is - assumed that it already contains the content length. - """ - ifmatch_header = None - if isinstance(contact_entry_or_url, gdata.contacts.data.ContactEntry): - photo_link = contact_entry_or_url.GetPhotoLink() - uri = photo_link.href - ifmatch_header = atom.client.CustomHeaders( - **{'if-match': photo_link.etag}) - else: - uri = contact_entry_or_url - if isinstance(media, gdata.data.MediaSource): - payload = media - # If the media object is a file-like object, then use it as the file - # handle in the in the MediaSource. - elif hasattr(media, 'read'): - payload = gdata.data.MediaSource(file_handle=media, - content_type=content_type, content_length=content_length) - # Assume that the media object is a file name. - else: - payload = gdata.data.MediaSource(content_type=content_type, - content_length=content_length, file_path=media) - return self.Put(uri=uri, data=payload, auth_token=auth_token, - ifmatch_header=ifmatch_header, **kwargs) - - ChangePhoto = change_photo - - def get_photo(self, contact_entry_or_url, auth_token=None, **kwargs): - """Retrives the binary data for the contact's profile photo as a string. - - Args: - contact_entry_or_url: a gdata.contacts.ContactEntry object or a string - containing the photo link's URL. If the contact entry does not - contain a photo link, the image will not be fetched and this method - will return None. - """ - # TODO: add the ability to write out the binary image data to a file, - # reading and writing a chunk at a time to avoid potentially using up - # large amounts of memory. - url = None - if isinstance(contact_entry_or_url, gdata.contacts.data.ContactEntry): - photo_link = contact_entry_or_url.GetPhotoLink() - if photo_link: - url = photo_link.href - else: - url = contact_entry_or_url - if url: - return self.Get(url, auth_token=auth_token, **kwargs).read() - else: - return None - - GetPhoto = get_photo - - def delete_photo(self, contact_entry_or_url, auth_token=None, **kwargs): - """Delete the contact's profile photo. - - Args: - contact_entry_or_url: a gdata.contacts.ContactEntry object or a string - containing the photo link's URL. - """ - uri = None - ifmatch_header = None - if isinstance(contact_entry_or_url, gdata.contacts.data.ContactEntry): - photo_link = contact_entry_or_url.GetPhotoLink() - if photo_link.etag: - uri = photo_link.href - ifmatch_header = atom.client.CustomHeaders( - **{'if-match': photo_link.etag}) - else: - # No etag means no photo has been assigned to this contact. - return - else: - uri = contact_entry_or_url - if uri: - self.Delete(entry_or_uri=uri, auth_token=auth_token, - ifmatch_header=ifmatch_header, **kwargs) - - DeletePhoto = delete_photo - - def get_profiles_feed(self, uri=None, auth_token=None, **kwargs): - """Retrieves a feed containing all domain's profiles. - - Args: - uri: string (optional) the URL to retrieve the profiles feed, - for example /m8/feeds/profiles/default/full - - Returns: - On success, a ProfilesFeed containing the profiles. - On failure, raises a RequestError. - """ - - uri = uri or self.GetFeedUri('profiles') - return self.get_feed(uri, auth_token=auth_token, - desired_class=gdata.contacts.data.ProfilesFeed, **kwargs) - - GetProfilesFeed = get_profiles_feed - - def get_profile(self, uri, auth_token=None, **kwargs): - """Retrieves a domain's profile for the user. - - Args: - uri: string the URL to retrieve the profiles feed, - for example /m8/feeds/profiles/default/full/username - - Returns: - On success, a ProfileEntry containing the profile for the user. - On failure, raises a RequestError - """ - return self.get_entry(uri, - desired_class=gdata.contacts.data.ProfileEntry, - auth_token=auth_token, **kwargs) - - GetProfile = get_profile - - def update_profile(self, updated_profile, auth_token=None, force=False, **kwargs): - """Updates an existing profile. - - Args: - updated_profile: atom.Entry or subclass containing - the Atom Entry which will replace the profile which is - stored at the edit_url. - auth_token: An object which sets the Authorization HTTP header in its - modify_request method. Recommended classes include - gdata.gauth.ClientLoginToken and gdata.gauth.AuthSubToken - among others. Represents the current user. Defaults to None - and if None, this method will look for a value in the - auth_token member of ContactsClient. - force: boolean stating whether an update should be forced. Defaults to - False. Normally, if a change has been made since the passed in - entry was obtained, the server will not overwrite the entry since - the changes were based on an obsolete version of the entry. - Setting force to True will cause the update to silently - overwrite whatever version is present. - url_params: dict (optional) Additional URL parameters to be included - in the insertion request. - escape_params: boolean (optional) If true, the url_parameters will be - escaped before they are included in the request. - - Returns: - On successful update, a httplib.HTTPResponse containing the server's - response to the PUT request. - On failure, raises a RequestError. - """ - return self.Update(updated_profile, auth_token=auth_token, force=force, **kwargs) - - UpdateProfile = update_profile - - def execute_batch(self, batch_feed, url=DEFAULT_BATCH_URL, desired_class=None, - auth_token=None, **kwargs): - """Sends a batch request feed to the server. - - Args: - batch_feed: gdata.contacts.ContactFeed A feed containing batch - request entries. Each entry contains the operation to be performed - on the data contained in the entry. For example an entry with an - operation type of insert will be used as if the individual entry - had been inserted. - url: str The batch URL to which these operations should be applied. - converter: Function (optional) The function used to convert the server's - response to an object. - - Returns: - The results of the batch request's execution on the server. If the - default converter is used, this is stored in a ContactsFeed. - """ - return self.Post(batch_feed, url, desired_class=desired_class, - auth_token=None, **kwargs) - - ExecuteBatch = execute_batch - - def execute_batch_profiles(self, batch_feed, url=None, - desired_class=gdata.contacts.data.ProfilesFeed, - auth_token=None, **kwargs): - """Sends a batch request feed to the server. - - Args: - batch_feed: gdata.profiles.ProfilesFeed A feed containing batch - request entries. Each entry contains the operation to be performed - on the data contained in the entry. For example an entry with an - operation type of insert will be used as if the individual entry - had been inserted. - url: string The batch URL to which these operations should be applied. - converter: Function (optional) The function used to convert the server's - response to an object. The default value is - gdata.profiles.ProfilesFeedFromString. - - Returns: - The results of the batch request's execution on the server. If the - default converter is used, this is stored in a ProfilesFeed. - """ - url = url or (DEFAULT_PROFILES_BATCH_URL % self.domain) - return self.Post(batch_feed, url, desired_class=desired_class, - auth_token=auth_token, **kwargs) - - ExecuteBatchProfiles = execute_batch_profiles - - def _CleanUri(self, uri): - """Sanitizes a feed URI. - - Args: - uri: The URI to sanitize, can be relative or absolute. - - Returns: - The given URI without its http://server prefix, if any. - Keeps the leading slash of the URI. - """ - url_prefix = 'http://%s' % self.server - if uri.startswith(url_prefix): - uri = uri[len(url_prefix):] - return uri - -class ContactsQuery(gdata.client.Query): - """ - Create a custom Contacts Query - - Full specs can be found at: U{Contacts query parameters reference - } - """ - - def __init__(self, feed=None, group=None, orderby=None, showdeleted=None, - sortorder=None, requirealldeleted=None, **kwargs): - """ - @param max_results: The maximum number of entries to return. If you want - to receive all of the contacts, rather than only the default maximum, you - can specify a very large number for max-results. - @param start-index: The 1-based index of the first result to be retrieved. - @param updated-min: The lower bound on entry update dates. - @param group: Constrains the results to only the contacts belonging to the - group specified. Value of this parameter specifies group ID - @param orderby: Sorting criterion. The only supported value is - lastmodified. - @param showdeleted: Include deleted contacts in the returned contacts feed - @pram sortorder: Sorting order direction. Can be either ascending or - descending. - @param requirealldeleted: Only relevant if showdeleted and updated-min - are also provided. It dictates the behavior of the server in case it - detects that placeholders of some entries deleted since the point in - time specified as updated-min may have been lost. - """ - gdata.client.Query.__init__(self, **kwargs) - self.group = group - self.orderby = orderby - self.sortorder = sortorder - self.showdeleted = showdeleted - - def modify_request(self, http_request): - if self.group: - gdata.client._add_query_param('group', self.group, http_request) - if self.orderby: - gdata.client._add_query_param('orderby', self.orderby, http_request) - if self.sortorder: - gdata.client._add_query_param('sortorder', self.sortorder, http_request) - if self.showdeleted: - gdata.client._add_query_param('showdeleted', self.showdeleted, http_request) - gdata.client.Query.modify_request(self, http_request) - - ModifyRequest = modify_request - - -class ProfilesQuery(gdata.client.Query): - """ - Create a custom Profiles Query - - Full specs can be found at: U{Profiless query parameters reference - } - """ - - def __init__(self, feed=None, start_key=None, **kwargs): - """ - @param start_key: Opaque key of the first element to retrieve. Present in - the next link of an earlier request, if further pages of response are - available. - """ - gdata.client.Query.__init__(self, **kwargs) - self.feed = feed or 'https://www.google.com/m8/feeds/profiles/default/full' - self.start_key = start_key - - def modify_request(self, http_request): - if self.start_key: - gdata.client._add_query_param('start-key', self.start_key, http_request) - gdata.client.Query.modify_request(self, http_request) - - ModifyRequest = modify_request diff --git a/gdata/analytics/contacts/data.py b/gdata/analytics/contacts/data.py deleted file mode 100644 index ce2385204a..0000000000 --- a/gdata/analytics/contacts/data.py +++ /dev/null @@ -1,493 +0,0 @@ -#!/usr/bin/env python -# -# Copyright (C) 2009 Google 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. - -"""Data model classes for parsing and generating XML for the Contacts API.""" - - -__author__ = 'vinces1979@gmail.com (Vince Spicer)' - - -import atom.core -import gdata -import gdata.data - - -PHOTO_LINK_REL = 'http://schemas.google.com/contacts/2008/rel#photo' -PHOTO_EDIT_LINK_REL = 'http://schemas.google.com/contacts/2008/rel#edit-photo' - -EXTERNAL_ID_ORGANIZATION = 'organization' - -RELATION_MANAGER = 'manager' - -CONTACTS_NAMESPACE = 'http://schemas.google.com/contact/2008' -CONTACTS_TEMPLATE = '{%s}%%s' % CONTACTS_NAMESPACE - - -class BillingInformation(atom.core.XmlElement): - """ - gContact:billingInformation - Specifies billing information of the entity represented by the contact. The element cannot be repeated. - """ - - _qname = CONTACTS_TEMPLATE % 'billingInformation' - - -class Birthday(atom.core.XmlElement): - """ - Stores birthday date of the person represented by the contact. The element cannot be repeated. - """ - - _qname = CONTACTS_TEMPLATE % 'birthday' - when = 'when' - - -class ContactLink(atom.data.Link): - """ - Extends atom.data.Link to add gd:etag attribute for photo link. - """ - etag = gdata.data.GD_TEMPLATE % 'etag' - - -class CalendarLink(atom.core.XmlElement): - """ - Storage for URL of the contact's calendar. The element can be repeated. - """ - - _qname = CONTACTS_TEMPLATE % 'calendarLink' - rel = 'rel' - label = 'label' - primary = 'primary' - href = 'href' - - -class DirectoryServer(atom.core.XmlElement): - """ - A directory server associated with this contact. - May not be repeated. - """ - - _qname = CONTACTS_TEMPLATE % 'directoryServer' - - -class Event(atom.core.XmlElement): - """ - These elements describe events associated with a contact. - They may be repeated - """ - - _qname = CONTACTS_TEMPLATE % 'event' - label = 'label' - rel = 'rel' - when = gdata.data.When - - -class ExternalId(atom.core.XmlElement): - """ - Describes an ID of the contact in an external system of some kind. - This element may be repeated. - """ - - _qname = CONTACTS_TEMPLATE % 'externalId' - label = 'label' - rel = 'rel' - value = 'value' - - -def ExternalIdFromString(xml_string): - return atom.core.parse(ExternalId, xml_string) - - -class Gender(atom.core.XmlElement): - """ - Specifies the gender of the person represented by the contact. - The element cannot be repeated. - """ - - _qname = CONTACTS_TEMPLATE % 'directoryServer' - value = 'value' - - -class Hobby(atom.core.XmlElement): - """ - Describes an ID of the contact in an external system of some kind. - This element may be repeated. - """ - - _qname = CONTACTS_TEMPLATE % 'hobby' - - -class Initials(atom.core.XmlElement): - """ Specifies the initials of the person represented by the contact. The - element cannot be repeated. """ - - _qname = CONTACTS_TEMPLATE % 'initials' - - -class Jot(atom.core.XmlElement): - """ - Storage for arbitrary pieces of information about the contact. Each jot - has a type specified by the rel attribute and a text value. - The element can be repeated. - """ - - _qname = CONTACTS_TEMPLATE % 'jot' - rel = 'rel' - - -class Language(atom.core.XmlElement): - """ - Specifies the preferred languages of the contact. - The element can be repeated. - - The language must be specified using one of two mutually exclusive methods: - using the freeform @label attribute, or using the @code attribute, whose value - must conform to the IETF BCP 47 specification. - """ - - _qname = CONTACTS_TEMPLATE % 'language' - code = 'code' - label = 'label' - - -class MaidenName(atom.core.XmlElement): - """ - Specifies maiden name of the person represented by the contact. - The element cannot be repeated. - """ - - _qname = CONTACTS_TEMPLATE % 'maidenName' - - -class Mileage(atom.core.XmlElement): - """ - Specifies the mileage for the entity represented by the contact. - Can be used for example to document distance needed for reimbursement - purposes. The value is not interpreted. The element cannot be repeated. - """ - - _qname = CONTACTS_TEMPLATE % 'mileage' - - -class NickName(atom.core.XmlElement): - """ - Specifies the nickname of the person represented by the contact. - The element cannot be repeated. - """ - - _qname = CONTACTS_TEMPLATE % 'nickname' - - -class Occupation(atom.core.XmlElement): - """ - Specifies the occupation/profession of the person specified by the contact. - The element cannot be repeated. - """ - - _qname = CONTACTS_TEMPLATE % 'occupation' - - -class Priority(atom.core.XmlElement): - """ - Classifies importance of the contact into 3 categories: - * Low - * Normal - * High - - The priority element cannot be repeated. - """ - - _qname = CONTACTS_TEMPLATE % 'priority' - - -class Relation(atom.core.XmlElement): - """ - This element describe another entity (usually a person) that is in a - relation of some kind with the contact. - """ - - _qname = CONTACTS_TEMPLATE % 'relation' - rel = 'rel' - label = 'label' - - -class Sensitivity(atom.core.XmlElement): - """ - Classifies sensitivity of the contact into the following categories: - * Confidential - * Normal - * Personal - * Private - - The sensitivity element cannot be repeated. - """ - - _qname = CONTACTS_TEMPLATE % 'sensitivity' - rel = 'rel' - - -class UserDefinedField(atom.core.XmlElement): - """ - Represents an arbitrary key-value pair attached to the contact. - """ - - _qname = CONTACTS_TEMPLATE % 'userDefinedField' - key = 'key' - value = 'value' - - -def UserDefinedFieldFromString(xml_string): - return atom.core.parse(UserDefinedField, xml_string) - - -class Website(atom.core.XmlElement): - """ - Describes websites associated with the contact, including links. - May be repeated. - """ - - _qname = CONTACTS_TEMPLATE % 'website' - - href = 'href' - label = 'label' - primary = 'primary' - rel = 'rel' - - -def WebsiteFromString(xml_string): - return atom.core.parse(Website, xml_string) - - -class HouseName(atom.core.XmlElement): - """ - Used in places where houses or buildings have names (and - not necessarily numbers), eg. "The Pillars". - """ - - _qname = CONTACTS_TEMPLATE % 'housename' - - -class Street(atom.core.XmlElement): - """ - Can be street, avenue, road, etc. This element also includes the house - number and room/apartment/flat/floor number. - """ - - _qname = CONTACTS_TEMPLATE % 'street' - - -class POBox(atom.core.XmlElement): - """ - Covers actual P.O. boxes, drawers, locked bags, etc. This is usually but not - always mutually exclusive with street - """ - - _qname = CONTACTS_TEMPLATE % 'pobox' - - -class Neighborhood(atom.core.XmlElement): - """ - This is used to disambiguate a street address when a city contains more than - one street with the same name, or to specify a small place whose mail is - routed through a larger postal town. In China it could be a county or a - minor city. - """ - - _qname = CONTACTS_TEMPLATE % 'neighborhood' - - -class City(atom.core.XmlElement): - """ - Can be city, village, town, borough, etc. This is the postal town and not - necessarily the place of residence or place of business. - """ - - _qname = CONTACTS_TEMPLATE % 'city' - - -class SubRegion(atom.core.XmlElement): - """ - Handles administrative districts such as U.S. or U.K. counties that are not - used for mail addressing purposes. Subregion is not intended for - delivery addresses. - """ - - _qname = CONTACTS_TEMPLATE % 'subregion' - - -class Region(atom.core.XmlElement): - """ - A state, province, county (in Ireland), Land (in Germany), - departement (in France), etc. - """ - - _qname = CONTACTS_TEMPLATE % 'region' - - -class PostalCode(atom.core.XmlElement): - """ - Postal code. Usually country-wide, but sometimes specific to the - city (e.g. "2" in "Dublin 2, Ireland" addresses). - """ - - _qname = CONTACTS_TEMPLATE % 'postcode' - - -class Country(atom.core.XmlElement): - """ The name or code of the country. """ - - _qname = CONTACTS_TEMPLATE % 'country' - - -class Status(atom.core.XmlElement): - """Person's status element.""" - - _qname = CONTACTS_TEMPLATE % 'status' - indexed = 'indexed' - - -class PersonEntry(gdata.data.BatchEntry): - """Represents a google contact""" - - link = [ContactLink] - - billing_information = BillingInformation - birthday = Birthday - calendar_link = [CalendarLink] - directory_server = DirectoryServer - event = [Event] - external_id = [ExternalId] - gender = Gender - hobby = [Hobby] - initials = Initials - jot = [Jot] - language= [Language] - maiden_name = MaidenName - mileage = Mileage - nickname = NickName - occupation = Occupation - priority = Priority - relation = [Relation] - sensitivity = Sensitivity - user_defined_field = [UserDefinedField] - website = [Website] - - name = gdata.data.Name - phone_number = [gdata.data.PhoneNumber] - organization = gdata.data.Organization - postal_address = [gdata.data.PostalAddress] - email = [gdata.data.Email] - im = [gdata.data.Im] - structured_postal_address = [gdata.data.StructuredPostalAddress] - extended_property = [gdata.data.ExtendedProperty] - - status = Status - - -class Deleted(atom.core.XmlElement): - """If present, indicates that this contact has been deleted.""" - _qname = gdata.GDATA_TEMPLATE % 'deleted' - - -class GroupMembershipInfo(atom.core.XmlElement): - """ - Identifies the group to which the contact belongs or belonged. - The group is referenced by its id. - """ - - _qname = CONTACTS_TEMPLATE % 'groupMembershipInfo' - - href = 'href' - deleted = 'deleted' - - -class ContactEntry(PersonEntry): - """A Google Contacts flavor of an Atom Entry.""" - - deleted = Deleted - group_membership_info = [GroupMembershipInfo] - organization = gdata.data.Organization - - def GetPhotoLink(self): - for a_link in self.link: - if a_link.rel == PHOTO_LINK_REL: - return a_link - return None - - def GetPhotoEditLink(self): - for a_link in self.link: - if a_link.rel == PHOTO_EDIT_LINK_REL: - return a_link - return None - - -class ContactsFeed(gdata.data.BatchFeed): - """A collection of Contacts.""" - entry = [ContactEntry] - - -class SystemGroup(atom.core.XmlElement): - """The contacts systemGroup element. - - When used within a contact group entry, indicates that the group in - question is one of the predefined system groups.""" - - _qname = CONTACTS_TEMPLATE % 'systemGroup' - id = 'id' - - -class GroupEntry(gdata.data.BatchEntry): - """Represents a contact group.""" - extended_property = [gdata.data.ExtendedProperty] - system_group = SystemGroup - - -class GroupsFeed(gdata.data.BatchFeed): - """A Google contact groups feed flavor of an Atom Feed.""" - entry = [GroupEntry] - - -class ProfileEntry(PersonEntry): - """A Google Profiles flavor of an Atom Entry.""" - - -def ProfileEntryFromString(xml_string): - """Converts an XML string into a ProfileEntry object. - - Args: - xml_string: string The XML describing a Profile entry. - - Returns: - A ProfileEntry object corresponding to the given XML. - """ - return atom.core.parse(ProfileEntry, xml_string) - - -class ProfilesFeed(gdata.data.BatchFeed): - """A Google Profiles feed flavor of an Atom Feed.""" - _qname = atom.data.ATOM_TEMPLATE % 'feed' - entry = [ProfileEntry] - - -def ProfilesFeedFromString(xml_string): - """Converts an XML string into a ProfilesFeed object. - - Args: - xml_string: string The XML describing a Profiles feed. - - Returns: - A ProfilesFeed object corresponding to the given XML. - """ - return atom.core.parse(ProfilesFeed, xml_string) diff --git a/gdata/analytics/contacts/service.py b/gdata/analytics/contacts/service.py deleted file mode 100644 index 4b017c0451..0000000000 --- a/gdata/analytics/contacts/service.py +++ /dev/null @@ -1,427 +0,0 @@ -#!/usr/bin/env python -# -# Copyright 2009 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. - -"""ContactsService extends the GDataService for Google Contacts operations. - - ContactsService: Provides methods to query feeds and manipulate items. - Extends GDataService. - - DictionaryToParamList: Function which converts a dictionary into a list of - URL arguments (represented as strings). This is a - utility function used in CRUD operations. -""" - -__author__ = 'dbrattli (Dag Brattli)' - - -import gdata -import gdata.calendar -import gdata.service - - -DEFAULT_BATCH_URL = ('http://www.google.com/m8/feeds/contacts/default/full' - '/batch') -DEFAULT_PROFILES_BATCH_URL = ('http://www.google.com' - '/m8/feeds/profiles/default/full/batch') - -GDATA_VER_HEADER = 'GData-Version' - - -class Error(Exception): - pass - - -class RequestError(Error): - pass - - -class ContactsService(gdata.service.GDataService): - """Client for the Google Contacts service.""" - - def __init__(self, email=None, password=None, source=None, - server='www.google.com', additional_headers=None, - contact_list='default', **kwargs): - """Creates a client for the Contacts 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: 'www.google.com'. - contact_list: string (optional) The name of the default contact list to - use when no URI is specified to the methods of the service. - Default value: 'default' (the logged in user's contact list). - **kwargs: The other parameters to pass to gdata.service.GDataService - constructor. - """ - - self.contact_list = contact_list - gdata.service.GDataService.__init__( - self, email=email, password=password, service='cp', source=source, - server=server, additional_headers=additional_headers, **kwargs) - - def GetFeedUri(self, kind='contacts', contact_list=None, projection='full', - scheme=None): - """Builds a feed URI. - - Args: - kind: The type of feed to return, typically 'groups' or 'contacts'. - Default value: 'contacts'. - contact_list: The contact list to return a feed for. - Default value: self.contact_list. - projection: The projection to apply to the feed contents, for example - 'full', 'base', 'base/12345', 'full/batch'. Default value: 'full'. - scheme: The URL scheme such as 'http' or 'https', None to return a - relative URI without hostname. - - Returns: - A feed URI using the given kind, contact list, and projection. - Example: '/m8/feeds/contacts/default/full'. - """ - contact_list = contact_list or self.contact_list - if kind == 'profiles': - contact_list = 'domain/%s' % contact_list - prefix = scheme and '%s://%s' % (scheme, self.server) or '' - return '%s/m8/feeds/%s/%s/%s' % (prefix, kind, contact_list, projection) - - def GetContactsFeed(self, uri=None): - uri = uri or self.GetFeedUri() - return self.Get(uri, converter=gdata.contacts.ContactsFeedFromString) - - def GetContact(self, uri): - return self.Get(uri, converter=gdata.contacts.ContactEntryFromString) - - def CreateContact(self, new_contact, insert_uri=None, url_params=None, - escape_params=True): - """Adds an new contact to Google Contacts. - - Args: - new_contact: atom.Entry or subclass A new contact which is to be added to - Google Contacts. - insert_uri: the URL to post new contacts to the feed - url_params: dict (optional) Additional URL parameters to be included - in the insertion request. - escape_params: boolean (optional) If true, the url_parameters will be - escaped before they are included in the request. - - Returns: - On successful insert, an entry containing the contact created - On failure, a RequestError is raised of the form: - {'status': HTTP status code from server, - 'reason': HTTP reason from the server, - 'body': HTTP body of the server's response} - """ - insert_uri = insert_uri or self.GetFeedUri() - return self.Post(new_contact, insert_uri, url_params=url_params, - escape_params=escape_params, - converter=gdata.contacts.ContactEntryFromString) - - def UpdateContact(self, edit_uri, updated_contact, url_params=None, - escape_params=True): - """Updates an existing contact. - - Args: - edit_uri: string The edit link URI for the element being updated - updated_contact: string, atom.Entry or subclass containing - the Atom Entry which will replace the contact which is - stored at the edit_url - url_params: dict (optional) Additional URL parameters to be included - in the update request. - escape_params: boolean (optional) If true, the url_parameters will be - escaped before they are included in the request. - - Returns: - On successful update, a httplib.HTTPResponse containing the server's - response to the PUT request. - On failure, a RequestError is raised of the form: - {'status': HTTP status code from server, - 'reason': HTTP reason from the server, - 'body': HTTP body of the server's response} - """ - return self.Put(updated_contact, self._CleanUri(edit_uri), - url_params=url_params, - escape_params=escape_params, - converter=gdata.contacts.ContactEntryFromString) - - def DeleteContact(self, edit_uri, extra_headers=None, - url_params=None, escape_params=True): - """Removes an contact with the specified ID from Google Contacts. - - Args: - edit_uri: string The edit URL of the entry to be deleted. Example: - '/m8/feeds/contacts/default/full/xxx/yyy' - url_params: dict (optional) Additional URL parameters to be included - in the deletion request. - escape_params: boolean (optional) If true, the url_parameters will be - escaped before they are included in the request. - - Returns: - On successful delete, a httplib.HTTPResponse containing the server's - response to the DELETE request. - On failure, a RequestError is raised of the form: - {'status': HTTP status code from server, - 'reason': HTTP reason from the server, - 'body': HTTP body of the server's response} - """ - return self.Delete(self._CleanUri(edit_uri), - url_params=url_params, escape_params=escape_params) - - def GetGroupsFeed(self, uri=None): - uri = uri or self.GetFeedUri('groups') - return self.Get(uri, converter=gdata.contacts.GroupsFeedFromString) - - def CreateGroup(self, new_group, insert_uri=None, url_params=None, - escape_params=True): - insert_uri = insert_uri or self.GetFeedUri('groups') - return self.Post(new_group, insert_uri, url_params=url_params, - escape_params=escape_params, - converter=gdata.contacts.GroupEntryFromString) - - def UpdateGroup(self, edit_uri, updated_group, url_params=None, - escape_params=True): - return self.Put(updated_group, self._CleanUri(edit_uri), - url_params=url_params, - escape_params=escape_params, - converter=gdata.contacts.GroupEntryFromString) - - def DeleteGroup(self, edit_uri, extra_headers=None, - url_params=None, escape_params=True): - return self.Delete(self._CleanUri(edit_uri), - url_params=url_params, escape_params=escape_params) - - def ChangePhoto(self, media, contact_entry_or_url, content_type=None, - content_length=None): - """Change the photo for the contact by uploading a new photo. - - Performs a PUT against the photo edit URL to send the binary data for the - photo. - - Args: - media: filename, file-like-object, or a gdata.MediaSource object to send. - contact_entry_or_url: ContactEntry or str If it is a ContactEntry, this - method will search for an edit photo link URL and - perform a PUT to the URL. - content_type: str (optional) the mime type for the photo data. This is - necessary if media is a file or file name, but if media - is a MediaSource object then the media object can contain - the mime type. If media_type is set, it will override the - mime type in the media object. - content_length: int or str (optional) Specifying the content length is - only required if media is a file-like object. If media - is a filename, the length is determined using - os.path.getsize. If media is a MediaSource object, it is - assumed that it already contains the content length. - """ - if isinstance(contact_entry_or_url, gdata.contacts.ContactEntry): - url = contact_entry_or_url.GetPhotoEditLink().href - else: - url = contact_entry_or_url - if isinstance(media, gdata.MediaSource): - payload = media - # If the media object is a file-like object, then use it as the file - # handle in the in the MediaSource. - elif hasattr(media, 'read'): - payload = gdata.MediaSource(file_handle=media, - content_type=content_type, content_length=content_length) - # Assume that the media object is a file name. - else: - payload = gdata.MediaSource(content_type=content_type, - content_length=content_length, file_path=media) - return self.Put(payload, url) - - def GetPhoto(self, contact_entry_or_url): - """Retrives the binary data for the contact's profile photo as a string. - - Args: - contact_entry_or_url: a gdata.contacts.ContactEntry objecr or a string - containing the photo link's URL. If the contact entry does not - contain a photo link, the image will not be fetched and this method - will return None. - """ - # TODO: add the ability to write out the binary image data to a file, - # reading and writing a chunk at a time to avoid potentially using up - # large amounts of memory. - url = None - if isinstance(contact_entry_or_url, gdata.contacts.ContactEntry): - photo_link = contact_entry_or_url.GetPhotoLink() - if photo_link: - url = photo_link.href - else: - url = contact_entry_or_url - if url: - return self.Get(url, converter=str) - else: - return None - - def DeletePhoto(self, contact_entry_or_url): - url = None - if isinstance(contact_entry_or_url, gdata.contacts.ContactEntry): - url = contact_entry_or_url.GetPhotoEditLink().href - else: - url = contact_entry_or_url - if url: - self.Delete(url) - - def GetProfilesFeed(self, uri=None): - """Retrieves a feed containing all domain's profiles. - - Args: - uri: string (optional) the URL to retrieve the profiles feed, - for example /m8/feeds/profiles/default/full - - Returns: - On success, a ProfilesFeed containing the profiles. - On failure, raises a RequestError. - """ - - uri = uri or self.GetFeedUri('profiles') - return self.Get(uri, - converter=gdata.contacts.ProfilesFeedFromString) - - def GetProfile(self, uri): - """Retrieves a domain's profile for the user. - - Args: - uri: string the URL to retrieve the profiles feed, - for example /m8/feeds/profiles/default/full/username - - Returns: - On success, a ProfileEntry containing the profile for the user. - On failure, raises a RequestError - """ - return self.Get(uri, - converter=gdata.contacts.ProfileEntryFromString) - - def UpdateProfile(self, edit_uri, updated_profile, url_params=None, - escape_params=True): - """Updates an existing profile. - - Args: - edit_uri: string The edit link URI for the element being updated - updated_profile: string atom.Entry or subclass containing - the Atom Entry which will replace the profile which is - stored at the edit_url. - url_params: dict (optional) Additional URL parameters to be included - in the update request. - escape_params: boolean (optional) If true, the url_params will be - escaped before they are included in the request. - - Returns: - On successful update, a httplib.HTTPResponse containing the server's - response to the PUT request. - On failure, raises a RequestError. - """ - return self.Put(updated_profile, self._CleanUri(edit_uri), - url_params=url_params, escape_params=escape_params, - converter=gdata.contacts.ProfileEntryFromString) - - def ExecuteBatch(self, batch_feed, url, - converter=gdata.contacts.ContactsFeedFromString): - """Sends a batch request feed to the server. - - Args: - batch_feed: gdata.contacts.ContactFeed A feed containing batch - request entries. Each entry contains the operation to be performed - on the data contained in the entry. For example an entry with an - operation type of insert will be used as if the individual entry - had been inserted. - url: str The batch URL to which these operations should be applied. - converter: Function (optional) The function used to convert the server's - response to an object. The default value is ContactsFeedFromString. - - Returns: - The results of the batch request's execution on the server. If the - default converter is used, this is stored in a ContactsFeed. - """ - return self.Post(batch_feed, url, converter=converter) - - def ExecuteBatchProfiles(self, batch_feed, url, - converter=gdata.contacts.ProfilesFeedFromString): - """Sends a batch request feed to the server. - - Args: - batch_feed: gdata.profiles.ProfilesFeed A feed containing batch - request entries. Each entry contains the operation to be performed - on the data contained in the entry. For example an entry with an - operation type of insert will be used as if the individual entry - had been inserted. - url: string The batch URL to which these operations should be applied. - converter: Function (optional) The function used to convert the server's - response to an object. The default value is - gdata.profiles.ProfilesFeedFromString. - - Returns: - The results of the batch request's execution on the server. If the - default converter is used, this is stored in a ProfilesFeed. - """ - return self.Post(batch_feed, url, converter=converter) - - def _CleanUri(self, uri): - """Sanitizes a feed URI. - - Args: - uri: The URI to sanitize, can be relative or absolute. - - Returns: - The given URI without its http://server prefix, if any. - Keeps the leading slash of the URI. - """ - url_prefix = 'http://%s' % self.server - if uri.startswith(url_prefix): - uri = uri[len(url_prefix):] - return uri - -class ContactsQuery(gdata.service.Query): - - def __init__(self, feed=None, text_query=None, params=None, - categories=None, group=None): - self.feed = feed or '/m8/feeds/contacts/default/full' - if group: - self._SetGroup(group) - gdata.service.Query.__init__(self, feed=self.feed, text_query=text_query, - params=params, categories=categories) - - def _GetGroup(self): - if 'group' in self: - return self['group'] - else: - return None - - def _SetGroup(self, group_id): - self['group'] = group_id - - group = property(_GetGroup, _SetGroup, - doc='The group query parameter to find only contacts in this group') - -class GroupsQuery(gdata.service.Query): - - def __init__(self, feed=None, text_query=None, params=None, - categories=None): - self.feed = feed or '/m8/feeds/groups/default/full' - gdata.service.Query.__init__(self, feed=self.feed, text_query=text_query, - params=params, categories=categories) - - -class ProfilesQuery(gdata.service.Query): - """Constructs a query object for the profiles feed.""" - - def __init__(self, feed=None, text_query=None, params=None, - categories=None): - self.feed = feed or '/m8/feeds/profiles/default/full' - gdata.service.Query.__init__(self, feed=self.feed, text_query=text_query, - params=params, categories=categories) diff --git a/gdata/analytics/contentforshopping/__init__.py b/gdata/analytics/contentforshopping/__init__.py deleted file mode 100644 index 5a5ed8e1dd..0000000000 --- a/gdata/analytics/contentforshopping/__init__.py +++ /dev/null @@ -1,21 +0,0 @@ -#!/usr/bin/python -# -# Copyright (C) 2010-2011 Google 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. - - -"""Support for the Content API for Shopping - -See: http://code.google.com/apis/shopping/content/index.html -""" diff --git a/gdata/analytics/contentforshopping/client.py b/gdata/analytics/contentforshopping/client.py deleted file mode 100644 index 815201e87a..0000000000 --- a/gdata/analytics/contentforshopping/client.py +++ /dev/null @@ -1,237 +0,0 @@ -#!/usr/bin/python -# -# Copyright (C) 2010-2011 Google 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. - - -"""Extend the gdata client for the Content API for Shopping. - -TODO: - -1. Proper MCA Support. -2. Better datafeed Support. -""" - - -__author__ = 'afshar (Ali Afshar)' - - -import gdata.client -import atom.data - -from gdata.contentforshopping.data import (ProductEntry, ProductFeed, - DatafeedFeed, ClientAccountFeed, ClientAccount) - - -CFS_VERSION = 'v1' -CFS_HOST = 'content.googleapis.com' -CFS_URI = 'https://%s/content' % CFS_HOST -CFS_PROJECTION = 'generic' - - -class ContentForShoppingClient(gdata.client.GDClient): - """Client for Content for Shopping API. - - :param account_id: Merchant account ID. This value will be used by default - for all requests, but may be overridden on a - request-by-request basis. - :param api_version: The version of the API to target. Default value: 'v1'. - :param **kwargs: Pass all addtional keywords to the GDClient constructor. - """ - - api_version = '1.0' - - def __init__(self, account_id=None, api_version=CFS_VERSION, **kwargs): - self.cfs_account_id = account_id - self.cfs_api_version = api_version - gdata.client.GDClient.__init__(self, **kwargs) - - def _create_uri(self, account_id, resource, path=(), use_projection=True): - """Create a request uri from the given arguments. - - If arguments are None, use the default client attributes. - """ - account_id = account_id or self.cfs_account_id - if account_id is None: - raise ValueError('No Account ID set. ' - 'Either set for the client, or per request') - segments = [CFS_URI, self.cfs_api_version, account_id, resource] - if use_projection: - segments.append(CFS_PROJECTION) - segments.extend(path) - return '/'.join(segments) - - def _create_product_id(self, id, country, language): - return 'online:%s:%s:%s' % (language, country, id) - - def _create_batch_feed(self, entries, operation, feed=None): - if feed is None: - feed = ProductFeed() - for entry in entries: - entry.batch_operation = gdata.data.BatchOperation(type=operation) - feed.entry.append(entry) - return feed - - def get_products(self, start_index=None, max_results=None, account_id=None, - auth_token=None): - """Get a feed of products for the account. - - :param max_results: The maximum number of results to return (default 25, - maximum 250). - :param start_index: The starting index of the feed to return (default 1, - maximum 10000) - :param account_id: The Merchant Center Account ID. If ommitted the default - Account ID will be used for this client - """ - uri = self._create_uri(account_id, 'items/products') - return self.get_feed(uri, auth_token=auth_token, - desired_class=gdata.contentforshopping.data.ProductFeed) - - def get_product(self, id, country, language, account_id=None, - auth_token=None): - """Get a product by id, country and language. - - :param id: The product ID - :param country: The country (target_country) - :param language: The language (content_language) - """ - pid = self._create_product_id(id, country, language) - uri = self._create_uri(account_id, 'items/products', [pid]) - return self.get_entry(uri, desired_class=ProductEntry, - auth_token=auth_token) - - def insert_product(self, product, account_id=None, auth_token=None): - """Create a new product, by posting the product entry feed. - - :param product: A :class:`gdata.contentforshopping.data.ProductEntry` with - the required product data. - :param account_id: The Merchant Center Account ID. If ommitted the default - Account ID will be used for this client - """ - uri = self._create_uri(account_id, 'items/products') - return self.post(product, uri=uri, auth_token=auth_token) - - def insert_products(self, products, account_id=None, auth_token=None): - """Insert the products using a batch request - - :param products: A list of product entries - """ - feed = self._create_batch_feed(products, 'insert') - return self.batch(feed) - - def delete_products(self, products, account_id=None, auth_token=None): - """Delete the products using a batch request. - - :param products: A list of product entries - - .. note:: Entries must have the atom:id element set. - """ - feed = self._create_batch_feed(products, 'delete') - return self.batch(feed) - - def update_products(self, products, account_id=None, auth_token=None): - """Update the products using a batch request - - :param products: A list of product entries - - .. note:: Entries must have the atom:id element set. - """ - feed = self._create_batch_feed(products, 'update') - return self.batch(feed) - - def batch(self, feed, account_id=None, auth_token=None): - """Send a batch request. - - :param feed: The feed of batch entries to send. - :param account_id: The Merchant Center Account ID. If ommitted the default - Account ID will be used for this client - """ - uri = self._create_uri(account_id, 'items/products', ['batch']) - return self.post(feed, uri=uri, auth_token=auth_token, - desired_class=ProductFeed) - - def update_product(self, product, account_id=None, - auth_token=None): - """Update a product, by putting the product entry feed. - - :param product: A :class:`gdata.contentforshopping.data.ProductEntry` with - the required product data. - :param account_id: The Merchant Center Account ID. If ommitted the default - Account ID will be used for this client - """ - pid = self._create_product_id(product.id.text, product.target_country.text, - product.content_language.text) - uri = self._create_uri(account_id, 'items/products', [pid]) - return self.update(product, uri=uri, auth_token=auth_token) - - def get_datafeeds(self, account_id=None): - """Get the feed of datafeeds. - """ - uri = self._create_uri(account_id, 'datafeeds/products', - use_projection=False) - return self.get_feed(uri, desired_class=DatafeedFeed) - - def insert_datafeed(self, entry, account_id=None, auth_token=None): - """Insert a datafeed. - """ - uri = self._create_uri(account_id, 'datafeeds/products', - use_projection=False) - return self.post(entry, uri=uri, auth_token=auth_token) - - def get_client_accounts(self, account_id=None, auth_token=None): - """Get the feed of managed accounts - - :param account_id: The Merchant Center Account ID. If ommitted the default - Account ID will be used for this client - """ - uri = self._create_uri(account_id, 'managedaccounts/products', - use_projection=False) - return self.get_feed(uri, desired_class=ClientAccountFeed, - auth_token=auth_token) - - def insert_client_account(self, entry, account_id=None, auth_token=None): - """Insert a client account entry - - :param entry: An entry of type ClientAccount - :param account_id: The Merchant Center Account ID. If ommitted the default - Account ID will be used for this client - """ - uri = self._create_uri(account_id, 'managedaccounts/products', - use_projection=False) - return self.post(entry, uri=uri, auth_token=auth_token) - - def update_client_account(self, entry, client_account_id, account_id=None, auth_token=None): - """Update a client account - - :param entry: An entry of type ClientAccount to update to - :param client_account_id: The client account ID - :param account_id: The Merchant Center Account ID. If ommitted the default - Account ID will be used for this client - """ - uri = self._create_uri(account_id, 'managedaccounts/products', - [client_account_id], use_projection=False) - return self.update(entry, uri=uri, auth_token=auth_token) - - def delete_client_account(self, client_account_id, account_id=None, - auth_token=None): - """Delete a client account - - :param client_account_id: The client account ID - :param account_id: The Merchant Center Account ID. If ommitted the default - Account ID will be used for this client - """ - - uri = self._create_uri(account_id, 'managedaccounts/products', - [client_account_id], use_projection=False) - return self.delete(uri, auth_token=auth_token) diff --git a/gdata/analytics/contentforshopping/data.py b/gdata/analytics/contentforshopping/data.py deleted file mode 100644 index 2f09a42d28..0000000000 --- a/gdata/analytics/contentforshopping/data.py +++ /dev/null @@ -1,1175 +0,0 @@ -#!/usr/bin/python -# -# Copyright (C) 2010-2011 Google 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. - - -"""GData definitions for Content API for Shopping""" - - -__author__ = 'afshar (Ali Afshar)' - - -import atom.core -import atom.data -import gdata.data - - -SC_NAMESPACE_TEMPLATE = ('{http://schemas.google.com/' - 'structuredcontent/2009}%s') -SCP_NAMESPACE_TEMPLATE = ('{http://schemas.google.com/' - 'structuredcontent/2009/products}%s') - - -class ProductId(atom.core.XmlElement): - """sc:id element - - It is required that all inserted products are provided with a unique - alphanumeric ID, in this element. - """ - _qname = SC_NAMESPACE_TEMPLATE % 'id' - - -class RequiredDestination(atom.core.XmlElement): - """sc:required_destination element - - This element defines the required destination for a product, namely - "ProductSearch", "ProductAds" or "CommerceSearch". It should be added to the - app:control element (ProductEntry's "control" attribute) to specify where the - product should appear in search APIs. - - By default, when omitted, the api attempts to upload to as many destinations - as possible. - """ - _qname = SC_NAMESPACE_TEMPLATE % 'required_destination' - dest = 'dest' - - -class ExcludedDestination(atom.core.XmlElement): - """sc:excluded_destination element - - This element defines the required destination for a product, namely - "ProductSearch", "ProductAds" or "CommerceSearch". It should be added to the - app:control element (ProductEntry's "control" attribute) to specify where the - product should not appear in search APIs. - - By default, when omitted, the api attempts to upload to as many destinations - as possible. - """ - _qname = SC_NAMESPACE_TEMPLATE % 'excluded_destination' - dest = 'dest' - - -class ProductControl(atom.data.Control): - """ - app:control element - - overridden to provide additional elements in the sc namespace. - """ - required_destination = RequiredDestination - excluded_destination = ExcludedDestination - - -class ContentLanguage(atom.core.XmlElement): - """ - sc:content_language element - - Language used in the item content for the product - """ - _qname = SC_NAMESPACE_TEMPLATE % 'content_language' - - -class TargetCountry(atom.core.XmlElement): - """ - sc:target_country element - - The target country of the product - """ - _qname = SC_NAMESPACE_TEMPLATE % 'target_country' - - -class ImageLink(atom.core.XmlElement): - """sc:image_link element - - This is the URL of an associated image for a product. Please use full size - images (400x400 pixels or larger), not thumbnails. - """ - _qname = SC_NAMESPACE_TEMPLATE % 'image_link' - - -class ExpirationDate(atom.core.XmlElement): - """sc:expiration_date - - This is the date when the product listing will expire. If omitted, this will - default to 30 days after the product was created. - """ - _qname = SC_NAMESPACE_TEMPLATE % 'expiration_date' - - -class Adult(atom.core.XmlElement): - """sc:adult element - - Indicates whether the content is targeted towards adults, with possible - values of "true" or "false". Defaults to "false". - """ - _qname = SC_NAMESPACE_TEMPLATE % 'adult' - - -class Author(atom.core.XmlElement): - """ - scp:author element - - Defines the author of the information, recommended for books. - """ - _qname = SCP_NAMESPACE_TEMPLATE % 'author' - - -class Availability(atom.core.XmlElement): - """ - scp:availability element - - The retailer's suggested label for product availability. Supported values - include: 'in stock', 'out of stock', 'limited availability'. - """ - _qname = SCP_NAMESPACE_TEMPLATE % 'availability' - - -class Brand(atom.core.XmlElement): - """ - scp:brand element - - The brand of the product - """ - _qname = SCP_NAMESPACE_TEMPLATE % 'brand' - - -class Color(atom.core.XmlElement): - """scp:color element - - The color of the product. - """ - _qname = SCP_NAMESPACE_TEMPLATE % 'color' - - -class Condition(atom.core.XmlElement): - """scp:condition element - - The condition of the product, one of "new", "used", "refurbished" - """ - _qname = SCP_NAMESPACE_TEMPLATE % 'condition' - - -class Edition(atom.core.XmlElement): - """scp:edition element - - The edition of the product. Recommended for products with multiple editions - such as collectors' editions etc, such as books. - """ - _qname = SCP_NAMESPACE_TEMPLATE % 'edition' - - -class Feature(atom.core.XmlElement): - """scp:feature element - - A product feature. A product may have multiple features, each being text, for - example a smartphone may have features: "wifi", "gps" etc. - """ - _qname = SCP_NAMESPACE_TEMPLATE % 'feature' - - -class FeaturedProduct(atom.core.XmlElement): - """scp:featured_product element - - Used to indicate that this item is a special, featured product; Supported - values are: "true", "false". - """ - _qname = SCP_NAMESPACE_TEMPLATE % 'featured_product' - - -class Genre(atom.core.XmlElement): - """scp:genre element - - Describes the genre of a product, eg "comedy". Strongly recommended for media. - """ - _qname = SCP_NAMESPACE_TEMPLATE % 'genre' - - -class Gtin(atom.core.XmlElement): - """scp:gtin element - - GTIN of the product (isbn/upc/ean) - """ - _qname = SCP_NAMESPACE_TEMPLATE % 'gtin' - - -class Manufacturer(atom.core.XmlElement): - """scp:manufacturer element - - Manufacturer of the product. - """ - _qname = SCP_NAMESPACE_TEMPLATE % 'manufacturer' - - -class Mpn(atom.core.XmlElement): - """scp:mpn element - - Manufacturer's Part Number. A unique code determined by the manufacturer for - the product. - """ - _qname = SCP_NAMESPACE_TEMPLATE % 'mpn' - - -class Price(atom.core.XmlElement): - """scp:price element - - The price of the product. The unit attribute must be set, and should represent - the currency. - - Note: Required Element - """ - _qname = SCP_NAMESPACE_TEMPLATE % 'price' - unit = 'unit' - - -class ProductType(atom.core.XmlElement): - """scp:product_type element - - Describes the type of product. A taxonomy of available product types is - listed at http://www.google.com/basepages/producttype/taxonomy.txt and the - entire line in the taxonomy should be included, for example "Electronics > - Video > Projectors". - """ - _qname = SCP_NAMESPACE_TEMPLATE % 'product_type' - - -class Quantity(atom.core.XmlElement): - """scp:quantity element - - The number of items available. A value of 0 indicates items that are - currently out of stock. - """ - _qname = SCP_NAMESPACE_TEMPLATE % 'quantity' - - -class ShippingCountry(atom.core.XmlElement): - """scp:shipping_country element - - The two-letter ISO 3166 country code for the country to which an item will - ship. - - This element should be placed inside the scp:shipping element. - """ - _qname = SCP_NAMESPACE_TEMPLATE % 'shipping_country' - - -class ShippingPrice(atom.core.XmlElement): - """scp:shipping_price element - - Fixed shipping price, represented as a number. Specify the currency as the - "unit" attribute". - - This element should be placed inside the scp:shipping element. - """ - _qname = SCP_NAMESPACE_TEMPLATE % 'shipping_price' - unit = 'unit' - - -class ShippingRegion(atom.core.XmlElement): - """scp:shipping_region element - - The geographic region to which a shipping rate applies, e.g., in the US, the - two-letter state abbreviation, ZIP code, or ZIP code range using * wildcard. - - This element should be placed inside the scp:shipping element. - """ - _qname = SCP_NAMESPACE_TEMPLATE % 'shipping_region' - - -class ShippingService(atom.core.XmlElement): - """scp:shipping_service element - - A free-form description of the service class or delivery speed. - - This element should be placed inside the scp:shipping element. - """ - _qname = SCP_NAMESPACE_TEMPLATE % 'shipping_service' - - -class Shipping(atom.core.XmlElement): - """scp:shipping element - - Container for the shipping rules as provided by the shipping_country, - shipping_price, shipping_region and shipping_service tags. - """ - _qname = SCP_NAMESPACE_TEMPLATE % 'shipping' - shipping_price = ShippingPrice - shipping_country = ShippingCountry - shipping_service = ShippingService - shipping_region = ShippingRegion - - -class ShippingWeight(atom.core.XmlElement): - """scp:shipping_weight element - - The shipping weight of a product. Requires a value and a unit using the unit - attribute. Valid units include lb, pound, oz, ounce, g, gram, kg, kilogram. - """ - _qname = SCP_NAMESPACE_TEMPLATE % 'shipping_weight' - unit = 'unit' - - -class Size(atom.core.XmlElement): - """scp:size element - - Available sizes of an item. Appropriate values include: "small", "medium", - "large", etc. The product enttry may contain multiple sizes, to indicate the - available sizes. - """ - _qname = SCP_NAMESPACE_TEMPLATE % 'size' - - -class TaxRate(atom.core.XmlElement): - """scp:tax_rate element - - The tax rate as a percent of the item price, i.e., number, as a percentage. - - This element should be placed inside the scp:tax (Tax class) element. - """ - _qname = SCP_NAMESPACE_TEMPLATE % 'tax_rate' - - -class TaxCountry(atom.core.XmlElement): - """scp:tax_country element - - The country an item is taxed in (as a two-letter ISO 3166 country code). - - This element should be placed inside the scp:tax (Tax class) element. - """ - _qname = SCP_NAMESPACE_TEMPLATE % 'tax_country' - - -class TaxRegion(atom.core.XmlElement): - """scp:tax_region element - - The geographic region that a tax rate applies to, e.g., in the US, the - two-letter state abbreviation, ZIP code, or ZIP code range using * wildcard. - - This element should be placed inside the scp:tax (Tax class) element. - """ - _qname = SCP_NAMESPACE_TEMPLATE % 'tax_region' - - -class TaxShip(atom.core.XmlElement): - """scp:tax_ship element - - Whether tax is charged on shipping for this product. The default value is - "false". - - This element should be placed inside the scp:tax (Tax class) element. - """ - _qname = SCP_NAMESPACE_TEMPLATE % 'tax_ship' - - -class Tax(atom.core.XmlElement): - """scp:tax element - - Container for the tax rules for this product. Containing the tax_rate, - tax_country, tax_region, and tax_ship elements - """ - _qname = SCP_NAMESPACE_TEMPLATE % 'tax' - tax_rate = TaxRate - tax_country = TaxCountry - tax_region = TaxRegion - tax_ship = TaxShip - - -class Year(atom.core.XmlElement): - """scp:year element - - The year the product was produced. Expects four digits - """ - _qname = SCP_NAMESPACE_TEMPLATE % 'year' - - -class ProductEntry(gdata.data.BatchEntry): - """Product entry containing product information - - The elements of this entry that are used are made up of five different - namespaces. They are: - - atom: - Atom - app: - Atom Publishing Protocol - gd: - Google Data API - sc: - Content API for Shopping, general attributes - scp: - Content API for Shopping, product attributes - - Only the sc and scp namespace elements are defined here, but additional useful - elements are defined in superclasses, and are documented here because they are - part of the required Content for Shopping API. - - .. attribute:: title - - The title of the product. - - This should be a :class:`atom.data.Title` element, for example:: - - entry = ProductEntry() - entry.title = atom.data.Title(u'32GB MP3 Player') - - .. attribute:: author - - The author of the product. - - This should be a :class:`Author` element, for example:: - - entry = ProductEntry() - entry.author = atom.data.Author(u'Isaac Asimov') - - .. attribute:: availability - - The avilability of a product. - - This should be an :class:`Availability` instance, for example:: - - entry = ProductEntry() - entry.availability = Availability('in stock') - - .. attribute:: brand - - The brand of a product. - - This should be a :class:`Brand` element, for example:: - - entry = ProductEntry() - entry.brand = Brand(u'Sony') - - .. attribute:: color - - The color of a product. - - This should be a :class:`Color` element, for example:: - - entry = ProductEntry() - entry.color = Color(u'purple') - - .. attribute:: condition - - The condition of a product. - - This should be a :class:`Condition` element, for example:: - - entry = ProductEntry() - entry.condition = Condition(u'new') - - .. attribute:: content_language - - The language for the product. - - This should be a :class:`ContentLanguage` element, for example:: - - entry = ProductEntry() - entry.content_language = ContentLanguage('EN') - - .. attribute:: edition - - The edition of the product. - - This should be a :class:`Edition` element, for example:: - - entry = ProductEntry() - entry.edition = Edition('1') - - .. attribute:: expiration - - The expiration date of this product listing. - - This should be a :class:`ExpirationDate` element, for example:: - - entry = ProductEntry() - entry.expiration_date = ExpirationDate('2011-22-03') - - .. attribute:: feature - - A list of features for this product. - - Each feature should be a :class:`Feature` element, for example:: - - entry = ProductEntry() - entry.feature.append(Feature(u'wifi')) - entry.feature.append(Feature(u'gps')) - - .. attribute:: featured_product - - Whether the product is featured. - - This should be a :class:`FeaturedProduct` element, for example:: - - entry = ProductEntry() - entry.featured_product = FeaturedProduct('true') - - .. attribute:: genre - - The genre of the product. - - This should be a :class:`Genre` element, for example:: - - entry = ProductEntry() - entry.genre = Genre(u'comedy') - - .. attribute:: image_link - - A list of links to images of the product. Each link should be an - :class:`ImageLink` element, for example:: - - entry = ProductEntry() - entry.image_link.append(ImageLink('http://myshop/cdplayer.jpg')) - - .. attribute:: manufacturer - - The manufacturer of the product. - - This should be a :class:`Manufacturer` element, for example:: - - entry = ProductEntry() - entry.manufacturer = Manufacturer('Sony') - - .. attribute:: mpn - - The manufacturer's part number for this product. - - This should be a :class:`Mpn` element, for example:: - - entry = ProductEntry() - entry.mpn = Mpn('cd700199US') - - .. attribute:: price - - The price for this product. - - This should be a :class:`Price` element, including a unit argument to - indicate the currency, for example:: - - entry = ProductEntry() - entry.price = Price('20.00', unit='USD') - - .. attribute:: gtin - - The gtin for this product. - - This should be a :class:`Gtin` element, for example:: - - entry = ProductEntry() - entry.gtin = Gtin('A888998877997') - - .. attribute:: product_type - - The type of product. - - This should be a :class:`ProductType` element, for example:: - - entry = ProductEntry() - entry.product_type = ProductType("Electronics > Video > Projectors") - - .. attribute:: publisher - - The publisher of this product. - - This should be a :class:`Publisher` element, for example:: - - entry = ProductEntry() - entry.publisher = Publisher(u'Oxford University Press') - - .. attribute:: quantity - - The quantity of product available in stock. - - This should be a :class:`Quantity` element, for example:: - - entry = ProductEntry() - entry.quantity = Quantity('100') - - .. attribute:: shipping - - The shipping rules for the product. - - This should be a :class:`Shipping` with the necessary rules embedded as - elements, for example:: - - entry = ProductEntry() - entry.shipping = Shipping() - entry.shipping.shipping_price = ShippingPrice('10.00', unit='USD') - - .. attribute:: shipping_weight - - The shipping weight for this product. - - This should be a :class:`ShippingWeight` element, including a unit parameter - for the unit of weight, for example:: - - entry = ProductEntry() - entry.shipping_weight = ShippingWeight('10.45', unit='kg') - - .. attribute:: size - - A list of the available sizes for this product. - - Each item in this list should be a :class:`Size` element, for example:: - - entry = ProductEntry() - entry.size.append(Size('Small')) - entry.size.append(Size('Medium')) - entry.size.append(Size('Large')) - - .. attribute:: target_country - - The target country for the product. - - This should be a :class:`TargetCountry` element, for example:: - - entry = ProductEntry() - entry.target_country = TargetCountry('US') - - .. attribute:: tax - - The tax rules for this product. - - This should be a :class:`Tax` element, with the tax rule elements embedded - within, for example:: - - entry = ProductEntry() - entry.tax = Tax() - entry.tax.tax_rate = TaxRate('17.5') - - .. attribute:: year - - The year the product was created. - - This should be a :class:`Year` element, for example:: - - entry = ProductEntry() - entry.year = Year('2001') - - - #TODO Document these atom elements which are part of the required API - - <link> - <entry> - <id> - <category> - <content> - <author> - <created> - <updated> -""" - - author = Author - product_id = ProductId - availability = Availability - brand = Brand - color = Color - condition = Condition - content_language = ContentLanguage - edition = Edition - expiration_date = ExpirationDate - feature = [Feature] - featured_product = FeaturedProduct - genre = Genre - image_link = [ImageLink] - manufacturer = Manufacturer - mpn = Mpn - price = Price - gtin = Gtin - product_type = ProductType - quantity = Quantity - shipping = Shipping - shipping_weight = ShippingWeight - size = [Size] - target_country = TargetCountry - tax = Tax - year = Year - control = ProductControl - - -# opensearch needs overriding for wrong version -# see http://code.google.com/p/gdata-python-client/issues/detail?id=483 -class TotalResults(gdata.data.TotalResults): - - _qname = gdata.data.TotalResults._qname[1] - - -class ItemsPerPage(gdata.data.ItemsPerPage): - - _qname = gdata.data.ItemsPerPage._qname[1] - - -class StartIndex(gdata.data.StartIndex): - - _qname = gdata.data.StartIndex._qname[1] - - -class ProductFeed(gdata.data.BatchFeed): - """Represents a feed of a merchant's products.""" - entry = [ProductEntry] - total_results = TotalResults - items_per_page = ItemsPerPage - start_index = StartIndex - - -def build_entry(product_id=None, title=None, content=None, link=None, condition=None, - target_country=None, content_language=None, price=None, - price_unit=None, tax_rate=None, shipping_price=None, - shipping_price_unit=None, image_links=(), expiration_date=None, - adult=None, author=None, brand=None, color=None, edition=None, - features=(), featured_product=None, genre=None, - manufacturer=None, mpn=None, gtin=None, product_type=None, - quantity=None, shipping_country=None, shipping_region=None, - shipping_service=None, shipping_weight=None, - shipping_weight_unit=None, sizes=(), tax_country=None, - tax_region=None, tax_ship=None, year=None, product=None): - """Create a new product with the required attributes. - - This function exists as an alternative constructor to help alleviate the - boilerplate involved in creating product definitions. You may well want to - fine-tune your products after creating them. - - Documentation of each attribute attempts to explain the "long-hand" way of - achieving the same goal. - - :param product_id: The unique ID for this product. - - This is equivalent to creating and setting an product_id element:: - - entry = ProductEntry() - entry.product_id = ProductId(product_id) - - :param title: The title of this product. - - This is equivalent to creating and setting a title element:: - - entry = ProductEntry - entry.title = atom.data.Title(title) - - :param content: The description of this product. - - This is equivalent to creating and setting the content element:: - - entry.content = atom.data.Content(content) - - :param link: The uri of the link to a page describing the product. - - This is equivalent to creating and setting the link element:: - - entry.link = atom.data.Link(href=link, rel='alternate', - type='text/html') - - :param condition: The condition of the product. - - This is equivalent to creating and setting the condition element:: - - entry.condition = Condition(condition) - - :param target_country: The target country of the product - - This is equivalent to creating and setting the target_country element:: - - entry.target_country = TargetCountry(target_country) - - :param content_language: The language of the content - - This is equivalent to creating and setting the content_language element:: - - entry.content_language = ContentLanguage(content_language) - - :param price: The price of the product - - This is equivalent to creating and setting the price element, using the - price_unit parameter as the unit:: - - entry.price = Price(price, unit=price_unit) - - :param price_unit: The price unit of the product - - See price parameter. - - :param tax_rate: The tax rate for this product - - This is equivalent to creating and setting the tax element and its required - children:: - - entry.tax = Tax() - entry.tax.tax_rate = TaxRate(tax_rate) - - :param shipping_price: Thie price of shipping for this product - - This is equivalent to creating and setting the shipping element and its - required children. The unit for the price is taken from the - shipping_price_unit parameter:: - - entry.shipping = Shipping() - entry.shipping.shipping_price = ShippingPrice(shipping_price, - unit=shipping_price_unit) - - :param shipping_price_unit: The unit of the shipping price - - See shipping_price - - :param image_links: A sequence of links for images for this product. - - This is equivalent to creating a single image_link element for each image:: - - for image_link in image_links: - entry.image_link.append(ImageLink(image_link)) - - :param expiration_date: The date that this product listing expires - - This is equivalent to creating and setting an expiration_date element:: - - entry.expiration_date = ExpirationDate(expiration_date) - - :param adult: Whether this product listing contains adult content - - This is equivalent to creating and setting the adult element:: - - entry.adult = Adult(adult) - - :param author: The author of the product - - This is equivalent to creating and setting the author element:: - - entry.author = Author(author) - - :param brand: The brand of the product - - This is equivalent to creating and setting the brand element:: - - entry.brand = Brand(brand) - - :param color: The color of the product - - This is equivalent to creating and setting the color element:: - - entry.color = Color(color) - - :param edition: The edition of the product - - This is equivalent to creating and setting the edition element:: - - entry.edition = Edition('1') - - :param features=(): Features for this product - - Each feature in the provided sequence will create a Feature element in the - entry, equivalent to:: - - for feature in features: - entry.feature.append(Feature(feature))) - - :param featured_product: Whether this product is featured - - This is equivalent to creating and setting the featured_product element:: - - entry.featured_product = FeaturedProduct(featured_product) - - :param genre: The genre of the product - - This is equivalent to creating and setting the genre element:: - - entry.genre = Genre(genre) - - :param manufacturer: The manufacturer of the product - - This is equivalent to creating and setting the manufacturer element:: - - entry.manufacturer = Manufacturer(manufacturer) - - :param mpn: The manufacturer's part number for a product - - This is equivalent to creating and setting the mpn element:: - - entry.mpn = Mpn(mpn) - - :param gtin: The gtin for a product - - This is equivalent to creating and setting the gtin element:: - - entry.gtin = Gtin(gtin) - - :param product_type: The type of a product - - This is equivalent to creating and setting the product_type element:: - - entry.product_type = ProductType(product_type) - - :param quantity: The quantity of the product in stock - - This is equivalent to creating and setting the quantity element:: - - entry.quantity = Quantity(quantity) - - :param shipping_country: The country that this product can be shipped to - - This is equivalent to creating a Shipping element, and creating and setting - the required element within:: - - entry.shipping = Shipping() - entry.shipping.shipping_country = ShippingCountry(shipping_country) - - :param shipping_region: The region that this product can be shipped to - - This is equivalent to creating a Shipping element, and creating and setting - the required element within:: - - entry.shipping = Shipping() - entry.shipping.shipping_region = ShippingRegion(shipping_region) - - :param shipping_service: The service for shipping. - - This is equivalent to creating a Shipping element, and creating and setting - the required element within:: - - entry.shipping = Shipping() - entry.shipping.shipping_service = ShippingRegion(shipping_service) - - :param shipping_weight: The shipping weight of a product - - Along with the shipping_weight_unit, this is equivalent to creating and - setting the shipping_weight element:: - - entry.shipping_weight = ShippingWeight(shipping_weight, - unit=shipping_weight_unit) - - :param shipping_weight_unit: The unit of shipping weight - - See shipping_weight. - - :param: The sizes that are available for this product. - - Each size of a list will add a size element to the entry, like so:: - - for size in sizes: - product.size.append(Size(size)) - - :param tax_country: The country that tax rules will apply - - This is equivalent to creating a Tax element, and creating and setting the - required sub-element:: - - entry.tax = Tax() - entry.tax.tax_country = TaxCountry(tax_country) - - :param tax_region: The region that the tax rule applies in - - This is equivalent to creating a Tax element, and creating and setting the - required sub-element:: - - entry.tax = Tax() - entry.tax.tax_region = TaxRegion(tax_region) - - :param tax_ship: Whether shipping cost is taxable - - This is equivalent to creating a Tax element, and creating and setting the - required sub-element:: - - entry.tax = Tax() - entry.tax.tax_ship = TaxShip(tax_ship) - - :param year: The year the product was created - - This is equivalent to creating and setting a year element:: - - entry.year = Year('2001') - """ - - product = product or ProductEntry() - if product_id is not None: - product.product_id = ProductId(product_id) - if content is not None: - product.content = atom.data.Content(content) - if title is not None: - product.title = atom.data.Title(title) - if condition is not None: - product.condition = Condition(condition) - if price is not None: - product.price = Price(price, unit=price_unit) - if content_language is not None: - product.content_language = ContentLanguage(content_language) - if target_country is not None: - product.target_country = TargetCountry(target_country) - if tax_rate is not None: - product.tax = Tax() - product.tax.tax_rate = TaxRate(tax_rate) - if shipping_price is not None: - if shipping_price_unit is None: - raise ValueError('Must provide shipping_price_unit if ' - 'shipping_price is provided') - product.shipping = Shipping() - product.shipping.shipping_price = ShippingPrice(shipping_price, - unit=shipping_price_unit) - if link is not None: - product.link.append(atom.data.Link(href=link, type='text/html', - rel='alternate')) - for image_link in image_links: - product.image_link.append(ImageLink(image_link)) - if expiration_date is not None: - product.expiration_date = ExpirationDate(expiration_date) - if adult is not None: - product.adult = Adult(adult) - if author is not None: - product.author = Author(author) - if brand is not None: - product.brand = Brand(brand) - if color is not None: - product.color = Color(color) - if edition is not None: - product.edition = Edition(edition) - for feature in features: - product.feature.append(Feature(feature)) - if featured_product is not None: - product.featured_product = FeaturedProduct(featured_product) - if genre is not None: - product.genre = Genre(genre) - if manufacturer is not None: - product.manufacturer = Manufacturer(manufacturer) - if mpn is not None: - product.mpn = Mpn(mpn) - if gtin is not None: - product.gtin = Gtin(gtin) - if product_type is not None: - product.product_type = ProductType(product_type) - if quantity is not None: - product.quantity = Quantity(quantity) - if shipping_country is not None: - product.shipping.shipping_country = ShippingCountry( - shipping_country) - if shipping_region is not None: - product.shipping.shipping_region = ShippingRegion(shipping_region) - if shipping_service is not None: - product.shipping.shipping_service = ShippingService( - shipping_service) - if shipping_weight is not None: - product.shipping_weight = ShippingWeight(shipping_weight) - if shipping_weight_unit is not None: - product.shipping_weight.unit = shipping_weight_unit - for size in sizes: - product.size.append(Size(size)) - if tax_country is not None: - product.tax.tax_country = TaxCountry(tax_country) - if tax_region is not None: - product.tax.tax_region = TaxRegion(tax_region) - if tax_ship is not None: - product.tax.tax_ship = TaxShip(tax_ship) - if year is not None: - product.year = Year(year) - return product - - -class Edited(atom.core.XmlElement): - """sc:edited element - """ - _qname = SC_NAMESPACE_TEMPLATE % 'edited' - - -class AttributeLanguage(atom.core.XmlElement): - """sc:attribute_language element - """ - _qname = SC_NAMESPACE_TEMPLATE % 'attribute_language' - - -class Channel(atom.core.XmlElement): - """sc:channel element - """ - _qname = SC_NAMESPACE_TEMPLATE % 'channel' - - -class FeedFileName(atom.core.XmlElement): - """sc:feed_file_name element - """ - _qname = SC_NAMESPACE_TEMPLATE % 'feed_file_name' - - -class FeedType(atom.core.XmlElement): - """sc:feed_type element - """ - _qname = SC_NAMESPACE_TEMPLATE % 'feed_type' - - -class UseQuotedFields(atom.core.XmlElement): - """sc:use_quoted_fields element - """ - _qname = SC_NAMESPACE_TEMPLATE % 'use_quoted_fields' - - -class FileFormat(atom.core.XmlElement): - """sc:file_format element - """ - _qname = SC_NAMESPACE_TEMPLATE % 'file_format' - use_quoted_fields = UseQuotedFields - format = 'format' - - -class ProcessingStatus(atom.core.XmlElement): - """sc:processing_status element - """ - _qname = SC_NAMESPACE_TEMPLATE % 'processing_status' - - -class DatafeedEntry(gdata.data.GDEntry): - """An entry for a Datafeed - """ - content_language = ContentLanguage - target_country = TargetCountry - feed_file_name = FeedFileName - file_format = FileFormat - attribute_language = AttributeLanguage - processing_status = ProcessingStatus - edited = Edited - feed_type = FeedType - - -class DatafeedFeed(gdata.data.GDFeed): - """A datafeed feed - """ - entry = [DatafeedEntry] - - -class AdultContent(atom.core.XmlElement): - """sc:adult_content element - """ - _qname = SC_NAMESPACE_TEMPLATE % 'adult_content' - - -class InternalId(atom.core.XmlElement): - """sc:internal_id element - """ - _qname = SC_NAMESPACE_TEMPLATE % 'internal_id' - - -class ReviewsUrl(atom.core.XmlElement): - """sc:reviews_url element - """ - _qname = SC_NAMESPACE_TEMPLATE % 'reviews_url' - - -class ClientAccount(gdata.data.GDEntry): - """A multiclient account entry - """ - adult_content = AdultContent - internal_id = InternalId - reviews_url = ReviewsUrl - - -class ClientAccountFeed(gdata.data.GDFeed): - """A multiclient account feed - """ - entry = [ClientAccount] diff --git a/gdata/analytics/core.py b/gdata/analytics/core.py deleted file mode 100644 index 0661ec6a0a..0000000000 --- a/gdata/analytics/core.py +++ /dev/null @@ -1,279 +0,0 @@ -#!/usr/bin/env python -# -# Copyright (C) 2010 Google 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. - - -# This module is used for version 2 of the Google Data APIs. - - -__author__ = 'j.s@google.com (Jeff Scudder)' - - -"""Provides classes and methods for working with JSON-C. - -This module is experimental and subject to backwards incompatible changes. - - Jsonc: Class which represents JSON-C data and provides pythonic member - access which is a bit cleaner than working with plain old dicts. - parse_json: Converts a JSON-C string into a Jsonc object. - jsonc_to_string: Converts a Jsonc object into a string of JSON-C. -""" - - -try: - import simplejson -except ImportError: - try: - # Try to import from django, should work on App Engine - from django.utils import simplejson - except ImportError: - # Should work for Python2.6 and higher. - import json as simplejson - - -def _convert_to_jsonc(x): - """Builds a Jsonc objects which wraps the argument's members.""" - - if isinstance(x, dict): - jsonc_obj = Jsonc() - # Recursively transform all members of the dict. - # When converting a dict, we do not convert _name items into private - # Jsonc members. - for key, value in x.iteritems(): - jsonc_obj._dict[key] = _convert_to_jsonc(value) - return jsonc_obj - elif isinstance(x, list): - # Recursively transform all members of the list. - members = [] - for item in x: - members.append(_convert_to_jsonc(item)) - return members - else: - # Return the base object. - return x - - -def parse_json(json_string): - """Converts a JSON-C string into a Jsonc object. - - Args: - json_string: str or unicode The JSON to be parsed. - - Returns: - A new Jsonc object. - """ - - return _convert_to_jsonc(simplejson.loads(json_string)) - - -def parse_json_file(json_file): - return _convert_to_jsonc(simplejson.load(json_file)) - - -def jsonc_to_string(jsonc_obj): - """Converts a Jsonc object into a string of JSON-C.""" - - return simplejson.dumps(_convert_to_object(jsonc_obj)) - - -def prettify_jsonc(jsonc_obj, indentation=2): - """Converts a Jsonc object to a pretified (intented) JSON string.""" - - return simplejson.dumps(_convert_to_object(jsonc_obj), indent=indentation) - - - -def _convert_to_object(jsonc_obj): - """Creates a new dict or list which has the data in the Jsonc object. - - Used to convert the Jsonc object to a plain old Python object to simplify - conversion to a JSON-C string. - - Args: - jsonc_obj: A Jsonc object to be converted into simple Python objects - (dicts, lists, etc.) - - Returns: - Either a dict, list, or other object with members converted from Jsonc - objects to the corresponding simple Python object. - """ - - if isinstance(jsonc_obj, Jsonc): - plain = {} - for key, value in jsonc_obj._dict.iteritems(): - plain[key] = _convert_to_object(value) - return plain - elif isinstance(jsonc_obj, list): - plain = [] - for item in jsonc_obj: - plain.append(_convert_to_object(item)) - return plain - else: - return jsonc_obj - - -def _to_jsonc_name(member_name): - """Converts a Python style member name to a JSON-C style name. - - JSON-C uses camelCaseWithLower while Python tends to use - lower_with_underscores so this method converts as follows: - - spam becomes spam - spam_and_eggs becomes spamAndEggs - - Args: - member_name: str or unicode The Python syle name which should be - converted to JSON-C style. - - Returns: - The JSON-C style name as a str or unicode. - """ - - characters = [] - uppercase_next = False - for character in member_name: - if character == '_': - uppercase_next = True - elif uppercase_next: - characters.append(character.upper()) - uppercase_next = False - else: - characters.append(character) - return ''.join(characters) - - -class Jsonc(object): - """Represents JSON-C data in an easy to access object format. - - To access the members of a JSON structure which looks like this: - { - "data": { - "totalItems": 800, - "items": [ - { - "content": { - "1": "rtsp://v5.cache3.c.youtube.com/CiILENy.../0/0/0/video.3gp" - }, - "viewCount": 220101, - "commentCount": 22, - "favoriteCount": 201 - } - ] - }, - "apiVersion": "2.0" - } - - You would do the following: - x = gdata.core.parse_json(the_above_string) - # Gives you 800 - x.data.total_items - # Should be 22 - x.data.items[0].comment_count - # The apiVersion is '2.0' - x.api_version - - To create a Jsonc object which would produce the above JSON, you would do: - gdata.core.Jsonc( - api_version='2.0', - data=gdata.core.Jsonc( - total_items=800, - items=[ - gdata.core.Jsonc( - view_count=220101, - comment_count=22, - favorite_count=201, - content={ - '1': ('rtsp://v5.cache3.c.youtube.com' - '/CiILENy.../0/0/0/video.3gp')})])) - or - x = gdata.core.Jsonc() - x.api_version = '2.0' - x.data = gdata.core.Jsonc() - x.data.total_items = 800 - x.data.items = [] - # etc. - - How it works: - The JSON-C data is stored in an internal dictionary (._dict) and the - getattr, setattr, and delattr methods rewrite the name which you provide - to mirror the expected format in JSON-C. (For more details on name - conversion see _to_jsonc_name.) You may also access members using - getitem, setitem, delitem as you would for a dictionary. For example - x.data.total_items is equivalent to x['data']['totalItems'] - (Not all dict methods are supported so if you need something other than - the item operations, then you will want to use the ._dict member). - - You may need to use getitem or the _dict member to access certain - properties in cases where the JSON-C syntax does not map neatly to Python - objects. For example the YouTube Video feed has some JSON like this: - "content": {"1": "rtsp://v5.cache3.c.youtube.com..."...} - You cannot do x.content.1 in Python, so you would use the getitem as - follows: - x.content['1'] - or you could use the _dict member as follows: - x.content._dict['1'] - - If you need to create a new object with such a mapping you could use. - - x.content = gdata.core.Jsonc(_dict={'1': 'rtsp://cache3.c.youtube.com...'}) - """ - - def __init__(self, _dict=None, **kwargs): - json = _dict or {} - for key, value in kwargs.iteritems(): - if key.startswith('_'): - object.__setattr__(self, key, value) - else: - json[_to_jsonc_name(key)] = _convert_to_jsonc(value) - - object.__setattr__(self, '_dict', json) - - def __setattr__(self, name, value): - if name.startswith('_'): - object.__setattr__(self, name, value) - else: - object.__getattribute__( - self, '_dict')[_to_jsonc_name(name)] = _convert_to_jsonc(value) - - def __getattr__(self, name): - if name.startswith('_'): - object.__getattribute__(self, name) - else: - try: - return object.__getattribute__(self, '_dict')[_to_jsonc_name(name)] - except KeyError: - raise AttributeError( - 'No member for %s or [\'%s\']' % (name, _to_jsonc_name(name))) - - - def __delattr__(self, name): - if name.startswith('_'): - object.__delattr__(self, name) - else: - try: - del object.__getattribute__(self, '_dict')[_to_jsonc_name(name)] - except KeyError: - raise AttributeError( - 'No member for %s (or [\'%s\'])' % (name, _to_jsonc_name(name))) - - # For container methods pass-through to the underlying dict. - def __getitem__(self, key): - return self._dict[key] - - def __setitem__(self, key, value): - self._dict[key] = value - - def __delitem__(self, key): - del self._dict[key] diff --git a/gdata/analytics/data.py b/gdata/analytics/data.py deleted file mode 100644 index 3bf1850269..0000000000 --- a/gdata/analytics/data.py +++ /dev/null @@ -1,1219 +0,0 @@ -#!/usr/bin/env python -# -# Copyright (C) 2009 Google 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. - - -# This module is used for version 2 of the Google Data APIs. - - -"""Provides classes and constants for the XML in the Google Data namespace. - -Documentation for the raw XML which these classes represent can be found here: -http://code.google.com/apis/gdata/docs/2.0/elements.html -""" - - -__author__ = 'j.s@google.com (Jeff Scudder)' - - -import os -import atom.core -import atom.data - - -GDATA_TEMPLATE = '{http://schemas.google.com/g/2005}%s' -GD_TEMPLATE = GDATA_TEMPLATE -OPENSEARCH_TEMPLATE_V1 = '{http://a9.com/-/spec/opensearchrss/1.0/}%s' -OPENSEARCH_TEMPLATE_V2 = '{http://a9.com/-/spec/opensearch/1.1/}%s' -BATCH_TEMPLATE = '{http://schemas.google.com/gdata/batch}%s' - - -# Labels used in batch request entries to specify the desired CRUD operation. -BATCH_INSERT = 'insert' -BATCH_UPDATE = 'update' -BATCH_DELETE = 'delete' -BATCH_QUERY = 'query' - -EVENT_LOCATION = 'http://schemas.google.com/g/2005#event' -ALTERNATE_LOCATION = 'http://schemas.google.com/g/2005#event.alternate' -PARKING_LOCATION = 'http://schemas.google.com/g/2005#event.parking' - -CANCELED_EVENT = 'http://schemas.google.com/g/2005#event.canceled' -CONFIRMED_EVENT = 'http://schemas.google.com/g/2005#event.confirmed' -TENTATIVE_EVENT = 'http://schemas.google.com/g/2005#event.tentative' - -CONFIDENTIAL_EVENT = 'http://schemas.google.com/g/2005#event.confidential' -DEFAULT_EVENT = 'http://schemas.google.com/g/2005#event.default' -PRIVATE_EVENT = 'http://schemas.google.com/g/2005#event.private' -PUBLIC_EVENT = 'http://schemas.google.com/g/2005#event.public' - -OPAQUE_EVENT = 'http://schemas.google.com/g/2005#event.opaque' -TRANSPARENT_EVENT = 'http://schemas.google.com/g/2005#event.transparent' - -CHAT_MESSAGE = 'http://schemas.google.com/g/2005#message.chat' -INBOX_MESSAGE = 'http://schemas.google.com/g/2005#message.inbox' -SENT_MESSAGE = 'http://schemas.google.com/g/2005#message.sent' -SPAM_MESSAGE = 'http://schemas.google.com/g/2005#message.spam' -STARRED_MESSAGE = 'http://schemas.google.com/g/2005#message.starred' -UNREAD_MESSAGE = 'http://schemas.google.com/g/2005#message.unread' - -BCC_RECIPIENT = 'http://schemas.google.com/g/2005#message.bcc' -CC_RECIPIENT = 'http://schemas.google.com/g/2005#message.cc' -SENDER = 'http://schemas.google.com/g/2005#message.from' -REPLY_TO = 'http://schemas.google.com/g/2005#message.reply-to' -TO_RECIPIENT = 'http://schemas.google.com/g/2005#message.to' - -ASSISTANT_REL = 'http://schemas.google.com/g/2005#assistant' -CALLBACK_REL = 'http://schemas.google.com/g/2005#callback' -CAR_REL = 'http://schemas.google.com/g/2005#car' -COMPANY_MAIN_REL = 'http://schemas.google.com/g/2005#company_main' -FAX_REL = 'http://schemas.google.com/g/2005#fax' -HOME_REL = 'http://schemas.google.com/g/2005#home' -HOME_FAX_REL = 'http://schemas.google.com/g/2005#home_fax' -ISDN_REL = 'http://schemas.google.com/g/2005#isdn' -MAIN_REL = 'http://schemas.google.com/g/2005#main' -MOBILE_REL = 'http://schemas.google.com/g/2005#mobile' -OTHER_REL = 'http://schemas.google.com/g/2005#other' -OTHER_FAX_REL = 'http://schemas.google.com/g/2005#other_fax' -PAGER_REL = 'http://schemas.google.com/g/2005#pager' -RADIO_REL = 'http://schemas.google.com/g/2005#radio' -TELEX_REL = 'http://schemas.google.com/g/2005#telex' -TTL_TDD_REL = 'http://schemas.google.com/g/2005#tty_tdd' -WORK_REL = 'http://schemas.google.com/g/2005#work' -WORK_FAX_REL = 'http://schemas.google.com/g/2005#work_fax' -WORK_MOBILE_REL = 'http://schemas.google.com/g/2005#work_mobile' -WORK_PAGER_REL = 'http://schemas.google.com/g/2005#work_pager' -NETMEETING_REL = 'http://schemas.google.com/g/2005#netmeeting' -OVERALL_REL = 'http://schemas.google.com/g/2005#overall' -PRICE_REL = 'http://schemas.google.com/g/2005#price' -QUALITY_REL = 'http://schemas.google.com/g/2005#quality' -EVENT_REL = 'http://schemas.google.com/g/2005#event' -EVENT_ALTERNATE_REL = 'http://schemas.google.com/g/2005#event.alternate' -EVENT_PARKING_REL = 'http://schemas.google.com/g/2005#event.parking' - -AIM_PROTOCOL = 'http://schemas.google.com/g/2005#AIM' -MSN_PROTOCOL = 'http://schemas.google.com/g/2005#MSN' -YAHOO_MESSENGER_PROTOCOL = 'http://schemas.google.com/g/2005#YAHOO' -SKYPE_PROTOCOL = 'http://schemas.google.com/g/2005#SKYPE' -QQ_PROTOCOL = 'http://schemas.google.com/g/2005#QQ' -GOOGLE_TALK_PROTOCOL = 'http://schemas.google.com/g/2005#GOOGLE_TALK' -ICQ_PROTOCOL = 'http://schemas.google.com/g/2005#ICQ' -JABBER_PROTOCOL = 'http://schemas.google.com/g/2005#JABBER' - -REGULAR_COMMENTS = 'http://schemas.google.com/g/2005#regular' -REVIEW_COMMENTS = 'http://schemas.google.com/g/2005#reviews' - -MAIL_BOTH = 'http://schemas.google.com/g/2005#both' -MAIL_LETTERS = 'http://schemas.google.com/g/2005#letters' -MAIL_PARCELS = 'http://schemas.google.com/g/2005#parcels' -MAIL_NEITHER = 'http://schemas.google.com/g/2005#neither' - -GENERAL_ADDRESS = 'http://schemas.google.com/g/2005#general' -LOCAL_ADDRESS = 'http://schemas.google.com/g/2005#local' - -OPTIONAL_ATENDEE = 'http://schemas.google.com/g/2005#event.optional' -REQUIRED_ATENDEE = 'http://schemas.google.com/g/2005#event.required' - -ATTENDEE_ACCEPTED = 'http://schemas.google.com/g/2005#event.accepted' -ATTENDEE_DECLINED = 'http://schemas.google.com/g/2005#event.declined' -ATTENDEE_INVITED = 'http://schemas.google.com/g/2005#event.invited' -ATTENDEE_TENTATIVE = 'http://schemas.google.com/g/2005#event.tentative' - -FULL_PROJECTION = 'full' -VALUES_PROJECTION = 'values' -BASIC_PROJECTION = 'basic' - -PRIVATE_VISIBILITY = 'private' -PUBLIC_VISIBILITY = 'public' - -OPAQUE_TRANSPARENCY = 'http://schemas.google.com/g/2005#event.opaque' -TRANSPARENT_TRANSPARENCY = 'http://schemas.google.com/g/2005#event.transparent' - -CONFIDENTIAL_EVENT_VISIBILITY = 'http://schemas.google.com/g/2005#event.confidential' -DEFAULT_EVENT_VISIBILITY = 'http://schemas.google.com/g/2005#event.default' -PRIVATE_EVENT_VISIBILITY = 'http://schemas.google.com/g/2005#event.private' -PUBLIC_EVENT_VISIBILITY = 'http://schemas.google.com/g/2005#event.public' - -CANCELED_EVENT_STATUS = 'http://schemas.google.com/g/2005#event.canceled' -CONFIRMED_EVENT_STATUS = 'http://schemas.google.com/g/2005#event.confirmed' -TENTATIVE_EVENT_STATUS = 'http://schemas.google.com/g/2005#event.tentative' - -ACL_REL = 'http://schemas.google.com/acl/2007#accessControlList' - - -class Error(Exception): - pass - - -class MissingRequiredParameters(Error): - pass - - -class LinkFinder(atom.data.LinkFinder): - """Mixin used in Feed and Entry classes to simplify link lookups by type. - - Provides lookup methods for edit, edit-media, post, ACL and other special - links which are common across Google Data APIs. - """ - - def find_html_link(self): - """Finds the first link with rel of alternate and type of text/html.""" - for link in self.link: - if link.rel == 'alternate' and link.type == 'text/html': - return link.href - return None - - FindHtmlLink = find_html_link - - def get_html_link(self): - for a_link in self.link: - if a_link.rel == 'alternate' and a_link.type == 'text/html': - return a_link - return None - - GetHtmlLink = get_html_link - - def find_post_link(self): - """Get the URL to which new entries should be POSTed. - - The POST target URL is used to insert new entries. - - Returns: - A str for the URL in the link with a rel matching the POST type. - """ - return self.find_url('http://schemas.google.com/g/2005#post') - - FindPostLink = find_post_link - - def get_post_link(self): - return self.get_link('http://schemas.google.com/g/2005#post') - - GetPostLink = get_post_link - - def find_acl_link(self): - acl_link = self.get_acl_link() - if acl_link: - return acl_link.href - - return None - - FindAclLink = find_acl_link - - def get_acl_link(self): - """Searches for a link or feed_link (if present) with the rel for ACL.""" - - acl_link = self.get_link(ACL_REL) - if acl_link: - return acl_link - elif hasattr(self, 'feed_link'): - for a_feed_link in self.feed_link: - if a_feed_link.rel == ACL_REL: - return a_feed_link - - return None - - GetAclLink = get_acl_link - - def find_feed_link(self): - return self.find_url('http://schemas.google.com/g/2005#feed') - - FindFeedLink = find_feed_link - - def get_feed_link(self): - return self.get_link('http://schemas.google.com/g/2005#feed') - - GetFeedLink = get_feed_link - - def find_previous_link(self): - return self.find_url('previous') - - FindPreviousLink = find_previous_link - - def get_previous_link(self): - return self.get_link('previous') - - GetPreviousLink = get_previous_link - - -class TotalResults(atom.core.XmlElement): - """opensearch:TotalResults for a GData feed.""" - _qname = (OPENSEARCH_TEMPLATE_V1 % 'totalResults', - OPENSEARCH_TEMPLATE_V2 % 'totalResults') - - -class StartIndex(atom.core.XmlElement): - """The opensearch:startIndex element in GData feed.""" - _qname = (OPENSEARCH_TEMPLATE_V1 % 'startIndex', - OPENSEARCH_TEMPLATE_V2 % 'startIndex') - - -class ItemsPerPage(atom.core.XmlElement): - """The opensearch:itemsPerPage element in GData feed.""" - _qname = (OPENSEARCH_TEMPLATE_V1 % 'itemsPerPage', - OPENSEARCH_TEMPLATE_V2 % 'itemsPerPage') - - -class ExtendedProperty(atom.core.XmlElement): - """The Google Data extendedProperty element. - - Used to store arbitrary key-value information specific to your - application. The value can either be a text string stored as an XML - attribute (.value), or an XML node (XmlBlob) as a child element. - - This element is used in the Google Calendar data API and the Google - Contacts data API. - """ - _qname = GDATA_TEMPLATE % 'extendedProperty' - name = 'name' - value = 'value' - - def get_xml_blob(self): - """Returns the XML blob as an atom.core.XmlElement. - - Returns: - An XmlElement representing the blob's XML, or None if no - blob was set. - """ - if self._other_elements: - return self._other_elements[0] - else: - return None - - GetXmlBlob = get_xml_blob - - def set_xml_blob(self, blob): - """Sets the contents of the extendedProperty to XML as a child node. - - Since the extendedProperty is only allowed one child element as an XML - blob, setting the XML blob will erase any preexisting member elements - in this object. - - Args: - blob: str or atom.core.XmlElement representing the XML blob stored in - the extendedProperty. - """ - # Erase any existing extension_elements, clears the child nodes from the - # extendedProperty. - if isinstance(blob, atom.core.XmlElement): - self._other_elements = [blob] - else: - self._other_elements = [atom.core.parse(str(blob))] - - SetXmlBlob = set_xml_blob - - -class GDEntry(atom.data.Entry, LinkFinder): - """Extends Atom Entry to provide data processing""" - etag = '{http://schemas.google.com/g/2005}etag' - - def get_id(self): - if self.id is not None and self.id.text is not None: - return self.id.text.strip() - return None - - GetId = get_id - - def is_media(self): - if self.find_edit_media_link(): - return True - return False - - IsMedia = is_media - - def find_media_link(self): - """Returns the URL to the media content, if the entry is a media entry. - Otherwise returns None. - """ - if self.is_media(): - return self.content.src - return None - - FindMediaLink = find_media_link - - -class GDFeed(atom.data.Feed, LinkFinder): - """A Feed from a GData service.""" - etag = '{http://schemas.google.com/g/2005}etag' - total_results = TotalResults - start_index = StartIndex - items_per_page = ItemsPerPage - entry = [GDEntry] - - def get_id(self): - if self.id is not None and self.id.text is not None: - return self.id.text.strip() - return None - - GetId = get_id - - def get_generator(self): - if self.generator and self.generator.text: - return self.generator.text.strip() - return None - - -class BatchId(atom.core.XmlElement): - """Identifies a single operation in a batch request.""" - _qname = BATCH_TEMPLATE % 'id' - - -class BatchOperation(atom.core.XmlElement): - """The CRUD operation which this batch entry represents.""" - _qname = BATCH_TEMPLATE % 'operation' - type = 'type' - - -class BatchStatus(atom.core.XmlElement): - """The batch:status element present in a batch response entry. - - A status element contains the code (HTTP response code) and - reason as elements. In a single request these fields would - be part of the HTTP response, but in a batch request each - Entry operation has a corresponding Entry in the response - feed which includes status information. - - See http://code.google.com/apis/gdata/batch.html#Handling_Errors - """ - _qname = BATCH_TEMPLATE % 'status' - code = 'code' - reason = 'reason' - content_type = 'content-type' - - -class BatchEntry(GDEntry): - """An atom:entry for use in batch requests. - - The BatchEntry contains additional members to specify the operation to be - performed on this entry and a batch ID so that the server can reference - individual operations in the response feed. For more information, see: - http://code.google.com/apis/gdata/batch.html - """ - batch_operation = BatchOperation - batch_id = BatchId - batch_status = BatchStatus - - -class BatchInterrupted(atom.core.XmlElement): - """The batch:interrupted element sent if batch request was interrupted. - - Only appears in a feed if some of the batch entries could not be processed. - See: http://code.google.com/apis/gdata/batch.html#Handling_Errors - """ - _qname = BATCH_TEMPLATE % 'interrupted' - reason = 'reason' - success = 'success' - failures = 'failures' - parsed = 'parsed' - - -class BatchFeed(GDFeed): - """A feed containing a list of batch request entries.""" - interrupted = BatchInterrupted - entry = [BatchEntry] - - def add_batch_entry(self, entry=None, id_url_string=None, - batch_id_string=None, operation_string=None): - """Logic for populating members of a BatchEntry and adding to the feed. - - If the entry is not a BatchEntry, it is converted to a BatchEntry so - that the batch specific members will be present. - - The id_url_string can be used in place of an entry if the batch operation - applies to a URL. For example query and delete operations require just - the URL of an entry, no body is sent in the HTTP request. If an - id_url_string is sent instead of an entry, a BatchEntry is created and - added to the feed. - - This method also assigns the desired batch id to the entry so that it - can be referenced in the server's response. If the batch_id_string is - None, this method will assign a batch_id to be the index at which this - entry will be in the feed's entry list. - - Args: - entry: BatchEntry, atom.data.Entry, or another Entry flavor (optional) - The entry which will be sent to the server as part of the batch - request. The item must have a valid atom id so that the server - knows which entry this request references. - id_url_string: str (optional) The URL of the entry to be acted on. You - can find this URL in the text member of the atom id for an entry. - If an entry is not sent, this id will be used to construct a new - BatchEntry which will be added to the request feed. - batch_id_string: str (optional) The batch ID to be used to reference - this batch operation in the results feed. If this parameter is None, - the current length of the feed's entry array will be used as a - count. Note that batch_ids should either always be specified or - never, mixing could potentially result in duplicate batch ids. - operation_string: str (optional) The desired batch operation which will - set the batch_operation.type member of the entry. Options are - 'insert', 'update', 'delete', and 'query' - - Raises: - MissingRequiredParameters: Raised if neither an id_ url_string nor an - entry are provided in the request. - - Returns: - The added entry. - """ - if entry is None and id_url_string is None: - raise MissingRequiredParameters('supply either an entry or URL string') - if entry is None and id_url_string is not None: - entry = BatchEntry(id=atom.data.Id(text=id_url_string)) - if batch_id_string is not None: - entry.batch_id = BatchId(text=batch_id_string) - elif entry.batch_id is None or entry.batch_id.text is None: - entry.batch_id = BatchId(text=str(len(self.entry))) - if operation_string is not None: - entry.batch_operation = BatchOperation(type=operation_string) - self.entry.append(entry) - return entry - - AddBatchEntry = add_batch_entry - - def add_insert(self, entry, batch_id_string=None): - """Add an insert request to the operations in this batch request feed. - - If the entry doesn't yet have an operation or a batch id, these will - be set to the insert operation and a batch_id specified as a parameter. - - Args: - entry: BatchEntry The entry which will be sent in the batch feed as an - insert request. - batch_id_string: str (optional) The batch ID to be used to reference - this batch operation in the results feed. If this parameter is None, - the current length of the feed's entry array will be used as a - count. Note that batch_ids should either always be specified or - never, mixing could potentially result in duplicate batch ids. - """ - self.add_batch_entry(entry=entry, batch_id_string=batch_id_string, - operation_string=BATCH_INSERT) - - AddInsert = add_insert - - def add_update(self, entry, batch_id_string=None): - """Add an update request to the list of batch operations in this feed. - - Sets the operation type of the entry to insert if it is not already set - and assigns the desired batch id to the entry so that it can be - referenced in the server's response. - - Args: - entry: BatchEntry The entry which will be sent to the server as an - update (HTTP PUT) request. The item must have a valid atom id - so that the server knows which entry to replace. - batch_id_string: str (optional) The batch ID to be used to reference - this batch operation in the results feed. If this parameter is None, - the current length of the feed's entry array will be used as a - count. See also comments for AddInsert. - """ - self.add_batch_entry(entry=entry, batch_id_string=batch_id_string, - operation_string=BATCH_UPDATE) - - AddUpdate = add_update - - def add_delete(self, url_string=None, entry=None, batch_id_string=None): - """Adds a delete request to the batch request feed. - - This method takes either the url_string which is the atom id of the item - to be deleted, or the entry itself. The atom id of the entry must be - present so that the server knows which entry should be deleted. - - Args: - url_string: str (optional) The URL of the entry to be deleted. You can - find this URL in the text member of the atom id for an entry. - entry: BatchEntry (optional) The entry to be deleted. - batch_id_string: str (optional) - - Raises: - MissingRequiredParameters: Raised if neither a url_string nor an entry - are provided in the request. - """ - self.add_batch_entry(entry=entry, id_url_string=url_string, - batch_id_string=batch_id_string, operation_string=BATCH_DELETE) - - AddDelete = add_delete - - def add_query(self, url_string=None, entry=None, batch_id_string=None): - """Adds a query request to the batch request feed. - - This method takes either the url_string which is the query URL - whose results will be added to the result feed. The query URL will - be encapsulated in a BatchEntry, and you may pass in the BatchEntry - with a query URL instead of sending a url_string. - - Args: - url_string: str (optional) - entry: BatchEntry (optional) - batch_id_string: str (optional) - - Raises: - MissingRequiredParameters - """ - self.add_batch_entry(entry=entry, id_url_string=url_string, - batch_id_string=batch_id_string, operation_string=BATCH_QUERY) - - AddQuery = add_query - - def find_batch_link(self): - return self.find_url('http://schemas.google.com/g/2005#batch') - - FindBatchLink = find_batch_link - - -class EntryLink(atom.core.XmlElement): - """The gd:entryLink element. - - Represents a logically nested entry. For example, a <gd:who> - representing a contact might have a nested entry from a contact feed. - """ - _qname = GDATA_TEMPLATE % 'entryLink' - entry = GDEntry - rel = 'rel' - read_only = 'readOnly' - href = 'href' - - -class FeedLink(atom.core.XmlElement): - """The gd:feedLink element. - - Represents a logically nested feed. For example, a calendar feed might - have a nested feed representing all comments on entries. - """ - _qname = GDATA_TEMPLATE % 'feedLink' - feed = GDFeed - rel = 'rel' - read_only = 'readOnly' - count_hint = 'countHint' - href = 'href' - - -class AdditionalName(atom.core.XmlElement): - """The gd:additionalName element. - - Specifies additional (eg. middle) name of the person. - Contains an attribute for the phonetic representaton of the name. - """ - _qname = GDATA_TEMPLATE % 'additionalName' - yomi = 'yomi' - - -class Comments(atom.core.XmlElement): - """The gd:comments element. - - Contains a comments feed for the enclosing entry (such as a calendar event). - """ - _qname = GDATA_TEMPLATE % 'comments' - rel = 'rel' - feed_link = FeedLink - - -class Country(atom.core.XmlElement): - """The gd:country element. - - Country name along with optional country code. The country code is - given in accordance with ISO 3166-1 alpha-2: - http://www.iso.org/iso/iso-3166-1_decoding_table - """ - _qname = GDATA_TEMPLATE % 'country' - code = 'code' - - -class EmailImParent(atom.core.XmlElement): - address = 'address' - label = 'label' - rel = 'rel' - primary = 'primary' - - -class Email(EmailImParent): - """The gd:email element. - - An email address associated with the containing entity (which is - usually an entity representing a person or a location). - """ - _qname = GDATA_TEMPLATE % 'email' - display_name = 'displayName' - - -class FamilyName(atom.core.XmlElement): - """The gd:familyName element. - - Specifies family name of the person, eg. "Smith". - """ - _qname = GDATA_TEMPLATE % 'familyName' - yomi = 'yomi' - - -class Im(EmailImParent): - """The gd:im element. - - An instant messaging address associated with the containing entity. - """ - _qname = GDATA_TEMPLATE % 'im' - protocol = 'protocol' - - -class GivenName(atom.core.XmlElement): - """The gd:givenName element. - - Specifies given name of the person, eg. "John". - """ - _qname = GDATA_TEMPLATE % 'givenName' - yomi = 'yomi' - - -class NamePrefix(atom.core.XmlElement): - """The gd:namePrefix element. - - Honorific prefix, eg. 'Mr' or 'Mrs'. - """ - _qname = GDATA_TEMPLATE % 'namePrefix' - - -class NameSuffix(atom.core.XmlElement): - """The gd:nameSuffix element. - - Honorific suffix, eg. 'san' or 'III'. - """ - _qname = GDATA_TEMPLATE % 'nameSuffix' - - -class FullName(atom.core.XmlElement): - """The gd:fullName element. - - Unstructured representation of the name. - """ - _qname = GDATA_TEMPLATE % 'fullName' - - -class Name(atom.core.XmlElement): - """The gd:name element. - - Allows storing person's name in a structured way. Consists of - given name, additional name, family name, prefix, suffix and full name. - """ - _qname = GDATA_TEMPLATE % 'name' - given_name = GivenName - additional_name = AdditionalName - family_name = FamilyName - name_prefix = NamePrefix - name_suffix = NameSuffix - full_name = FullName - - -class OrgDepartment(atom.core.XmlElement): - """The gd:orgDepartment element. - - Describes a department within an organization. Must appear within a - gd:organization element. - """ - _qname = GDATA_TEMPLATE % 'orgDepartment' - - -class OrgJobDescription(atom.core.XmlElement): - """The gd:orgJobDescription element. - - Describes a job within an organization. Must appear within a - gd:organization element. - """ - _qname = GDATA_TEMPLATE % 'orgJobDescription' - - -class OrgName(atom.core.XmlElement): - """The gd:orgName element. - - The name of the organization. Must appear within a gd:organization - element. - - Contains a Yomigana attribute (Japanese reading aid) for the - organization name. - """ - _qname = GDATA_TEMPLATE % 'orgName' - yomi = 'yomi' - - -class OrgSymbol(atom.core.XmlElement): - """The gd:orgSymbol element. - - Provides a symbol of an organization. Must appear within a - gd:organization element. - """ - _qname = GDATA_TEMPLATE % 'orgSymbol' - - -class OrgTitle(atom.core.XmlElement): - """The gd:orgTitle element. - - The title of a person within an organization. Must appear within a - gd:organization element. - """ - _qname = GDATA_TEMPLATE % 'orgTitle' - - -class Organization(atom.core.XmlElement): - """The gd:organization element. - - An organization, typically associated with a contact. - """ - _qname = GDATA_TEMPLATE % 'organization' - label = 'label' - primary = 'primary' - rel = 'rel' - department = OrgDepartment - job_description = OrgJobDescription - name = OrgName - symbol = OrgSymbol - title = OrgTitle - - -class When(atom.core.XmlElement): - """The gd:when element. - - Represents a period of time or an instant. - """ - _qname = GDATA_TEMPLATE % 'when' - end = 'endTime' - start = 'startTime' - value = 'valueString' - - -class OriginalEvent(atom.core.XmlElement): - """The gd:originalEvent element. - - Equivalent to the Recurrence ID property specified in section 4.8.4.4 - of RFC 2445. Appears in every instance of a recurring event, to identify - the original event. - - Contains a <gd:when> element specifying the original start time of the - instance that has become an exception. - """ - _qname = GDATA_TEMPLATE % 'originalEvent' - id = 'id' - href = 'href' - when = When - - -class PhoneNumber(atom.core.XmlElement): - """The gd:phoneNumber element. - - A phone number associated with the containing entity (which is usually - an entity representing a person or a location). - """ - _qname = GDATA_TEMPLATE % 'phoneNumber' - label = 'label' - rel = 'rel' - uri = 'uri' - primary = 'primary' - - -class PostalAddress(atom.core.XmlElement): - """The gd:postalAddress element.""" - _qname = GDATA_TEMPLATE % 'postalAddress' - label = 'label' - rel = 'rel' - uri = 'uri' - primary = 'primary' - - -class Rating(atom.core.XmlElement): - """The gd:rating element. - - Represents a numeric rating of the enclosing entity, such as a - comment. Each rating supplies its own scale, although it may be - normalized by a service; for example, some services might convert all - ratings to a scale from 1 to 5. - """ - _qname = GDATA_TEMPLATE % 'rating' - average = 'average' - max = 'max' - min = 'min' - num_raters = 'numRaters' - rel = 'rel' - value = 'value' - - -class Recurrence(atom.core.XmlElement): - """The gd:recurrence element. - - Represents the dates and times when a recurring event takes place. - - The string that defines the recurrence consists of a set of properties, - each of which is defined in the iCalendar standard (RFC 2445). - - Specifically, the string usually begins with a DTSTART property that - indicates the starting time of the first instance of the event, and - often a DTEND property or a DURATION property to indicate when the - first instance ends. Next come RRULE, RDATE, EXRULE, and/or EXDATE - properties, which collectively define a recurring event and its - exceptions (but see below). (See section 4.8.5 of RFC 2445 for more - information about these recurrence component properties.) Last comes a - VTIMEZONE component, providing detailed timezone rules for any timezone - ID mentioned in the preceding properties. - - Google services like Google Calendar don't generally generate EXRULE - and EXDATE properties to represent exceptions to recurring events; - instead, they generate <gd:recurrenceException> elements. However, - Google services may include EXRULE and/or EXDATE properties anyway; - for example, users can import events and exceptions into Calendar, and - if those imported events contain EXRULE or EXDATE properties, then - Calendar will provide those properties when it sends a <gd:recurrence> - element. - - Note the the use of <gd:recurrenceException> means that you can't be - sure just from examining a <gd:recurrence> element whether there are - any exceptions to the recurrence description. To ensure that you find - all exceptions, look for <gd:recurrenceException> elements in the feed, - and use their <gd:originalEvent> elements to match them up with - <gd:recurrence> elements. - """ - _qname = GDATA_TEMPLATE % 'recurrence' - - -class RecurrenceException(atom.core.XmlElement): - """The gd:recurrenceException element. - - Represents an event that's an exception to a recurring event-that is, - an instance of a recurring event in which one or more aspects of the - recurring event (such as attendance list, time, or location) have been - changed. - - Contains a <gd:originalEvent> element that specifies the original - recurring event that this event is an exception to. - - When you change an instance of a recurring event, that instance becomes - an exception. Depending on what change you made to it, the exception - behaves in either of two different ways when the original recurring - event is changed: - - - If you add, change, or remove comments, attendees, or attendee - responses, then the exception remains tied to the original event, and - changes to the original event also change the exception. - - If you make any other changes to the exception (such as changing the - time or location) then the instance becomes "specialized," which means - that it's no longer as tightly tied to the original event. If you - change the original event, specialized exceptions don't change. But - see below. - - For example, say you have a meeting every Tuesday and Thursday at - 2:00 p.m. If you change the attendance list for this Thursday's meeting - (but not for the regularly scheduled meeting), then it becomes an - exception. If you change the time for this Thursday's meeting (but not - for the regularly scheduled meeting), then it becomes specialized. - - Regardless of whether an exception is specialized or not, if you do - something that deletes the instance that the exception was derived from, - then the exception is deleted. Note that changing the day or time of a - recurring event deletes all instances, and creates new ones. - - For example, after you've specialized this Thursday's meeting, say you - change the recurring meeting to happen on Monday, Wednesday, and Friday. - That change deletes all of the recurring instances of the - Tuesday/Thursday meeting, including the specialized one. - - If a particular instance of a recurring event is deleted, then that - instance appears as a <gd:recurrenceException> containing a - <gd:entryLink> that has its <gd:eventStatus> set to - "http://schemas.google.com/g/2005#event.canceled". (For more - information about canceled events, see RFC 2445.) - """ - _qname = GDATA_TEMPLATE % 'recurrenceException' - specialized = 'specialized' - entry_link = EntryLink - original_event = OriginalEvent - - -class Reminder(atom.core.XmlElement): - """The gd:reminder element. - - A time interval, indicating how long before the containing entity's start - time or due time attribute a reminder should be issued. Alternatively, - may specify an absolute time at which a reminder should be issued. Also - specifies a notification method, indicating what medium the system - should use to remind the user. - """ - _qname = GDATA_TEMPLATE % 'reminder' - absolute_time = 'absoluteTime' - method = 'method' - days = 'days' - hours = 'hours' - minutes = 'minutes' - - -class Transparency(atom.core.XmlElement): - """The gd:transparency element: - - Extensible enum corresponding to the TRANSP property defined in RFC 244. - """ - _qname = GDATA_TEMPLATE % 'transparency' - value = 'value' - - -class Agent(atom.core.XmlElement): - """The gd:agent element. - - The agent who actually receives the mail. Used in work addresses. - Also for 'in care of' or 'c/o'. - """ - _qname = GDATA_TEMPLATE % 'agent' - - -class HouseName(atom.core.XmlElement): - """The gd:housename element. - - Used in places where houses or buildings have names (and not - necessarily numbers), eg. "The Pillars". - """ - _qname = GDATA_TEMPLATE % 'housename' - - -class Street(atom.core.XmlElement): - """The gd:street element. - - Can be street, avenue, road, etc. This element also includes the - house number and room/apartment/flat/floor number. - """ - _qname = GDATA_TEMPLATE % 'street' - - -class PoBox(atom.core.XmlElement): - """The gd:pobox element. - - Covers actual P.O. boxes, drawers, locked bags, etc. This is usually - but not always mutually exclusive with street. - """ - _qname = GDATA_TEMPLATE % 'pobox' - - -class Neighborhood(atom.core.XmlElement): - """The gd:neighborhood element. - - This is used to disambiguate a street address when a city contains more - than one street with the same name, or to specify a small place whose - mail is routed through a larger postal town. In China it could be a - county or a minor city. - """ - _qname = GDATA_TEMPLATE % 'neighborhood' - - -class City(atom.core.XmlElement): - """The gd:city element. - - Can be city, village, town, borough, etc. This is the postal town and - not necessarily the place of residence or place of business. - """ - _qname = GDATA_TEMPLATE % 'city' - - -class Subregion(atom.core.XmlElement): - """The gd:subregion element. - - Handles administrative districts such as U.S. or U.K. counties that are - not used for mail addressing purposes. Subregion is not intended for - delivery addresses. - """ - _qname = GDATA_TEMPLATE % 'subregion' - - -class Region(atom.core.XmlElement): - """The gd:region element. - - A state, province, county (in Ireland), Land (in Germany), - departement (in France), etc. - """ - _qname = GDATA_TEMPLATE % 'region' - - -class Postcode(atom.core.XmlElement): - """The gd:postcode element. - - Postal code. Usually country-wide, but sometimes specific to the - city (e.g. "2" in "Dublin 2, Ireland" addresses). - """ - _qname = GDATA_TEMPLATE % 'postcode' - - -class Country(atom.core.XmlElement): - """The gd:country element. - - The name or code of the country. - """ - _qname = GDATA_TEMPLATE % 'country' - - -class FormattedAddress(atom.core.XmlElement): - """The gd:formattedAddress element. - - The full, unstructured postal address. - """ - _qname = GDATA_TEMPLATE % 'formattedAddress' - - -class StructuredPostalAddress(atom.core.XmlElement): - """The gd:structuredPostalAddress element. - - Postal address split into components. It allows to store the address - in locale independent format. The fields can be interpreted and used - to generate formatted, locale dependent address. The following elements - reperesent parts of the address: agent, house name, street, P.O. box, - neighborhood, city, subregion, region, postal code, country. The - subregion element is not used for postal addresses, it is provided for - extended uses of addresses only. In order to store postal address in an - unstructured form formatted address field is provided. - """ - _qname = GDATA_TEMPLATE % 'structuredPostalAddress' - rel = 'rel' - mail_class = 'mailClass' - usage = 'usage' - label = 'label' - primary = 'primary' - agent = Agent - house_name = HouseName - street = Street - po_box = PoBox - neighborhood = Neighborhood - city = City - subregion = Subregion - region = Region - postcode = Postcode - country = Country - formatted_address = FormattedAddress - - -class Where(atom.core.XmlElement): - """The gd:where element. - - A place (such as an event location) associated with the containing - entity. The type of the association is determined by the rel attribute; - the details of the location are contained in an embedded or linked-to - Contact entry. - - A <gd:where> element is more general than a <gd:geoPt> element. The - former identifies a place using a text description and/or a Contact - entry, while the latter identifies a place using a specific geographic - location. - """ - _qname = GDATA_TEMPLATE % 'where' - label = 'label' - rel = 'rel' - value = 'valueString' - entry_link = EntryLink - - -class AttendeeType(atom.core.XmlElement): - """The gd:attendeeType element.""" - _qname = GDATA_TEMPLATE % 'attendeeType' - value = 'value' - - -class AttendeeStatus(atom.core.XmlElement): - """The gd:attendeeStatus element.""" - _qname = GDATA_TEMPLATE % 'attendeeStatus' - value = 'value' - - -class EventStatus(atom.core.XmlElement): - """The gd:eventStatus element.""" - _qname = GDATA_TEMPLATE % 'eventStatus' - value = 'value' - - -class Visibility(atom.core.XmlElement): - """The gd:visibility element.""" - _qname = GDATA_TEMPLATE % 'visibility' - value = 'value' - - -class Who(atom.core.XmlElement): - """The gd:who element. - - A person associated with the containing entity. The type of the - association is determined by the rel attribute; the details about the - person are contained in an embedded or linked-to Contact entry. - - The <gd:who> element can be used to specify email senders and - recipients, calendar event organizers, and so on. - """ - _qname = GDATA_TEMPLATE % 'who' - email = 'email' - rel = 'rel' - value = 'valueString' - attendee_status = AttendeeStatus - attendee_type = AttendeeType - entry_link = EntryLink - - -class Deleted(atom.core.XmlElement): - """gd:deleted when present, indicates the containing entry is deleted.""" - _qname = GD_TEMPLATE % 'deleted' - - -class Money(atom.core.XmlElement): - """Describes money""" - _qname = GD_TEMPLATE % 'money' - amount = 'amount' - currency_code = 'currencyCode' - - -class MediaSource(object): - """GData Entries can refer to media sources, so this class provides a - place to store references to these objects along with some metadata. - """ - - def __init__(self, file_handle=None, content_type=None, content_length=None, - file_path=None, file_name=None): - """Creates an object of type MediaSource. - - Args: - file_handle: A file handle pointing to the file to be encapsulated in the - MediaSource. - content_type: string The MIME type of the file. Required if a file_handle - is given. - content_length: int The size of the file. Required if a file_handle is - given. - file_path: string (optional) A full path name to the file. Used in - place of a file_handle. - file_name: string The name of the file without any path information. - Required if a file_handle is given. - """ - self.file_handle = file_handle - self.content_type = content_type - self.content_length = content_length - self.file_name = file_name - - if (file_handle is None and content_type is not None and - file_path is not None): - self.set_file_handle(file_path, content_type) - - def set_file_handle(self, file_name, content_type): - """A helper function which can create a file handle from a given filename - and set the content type and length all at once. - - Args: - file_name: string The path and file name to the file containing the media - content_type: string A MIME type representing the type of the media - """ - - self.file_handle = open(file_name, 'rb') - self.content_type = content_type - self.content_length = os.path.getsize(file_name) - self.file_name = os.path.basename(file_name) - - SetFileHandle = set_file_handle - - def modify_request(self, http_request): - http_request.add_body_part(self.file_handle, self.content_type, - self.content_length) - return http_request - - ModifyRequest = modify_request diff --git a/gdata/analytics/docs/__init__.py b/gdata/analytics/docs/__init__.py deleted file mode 100644 index 8031bc9b7a..0000000000 --- a/gdata/analytics/docs/__init__.py +++ /dev/null @@ -1,269 +0,0 @@ -#!/usr/bin/python -# -# Copyright 2009 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. - -"""Contains extensions to Atom objects used with Google Documents.""" - -__author__ = ('api.jfisher (Jeff Fisher), ' - 'api.eric@google.com (Eric Bidelman)') - -import atom -import gdata - - -DOCUMENTS_NAMESPACE = 'http://schemas.google.com/docs/2007' - - -class Scope(atom.AtomBase): - """The DocList ACL scope element""" - - _tag = 'scope' - _namespace = gdata.GACL_NAMESPACE - _children = atom.AtomBase._children.copy() - _attributes = atom.AtomBase._attributes.copy() - _attributes['value'] = 'value' - _attributes['type'] = 'type' - - def __init__(self, value=None, type=None, extension_elements=None, - extension_attributes=None, text=None): - self.value = value - self.type = type - self.text = text - self.extension_elements = extension_elements or [] - self.extension_attributes = extension_attributes or {} - - -class Role(atom.AtomBase): - """The DocList ACL role element""" - - _tag = 'role' - _namespace = gdata.GACL_NAMESPACE - _children = atom.AtomBase._children.copy() - _attributes = atom.AtomBase._attributes.copy() - _attributes['value'] = 'value' - - def __init__(self, value=None, extension_elements=None, - extension_attributes=None, text=None): - self.value = value - self.text = text - self.extension_elements = extension_elements or [] - self.extension_attributes = extension_attributes or {} - - -class FeedLink(atom.AtomBase): - """The DocList gd:feedLink element""" - - _tag = 'feedLink' - _namespace = gdata.GDATA_NAMESPACE - _attributes = atom.AtomBase._attributes.copy() - _attributes['rel'] = 'rel' - _attributes['href'] = 'href' - - def __init__(self, href=None, rel=None, text=None, extension_elements=None, - extension_attributes=None): - self.href = href - self.rel = rel - atom.AtomBase.__init__(self, extension_elements=extension_elements, - extension_attributes=extension_attributes, text=text) - - -class ResourceId(atom.AtomBase): - """The DocList gd:resourceId element""" - - _tag = 'resourceId' - _namespace = gdata.GDATA_NAMESPACE - _children = atom.AtomBase._children.copy() - _attributes = atom.AtomBase._attributes.copy() - _attributes['value'] = 'value' - - def __init__(self, value=None, extension_elements=None, - extension_attributes=None, text=None): - self.value = value - self.text = text - self.extension_elements = extension_elements or [] - self.extension_attributes = extension_attributes or {} - - -class LastModifiedBy(atom.Person): - """The DocList gd:lastModifiedBy element""" - - _tag = 'lastModifiedBy' - _namespace = gdata.GDATA_NAMESPACE - - -class LastViewed(atom.Person): - """The DocList gd:lastViewed element""" - - _tag = 'lastViewed' - _namespace = gdata.GDATA_NAMESPACE - - -class WritersCanInvite(atom.AtomBase): - """The DocList docs:writersCanInvite element""" - - _tag = 'writersCanInvite' - _namespace = DOCUMENTS_NAMESPACE - _attributes = atom.AtomBase._attributes.copy() - _attributes['value'] = 'value' - - -class DocumentListEntry(gdata.GDataEntry): - """The Google Documents version of an Atom Entry""" - - _tag = gdata.GDataEntry._tag - _namespace = atom.ATOM_NAMESPACE - _children = gdata.GDataEntry._children.copy() - _attributes = gdata.GDataEntry._attributes.copy() - _children['{%s}feedLink' % gdata.GDATA_NAMESPACE] = ('feedLink', FeedLink) - _children['{%s}resourceId' % gdata.GDATA_NAMESPACE] = ('resourceId', - ResourceId) - _children['{%s}lastModifiedBy' % gdata.GDATA_NAMESPACE] = ('lastModifiedBy', - LastModifiedBy) - _children['{%s}lastViewed' % gdata.GDATA_NAMESPACE] = ('lastViewed', - LastViewed) - _children['{%s}writersCanInvite' % DOCUMENTS_NAMESPACE] = ( - 'writersCanInvite', WritersCanInvite) - - def __init__(self, resourceId=None, feedLink=None, lastViewed=None, - lastModifiedBy=None, writersCanInvite=None, author=None, - category=None, content=None, atom_id=None, link=None, - published=None, title=None, updated=None, text=None, - extension_elements=None, extension_attributes=None): - self.feedLink = feedLink - self.lastViewed = lastViewed - self.lastModifiedBy = lastModifiedBy - self.resourceId = resourceId - self.writersCanInvite = writersCanInvite - gdata.GDataEntry.__init__( - self, author=author, category=category, content=content, - atom_id=atom_id, link=link, published=published, title=title, - updated=updated, extension_elements=extension_elements, - extension_attributes=extension_attributes, text=text) - - def GetAclLink(self): - """Extracts the DocListEntry's <gd:feedLink>. - - Returns: - A FeedLink object. - """ - return self.feedLink - - def GetDocumentType(self): - """Extracts the type of document from the DocListEntry. - - This method returns the type of document the DocListEntry - represents. Possible values are document, presentation, - spreadsheet, folder, or pdf. - - Returns: - A string representing the type of document. - """ - if self.category: - for category in self.category: - if category.scheme == gdata.GDATA_NAMESPACE + '#kind': - return category.label - else: - return None - - -def DocumentListEntryFromString(xml_string): - """Converts an XML string into a DocumentListEntry object. - - Args: - xml_string: string The XML describing a Document List feed entry. - - Returns: - A DocumentListEntry object corresponding to the given XML. - """ - return atom.CreateClassFromXMLString(DocumentListEntry, xml_string) - - -class DocumentListAclEntry(gdata.GDataEntry): - """A DocList ACL Entry flavor of an Atom Entry""" - - _tag = gdata.GDataEntry._tag - _namespace = gdata.GDataEntry._namespace - _children = gdata.GDataEntry._children.copy() - _attributes = gdata.GDataEntry._attributes.copy() - _children['{%s}scope' % gdata.GACL_NAMESPACE] = ('scope', Scope) - _children['{%s}role' % gdata.GACL_NAMESPACE] = ('role', Role) - - def __init__(self, category=None, atom_id=None, link=None, - title=None, updated=None, scope=None, role=None, - extension_elements=None, extension_attributes=None, text=None): - gdata.GDataEntry.__init__(self, author=None, category=category, - content=None, atom_id=atom_id, link=link, - published=None, title=title, - updated=updated, text=None) - self.scope = scope - self.role = role - - -def DocumentListAclEntryFromString(xml_string): - """Converts an XML string into a DocumentListAclEntry object. - - Args: - xml_string: string The XML describing a Document List ACL feed entry. - - Returns: - A DocumentListAclEntry object corresponding to the given XML. - """ - return atom.CreateClassFromXMLString(DocumentListAclEntry, xml_string) - - -class DocumentListFeed(gdata.GDataFeed): - """A feed containing a list of Google Documents Items""" - - _tag = gdata.GDataFeed._tag - _namespace = atom.ATOM_NAMESPACE - _children = gdata.GDataFeed._children.copy() - _attributes = gdata.GDataFeed._attributes.copy() - _children['{%s}entry' % atom.ATOM_NAMESPACE] = ('entry', - [DocumentListEntry]) - - -def DocumentListFeedFromString(xml_string): - """Converts an XML string into a DocumentListFeed object. - - Args: - xml_string: string The XML describing a DocumentList feed. - - Returns: - A DocumentListFeed object corresponding to the given XML. - """ - return atom.CreateClassFromXMLString(DocumentListFeed, xml_string) - - -class DocumentListAclFeed(gdata.GDataFeed): - """A DocList ACL feed flavor of a Atom feed""" - - _tag = gdata.GDataFeed._tag - _namespace = atom.ATOM_NAMESPACE - _children = gdata.GDataFeed._children.copy() - _attributes = gdata.GDataFeed._attributes.copy() - _children['{%s}entry' % atom.ATOM_NAMESPACE] = ('entry', - [DocumentListAclEntry]) - - -def DocumentListAclFeedFromString(xml_string): - """Converts an XML string into a DocumentListAclFeed object. - - Args: - xml_string: string The XML describing a DocumentList feed. - - Returns: - A DocumentListFeed object corresponding to the given XML. - """ - return atom.CreateClassFromXMLString(DocumentListAclFeed, xml_string) diff --git a/gdata/analytics/docs/client.py b/gdata/analytics/docs/client.py deleted file mode 100644 index 8a84ccbd49..0000000000 --- a/gdata/analytics/docs/client.py +++ /dev/null @@ -1,1027 +0,0 @@ -#!/usr/bin/python -# -# Copyright 2011 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. - -"""DocsClient simplifies interactions with the Documents List API.""" - -__author__ = 'vicfryzel@google.com (Vic Fryzel)' - -import copy -import mimetypes -import re -import urllib -import atom.data -import atom.http_core -import gdata.client -import gdata.docs.data -import gdata.gauth - - -# Feed URIs that are given by the API, but cannot be obtained without -# making a mostly unnecessary HTTP request. -RESOURCE_FEED_URI = '/feeds/default/private/full' -RESOURCE_UPLOAD_URI = '/feeds/upload/create-session/default/private/full' -COLLECTION_UPLOAD_URI_TEMPLATE = \ - '/feeds/upload/create-session/feeds/default/private/full/%s/contents' -ARCHIVE_FEED_URI = '/feeds/default/private/archive' -METADATA_URI = '/feeds/metadata/default' -CHANGE_FEED_URI = '/feeds/default/private/changes' - - -class DocsClient(gdata.client.GDClient): - """Client for all features of the Google Documents List API.""" - - host = 'docs.google.com' - api_version = '3.0' - auth_service = 'writely' - alt_auth_service = 'wise' - alt_auth_token = None - auth_scopes = gdata.gauth.AUTH_SCOPES['writely'] - ssl = True - - def request(self, method=None, uri=None, **kwargs): - """Add support for imitating other users via 2-Legged OAuth. - - Args: - uri: (optional) URI of the request in which to replace default with - self.xoauth_requestor_id. - Returns: - Result of super(DocsClient, self).request(). - """ - if self.xoauth_requestor_id is not None and uri is not None: - if isinstance(uri, (str, unicode)): - uri = atom.http_core.Uri.parse_uri(uri) - uri.path.replace('/default', '/%s' % self.xoauth_requestor_id) - return super(DocsClient, self).request(method=method, uri=uri, **kwargs) - - Request = request - - def get_metadata(self, **kwargs): - """Retrieves the metadata of a user account. - - Args: - kwargs: Other parameters to pass to self.get_entry(). - - Returns: - gdata.docs.data.Metadata representing metadata of user's account. - """ - return self.get_entry( - METADATA_URI, desired_class=gdata.docs.data.Metadata, **kwargs) - - GetMetadata = get_metadata - - def get_changes(self, changestamp=None, max_results=None, **kwargs): - """Retrieves changes to a user's documents list. - - Args: - changestamp: (optional) String changestamp value to query since. - If provided, returned changes will have a changestamp larger than - the given one. - max_results: (optional) Number of results to fetch. API will limit - this number to 100 at most. - kwargs: Other parameters to pass to self.get_feed(). - - Returns: - gdata.docs.data.ChangeFeed. - """ - uri = atom.http_core.Uri.parse_uri(CHANGE_FEED_URI) - - if changestamp is not None: - uri.query['start-index'] = changestamp - if max_results is not None: - uri.query['max-results'] = max_results - - return self.get_feed( - uri, desired_class=gdata.docs.data.ChangeFeed, **kwargs) - - GetChanges = get_changes - - def get_resources(self, uri=None, limit=None, **kwargs): - """Retrieves the resources in a user's docslist, or the given URI. - - Args: - uri: (optional) URI to query for resources. If None, then - gdata.docs.client.DocsClient.RESOURCE_FEED_URI is used, which will - query for all non-collections. - limit: int (optional) A maximum cap for the number of results to - return in the feed. By default, the API returns a maximum of 100 - per page. Thus, if you set limit=5000, you will get <= 5000 - documents (guarenteed no more than 5000), and will need to follow the - feed's next links (feed.GetNextLink()) to the rest. See - get_everything(). Similarly, if you set limit=50, only <= 50 - documents are returned. Note: if the max-results parameter is set in - the uri parameter, it is chosen over a value set for limit. - kwargs: Other parameters to pass to self.get_feed(). - - Returns: - gdata.docs.data.ResourceFeed feed. - """ - if uri is None: - uri = RESOURCE_FEED_URI - - if isinstance(uri, basestring): - uri = atom.http_core.Uri.parse_uri(uri) - - # Add max-results param if it wasn't included in the uri. - if limit is not None and not 'max-results' in uri.query: - uri.query['max-results'] = limit - - return self.get_feed(uri, desired_class=gdata.docs.data.ResourceFeed, - **kwargs) - - GetResources = get_resources - - def get_all_resources(self, uri=None, **kwargs): - """Retrieves all of a user's non-collections or everything at the given URI. - - Folders are not included in this by default. Pass in a custom URI to - include collections in your query. The DocsQuery class is an easy way to - generate such a URI. - - This method makes multiple HTTP requests (by following the feed's next - links) in order to fetch the user's entire document list. - - Args: - uri: (optional) URI to query the doclist feed with. If None, then use - DocsClient.RESOURCE_FEED_URI, which will retrieve all - non-collections. - kwargs: Other parameters to pass to self.GetResources(). - - Returns: - List of gdata.docs.data.Resource objects representing the retrieved - entries. - """ - if uri is None: - uri = RESOURCE_FEED_URI - - feed = self.GetResources(uri=uri, **kwargs) - entries = feed.entry - - while feed.GetNextLink() is not None: - feed = self.GetResources(feed.GetNextLink().href, **kwargs) - entries.extend(feed.entry) - - return entries - - GetAllResources = get_all_resources - - def get_resource(self, entry, **kwargs): - """Retrieves a resource again given its entry. - - Args: - entry: gdata.docs.data.Resource to fetch and return. - kwargs: Other args to pass to GetResourceBySelfLink(). - Returns: - gdata.docs.data.Resource representing retrieved resource. - """ - - return self.GetResourceBySelfLink(entry.GetSelfLink().href, **kwargs) - - GetResource = get_resource - - def get_resource_by_self_link(self, self_link, etag=None, **kwargs): - """Retrieves a particular resource by its self link. - - Args: - self_link: URI at which to query for given resource. This can be found - using entry.GetSelfLink(). - etag: str (optional) The document/item's etag value to be used in a - conditional GET. See http://code.google.com/apis/documents/docs/3.0/ - developers_guide_protocol.html#RetrievingCached. - kwargs: Other parameters to pass to self.get_entry(). - - Returns: - gdata.docs.data.Resource representing the retrieved resource. - """ - if isinstance(self_link, atom.data.Link): - self_link = self_link.href - - return self.get_entry( - self_link, etag=etag, desired_class=gdata.docs.data.Resource, **kwargs) - - GetResourceBySelfLink = get_resource_by_self_link - - def get_resource_acl(self, entry, **kwargs): - """Retrieves the ACL sharing permissions for the given entry. - - Args: - entry: gdata.docs.data.Resource for which to get ACL. - kwargs: Other parameters to pass to self.get_feed(). - - Returns: - gdata.docs.data.AclFeed representing the resource's ACL. - """ - self._check_entry_is_resource(entry) - return self.get_feed(entry.GetAclFeedLink().href, - desired_class=gdata.docs.data.AclFeed, **kwargs) - - GetResourceAcl = get_resource_acl - - def create_resource(self, entry, media=None, collection=None, - create_uri=None, **kwargs): - """Creates new entries in Google Docs, and uploads their contents. - - Args: - entry: gdata.docs.data.Resource representing initial version - of entry being created. If media is also provided, the entry will - first be created with the given metadata and content. - media: (optional) gdata.data.MediaSource containing the file to be - uploaded. - collection: (optional) gdata.docs.data.Resource representing a collection - in which this new entry should be created. If provided along - with create_uri, create_uri will win (e.g. entry will be created at - create_uri, not necessarily in given collection). - create_uri: (optional) String URI at which to create the given entry. If - collection, media and create_uri are None, use - gdata.docs.client.RESOURCE_FEED_URI. If collection and create_uri are - None, use gdata.docs.client.RESOURCE_UPLOAD_URI. If collection and - media are not None, - gdata.docs.client.COLLECTION_UPLOAD_URI_TEMPLATE is used, - with the collection's resource ID substituted in. - kwargs: Other parameters to pass to self.post() and self.update(). - - Returns: - gdata.docs.data.Resource containing information about new entry. - """ - if media is not None: - if create_uri is None and collection is not None: - create_uri = COLLECTION_UPLOAD_URI_TEMPLATE % \ - collection.resource_id.text - elif create_uri is None: - create_uri = RESOURCE_UPLOAD_URI - uploader = gdata.client.ResumableUploader( - self, media.file_handle, media.content_type, media.content_length, - desired_class=gdata.docs.data.Resource) - return uploader.upload_file(create_uri, entry, **kwargs) - else: - if create_uri is None and collection is not None: - create_uri = collection.content.src - elif create_uri is None: - create_uri = RESOURCE_FEED_URI - return self.post( - entry, create_uri, desired_class=gdata.docs.data.Resource, **kwargs) - - CreateResource = create_resource - - def update_resource(self, entry, media=None, update_metadata=True, - new_revision=False, **kwargs): - """Updates an entry in Google Docs with new metadata and/or new data. - - Args: - entry: Entry to update. Make any metadata changes to this entry. - media: (optional) gdata.data.MediaSource object containing the file with - which to replace the entry's data. - update_metadata: (optional) True to update the metadata from the entry - itself. You might set this to False to only update an entry's - file content, and not its metadata. - new_revision: (optional) True to create a new revision with this update, - False otherwise. - kwargs: Other parameters to pass to self.post(). - - Returns: - gdata.docs.data.Resource representing the updated entry. - """ - - uri_params = {} - if new_revision: - uri_params['new-revision'] = 'true' - - if update_metadata and media is None: - uri = atom.http_core.parse_uri(entry.GetEditLink().href) - uri.query.update(uri_params) - return super(DocsClient, self).update(entry, **kwargs) - else: - uploader = gdata.client.ResumableUploader( - self, media.file_handle, media.content_type, media.content_length, - desired_class=gdata.docs.data.Resource) - return uploader.UpdateFile(entry_or_resumable_edit_link=entry, - update_metadata=update_metadata, - uri_params=uri_params, **kwargs) - - UpdateResource = update_resource - - def download_resource(self, entry, file_path, extra_params=None, **kwargs): - """Downloads the contents of the given entry to disk. - - Note: to download a file in memory, use the DownloadResourceToMemory() - method. - - Args: - entry: gdata.docs.data.Resource whose contents to fetch. - file_path: str Full path to which to save file. - extra_params: dict (optional) A map of any further parameters to control - how the document is downloaded/exported. For example, exporting a - spreadsheet as a .csv: extra_params={'gid': 0, 'exportFormat': 'csv'} - kwargs: Other parameters to pass to self._download_file(). - - Raises: - gdata.client.RequestError if the download URL is malformed or the server's - response was not successful. - """ - self._check_entry_is_not_collection(entry) - uri = self._get_download_uri(entry.content.src, extra_params) - self._download_file(uri, file_path, **kwargs) - - DownloadResource = download_resource - - def download_resource_to_memory(self, entry, extra_params=None, **kwargs): - """Returns the contents of the given entry. - - Args: - entry: gdata.docs.data.Resource whose contents to fetch. - extra_params: dict (optional) A map of any further parameters to control - how the document is downloaded/exported. For example, exporting a - spreadsheet as a .csv: extra_params={'gid': 0, 'exportFormat': 'csv'} - kwargs: Other parameters to pass to self._get_content(). - - Returns: - Content of given resource after being downloaded. - - Raises: - gdata.client.RequestError if the download URL is malformed or the server's - response was not successful. - """ - self._check_entry_is_not_collection(entry) - uri = self._get_download_uri(entry.content.src, extra_params) - return self._get_content(uri, **kwargs) - - DownloadResourceToMemory = download_resource_to_memory - - def _get_download_uri(self, base_uri, extra_params=None): - uri = base_uri.replace('&', '&') - if extra_params is not None: - if 'exportFormat' in extra_params and '/Export?' not in uri: - raise gdata.client.Error, ('This entry type cannot be exported ' - 'as a different format.') - - if 'gid' in extra_params and uri.find('spreadsheets') == -1: - raise gdata.client.Error, 'gid param is not valid for this resource type.' - - uri += '&' + urllib.urlencode(extra_params) - return uri - - def _get_content(self, uri, extra_params=None, auth_token=None, **kwargs): - """Fetches the given resource's content. - - This method is useful for downloading/exporting a file within enviornments - like Google App Engine, where the user may not have the ability to write - the file to a local disk. - - Be warned, this method will use as much memory as needed to store the - fetched content. This could cause issues in your environment or app. This - is only different from Download() in that you will probably retain an - open reference to the data returned from this method, where as the data - from Download() will be immediately written to disk and the memory - freed. This client library currently doesn't support reading server - responses into a buffer or yielding an open file pointer to responses. - - Args: - entry: Resource to fetch. - extra_params: dict (optional) A map of any further parameters to control - how the document is downloaded/exported. For example, exporting a - spreadsheet as a .csv: extra_params={'gid': 0, 'exportFormat': 'csv'} - auth_token: (optional) gdata.gauth.ClientLoginToken, AuthSubToken, or - OAuthToken which authorizes this client to edit the user's data. - kwargs: Other parameters to pass to self.request(). - - Returns: - The binary file content. - - Raises: - gdata.client.RequestError: on error response from server. - """ - server_response = None - token = auth_token - if 'spreadsheets' in uri and token is None \ - and self.alt_auth_token is not None: - token = self.alt_auth_token - server_response = self.request( - 'GET', uri, auth_token=token, **kwargs) - if server_response.status != 200: - raise gdata.client.RequestError, {'status': server_response.status, - 'reason': server_response.reason, - 'body': server_response.read()} - return server_response.read() - - def _download_file(self, uri, file_path, **kwargs): - """Downloads a file to disk from the specified URI. - - Note: to download a file in memory, use the GetContent() method. - - Args: - uri: str The full URL to download the file from. - file_path: str The full path to save the file to. - kwargs: Other parameters to pass to self.get_content(). - - Raises: - gdata.client.RequestError: on error response from server. - """ - f = open(file_path, 'wb') - try: - f.write(self._get_content(uri, **kwargs)) - except gdata.client.RequestError, e: - f.close() - raise e - f.flush() - f.close() - - _DownloadFile = _download_file - - def copy_resource(self, entry, title, **kwargs): - """Copies the given entry to a new entry with the given title. - - Note: Files do not support this feature. - - Args: - entry: gdata.docs.data.Resource to copy. - title: String title for the new entry. - kwargs: Other parameters to pass to self.post(). - - Returns: - gdata.docs.data.Resource representing duplicated resource. - """ - self._check_entry_is_resource(entry) - new_entry = gdata.docs.data.Resource( - title=atom.data.Title(text=title), - id=atom.data.Id(text=entry.GetSelfLink().href)) - return self.post(new_entry, RESOURCE_FEED_URI, **kwargs) - - CopyResource = copy_resource - - def move_resource(self, entry, collection=None, keep_in_collections=False, - **kwargs): - """Moves an item into a different collection (or out of all collections). - - Args: - entry: gdata.docs.data.Resource to move. - collection: gdata.docs.data.Resource (optional) An object representing - the destination collection. If None, set keep_in_collections to - False to remove the item from all collections. - keep_in_collections: boolean (optional) If True, the given entry - is not removed from any existing collections it is already in. - kwargs: Other parameters to pass to self.post(). - - Returns: - gdata.docs.data.Resource of the moved entry. - """ - self._check_entry_is_resource(entry) - - # Remove the item from any collections it is already in. - if not keep_in_collections: - for collection in entry.InCollections(): - uri = '%s/contents/%s' % ( - collection.href, - urllib.quote(entry.resource_id.text)) - self.delete(uri, force=True) - - if collection is not None: - self._check_entry_is_collection(collection) - entry = self.post(entry, collection.content.src, **kwargs) - return entry - - MoveResource = move_resource - - def delete_resource(self, entry, permanent=False, **kwargs): - """Trashes or deletes the given entry. - - Args: - entry: gdata.docs.data.Resource to trash or delete. - permanent: True to skip the trash and delete the entry forever. - kwargs: Other args to pass to gdata.client.GDClient.Delete() - - Returns: - Result of delete request. - """ - uri = entry.GetEditLink().href - if permanent: - uri += '?delete=true' - return super(DocsClient, self).delete(uri, **kwargs) - - DeleteResource = delete_resource - - def _check_entry_is_resource(self, entry): - """Ensures given entry is a gdata.docs.data.Resource. - - Args: - entry: Entry to test. - Raises: - ValueError: If given entry is not a resource. - """ - if not isinstance(entry, gdata.docs.data.Resource): - raise ValueError('%s is not a gdata.docs.data.Resource' % str(entry)) - - def _check_entry_is_collection(self, entry): - """Ensures given entry is a collection. - - Args: - entry: Entry to test. - Raises: - ValueError: If given entry is a collection. - """ - self._check_entry_is_resource(entry) - if entry.get_resource_type() != gdata.docs.data.COLLECTION_LABEL: - raise ValueError('%s is not a collection' % str(entry)) - - def _check_entry_is_not_collection(self, entry): - """Ensures given entry is not a collection. - - Args: - entry: Entry to test. - Raises: - ValueError: If given entry is a collection. - """ - try: - self._check_entry_is_resource(entry) - except ValueError: - return - if entry.get_resource_type() == gdata.docs.data.COLLECTION_LABEL: - raise ValueError( - '%s is a collection, which is not valid in this method' % str(entry)) - - def get_acl_entry(self, entry, **kwargs): - """Retrieves an AclEntry again. - - This is useful if you need to poll for an ACL changing. - - Args: - entry: gdata.docs.data.AclEntry to fetch and return. - kwargs: Other args to pass to GetAclEntryBySelfLink(). - Returns: - gdata.docs.data.AclEntry representing retrieved entry. - """ - - return self.GetAclEntryBySelfLink(entry.GetSelfLink().href, **kwargs) - - GetAclEntry = get_acl_entry - - def get_acl_entry_by_self_link(self, self_link, **kwargs): - """Retrieves a particular AclEntry by its self link. - - Args: - self_link: URI at which to query for given ACL entry. This can be found - using entry.GetSelfLink(). - kwargs: Other parameters to pass to self.get_entry(). - - Returns: - gdata.docs.data.AclEntry representing the retrieved entry. - """ - if isinstance(self_link, atom.data.Link): - self_link = self_link.href - - return self.get_entry(self_link, desired_class=gdata.docs.data.AclEntry, - **kwargs) - - GetAclEntryBySelfLink = get_acl_entry_by_self_link - - def add_acl_entry(self, resource, acl_entry, send_notifications=None, - **kwargs): - """Adds the given AclEntry to the given Resource. - - Args: - resource: gdata.docs.data.Resource to which to add AclEntry. - acl_entry: gdata.docs.data.AclEntry representing ACL entry to add. - send_notifications: True if users should be notified by email when - this AclEntry is added. - kwargs: Other parameters to pass to self.post(). - - Returns: - gdata.docs.data.AclEntry containing information about new entry. - Raises: - ValueError: If given resource has no ACL link. - """ - uri = resource.GetAclLink().href - if uri is None: - raise ValueError(('Given resource has no ACL link. Did you fetch this' - 'resource from the API?')) - if send_notifications is not None: - if send_notifications: - uri += '?send-notification-emails=true' - - return self.post(acl_entry, uri, desired_class=gdata.docs.data.AclEntry, - **kwargs) - - AddAclEntry = add_acl_entry - - def update_acl_entry(self, entry, send_notifications=None, **kwargs): - """Updates the given AclEntry with new metadata. - - Args: - entry: AclEntry to update. Make any metadata changes to this entry. - send_notifications: True if users should be notified by email when - this AclEntry is updated. - kwargs: Other parameters to pass to super(DocsClient, self).update(). - - Returns: - gdata.docs.data.AclEntry representing the updated ACL entry. - """ - uri = entry.GetEditLink().href - if send_notifications: - uri += '?send-notification-emails=true' - return super(DocsClient, self).update(entry, uri=uri, force=True, **kwargs) - - UpdateAclEntry = update_acl_entry - - def delete_acl_entry(self, entry, **kwargs): - """Deletes the given AclEntry. - - Args: - entry: gdata.docs.data.AclEntry to delete. - kwargs: Other args to pass to gdata.client.GDClient.Delete() - - Returns: - Result of delete request. - """ - return super(DocsClient, self).delete(entry.GetEditLink().href, force=True, - **kwargs) - - DeleteAclEntry = delete_acl_entry - - def batch_process_acl_entries(self, resource, entries, **kwargs): - """Applies the specified operation of each entry in a single request. - - To use this, simply set acl_entry.batch_operation to one of - ['query', 'insert', 'update', 'delete'], and optionally set - acl_entry.batch_id to a string of your choice. - - Then, put all of your modified AclEntry objects into a list and pass - that list as the entries parameter. - - Args: - resource: gdata.docs.data.Resource to which the given entries belong. - entries: [gdata.docs.data.AclEntry] to modify in some way. - kwargs: Other args to pass to gdata.client.GDClient.post() - - Returns: - Resulting gdata.docs.data.AclFeed of changes. - """ - feed = gdata.docs.data.AclFeed() - feed.entry = entries - return super(DocsClient, self).post( - feed, uri=resource.GetAclLink().href + '/acl', force=True, **kwargs) - - BatchProcessAclEntries = batch_process_acl_entries - - def get_revisions(self, entry, **kwargs): - """Retrieves the revision history for a resource. - - Args: - entry: gdata.docs.data.Resource for which to get revisions. - kwargs: Other parameters to pass to self.get_feed(). - - Returns: - gdata.docs.data.RevisionFeed representing the resource's revisions. - """ - self._check_entry_is_resource(entry) - return self.get_feed( - entry.GetRevisionsFeedLink().href, - desired_class=gdata.docs.data.RevisionFeed, **kwargs) - - GetRevisions = get_revisions - - def get_revision(self, entry, **kwargs): - """Retrieves a revision again given its entry. - - Args: - entry: gdata.docs.data.Revision to fetch and return. - kwargs: Other args to pass to GetRevisionBySelfLink(). - Returns: - gdata.docs.data.Revision representing retrieved revision. - """ - return self.GetRevisionBySelfLink(entry.GetSelfLink().href, **kwargs) - - GetRevision = get_revision - - def get_revision_by_self_link(self, self_link, **kwargs): - """Retrieves a particular reivision by its self link. - - Args: - self_link: URI at which to query for given revision. This can be found - using entry.GetSelfLink(). - kwargs: Other parameters to pass to self.get_entry(). - - Returns: - gdata.docs.data.Revision representing the retrieved revision. - """ - if isinstance(self_link, atom.data.Link): - self_link = self_link.href - - return self.get_entry(self_link, desired_class=gdata.docs.data.Revision, - **kwargs) - - GetRevisionBySelfLink = get_revision_by_self_link - - def download_revision(self, entry, file_path, extra_params=None, **kwargs): - """Downloads the contents of the given revision to disk. - - Note: to download a revision in memory, use the DownloadRevisionToMemory() - method. - - Args: - entry: gdata.docs.data.Revision whose contents to fetch. - file_path: str Full path to which to save file. - extra_params: dict (optional) A map of any further parameters to control - how the document is downloaded. - kwargs: Other parameters to pass to self._download_file(). - - Raises: - gdata.client.RequestError if the download URL is malformed or the server's - response was not successful. - """ - uri = self._get_download_uri(entry.content.src, extra_params) - self._download_file(uri, file_path, **kwargs) - - DownloadRevision = download_revision - - def download_revision_to_memory(self, entry, extra_params=None, **kwargs): - """Returns the contents of the given revision. - - Args: - entry: gdata.docs.data.Revision whose contents to fetch. - extra_params: dict (optional) A map of any further parameters to control - how the document is downloaded/exported. - kwargs: Other parameters to pass to self._get_content(). - - Returns: - Content of given revision after being downloaded. - - Raises: - gdata.client.RequestError if the download URL is malformed or the server's - response was not successful. - """ - self._check_entry_is_not_collection(entry) - uri = self._get_download_uri(entry.content.src, extra_params) - return self._get_content(uri, **kwargs) - - DownloadRevisionToMemory = download_revision_to_memory - - def publish_revision(self, entry, publish_auto=None, - publish_outside_domain=False, **kwargs): - """Publishes the given revision. - - This method can only be used for document revisions. - - Args: - entry: Revision to update. - publish_auto: True to automatically publish future revisions of the - document. False to not automatically publish future revisions. - None to take no action and use the default value. - publish_outside_domain: True to make the published revision available - outside of a Google Apps domain. False to not publish outside - the domain. None to use the default value. - kwargs: Other parameters to pass to super(DocsClient, self).update(). - - Returns: - gdata.docs.data.Revision representing the updated revision. - """ - entry.publish = gdata.docs.data.Publish(value='true') - if publish_auto == True: - entry.publish_auto = gdata.docs.data.PublishAuto(value='true') - elif publish_auto == False: - entry.publish_auto = gdata.docs.data.PublishAuto(value='false') - if publish_outside_domain == True: - entry.publish_outside_domain = \ - gdata.docs.data.PublishOutsideDomain(value='true') - elif publish_outside_domain == False: - entry.publish_outside_domain = \ - gdata.docs.data.PublishOutsideDomain(value='false') - return super(DocsClient, self).update(entry, force=True, **kwargs) - - PublishRevision = publish_revision - - def unpublish_revision(self, entry, **kwargs): - """Unpublishes the given revision. - - This method can only be used for document revisions. - - Args: - entry: Revision to update. - kwargs: Other parameters to pass to super(DocsClient, self).update(). - - Returns: - gdata.docs.data.Revision representing the updated revision. - """ - entry.publish = gdata.docs.data.Publish(value='false') - return super(DocsClient, self).update(entry, force=True, **kwargs) - - UnpublishRevision = unpublish_revision - - def delete_revision(self, entry, **kwargs): - """Deletes the given Revision. - - Args: - entry: gdata.docs.data.Revision to delete. - kwargs: Other args to pass to gdata.client.GDClient.Delete() - - Returns: - Result of delete request. - """ - return super(DocsClient, self).delete(entry, force=True, **kwargs) - - DeleteRevision = delete_revision - - def get_archive(self, entry, **kwargs): - """Retrieves an archive again given its entry. - - This is useful if you need to poll for an archive completing. - - Args: - entry: gdata.docs.data.Archive to fetch and return. - kwargs: Other args to pass to GetArchiveBySelfLink(). - Returns: - gdata.docs.data.Archive representing retrieved archive. - """ - - return self.GetArchiveBySelfLink(entry.GetSelfLink().href, **kwargs) - - GetArchive = get_archive - - def get_archive_by_self_link(self, self_link, **kwargs): - """Retrieves a particular archive by its self link. - - Args: - self_link: URI at which to query for given archive. This can be found - using entry.GetSelfLink(). - kwargs: Other parameters to pass to self.get_entry(). - - Returns: - gdata.docs.data.Archive representing the retrieved archive. - """ - if isinstance(self_link, atom.data.Link): - self_link = self_link.href - - return self.get_entry(self_link, desired_class=gdata.docs.data.Archive, - **kwargs) - - GetArchiveBySelfLink = get_archive_by_self_link - - def create_archive(self, entry, **kwargs): - """Creates a new archive of resources. - - Args: - entry: gdata.docs.data.Archive representing metadata of archive to - create. - kwargs: Other parameters to pass to self.post(). - - Returns: - gdata.docs.data.Archive containing information about new archive. - """ - return self.post(entry, ARCHIVE_FEED_URI, - desired_class=gdata.docs.data.Archive, **kwargs) - - CreateArchive = create_archive - - def update_archive(self, entry, **kwargs): - """Updates the given Archive with new metadata. - - This method is really only useful for updating the notification email - address of an archive that is being processed. - - Args: - entry: Archive to update. Make any metadata changes to this entry. - kwargs: Other parameters to pass to super(DocsClient, self).update(). - - Returns: - gdata.docs.data.Archive representing the updated archive. - """ - return super(DocsClient, self).update(entry, **kwargs) - - UpdateArchive = update_archive - - download_archive = DownloadResource - DownloadArchive = download_archive - download_archive_to_memory = DownloadResourceToMemory - DownloadArchiveToMemory = download_archive_to_memory - - def delete_archive(self, entry, **kwargs): - """Aborts the given Archive operation, or deletes the Archive. - - Args: - entry: gdata.docs.data.Archive to delete. - kwargs: Other args to pass to gdata.client.GDClient.Delete() - - Returns: - Result of delete request. - """ - return super(DocsClient, self).delete(entry, force=True, **kwargs) - - DeleteArchive = delete_archive - - -class DocsQuery(gdata.client.Query): - - def __init__(self, title=None, title_exact=None, opened_min=None, - opened_max=None, edited_min=None, edited_max=None, owner=None, - writer=None, reader=None, show_collections=None, show_root=None, - show_deleted=None, ocr=None, target_language=None, - source_language=None, convert=None, query=None, **kwargs): - """Constructs a query URL for the Google Documents List API. - - Args: - title: str (optional) Specifies the search terms for the title of a - document. This parameter used without title_exact will only - submit partial queries, not exact queries. - title_exact: str (optional) Meaningless without title. Possible values - are 'true' and 'false'. Note: Matches are case-insensitive. - opened_min: str (optional) Lower bound on the last time a document was - opened by the current user. Use the RFC 3339 timestamp - format. For example: opened_min='2005-08-09T09:57:00-08:00'. - opened_max: str (optional) Upper bound on the last time a document was - opened by the current user. (See also opened_min.) - edited_min: str (optional) Lower bound on the last time a document was - edited by the current user. This value corresponds to the edited.text - value in the doc's entry object, which represents changes to the - document's content or metadata. Use the RFC 3339 timestamp format. - For example: edited_min='2005-08-09T09:57:00-08:00' - edited_max: str (optional) Upper bound on the last time a document was - edited by the user. (See also edited_min.) - owner: str (optional) Searches for documents with a specific owner. Use - the email address of the owner. For example: owner='user@gmail.com' - writer: str (optional) Searches for documents which can be written to - by specific users. Use a single email address or a comma separated list - of email addresses. For example: writer='user1@gmail.com,user@example.com' - reader: str (optional) Searches for documents which can be read by - specific users. (See also writer.) - show_collections: str (optional) Specifies whether the query should return - collections as well as documents and files. Possible values are 'true' - and 'false'. Default is 'false'. - show_root: (optional) 'true' to specify when an item is in the root - collection. Default is 'false' - show_deleted: str (optional) Specifies whether the query should return - documents which are in the trash as well as other documents. - Possible values are 'true' and 'false'. Default is false. - ocr: str (optional) Specifies whether to attempt OCR on a .jpg, .png, or - .gif upload. Possible values are 'true' and 'false'. Default is - false. See OCR in the Protocol Guide: - http://code.google.com/apis/documents/docs/3.0/developers_guide_protocol.html#OCR - target_language: str (optional) Specifies the language to translate a - document into. See Document Translation in the Protocol Guide for a - table of possible values: - http://code.google.com/apis/documents/docs/3.0/developers_guide_protocol.html#DocumentTranslation - source_language: str (optional) Specifies the source language of the - original document. Optional when using the translation service. - If not provided, Google will attempt to auto-detect the source - language. See Document Translation in the Protocol Guide for a table of - possible values (link in target_language). - convert: str (optional) Used when uploading files specify if document uploads - should convert to a native Google Docs format. - Possible values are 'true' and 'false'. The default is 'true'. - query: str (optional) Full-text query to use. See the 'q' parameter in - the documentation. - """ - gdata.client.Query.__init__(self, **kwargs) - self.convert = convert - self.title = title - self.title_exact = title_exact - self.opened_min = opened_min - self.opened_max = opened_max - self.edited_min = edited_min - self.edited_max = edited_max - self.owner = owner - self.writer = writer - self.reader = reader - self.show_collections = show_collections - self.show_root = show_root - self.show_deleted = show_deleted - self.ocr = ocr - self.target_language = target_language - self.source_language = source_language - self.query = query - - def modify_request(self, http_request): - gdata.client._add_query_param('convert', self.convert, http_request) - gdata.client._add_query_param('title', self.title, http_request) - gdata.client._add_query_param('title-exact', self.title_exact, - http_request) - gdata.client._add_query_param('opened-min', self.opened_min, http_request) - gdata.client._add_query_param('opened-max', self.opened_max, http_request) - gdata.client._add_query_param('edited-min', self.edited_min, http_request) - gdata.client._add_query_param('edited-max', self.edited_max, http_request) - gdata.client._add_query_param('owner', self.owner, http_request) - gdata.client._add_query_param('writer', self.writer, http_request) - gdata.client._add_query_param('reader', self.reader, http_request) - gdata.client._add_query_param('query', self.query, http_request) - gdata.client._add_query_param('showfolders', self.show_collections, - http_request) - gdata.client._add_query_param('showroot', self.show_root, http_request) - gdata.client._add_query_param('showdeleted', self.show_deleted, - http_request) - gdata.client._add_query_param('ocr', self.ocr, http_request) - gdata.client._add_query_param('targetLanguage', self.target_language, - http_request) - gdata.client._add_query_param('sourceLanguage', self.source_language, - http_request) - gdata.client.Query.modify_request(self, http_request) - - ModifyRequest = modify_request diff --git a/gdata/analytics/docs/data.py b/gdata/analytics/docs/data.py deleted file mode 100644 index 826fd98232..0000000000 --- a/gdata/analytics/docs/data.py +++ /dev/null @@ -1,654 +0,0 @@ -#!/usr/bin/python -# -# Copyright 2011 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 representing elements of the Documents List API.""" - -__author__ = 'vicfryzel@google.com (Vic Fryzel)' - -import re -import atom.core -import atom.data -import gdata.acl.data -import gdata.data - - -DOCUMENTS_NS = 'http://schemas.google.com/docs/2007' -LABELS_NS = 'http://schemas.google.com/g/2005/labels' -DOCUMENTS_TEMPLATE = '{http://schemas.google.com/docs/2007}%s' -ACL_FEEDLINK_REL = 'http://schemas.google.com/acl/2007#accessControlList' -REVISION_FEEDLINK_REL = DOCUMENTS_NS + '/revisions' -PARENT_LINK_REL = DOCUMENTS_NS + '#parent' -PUBLISH_LINK_REL = DOCUMENTS_NS + '#publish' -DATA_KIND_SCHEME = 'http://schemas.google.com/g/2005#kind' -LABELS_SCHEME = LABELS_NS -DOCUMENT_LABEL = 'document' -SPREADSHEET_LABEL = 'spreadsheet' -DRAWING_LABEL = 'drawing' -PRESENTATION_LABEL = 'presentation' -FILE_LABEL = 'file' -PDF_LABEL = 'pdf' -FORM_LABEL = 'form' -COLLECTION_LABEL = 'folder' -STARRED_LABEL = 'starred' -VIEWED_LABEL = 'viewed' -HIDDEN_LABEL = 'hidden' -TRASHED_LABEL = 'trashed' -MINE_LABEL = 'mine' -PRIVATE_LABEL = 'private' -SHAREDWITHDOMAIN_LABEL = 'shared-with-domain' -RESTRICTEDDOWNLOAD_LABEL = 'restricted-download' - - -class ResourceId(atom.core.XmlElement): - """The DocList gd:resourceId element.""" - _qname = gdata.data.GDATA_TEMPLATE % 'resourceId' - - -class LastModifiedBy(atom.data.Person): - """The DocList gd:lastModifiedBy element.""" - _qname = gdata.data.GDATA_TEMPLATE % 'lastModifiedBy' - - -class LastViewed(atom.data.Person): - """The DocList gd:lastViewed element.""" - _qname = gdata.data.GDATA_TEMPLATE % 'lastViewed' - - -class WritersCanInvite(atom.core.XmlElement): - """The DocList docs:writersCanInvite element.""" - _qname = DOCUMENTS_TEMPLATE % 'writersCanInvite' - value = 'value' - - -class Deleted(atom.core.XmlElement): - """The DocList gd:deleted element.""" - _qname = gdata.data.GDATA_TEMPLATE % 'deleted' - - -class QuotaBytesUsed(atom.core.XmlElement): - """The DocList gd:quotaBytesUsed element.""" - _qname = gdata.data.GDATA_TEMPLATE % 'quotaBytesUsed' - - -class Publish(atom.core.XmlElement): - """The DocList docs:publish element.""" - _qname = DOCUMENTS_TEMPLATE % 'publish' - value = 'value' - - -class PublishAuto(atom.core.XmlElement): - """The DocList docs:publishAuto element.""" - _qname = DOCUMENTS_TEMPLATE % 'publishAuto' - value = 'value' - - -class PublishOutsideDomain(atom.core.XmlElement): - """The DocList docs:publishOutsideDomain element.""" - _qname = DOCUMENTS_TEMPLATE % 'publishOutsideDomain' - value = 'value' - - -class Filename(atom.core.XmlElement): - """The DocList docs:filename element.""" - _qname = DOCUMENTS_TEMPLATE % 'filename' - - -class SuggestedFilename(atom.core.XmlElement): - """The DocList docs:suggestedFilename element.""" - _qname = DOCUMENTS_TEMPLATE % 'suggestedFilename' - - -class CategoryFinder(object): - """Mixin to provide category finding functionality. - - Analogous to atom.data.LinkFinder, but a simpler API, specialized for - DocList categories. - """ - - def add_category(self, scheme, term, label): - """Add a category for a scheme, term and label. - - Args: - scheme: The scheme for the category. - term: The term for the category. - label: The label for the category - - Returns: - The newly created atom.data.Category. - """ - category = atom.data.Category(scheme=scheme, term=term, label=label) - self.category.append(category) - return category - - AddCategory = add_category - - def get_categories(self, scheme): - """Fetch the category elements for a scheme. - - Args: - scheme: The scheme to fetch the elements for. - - Returns: - Generator of atom.data.Category elements. - """ - for category in self.category: - if category.scheme == scheme: - yield category - - GetCategories = get_categories - - def remove_categories(self, scheme): - """Remove category elements for a scheme. - - Args: - scheme: The scheme of category to remove. - """ - for category in list(self.get_categories(scheme)): - self.category.remove(category) - - RemoveCategories = remove_categories - - def get_first_category(self, scheme): - """Fetch the first category element for a scheme. - - Args: - scheme: The scheme of category to return. - - Returns: - atom.data.Category if found or None. - """ - try: - return self.get_categories(scheme).next() - except StopIteration, e: - # The entry doesn't have the category - return None - - GetFirstCategory = get_first_category - - def set_resource_type(self, label): - """Set the document type for an entry, by building the appropriate - atom.data.Category - - Args: - label: str The value for the category entry. If None is passed the - category is removed and not set. - - Returns: - An atom.data.Category or None if label is None. - """ - self.remove_categories(DATA_KIND_SCHEME) - if label is not None: - return self.add_category(scheme=DATA_KIND_SCHEME, - term='%s#%s' % (DOCUMENTS_NS, label), - label=label) - else: - return None - - SetResourceType = set_resource_type - - def get_resource_type(self): - """Extracts the type of document this Resource is. - - This method returns the type of document the Resource represents. Possible - values are document, presentation, drawing, spreadsheet, file, folder, - form, or pdf. - - 'folder' is a possible return value of this method because, for legacy - support, we have not yet renamed the folder keyword to collection in - the API itself. - - Returns: - String representing the type of document. - """ - category = self.get_first_category(DATA_KIND_SCHEME) - if category is not None: - return category.label - else: - return None - - GetResourceType = get_resource_type - - def get_labels(self): - """Extracts the labels for this Resource. - - This method returns the labels as a set, for example: 'hidden', 'starred', - 'viewed'. - - Returns: - Set of string labels. - """ - return set(category.label for category in - self.get_categories(LABELS_SCHEME)) - - GetLabels = get_labels - - def has_label(self, label): - """Whether this Resource has a label. - - Args: - label: The str label to test for - - Returns: - Boolean value indicating presence of label. - """ - return label in self.get_labels() - - HasLabel = has_label - - def add_label(self, label): - """Add a label, if it is not present. - - Args: - label: The str label to set - """ - if not self.has_label(self): - self.add_category(scheme=LABELS_SCHEME, - term='%s#%s' % (LABELS_NS, label), - label=label) - AddLabel = add_label - - def remove_label(self, label): - """Remove a label, if it is present. - - Args: - label: The str label to remove - """ - for category in self.get_categories(LABELS_SCHEME): - if category.label == label: - self.category.remove(category) - - RemoveLabel = remove_label - - def is_starred(self): - """Whether this Resource is starred. - - Returns: - Boolean value indicating that the resource is starred. - """ - return self.has_label(STARRED_LABEL) - - IsStarred = is_starred - - def is_hidden(self): - """Whether this Resource is hidden. - - Returns: - Boolean value indicating that the resource is hidden. - """ - return self.has_label(HIDDEN_LABEL) - - IsHidden = is_hidden - - def is_viewed(self): - """Whether this Resource is viewed. - - Returns: - Boolean value indicating that the resource is viewed. - """ - return self.has_label(VIEWED_LABEL) - - IsViewed = is_viewed - - def is_trashed(self): - """Whether this resource is trashed. - - Returns: - Boolean value indicating that the resource is trashed. - """ - return self.has_label(TRASHED_LABEL) - - IsTrashed = is_trashed - - def is_mine(self): - """Whether this resource is marked as mine. - - Returns: - Boolean value indicating that the resource is marked as mine. - """ - return self.has_label(MINE_LABEL) - - IsMine = is_mine - - def is_private(self): - """Whether this resource is private. - - Returns: - Boolean value indicating that the resource is private. - """ - return self.has_label(PRIVATE_LABEL) - - IsPrivate = is_private - - def is_shared_with_domain(self): - """Whether this resource is shared with the domain. - - Returns: - Boolean value indicating that the resource is shared with the domain. - """ - return self.has_label(SHAREDWITHDOMAIN_LABEL) - - IsSharedWithDomain = is_shared_with_domain - - def is_restricted_download(self): - """Whether this resource is restricted download. - - Returns: - Boolean value indicating whether the resource is restricted download. - """ - return self.has_label(RESTRICTEDDOWNLOAD_LABEL) - - IsRestrictedDownload = is_restricted_download - - -class AclEntry(gdata.acl.data.AclEntry, gdata.data.BatchEntry): - """Resource ACL entry.""" - @staticmethod - def get_instance(role=None, scope_type=None, scope_value=None, key=False): - entry = AclEntry() - - if role is not None: - if isinstance(role, basestring): - role = gdata.acl.data.AclRole(value=role) - - if key: - entry.with_key = gdata.acl.data.AclWithKey(key='', role=role) - else: - entry.role = role - - if scope_type is not None: - if scope_value is not None: - entry.scope = gdata.acl.data.AclScope(type=scope_type, - value=scope_value) - else: - entry.scope = gdata.acl.data.AclScope(type=scope_type) - return entry - - GetInstance = get_instance - - -class AclFeed(gdata.acl.data.AclFeed): - """Resource ACL feed.""" - entry = [AclEntry] - - -class Resource(gdata.data.GDEntry, CategoryFinder): - """DocList version of an Atom Entry.""" - - last_viewed = LastViewed - last_modified_by = LastModifiedBy - resource_id = ResourceId - deleted = Deleted - writers_can_invite = WritersCanInvite - quota_bytes_used = QuotaBytesUsed - feed_link = [gdata.data.FeedLink] - filename = Filename - suggested_filename = SuggestedFilename - # Only populated if you request /feeds/default/private/expandAcl - acl_feed = AclFeed - - def __init__(self, type=None, title=None, **kwargs): - super(Resource, self).__init__(**kwargs) - if isinstance(type, basestring): - self.set_resource_type(type) - - if title is not None: - if isinstance(title, basestring): - self.title = atom.data.Title(text=title) - else: - self.title = title - - def get_acl_feed_link(self): - """Extracts the Resource's ACL feed <gd:feedLink>. - - Returns: - A gdata.data.FeedLink object. - """ - for feed_link in self.feed_link: - if feed_link.rel == ACL_FEEDLINK_REL: - return feed_link - return None - - GetAclFeedLink = get_acl_feed_link - - def get_revisions_feed_link(self): - """Extracts the Resource's revisions feed <gd:feedLink>. - - Returns: - A gdata.data.FeedLink object. - """ - for feed_link in self.feed_link: - if feed_link.rel == REVISION_FEEDLINK_REL: - return feed_link - return None - - GetRevisionsFeedLink = get_revisions_feed_link - - def get_resumable_edit_media_link(self): - """Extracts the Resource's resumable update link. - - Returns: - A gdata.data.FeedLink object. - """ - for feed_link in self.feed_link: - if feed_link.rel == RESUMABLE_EDIT_MEDIA_LINK_REL: - return feed_link - return None - - GetRevisionsFeedLink = get_revisions_feed_link - - def in_collections(self): - """Returns the parents link(s) (collections) of this entry.""" - links = [] - for link in self.link: - if link.rel == PARENT_LINK_REL and link.href: - links.append(link) - return links - - InCollections = in_collections - - -class ResourceFeed(gdata.data.GDFeed): - """Main feed containing a list of resources.""" - entry = [Resource] - - -class Revision(gdata.data.GDEntry): - """Resource Revision entry.""" - publish = Publish - publish_auto = PublishAuto - publish_outside_domain = PublishOutsideDomain - - def find_publish_link(self): - """Get the link that points to the published resource on the web. - - Returns: - A str for the URL in the link with a rel ending in #publish. - """ - return self.find_url(PUBLISH_LINK_REL) - - FindPublishLink = find_publish_link - - def get_publish_link(self): - """Get the link that points to the published resource on the web. - - Returns: - A gdata.data.Link for the link with a rel ending in #publish. - """ - return self.get_link(PUBLISH_LINK_REL) - - GetPublishLink = get_publish_link - - -class RevisionFeed(gdata.data.GDFeed): - """A DocList Revision feed.""" - entry = [Revision] - - -class ArchiveResourceId(atom.core.XmlElement): - """The DocList docs:removed element.""" - _qname = DOCUMENTS_TEMPLATE % 'archiveResourceId' - - -class ArchiveFailure(atom.core.XmlElement): - """The DocList docs:archiveFailure element.""" - _qname = DOCUMENTS_TEMPLATE % 'archiveFailure' - - -class ArchiveComplete(atom.core.XmlElement): - """The DocList docs:archiveComplete element.""" - _qname = DOCUMENTS_TEMPLATE % 'archiveComplete' - - -class ArchiveTotal(atom.core.XmlElement): - """The DocList docs:archiveTotal element.""" - _qname = DOCUMENTS_TEMPLATE % 'archiveTotal' - - -class ArchiveTotalComplete(atom.core.XmlElement): - """The DocList docs:archiveTotalComplete element.""" - _qname = DOCUMENTS_TEMPLATE % 'archiveTotalComplete' - - -class ArchiveTotalFailure(atom.core.XmlElement): - """The DocList docs:archiveTotalFailure element.""" - _qname = DOCUMENTS_TEMPLATE % 'archiveTotalFailure' - - -class ArchiveConversion(atom.core.XmlElement): - """The DocList docs:removed element.""" - _qname = DOCUMENTS_TEMPLATE % 'archiveConversion' - source = 'source' - target = 'target' - - -class ArchiveNotify(atom.core.XmlElement): - """The DocList docs:archiveNotify element.""" - _qname = DOCUMENTS_TEMPLATE % 'archiveNotify' - - -class ArchiveStatus(atom.core.XmlElement): - """The DocList docs:archiveStatus element.""" - _qname = DOCUMENTS_TEMPLATE % 'archiveStatus' - - -class ArchiveNotifyStatus(atom.core.XmlElement): - """The DocList docs:archiveNotifyStatus element.""" - _qname = DOCUMENTS_TEMPLATE % 'archiveNotifyStatus' - - -class Archive(gdata.data.GDEntry): - """Archive entry.""" - archive_resource_ids = [ArchiveResourceId] - status = ArchiveStatus - date_completed = ArchiveComplete - num_resources = ArchiveTotal - num_complete_resources = ArchiveTotalComplete - num_failed_resources = ArchiveTotalFailure - failed_resource_ids = [ArchiveFailure] - notify_status = ArchiveNotifyStatus - conversions = [ArchiveConversion] - notification_email = ArchiveNotify - size = QuotaBytesUsed - - @staticmethod - def from_resource_list(resources): - resource_ids = [] - for resource in resources: - id = ArchiveResourceId(text=resource.resource_id.text) - resource_ids.append(id) - return Archive(archive_resource_ids=resource_ids) - - FromResourceList = from_resource_list - - -class Removed(atom.core.XmlElement): - """The DocList docs:removed element.""" - _qname = DOCUMENTS_TEMPLATE % 'removed' - - -class Changestamp(atom.core.XmlElement): - """The DocList docs:changestamp element.""" - _qname = DOCUMENTS_TEMPLATE % 'changestamp' - value = 'value' - - -class Change(Resource): - """Change feed entry.""" - changestamp = Changestamp - removed = Removed - - -class ChangeFeed(gdata.data.GDFeed): - """DocList Changes feed.""" - entry = [Change] - - -class QuotaBytesTotal(atom.core.XmlElement): - """The DocList gd:quotaBytesTotal element.""" - _qname = gdata.data.GDATA_TEMPLATE % 'quotaBytesTotal' - - -class QuotaBytesUsedInTrash(atom.core.XmlElement): - """The DocList docs:quotaBytesUsedInTrash element.""" - _qname = DOCUMENTS_TEMPLATE % 'quotaBytesUsedInTrash' - - -class ImportFormat(atom.core.XmlElement): - """The DocList docs:importFormat element.""" - _qname = DOCUMENTS_TEMPLATE % 'importFormat' - source = 'source' - target = 'target' - - -class ExportFormat(atom.core.XmlElement): - """The DocList docs:exportFormat element.""" - _qname = DOCUMENTS_TEMPLATE % 'exportFormat' - source = 'source' - target = 'target' - - -class FeatureName(atom.core.XmlElement): - """The DocList docs:featureName element.""" - _qname = DOCUMENTS_TEMPLATE % 'featureName' - - -class FeatureRate(atom.core.XmlElement): - """The DocList docs:featureRate element.""" - _qname = DOCUMENTS_TEMPLATE % 'featureRate' - - -class Feature(atom.core.XmlElement): - """The DocList docs:feature element.""" - _qname = DOCUMENTS_TEMPLATE % 'feature' - name = FeatureName - rate = FeatureRate - - -class MaxUploadSize(atom.core.XmlElement): - """The DocList docs:maxUploadSize element.""" - _qname = DOCUMENTS_TEMPLATE % 'maxUploadSize' - kind = 'kind' - - -class Metadata(gdata.data.GDEntry): - """Metadata entry for a user.""" - quota_bytes_total = QuotaBytesTotal - quota_bytes_used = QuotaBytesUsed - quota_bytes_used_in_trash = QuotaBytesUsedInTrash - import_formats = [ImportFormat] - export_formats = [ExportFormat] - features = [Feature] - max_upload_sizes = [MaxUploadSize] diff --git a/gdata/analytics/docs/service.py b/gdata/analytics/docs/service.py deleted file mode 100644 index b6f39f6a4a..0000000000 --- a/gdata/analytics/docs/service.py +++ /dev/null @@ -1,618 +0,0 @@ -#!/usr/bin/python -# -# Copyright 2009 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. - -"""DocsService extends the GDataService to streamline Google Documents - operations. - - DocsService: Provides methods to query feeds and manipulate items. - Extends GDataService. - - DocumentQuery: Queries a Google Document list feed. - - DocumentAclQuery: Queries a Google Document Acl feed. -""" - - -__author__ = ('api.jfisher (Jeff Fisher), ' - 'e.bidelman (Eric Bidelman)') - -import re -import atom -import gdata.service -import gdata.docs -import urllib - -# XML Namespaces used in Google Documents entities. -DATA_KIND_SCHEME = gdata.GDATA_NAMESPACE + '#kind' -DOCUMENT_LABEL = 'document' -SPREADSHEET_LABEL = 'spreadsheet' -PRESENTATION_LABEL = 'presentation' -FOLDER_LABEL = 'folder' -PDF_LABEL = 'pdf' - -LABEL_SCHEME = gdata.GDATA_NAMESPACE + '/labels' -STARRED_LABEL_TERM = LABEL_SCHEME + '#starred' -TRASHED_LABEL_TERM = LABEL_SCHEME + '#trashed' -HIDDEN_LABEL_TERM = LABEL_SCHEME + '#hidden' -MINE_LABEL_TERM = LABEL_SCHEME + '#mine' -PRIVATE_LABEL_TERM = LABEL_SCHEME + '#private' -SHARED_WITH_DOMAIN_LABEL_TERM = LABEL_SCHEME + '#shared-with-domain' -VIEWED_LABEL_TERM = LABEL_SCHEME + '#viewed' - -FOLDERS_SCHEME_PREFIX = gdata.docs.DOCUMENTS_NAMESPACE + '/folders/' - -# File extensions of documents that are permitted to be uploaded or downloaded. -SUPPORTED_FILETYPES = { - 'CSV': 'text/csv', - 'TSV': 'text/tab-separated-values', - 'TAB': 'text/tab-separated-values', - 'DOC': 'application/msword', - 'DOCX': ('application/vnd.openxmlformats-officedocument.' - 'wordprocessingml.document'), - 'ODS': 'application/x-vnd.oasis.opendocument.spreadsheet', - 'ODT': 'application/vnd.oasis.opendocument.text', - 'RTF': 'application/rtf', - 'SXW': 'application/vnd.sun.xml.writer', - 'TXT': 'text/plain', - 'XLS': 'application/vnd.ms-excel', - 'XLSX': 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', - 'PDF': 'application/pdf', - 'PNG': 'image/png', - 'PPT': 'application/vnd.ms-powerpoint', - 'PPS': 'application/vnd.ms-powerpoint', - 'HTM': 'text/html', - 'HTML': 'text/html', - 'ZIP': 'application/zip', - 'SWF': 'application/x-shockwave-flash' - } - - -class DocsService(gdata.service.GDataService): - - """Client extension for the Google Documents service Document List feed.""" - - __FILE_EXT_PATTERN = re.compile('.*\.([a-zA-Z]{3,}$)') - __RESOURCE_ID_PATTERN = re.compile('^([a-z]*)(:|%3A)([\w-]*)$') - - def __init__(self, email=None, password=None, source=None, - server='docs.google.com', additional_headers=None, **kwargs): - """Creates a client for the Google Documents 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='writely', source=source, - server=server, additional_headers=additional_headers, **kwargs) - self.ssl = True - - def _MakeKindCategory(self, label): - if label is None: - return None - return atom.Category(scheme=DATA_KIND_SCHEME, - term=gdata.docs.DOCUMENTS_NAMESPACE + '#' + label, label=label) - - def _MakeContentLinkFromId(self, resource_id): - match = self.__RESOURCE_ID_PATTERN.match(resource_id) - label = match.group(1) - doc_id = match.group(3) - if label == DOCUMENT_LABEL: - return '/feeds/download/documents/Export?docId=%s' % doc_id - if label == PRESENTATION_LABEL: - return '/feeds/download/presentations/Export?docId=%s' % doc_id - if label == SPREADSHEET_LABEL: - return ('https://spreadsheets.google.com/feeds/download/spreadsheets/' - 'Export?key=%s' % doc_id) - raise ValueError, 'Invalid resource id: %s' % resource_id - - def _UploadFile(self, media_source, title, category, folder_or_uri=None): - """Uploads a file to the Document List feed. - - Args: - media_source: A gdata.MediaSource object containing the file to be - uploaded. - title: string The title of the document on the server after being - uploaded. - category: An atom.Category object specifying the appropriate document - type. - folder_or_uri: DocumentListEntry or string (optional) An object with a - link to a folder or a uri to a folder to upload to. - Note: A valid uri for a folder is of the form: - /feeds/folders/private/full/folder%3Afolder_id - - Returns: - A DocumentListEntry containing information about the document created on - the Google Documents service. - """ - if folder_or_uri: - try: - uri = folder_or_uri.content.src - except AttributeError: - uri = folder_or_uri - else: - uri = '/feeds/documents/private/full' - - entry = gdata.docs.DocumentListEntry() - entry.title = atom.Title(text=title) - if category is not None: - entry.category.append(category) - entry = self.Post(entry, uri, media_source=media_source, - extra_headers={'Slug': media_source.file_name}, - converter=gdata.docs.DocumentListEntryFromString) - return entry - - def _DownloadFile(self, uri, file_path): - """Downloads a file. - - Args: - uri: string The full Export URL to download the file from. - file_path: string The full path to save the file to. - - Raises: - RequestError: on error response from server. - """ - server_response = self.request('GET', uri) - response_body = server_response.read() - timeout = 5 - while server_response.status == 302 and timeout > 0: - server_response = self.request('GET', - server_response.getheader('Location')) - response_body = server_response.read() - timeout -= 1 - if server_response.status != 200: - raise gdata.service.RequestError, {'status': server_response.status, - 'reason': server_response.reason, - 'body': response_body} - f = open(file_path, 'wb') - f.write(response_body) - f.flush() - f.close() - - def MoveIntoFolder(self, source_entry, folder_entry): - """Moves a document into a folder in the Document List Feed. - - Args: - source_entry: DocumentListEntry An object representing the source - document/folder. - folder_entry: DocumentListEntry An object with a link to the destination - folder. - - Returns: - A DocumentListEntry containing information about the document created on - the Google Documents service. - """ - entry = gdata.docs.DocumentListEntry() - entry.id = source_entry.id - entry = self.Post(entry, folder_entry.content.src, - converter=gdata.docs.DocumentListEntryFromString) - return entry - - def Query(self, uri, converter=gdata.docs.DocumentListFeedFromString): - """Queries the Document List feed and returns the resulting feed of - entries. - - Args: - uri: string The full URI to be queried. This can contain query - parameters, a hostname, or simply the relative path to a Document - List feed. The DocumentQuery object is useful when constructing - query parameters. - converter: func (optional) A function which will be executed on the - retrieved item, generally to render it into a Python object. - By default the DocumentListFeedFromString function is used to - return a DocumentListFeed object. This is because most feed - queries will result in a feed and not a single entry. - """ - return self.Get(uri, converter=converter) - - def QueryDocumentListFeed(self, uri): - """Retrieves a DocumentListFeed by retrieving a URI based off the Document - List feed, including any query parameters. A DocumentQuery object can - be used to construct these parameters. - - Args: - uri: string The URI of the feed being retrieved possibly with query - parameters. - - Returns: - A DocumentListFeed object representing the feed returned by the server. - """ - return self.Get(uri, converter=gdata.docs.DocumentListFeedFromString) - - def GetDocumentListEntry(self, uri): - """Retrieves a particular DocumentListEntry by its unique URI. - - Args: - uri: string The unique URI of an entry in a Document List feed. - - Returns: - A DocumentListEntry object representing the retrieved entry. - """ - return self.Get(uri, converter=gdata.docs.DocumentListEntryFromString) - - def GetDocumentListFeed(self, uri=None): - """Retrieves a feed containing all of a user's documents. - - Args: - uri: string A full URI to query the Document List feed. - """ - if not uri: - uri = gdata.docs.service.DocumentQuery().ToUri() - return self.QueryDocumentListFeed(uri) - - def GetDocumentListAclEntry(self, uri): - """Retrieves a particular DocumentListAclEntry by its unique URI. - - Args: - uri: string The unique URI of an entry in a Document List feed. - - Returns: - A DocumentListAclEntry object representing the retrieved entry. - """ - return self.Get(uri, converter=gdata.docs.DocumentListAclEntryFromString) - - def GetDocumentListAclFeed(self, uri): - """Retrieves a feed containing all of a user's documents. - - Args: - uri: string The URI of a document's Acl feed to retrieve. - - Returns: - A DocumentListAclFeed object representing the ACL feed - returned by the server. - """ - return self.Get(uri, converter=gdata.docs.DocumentListAclFeedFromString) - - def Upload(self, media_source, title, folder_or_uri=None, label=None): - """Uploads a document inside of a MediaSource object to the Document List - feed with the given title. - - Args: - media_source: MediaSource The gdata.MediaSource object containing a - document file to be uploaded. - title: string The title of the document on the server after being - uploaded. - folder_or_uri: DocumentListEntry or string (optional) An object with a - link to a folder or a uri to a folder to upload to. - Note: A valid uri for a folder is of the form: - /feeds/folders/private/full/folder%3Afolder_id - label: optional label describing the type of the document to be created. - - Returns: - A DocumentListEntry containing information about the document created - on the Google Documents service. - """ - - return self._UploadFile(media_source, title, self._MakeKindCategory(label), - folder_or_uri) - - def Download(self, entry_or_id_or_url, file_path, export_format=None, - gid=None, extra_params=None): - """Downloads a document from the Document List. - - Args: - entry_or_id_or_url: a DocumentListEntry, or the resource id of an entry, - or a url to download from (such as the content src). - file_path: string The full path to save the file to. - export_format: the format to convert to, if conversion is required. - gid: grid id, for downloading a single grid of a spreadsheet - extra_params: a map of any further parameters to control how the document - is downloaded - - Raises: - RequestError if the service does not respond with success - """ - - if isinstance(entry_or_id_or_url, gdata.docs.DocumentListEntry): - url = entry_or_id_or_url.content.src - else: - if self.__RESOURCE_ID_PATTERN.match(entry_or_id_or_url): - url = self._MakeContentLinkFromId(entry_or_id_or_url) - else: - url = entry_or_id_or_url - - if export_format is not None: - if url.find('/Export?') == -1: - raise gdata.service.Error, ('This entry cannot be exported ' - 'as a different format') - url += '&exportFormat=%s' % export_format - - if gid is not None: - if url.find('spreadsheets') == -1: - raise gdata.service.Error, 'grid id param is not valid for this entry' - url += '&gid=%s' % gid - - if extra_params: - url += '&' + urllib.urlencode(extra_params) - - self._DownloadFile(url, file_path) - - def Export(self, entry_or_id_or_url, file_path, gid=None, extra_params=None): - """Downloads a document from the Document List in a different format. - - Args: - entry_or_id_or_url: a DocumentListEntry, or the resource id of an entry, - or a url to download from (such as the content src). - file_path: string The full path to save the file to. The export - format is inferred from the the file extension. - gid: grid id, for downloading a single grid of a spreadsheet - extra_params: a map of any further parameters to control how the document - is downloaded - - Raises: - RequestError if the service does not respond with success - """ - ext = None - match = self.__FILE_EXT_PATTERN.match(file_path) - if match: - ext = match.group(1) - self.Download(entry_or_id_or_url, file_path, ext, gid, extra_params) - - def CreateFolder(self, title, folder_or_uri=None): - """Creates a folder in the Document List feed. - - Args: - title: string The title of the folder on the server after being created. - folder_or_uri: DocumentListEntry or string (optional) An object with a - link to a folder or a uri to a folder to upload to. - Note: A valid uri for a folder is of the form: - /feeds/folders/private/full/folder%3Afolder_id - - Returns: - A DocumentListEntry containing information about the folder created on - the Google Documents service. - """ - if folder_or_uri: - try: - uri = folder_or_uri.content.src - except AttributeError: - uri = folder_or_uri - else: - uri = '/feeds/documents/private/full' - - folder_entry = gdata.docs.DocumentListEntry() - folder_entry.title = atom.Title(text=title) - folder_entry.category.append(self._MakeKindCategory(FOLDER_LABEL)) - folder_entry = self.Post(folder_entry, uri, - converter=gdata.docs.DocumentListEntryFromString) - - return folder_entry - - - def MoveOutOfFolder(self, source_entry): - """Moves a document into a folder in the Document List Feed. - - Args: - source_entry: DocumentListEntry An object representing the source - document/folder. - - Returns: - True if the entry was moved out. - """ - return self.Delete(source_entry.GetEditLink().href) - - # Deprecated methods - - #@atom.deprecated('Please use Upload instead') - def UploadPresentation(self, media_source, title, folder_or_uri=None): - """Uploads a presentation inside of a MediaSource object to the Document - List feed with the given title. - - This method is deprecated, use Upload instead. - - Args: - media_source: MediaSource The MediaSource object containing a - presentation file to be uploaded. - title: string The title of the presentation on the server after being - uploaded. - folder_or_uri: DocumentListEntry or string (optional) An object with a - link to a folder or a uri to a folder to upload to. - Note: A valid uri for a folder is of the form: - /feeds/folders/private/full/folder%3Afolder_id - - Returns: - A DocumentListEntry containing information about the presentation created - on the Google Documents service. - """ - return self._UploadFile( - media_source, title, self._MakeKindCategory(PRESENTATION_LABEL), - folder_or_uri=folder_or_uri) - - UploadPresentation = atom.deprecated('Please use Upload instead')( - UploadPresentation) - - #@atom.deprecated('Please use Upload instead') - def UploadSpreadsheet(self, media_source, title, folder_or_uri=None): - """Uploads a spreadsheet inside of a MediaSource object to the Document - List feed with the given title. - - This method is deprecated, use Upload instead. - - Args: - media_source: MediaSource The MediaSource object containing a spreadsheet - file to be uploaded. - title: string The title of the spreadsheet on the server after being - uploaded. - folder_or_uri: DocumentListEntry or string (optional) An object with a - link to a folder or a uri to a folder to upload to. - Note: A valid uri for a folder is of the form: - /feeds/folders/private/full/folder%3Afolder_id - - Returns: - A DocumentListEntry containing information about the spreadsheet created - on the Google Documents service. - """ - return self._UploadFile( - media_source, title, self._MakeKindCategory(SPREADSHEET_LABEL), - folder_or_uri=folder_or_uri) - - UploadSpreadsheet = atom.deprecated('Please use Upload instead')( - UploadSpreadsheet) - - #@atom.deprecated('Please use Upload instead') - def UploadDocument(self, media_source, title, folder_or_uri=None): - """Uploads a document inside of a MediaSource object to the Document List - feed with the given title. - - This method is deprecated, use Upload instead. - - Args: - media_source: MediaSource The gdata.MediaSource object containing a - document file to be uploaded. - title: string The title of the document on the server after being - uploaded. - folder_or_uri: DocumentListEntry or string (optional) An object with a - link to a folder or a uri to a folder to upload to. - Note: A valid uri for a folder is of the form: - /feeds/folders/private/full/folder%3Afolder_id - - Returns: - A DocumentListEntry containing information about the document created - on the Google Documents service. - """ - return self._UploadFile( - media_source, title, self._MakeKindCategory(DOCUMENT_LABEL), - folder_or_uri=folder_or_uri) - - UploadDocument = atom.deprecated('Please use Upload instead')( - UploadDocument) - - """Calling any of these functions is the same as calling Export""" - DownloadDocument = atom.deprecated('Please use Export instead')(Export) - DownloadPresentation = atom.deprecated('Please use Export instead')(Export) - DownloadSpreadsheet = atom.deprecated('Please use Export instead')(Export) - - """Calling any of these functions is the same as calling MoveIntoFolder""" - MoveDocumentIntoFolder = atom.deprecated( - 'Please use MoveIntoFolder instead')(MoveIntoFolder) - MovePresentationIntoFolder = atom.deprecated( - 'Please use MoveIntoFolder instead')(MoveIntoFolder) - MoveSpreadsheetIntoFolder = atom.deprecated( - 'Please use MoveIntoFolder instead')(MoveIntoFolder) - MoveFolderIntoFolder = atom.deprecated( - 'Please use MoveIntoFolder instead')(MoveIntoFolder) - - -class DocumentQuery(gdata.service.Query): - - """Object used to construct a URI to query the Google Document List feed""" - - def __init__(self, feed='/feeds/documents', visibility='private', - projection='full', text_query=None, params=None, - categories=None): - """Constructor for Document 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. - - Yields: - A DocumentQuery object used to construct a URI based on the Document - List feed. - """ - self.visibility = visibility - self.projection = projection - 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 Document - List feed. - """ - old_feed = self.feed - self.feed = '/'.join([old_feed, self.visibility, self.projection]) - new_feed = gdata.service.Query.ToUri(self) - self.feed = old_feed - return new_feed - - def AddNamedFolder(self, email, folder_name): - """Adds a named folder category, qualified by a schema. - - This function lets you query for documents that are contained inside a - named folder without fear of collision with other categories. - - Args: - email: string The email of the user who owns the folder. - folder_name: string The name of the folder. - - Returns: - The string of the category that was added to the object. - """ - - category = '{%s%s}%s' % (FOLDERS_SCHEME_PREFIX, email, folder_name) - self.categories.append(category) - return category - - def RemoveNamedFolder(self, email, folder_name): - """Removes a named folder category, qualified by a schema. - - Args: - email: string The email of the user who owns the folder. - folder_name: string The name of the folder. - - Returns: - The string of the category that was removed to the object. - """ - category = '{%s%s}%s' % (FOLDERS_SCHEME_PREFIX, email, folder_name) - self.categories.remove(category) - return category - - -class DocumentAclQuery(gdata.service.Query): - - """Object used to construct a URI to query a Document's ACL feed""" - - def __init__(self, resource_id, feed='/feeds/acl/private/full'): - """Constructor for Document ACL Query - - Args: - resource_id: string The resource id. (e.g. 'document%3Adocument_id', - 'spreadsheet%3Aspreadsheet_id', etc.) - feed: string (optional) The path for the feed. - (e.g. '/feeds/acl/private/full') - - Yields: - A DocumentAclQuery object used to construct a URI based on the Document - ACL feed. - """ - self.resource_id = resource_id - gdata.service.Query.__init__(self, feed) - - 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 Document - ACL feed. - """ - return '%s/%s' % (gdata.service.Query.ToUri(self), self.resource_id) diff --git a/gdata/analytics/dublincore/__init__.py b/gdata/analytics/dublincore/__init__.py deleted file mode 100644 index 22071f7a11..0000000000 --- a/gdata/analytics/dublincore/__init__.py +++ /dev/null @@ -1,15 +0,0 @@ -#!/usr/bin/python -# -# Copyright (C) 2009 Google 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. diff --git a/gdata/analytics/dublincore/data.py b/gdata/analytics/dublincore/data.py deleted file mode 100644 index c6345c16fb..0000000000 --- a/gdata/analytics/dublincore/data.py +++ /dev/null @@ -1,78 +0,0 @@ -#!/usr/bin/python -# -# Copyright (C) 2009 Google 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. - -"""Contains the data classes of the Dublin Core Metadata Initiative (DCMI) Extension""" - - -__author__ = 'j.s@google.com (Jeff Scudder)' - - -import atom.core - - -DC_TEMPLATE = '{http://purl.org/dc/terms/}%s' - - -class Creator(atom.core.XmlElement): - """Entity primarily responsible for making the resource.""" - _qname = DC_TEMPLATE % 'creator' - - -class Date(atom.core.XmlElement): - """Point or period of time associated with an event in the lifecycle of the resource.""" - _qname = DC_TEMPLATE % 'date' - - -class Description(atom.core.XmlElement): - """Account of the resource.""" - _qname = DC_TEMPLATE % 'description' - - -class Format(atom.core.XmlElement): - """File format, physical medium, or dimensions of the resource.""" - _qname = DC_TEMPLATE % 'format' - - -class Identifier(atom.core.XmlElement): - """An unambiguous reference to the resource within a given context.""" - _qname = DC_TEMPLATE % 'identifier' - - -class Language(atom.core.XmlElement): - """Language of the resource.""" - _qname = DC_TEMPLATE % 'language' - - -class Publisher(atom.core.XmlElement): - """Entity responsible for making the resource available.""" - _qname = DC_TEMPLATE % 'publisher' - - -class Rights(atom.core.XmlElement): - """Information about rights held in and over the resource.""" - _qname = DC_TEMPLATE % 'rights' - - -class Subject(atom.core.XmlElement): - """Topic of the resource.""" - _qname = DC_TEMPLATE % 'subject' - - -class Title(atom.core.XmlElement): - """Name given to the resource.""" - _qname = DC_TEMPLATE % 'title' - - diff --git a/gdata/analytics/exif/__init__.py b/gdata/analytics/exif/__init__.py deleted file mode 100644 index 7f1f9c2abd..0000000000 --- a/gdata/analytics/exif/__init__.py +++ /dev/null @@ -1,217 +0,0 @@ -# -*-*- encoding: utf-8 -*-*- -# -# This is gdata.photos.exif, implementing the exif namespace in gdata -# -# $Id: __init__.py 81 2007-10-03 14:41:42Z havard.gulldahl $ -# -# Copyright 2007 HÃ¥vard Gulldahl -# Portions copyright 2007 Google 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. - -"""This module maps elements from the {EXIF} namespace[1] to GData objects. -These elements describe image data, using exif attributes[2]. - -Picasa Web Albums uses the exif namespace to represent Exif data encoded -in a photo [3]. - -Picasa Web Albums uses the following exif elements: -exif:distance -exif:exposure -exif:flash -exif:focallength -exif:fstop -exif:imageUniqueID -exif:iso -exif:make -exif:model -exif:tags -exif:time - -[1]: http://schemas.google.com/photos/exif/2007. -[2]: http://en.wikipedia.org/wiki/Exif -[3]: http://code.google.com/apis/picasaweb/reference.html#exif_reference -""" - - -__author__ = u'havard@gulldahl.no'# (HÃ¥vard Gulldahl)' #BUG: pydoc chokes on non-ascii chars in __author__ -__license__ = 'Apache License v2' - - -import atom -import gdata - -EXIF_NAMESPACE = 'http://schemas.google.com/photos/exif/2007' - -class ExifBaseElement(atom.AtomBase): - """Base class for elements in the EXIF_NAMESPACE (%s). To add new elements, you only need to add the element tag name to self._tag - """ % EXIF_NAMESPACE - - _tag = '' - _namespace = EXIF_NAMESPACE - _children = atom.AtomBase._children.copy() - _attributes = atom.AtomBase._attributes.copy() - - def __init__(self, name=None, extension_elements=None, - extension_attributes=None, text=None): - self.name = name - self.text = text - self.extension_elements = extension_elements or [] - self.extension_attributes = extension_attributes or {} - -class Distance(ExifBaseElement): - "(float) The distance to the subject, e.g. 0.0" - - _tag = 'distance' -def DistanceFromString(xml_string): - return atom.CreateClassFromXMLString(Distance, xml_string) - -class Exposure(ExifBaseElement): - "(float) The exposure time used, e.g. 0.025 or 8.0E4" - - _tag = 'exposure' -def ExposureFromString(xml_string): - return atom.CreateClassFromXMLString(Exposure, xml_string) - -class Flash(ExifBaseElement): - """(string) Boolean value indicating whether the flash was used. - The .text attribute will either be `true' or `false' - - As a convenience, this object's .bool method will return what you want, - so you can say: - - flash_used = bool(Flash) - - """ - - _tag = 'flash' - def __bool__(self): - if self.text.lower() in ('true','false'): - return self.text.lower() == 'true' -def FlashFromString(xml_string): - return atom.CreateClassFromXMLString(Flash, xml_string) - -class Focallength(ExifBaseElement): - "(float) The focal length used, e.g. 23.7" - - _tag = 'focallength' -def FocallengthFromString(xml_string): - return atom.CreateClassFromXMLString(Focallength, xml_string) - -class Fstop(ExifBaseElement): - "(float) The fstop value used, e.g. 5.0" - - _tag = 'fstop' -def FstopFromString(xml_string): - return atom.CreateClassFromXMLString(Fstop, xml_string) - -class ImageUniqueID(ExifBaseElement): - "(string) The unique image ID for the photo. Generated by Google Photo servers" - - _tag = 'imageUniqueID' -def ImageUniqueIDFromString(xml_string): - return atom.CreateClassFromXMLString(ImageUniqueID, xml_string) - -class Iso(ExifBaseElement): - "(int) The iso equivalent value used, e.g. 200" - - _tag = 'iso' -def IsoFromString(xml_string): - return atom.CreateClassFromXMLString(Iso, xml_string) - -class Make(ExifBaseElement): - "(string) The make of the camera used, e.g. Fictitious Camera Company" - - _tag = 'make' -def MakeFromString(xml_string): - return atom.CreateClassFromXMLString(Make, xml_string) - -class Model(ExifBaseElement): - "(string) The model of the camera used,e.g AMAZING-100D" - - _tag = 'model' -def ModelFromString(xml_string): - return atom.CreateClassFromXMLString(Model, xml_string) - -class Time(ExifBaseElement): - """(int) The date/time the photo was taken, e.g. 1180294337000. - Represented as the number of milliseconds since January 1st, 1970. - - The value of this element will always be identical to the value - of the <gphoto:timestamp>. - - Look at this object's .isoformat() for a human friendly datetime string: - - photo_epoch = Time.text # 1180294337000 - photo_isostring = Time.isoformat() # '2007-05-27T19:32:17.000Z' - - Alternatively: - photo_datetime = Time.datetime() # (requires python >= 2.3) - """ - - _tag = 'time' - def isoformat(self): - """(string) Return the timestamp as a ISO 8601 formatted string, - e.g. '2007-05-27T19:32:17.000Z' - """ - import time - epoch = float(self.text)/1000 - return time.strftime('%Y-%m-%dT%H:%M:%S.000Z', time.gmtime(epoch)) - - def datetime(self): - """(datetime.datetime) Return the timestamp as a datetime.datetime object - - Requires python 2.3 - """ - import datetime - epoch = float(self.text)/1000 - return datetime.datetime.fromtimestamp(epoch) - -def TimeFromString(xml_string): - return atom.CreateClassFromXMLString(Time, xml_string) - -class Tags(ExifBaseElement): - """The container for all exif elements. - The <exif:tags> element can appear as a child of a photo entry. - """ - - _tag = 'tags' - _children = atom.AtomBase._children.copy() - _children['{%s}fstop' % EXIF_NAMESPACE] = ('fstop', Fstop) - _children['{%s}make' % EXIF_NAMESPACE] = ('make', Make) - _children['{%s}model' % EXIF_NAMESPACE] = ('model', Model) - _children['{%s}distance' % EXIF_NAMESPACE] = ('distance', Distance) - _children['{%s}exposure' % EXIF_NAMESPACE] = ('exposure', Exposure) - _children['{%s}flash' % EXIF_NAMESPACE] = ('flash', Flash) - _children['{%s}focallength' % EXIF_NAMESPACE] = ('focallength', Focallength) - _children['{%s}iso' % EXIF_NAMESPACE] = ('iso', Iso) - _children['{%s}time' % EXIF_NAMESPACE] = ('time', Time) - _children['{%s}imageUniqueID' % EXIF_NAMESPACE] = ('imageUniqueID', ImageUniqueID) - - def __init__(self, extension_elements=None, extension_attributes=None, text=None): - ExifBaseElement.__init__(self, extension_elements=extension_elements, - extension_attributes=extension_attributes, - text=text) - self.fstop=None - self.make=None - self.model=None - self.distance=None - self.exposure=None - self.flash=None - self.focallength=None - self.iso=None - self.time=None - self.imageUniqueID=None -def TagsFromString(xml_string): - return atom.CreateClassFromXMLString(Tags, xml_string) - diff --git a/gdata/analytics/finance/__init__.py b/gdata/analytics/finance/__init__.py deleted file mode 100644 index 28ab898d06..0000000000 --- a/gdata/analytics/finance/__init__.py +++ /dev/null @@ -1,486 +0,0 @@ -#!/usr/bin/python -# -# Copyright (C) 2009 Tan Swee Heng -# -# 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. - - -"""Contains extensions to Atom objects used with Google Finance.""" - - -__author__ = 'thesweeheng@gmail.com' - - -import atom -import gdata - - -GD_NAMESPACE = 'http://schemas.google.com/g/2005' -GF_NAMESPACE = 'http://schemas.google.com/finance/2007' - - -class Money(atom.AtomBase): - """The <gd:money> element.""" - _tag = 'money' - _namespace = GD_NAMESPACE - _attributes = atom.AtomBase._attributes.copy() - _attributes['amount'] = 'amount' - _attributes['currencyCode'] = 'currency_code' - - def __init__(self, amount=None, currency_code=None, **kwargs): - self.amount = amount - self.currency_code = currency_code - atom.AtomBase.__init__(self, **kwargs) - - def __str__(self): - return "%s %s" % (self.amount, self.currency_code) - - -def MoneyFromString(xml_string): - return atom.CreateClassFromXMLString(Money, xml_string) - - -class _Monies(atom.AtomBase): - """An element containing multiple <gd:money> in multiple currencies.""" - _namespace = GF_NAMESPACE - _children = atom.AtomBase._children.copy() - _children['{%s}money' % GD_NAMESPACE] = ('money', [Money]) - - def __init__(self, money=None, **kwargs): - self.money = money or [] - atom.AtomBase.__init__(self, **kwargs) - - def __str__(self): - return " / ".join(["%s" % i for i in self.money]) - - -class CostBasis(_Monies): - """The <gf:costBasis> element.""" - _tag = 'costBasis' - - -def CostBasisFromString(xml_string): - return atom.CreateClassFromXMLString(CostBasis, xml_string) - - -class DaysGain(_Monies): - """The <gf:daysGain> element.""" - _tag = 'daysGain' - - -def DaysGainFromString(xml_string): - return atom.CreateClassFromXMLString(DaysGain, xml_string) - - -class Gain(_Monies): - """The <gf:gain> element.""" - _tag = 'gain' - - -def GainFromString(xml_string): - return atom.CreateClassFromXMLString(Gain, xml_string) - - -class MarketValue(_Monies): - """The <gf:marketValue> element.""" - _tag = 'gain' - _tag = 'marketValue' - - -def MarketValueFromString(xml_string): - return atom.CreateClassFromXMLString(MarketValue, xml_string) - - -class Commission(_Monies): - """The <gf:commission> element.""" - _tag = 'commission' - - -def CommissionFromString(xml_string): - return atom.CreateClassFromXMLString(Commission, xml_string) - - -class Price(_Monies): - """The <gf:price> element.""" - _tag = 'price' - - -def PriceFromString(xml_string): - return atom.CreateClassFromXMLString(Price, xml_string) - - -class Symbol(atom.AtomBase): - """The <gf:symbol> element.""" - _tag = 'symbol' - _namespace = GF_NAMESPACE - _attributes = atom.AtomBase._attributes.copy() - _attributes['fullName'] = 'full_name' - _attributes['exchange'] = 'exchange' - _attributes['symbol'] = 'symbol' - - def __init__(self, full_name=None, exchange=None, symbol=None, **kwargs): - self.full_name = full_name - self.exchange = exchange - self.symbol = symbol - atom.AtomBase.__init__(self, **kwargs) - - def __str__(self): - return "%s:%s (%s)" % (self.exchange, self.symbol, self.full_name) - - -def SymbolFromString(xml_string): - return atom.CreateClassFromXMLString(Symbol, xml_string) - - -class TransactionData(atom.AtomBase): - """The <gf:transactionData> element.""" - _tag = 'transactionData' - _namespace = GF_NAMESPACE - _attributes = atom.AtomBase._attributes.copy() - _attributes['type'] = 'type' - _attributes['date'] = 'date' - _attributes['shares'] = 'shares' - _attributes['notes'] = 'notes' - _children = atom.AtomBase._children.copy() - _children['{%s}commission' % GF_NAMESPACE] = ('commission', Commission) - _children['{%s}price' % GF_NAMESPACE] = ('price', Price) - - def __init__(self, type=None, date=None, shares=None, - notes=None, commission=None, price=None, **kwargs): - self.type = type - self.date = date - self.shares = shares - self.notes = notes - self.commission = commission - self.price = price - atom.AtomBase.__init__(self, **kwargs) - - -def TransactionDataFromString(xml_string): - return atom.CreateClassFromXMLString(TransactionData, xml_string) - - -class TransactionEntry(gdata.GDataEntry): - """An entry of the transaction feed. - - A TransactionEntry contains TransactionData such as the transaction - type (Buy, Sell, Sell Short, or Buy to Cover), the number of units, - the date, the price, any commission, and any notes. - """ - _tag = 'entry' - _namespace = atom.ATOM_NAMESPACE - _children = gdata.GDataEntry._children.copy() - _children['{%s}transactionData' % GF_NAMESPACE] = ( - 'transaction_data', TransactionData) - - def __init__(self, transaction_data=None, **kwargs): - self.transaction_data = transaction_data - gdata.GDataEntry.__init__(self, **kwargs) - - def transaction_id(self): - return self.id.text.split("/")[-1] - - transaction_id = property(transaction_id, doc='The transaction ID.') - - -def TransactionEntryFromString(xml_string): - return atom.CreateClassFromXMLString(TransactionEntry, xml_string) - - -class TransactionFeed(gdata.GDataFeed): - """A feed that lists all of the transactions that have been recorded for - a particular position. - - A transaction is a collection of information about an instance of - buying or selling a particular security. The TransactionFeed lists all - of the transactions that have been recorded for a particular position - as a list of TransactionEntries. - """ - _tag = 'feed' - _namespace = atom.ATOM_NAMESPACE - _children = gdata.GDataFeed._children.copy() - _children['{%s}entry' % atom.ATOM_NAMESPACE] = ('entry', [TransactionEntry]) - - -def TransactionFeedFromString(xml_string): - return atom.CreateClassFromXMLString(TransactionFeed, xml_string) - - -class TransactionFeedLink(atom.AtomBase): - """Link to TransactionFeed embedded in PositionEntry. - - If a PositionFeed is queried with transactions='true', TransactionFeeds - are inlined in the returned PositionEntries. These TransactionFeeds are - accessible via TransactionFeedLink's feed attribute. - """ - _tag = 'feedLink' - _namespace = GD_NAMESPACE - _attributes = atom.AtomBase._attributes.copy() - _attributes['href'] = 'href' - _children = atom.AtomBase._children.copy() - _children['{%s}feed' % atom.ATOM_NAMESPACE] = ( - 'feed', TransactionFeed) - - def __init__(self, href=None, feed=None, **kwargs): - self.href = href - self.feed = feed - atom.AtomBase.__init__(self, **kwargs) - - -class PositionData(atom.AtomBase): - """The <gf:positionData> element.""" - _tag = 'positionData' - _namespace = GF_NAMESPACE - _attributes = atom.AtomBase._attributes.copy() - _attributes['gainPercentage'] = 'gain_percentage' - _attributes['return1w'] = 'return1w' - _attributes['return4w'] = 'return4w' - _attributes['return3m'] = 'return3m' - _attributes['returnYTD'] = 'returnYTD' - _attributes['return1y'] = 'return1y' - _attributes['return3y'] = 'return3y' - _attributes['return5y'] = 'return5y' - _attributes['returnOverall'] = 'return_overall' - _attributes['shares'] = 'shares' - _children = atom.AtomBase._children.copy() - _children['{%s}costBasis' % GF_NAMESPACE] = ('cost_basis', CostBasis) - _children['{%s}daysGain' % GF_NAMESPACE] = ('days_gain', DaysGain) - _children['{%s}gain' % GF_NAMESPACE] = ('gain', Gain) - _children['{%s}marketValue' % GF_NAMESPACE] = ('market_value', MarketValue) - - def __init__(self, gain_percentage=None, - return1w=None, return4w=None, return3m=None, returnYTD=None, - return1y=None, return3y=None, return5y=None, return_overall=None, - shares=None, cost_basis=None, days_gain=None, - gain=None, market_value=None, **kwargs): - self.gain_percentage = gain_percentage - self.return1w = return1w - self.return4w = return4w - self.return3m = return3m - self.returnYTD = returnYTD - self.return1y = return1y - self.return3y = return3y - self.return5y = return5y - self.return_overall = return_overall - self.shares = shares - self.cost_basis = cost_basis - self.days_gain = days_gain - self.gain = gain - self.market_value = market_value - atom.AtomBase.__init__(self, **kwargs) - - -def PositionDataFromString(xml_string): - return atom.CreateClassFromXMLString(PositionData, xml_string) - - -class PositionEntry(gdata.GDataEntry): - """An entry of the position feed. - - A PositionEntry contains the ticker exchange and Symbol for a stock, - mutual fund, or other security, along with PositionData such as the - number of units of that security that the user holds, and performance - statistics. - """ - _tag = 'entry' - _namespace = atom.ATOM_NAMESPACE - _children = gdata.GDataEntry._children.copy() - _children['{%s}positionData' % GF_NAMESPACE] = ( - 'position_data', PositionData) - _children['{%s}symbol' % GF_NAMESPACE] = ('symbol', Symbol) - _children['{%s}feedLink' % GD_NAMESPACE] = ( - 'feed_link', TransactionFeedLink) - - def __init__(self, position_data=None, symbol=None, feed_link=None, - **kwargs): - self.position_data = position_data - self.symbol = symbol - self.feed_link = feed_link - gdata.GDataEntry.__init__(self, **kwargs) - - def position_title(self): - return self.title.text - - position_title = property(position_title, - doc='The position title as a string (i.e. position.title.text).') - - def ticker_id(self): - return self.id.text.split("/")[-1] - - ticker_id = property(ticker_id, doc='The position TICKER ID.') - - def transactions(self): - if self.feed_link.feed: - return self.feed_link.feed.entry - else: - return None - - transactions = property(transactions, doc=""" - Inlined TransactionEntries are returned if PositionFeed is queried - with transactions='true'.""") - - -def PositionEntryFromString(xml_string): - return atom.CreateClassFromXMLString(PositionEntry, xml_string) - - -class PositionFeed(gdata.GDataFeed): - """A feed that lists all of the positions in a particular portfolio. - - A position is a collection of information about a security that the - user holds. The PositionFeed lists all of the positions in a particular - portfolio as a list of PositionEntries. - """ - _tag = 'feed' - _namespace = atom.ATOM_NAMESPACE - _children = gdata.GDataFeed._children.copy() - _children['{%s}entry' % atom.ATOM_NAMESPACE] = ('entry', [PositionEntry]) - - -def PositionFeedFromString(xml_string): - return atom.CreateClassFromXMLString(PositionFeed, xml_string) - - -class PositionFeedLink(atom.AtomBase): - """Link to PositionFeed embedded in PortfolioEntry. - - If a PortfolioFeed is queried with positions='true', the PositionFeeds - are inlined in the returned PortfolioEntries. These PositionFeeds are - accessible via PositionFeedLink's feed attribute. - """ - _tag = 'feedLink' - _namespace = GD_NAMESPACE - _attributes = atom.AtomBase._attributes.copy() - _attributes['href'] = 'href' - _children = atom.AtomBase._children.copy() - _children['{%s}feed' % atom.ATOM_NAMESPACE] = ( - 'feed', PositionFeed) - - def __init__(self, href=None, feed=None, **kwargs): - self.href = href - self.feed = feed - atom.AtomBase.__init__(self, **kwargs) - - -class PortfolioData(atom.AtomBase): - """The <gf:portfolioData> element.""" - _tag = 'portfolioData' - _namespace = GF_NAMESPACE - _attributes = atom.AtomBase._attributes.copy() - _attributes['currencyCode'] = 'currency_code' - _attributes['gainPercentage'] = 'gain_percentage' - _attributes['return1w'] = 'return1w' - _attributes['return4w'] = 'return4w' - _attributes['return3m'] = 'return3m' - _attributes['returnYTD'] = 'returnYTD' - _attributes['return1y'] = 'return1y' - _attributes['return3y'] = 'return3y' - _attributes['return5y'] = 'return5y' - _attributes['returnOverall'] = 'return_overall' - _children = atom.AtomBase._children.copy() - _children['{%s}costBasis' % GF_NAMESPACE] = ('cost_basis', CostBasis) - _children['{%s}daysGain' % GF_NAMESPACE] = ('days_gain', DaysGain) - _children['{%s}gain' % GF_NAMESPACE] = ('gain', Gain) - _children['{%s}marketValue' % GF_NAMESPACE] = ('market_value', MarketValue) - - def __init__(self, currency_code=None, gain_percentage=None, - return1w=None, return4w=None, return3m=None, returnYTD=None, - return1y=None, return3y=None, return5y=None, return_overall=None, - cost_basis=None, days_gain=None, gain=None, market_value=None, **kwargs): - self.currency_code = currency_code - self.gain_percentage = gain_percentage - self.return1w = return1w - self.return4w = return4w - self.return3m = return3m - self.returnYTD = returnYTD - self.return1y = return1y - self.return3y = return3y - self.return5y = return5y - self.return_overall = return_overall - self.cost_basis = cost_basis - self.days_gain = days_gain - self.gain = gain - self.market_value = market_value - atom.AtomBase.__init__(self, **kwargs) - - -def PortfolioDataFromString(xml_string): - return atom.CreateClassFromXMLString(PortfolioData, xml_string) - - -class PortfolioEntry(gdata.GDataEntry): - """An entry of the PortfolioFeed. - - A PortfolioEntry contains the portfolio's title along with PortfolioData - such as currency, total market value, and overall performance statistics. - """ - _tag = 'entry' - _namespace = atom.ATOM_NAMESPACE - _children = gdata.GDataEntry._children.copy() - _children['{%s}portfolioData' % GF_NAMESPACE] = ( - 'portfolio_data', PortfolioData) - _children['{%s}feedLink' % GD_NAMESPACE] = ( - 'feed_link', PositionFeedLink) - - def __init__(self, portfolio_data=None, feed_link=None, **kwargs): - self.portfolio_data = portfolio_data - self.feed_link = feed_link - gdata.GDataEntry.__init__(self, **kwargs) - - def portfolio_title(self): - return self.title.text - - def set_portfolio_title(self, portfolio_title): - self.title = atom.Title(text=portfolio_title, title_type='text') - - portfolio_title = property(portfolio_title, set_portfolio_title, - doc='The portfolio title as a string (i.e. portfolio.title.text).') - - def portfolio_id(self): - return self.id.text.split("/")[-1] - - portfolio_id = property(portfolio_id, - doc='The portfolio ID. Do not confuse with portfolio.id.') - - def positions(self): - if self.feed_link.feed: - return self.feed_link.feed.entry - else: - return None - - positions = property(positions, doc=""" - Inlined PositionEntries are returned if PortfolioFeed was queried - with positions='true'.""") - - -def PortfolioEntryFromString(xml_string): - return atom.CreateClassFromXMLString(PortfolioEntry, xml_string) - - -class PortfolioFeed(gdata.GDataFeed): - """A feed that lists all of the user's portfolios. - - A portfolio is a collection of positions that the user holds in various - securities, plus metadata. The PortfolioFeed lists all of the user's - portfolios as a list of PortfolioEntries. - """ - _tag = 'feed' - _namespace = atom.ATOM_NAMESPACE - _children = gdata.GDataFeed._children.copy() - _children['{%s}entry' % atom.ATOM_NAMESPACE] = ('entry', [PortfolioEntry]) - - -def PortfolioFeedFromString(xml_string): - return atom.CreateClassFromXMLString(PortfolioFeed, xml_string) - - diff --git a/gdata/analytics/finance/data.py b/gdata/analytics/finance/data.py deleted file mode 100644 index 5e0caa8920..0000000000 --- a/gdata/analytics/finance/data.py +++ /dev/null @@ -1,156 +0,0 @@ -#!/usr/bin/python -# -# Copyright (C) 2009 Google 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. - - -"""Contains the data classes of the Google Finance Portfolio Data API""" - - -__author__ = 'j.s@google.com (Jeff Scudder)' - - -import atom.core -import atom.data -import gdata.data -import gdata.opensearch.data - - -GF_TEMPLATE = '{http://schemas.google.com/finance/2007/}%s' - - -class Commission(atom.core.XmlElement): - """Commission for the transaction""" - _qname = GF_TEMPLATE % 'commission' - money = [gdata.data.Money] - - -class CostBasis(atom.core.XmlElement): - """Cost basis for the portfolio or position""" - _qname = GF_TEMPLATE % 'costBasis' - money = [gdata.data.Money] - - -class DaysGain(atom.core.XmlElement): - """Today's gain for the portfolio or position""" - _qname = GF_TEMPLATE % 'daysGain' - money = [gdata.data.Money] - - -class Gain(atom.core.XmlElement): - """Total gain for the portfolio or position""" - _qname = GF_TEMPLATE % 'gain' - money = [gdata.data.Money] - - -class MarketValue(atom.core.XmlElement): - """Market value for the portfolio or position""" - _qname = GF_TEMPLATE % 'marketValue' - money = [gdata.data.Money] - - -class PortfolioData(atom.core.XmlElement): - """Data for the portfolio""" - _qname = GF_TEMPLATE % 'portfolioData' - return_overall = 'returnOverall' - currency_code = 'currencyCode' - return3y = 'return3y' - return4w = 'return4w' - market_value = MarketValue - return_y_t_d = 'returnYTD' - cost_basis = CostBasis - gain_percentage = 'gainPercentage' - days_gain = DaysGain - return3m = 'return3m' - return5y = 'return5y' - return1w = 'return1w' - gain = Gain - return1y = 'return1y' - - -class PortfolioEntry(gdata.data.GDEntry): - """Describes an entry in a feed of Finance portfolios""" - portfolio_data = PortfolioData - - -class PortfolioFeed(gdata.data.GDFeed): - """Describes a Finance portfolio feed""" - entry = [PortfolioEntry] - - -class PositionData(atom.core.XmlElement): - """Data for the position""" - _qname = GF_TEMPLATE % 'positionData' - return_y_t_d = 'returnYTD' - return5y = 'return5y' - return_overall = 'returnOverall' - cost_basis = CostBasis - return3y = 'return3y' - return1y = 'return1y' - return4w = 'return4w' - shares = 'shares' - days_gain = DaysGain - gain_percentage = 'gainPercentage' - market_value = MarketValue - gain = Gain - return3m = 'return3m' - return1w = 'return1w' - - -class Price(atom.core.XmlElement): - """Price of the transaction""" - _qname = GF_TEMPLATE % 'price' - money = [gdata.data.Money] - - -class Symbol(atom.core.XmlElement): - """Stock symbol for the company""" - _qname = GF_TEMPLATE % 'symbol' - symbol = 'symbol' - exchange = 'exchange' - full_name = 'fullName' - - -class PositionEntry(gdata.data.GDEntry): - """Describes an entry in a feed of Finance positions""" - symbol = Symbol - position_data = PositionData - - -class PositionFeed(gdata.data.GDFeed): - """Describes a Finance position feed""" - entry = [PositionEntry] - - -class TransactionData(atom.core.XmlElement): - """Data for the transction""" - _qname = GF_TEMPLATE % 'transactionData' - shares = 'shares' - notes = 'notes' - date = 'date' - type = 'type' - commission = Commission - price = Price - - -class TransactionEntry(gdata.data.GDEntry): - """Describes an entry in a feed of Finance transactions""" - transaction_data = TransactionData - - -class TransactionFeed(gdata.data.GDFeed): - """Describes a Finance transaction feed""" - entry = [TransactionEntry] - - diff --git a/gdata/analytics/finance/service.py b/gdata/analytics/finance/service.py deleted file mode 100644 index 6e3eb86d47..0000000000 --- a/gdata/analytics/finance/service.py +++ /dev/null @@ -1,243 +0,0 @@ -#!/usr/bin/python -# -# Copyright (C) 2009 Tan Swee Heng -# -# 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. - - -"""Classes to interact with the Google Finance server.""" - - -__author__ = 'thesweeheng@gmail.com' - - -import gdata.service -import gdata.finance -import atom - - -class PortfolioQuery(gdata.service.Query): - """A query object for the list of a user's portfolios.""" - - def returns(self): - return self.get('returns', False) - - def set_returns(self, value): - if value is 'true' or value is True: - self['returns'] = 'true' - - returns = property(returns, set_returns, doc="The returns query parameter") - - def positions(self): - return self.get('positions', False) - - def set_positions(self, value): - if value is 'true' or value is True: - self['positions'] = 'true' - - positions = property(positions, set_positions, - doc="The positions query parameter") - - -class PositionQuery(gdata.service.Query): - """A query object for the list of a user's positions in a portfolio.""" - - def returns(self): - return self.get('returns', False) - - def set_returns(self, value): - if value is 'true' or value is True: - self['returns'] = 'true' - - returns = property(returns, set_returns, - doc="The returns query parameter") - - def transactions(self): - return self.get('transactions', False) - - def set_transactions(self, value): - if value is 'true' or value is True: - self['transactions'] = 'true' - - transactions = property(transactions, set_transactions, - doc="The transactions query parameter") - - -class FinanceService(gdata.service.GDataService): - - def __init__(self, email=None, password=None, source=None, - server='finance.google.com', **kwargs): - """Creates a client for the Finance 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: 'finance.google.com'. - **kwargs: The other parameters to pass to gdata.service.GDataService - constructor. - """ - gdata.service.GDataService.__init__(self, - email=email, password=password, service='finance', server=server, - **kwargs) - - def GetPortfolioFeed(self, query=None): - uri = '/finance/feeds/default/portfolios' - if query: - uri = PortfolioQuery(feed=uri, params=query).ToUri() - return self.Get(uri, converter=gdata.finance.PortfolioFeedFromString) - - def GetPositionFeed(self, portfolio_entry=None, portfolio_id=None, - query=None): - """ - Args: - portfolio_entry: PortfolioEntry (optional; see Notes) - portfolio_id: string (optional; see Notes) This may be obtained - from a PortfolioEntry's portfolio_id attribute. - query: PortfolioQuery (optional) - - Notes: - Either a PortfolioEntry OR a portfolio ID must be provided. - """ - if portfolio_entry: - uri = portfolio_entry.GetSelfLink().href + '/positions' - elif portfolio_id: - uri = '/finance/feeds/default/portfolios/%s/positions' % portfolio_id - if query: - uri = PositionQuery(feed=uri, params=query).ToUri() - return self.Get(uri, converter=gdata.finance.PositionFeedFromString) - - def GetTransactionFeed(self, position_entry=None, - portfolio_id=None, ticker_id=None): - """ - Args: - position_entry: PositionEntry (optional; see Notes) - portfolio_id: string (optional; see Notes) This may be obtained - from a PortfolioEntry's portfolio_id attribute. - ticker_id: string (optional; see Notes) This may be obtained from - a PositionEntry's ticker_id attribute. Alternatively it can - be constructed using the security's exchange and symbol, - e.g. 'NASDAQ:GOOG' - - Notes: - Either a PositionEntry OR (a portfolio ID AND ticker ID) must - be provided. - """ - if position_entry: - uri = position_entry.GetSelfLink().href + '/transactions' - elif portfolio_id and ticker_id: - uri = '/finance/feeds/default/portfolios/%s/positions/%s/transactions' \ - % (portfolio_id, ticker_id) - return self.Get(uri, converter=gdata.finance.TransactionFeedFromString) - - def GetPortfolio(self, portfolio_id=None, query=None): - uri = '/finance/feeds/default/portfolios/%s' % portfolio_id - if query: - uri = PortfolioQuery(feed=uri, params=query).ToUri() - return self.Get(uri, converter=gdata.finance.PortfolioEntryFromString) - - def AddPortfolio(self, portfolio_entry=None): - uri = '/finance/feeds/default/portfolios' - return self.Post(portfolio_entry, uri, - converter=gdata.finance.PortfolioEntryFromString) - - def UpdatePortfolio(self, portfolio_entry=None): - uri = portfolio_entry.GetEditLink().href - return self.Put(portfolio_entry, uri, - converter=gdata.finance.PortfolioEntryFromString) - - def DeletePortfolio(self, portfolio_entry=None): - uri = portfolio_entry.GetEditLink().href - return self.Delete(uri) - - def GetPosition(self, portfolio_id=None, ticker_id=None, query=None): - uri = '/finance/feeds/default/portfolios/%s/positions/%s' \ - % (portfolio_id, ticker_id) - if query: - uri = PositionQuery(feed=uri, params=query).ToUri() - return self.Get(uri, converter=gdata.finance.PositionEntryFromString) - - def DeletePosition(self, position_entry=None, - portfolio_id=None, ticker_id=None, transaction_feed=None): - """A position is deleted by deleting all its transactions. - - Args: - position_entry: PositionEntry (optional; see Notes) - portfolio_id: string (optional; see Notes) This may be obtained - from a PortfolioEntry's portfolio_id attribute. - ticker_id: string (optional; see Notes) This may be obtained from - a PositionEntry's ticker_id attribute. Alternatively it can - be constructed using the security's exchange and symbol, - e.g. 'NASDAQ:GOOG' - transaction_feed: TransactionFeed (optional; see Notes) - - Notes: - Either a PositionEntry OR (a portfolio ID AND ticker ID) OR - a TransactionFeed must be provided. - """ - if transaction_feed: - feed = transaction_feed - else: - if position_entry: - feed = self.GetTransactionFeed(position_entry=position_entry) - elif portfolio_id and ticker_id: - feed = self.GetTransactionFeed( - portfolio_id=portfolio_id, ticker_id=ticker_id) - for txn in feed.entry: - self.DeleteTransaction(txn) - return True - - def GetTransaction(self, portfolio_id=None, ticker_id=None, - transaction_id=None): - uri = '/finance/feeds/default/portfolios/%s/positions/%s/transactions/%s' \ - % (portfolio_id, ticker_id, transaction_id) - return self.Get(uri, converter=gdata.finance.TransactionEntryFromString) - - def AddTransaction(self, transaction_entry=None, transaction_feed = None, - position_entry=None, portfolio_id=None, ticker_id=None): - """ - Args: - transaction_entry: TransactionEntry (required) - transaction_feed: TransactionFeed (optional; see Notes) - position_entry: PositionEntry (optional; see Notes) - portfolio_id: string (optional; see Notes) This may be obtained - from a PortfolioEntry's portfolio_id attribute. - ticker_id: string (optional; see Notes) This may be obtained from - a PositionEntry's ticker_id attribute. Alternatively it can - be constructed using the security's exchange and symbol, - e.g. 'NASDAQ:GOOG' - - Notes: - Either a TransactionFeed OR a PositionEntry OR (a portfolio ID AND - ticker ID) must be provided. - """ - if transaction_feed: - uri = transaction_feed.GetPostLink().href - elif position_entry: - uri = position_entry.GetSelfLink().href + '/transactions' - elif portfolio_id and ticker_id: - uri = '/finance/feeds/default/portfolios/%s/positions/%s/transactions' \ - % (portfolio_id, ticker_id) - return self.Post(transaction_entry, uri, - converter=gdata.finance.TransactionEntryFromString) - - def UpdateTransaction(self, transaction_entry=None): - uri = transaction_entry.GetEditLink().href - return self.Put(transaction_entry, uri, - converter=gdata.finance.TransactionEntryFromString) - - def DeleteTransaction(self, transaction_entry=None): - uri = transaction_entry.GetEditLink().href - return self.Delete(uri) diff --git a/gdata/analytics/gauth.py b/gdata/analytics/gauth.py deleted file mode 100644 index 4f2f6d1412..0000000000 --- a/gdata/analytics/gauth.py +++ /dev/null @@ -1,1552 +0,0 @@ -#!/usr/bin/env python -# -# Copyright (C) 2009 Google 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. - - -# This module is used for version 2 of the Google Data APIs. - - -"""Provides auth related token classes and functions for Google Data APIs. - -Token classes represent a user's authorization of this app to access their -data. Usually these are not created directly but by a GDClient object. - -ClientLoginToken -AuthSubToken -SecureAuthSubToken -OAuthHmacToken -OAuthRsaToken -TwoLeggedOAuthHmacToken -TwoLeggedOAuthRsaToken - -Functions which are often used in application code (as opposed to just within -the gdata-python-client library) are the following: - -generate_auth_sub_url -authorize_request_token - -The following are helper functions which are used to save and load auth token -objects in the App Engine datastore. These should only be used if you are using -this library within App Engine: - -ae_load -ae_save -""" - - -import datetime -import time -import random -import urllib -import urlparse -import atom.http_core - -try: - import simplejson -except ImportError: - try: - # Try to import from django, should work on App Engine - from django.utils import simplejson - except ImportError: - # Should work for Python2.6 and higher. - import json as simplejson - -try: - from urlparse import parse_qsl -except ImportError: - from cgi import parse_qsl - - -__author__ = 'j.s@google.com (Jeff Scudder)' - - -PROGRAMMATIC_AUTH_LABEL = 'GoogleLogin auth=' -AUTHSUB_AUTH_LABEL = 'AuthSub token=' -OAUTH2_AUTH_LABEL = 'OAuth ' - - -# This dict provides the AuthSub and OAuth scopes for all services by service -# name. The service name (key) is used in ClientLogin requests. -AUTH_SCOPES = { - 'cl': ( # Google Calendar API - 'https://www.google.com/calendar/feeds/', - 'http://www.google.com/calendar/feeds/'), - 'gbase': ( # Google Base API - 'http://base.google.com/base/feeds/', - 'http://www.google.com/base/feeds/'), - 'blogger': ( # Blogger API - 'http://www.blogger.com/feeds/',), - 'codesearch': ( # Google Code Search API - 'http://www.google.com/codesearch/feeds/',), - 'cp': ( # Contacts API - 'https://www.google.com/m8/feeds/', - 'http://www.google.com/m8/feeds/'), - 'finance': ( # Google Finance API - 'http://finance.google.com/finance/feeds/',), - 'health': ( # Google Health API - 'https://www.google.com/health/feeds/',), - 'writely': ( # Documents List API - 'https://docs.google.com/feeds/', - 'https://spreadsheets.google.com/feeds/', - 'https://docs.googleusercontent.com/'), - 'lh2': ( # Picasa Web Albums API - 'http://picasaweb.google.com/data/',), - 'apps': ( # Google Apps Provisioning API - 'http://www.google.com/a/feeds/', - 'https://www.google.com/a/feeds/', - 'http://apps-apis.google.com/a/feeds/', - 'https://apps-apis.google.com/a/feeds/'), - 'weaver': ( # Health H9 Sandbox - 'https://www.google.com/h9/feeds/',), - 'wise': ( # Spreadsheets Data API - 'https://spreadsheets.google.com/feeds/'), - 'sitemaps': ( # Google Webmaster Tools API - 'https://www.google.com/webmasters/tools/feeds/',), - 'youtube': ( # YouTube API - 'http://gdata.youtube.com/feeds/api/', - 'http://uploads.gdata.youtube.com/feeds/api', - 'http://gdata.youtube.com/action/GetUploadToken'), - 'books': ( # Google Books API - 'http://www.google.com/books/feeds/',), - 'analytics': ( # Google Analytics API - 'https://www.google.com/analytics/feeds/',), - 'jotspot': ( # Google Sites API - 'http://sites.google.com/feeds/', - 'https://sites.google.com/feeds/'), - 'local': ( # Google Maps Data API - 'http://maps.google.com/maps/feeds/',), - 'code': ( # Project Hosting Data API - 'http://code.google.com/feeds/issues',)} - - - -class Error(Exception): - pass - - -class UnsupportedTokenType(Error): - """Raised when token to or from blob is unable to convert the token.""" - pass - - -class OAuth2AccessTokenError(Error): - """Raised when an OAuth2 error occurs.""" - def __init__(self, error_message): - self.error_message = error_message - - -# ClientLogin functions and classes. -def generate_client_login_request_body(email, password, service, source, - account_type='HOSTED_OR_GOOGLE', captcha_token=None, - captcha_response=None): - """Creates the body of the autentication request - - See http://code.google.com/apis/accounts/AuthForInstalledApps.html#Request - for more details. - - Args: - email: str - password: str - service: str - source: str - account_type: str (optional) Defaul is 'HOSTED_OR_GOOGLE', other valid - values are 'GOOGLE' and 'HOSTED' - captcha_token: str (optional) - captcha_response: str (optional) - - Returns: - The HTTP body to send in a request for a client login token. - """ - # Create a POST body containing the user's credentials. - request_fields = {'Email': email, - 'Passwd': password, - 'accountType': account_type, - 'service': service, - 'source': source} - if captcha_token and captcha_response: - # Send the captcha token and response as part of the POST body if the - # user is responding to a captch challenge. - request_fields['logintoken'] = captcha_token - request_fields['logincaptcha'] = captcha_response - return urllib.urlencode(request_fields) - - -GenerateClientLoginRequestBody = generate_client_login_request_body - - -def get_client_login_token_string(http_body): - """Returns the token value for a ClientLoginToken. - - Reads the token from the server's response to a Client Login request and - creates the token value string to use in requests. - - Args: - http_body: str The body of the server's HTTP response to a Client Login - request - - Returns: - The token value string for a ClientLoginToken. - """ - for response_line in http_body.splitlines(): - if response_line.startswith('Auth='): - # Strip off the leading Auth= and return the Authorization value. - return response_line[5:] - return None - - -GetClientLoginTokenString = get_client_login_token_string - - -def get_captcha_challenge(http_body, - captcha_base_url='http://www.google.com/accounts/'): - """Returns the URL and token for a CAPTCHA challenge issued by the server. - - Args: - http_body: str The body of the HTTP response from the server which - contains the CAPTCHA challenge. - captcha_base_url: str This function returns a full URL for viewing the - challenge image which is built from the server's response. This - base_url is used as the beginning of the URL because the server - only provides the end of the URL. For example the server provides - 'Captcha?ctoken=Hi...N' and the URL for the image is - 'http://www.google.com/accounts/Captcha?ctoken=Hi...N' - - Returns: - A dictionary containing the information needed to repond to the CAPTCHA - challenge, the image URL and the ID token of the challenge. The - dictionary is in the form: - {'token': string identifying the CAPTCHA image, - 'url': string containing the URL of the image} - Returns None if there was no CAPTCHA challenge in the response. - """ - contains_captcha_challenge = False - captcha_parameters = {} - for response_line in http_body.splitlines(): - if response_line.startswith('Error=CaptchaRequired'): - contains_captcha_challenge = True - elif response_line.startswith('CaptchaToken='): - # Strip off the leading CaptchaToken= - captcha_parameters['token'] = response_line[13:] - elif response_line.startswith('CaptchaUrl='): - captcha_parameters['url'] = '%s%s' % (captcha_base_url, - response_line[11:]) - if contains_captcha_challenge: - return captcha_parameters - else: - return None - - -GetCaptchaChallenge = get_captcha_challenge - - -class ClientLoginToken(object): - - def __init__(self, token_string): - self.token_string = token_string - - def modify_request(self, http_request): - http_request.headers['Authorization'] = '%s%s' % (PROGRAMMATIC_AUTH_LABEL, - self.token_string) - - ModifyRequest = modify_request - - -# AuthSub functions and classes. -def _to_uri(str_or_uri): - if isinstance(str_or_uri, (str, unicode)): - return atom.http_core.Uri.parse_uri(str_or_uri) - return str_or_uri - - -def generate_auth_sub_url(next, scopes, secure=False, session=True, - request_url=atom.http_core.parse_uri( - 'https://www.google.com/accounts/AuthSubRequest'), - domain='default', scopes_param_prefix='auth_sub_scopes'): - """Constructs a URI for requesting a multiscope AuthSub token. - - The generated token will contain a URL parameter to pass along the - requested scopes to the next URL. When the Google Accounts page - redirects the broswser to the 'next' URL, it appends the single use - AuthSub token value to the URL as a URL parameter with the key 'token'. - However, the information about which scopes were requested is not - included by Google Accounts. This method adds the scopes to the next - URL before making the request so that the redirect will be sent to - a page, and both the token value and the list of scopes for which the token - was requested. - - Args: - next: atom.http_core.Uri or string The URL user will be sent to after - authorizing this web application to access their data. - scopes: list containint strings or atom.http_core.Uri objects. The URLs - of the services to be accessed. Could also be a single string - or single atom.http_core.Uri for requesting just one scope. - secure: boolean (optional) Determines whether or not the issued token - is a secure token. - session: boolean (optional) Determines whether or not the issued token - can be upgraded to a session token. - request_url: atom.http_core.Uri or str The beginning of the request URL. - This is normally - 'http://www.google.com/accounts/AuthSubRequest' or - '/accounts/AuthSubRequest' - domain: The domain which the account is part of. This is used for Google - Apps accounts, the default value is 'default' which means that - the requested account is a Google Account (@gmail.com for - example) - scopes_param_prefix: str (optional) The requested scopes are added as a - URL parameter to the next URL so that the page at - the 'next' URL can extract the token value and the - valid scopes from the URL. The key for the URL - parameter defaults to 'auth_sub_scopes' - - Returns: - An atom.http_core.Uri which the user's browser should be directed to in - order to authorize this application to access their information. - """ - if isinstance(next, (str, unicode)): - next = atom.http_core.Uri.parse_uri(next) - # If the user passed in a string instead of a list for scopes, convert to - # a single item tuple. - if isinstance(scopes, (str, unicode, atom.http_core.Uri)): - scopes = (scopes,) - scopes_string = ' '.join([str(scope) for scope in scopes]) - next.query[scopes_param_prefix] = scopes_string - - if isinstance(request_url, (str, unicode)): - request_url = atom.http_core.Uri.parse_uri(request_url) - request_url.query['next'] = str(next) - request_url.query['scope'] = scopes_string - if session: - request_url.query['session'] = '1' - else: - request_url.query['session'] = '0' - if secure: - request_url.query['secure'] = '1' - else: - request_url.query['secure'] = '0' - request_url.query['hd'] = domain - return request_url - - -def auth_sub_string_from_url(url, scopes_param_prefix='auth_sub_scopes'): - """Finds the token string (and scopes) after the browser is redirected. - - After the Google Accounts AuthSub pages redirect the user's broswer back to - the web application (using the 'next' URL from the request) the web app must - extract the token from the current page's URL. The token is provided as a - URL parameter named 'token' and if generate_auth_sub_url was used to create - the request, the token's valid scopes are included in a URL parameter whose - name is specified in scopes_param_prefix. - - Args: - url: atom.url.Url or str representing the current URL. The token value - and valid scopes should be included as URL parameters. - scopes_param_prefix: str (optional) The URL parameter key which maps to - the list of valid scopes for the token. - - Returns: - A tuple containing the token value as a string, and a tuple of scopes - (as atom.http_core.Uri objects) which are URL prefixes under which this - token grants permission to read and write user data. - (token_string, (scope_uri, scope_uri, scope_uri, ...)) - If no scopes were included in the URL, the second value in the tuple is - None. If there was no token param in the url, the tuple returned is - (None, None) - """ - if isinstance(url, (str, unicode)): - url = atom.http_core.Uri.parse_uri(url) - if 'token' not in url.query: - return (None, None) - token = url.query['token'] - # TODO: decide whether no scopes should be None or (). - scopes = None # Default to None for no scopes. - if scopes_param_prefix in url.query: - scopes = tuple(url.query[scopes_param_prefix].split(' ')) - return (token, scopes) - - -AuthSubStringFromUrl = auth_sub_string_from_url - - -def auth_sub_string_from_body(http_body): - """Extracts the AuthSub token from an HTTP body string. - - Used to find the new session token after making a request to upgrade a - single use AuthSub token. - - Args: - http_body: str The repsonse from the server which contains the AuthSub - key. For example, this function would find the new session token - from the server's response to an upgrade token request. - - Returns: - The raw token value string to use in an AuthSubToken object. - """ - for response_line in http_body.splitlines(): - if response_line.startswith('Token='): - # Strip off Token= and return the token value string. - return response_line[6:] - return None - - -class AuthSubToken(object): - - def __init__(self, token_string, scopes=None): - self.token_string = token_string - self.scopes = scopes or [] - - def modify_request(self, http_request): - """Sets Authorization header, allows app to act on the user's behalf.""" - http_request.headers['Authorization'] = '%s%s' % (AUTHSUB_AUTH_LABEL, - self.token_string) - - ModifyRequest = modify_request - - def from_url(str_or_uri): - """Creates a new AuthSubToken using information in the URL. - - Uses auth_sub_string_from_url. - - Args: - str_or_uri: The current page's URL (as a str or atom.http_core.Uri) - which should contain a token query parameter since the - Google auth server redirected the user's browser to this - URL. - """ - token_and_scopes = auth_sub_string_from_url(str_or_uri) - return AuthSubToken(token_and_scopes[0], token_and_scopes[1]) - - from_url = staticmethod(from_url) - FromUrl = from_url - - def _upgrade_token(self, http_body): - """Replaces the token value with a session token from the auth server. - - Uses the response of a token upgrade request to modify this token. Uses - auth_sub_string_from_body. - """ - self.token_string = auth_sub_string_from_body(http_body) - - -# Functions and classes for Secure-mode AuthSub -def build_auth_sub_data(http_request, timestamp, nonce): - """Creates the data string which must be RSA-signed in secure requests. - - For more details see the documenation on secure AuthSub requests: - http://code.google.com/apis/accounts/docs/AuthSub.html#signingrequests - - Args: - http_request: The request being made to the server. The Request's URL - must be complete before this signature is calculated as any changes - to the URL will invalidate the signature. - nonce: str Random 64-bit, unsigned number encoded as an ASCII string in - decimal format. The nonce/timestamp pair should always be unique to - prevent replay attacks. - timestamp: Integer representing the time the request is sent. The - timestamp should be expressed in number of seconds after January 1, - 1970 00:00:00 GMT. - """ - return '%s %s %s %s' % (http_request.method, str(http_request.uri), - str(timestamp), nonce) - - -def generate_signature(data, rsa_key): - """Signs the data string for a secure AuthSub request.""" - import base64 - try: - from tlslite.utils import keyfactory - except ImportError: - from gdata.tlslite.utils import keyfactory - private_key = keyfactory.parsePrivateKey(rsa_key) - signed = private_key.hashAndSign(data) - # Python2.3 and lower does not have the base64.b64encode function. - if hasattr(base64, 'b64encode'): - return base64.b64encode(signed) - else: - return base64.encodestring(signed).replace('\n', '') - - -class SecureAuthSubToken(AuthSubToken): - - def __init__(self, token_string, rsa_private_key, scopes=None): - self.token_string = token_string - self.scopes = scopes or [] - self.rsa_private_key = rsa_private_key - - def from_url(str_or_uri, rsa_private_key): - """Creates a new SecureAuthSubToken using information in the URL. - - Uses auth_sub_string_from_url. - - Args: - str_or_uri: The current page's URL (as a str or atom.http_core.Uri) - which should contain a token query parameter since the Google auth - server redirected the user's browser to this URL. - rsa_private_key: str the private RSA key cert used to sign all requests - made with this token. - """ - token_and_scopes = auth_sub_string_from_url(str_or_uri) - return SecureAuthSubToken(token_and_scopes[0], rsa_private_key, - token_and_scopes[1]) - - from_url = staticmethod(from_url) - FromUrl = from_url - - def modify_request(self, http_request): - """Sets the Authorization header and includes a digital signature. - - Calculates a digital signature using the private RSA key, a timestamp - (uses now at the time this method is called) and a random nonce. - - Args: - http_request: The atom.http_core.HttpRequest which contains all of the - information needed to send a request to the remote server. The - URL and the method of the request must be already set and cannot be - changed after this token signs the request, or the signature will - not be valid. - """ - timestamp = str(int(time.time())) - nonce = ''.join([str(random.randint(0, 9)) for i in xrange(15)]) - data = build_auth_sub_data(http_request, timestamp, nonce) - signature = generate_signature(data, self.rsa_private_key) - http_request.headers['Authorization'] = ( - '%s%s sigalg="rsa-sha1" data="%s" sig="%s"' % (AUTHSUB_AUTH_LABEL, - self.token_string, data, signature)) - - ModifyRequest = modify_request - - -# OAuth functions and classes. -RSA_SHA1 = 'RSA-SHA1' -HMAC_SHA1 = 'HMAC-SHA1' - - -def build_oauth_base_string(http_request, consumer_key, nonce, signaure_type, - timestamp, version, next='oob', token=None, - verifier=None): - """Generates the base string to be signed in the OAuth request. - - Args: - http_request: The request being made to the server. The Request's URL - must be complete before this signature is calculated as any changes - to the URL will invalidate the signature. - consumer_key: Domain identifying the third-party web application. This is - the domain used when registering the application with Google. It - identifies who is making the request on behalf of the user. - nonce: Random 64-bit, unsigned number encoded as an ASCII string in decimal - format. The nonce/timestamp pair should always be unique to prevent - replay attacks. - signaure_type: either RSA_SHA1 or HMAC_SHA1 - timestamp: Integer representing the time the request is sent. The - timestamp should be expressed in number of seconds after January 1, - 1970 00:00:00 GMT. - version: The OAuth version used by the requesting web application. This - value must be '1.0' or '1.0a'. If not provided, Google assumes version - 1.0 is in use. - next: The URL the user should be redirected to after granting access - to a Google service(s). It can include url-encoded query parameters. - The default value is 'oob'. (This is the oauth_callback.) - token: The string for the OAuth request token or OAuth access token. - verifier: str Sent as the oauth_verifier and required when upgrading a - request token to an access token. - """ - # First we must build the canonical base string for the request. - params = http_request.uri.query.copy() - params['oauth_consumer_key'] = consumer_key - params['oauth_nonce'] = nonce - params['oauth_signature_method'] = signaure_type - params['oauth_timestamp'] = str(timestamp) - if next is not None: - params['oauth_callback'] = str(next) - if token is not None: - params['oauth_token'] = token - if version is not None: - params['oauth_version'] = version - if verifier is not None: - params['oauth_verifier'] = verifier - # We need to get the key value pairs in lexigraphically sorted order. - sorted_keys = None - try: - sorted_keys = sorted(params.keys()) - # The sorted function is not available in Python2.3 and lower - except NameError: - sorted_keys = params.keys() - sorted_keys.sort() - pairs = [] - for key in sorted_keys: - pairs.append('%s=%s' % (urllib.quote(key, safe='~'), - urllib.quote(params[key], safe='~'))) - # We want to escape /'s too, so use safe='~' - all_parameters = urllib.quote('&'.join(pairs), safe='~') - normailzed_host = http_request.uri.host.lower() - normalized_scheme = (http_request.uri.scheme or 'http').lower() - non_default_port = None - if (http_request.uri.port is not None - and ((normalized_scheme == 'https' and http_request.uri.port != 443) - or (normalized_scheme == 'http' and http_request.uri.port != 80))): - non_default_port = http_request.uri.port - path = http_request.uri.path or '/' - request_path = None - if not path.startswith('/'): - path = '/%s' % path - if non_default_port is not None: - # Set the only safe char in url encoding to ~ since we want to escape / - # as well. - request_path = urllib.quote('%s://%s:%s%s' % ( - normalized_scheme, normailzed_host, non_default_port, path), safe='~') - else: - # Set the only safe char in url encoding to ~ since we want to escape / - # as well. - request_path = urllib.quote('%s://%s%s' % ( - normalized_scheme, normailzed_host, path), safe='~') - # TODO: ensure that token escaping logic is correct, not sure if the token - # value should be double escaped instead of single. - base_string = '&'.join((http_request.method.upper(), request_path, - all_parameters)) - # Now we have the base string, we can calculate the oauth_signature. - return base_string - - -def generate_hmac_signature(http_request, consumer_key, consumer_secret, - timestamp, nonce, version, next='oob', - token=None, token_secret=None, verifier=None): - import hmac - import base64 - base_string = build_oauth_base_string( - http_request, consumer_key, nonce, HMAC_SHA1, timestamp, version, - next, token, verifier=verifier) - hash_key = None - hashed = None - if token_secret is not None: - hash_key = '%s&%s' % (urllib.quote(consumer_secret, safe='~'), - urllib.quote(token_secret, safe='~')) - else: - hash_key = '%s&' % urllib.quote(consumer_secret, safe='~') - try: - import hashlib - hashed = hmac.new(hash_key, base_string, hashlib.sha1) - except ImportError: - import sha - hashed = hmac.new(hash_key, base_string, sha) - # Python2.3 does not have base64.b64encode. - if hasattr(base64, 'b64encode'): - return base64.b64encode(hashed.digest()) - else: - return base64.encodestring(hashed.digest()).replace('\n', '') - - -def generate_rsa_signature(http_request, consumer_key, rsa_key, - timestamp, nonce, version, next='oob', - token=None, token_secret=None, verifier=None): - import base64 - try: - from tlslite.utils import keyfactory - except ImportError: - from gdata.tlslite.utils import keyfactory - base_string = build_oauth_base_string( - http_request, consumer_key, nonce, RSA_SHA1, timestamp, version, - next, token, verifier=verifier) - private_key = keyfactory.parsePrivateKey(rsa_key) - # Sign using the key - signed = private_key.hashAndSign(base_string) - # Python2.3 does not have base64.b64encode. - if hasattr(base64, 'b64encode'): - return base64.b64encode(signed) - else: - return base64.encodestring(signed).replace('\n', '') - - -def generate_auth_header(consumer_key, timestamp, nonce, signature_type, - signature, version='1.0', next=None, token=None, - verifier=None): - """Builds the Authorization header to be sent in the request. - - Args: - consumer_key: Identifies the application making the request (str). - timestamp: - nonce: - signature_type: One of either HMAC_SHA1 or RSA_SHA1 - signature: The HMAC or RSA signature for the request as a base64 - encoded string. - version: The version of the OAuth protocol that this request is using. - Default is '1.0' - next: The URL of the page that the user's browser should be sent to - after they authorize the token. (Optional) - token: str The OAuth token value to be used in the oauth_token parameter - of the header. - verifier: str The OAuth verifier which must be included when you are - upgrading a request token to an access token. - """ - params = { - 'oauth_consumer_key': consumer_key, - 'oauth_version': version, - 'oauth_nonce': nonce, - 'oauth_timestamp': str(timestamp), - 'oauth_signature_method': signature_type, - 'oauth_signature': signature} - if next is not None: - params['oauth_callback'] = str(next) - if token is not None: - params['oauth_token'] = token - if verifier is not None: - params['oauth_verifier'] = verifier - pairs = [ - '%s="%s"' % ( - k, urllib.quote(v, safe='~')) for k, v in params.iteritems()] - return 'OAuth %s' % (', '.join(pairs)) - - -REQUEST_TOKEN_URL = 'https://www.google.com/accounts/OAuthGetRequestToken' -ACCESS_TOKEN_URL = 'https://www.google.com/accounts/OAuthGetAccessToken' - - -def generate_request_for_request_token( - consumer_key, signature_type, scopes, rsa_key=None, consumer_secret=None, - auth_server_url=REQUEST_TOKEN_URL, next='oob', version='1.0'): - """Creates request to be sent to auth server to get an OAuth request token. - - Args: - consumer_key: - signature_type: either RSA_SHA1 or HMAC_SHA1. The rsa_key must be - provided if the signature type is RSA but if the signature method - is HMAC, the consumer_secret must be used. - scopes: List of URL prefixes for the data which we want to access. For - example, to request access to the user's Blogger and Google Calendar - data, we would request - ['http://www.blogger.com/feeds/', - 'https://www.google.com/calendar/feeds/', - 'http://www.google.com/calendar/feeds/'] - rsa_key: Only used if the signature method is RSA_SHA1. - consumer_secret: Only used if the signature method is HMAC_SHA1. - auth_server_url: The URL to which the token request should be directed. - Defaults to 'https://www.google.com/accounts/OAuthGetRequestToken'. - next: The URL of the page that the user's browser should be sent to - after they authorize the token. (Optional) - version: The OAuth version used by the requesting web application. - Defaults to '1.0a' - - Returns: - An atom.http_core.HttpRequest object with the URL, Authorization header - and body filled in. - """ - request = atom.http_core.HttpRequest(auth_server_url, 'POST') - # Add the requested auth scopes to the Auth request URL. - if scopes: - request.uri.query['scope'] = ' '.join(scopes) - - timestamp = str(int(time.time())) - nonce = ''.join([str(random.randint(0, 9)) for i in xrange(15)]) - signature = None - if signature_type == HMAC_SHA1: - signature = generate_hmac_signature( - request, consumer_key, consumer_secret, timestamp, nonce, version, - next=next) - elif signature_type == RSA_SHA1: - signature = generate_rsa_signature( - request, consumer_key, rsa_key, timestamp, nonce, version, next=next) - else: - return None - - request.headers['Authorization'] = generate_auth_header( - consumer_key, timestamp, nonce, signature_type, signature, version, - next) - request.headers['Content-Length'] = '0' - return request - - -def generate_request_for_access_token( - request_token, auth_server_url=ACCESS_TOKEN_URL): - """Creates a request to ask the OAuth server for an access token. - - Requires a request token which the user has authorized. See the - documentation on OAuth with Google Data for more details: - http://code.google.com/apis/accounts/docs/OAuth.html#AccessToken - - Args: - request_token: An OAuthHmacToken or OAuthRsaToken which the user has - approved using their browser. - auth_server_url: (optional) The URL at which the OAuth access token is - requested. Defaults to - https://www.google.com/accounts/OAuthGetAccessToken - - Returns: - A new HttpRequest object which can be sent to the OAuth server to - request an OAuth Access Token. - """ - http_request = atom.http_core.HttpRequest(auth_server_url, 'POST') - http_request.headers['Content-Length'] = '0' - return request_token.modify_request(http_request) - - -def oauth_token_info_from_body(http_body): - """Exracts an OAuth request token from the server's response. - - Returns: - A tuple of strings containing the OAuth token and token secret. If - neither of these are present in the body, returns (None, None) - """ - token = None - token_secret = None - for pair in http_body.split('&'): - if pair.startswith('oauth_token='): - token = urllib.unquote(pair[len('oauth_token='):]) - if pair.startswith('oauth_token_secret='): - token_secret = urllib.unquote(pair[len('oauth_token_secret='):]) - return (token, token_secret) - - -def hmac_token_from_body(http_body, consumer_key, consumer_secret, - auth_state): - token_value, token_secret = oauth_token_info_from_body(http_body) - token = OAuthHmacToken(consumer_key, consumer_secret, token_value, - token_secret, auth_state) - return token - - -def rsa_token_from_body(http_body, consumer_key, rsa_private_key, - auth_state): - token_value, token_secret = oauth_token_info_from_body(http_body) - token = OAuthRsaToken(consumer_key, rsa_private_key, token_value, - token_secret, auth_state) - return token - - -DEFAULT_DOMAIN = 'default' -OAUTH_AUTHORIZE_URL = 'https://www.google.com/accounts/OAuthAuthorizeToken' - - -def generate_oauth_authorization_url( - token, next=None, hd=DEFAULT_DOMAIN, hl=None, btmpl=None, - auth_server=OAUTH_AUTHORIZE_URL): - """Creates a URL for the page where the request token can be authorized. - - Args: - token: str The request token from the OAuth server. - next: str (optional) URL the user should be redirected to after granting - access to a Google service(s). It can include url-encoded query - parameters. - hd: str (optional) Identifies a particular hosted domain account to be - accessed (for example, 'mycollege.edu'). Uses 'default' to specify a - regular Google account ('username@gmail.com'). - hl: str (optional) An ISO 639 country code identifying what language the - approval page should be translated in (for example, 'hl=en' for - English). The default is the user's selected language. - btmpl: str (optional) Forces a mobile version of the approval page. The - only accepted value is 'mobile'. - auth_server: str (optional) The start of the token authorization web - page. Defaults to - 'https://www.google.com/accounts/OAuthAuthorizeToken' - - Returns: - An atom.http_core.Uri pointing to the token authorization page where the - user may allow or deny this app to access their Google data. - """ - uri = atom.http_core.Uri.parse_uri(auth_server) - uri.query['oauth_token'] = token - uri.query['hd'] = hd - if next is not None: - uri.query['oauth_callback'] = str(next) - if hl is not None: - uri.query['hl'] = hl - if btmpl is not None: - uri.query['btmpl'] = btmpl - return uri - - -def oauth_token_info_from_url(url): - """Exracts an OAuth access token from the redirected page's URL. - - Returns: - A tuple of strings containing the OAuth token and the OAuth verifier which - need to sent when upgrading a request token to an access token. - """ - if isinstance(url, (str, unicode)): - url = atom.http_core.Uri.parse_uri(url) - token = None - verifier = None - if 'oauth_token' in url.query: - token = urllib.unquote(url.query['oauth_token']) - if 'oauth_verifier' in url.query: - verifier = urllib.unquote(url.query['oauth_verifier']) - return (token, verifier) - - -def authorize_request_token(request_token, url): - """Adds information to request token to allow it to become an access token. - - Modifies the request_token object passed in by setting and unsetting the - necessary fields to allow this token to form a valid upgrade request. - - Args: - request_token: The OAuth request token which has been authorized by the - user. In order for this token to be upgraded to an access token, - certain fields must be extracted from the URL and added to the token - so that they can be passed in an upgrade-token request. - url: The URL of the current page which the user's browser was redirected - to after they authorized access for the app. This function extracts - information from the URL which is needed to upgraded the token from - a request token to an access token. - - Returns: - The same token object which was passed in. - """ - token, verifier = oauth_token_info_from_url(url) - request_token.token = token - request_token.verifier = verifier - request_token.auth_state = AUTHORIZED_REQUEST_TOKEN - return request_token - - -AuthorizeRequestToken = authorize_request_token - - -def upgrade_to_access_token(request_token, server_response_body): - """Extracts access token information from response to an upgrade request. - - Once the server has responded with the new token info for the OAuth - access token, this method modifies the request_token to set and unset - necessary fields to create valid OAuth authorization headers for requests. - - Args: - request_token: An OAuth token which this function modifies to allow it - to be used as an access token. - server_response_body: str The server's response to an OAuthAuthorizeToken - request. This should contain the new token and token_secret which - are used to generate the signature and parameters of the Authorization - header in subsequent requests to Google Data APIs. - - Returns: - The same token object which was passed in. - """ - token, token_secret = oauth_token_info_from_body(server_response_body) - request_token.token = token - request_token.token_secret = token_secret - request_token.auth_state = ACCESS_TOKEN - request_token.next = None - request_token.verifier = None - return request_token - - -UpgradeToAccessToken = upgrade_to_access_token - - -REQUEST_TOKEN = 1 -AUTHORIZED_REQUEST_TOKEN = 2 -ACCESS_TOKEN = 3 - - -class OAuthHmacToken(object): - SIGNATURE_METHOD = HMAC_SHA1 - - def __init__(self, consumer_key, consumer_secret, token, token_secret, - auth_state, next=None, verifier=None): - self.consumer_key = consumer_key - self.consumer_secret = consumer_secret - self.token = token - self.token_secret = token_secret - self.auth_state = auth_state - self.next = next - self.verifier = verifier # Used to convert request token to access token. - - def generate_authorization_url( - self, google_apps_domain=DEFAULT_DOMAIN, language=None, btmpl=None, - auth_server=OAUTH_AUTHORIZE_URL): - """Creates the URL at which the user can authorize this app to access. - - Args: - google_apps_domain: str (optional) If the user should be signing in - using an account under a known Google Apps domain, provide the - domain name ('example.com') here. If not provided, 'default' - will be used, and the user will be prompted to select an account - if they are signed in with a Google Account and Google Apps - accounts. - language: str (optional) An ISO 639 country code identifying what - language the approval page should be translated in (for example, - 'en' for English). The default is the user's selected language. - btmpl: str (optional) Forces a mobile version of the approval page. The - only accepted value is 'mobile'. - auth_server: str (optional) The start of the token authorization web - page. Defaults to - 'https://www.google.com/accounts/OAuthAuthorizeToken' - """ - return generate_oauth_authorization_url( - self.token, hd=google_apps_domain, hl=language, btmpl=btmpl, - auth_server=auth_server) - - GenerateAuthorizationUrl = generate_authorization_url - - def modify_request(self, http_request): - """Sets the Authorization header in the HTTP request using the token. - - Calculates an HMAC signature using the information in the token to - indicate that the request came from this application and that this - application has permission to access a particular user's data. - - Returns: - The same HTTP request object which was passed in. - """ - timestamp = str(int(time.time())) - nonce = ''.join([str(random.randint(0, 9)) for i in xrange(15)]) - signature = generate_hmac_signature( - http_request, self.consumer_key, self.consumer_secret, timestamp, - nonce, version='1.0', next=self.next, token=self.token, - token_secret=self.token_secret, verifier=self.verifier) - http_request.headers['Authorization'] = generate_auth_header( - self.consumer_key, timestamp, nonce, HMAC_SHA1, signature, - version='1.0', next=self.next, token=self.token, - verifier=self.verifier) - return http_request - - ModifyRequest = modify_request - - -class OAuthRsaToken(OAuthHmacToken): - SIGNATURE_METHOD = RSA_SHA1 - - def __init__(self, consumer_key, rsa_private_key, token, token_secret, - auth_state, next=None, verifier=None): - self.consumer_key = consumer_key - self.rsa_private_key = rsa_private_key - self.token = token - self.token_secret = token_secret - self.auth_state = auth_state - self.next = next - self.verifier = verifier # Used to convert request token to access token. - - def modify_request(self, http_request): - """Sets the Authorization header in the HTTP request using the token. - - Calculates an RSA signature using the information in the token to - indicate that the request came from this application and that this - application has permission to access a particular user's data. - - Returns: - The same HTTP request object which was passed in. - """ - timestamp = str(int(time.time())) - nonce = ''.join([str(random.randint(0, 9)) for i in xrange(15)]) - signature = generate_rsa_signature( - http_request, self.consumer_key, self.rsa_private_key, timestamp, - nonce, version='1.0', next=self.next, token=self.token, - token_secret=self.token_secret, verifier=self.verifier) - http_request.headers['Authorization'] = generate_auth_header( - self.consumer_key, timestamp, nonce, RSA_SHA1, signature, - version='1.0', next=self.next, token=self.token, - verifier=self.verifier) - return http_request - - ModifyRequest = modify_request - - -class TwoLeggedOAuthHmacToken(OAuthHmacToken): - - def __init__(self, consumer_key, consumer_secret, requestor_id): - self.requestor_id = requestor_id - OAuthHmacToken.__init__( - self, consumer_key, consumer_secret, None, None, ACCESS_TOKEN, - next=None, verifier=None) - - def modify_request(self, http_request): - """Sets the Authorization header in the HTTP request using the token. - - Calculates an HMAC signature using the information in the token to - indicate that the request came from this application and that this - application has permission to access a particular user's data using 2LO. - - Returns: - The same HTTP request object which was passed in. - """ - http_request.uri.query['xoauth_requestor_id'] = self.requestor_id - return OAuthHmacToken.modify_request(self, http_request) - - ModifyRequest = modify_request - - -class TwoLeggedOAuthRsaToken(OAuthRsaToken): - - def __init__(self, consumer_key, rsa_private_key, requestor_id): - self.requestor_id = requestor_id - OAuthRsaToken.__init__( - self, consumer_key, rsa_private_key, None, None, ACCESS_TOKEN, - next=None, verifier=None) - - def modify_request(self, http_request): - """Sets the Authorization header in the HTTP request using the token. - - Calculates an RSA signature using the information in the token to - indicate that the request came from this application and that this - application has permission to access a particular user's data using 2LO. - - Returns: - The same HTTP request object which was passed in. - """ - http_request.uri.query['xoauth_requestor_id'] = self.requestor_id - return OAuthRsaToken.modify_request(self, http_request) - - ModifyRequest = modify_request - - -class OAuth2Token(object): - """Token object for OAuth 2.0 as described on - <http://code.google.com/apis/accounts/docs/OAuth2.html>. - - Token can be applied to a gdata.client.GDClient object using the authorize() - method, which then signs each request from that object with the OAuth 2.0 - access token. - This class supports 3 flows of OAuth 2.0: - Client-side web flow: call generate_authorize_url with `response_type='token'' - and the registered `redirect_uri'. - Server-side web flow: call generate_authorize_url with the registered - `redirect_url'. - Native applications flow: call generate_authorize_url as it is. You will have - to ask the user to go to the generated url and pass in the authorization - code to your application. - """ - - def __init__(self, client_id, client_secret, scope, user_agent, - auth_uri='https://accounts.google.com/o/oauth2/auth', - token_uri='https://accounts.google.com/o/oauth2/token', - access_token=None, refresh_token=None): - """Create an instance of OAuth2Token - - This constructor is not usually called by the user, instead - OAuth2Credentials objects are instantiated by the OAuth2WebServerFlow. - - Args: - client_id: string, client identifier. - client_secret: string client secret. - scope: string, scope of the credentials being requested. - user_agent: string, HTTP User-Agent to provide for this application. - auth_uri: string, URI for authorization endpoint. For convenience - defaults to Google's endpoints but any OAuth 2.0 provider can be used. - token_uri: string, URI for token endpoint. For convenience - defaults to Google's endpoints but any OAuth 2.0 provider can be used. - access_token: string, access token. - refresh_token: string, refresh token. - """ - self.client_id = client_id - self.client_secret = client_secret - self.scope = scope - self.user_agent = user_agent - self.auth_uri = auth_uri - self.token_uri = token_uri - self.access_token = access_token - self.refresh_token = refresh_token - - # True if the credentials have been revoked or expired and can't be - # refreshed. - self._invalid = False - - @property - def invalid(self): - """True if the credentials are invalid, such as being revoked.""" - return getattr(self, '_invalid', False) - - def _refresh(self, request): - """Refresh the access_token using the refresh_token. - - Args: - http: An instance of httplib2.Http.request - or something that acts like it. - """ - body = urllib.urlencode({ - 'grant_type': 'refresh_token', - 'client_id': self.client_id, - 'client_secret': self.client_secret, - 'refresh_token' : self.refresh_token - }) - headers = { - 'user-agent': self.user_agent, - } - - print 'REFRESHING!' - http_request = atom.http_core.HttpRequest(uri=self.token_uri, method='POST', headers=headers) - http_request.add_body_part(body, mime_type='application/x-www-form-urlencoded') - response = request(http_request) - body = response.read() - if response.status == 200: - self._extract_tokens(body) - else: - self._invalid = True - return response - - def _extract_tokens(self, body): - d = simplejson.loads(body) - self.access_token = d['access_token'] - self.refresh_token = d.get('refresh_token', self.refresh_token) - if 'expires_in' in d: - self.token_expiry = datetime.timedelta( - seconds = int(d['expires_in'])) + datetime.datetime.now() - else: - self.token_expiry = None - - def generate_authorize_url(self, redirect_uri='oob', response_type='code', **kwargs): - """Returns a URI to redirect to the provider. - - Args: - redirect_uri: string, Either the string 'oob' for a non-web-based - application, or a URI that handles the callback from - the authorization server. - response_type: string, Either the string 'code' for server-side or - native application, or the string 'token' for client- - side application. - - If redirect_uri is 'oob' then pass in the - generated verification code to get_access_token, - otherwise pass in the query parameters received - at the callback uri to get_access_token. - If the response_type is 'token', no need to call - get_access_token as the API will return it within - the query parameters received at the callback: - oauth2_token.access_token = YOUR_ACCESS_TOKEN - """ - self.redirect_uri = redirect_uri - query = { - 'response_type': response_type, - 'client_id': self.client_id, - 'redirect_uri': redirect_uri, - 'scope': self.scope, - } - query.update(kwargs) - parts = list(urlparse.urlparse(self.auth_uri)) - query.update(dict(parse_qsl(parts[4]))) # 4 is the index of the query part - parts[4] = urllib.urlencode(query) - return urlparse.urlunparse(parts) - - def get_access_token(self, code): - """Exhanges a code for an access token. - - Args: - code: string or dict, either the code as a string, or a dictionary - of the query parameters to the redirect_uri, which contains - the code. - """ - - if not (isinstance(code, str) or isinstance(code, unicode)): - code = code['code'] - - body = urllib.urlencode({ - 'grant_type': 'authorization_code', - 'client_id': self.client_id, - 'client_secret': self.client_secret, - 'code': code, - 'redirect_uri': self.redirect_uri, - 'scope': self.scope - }) - headers = { - 'user-agent': self.user_agent, - } - http_client = atom.http_core.HttpClient() - http_request = atom.http_core.HttpRequest(uri=self.token_uri, method='POST', - headers=headers) - http_request.add_body_part(data=body, mime_type='application/x-www-form-urlencoded') - response = http_client.request(http_request) - body = response.read() - if response.status == 200: - self._extract_tokens(body) - return self - else: - error_msg = 'Invalid response %s.' % response.status - try: - d = simplejson.loads(body) - if 'error' in d: - error_msg = d['error'] - except: - pass - raise OAuth2AccessTokenError(error_msg) - - def authorize(self, client): - """Authorize a gdata.client.GDClient instance with these credentials. - - Args: - client: An instance of gdata.client.GDClient - or something that acts like it. - - Returns: - A modified instance of client that was passed in. - - Example: - - c = gdata.client.GDClient(source='user-agent') - c = token.authorize(c) - """ - client.auth_token = self - request_orig = client.http_client.request - - def new_request(http_request): - response = request_orig(http_request) - if response.status == 401: - refresh_response = self._refresh(request_orig) - if self._invalid: - return refresh_response - else: - self.modify_request(http_request) - return request_orig(http_request) - else: - return response - - client.http_client.request = new_request - return client - - def modify_request(self, http_request): - """Sets the Authorization header in the HTTP request using the token. - - Returns: - The same HTTP request object which was passed in. - """ - http_request.headers['Authorization'] = '%s%s' % (OAUTH2_AUTH_LABEL, self.access_token) - return http_request - - ModifyRequest = modify_request - - -def _join_token_parts(*args): - """"Escapes and combines all strings passed in. - - Used to convert a token object's members into a string instead of - using pickle. - - Note: A None value will be converted to an empty string. - - Returns: - A string in the form 1x|member1|member2|member3... - """ - return '|'.join([urllib.quote_plus(a or '') for a in args]) - - -def _split_token_parts(blob): - """Extracts and unescapes fields from the provided binary string. - - Reverses the packing performed by _join_token_parts. Used to extract - the members of a token object. - - Note: An empty string from the blob will be interpreted as None. - - Args: - blob: str A string of the form 1x|member1|member2|member3 as created - by _join_token_parts - - Returns: - A list of unescaped strings. - """ - return [urllib.unquote_plus(part) or None for part in blob.split('|')] - - -def token_to_blob(token): - """Serializes the token data as a string for storage in a datastore. - - Supported token classes: ClientLoginToken, AuthSubToken, SecureAuthSubToken, - OAuthRsaToken, and OAuthHmacToken, TwoLeggedOAuthRsaToken, - TwoLeggedOAuthHmacToken and OAuth2Token. - - Args: - token: A token object which must be of one of the supported token classes. - - Raises: - UnsupportedTokenType if the token is not one of the supported token - classes listed above. - - Returns: - A string represenging this token. The string can be converted back into - an equivalent token object using token_from_blob. Note that any members - which are set to '' will be set to None when the token is deserialized - by token_from_blob. - """ - if isinstance(token, ClientLoginToken): - return _join_token_parts('1c', token.token_string) - # Check for secure auth sub type first since it is a subclass of - # AuthSubToken. - elif isinstance(token, SecureAuthSubToken): - return _join_token_parts('1s', token.token_string, token.rsa_private_key, - *token.scopes) - elif isinstance(token, AuthSubToken): - return _join_token_parts('1a', token.token_string, *token.scopes) - elif isinstance(token, TwoLeggedOAuthRsaToken): - return _join_token_parts( - '1rtl', token.consumer_key, token.rsa_private_key, token.requestor_id) - elif isinstance(token, TwoLeggedOAuthHmacToken): - return _join_token_parts( - '1htl', token.consumer_key, token.consumer_secret, token.requestor_id) - # Check RSA OAuth token first since the OAuthRsaToken is a subclass of - # OAuthHmacToken. - elif isinstance(token, OAuthRsaToken): - return _join_token_parts( - '1r', token.consumer_key, token.rsa_private_key, token.token, - token.token_secret, str(token.auth_state), token.next, - token.verifier) - elif isinstance(token, OAuthHmacToken): - return _join_token_parts( - '1h', token.consumer_key, token.consumer_secret, token.token, - token.token_secret, str(token.auth_state), token.next, - token.verifier) - elif isinstance(token, OAuth2Token): - return _join_token_parts( - '2o', token.client_id, token.client_secret, token.scope, - token.user_agent, token.auth_uri, token.token_uri, - token.access_token, token.refresh_token) - else: - raise UnsupportedTokenType( - 'Unable to serialize token of type %s' % type(token)) - - -TokenToBlob = token_to_blob - - -def token_from_blob(blob): - """Deserializes a token string from the datastore back into a token object. - - Supported token classes: ClientLoginToken, AuthSubToken, SecureAuthSubToken, - OAuthRsaToken, and OAuthHmacToken, TwoLeggedOAuthRsaToken, - TwoLeggedOAuthHmacToken and OAuth2Token. - - Args: - blob: string created by token_to_blob. - - Raises: - UnsupportedTokenType if the token is not one of the supported token - classes listed above. - - Returns: - A new token object with members set to the values serialized in the - blob string. Note that any members which were set to '' in the original - token will now be None. - """ - parts = _split_token_parts(blob) - if parts[0] == '1c': - return ClientLoginToken(parts[1]) - elif parts[0] == '1a': - return AuthSubToken(parts[1], parts[2:]) - elif parts[0] == '1s': - return SecureAuthSubToken(parts[1], parts[2], parts[3:]) - elif parts[0] == '1rtl': - return TwoLeggedOAuthRsaToken(parts[1], parts[2], parts[3]) - elif parts[0] == '1htl': - return TwoLeggedOAuthHmacToken(parts[1], parts[2], parts[3]) - elif parts[0] == '1r': - auth_state = int(parts[5]) - return OAuthRsaToken(parts[1], parts[2], parts[3], parts[4], auth_state, - parts[6], parts[7]) - elif parts[0] == '1h': - auth_state = int(parts[5]) - return OAuthHmacToken(parts[1], parts[2], parts[3], parts[4], auth_state, - parts[6], parts[7]) - elif parts[0] == '2o': - return OAuth2Token(parts[1], parts[2], parts[3], parts[4], parts[5], - parts[6], parts[7], parts[8]) - else: - raise UnsupportedTokenType( - 'Unable to deserialize token with type marker of %s' % parts[0]) - - -TokenFromBlob = token_from_blob - - -def dump_tokens(tokens): - return ','.join([token_to_blob(t) for t in tokens]) - - -def load_tokens(blob): - return [token_from_blob(s) for s in blob.split(',')] - - -def find_scopes_for_services(service_names=None): - """Creates a combined list of scope URLs for the desired services. - - This method searches the AUTH_SCOPES dictionary. - - Args: - service_names: list of strings (optional) Each name must be a key in the - AUTH_SCOPES dictionary. If no list is provided (None) then - the resulting list will contain all scope URLs in the - AUTH_SCOPES dict. - - Returns: - A list of URL strings which are the scopes needed to access these services - when requesting a token using AuthSub or OAuth. - """ - result_scopes = [] - if service_names is None: - for service_name, scopes in AUTH_SCOPES.iteritems(): - result_scopes.extend(scopes) - else: - for service_name in service_names: - result_scopes.extend(AUTH_SCOPES[service_name]) - return result_scopes - - -FindScopesForServices = find_scopes_for_services - - -def ae_save(token, token_key): - """Stores an auth token in the App Engine datastore. - - This is a convenience method for using the library with App Engine. - Recommended usage is to associate the auth token with the current_user. - If a user is signed in to the app using the App Engine users API, you - can use - gdata.gauth.ae_save(some_token, users.get_current_user().user_id()) - If you are not using the Users API you are free to choose whatever - string you would like for a token_string. - - Args: - token: an auth token object. Must be one of ClientLoginToken, - AuthSubToken, SecureAuthSubToken, OAuthRsaToken, or OAuthHmacToken - (see token_to_blob). - token_key: str A unique identified to be used when you want to retrieve - the token. If the user is signed in to App Engine using the - users API, I recommend using the user ID for the token_key: - users.get_current_user().user_id() - """ - import gdata.alt.app_engine - key_name = ''.join(('gd_auth_token', token_key)) - return gdata.alt.app_engine.set_token(key_name, token_to_blob(token)) - - -AeSave = ae_save - - -def ae_load(token_key): - """Retrieves a token object from the App Engine datastore. - - This is a convenience method for using the library with App Engine. - See also ae_save. - - Args: - token_key: str The unique key associated with the desired token when it - was saved using ae_save. - - Returns: - A token object if there was a token associated with the token_key or None - if the key could not be found. - """ - import gdata.alt.app_engine - key_name = ''.join(('gd_auth_token', token_key)) - token_string = gdata.alt.app_engine.get_token(key_name) - if token_string is not None: - return token_from_blob(token_string) - else: - return None - - -AeLoad = ae_load - - -def ae_delete(token_key): - """Removes the token object from the App Engine datastore.""" - import gdata.alt.app_engine - key_name = ''.join(('gd_auth_token', token_key)) - gdata.alt.app_engine.delete_token(key_name) - - -AeDelete = ae_delete diff --git a/gdata/analytics/geo/__init__.py b/gdata/analytics/geo/__init__.py deleted file mode 100644 index 1fcf604c2c..0000000000 --- a/gdata/analytics/geo/__init__.py +++ /dev/null @@ -1,185 +0,0 @@ -# -*-*- encoding: utf-8 -*-*- -# -# This is gdata.photos.geo, implementing geological positioning in gdata structures -# -# $Id: __init__.py 81 2007-10-03 14:41:42Z havard.gulldahl $ -# -# Copyright 2007 HÃ¥vard Gulldahl -# Portions copyright 2007 Google 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. - -"""Picasa Web Albums uses the georss and gml namespaces for -elements defined in the GeoRSS and Geography Markup Language specifications. - -Specifically, Picasa Web Albums uses the following elements: - -georss:where -gml:Point -gml:pos - -http://code.google.com/apis/picasaweb/reference.html#georss_reference - - -Picasa Web Albums also accepts geographic-location data in two other formats: -W3C format and plain-GeoRSS (without GML) format. -""" -# -#Over the wire, the Picasa Web Albums only accepts and sends the -#elements mentioned above, but this module will let you seamlessly convert -#between the different formats (TODO 2007-10-18 hg) - -__author__ = u'havard@gulldahl.no'# (HÃ¥vard Gulldahl)' #BUG: api chokes on non-ascii chars in __author__ -__license__ = 'Apache License v2' - - -import atom -import gdata - -GEO_NAMESPACE = 'http://www.w3.org/2003/01/geo/wgs84_pos#' -GML_NAMESPACE = 'http://www.opengis.net/gml' -GEORSS_NAMESPACE = 'http://www.georss.org/georss' - -class GeoBaseElement(atom.AtomBase): - """Base class for elements. - - To add new elements, you only need to add the element tag name to self._tag - and the namespace to self._namespace - """ - - _tag = '' - _namespace = GML_NAMESPACE - _children = atom.AtomBase._children.copy() - _attributes = atom.AtomBase._attributes.copy() - - def __init__(self, name=None, extension_elements=None, - extension_attributes=None, text=None): - self.name = name - self.text = text - self.extension_elements = extension_elements or [] - self.extension_attributes = extension_attributes or {} - -class Pos(GeoBaseElement): - """(string) Specifies a latitude and longitude, separated by a space, - e.g. `35.669998 139.770004'""" - - _tag = 'pos' -def PosFromString(xml_string): - return atom.CreateClassFromXMLString(Pos, xml_string) - -class Point(GeoBaseElement): - """(container) Specifies a particular geographical point, by means of - a <gml:pos> element.""" - - _tag = 'Point' - _children = atom.AtomBase._children.copy() - _children['{%s}pos' % GML_NAMESPACE] = ('pos', Pos) - def __init__(self, pos=None, extension_elements=None, extension_attributes=None, text=None): - GeoBaseElement.__init__(self, extension_elements=extension_elements, - extension_attributes=extension_attributes, - text=text) - if pos is None: - pos = Pos() - self.pos=pos -def PointFromString(xml_string): - return atom.CreateClassFromXMLString(Point, xml_string) - -class Where(GeoBaseElement): - """(container) Specifies a geographical location or region. - A container element, containing a single <gml:Point> element. - (Not to be confused with <gd:where>.) - - Note that the (only) child attribute, .Point, is title-cased. - This reflects the names of elements in the xml stream - (principle of least surprise). - - As a convenience, you can get a tuple of (lat, lon) with Where.location(), - and set the same data with Where.setLocation( (lat, lon) ). - - Similarly, there are methods to set and get only latitude and longitude. - """ - - _tag = 'where' - _namespace = GEORSS_NAMESPACE - _children = atom.AtomBase._children.copy() - _children['{%s}Point' % GML_NAMESPACE] = ('Point', Point) - def __init__(self, point=None, extension_elements=None, extension_attributes=None, text=None): - GeoBaseElement.__init__(self, extension_elements=extension_elements, - extension_attributes=extension_attributes, - text=text) - if point is None: - point = Point() - self.Point=point - def location(self): - "(float, float) Return Where.Point.pos.text as a (lat,lon) tuple" - try: - return tuple([float(z) for z in self.Point.pos.text.split(' ')]) - except AttributeError: - return tuple() - def set_location(self, latlon): - """(bool) Set Where.Point.pos.text from a (lat,lon) tuple. - - Arguments: - lat (float): The latitude in degrees, from -90.0 to 90.0 - lon (float): The longitude in degrees, from -180.0 to 180.0 - - Returns True on success. - - """ - - assert(isinstance(latlon[0], float)) - assert(isinstance(latlon[1], float)) - try: - self.Point.pos.text = "%s %s" % (latlon[0], latlon[1]) - return True - except AttributeError: - return False - def latitude(self): - "(float) Get the latitude value of the geo-tag. See also .location()" - lat, lon = self.location() - return lat - - def longitude(self): - "(float) Get the longtitude value of the geo-tag. See also .location()" - lat, lon = self.location() - return lon - - longtitude = longitude - - def set_latitude(self, lat): - """(bool) Set the latitude value of the geo-tag. - - Args: - lat (float): The new latitude value - - See also .set_location() - """ - _lat, lon = self.location() - return self.set_location(lat, lon) - - def set_longitude(self, lon): - """(bool) Set the longtitude value of the geo-tag. - - Args: - lat (float): The new latitude value - - See also .set_location() - """ - lat, _lon = self.location() - return self.set_location(lat, lon) - - set_longtitude = set_longitude - -def WhereFromString(xml_string): - return atom.CreateClassFromXMLString(Where, xml_string) - diff --git a/gdata/analytics/geo/data.py b/gdata/analytics/geo/data.py deleted file mode 100644 index 2aec9112bb..0000000000 --- a/gdata/analytics/geo/data.py +++ /dev/null @@ -1,92 +0,0 @@ -#!/usr/bin/python -# -# Copyright (C) 2009 Google 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. - -"""Contains the data classes of the Geography Extension""" - - -__author__ = 'j.s@google.com (Jeff Scudder)' - - -import atom.core - - -GEORSS_TEMPLATE = '{http://www.georss.org/georss/}%s' -GML_TEMPLATE = '{http://www.opengis.net/gml/}%s' -GEO_TEMPLATE = '{http://www.w3.org/2003/01/geo/wgs84_pos#/}%s' - - -class GeoLat(atom.core.XmlElement): - """Describes a W3C latitude.""" - _qname = GEO_TEMPLATE % 'lat' - - -class GeoLong(atom.core.XmlElement): - """Describes a W3C longitude.""" - _qname = GEO_TEMPLATE % 'long' - - -class GeoRssBox(atom.core.XmlElement): - """Describes a geographical region.""" - _qname = GEORSS_TEMPLATE % 'box' - - -class GeoRssPoint(atom.core.XmlElement): - """Describes a geographical location.""" - _qname = GEORSS_TEMPLATE % 'point' - - -class GmlLowerCorner(atom.core.XmlElement): - """Describes a lower corner of a region.""" - _qname = GML_TEMPLATE % 'lowerCorner' - - -class GmlPos(atom.core.XmlElement): - """Describes a latitude and longitude.""" - _qname = GML_TEMPLATE % 'pos' - - -class GmlPoint(atom.core.XmlElement): - """Describes a particular geographical point.""" - _qname = GML_TEMPLATE % 'Point' - pos = GmlPos - - -class GmlUpperCorner(atom.core.XmlElement): - """Describes an upper corner of a region.""" - _qname = GML_TEMPLATE % 'upperCorner' - - -class GmlEnvelope(atom.core.XmlElement): - """Describes a Gml geographical region.""" - _qname = GML_TEMPLATE % 'Envelope' - lower_corner = GmlLowerCorner - upper_corner = GmlUpperCorner - - -class GeoRssWhere(atom.core.XmlElement): - """Describes a geographical location or region.""" - _qname = GEORSS_TEMPLATE % 'where' - Point = GmlPoint - Envelope = GmlEnvelope - - -class W3CPoint(atom.core.XmlElement): - """Describes a W3C geographical location.""" - _qname = GEO_TEMPLATE % 'Point' - long = GeoLong - lat = GeoLat - - diff --git a/gdata/analytics/health/__init__.py b/gdata/analytics/health/__init__.py deleted file mode 100644 index 1904ecdea6..0000000000 --- a/gdata/analytics/health/__init__.py +++ /dev/null @@ -1,229 +0,0 @@ -#!/usr/bin/python -# -# Copyright 2009 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. - -"""Contains extensions to Atom objects used with Google Health.""" - -__author__ = 'api.eric@google.com (Eric Bidelman)' - -import atom -import gdata - - -CCR_NAMESPACE = 'urn:astm-org:CCR' -METADATA_NAMESPACE = 'http://schemas.google.com/health/metadata' - - -class Ccr(atom.AtomBase): - """Represents a Google Health <ContinuityOfCareRecord>.""" - - _tag = 'ContinuityOfCareRecord' - _namespace = CCR_NAMESPACE - _children = atom.AtomBase._children.copy() - - def __init__(self, extension_elements=None, - extension_attributes=None, text=None): - atom.AtomBase.__init__(self, extension_elements=extension_elements, - extension_attributes=extension_attributes, text=text) - - def GetAlerts(self): - """Helper for extracting Alert/Allergy data from the CCR. - - Returns: - A list of ExtensionElements (one for each allergy found) or None if - no allergies where found in this CCR. - """ - try: - body = self.FindExtensions('Body')[0] - return body.FindChildren('Alerts')[0].FindChildren('Alert') - except: - return None - - def GetAllergies(self): - """Alias for GetAlerts().""" - return self.GetAlerts() - - def GetProblems(self): - """Helper for extracting Problem/Condition data from the CCR. - - Returns: - A list of ExtensionElements (one for each problem found) or None if - no problems where found in this CCR. - """ - try: - body = self.FindExtensions('Body')[0] - return body.FindChildren('Problems')[0].FindChildren('Problem') - except: - return None - - def GetConditions(self): - """Alias for GetProblems().""" - return self.GetProblems() - - def GetProcedures(self): - """Helper for extracting Procedure data from the CCR. - - Returns: - A list of ExtensionElements (one for each procedure found) or None if - no procedures where found in this CCR. - """ - try: - body = self.FindExtensions('Body')[0] - return body.FindChildren('Procedures')[0].FindChildren('Procedure') - except: - return None - - def GetImmunizations(self): - """Helper for extracting Immunization data from the CCR. - - Returns: - A list of ExtensionElements (one for each immunization found) or None if - no immunizations where found in this CCR. - """ - try: - body = self.FindExtensions('Body')[0] - return body.FindChildren('Immunizations')[0].FindChildren('Immunization') - except: - return None - - def GetMedications(self): - """Helper for extracting Medication data from the CCR. - - Returns: - A list of ExtensionElements (one for each medication found) or None if - no medications where found in this CCR. - """ - try: - body = self.FindExtensions('Body')[0] - return body.FindChildren('Medications')[0].FindChildren('Medication') - except: - return None - - def GetResults(self): - """Helper for extracting Results/Labresults data from the CCR. - - Returns: - A list of ExtensionElements (one for each result found) or None if - no results where found in this CCR. - """ - try: - body = self.FindExtensions('Body')[0] - return body.FindChildren('Results')[0].FindChildren('Result') - except: - return None - - -class ProfileEntry(gdata.GDataEntry): - """The Google Health version of an Atom Entry.""" - - _tag = gdata.GDataEntry._tag - _namespace = atom.ATOM_NAMESPACE - _children = gdata.GDataEntry._children.copy() - _attributes = gdata.GDataEntry._attributes.copy() - _children['{%s}ContinuityOfCareRecord' % CCR_NAMESPACE] = ('ccr', Ccr) - - def __init__(self, ccr=None, author=None, category=None, content=None, - atom_id=None, link=None, published=None, title=None, - updated=None, text=None, extension_elements=None, - extension_attributes=None): - self.ccr = ccr - gdata.GDataEntry.__init__( - self, author=author, category=category, content=content, - atom_id=atom_id, link=link, published=published, title=title, - updated=updated, extension_elements=extension_elements, - extension_attributes=extension_attributes, text=text) - - -class ProfileFeed(gdata.GDataFeed): - """A feed containing a list of Google Health profile entries.""" - - _tag = gdata.GDataFeed._tag - _namespace = atom.ATOM_NAMESPACE - _children = gdata.GDataFeed._children.copy() - _attributes = gdata.GDataFeed._attributes.copy() - _children['{%s}entry' % atom.ATOM_NAMESPACE] = ('entry', [ProfileEntry]) - - -class ProfileListEntry(gdata.GDataEntry): - """The Atom Entry in the Google Health profile list feed.""" - - _tag = gdata.GDataEntry._tag - _namespace = atom.ATOM_NAMESPACE - _children = gdata.GDataEntry._children.copy() - _attributes = gdata.GDataEntry._attributes.copy() - - def GetProfileId(self): - return self.content.text - - def GetProfileName(self): - return self.title.text - - -class ProfileListFeed(gdata.GDataFeed): - """A feed containing a list of Google Health profile list entries.""" - - _tag = gdata.GDataFeed._tag - _namespace = atom.ATOM_NAMESPACE - _children = gdata.GDataFeed._children.copy() - _attributes = gdata.GDataFeed._attributes.copy() - _children['{%s}entry' % atom.ATOM_NAMESPACE] = ('entry', [ProfileListEntry]) - - -def ProfileEntryFromString(xml_string): - """Converts an XML string into a ProfileEntry object. - - Args: - xml_string: string The XML describing a Health profile feed entry. - - Returns: - A ProfileEntry object corresponding to the given XML. - """ - return atom.CreateClassFromXMLString(ProfileEntry, xml_string) - - -def ProfileListEntryFromString(xml_string): - """Converts an XML string into a ProfileListEntry object. - - Args: - xml_string: string The XML describing a Health profile list feed entry. - - Returns: - A ProfileListEntry object corresponding to the given XML. - """ - return atom.CreateClassFromXMLString(ProfileListEntry, xml_string) - - -def ProfileFeedFromString(xml_string): - """Converts an XML string into a ProfileFeed object. - - Args: - xml_string: string The XML describing a ProfileFeed feed. - - Returns: - A ProfileFeed object corresponding to the given XML. - """ - return atom.CreateClassFromXMLString(ProfileFeed, xml_string) - - -def ProfileListFeedFromString(xml_string): - """Converts an XML string into a ProfileListFeed object. - - Args: - xml_string: string The XML describing a ProfileListFeed feed. - - Returns: - A ProfileListFeed object corresponding to the given XML. - """ - return atom.CreateClassFromXMLString(ProfileListFeed, xml_string) diff --git a/gdata/analytics/health/service.py b/gdata/analytics/health/service.py deleted file mode 100644 index 3d38411ebe..0000000000 --- a/gdata/analytics/health/service.py +++ /dev/null @@ -1,263 +0,0 @@ -#!/usr/bin/python -# -# Copyright 2009 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. - -"""HealthService extends GDataService to streamline Google Health API access. - - HealthService: Provides methods to interact with the profile, profile list, - and register/notices feeds. Extends GDataService. - - HealthProfileQuery: Queries the Google Health Profile feed. - - HealthProfileListQuery: Queries the Google Health Profile list feed. -""" - -__author__ = 'api.eric@google.com (Eric Bidelman)' - - -import atom -import gdata.health -import gdata.service - - -class HealthService(gdata.service.GDataService): - - """Client extension for the Google Health service Document List feed.""" - - def __init__(self, email=None, password=None, source=None, - use_h9_sandbox=False, server='www.google.com', - additional_headers=None, **kwargs): - """Creates a client for the Google Health 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. - use_h9_sandbox: boolean (optional) True to issue requests against the - /h9 developer's sandbox. - server: string (optional) The name of the server to which a connection - will be opened. - additional_headers: dictionary (optional) Any additional headers which - should be included with CRUD operations. - kwargs: The other parameters to pass to gdata.service.GDataService - constructor. - """ - service = use_h9_sandbox and 'weaver' or 'health' - gdata.service.GDataService.__init__( - self, email=email, password=password, service=service, source=source, - server=server, additional_headers=additional_headers, **kwargs) - self.ssl = True - self.use_h9_sandbox = use_h9_sandbox - - def __get_service(self): - return self.use_h9_sandbox and 'h9' or 'health' - - def GetProfileFeed(self, query=None, profile_id=None): - """Fetches the users Google Health profile feed. - - Args: - query: HealthProfileQuery or string (optional) A query to use on the - profile feed. If None, a HealthProfileQuery is constructed. - profile_id: string (optional) The profile id to query the profile feed - with when using ClientLogin. Note: this parameter is ignored if - query is set. - - Returns: - A gdata.health.ProfileFeed object containing the user's Health profile. - """ - if query is None: - projection = profile_id and 'ui' or 'default' - uri = HealthProfileQuery( - service=self.__get_service(), projection=projection, - profile_id=profile_id).ToUri() - elif isinstance(query, HealthProfileQuery): - uri = query.ToUri() - else: - uri = query - - return self.GetFeed(uri, converter=gdata.health.ProfileFeedFromString) - - def GetProfileListFeed(self, query=None): - """Fetches the users Google Health profile feed. - - Args: - query: HealthProfileListQuery or string (optional) A query to use - on the profile list feed. If None, a HealthProfileListQuery is - constructed to /health/feeds/profile/list or /h9/feeds/profile/list. - - Returns: - A gdata.health.ProfileListFeed object containing the user's list - of profiles. - """ - if not query: - uri = HealthProfileListQuery(service=self.__get_service()).ToUri() - elif isinstance(query, HealthProfileListQuery): - uri = query.ToUri() - else: - uri = query - - return self.GetFeed(uri, converter=gdata.health.ProfileListFeedFromString) - - def SendNotice(self, subject, body=None, content_type='html', - ccr=None, profile_id=None): - """Sends (posts) a notice to the user's Google Health profile. - - Args: - subject: A string representing the message's subject line. - body: string (optional) The message body. - content_type: string (optional) The content type of the notice message - body. This parameter is only honored when a message body is - specified. - ccr: string (optional) The CCR XML document to reconcile into the - user's profile. - profile_id: string (optional) The profile id to work with when using - ClientLogin. Note: this parameter is ignored if query is set. - - Returns: - A gdata.health.ProfileEntry object of the posted entry. - """ - if body: - content = atom.Content(content_type=content_type, text=body) - else: - content = body - - entry = gdata.GDataEntry( - title=atom.Title(text=subject), content=content, - extension_elements=[atom.ExtensionElementFromString(ccr)]) - - projection = profile_id and 'ui' or 'default' - query = HealthRegisterQuery(service=self.__get_service(), - projection=projection, profile_id=profile_id) - return self.Post(entry, query.ToUri(), - converter=gdata.health.ProfileEntryFromString) - - -class HealthProfileQuery(gdata.service.Query): - - """Object used to construct a URI to query the Google Health profile feed.""" - - def __init__(self, service='health', feed='feeds/profile', - projection='default', profile_id=None, text_query=None, - params=None, categories=None): - """Constructor for Health profile feed query. - - Args: - service: string (optional) The service to query. Either 'health' or 'h9'. - feed: string (optional) The path for the feed. The default value is - 'feeds/profile'. - projection: string (optional) The visibility of the data. Possible values - are 'default' for AuthSub and 'ui' for ClientLogin. If this value - is set to 'ui', the profile_id parameter should also be set. - profile_id: string (optional) The profile id to query. This should only - be used when using ClientLogin. - text_query: str (optional) The contents of the q query parameter. The - contents of the text_query are URL escaped upon conversion to a URI. - Note: this parameter can only be used on the register feed using - ClientLogin. - 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. - """ - self.service = service - self.profile_id = profile_id - self.projection = projection - gdata.service.Query.__init__(self, feed=feed, text_query=text_query, - params=params, categories=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 Health - profile feed. - """ - old_feed = self.feed - self.feed = '/'.join([self.service, old_feed, self.projection]) - - if self.profile_id: - self.feed += '/' + self.profile_id - self.feed = '/%s' % (self.feed,) - - new_feed = gdata.service.Query.ToUri(self) - self.feed = old_feed - return new_feed - - -class HealthProfileListQuery(gdata.service.Query): - - """Object used to construct a URI to query a Health profile list feed.""" - - def __init__(self, service='health', feed='feeds/profile/list'): - """Constructor for Health profile list feed query. - - Args: - service: string (optional) The service to query. Either 'health' or 'h9'. - feed: string (optional) The path for the feed. The default value is - 'feeds/profile/list'. - """ - gdata.service.Query.__init__(self, feed) - self.service = service - - 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 - profile list feed. - """ - return '/%s' % ('/'.join([self.service, self.feed]),) - - -class HealthRegisterQuery(gdata.service.Query): - - """Object used to construct a URI to query a Health register/notice feed.""" - - def __init__(self, service='health', feed='feeds/register', - projection='default', profile_id=None): - """Constructor for Health profile list feed query. - - Args: - service: string (optional) The service to query. Either 'health' or 'h9'. - feed: string (optional) The path for the feed. The default value is - 'feeds/register'. - projection: string (optional) The visibility of the data. Possible values - are 'default' for AuthSub and 'ui' for ClientLogin. If this value - is set to 'ui', the profile_id parameter should also be set. - profile_id: string (optional) The profile id to query. This should only - be used when using ClientLogin. - """ - gdata.service.Query.__init__(self, feed) - self.service = service - self.projection = projection - self.profile_id = profile_id - - def ToUri(self): - """Generates a URI from the query parameters set in the object. - - Returns: - A string containing the URI needed to interact with the register feed. - """ - old_feed = self.feed - self.feed = '/'.join([self.service, old_feed, self.projection]) - new_feed = gdata.service.Query.ToUri(self) - self.feed = old_feed - - if self.profile_id: - new_feed += '/' + self.profile_id - return '/%s' % (new_feed,) diff --git a/gdata/analytics/marketplace/__init__.py b/gdata/analytics/marketplace/__init__.py deleted file mode 100644 index 8b13789179..0000000000 --- a/gdata/analytics/marketplace/__init__.py +++ /dev/null @@ -1 +0,0 @@ - diff --git a/gdata/analytics/marketplace/client.py b/gdata/analytics/marketplace/client.py deleted file mode 100644 index 8ffc348659..0000000000 --- a/gdata/analytics/marketplace/client.py +++ /dev/null @@ -1,160 +0,0 @@ -#!/usr/bin/python -# -# Copyright 2009 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. - -"""LicensingClient simplifies Google Apps Marketplace Licensing API calls. - -LicensingClient extends gdata.client.GDClient to ease interaction with -the Google Apps Marketplace Licensing API. These interactions include the ability -to retrieve License informations for an application in the Google Apps Marketplace. -""" - - -__author__ = 'Alexandre Vivien <alex@simplecode.fr>' - -import gdata.marketplace.data -import gdata.client -import urllib - - -# Feed URI template. This must end with a / -# The strings in this template are eventually replaced with the API version -# and Google Apps domain name, respectively. -LICENSE_ROOT_URL = 'http://feedserver-enterprise.googleusercontent.com' -LICENSE_FEED_TEMPLATE = '%s/license?bq=' % LICENSE_ROOT_URL -LICENSE_NOTIFICATIONS_FEED_TEMPLATE = '%s/licensenotification?bq=' % LICENSE_ROOT_URL - - -class LicensingClient(gdata.client.GDClient): - """Client extension for the Google Apps Marketplace Licensing API service. - - Attributes: - host: string The hostname for the Google Apps Marketplace Licensing API service. - api_version: string The version of the Google Apps Marketplace Licensing API. - """ - - api_version = '1.0' - auth_service = 'apps' - auth_scopes = gdata.gauth.AUTH_SCOPES['apps'] - ssl = False - - def __init__(self, domain, auth_token=None, **kwargs): - """Constructs a new client for the Google Apps Marketplace Licensing API. - - Args: - domain: string The Google Apps domain with the application installed. - auth_token: (optional) gdata.gauth.OAuthToken which authorizes this client to retrieve the License information. - kwargs: The other parameters to pass to the gdata.client.GDClient constructor. - """ - gdata.client.GDClient.__init__(self, auth_token=auth_token, **kwargs) - self.domain = domain - - def make_license_feed_uri(self, app_id=None, params=None): - """Creates a license feed URI for the Google Apps Marketplace Licensing API. - - Using this client's Google Apps domain, create a license feed URI for a particular application - in this domain. If params are provided, append them as GET params. - - Args: - app_id: string The ID of the application for which to make a license feed URI. - params: dict (optional) key -> value params to append as GET vars to the - URI. Example: params={'start': 'my-resource-id'} - Returns: - A string giving the URI for the application's license for this client's Google - Apps domain. - """ - parameters = '[appid=%s][domain=%s]' % (app_id, self.domain) - uri = LICENSE_FEED_TEMPLATE + urllib.quote_plus(parameters) - if params: - uri += '&' + urllib.urlencode(params) - return uri - - MakeLicenseFeedUri = make_license_feed_uri - - def make_license_notifications_feed_uri(self, app_id=None, startdatetime=None, max_results=None, params=None): - """Creates a license notifications feed URI for the Google Apps Marketplace Licensing API. - - Using this client's Google Apps domain, create a license notifications feed URI for a particular application. - If params are provided, append them as GET params. - - Args: - app_id: string The ID of the application for which to make a license feed URI. - startdatetime: Start date to retrieve the License notifications. - max_results: Number of results per page. Maximum is 100. - params: dict (optional) key -> value params to append as GET vars to the - URI. Example: params={'start': 'my-resource-id'} - Returns: - A string giving the URI for the application's license notifications for this client's Google - Apps domain. - """ - parameters = '[appid=%s]' % (app_id) - if startdatetime: - parameters += '[startdatetime=%s]' % startdatetime - else: - parameters += '[startdatetime=1970-01-01T00:00:00Z]' - if max_results: - parameters += '[max-results=%s]' % max_results - else: - parameters += '[max-results=100]' - uri = LICENSE_NOTIFICATIONS_FEED_TEMPLATE + urllib.quote_plus(parameters) - if params: - uri += '&' + urllib.urlencode(params) - return uri - - MakeLicenseNotificationsFeedUri = make_license_notifications_feed_uri - - def get_license(self, uri=None, app_id=None, **kwargs): - """Fetches the application's license by application ID. - - Args: - uri: string The base URI of the feed from which to fetch the license. - app_id: string The string ID of the application for which to fetch the license. - kwargs: The other parameters to pass to gdata.client.GDClient.get_entry(). - - Returns: - A License feed object representing the license with the given - base URI and application ID. - """ - - if uri is None: - uri = self.MakeLicenseFeedUri(app_id) - return self.get_feed(uri, - desired_class=gdata.marketplace.data.LicenseFeed, - **kwargs) - - GetLicense = get_license - - def get_license_notifications(self, uri=None, app_id=None, startdatetime=None, max_results=None, **kwargs): - """Fetches the application's license notifications by application ID. - - Args: - uri: string The base URI of the feed from which to fetch the license. - app_id: string The string ID of the application for which to fetch the license. - startdatetime: Start date to retrieve the License notifications. - max_results: Number of results per page. Maximum is 100. - kwargs: The other parameters to pass to gdata.client.GDClient.get_entry(). - - Returns: - A License feed object representing the license notifications with the given - base URI and application ID. - """ - - if uri is None: - uri = self.MakeLicenseNotificationsFeedUri(app_id, startdatetime, max_results) - return self.get_feed(uri, - desired_class=gdata.marketplace.data.LicenseFeed, - **kwargs) - - GetLicenseNotifications = get_license_notifications diff --git a/gdata/analytics/marketplace/data.py b/gdata/analytics/marketplace/data.py deleted file mode 100644 index e8c76a25bc..0000000000 --- a/gdata/analytics/marketplace/data.py +++ /dev/null @@ -1,115 +0,0 @@ -#!/usr/bin/python -# -# Copyright 2009 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 for parsing and generating XML for the Google Apps Marketplace Licensing API.""" - - -__author__ = 'Alexandre Vivien <alex@simplecode.fr>' - - -import atom.core -import gdata -import gdata.data - - -LICENSES_NAMESPACE = 'http://www.w3.org/2005/Atom' -LICENSES_TEMPLATE = '{%s}%%s' % LICENSES_NAMESPACE - - -class Enabled(atom.core.XmlElement): - """ """ - - _qname = LICENSES_TEMPLATE % 'enabled' - - -class Id(atom.core.XmlElement): - """ """ - - _qname = LICENSES_TEMPLATE % 'id' - - -class CustomerId(atom.core.XmlElement): - """ """ - - _qname = LICENSES_TEMPLATE % 'customerid' - - -class DomainName(atom.core.XmlElement): - """ """ - - _qname = LICENSES_TEMPLATE % 'domainname' - - -class InstallerEmail(atom.core.XmlElement): - """ """ - - _qname = LICENSES_TEMPLATE % 'installeremail' - - -class TosAcceptanceTime(atom.core.XmlElement): - """ """ - - _qname = LICENSES_TEMPLATE % 'tosacceptancetime' - - -class LastChangeTime(atom.core.XmlElement): - """ """ - - _qname = LICENSES_TEMPLATE % 'lastchangetime' - - -class ProductConfigId(atom.core.XmlElement): - """ """ - - _qname = LICENSES_TEMPLATE % 'productconfigid' - - -class State(atom.core.XmlElement): - """ """ - - _qname = LICENSES_TEMPLATE % 'state' - - -class Entity(atom.core.XmlElement): - """ The entity representing the License. """ - - _qname = LICENSES_TEMPLATE % 'entity' - - enabled = Enabled - id = Id - customer_id = CustomerId - domain_name = DomainName - installer_email = InstallerEmail - tos_acceptance_time = TosAcceptanceTime - last_change_time = LastChangeTime - product_config_id = ProductConfigId - state = State - - -class Content(atom.data.Content): - entity = Entity - -class LicenseEntry(gdata.data.GDEntry): - """ Represents a LicenseEntry object. """ - - content = Content - - -class LicenseFeed(gdata.data.GDFeed): - """ Represents a feed of LicenseEntry objects. """ - - # Override entry so that this feed knows how to type its list of entries. - entry = [LicenseEntry] diff --git a/gdata/analytics/media/__init__.py b/gdata/analytics/media/__init__.py deleted file mode 100644 index e6af1ae52d..0000000000 --- a/gdata/analytics/media/__init__.py +++ /dev/null @@ -1,355 +0,0 @@ -# -*-*- encoding: utf-8 -*-*- -# -# This is gdata.photos.media, implementing parts of the MediaRSS spec in gdata structures -# -# $Id: __init__.py 81 2007-10-03 14:41:42Z havard.gulldahl $ -# -# Copyright 2007 HÃ¥vard Gulldahl -# Portions copyright 2007 Google 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. - - -"""Essential attributes of photos in Google Photos/Picasa Web Albums are -expressed using elements from the `media' namespace, defined in the -MediaRSS specification[1]. - -Due to copyright issues, the elements herein are documented sparingly, please -consult with the Google Photos API Reference Guide[2], alternatively the -official MediaRSS specification[1] for details. -(If there is a version conflict between the two sources, stick to the -Google Photos API). - -[1]: http://search.yahoo.com/mrss (version 1.1.1) -[2]: http://code.google.com/apis/picasaweb/reference.html#media_reference - -Keep in mind that Google Photos only uses a subset of the MediaRSS elements -(and some of the attributes are trimmed down, too): - -media:content -media:credit -media:description -media:group -media:keywords -media:thumbnail -media:title -""" - -__author__ = u'havard@gulldahl.no'# (HÃ¥vard Gulldahl)' #BUG: api chokes on non-ascii chars in __author__ -__license__ = 'Apache License v2' - - -import atom -import gdata - -MEDIA_NAMESPACE = 'http://search.yahoo.com/mrss/' -YOUTUBE_NAMESPACE = 'http://gdata.youtube.com/schemas/2007' - - -class MediaBaseElement(atom.AtomBase): - """Base class for elements in the MEDIA_NAMESPACE. - To add new elements, you only need to add the element tag name to self._tag - """ - - _tag = '' - _namespace = MEDIA_NAMESPACE - _children = atom.AtomBase._children.copy() - _attributes = atom.AtomBase._attributes.copy() - - def __init__(self, name=None, extension_elements=None, - extension_attributes=None, text=None): - self.name = name - self.text = text - self.extension_elements = extension_elements or [] - self.extension_attributes = extension_attributes or {} - - -class Content(MediaBaseElement): - """(attribute container) This element describes the original content, - e.g. an image or a video. There may be multiple Content elements - in a media:Group. - - For example, a video may have a - <media:content medium="image"> element that specifies a JPEG - representation of the video, and a <media:content medium="video"> - element that specifies the URL of the video itself. - - Attributes: - url: non-ambigous reference to online object - width: width of the object frame, in pixels - height: width of the object frame, in pixels - medium: one of `image' or `video', allowing the api user to quickly - determine the object's type - type: Internet media Type[1] (a.k.a. mime type) of the object -- a more - verbose way of determining the media type. To set the type member - in the contructor, use the content_type parameter. - (optional) fileSize: the size of the object, in bytes - - [1]: http://en.wikipedia.org/wiki/Internet_media_type - """ - - _tag = 'content' - _attributes = atom.AtomBase._attributes.copy() - _attributes['url'] = 'url' - _attributes['width'] = 'width' - _attributes['height'] = 'height' - _attributes['medium'] = 'medium' - _attributes['type'] = 'type' - _attributes['fileSize'] = 'fileSize' - - def __init__(self, url=None, width=None, height=None, - medium=None, content_type=None, fileSize=None, format=None, - extension_elements=None, extension_attributes=None, text=None): - MediaBaseElement.__init__(self, extension_elements=extension_elements, - extension_attributes=extension_attributes, - text=text) - self.url = url - self.width = width - self.height = height - self.medium = medium - self.type = content_type - self.fileSize = fileSize - - -def ContentFromString(xml_string): - return atom.CreateClassFromXMLString(Content, xml_string) - - -class Credit(MediaBaseElement): - """(string) Contains the nickname of the user who created the content, - e.g. `Liz Bennet'. - - This is a user-specified value that should be used when referring to - the user by name. - - Note that none of the attributes from the MediaRSS spec are supported. - """ - - _tag = 'credit' - - -def CreditFromString(xml_string): - return atom.CreateClassFromXMLString(Credit, xml_string) - - -class Description(MediaBaseElement): - """(string) A description of the media object. - Either plain unicode text, or entity-encoded html (look at the `type' - attribute). - - E.g `A set of photographs I took while vacationing in Italy.' - - For `api' projections, the description is in plain text; - for `base' projections, the description is in HTML. - - Attributes: - type: either `text' or `html'. To set the type member in the contructor, - use the description_type parameter. - """ - - _tag = 'description' - _attributes = atom.AtomBase._attributes.copy() - _attributes['type'] = 'type' - def __init__(self, description_type=None, - extension_elements=None, extension_attributes=None, text=None): - MediaBaseElement.__init__(self, extension_elements=extension_elements, - extension_attributes=extension_attributes, - text=text) - - self.type = description_type - - -def DescriptionFromString(xml_string): - return atom.CreateClassFromXMLString(Description, xml_string) - - -class Keywords(MediaBaseElement): - """(string) Lists the tags associated with the entry, - e.g `italy, vacation, sunset'. - - Contains a comma-separated list of tags that have been added to the photo, or - all tags that have been added to photos in the album. - """ - - _tag = 'keywords' - - -def KeywordsFromString(xml_string): - return atom.CreateClassFromXMLString(Keywords, xml_string) - - -class Thumbnail(MediaBaseElement): - """(attributes) Contains the URL of a thumbnail of a photo or album cover. - - There can be multiple <media:thumbnail> elements for a given <media:group>; - for example, a given item may have multiple thumbnails at different sizes. - Photos generally have two thumbnails at different sizes; - albums generally have one cropped thumbnail. - - If the thumbsize parameter is set to the initial query, this element points - to thumbnails of the requested sizes; otherwise the thumbnails are the - default thumbnail size. - - This element must not be confused with the <gphoto:thumbnail> element. - - Attributes: - url: The URL of the thumbnail image. - height: The height of the thumbnail image, in pixels. - width: The width of the thumbnail image, in pixels. - """ - - _tag = 'thumbnail' - _attributes = atom.AtomBase._attributes.copy() - _attributes['url'] = 'url' - _attributes['width'] = 'width' - _attributes['height'] = 'height' - def __init__(self, url=None, width=None, height=None, - extension_attributes=None, text=None, extension_elements=None): - MediaBaseElement.__init__(self, extension_elements=extension_elements, - extension_attributes=extension_attributes, - text=text) - self.url = url - self.width = width - self.height = height - - -def ThumbnailFromString(xml_string): - return atom.CreateClassFromXMLString(Thumbnail, xml_string) - - -class Title(MediaBaseElement): - """(string) Contains the title of the entry's media content, in plain text. - - Attributes: - type: Always set to plain. To set the type member in the constructor, use - the title_type parameter. - """ - - _tag = 'title' - _attributes = atom.AtomBase._attributes.copy() - _attributes['type'] = 'type' - def __init__(self, title_type=None, - extension_attributes=None, text=None, extension_elements=None): - MediaBaseElement.__init__(self, extension_elements=extension_elements, - extension_attributes=extension_attributes, - text=text) - self.type = title_type - - -def TitleFromString(xml_string): - return atom.CreateClassFromXMLString(Title, xml_string) - - -class Player(MediaBaseElement): - """(string) Contains the embeddable player URL for the entry's media content - if the media is a video. - - Attributes: - url: Always set to plain - """ - - _tag = 'player' - _attributes = atom.AtomBase._attributes.copy() - _attributes['url'] = 'url' - - def __init__(self, player_url=None, - extension_attributes=None, extension_elements=None): - MediaBaseElement.__init__(self, extension_elements=extension_elements, - extension_attributes=extension_attributes) - self.url= player_url - - -class Private(atom.AtomBase): - """The YouTube Private element""" - _tag = 'private' - _namespace = YOUTUBE_NAMESPACE - - -class Duration(atom.AtomBase): - """The YouTube Duration element""" - _tag = 'duration' - _namespace = YOUTUBE_NAMESPACE - _attributes = atom.AtomBase._attributes.copy() - _attributes['seconds'] = 'seconds' - - -class Category(MediaBaseElement): - """The mediagroup:category element""" - - _tag = 'category' - _attributes = atom.AtomBase._attributes.copy() - _attributes['term'] = 'term' - _attributes['scheme'] = 'scheme' - _attributes['label'] = 'label' - - def __init__(self, term=None, scheme=None, label=None, text=None, - extension_elements=None, extension_attributes=None): - """Constructor for Category - - Args: - term: str - scheme: str - label: str - text: str The text data in the this element - extension_elements: list A list of ExtensionElement instances - extension_attributes: dict A dictionary of attribute value string pairs - """ - - self.term = term - self.scheme = scheme - self.label = label - self.text = text - self.extension_elements = extension_elements or [] - self.extension_attributes = extension_attributes or {} - - -class Group(MediaBaseElement): - """Container element for all media elements. - The <media:group> element can appear as a child of an album, photo or - video entry.""" - - _tag = 'group' - _children = atom.AtomBase._children.copy() - _children['{%s}content' % MEDIA_NAMESPACE] = ('content', [Content,]) - _children['{%s}credit' % MEDIA_NAMESPACE] = ('credit', Credit) - _children['{%s}description' % MEDIA_NAMESPACE] = ('description', Description) - _children['{%s}keywords' % MEDIA_NAMESPACE] = ('keywords', Keywords) - _children['{%s}thumbnail' % MEDIA_NAMESPACE] = ('thumbnail', [Thumbnail,]) - _children['{%s}title' % MEDIA_NAMESPACE] = ('title', Title) - _children['{%s}category' % MEDIA_NAMESPACE] = ('category', [Category,]) - _children['{%s}duration' % YOUTUBE_NAMESPACE] = ('duration', Duration) - _children['{%s}private' % YOUTUBE_NAMESPACE] = ('private', Private) - _children['{%s}player' % MEDIA_NAMESPACE] = ('player', Player) - - def __init__(self, content=None, credit=None, description=None, keywords=None, - thumbnail=None, title=None, duration=None, private=None, - category=None, player=None, extension_elements=None, - extension_attributes=None, text=None): - - MediaBaseElement.__init__(self, extension_elements=extension_elements, - extension_attributes=extension_attributes, - text=text) - self.content=content - self.credit=credit - self.description=description - self.keywords=keywords - self.thumbnail=thumbnail or [] - self.title=title - self.duration=duration - self.private=private - self.category=category or [] - self.player=player - - -def GroupFromString(xml_string): - return atom.CreateClassFromXMLString(Group, xml_string) diff --git a/gdata/analytics/media/data.py b/gdata/analytics/media/data.py deleted file mode 100644 index bb5d2c80f8..0000000000 --- a/gdata/analytics/media/data.py +++ /dev/null @@ -1,159 +0,0 @@ -#!/usr/bin/python -# -# Copyright (C) 2009 Google 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. - -"""Contains the data classes of the Yahoo! Media RSS Extension""" - - -__author__ = 'j.s@google.com (Jeff Scudder)' - - -import atom.core - - -MEDIA_TEMPLATE = '{http://search.yahoo.com/mrss//}%s' - - -class MediaCategory(atom.core.XmlElement): - """Describes a media category.""" - _qname = MEDIA_TEMPLATE % 'category' - scheme = 'scheme' - label = 'label' - - -class MediaCopyright(atom.core.XmlElement): - """Describes a media copyright.""" - _qname = MEDIA_TEMPLATE % 'copyright' - url = 'url' - - -class MediaCredit(atom.core.XmlElement): - """Describes a media credit.""" - _qname = MEDIA_TEMPLATE % 'credit' - role = 'role' - scheme = 'scheme' - - -class MediaDescription(atom.core.XmlElement): - """Describes a media description.""" - _qname = MEDIA_TEMPLATE % 'description' - type = 'type' - - -class MediaHash(atom.core.XmlElement): - """Describes a media hash.""" - _qname = MEDIA_TEMPLATE % 'hash' - algo = 'algo' - - -class MediaKeywords(atom.core.XmlElement): - """Describes a media keywords.""" - _qname = MEDIA_TEMPLATE % 'keywords' - - -class MediaPlayer(atom.core.XmlElement): - """Describes a media player.""" - _qname = MEDIA_TEMPLATE % 'player' - height = 'height' - width = 'width' - url = 'url' - - -class MediaRating(atom.core.XmlElement): - """Describes a media rating.""" - _qname = MEDIA_TEMPLATE % 'rating' - scheme = 'scheme' - - -class MediaRestriction(atom.core.XmlElement): - """Describes a media restriction.""" - _qname = MEDIA_TEMPLATE % 'restriction' - relationship = 'relationship' - type = 'type' - - -class MediaText(atom.core.XmlElement): - """Describes a media text.""" - _qname = MEDIA_TEMPLATE % 'text' - end = 'end' - lang = 'lang' - type = 'type' - start = 'start' - - -class MediaThumbnail(atom.core.XmlElement): - """Describes a media thumbnail.""" - _qname = MEDIA_TEMPLATE % 'thumbnail' - time = 'time' - url = 'url' - width = 'width' - height = 'height' - - -class MediaTitle(atom.core.XmlElement): - """Describes a media title.""" - _qname = MEDIA_TEMPLATE % 'title' - type = 'type' - - -class MediaContent(atom.core.XmlElement): - """Describes a media content.""" - _qname = MEDIA_TEMPLATE % 'content' - bitrate = 'bitrate' - is_default = 'isDefault' - medium = 'medium' - height = 'height' - credit = [MediaCredit] - language = 'language' - hash = MediaHash - width = 'width' - player = MediaPlayer - url = 'url' - file_size = 'fileSize' - channels = 'channels' - expression = 'expression' - text = [MediaText] - samplingrate = 'samplingrate' - title = MediaTitle - category = [MediaCategory] - rating = [MediaRating] - type = 'type' - description = MediaDescription - framerate = 'framerate' - thumbnail = [MediaThumbnail] - duration = 'duration' - copyright = MediaCopyright - keywords = MediaKeywords - restriction = [MediaRestriction] - - -class MediaGroup(atom.core.XmlElement): - """Describes a media group.""" - _qname = MEDIA_TEMPLATE % 'group' - credit = [MediaCredit] - content = [MediaContent] - copyright = MediaCopyright - description = MediaDescription - category = [MediaCategory] - player = MediaPlayer - rating = [MediaRating] - hash = MediaHash - title = MediaTitle - keywords = MediaKeywords - restriction = [MediaRestriction] - thumbnail = [MediaThumbnail] - text = [MediaText] - - diff --git a/gdata/analytics/notebook/__init__.py b/gdata/analytics/notebook/__init__.py deleted file mode 100644 index 22071f7a11..0000000000 --- a/gdata/analytics/notebook/__init__.py +++ /dev/null @@ -1,15 +0,0 @@ -#!/usr/bin/python -# -# Copyright (C) 2009 Google 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. diff --git a/gdata/analytics/notebook/data.py b/gdata/analytics/notebook/data.py deleted file mode 100644 index 53405e0181..0000000000 --- a/gdata/analytics/notebook/data.py +++ /dev/null @@ -1,55 +0,0 @@ -#!/usr/bin/python -# -# Copyright (C) 2009 Google 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. - -"""Contains the data classes of the Google Notebook Data API""" - - -__author__ = 'j.s@google.com (Jeff Scudder)' - - -import atom.core -import atom.data -import gdata.data -import gdata.opensearch.data - - -NB_TEMPLATE = '{http://schemas.google.com/notes/2008/}%s' - - -class ComesAfter(atom.core.XmlElement): - """Preceding element.""" - _qname = NB_TEMPLATE % 'comesAfter' - id = 'id' - - -class NoteEntry(gdata.data.GDEntry): - """Describes a note entry in the feed of a user's notebook.""" - - -class NotebookFeed(gdata.data.GDFeed): - """Describes a notebook feed.""" - entry = [NoteEntry] - - -class NotebookListEntry(gdata.data.GDEntry): - """Describes a note list entry in the feed of a user's list of public notebooks.""" - - -class NotebookListFeed(gdata.data.GDFeed): - """Describes a notebook list feed.""" - entry = [NotebookListEntry] - - diff --git a/gdata/analytics/oauth/CHANGES.txt b/gdata/analytics/oauth/CHANGES.txt deleted file mode 100644 index 7c2b92cd94..0000000000 --- a/gdata/analytics/oauth/CHANGES.txt +++ /dev/null @@ -1,17 +0,0 @@ -1. Moved oauth.py to __init__.py - -2. Refactored __init__.py for compatibility with python 2.2 (Issue 59) - -3. Refactored rsa.py for compatibility with python 2.2 (Issue 59) - -4. Refactored OAuthRequest.from_token_and_callback since the callback url was -getting double url-encoding the callback url in place of single. (Issue 43) - -5. Added build_signature_base_string method to rsa.py since it used the -implementation of this method from oauth.OAuthSignatureMethod_HMAC_SHA1 which -was incorrect since it enforced the presence of a consumer secret and a token -secret. Also, changed its super class from oauth.OAuthSignatureMethod_HMAC_SHA1 -to oauth.OAuthSignatureMethod (Issue 64) - -6. Refactored <OAuthRequest>.to_header method since it returned non-oauth params -as well which was incorrect. (Issue 31) \ No newline at end of file diff --git a/gdata/analytics/oauth/__init__.py b/gdata/analytics/oauth/__init__.py deleted file mode 100644 index 44d9c7a4e9..0000000000 --- a/gdata/analytics/oauth/__init__.py +++ /dev/null @@ -1,529 +0,0 @@ -import cgi -import urllib -import time -import random -import urlparse -import hmac -import binascii - -VERSION = '1.0' # Hi Blaine! -HTTP_METHOD = 'GET' -SIGNATURE_METHOD = 'PLAINTEXT' - -# Generic exception class -class OAuthError(RuntimeError): - def __init__(self, message='OAuth error occured.'): - self.message = message - -# optional WWW-Authenticate header (401 error) -def build_authenticate_header(realm=''): - return {'WWW-Authenticate': 'OAuth realm="%s"' % realm} - -# url escape -def escape(s): - # escape '/' too - return urllib.quote(s, safe='~') - -# util function: current timestamp -# seconds since epoch (UTC) -def generate_timestamp(): - return int(time.time()) - -# util function: nonce -# pseudorandom number -def generate_nonce(length=8): - return ''.join([str(random.randint(0, 9)) for i in range(length)]) - -# OAuthConsumer is a data type that represents the identity of the Consumer -# via its shared secret with the Service Provider. -class OAuthConsumer(object): - key = None - secret = None - - def __init__(self, key, secret): - self.key = key - self.secret = secret - -# OAuthToken is a data type that represents an End User via either an access -# or request token. -class OAuthToken(object): - # access tokens and request tokens - key = None - secret = None - - ''' - key = the token - secret = the token secret - ''' - def __init__(self, key, secret): - self.key = key - self.secret = secret - - def to_string(self): - return urllib.urlencode({'oauth_token': self.key, 'oauth_token_secret': self.secret}) - - # return a token from something like: - # oauth_token_secret=digg&oauth_token=digg - def from_string(s): - params = cgi.parse_qs(s, keep_blank_values=False) - key = params['oauth_token'][0] - secret = params['oauth_token_secret'][0] - return OAuthToken(key, secret) - from_string = staticmethod(from_string) - - def __str__(self): - return self.to_string() - -# OAuthRequest represents the request and can be serialized -class OAuthRequest(object): - ''' - OAuth parameters: - - oauth_consumer_key - - oauth_token - - oauth_signature_method - - oauth_signature - - oauth_timestamp - - oauth_nonce - - oauth_version - ... any additional parameters, as defined by the Service Provider. - ''' - parameters = None # oauth parameters - http_method = HTTP_METHOD - http_url = None - version = VERSION - - def __init__(self, http_method=HTTP_METHOD, http_url=None, parameters=None): - self.http_method = http_method - self.http_url = http_url - self.parameters = parameters or {} - - def set_parameter(self, parameter, value): - self.parameters[parameter] = value - - def get_parameter(self, parameter): - try: - return self.parameters[parameter] - except: - raise OAuthError('Parameter not found: %s' % parameter) - - def _get_timestamp_nonce(self): - return self.get_parameter('oauth_timestamp'), self.get_parameter('oauth_nonce') - - # get any non-oauth parameters - def get_nonoauth_parameters(self): - parameters = {} - for k, v in self.parameters.iteritems(): - # ignore oauth parameters - if k.find('oauth_') < 0: - parameters[k] = v - return parameters - - # serialize as a header for an HTTPAuth request - def to_header(self, realm=''): - auth_header = 'OAuth realm="%s"' % realm - # add the oauth parameters - if self.parameters: - for k, v in self.parameters.iteritems(): - if k[:6] == 'oauth_': - auth_header += ', %s="%s"' % (k, escape(str(v))) - return {'Authorization': auth_header} - - # serialize as post data for a POST request - def to_postdata(self): - return '&'.join(['%s=%s' % (escape(str(k)), escape(str(v))) for k, v in self.parameters.iteritems()]) - - # serialize as a url for a GET request - def to_url(self): - return '%s?%s' % (self.get_normalized_http_url(), self.to_postdata()) - - # return a string that consists of all the parameters that need to be signed - def get_normalized_parameters(self): - params = self.parameters - try: - # exclude the signature if it exists - del params['oauth_signature'] - except: - pass - key_values = params.items() - # sort lexicographically, first after key, then after value - key_values.sort() - # combine key value pairs in string and escape - return '&'.join(['%s=%s' % (escape(str(k)), escape(str(v))) for k, v in key_values]) - - # just uppercases the http method - def get_normalized_http_method(self): - return self.http_method.upper() - - # parses the url and rebuilds it to be scheme://host/path - def get_normalized_http_url(self): - parts = urlparse.urlparse(self.http_url) - host = parts[1].lower() - if host.endswith(':80') or host.endswith(':443'): - host = host.split(':')[0] - url_string = '%s://%s%s' % (parts[0], host, parts[2]) # scheme, netloc, path - return url_string - - # set the signature parameter to the result of build_signature - def sign_request(self, signature_method, consumer, token): - # set the signature method - self.set_parameter('oauth_signature_method', signature_method.get_name()) - # set the signature - self.set_parameter('oauth_signature', self.build_signature(signature_method, consumer, token)) - - def build_signature(self, signature_method, consumer, token): - # call the build signature method within the signature method - return signature_method.build_signature(self, consumer, token) - - def from_request(http_method, http_url, headers=None, parameters=None, query_string=None): - # combine multiple parameter sources - if parameters is None: - parameters = {} - - # headers - if headers and 'Authorization' in headers: - auth_header = headers['Authorization'] - # check that the authorization header is OAuth - if auth_header.index('OAuth') > -1: - try: - # get the parameters from the header - header_params = OAuthRequest._split_header(auth_header) - parameters.update(header_params) - except: - raise OAuthError('Unable to parse OAuth parameters from Authorization header.') - - # GET or POST query string - if query_string: - query_params = OAuthRequest._split_url_string(query_string) - parameters.update(query_params) - - # URL parameters - param_str = urlparse.urlparse(http_url)[4] # query - url_params = OAuthRequest._split_url_string(param_str) - parameters.update(url_params) - - if parameters: - return OAuthRequest(http_method, http_url, parameters) - - return None - from_request = staticmethod(from_request) - - def from_consumer_and_token(oauth_consumer, token=None, http_method=HTTP_METHOD, http_url=None, parameters=None): - if not parameters: - parameters = {} - - defaults = { - 'oauth_consumer_key': oauth_consumer.key, - 'oauth_timestamp': generate_timestamp(), - 'oauth_nonce': generate_nonce(), - 'oauth_version': OAuthRequest.version, - } - - defaults.update(parameters) - parameters = defaults - - if token: - parameters['oauth_token'] = token.key - - return OAuthRequest(http_method, http_url, parameters) - from_consumer_and_token = staticmethod(from_consumer_and_token) - - def from_token_and_callback(token, callback=None, http_method=HTTP_METHOD, http_url=None, parameters=None): - if not parameters: - parameters = {} - - parameters['oauth_token'] = token.key - - if callback: - parameters['oauth_callback'] = callback - - return OAuthRequest(http_method, http_url, parameters) - from_token_and_callback = staticmethod(from_token_and_callback) - - # util function: turn Authorization: header into parameters, has to do some unescaping - def _split_header(header): - params = {} - parts = header[6:].split(',') - for param in parts: - # ignore realm parameter - if param.find('realm') > -1: - continue - # remove whitespace - param = param.strip() - # split key-value - param_parts = param.split('=', 1) - # remove quotes and unescape the value - params[param_parts[0]] = urllib.unquote(param_parts[1].strip('\"')) - return params - _split_header = staticmethod(_split_header) - - # util function: turn url string into parameters, has to do some unescaping - # even empty values should be included - def _split_url_string(param_str): - parameters = cgi.parse_qs(param_str, keep_blank_values=True) - for k, v in parameters.iteritems(): - parameters[k] = urllib.unquote(v[0]) - return parameters - _split_url_string = staticmethod(_split_url_string) - -# OAuthServer is a worker to check a requests validity against a data store -class OAuthServer(object): - timestamp_threshold = 300 # in seconds, five minutes - version = VERSION - signature_methods = None - data_store = None - - def __init__(self, data_store=None, signature_methods=None): - self.data_store = data_store - self.signature_methods = signature_methods or {} - - def set_data_store(self, oauth_data_store): - self.data_store = oauth_data_store - - def get_data_store(self): - return self.data_store - - def add_signature_method(self, signature_method): - self.signature_methods[signature_method.get_name()] = signature_method - return self.signature_methods - - # process a request_token request - # returns the request token on success - def fetch_request_token(self, oauth_request): - try: - # get the request token for authorization - token = self._get_token(oauth_request, 'request') - except OAuthError: - # no token required for the initial token request - version = self._get_version(oauth_request) - consumer = self._get_consumer(oauth_request) - self._check_signature(oauth_request, consumer, None) - # fetch a new token - token = self.data_store.fetch_request_token(consumer) - return token - - # process an access_token request - # returns the access token on success - def fetch_access_token(self, oauth_request): - version = self._get_version(oauth_request) - consumer = self._get_consumer(oauth_request) - # get the request token - token = self._get_token(oauth_request, 'request') - self._check_signature(oauth_request, consumer, token) - new_token = self.data_store.fetch_access_token(consumer, token) - return new_token - - # verify an api call, checks all the parameters - def verify_request(self, oauth_request): - # -> consumer and token - version = self._get_version(oauth_request) - consumer = self._get_consumer(oauth_request) - # get the access token - token = self._get_token(oauth_request, 'access') - self._check_signature(oauth_request, consumer, token) - parameters = oauth_request.get_nonoauth_parameters() - return consumer, token, parameters - - # authorize a request token - def authorize_token(self, token, user): - return self.data_store.authorize_request_token(token, user) - - # get the callback url - def get_callback(self, oauth_request): - return oauth_request.get_parameter('oauth_callback') - - # optional support for the authenticate header - def build_authenticate_header(self, realm=''): - return {'WWW-Authenticate': 'OAuth realm="%s"' % realm} - - # verify the correct version request for this server - def _get_version(self, oauth_request): - try: - version = oauth_request.get_parameter('oauth_version') - except: - version = VERSION - if version and version != self.version: - raise OAuthError('OAuth version %s not supported.' % str(version)) - return version - - # figure out the signature with some defaults - def _get_signature_method(self, oauth_request): - try: - signature_method = oauth_request.get_parameter('oauth_signature_method') - except: - signature_method = SIGNATURE_METHOD - try: - # get the signature method object - signature_method = self.signature_methods[signature_method] - except: - signature_method_names = ', '.join(self.signature_methods.keys()) - raise OAuthError('Signature method %s not supported try one of the following: %s' % (signature_method, signature_method_names)) - - return signature_method - - def _get_consumer(self, oauth_request): - consumer_key = oauth_request.get_parameter('oauth_consumer_key') - if not consumer_key: - raise OAuthError('Invalid consumer key.') - consumer = self.data_store.lookup_consumer(consumer_key) - if not consumer: - raise OAuthError('Invalid consumer.') - return consumer - - # try to find the token for the provided request token key - def _get_token(self, oauth_request, token_type='access'): - token_field = oauth_request.get_parameter('oauth_token') - consumer = self._get_consumer(oauth_request) - token = self.data_store.lookup_token(consumer, token_type, token_field) - if not token: - raise OAuthError('Invalid %s token: %s' % (token_type, token_field)) - return token - - def _check_signature(self, oauth_request, consumer, token): - timestamp, nonce = oauth_request._get_timestamp_nonce() - self._check_timestamp(timestamp) - self._check_nonce(consumer, token, nonce) - signature_method = self._get_signature_method(oauth_request) - try: - signature = oauth_request.get_parameter('oauth_signature') - except: - raise OAuthError('Missing signature.') - # validate the signature - valid_sig = signature_method.check_signature(oauth_request, consumer, token, signature) - if not valid_sig: - key, base = signature_method.build_signature_base_string(oauth_request, consumer, token) - raise OAuthError('Invalid signature. Expected signature base string: %s' % base) - built = signature_method.build_signature(oauth_request, consumer, token) - - def _check_timestamp(self, timestamp): - # verify that timestamp is recentish - timestamp = int(timestamp) - now = int(time.time()) - lapsed = now - timestamp - if lapsed > self.timestamp_threshold: - raise OAuthError('Expired timestamp: given %d and now %s has a greater difference than threshold %d' % (timestamp, now, self.timestamp_threshold)) - - def _check_nonce(self, consumer, token, nonce): - # verify that the nonce is uniqueish - nonce = self.data_store.lookup_nonce(consumer, token, nonce) - if nonce: - raise OAuthError('Nonce already used: %s' % str(nonce)) - -# OAuthClient is a worker to attempt to execute a request -class OAuthClient(object): - consumer = None - token = None - - def __init__(self, oauth_consumer, oauth_token): - self.consumer = oauth_consumer - self.token = oauth_token - - def get_consumer(self): - return self.consumer - - def get_token(self): - return self.token - - def fetch_request_token(self, oauth_request): - # -> OAuthToken - raise NotImplementedError - - def fetch_access_token(self, oauth_request): - # -> OAuthToken - raise NotImplementedError - - def access_resource(self, oauth_request): - # -> some protected resource - raise NotImplementedError - -# OAuthDataStore is a database abstraction used to lookup consumers and tokens -class OAuthDataStore(object): - - def lookup_consumer(self, key): - # -> OAuthConsumer - raise NotImplementedError - - def lookup_token(self, oauth_consumer, token_type, token_token): - # -> OAuthToken - raise NotImplementedError - - def lookup_nonce(self, oauth_consumer, oauth_token, nonce, timestamp): - # -> OAuthToken - raise NotImplementedError - - def fetch_request_token(self, oauth_consumer): - # -> OAuthToken - raise NotImplementedError - - def fetch_access_token(self, oauth_consumer, oauth_token): - # -> OAuthToken - raise NotImplementedError - - def authorize_request_token(self, oauth_token, user): - # -> OAuthToken - raise NotImplementedError - -# OAuthSignatureMethod is a strategy class that implements a signature method -class OAuthSignatureMethod(object): - def get_name(self): - # -> str - raise NotImplementedError - - def build_signature_base_string(self, oauth_request, oauth_consumer, oauth_token): - # -> str key, str raw - raise NotImplementedError - - def build_signature(self, oauth_request, oauth_consumer, oauth_token): - # -> str - raise NotImplementedError - - def check_signature(self, oauth_request, consumer, token, signature): - built = self.build_signature(oauth_request, consumer, token) - return built == signature - -class OAuthSignatureMethod_HMAC_SHA1(OAuthSignatureMethod): - - def get_name(self): - return 'HMAC-SHA1' - - def build_signature_base_string(self, oauth_request, consumer, token): - sig = ( - escape(oauth_request.get_normalized_http_method()), - escape(oauth_request.get_normalized_http_url()), - escape(oauth_request.get_normalized_parameters()), - ) - - key = '%s&' % escape(consumer.secret) - if token: - key += escape(token.secret) - raw = '&'.join(sig) - return key, raw - - def build_signature(self, oauth_request, consumer, token): - # build the base signature string - key, raw = self.build_signature_base_string(oauth_request, consumer, token) - - # hmac object - try: - import hashlib # 2.5 - hashed = hmac.new(key, raw, hashlib.sha1) - except: - import sha # deprecated - hashed = hmac.new(key, raw, sha) - - # calculate the digest base 64 - return binascii.b2a_base64(hashed.digest())[:-1] - -class OAuthSignatureMethod_PLAINTEXT(OAuthSignatureMethod): - - def get_name(self): - return 'PLAINTEXT' - - def build_signature_base_string(self, oauth_request, consumer, token): - # concatenate the consumer key and secret - sig = escape(consumer.secret) + '&' - if token: - sig = sig + escape(token.secret) - return sig - - def build_signature(self, oauth_request, consumer, token): - return self.build_signature_base_string(oauth_request, consumer, token) diff --git a/gdata/analytics/oauth/rsa.py b/gdata/analytics/oauth/rsa.py deleted file mode 100644 index f8d9b8503f..0000000000 --- a/gdata/analytics/oauth/rsa.py +++ /dev/null @@ -1,120 +0,0 @@ -#!/usr/bin/python - -""" -requires tlslite - http://trevp.net/tlslite/ - -""" - -import binascii - -from gdata.tlslite.utils import keyfactory -from gdata.tlslite.utils import cryptomath - -# XXX andy: ugly local import due to module name, oauth.oauth -import gdata.oauth as oauth - -class OAuthSignatureMethod_RSA_SHA1(oauth.OAuthSignatureMethod): - def get_name(self): - return "RSA-SHA1" - - def _fetch_public_cert(self, oauth_request): - # not implemented yet, ideas are: - # (1) do a lookup in a table of trusted certs keyed off of consumer - # (2) fetch via http using a url provided by the requester - # (3) some sort of specific discovery code based on request - # - # either way should return a string representation of the certificate - raise NotImplementedError - - def _fetch_private_cert(self, oauth_request): - # not implemented yet, ideas are: - # (1) do a lookup in a table of trusted certs keyed off of consumer - # - # either way should return a string representation of the certificate - raise NotImplementedError - - def build_signature_base_string(self, oauth_request, consumer, token): - sig = ( - oauth.escape(oauth_request.get_normalized_http_method()), - oauth.escape(oauth_request.get_normalized_http_url()), - oauth.escape(oauth_request.get_normalized_parameters()), - ) - key = '' - raw = '&'.join(sig) - return key, raw - - def build_signature(self, oauth_request, consumer, token): - key, base_string = self.build_signature_base_string(oauth_request, - consumer, - token) - - # Fetch the private key cert based on the request - cert = self._fetch_private_cert(oauth_request) - - # Pull the private key from the certificate - privatekey = keyfactory.parsePrivateKey(cert) - - # Convert base_string to bytes - #base_string_bytes = cryptomath.createByteArraySequence(base_string) - - # Sign using the key - signed = privatekey.hashAndSign(base_string) - - return binascii.b2a_base64(signed)[:-1] - - def check_signature(self, oauth_request, consumer, token, signature): - decoded_sig = base64.b64decode(signature); - - key, base_string = self.build_signature_base_string(oauth_request, - consumer, - token) - - # Fetch the public key cert based on the request - cert = self._fetch_public_cert(oauth_request) - - # Pull the public key from the certificate - publickey = keyfactory.parsePEMKey(cert, public=True) - - # Check the signature - ok = publickey.hashAndVerify(decoded_sig, base_string) - - return ok - - -class TestOAuthSignatureMethod_RSA_SHA1(OAuthSignatureMethod_RSA_SHA1): - def _fetch_public_cert(self, oauth_request): - cert = """ ------BEGIN CERTIFICATE----- -MIIBpjCCAQ+gAwIBAgIBATANBgkqhkiG9w0BAQUFADAZMRcwFQYDVQQDDA5UZXN0 -IFByaW5jaXBhbDAeFw03MDAxMDEwODAwMDBaFw0zODEyMzEwODAwMDBaMBkxFzAV -BgNVBAMMDlRlc3QgUHJpbmNpcGFsMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKB -gQC0YjCwIfYoprq/FQO6lb3asXrxLlJFuCvtinTF5p0GxvQGu5O3gYytUvtC2JlY -zypSRjVxwxrsuRcP3e641SdASwfrmzyvIgP08N4S0IFzEURkV1wp/IpH7kH41Etb -mUmrXSwfNZsnQRE5SYSOhh+LcK2wyQkdgcMv11l4KoBkcwIDAQABMA0GCSqGSIb3 -DQEBBQUAA4GBAGZLPEuJ5SiJ2ryq+CmEGOXfvlTtEL2nuGtr9PewxkgnOjZpUy+d -4TvuXJbNQc8f4AMWL/tO9w0Fk80rWKp9ea8/df4qMq5qlFWlx6yOLQxumNOmECKb -WpkUQDIDJEoFUzKMVuJf4KO/FJ345+BNLGgbJ6WujreoM1X/gYfdnJ/J ------END CERTIFICATE----- -""" - return cert - - def _fetch_private_cert(self, oauth_request): - cert = """ ------BEGIN PRIVATE KEY----- -MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBALRiMLAh9iimur8V -A7qVvdqxevEuUkW4K+2KdMXmnQbG9Aa7k7eBjK1S+0LYmVjPKlJGNXHDGuy5Fw/d -7rjVJ0BLB+ubPK8iA/Tw3hLQgXMRRGRXXCn8ikfuQfjUS1uZSatdLB81mydBETlJ -hI6GH4twrbDJCR2Bwy/XWXgqgGRzAgMBAAECgYBYWVtleUzavkbrPjy0T5FMou8H -X9u2AC2ry8vD/l7cqedtwMPp9k7TubgNFo+NGvKsl2ynyprOZR1xjQ7WgrgVB+mm -uScOM/5HVceFuGRDhYTCObE+y1kxRloNYXnx3ei1zbeYLPCHdhxRYW7T0qcynNmw -rn05/KO2RLjgQNalsQJBANeA3Q4Nugqy4QBUCEC09SqylT2K9FrrItqL2QKc9v0Z -zO2uwllCbg0dwpVuYPYXYvikNHHg+aCWF+VXsb9rpPsCQQDWR9TT4ORdzoj+Nccn -qkMsDmzt0EfNaAOwHOmVJ2RVBspPcxt5iN4HI7HNeG6U5YsFBb+/GZbgfBT3kpNG -WPTpAkBI+gFhjfJvRw38n3g/+UeAkwMI2TJQS4n8+hid0uus3/zOjDySH3XHCUno -cn1xOJAyZODBo47E+67R4jV1/gzbAkEAklJaspRPXP877NssM5nAZMU0/O/NGCZ+ -3jPgDUno6WbJn5cqm8MqWhW1xGkImgRk+fkDBquiq4gPiT898jusgQJAd5Zrr6Q8 -AO/0isr/3aa6O6NLQxISLKcPDk2NOccAfS/xOtfOz4sJYM3+Bs4Io9+dZGSDCA54 -Lw03eHTNQghS0A== ------END PRIVATE KEY----- -""" - return cert diff --git a/gdata/analytics/opensearch/__init__.py b/gdata/analytics/opensearch/__init__.py deleted file mode 100644 index 22071f7a11..0000000000 --- a/gdata/analytics/opensearch/__init__.py +++ /dev/null @@ -1,15 +0,0 @@ -#!/usr/bin/python -# -# Copyright (C) 2009 Google 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. diff --git a/gdata/analytics/opensearch/data.py b/gdata/analytics/opensearch/data.py deleted file mode 100644 index 89d7a280f3..0000000000 --- a/gdata/analytics/opensearch/data.py +++ /dev/null @@ -1,48 +0,0 @@ -#!/usr/bin/python -# -# Copyright (C) 2009 Google 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. - - -"""Contains the data classes of the OpenSearch Extension""" - - -__author__ = 'j.s@google.com (Jeff Scudder)' - - -import atom.core - - -OPENSEARCH_TEMPLATE_V1 = '{http://a9.com/-/spec/opensearchrss/1.0//}%s' -OPENSEARCH_TEMPLATE_V2 = '{http://a9.com/-/spec/opensearch/1.1//}%s' - - -class ItemsPerPage(atom.core.XmlElement): - """Describes the number of items that will be returned per page for paged feeds""" - _qname = (OPENSEARCH_TEMPLATE_V1 % 'itemsPerPage', - OPENSEARCH_TEMPLATE_V2 % 'itemsPerPage') - - -class StartIndex(atom.core.XmlElement): - """Describes the starting index of the contained entries for paged feeds""" - _qname = (OPENSEARCH_TEMPLATE_V1 % 'startIndex', - OPENSEARCH_TEMPLATE_V2 % 'startIndex') - - -class TotalResults(atom.core.XmlElement): - """Describes the total number of results associated with this feed""" - _qname = (OPENSEARCH_TEMPLATE_V1 % 'totalResults', - OPENSEARCH_TEMPLATE_V2 % 'totalResults') - - diff --git a/gdata/analytics/photos/__init__.py b/gdata/analytics/photos/__init__.py deleted file mode 100644 index 1952135c46..0000000000 --- a/gdata/analytics/photos/__init__.py +++ /dev/null @@ -1,1112 +0,0 @@ -# -*-*- encoding: utf-8 -*-*- -# -# This is the base file for the PicasaWeb python client. -# It is used for lower level operations. -# -# $Id: __init__.py 148 2007-10-28 15:09:19Z havard.gulldahl $ -# -# Copyright 2007 HÃ¥vard Gulldahl -# Portions (C) 2006 Google 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. - -"""This module provides a pythonic, gdata-centric interface to Google Photos -(a.k.a. Picasa Web Services. - -It is modelled after the gdata/* interfaces from the gdata-python-client -project[1] by Google. - -You'll find the user-friendly api in photos.service. Please see the -documentation or live help() system for available methods. - -[1]: http://gdata-python-client.googlecode.com/ - - """ - -__author__ = u'havard@gulldahl.no'# (HÃ¥vard Gulldahl)' #BUG: pydoc chokes on non-ascii chars in __author__ -__license__ = 'Apache License v2' -__version__ = '$Revision: 164 $'[11:-2] - -import re -try: - from xml.etree import cElementTree as ElementTree -except ImportError: - try: - import cElementTree as ElementTree - except ImportError: - try: - from xml.etree import ElementTree - except ImportError: - from elementtree import ElementTree -import atom -import gdata - -# importing google photo submodules -import gdata.media as Media, gdata.exif as Exif, gdata.geo as Geo - -# XML namespaces which are often used in Google Photo elements -PHOTOS_NAMESPACE = 'http://schemas.google.com/photos/2007' -MEDIA_NAMESPACE = 'http://search.yahoo.com/mrss/' -EXIF_NAMESPACE = 'http://schemas.google.com/photos/exif/2007' -OPENSEARCH_NAMESPACE = 'http://a9.com/-/spec/opensearchrss/1.0/' -GEO_NAMESPACE = 'http://www.w3.org/2003/01/geo/wgs84_pos#' -GML_NAMESPACE = 'http://www.opengis.net/gml' -GEORSS_NAMESPACE = 'http://www.georss.org/georss' -PHEED_NAMESPACE = 'http://www.pheed.com/pheed/' -BATCH_NAMESPACE = 'http://schemas.google.com/gdata/batch' - - -class PhotosBaseElement(atom.AtomBase): - """Base class for elements in the PHOTO_NAMESPACE. To add new elements, - you only need to add the element tag name to self._tag - """ - - _tag = '' - _namespace = PHOTOS_NAMESPACE - _children = atom.AtomBase._children.copy() - _attributes = atom.AtomBase._attributes.copy() - - def __init__(self, name=None, extension_elements=None, - extension_attributes=None, text=None): - self.name = name - self.text = text - self.extension_elements = extension_elements or [] - self.extension_attributes = extension_attributes or {} - #def __str__(self): - #return str(self.text) - #def __unicode__(self): - #return unicode(self.text) - def __int__(self): - return int(self.text) - def bool(self): - return self.text == 'true' - -class GPhotosBaseFeed(gdata.GDataFeed, gdata.LinkFinder): - "Base class for all Feeds in gdata.photos" - _tag = 'feed' - _namespace = atom.ATOM_NAMESPACE - _attributes = gdata.GDataFeed._attributes.copy() - _children = gdata.GDataFeed._children.copy() - # We deal with Entry elements ourselves - del _children['{%s}entry' % atom.ATOM_NAMESPACE] - - def __init__(self, author=None, category=None, contributor=None, - generator=None, icon=None, atom_id=None, link=None, logo=None, - rights=None, subtitle=None, title=None, updated=None, - entry=None, total_results=None, start_index=None, - items_per_page=None, extension_elements=None, - extension_attributes=None, text=None): - gdata.GDataFeed.__init__(self, author=author, category=category, - contributor=contributor, generator=generator, - icon=icon, atom_id=atom_id, link=link, - logo=logo, rights=rights, subtitle=subtitle, - title=title, updated=updated, entry=entry, - total_results=total_results, - start_index=start_index, - items_per_page=items_per_page, - extension_elements=extension_elements, - extension_attributes=extension_attributes, - text=text) - - def kind(self): - "(string) Returns the kind" - try: - return self.category[0].term.split('#')[1] - except IndexError: - return None - - def _feedUri(self, kind): - "Convenience method to return a uri to a feed of a special kind" - assert(kind in ('album', 'tag', 'photo', 'comment', 'user')) - here_href = self.GetSelfLink().href - if 'kind=%s' % kind in here_href: - return here_href - if not 'kind=' in here_href: - sep = '?' - if '?' in here_href: sep = '&' - return here_href + "%skind=%s" % (sep, kind) - rx = re.match('.*(kind=)(album|tag|photo|comment)', here_href) - return here_href[:rx.end(1)] + kind + here_href[rx.end(2):] - - def _ConvertElementTreeToMember(self, child_tree): - """Re-implementing the method from AtomBase, since we deal with - Entry elements specially""" - category = child_tree.find('{%s}category' % atom.ATOM_NAMESPACE) - if category is None: - return atom.AtomBase._ConvertElementTreeToMember(self, child_tree) - namespace, kind = category.get('term').split('#') - if namespace != PHOTOS_NAMESPACE: - return atom.AtomBase._ConvertElementTreeToMember(self, child_tree) - ## TODO: is it safe to use getattr on gdata.photos? - entry_class = getattr(gdata.photos, '%sEntry' % kind.title()) - if not hasattr(self, 'entry') or self.entry is None: - self.entry = [] - self.entry.append(atom._CreateClassFromElementTree( - entry_class, child_tree)) - -class GPhotosBaseEntry(gdata.GDataEntry, gdata.LinkFinder): - "Base class for all Entry elements in gdata.photos" - _tag = 'entry' - _kind = '' - _namespace = atom.ATOM_NAMESPACE - _children = gdata.GDataEntry._children.copy() - _attributes = gdata.GDataEntry._attributes.copy() - - def __init__(self, author=None, category=None, content=None, - atom_id=None, link=None, published=None, - title=None, updated=None, - extended_property=None, - extension_elements=None, extension_attributes=None, text=None): - gdata.GDataEntry.__init__(self, author=author, category=category, - content=content, atom_id=atom_id, link=link, - published=published, title=title, - updated=updated, text=text, - extension_elements=extension_elements, - extension_attributes=extension_attributes) - self.category.append( - atom.Category(scheme='http://schemas.google.com/g/2005#kind', - term = 'http://schemas.google.com/photos/2007#%s' % self._kind)) - - def kind(self): - "(string) Returns the kind" - try: - return self.category[0].term.split('#')[1] - except IndexError: - return None - - def _feedUri(self, kind): - "Convenience method to get the uri to this entry's feed of the some kind" - try: - href = self.GetFeedLink().href - except AttributeError: - return None - sep = '?' - if '?' in href: sep = '&' - return '%s%skind=%s' % (href, sep, kind) - - -class PhotosBaseEntry(GPhotosBaseEntry): - pass - -class PhotosBaseFeed(GPhotosBaseFeed): - pass - -class GPhotosBaseData(object): - pass - -class Access(PhotosBaseElement): - """The Google Photo `Access' element. - - The album's access level. Valid values are `public' or `private'. - In documentation, access level is also referred to as `visibility.'""" - - _tag = 'access' -def AccessFromString(xml_string): - return atom.CreateClassFromXMLString(Access, xml_string) - -class Albumid(PhotosBaseElement): - "The Google Photo `Albumid' element" - - _tag = 'albumid' -def AlbumidFromString(xml_string): - return atom.CreateClassFromXMLString(Albumid, xml_string) - -class BytesUsed(PhotosBaseElement): - "The Google Photo `BytesUsed' element" - - _tag = 'bytesUsed' -def BytesUsedFromString(xml_string): - return atom.CreateClassFromXMLString(BytesUsed, xml_string) - -class Client(PhotosBaseElement): - "The Google Photo `Client' element" - - _tag = 'client' -def ClientFromString(xml_string): - return atom.CreateClassFromXMLString(Client, xml_string) - -class Checksum(PhotosBaseElement): - "The Google Photo `Checksum' element" - - _tag = 'checksum' -def ChecksumFromString(xml_string): - return atom.CreateClassFromXMLString(Checksum, xml_string) - -class CommentCount(PhotosBaseElement): - "The Google Photo `CommentCount' element" - - _tag = 'commentCount' -def CommentCountFromString(xml_string): - return atom.CreateClassFromXMLString(CommentCount, xml_string) - -class CommentingEnabled(PhotosBaseElement): - "The Google Photo `CommentingEnabled' element" - - _tag = 'commentingEnabled' -def CommentingEnabledFromString(xml_string): - return atom.CreateClassFromXMLString(CommentingEnabled, xml_string) - -class Height(PhotosBaseElement): - "The Google Photo `Height' element" - - _tag = 'height' -def HeightFromString(xml_string): - return atom.CreateClassFromXMLString(Height, xml_string) - -class Id(PhotosBaseElement): - "The Google Photo `Id' element" - - _tag = 'id' -def IdFromString(xml_string): - return atom.CreateClassFromXMLString(Id, xml_string) - -class Location(PhotosBaseElement): - "The Google Photo `Location' element" - - _tag = 'location' -def LocationFromString(xml_string): - return atom.CreateClassFromXMLString(Location, xml_string) - -class MaxPhotosPerAlbum(PhotosBaseElement): - "The Google Photo `MaxPhotosPerAlbum' element" - - _tag = 'maxPhotosPerAlbum' -def MaxPhotosPerAlbumFromString(xml_string): - return atom.CreateClassFromXMLString(MaxPhotosPerAlbum, xml_string) - -class Name(PhotosBaseElement): - "The Google Photo `Name' element" - - _tag = 'name' -def NameFromString(xml_string): - return atom.CreateClassFromXMLString(Name, xml_string) - -class Nickname(PhotosBaseElement): - "The Google Photo `Nickname' element" - - _tag = 'nickname' -def NicknameFromString(xml_string): - return atom.CreateClassFromXMLString(Nickname, xml_string) - -class Numphotos(PhotosBaseElement): - "The Google Photo `Numphotos' element" - - _tag = 'numphotos' -def NumphotosFromString(xml_string): - return atom.CreateClassFromXMLString(Numphotos, xml_string) - -class Numphotosremaining(PhotosBaseElement): - "The Google Photo `Numphotosremaining' element" - - _tag = 'numphotosremaining' -def NumphotosremainingFromString(xml_string): - return atom.CreateClassFromXMLString(Numphotosremaining, xml_string) - -class Position(PhotosBaseElement): - "The Google Photo `Position' element" - - _tag = 'position' -def PositionFromString(xml_string): - return atom.CreateClassFromXMLString(Position, xml_string) - -class Photoid(PhotosBaseElement): - "The Google Photo `Photoid' element" - - _tag = 'photoid' -def PhotoidFromString(xml_string): - return atom.CreateClassFromXMLString(Photoid, xml_string) - -class Quotacurrent(PhotosBaseElement): - "The Google Photo `Quotacurrent' element" - - _tag = 'quotacurrent' -def QuotacurrentFromString(xml_string): - return atom.CreateClassFromXMLString(Quotacurrent, xml_string) - -class Quotalimit(PhotosBaseElement): - "The Google Photo `Quotalimit' element" - - _tag = 'quotalimit' -def QuotalimitFromString(xml_string): - return atom.CreateClassFromXMLString(Quotalimit, xml_string) - -class Rotation(PhotosBaseElement): - "The Google Photo `Rotation' element" - - _tag = 'rotation' -def RotationFromString(xml_string): - return atom.CreateClassFromXMLString(Rotation, xml_string) - -class Size(PhotosBaseElement): - "The Google Photo `Size' element" - - _tag = 'size' -def SizeFromString(xml_string): - return atom.CreateClassFromXMLString(Size, xml_string) - -class Snippet(PhotosBaseElement): - """The Google Photo `snippet' element. - - When searching, the snippet element will contain a - string with the word you're looking for, highlighted in html markup - E.g. when your query is `hafjell', this element may contain: - `... here at <b>Hafjell</b>.' - - You'll find this element in searches -- that is, feeds that combine the - `kind=photo' and `q=yoursearch' parameters in the request. - - See also gphoto:truncated and gphoto:snippettype. - - """ - - _tag = 'snippet' -def SnippetFromString(xml_string): - return atom.CreateClassFromXMLString(Snippet, xml_string) - -class Snippettype(PhotosBaseElement): - """The Google Photo `Snippettype' element - - When searching, this element will tell you the type of element that matches. - - You'll find this element in searches -- that is, feeds that combine the - `kind=photo' and `q=yoursearch' parameters in the request. - - See also gphoto:snippet and gphoto:truncated. - - Possible values and their interpretation: - o ALBUM_TITLE - The album title matches - o PHOTO_TAGS - The match is a tag/keyword - o PHOTO_DESCRIPTION - The match is in the photo's description - - If you discover a value not listed here, please submit a patch to update this docstring. - - """ - - _tag = 'snippettype' -def SnippettypeFromString(xml_string): - return atom.CreateClassFromXMLString(Snippettype, xml_string) - -class Thumbnail(PhotosBaseElement): - """The Google Photo `Thumbnail' element - - Used to display user's photo thumbnail (hackergotchi). - - (Not to be confused with the <media:thumbnail> element, which gives you - small versions of the photo object.)""" - - _tag = 'thumbnail' -def ThumbnailFromString(xml_string): - return atom.CreateClassFromXMLString(Thumbnail, xml_string) - -class Timestamp(PhotosBaseElement): - """The Google Photo `Timestamp' element - Represented as the number of milliseconds since January 1st, 1970. - - - Take a look at the convenience methods .isoformat() and .datetime(): - - photo_epoch = Time.text # 1180294337000 - photo_isostring = Time.isoformat() # '2007-05-27T19:32:17.000Z' - - Alternatively: - photo_datetime = Time.datetime() # (requires python >= 2.3) - """ - - _tag = 'timestamp' - def isoformat(self): - """(string) Return the timestamp as a ISO 8601 formatted string, - e.g. '2007-05-27T19:32:17.000Z' - """ - import time - epoch = float(self.text)/1000 - return time.strftime('%Y-%m-%dT%H:%M:%S.000Z', time.gmtime(epoch)) - - def datetime(self): - """(datetime.datetime) Return the timestamp as a datetime.datetime object - - Requires python 2.3 - """ - import datetime - epoch = float(self.text)/1000 - return datetime.datetime.fromtimestamp(epoch) -def TimestampFromString(xml_string): - return atom.CreateClassFromXMLString(Timestamp, xml_string) - -class Truncated(PhotosBaseElement): - """The Google Photo `Truncated' element - - You'll find this element in searches -- that is, feeds that combine the - `kind=photo' and `q=yoursearch' parameters in the request. - - See also gphoto:snippet and gphoto:snippettype. - - Possible values and their interpretation: - 0 -- unknown - """ - - _tag = 'Truncated' -def TruncatedFromString(xml_string): - return atom.CreateClassFromXMLString(Truncated, xml_string) - -class User(PhotosBaseElement): - "The Google Photo `User' element" - - _tag = 'user' -def UserFromString(xml_string): - return atom.CreateClassFromXMLString(User, xml_string) - -class Version(PhotosBaseElement): - "The Google Photo `Version' element" - - _tag = 'version' -def VersionFromString(xml_string): - return atom.CreateClassFromXMLString(Version, xml_string) - -class Width(PhotosBaseElement): - "The Google Photo `Width' element" - - _tag = 'width' -def WidthFromString(xml_string): - return atom.CreateClassFromXMLString(Width, xml_string) - -class Weight(PhotosBaseElement): - """The Google Photo `Weight' element. - - The weight of the tag is the number of times the tag - appears in the collection of tags currently being viewed. - The default weight is 1, in which case this tags is omitted.""" - _tag = 'weight' -def WeightFromString(xml_string): - return atom.CreateClassFromXMLString(Weight, xml_string) - -class CommentAuthor(atom.Author): - """The Atom `Author' element in CommentEntry entries is augmented to - contain elements from the PHOTOS_NAMESPACE - - http://groups.google.com/group/Google-Picasa-Data-API/msg/819b0025b5ff5e38 - """ - _children = atom.Author._children.copy() - _children['{%s}user' % PHOTOS_NAMESPACE] = ('user', User) - _children['{%s}nickname' % PHOTOS_NAMESPACE] = ('nickname', Nickname) - _children['{%s}thumbnail' % PHOTOS_NAMESPACE] = ('thumbnail', Thumbnail) -def CommentAuthorFromString(xml_string): - return atom.CreateClassFromXMLString(CommentAuthor, xml_string) - -########################## ################################ - -class AlbumData(object): - _children = {} - _children['{%s}id' % PHOTOS_NAMESPACE] = ('gphoto_id', Id) - _children['{%s}name' % PHOTOS_NAMESPACE] = ('name', Name) - _children['{%s}location' % PHOTOS_NAMESPACE] = ('location', Location) - _children['{%s}access' % PHOTOS_NAMESPACE] = ('access', Access) - _children['{%s}bytesUsed' % PHOTOS_NAMESPACE] = ('bytesUsed', BytesUsed) - _children['{%s}timestamp' % PHOTOS_NAMESPACE] = ('timestamp', Timestamp) - _children['{%s}numphotos' % PHOTOS_NAMESPACE] = ('numphotos', Numphotos) - _children['{%s}numphotosremaining' % PHOTOS_NAMESPACE] = \ - ('numphotosremaining', Numphotosremaining) - _children['{%s}user' % PHOTOS_NAMESPACE] = ('user', User) - _children['{%s}nickname' % PHOTOS_NAMESPACE] = ('nickname', Nickname) - _children['{%s}commentingEnabled' % PHOTOS_NAMESPACE] = \ - ('commentingEnabled', CommentingEnabled) - _children['{%s}commentCount' % PHOTOS_NAMESPACE] = \ - ('commentCount', CommentCount) - ## NOTE: storing media:group as self.media, to create a self-explaining api - gphoto_id = None - name = None - location = None - access = None - bytesUsed = None - timestamp = None - numphotos = None - numphotosremaining = None - user = None - nickname = None - commentingEnabled = None - commentCount = None - -class AlbumEntry(GPhotosBaseEntry, AlbumData): - """All metadata for a Google Photos Album - - Take a look at AlbumData for metadata accessible as attributes to this object. - - Notes: - To avoid name clashes, and to create a more sensible api, some - objects have names that differ from the original elements: - - o media:group -> self.media, - o geo:where -> self.geo, - o photo:id -> self.gphoto_id - """ - - _kind = 'album' - _children = GPhotosBaseEntry._children.copy() - _children.update(AlbumData._children.copy()) - # child tags only for Album entries, not feeds - _children['{%s}where' % GEORSS_NAMESPACE] = ('geo', Geo.Where) - _children['{%s}group' % MEDIA_NAMESPACE] = ('media', Media.Group) - media = Media.Group() - geo = Geo.Where() - - def __init__(self, author=None, category=None, content=None, - atom_id=None, link=None, published=None, - title=None, updated=None, - #GPHOTO NAMESPACE: - gphoto_id=None, name=None, location=None, access=None, - timestamp=None, numphotos=None, user=None, nickname=None, - commentingEnabled=None, commentCount=None, thumbnail=None, - # MEDIA NAMESPACE: - media=None, - # GEORSS NAMESPACE: - geo=None, - extended_property=None, - extension_elements=None, extension_attributes=None, text=None): - GPhotosBaseEntry.__init__(self, author=author, category=category, - content=content, atom_id=atom_id, link=link, - published=published, title=title, - updated=updated, text=text, - extension_elements=extension_elements, - extension_attributes=extension_attributes) - - ## NOTE: storing photo:id as self.gphoto_id, to avoid name clash with atom:id - self.gphoto_id = gphoto_id - self.name = name - self.location = location - self.access = access - self.timestamp = timestamp - self.numphotos = numphotos - self.user = user - self.nickname = nickname - self.commentingEnabled = commentingEnabled - self.commentCount = commentCount - self.thumbnail = thumbnail - self.extended_property = extended_property or [] - self.text = text - ## NOTE: storing media:group as self.media, and geo:where as geo, - ## to create a self-explaining api - self.media = media or Media.Group() - self.geo = geo or Geo.Where() - - def GetAlbumId(self): - "Return the id of this album" - - return self.GetFeedLink().href.split('/')[-1] - - def GetPhotosUri(self): - "(string) Return the uri to this albums feed of the PhotoEntry kind" - return self._feedUri('photo') - - def GetCommentsUri(self): - "(string) Return the uri to this albums feed of the CommentEntry kind" - return self._feedUri('comment') - - def GetTagsUri(self): - "(string) Return the uri to this albums feed of the TagEntry kind" - return self._feedUri('tag') - -def AlbumEntryFromString(xml_string): - return atom.CreateClassFromXMLString(AlbumEntry, xml_string) - -class AlbumFeed(GPhotosBaseFeed, AlbumData): - """All metadata for a Google Photos Album, including its sub-elements - - This feed represents an album as the container for other objects. - - A Album feed contains entries of - PhotoEntry, CommentEntry or TagEntry, - depending on the `kind' parameter in the original query. - - Take a look at AlbumData for accessible attributes. - - """ - - _children = GPhotosBaseFeed._children.copy() - _children.update(AlbumData._children.copy()) - - def GetPhotosUri(self): - "(string) Return the uri to the same feed, but of the PhotoEntry kind" - - return self._feedUri('photo') - - def GetTagsUri(self): - "(string) Return the uri to the same feed, but of the TagEntry kind" - - return self._feedUri('tag') - - def GetCommentsUri(self): - "(string) Return the uri to the same feed, but of the CommentEntry kind" - - return self._feedUri('comment') - -def AlbumFeedFromString(xml_string): - return atom.CreateClassFromXMLString(AlbumFeed, xml_string) - - -class PhotoData(object): - _children = {} - ## NOTE: storing photo:id as self.gphoto_id, to avoid name clash with atom:id - _children['{%s}id' % PHOTOS_NAMESPACE] = ('gphoto_id', Id) - _children['{%s}albumid' % PHOTOS_NAMESPACE] = ('albumid', Albumid) - _children['{%s}checksum' % PHOTOS_NAMESPACE] = ('checksum', Checksum) - _children['{%s}client' % PHOTOS_NAMESPACE] = ('client', Client) - _children['{%s}height' % PHOTOS_NAMESPACE] = ('height', Height) - _children['{%s}position' % PHOTOS_NAMESPACE] = ('position', Position) - _children['{%s}rotation' % PHOTOS_NAMESPACE] = ('rotation', Rotation) - _children['{%s}size' % PHOTOS_NAMESPACE] = ('size', Size) - _children['{%s}timestamp' % PHOTOS_NAMESPACE] = ('timestamp', Timestamp) - _children['{%s}version' % PHOTOS_NAMESPACE] = ('version', Version) - _children['{%s}width' % PHOTOS_NAMESPACE] = ('width', Width) - _children['{%s}commentingEnabled' % PHOTOS_NAMESPACE] = \ - ('commentingEnabled', CommentingEnabled) - _children['{%s}commentCount' % PHOTOS_NAMESPACE] = \ - ('commentCount', CommentCount) - ## NOTE: storing media:group as self.media, exif:tags as self.exif, and - ## geo:where as self.geo, to create a self-explaining api - _children['{%s}tags' % EXIF_NAMESPACE] = ('exif', Exif.Tags) - _children['{%s}where' % GEORSS_NAMESPACE] = ('geo', Geo.Where) - _children['{%s}group' % MEDIA_NAMESPACE] = ('media', Media.Group) - # These elements show up in search feeds - _children['{%s}snippet' % PHOTOS_NAMESPACE] = ('snippet', Snippet) - _children['{%s}snippettype' % PHOTOS_NAMESPACE] = ('snippettype', Snippettype) - _children['{%s}truncated' % PHOTOS_NAMESPACE] = ('truncated', Truncated) - gphoto_id = None - albumid = None - checksum = None - client = None - height = None - position = None - rotation = None - size = None - timestamp = None - version = None - width = None - commentingEnabled = None - commentCount = None - snippet=None - snippettype=None - truncated=None - media = Media.Group() - geo = Geo.Where() - tags = Exif.Tags() - -class PhotoEntry(GPhotosBaseEntry, PhotoData): - """All metadata for a Google Photos Photo - - Take a look at PhotoData for metadata accessible as attributes to this object. - - Notes: - To avoid name clashes, and to create a more sensible api, some - objects have names that differ from the original elements: - - o media:group -> self.media, - o exif:tags -> self.exif, - o geo:where -> self.geo, - o photo:id -> self.gphoto_id - """ - - _kind = 'photo' - _children = GPhotosBaseEntry._children.copy() - _children.update(PhotoData._children.copy()) - - def __init__(self, author=None, category=None, content=None, - atom_id=None, link=None, published=None, - title=None, updated=None, text=None, - # GPHOTO NAMESPACE: - gphoto_id=None, albumid=None, checksum=None, client=None, height=None, - position=None, rotation=None, size=None, timestamp=None, version=None, - width=None, commentCount=None, commentingEnabled=None, - # MEDIARSS NAMESPACE: - media=None, - # EXIF_NAMESPACE: - exif=None, - # GEORSS NAMESPACE: - geo=None, - extension_elements=None, extension_attributes=None): - GPhotosBaseEntry.__init__(self, author=author, category=category, - content=content, - atom_id=atom_id, link=link, published=published, - title=title, updated=updated, text=text, - extension_elements=extension_elements, - extension_attributes=extension_attributes) - - - ## NOTE: storing photo:id as self.gphoto_id, to avoid name clash with atom:id - self.gphoto_id = gphoto_id - self.albumid = albumid - self.checksum = checksum - self.client = client - self.height = height - self.position = position - self.rotation = rotation - self.size = size - self.timestamp = timestamp - self.version = version - self.width = width - self.commentingEnabled = commentingEnabled - self.commentCount = commentCount - ## NOTE: storing media:group as self.media, to create a self-explaining api - self.media = media or Media.Group() - self.exif = exif or Exif.Tags() - self.geo = geo or Geo.Where() - - def GetPostLink(self): - "Return the uri to this photo's `POST' link (use it for updates of the object)" - - return self.GetFeedLink() - - def GetCommentsUri(self): - "Return the uri to this photo's feed of CommentEntry comments" - return self._feedUri('comment') - - def GetTagsUri(self): - "Return the uri to this photo's feed of TagEntry tags" - return self._feedUri('tag') - - def GetAlbumUri(self): - """Return the uri to the AlbumEntry containing this photo""" - - href = self.GetSelfLink().href - return href[:href.find('/photoid')] - -def PhotoEntryFromString(xml_string): - return atom.CreateClassFromXMLString(PhotoEntry, xml_string) - -class PhotoFeed(GPhotosBaseFeed, PhotoData): - """All metadata for a Google Photos Photo, including its sub-elements - - This feed represents a photo as the container for other objects. - - A Photo feed contains entries of - CommentEntry or TagEntry, - depending on the `kind' parameter in the original query. - - Take a look at PhotoData for metadata accessible as attributes to this object. - - """ - _children = GPhotosBaseFeed._children.copy() - _children.update(PhotoData._children.copy()) - - def GetTagsUri(self): - "(string) Return the uri to the same feed, but of the TagEntry kind" - - return self._feedUri('tag') - - def GetCommentsUri(self): - "(string) Return the uri to the same feed, but of the CommentEntry kind" - - return self._feedUri('comment') - -def PhotoFeedFromString(xml_string): - return atom.CreateClassFromXMLString(PhotoFeed, xml_string) - -class TagData(GPhotosBaseData): - _children = {} - _children['{%s}weight' % PHOTOS_NAMESPACE] = ('weight', Weight) - weight=None - -class TagEntry(GPhotosBaseEntry, TagData): - """All metadata for a Google Photos Tag - - The actual tag is stored in the .title.text attribute - - """ - - _kind = 'tag' - _children = GPhotosBaseEntry._children.copy() - _children.update(TagData._children.copy()) - - def __init__(self, author=None, category=None, content=None, - atom_id=None, link=None, published=None, - title=None, updated=None, - # GPHOTO NAMESPACE: - weight=None, - extended_property=None, - extension_elements=None, extension_attributes=None, text=None): - GPhotosBaseEntry.__init__(self, author=author, category=category, - content=content, - atom_id=atom_id, link=link, published=published, - title=title, updated=updated, text=text, - extension_elements=extension_elements, - extension_attributes=extension_attributes) - - self.weight = weight - - def GetAlbumUri(self): - """Return the uri to the AlbumEntry containing this tag""" - - href = self.GetSelfLink().href - pos = href.find('/photoid') - if pos == -1: - return None - return href[:pos] - - def GetPhotoUri(self): - """Return the uri to the PhotoEntry containing this tag""" - - href = self.GetSelfLink().href - pos = href.find('/tag') - if pos == -1: - return None - return href[:pos] - -def TagEntryFromString(xml_string): - return atom.CreateClassFromXMLString(TagEntry, xml_string) - - -class TagFeed(GPhotosBaseFeed, TagData): - """All metadata for a Google Photos Tag, including its sub-elements""" - - _children = GPhotosBaseFeed._children.copy() - _children.update(TagData._children.copy()) - -def TagFeedFromString(xml_string): - return atom.CreateClassFromXMLString(TagFeed, xml_string) - -class CommentData(GPhotosBaseData): - _children = {} - ## NOTE: storing photo:id as self.gphoto_id, to avoid name clash with atom:id - _children['{%s}id' % PHOTOS_NAMESPACE] = ('gphoto_id', Id) - _children['{%s}albumid' % PHOTOS_NAMESPACE] = ('albumid', Albumid) - _children['{%s}photoid' % PHOTOS_NAMESPACE] = ('photoid', Photoid) - _children['{%s}author' % atom.ATOM_NAMESPACE] = ('author', [CommentAuthor,]) - gphoto_id=None - albumid=None - photoid=None - author=None - -class CommentEntry(GPhotosBaseEntry, CommentData): - """All metadata for a Google Photos Comment - - The comment is stored in the .content.text attribute, - with a content type in .content.type. - - - """ - - _kind = 'comment' - _children = GPhotosBaseEntry._children.copy() - _children.update(CommentData._children.copy()) - def __init__(self, author=None, category=None, content=None, - atom_id=None, link=None, published=None, - title=None, updated=None, - # GPHOTO NAMESPACE: - gphoto_id=None, albumid=None, photoid=None, - extended_property=None, - extension_elements=None, extension_attributes=None, text=None): - - GPhotosBaseEntry.__init__(self, author=author, category=category, - content=content, - atom_id=atom_id, link=link, published=published, - title=title, updated=updated, - extension_elements=extension_elements, - extension_attributes=extension_attributes, - text=text) - - self.gphoto_id = gphoto_id - self.albumid = albumid - self.photoid = photoid - - def GetCommentId(self): - """Return the globally unique id of this comment""" - return self.GetSelfLink().href.split('/')[-1] - - def GetAlbumUri(self): - """Return the uri to the AlbumEntry containing this comment""" - - href = self.GetSelfLink().href - return href[:href.find('/photoid')] - - def GetPhotoUri(self): - """Return the uri to the PhotoEntry containing this comment""" - - href = self.GetSelfLink().href - return href[:href.find('/commentid')] - -def CommentEntryFromString(xml_string): - return atom.CreateClassFromXMLString(CommentEntry, xml_string) - -class CommentFeed(GPhotosBaseFeed, CommentData): - """All metadata for a Google Photos Comment, including its sub-elements""" - - _children = GPhotosBaseFeed._children.copy() - _children.update(CommentData._children.copy()) - -def CommentFeedFromString(xml_string): - return atom.CreateClassFromXMLString(CommentFeed, xml_string) - -class UserData(GPhotosBaseData): - _children = {} - _children['{%s}maxPhotosPerAlbum' % PHOTOS_NAMESPACE] = ('maxPhotosPerAlbum', MaxPhotosPerAlbum) - _children['{%s}nickname' % PHOTOS_NAMESPACE] = ('nickname', Nickname) - _children['{%s}quotalimit' % PHOTOS_NAMESPACE] = ('quotalimit', Quotalimit) - _children['{%s}quotacurrent' % PHOTOS_NAMESPACE] = ('quotacurrent', Quotacurrent) - _children['{%s}thumbnail' % PHOTOS_NAMESPACE] = ('thumbnail', Thumbnail) - _children['{%s}user' % PHOTOS_NAMESPACE] = ('user', User) - _children['{%s}id' % PHOTOS_NAMESPACE] = ('gphoto_id', Id) - - maxPhotosPerAlbum=None - nickname=None - quotalimit=None - quotacurrent=None - thumbnail=None - user=None - gphoto_id=None - - -class UserEntry(GPhotosBaseEntry, UserData): - """All metadata for a Google Photos User - - This entry represents an album owner and all appropriate metadata. - - Take a look at at the attributes of the UserData for metadata available. - """ - _children = GPhotosBaseEntry._children.copy() - _children.update(UserData._children.copy()) - _kind = 'user' - - def __init__(self, author=None, category=None, content=None, - atom_id=None, link=None, published=None, - title=None, updated=None, - # GPHOTO NAMESPACE: - gphoto_id=None, maxPhotosPerAlbum=None, nickname=None, quotalimit=None, - quotacurrent=None, thumbnail=None, user=None, - extended_property=None, - extension_elements=None, extension_attributes=None, text=None): - - GPhotosBaseEntry.__init__(self, author=author, category=category, - content=content, - atom_id=atom_id, link=link, published=published, - title=title, updated=updated, - extension_elements=extension_elements, - extension_attributes=extension_attributes, - text=text) - - - self.gphoto_id=gphoto_id - self.maxPhotosPerAlbum=maxPhotosPerAlbum - self.nickname=nickname - self.quotalimit=quotalimit - self.quotacurrent=quotacurrent - self.thumbnail=thumbnail - self.user=user - - def GetAlbumsUri(self): - "(string) Return the uri to this user's feed of the AlbumEntry kind" - return self._feedUri('album') - - def GetPhotosUri(self): - "(string) Return the uri to this user's feed of the PhotoEntry kind" - return self._feedUri('photo') - - def GetCommentsUri(self): - "(string) Return the uri to this user's feed of the CommentEntry kind" - return self._feedUri('comment') - - def GetTagsUri(self): - "(string) Return the uri to this user's feed of the TagEntry kind" - return self._feedUri('tag') - -def UserEntryFromString(xml_string): - return atom.CreateClassFromXMLString(UserEntry, xml_string) - -class UserFeed(GPhotosBaseFeed, UserData): - """Feed for a User in the google photos api. - - This feed represents a user as the container for other objects. - - A User feed contains entries of - AlbumEntry, PhotoEntry, CommentEntry, UserEntry or TagEntry, - depending on the `kind' parameter in the original query. - - The user feed itself also contains all of the metadata available - as part of a UserData object.""" - _children = GPhotosBaseFeed._children.copy() - _children.update(UserData._children.copy()) - - def GetAlbumsUri(self): - """Get the uri to this feed, but with entries of the AlbumEntry kind.""" - return self._feedUri('album') - - def GetTagsUri(self): - """Get the uri to this feed, but with entries of the TagEntry kind.""" - return self._feedUri('tag') - - def GetPhotosUri(self): - """Get the uri to this feed, but with entries of the PhotosEntry kind.""" - return self._feedUri('photo') - - def GetCommentsUri(self): - """Get the uri to this feed, but with entries of the CommentsEntry kind.""" - return self._feedUri('comment') - -def UserFeedFromString(xml_string): - return atom.CreateClassFromXMLString(UserFeed, xml_string) - - - -def AnyFeedFromString(xml_string): - """Creates an instance of the appropriate feed class from the - xml string contents. - - Args: - xml_string: str A string which contains valid XML. The root element - of the XML string should match the tag and namespace of the desired - class. - - Returns: - An instance of the target class with members assigned according to the - contents of the XML - or a basic gdata.GDataFeed instance if it is - impossible to determine the appropriate class (look for extra elements - in GDataFeed's .FindExtensions() and extension_elements[] ). - """ - tree = ElementTree.fromstring(xml_string) - category = tree.find('{%s}category' % atom.ATOM_NAMESPACE) - if category is None: - # TODO: is this the best way to handle this? - return atom._CreateClassFromElementTree(GPhotosBaseFeed, tree) - namespace, kind = category.get('term').split('#') - if namespace != PHOTOS_NAMESPACE: - # TODO: is this the best way to handle this? - return atom._CreateClassFromElementTree(GPhotosBaseFeed, tree) - ## TODO: is getattr safe this way? - feed_class = getattr(gdata.photos, '%sFeed' % kind.title()) - return atom._CreateClassFromElementTree(feed_class, tree) - -def AnyEntryFromString(xml_string): - """Creates an instance of the appropriate entry class from the - xml string contents. - - Args: - xml_string: str A string which contains valid XML. The root element - of the XML string should match the tag and namespace of the desired - class. - - Returns: - An instance of the target class with members assigned according to the - contents of the XML - or a basic gdata.GDataEndry instance if it is - impossible to determine the appropriate class (look for extra elements - in GDataEntry's .FindExtensions() and extension_elements[] ). - """ - tree = ElementTree.fromstring(xml_string) - category = tree.find('{%s}category' % atom.ATOM_NAMESPACE) - if category is None: - # TODO: is this the best way to handle this? - return atom._CreateClassFromElementTree(GPhotosBaseEntry, tree) - namespace, kind = category.get('term').split('#') - if namespace != PHOTOS_NAMESPACE: - # TODO: is this the best way to handle this? - return atom._CreateClassFromElementTree(GPhotosBaseEntry, tree) - ## TODO: is getattr safe this way? - feed_class = getattr(gdata.photos, '%sEntry' % kind.title()) - return atom._CreateClassFromElementTree(feed_class, tree) - diff --git a/gdata/analytics/photos/service.py b/gdata/analytics/photos/service.py deleted file mode 100644 index 23c5febc2a..0000000000 --- a/gdata/analytics/photos/service.py +++ /dev/null @@ -1,681 +0,0 @@ -#!/usr/bin/env python -# -*-*- encoding: utf-8 -*-*- -# -# This is the service file for the Google Photo python client. -# It is used for higher level operations. -# -# $Id: service.py 144 2007-10-25 21:03:34Z havard.gulldahl $ -# -# Copyright 2007 HÃ¥vard Gulldahl -# -# 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. - -"""Google PhotoService provides a human-friendly interface to -Google Photo (a.k.a Picasa Web) services[1]. - -It extends gdata.service.GDataService and as such hides all the -nasty details about authenticating, parsing and communicating with -Google Photos. - -[1]: http://code.google.com/apis/picasaweb/gdata.html - -Example: - import gdata.photos, gdata.photos.service - pws = gdata.photos.service.PhotosService() - pws.ClientLogin(username, password) - #Get all albums - albums = pws.GetUserFeed().entry - # Get all photos in second album - photos = pws.GetFeed(albums[1].GetPhotosUri()).entry - # Get all tags for photos in second album and print them - tags = pws.GetFeed(albums[1].GetTagsUri()).entry - print [ tag.summary.text for tag in tags ] - # Get all comments for the first photos in list and print them - comments = pws.GetCommentFeed(photos[0].GetCommentsUri()).entry - print [ c.summary.text for c in comments ] - - # Get a photo to work with - photo = photos[0] - # Update metadata - - # Attributes from the <gphoto:*> namespace - photo.summary.text = u'A nice view from my veranda' - photo.title.text = u'Verandaview.jpg' - - # Attributes from the <media:*> namespace - photo.media.keywords.text = u'Home, Long-exposure, Sunset' # Comma-separated - - # Adding attributes to media object - - # Rotate 90 degrees clockwise - photo.rotation = gdata.photos.Rotation(text='90') - - # Submit modified photo object - photo = pws.UpdatePhotoMetadata(photo) - - # Make sure you only modify the newly returned object, else you'll get - # versioning errors. See Optimistic-concurrency - - # Add comment to a picture - comment = pws.InsertComment(photo, u'I wish the water always was this warm') - - # Remove comment because it was silly - print "*blush*" - pws.Delete(comment.GetEditLink().href) - -""" - -__author__ = u'havard@gulldahl.no'# (HÃ¥vard Gulldahl)' #BUG: pydoc chokes on non-ascii chars in __author__ -__license__ = 'Apache License v2' -__version__ = '$Revision: 176 $'[11:-2] - - -import sys, os.path, StringIO -import time -import gdata.service -import gdata -import atom.service -import atom -import gdata.photos - -SUPPORTED_UPLOAD_TYPES = ('bmp', 'jpeg', 'jpg', 'gif', 'png') - -UNKOWN_ERROR=1000 -GPHOTOS_BAD_REQUEST=400 -GPHOTOS_CONFLICT=409 -GPHOTOS_INTERNAL_SERVER_ERROR=500 -GPHOTOS_INVALID_ARGUMENT=601 -GPHOTOS_INVALID_CONTENT_TYPE=602 -GPHOTOS_NOT_AN_IMAGE=603 -GPHOTOS_INVALID_KIND=604 - -class GooglePhotosException(Exception): - def __init__(self, response): - - self.error_code = response['status'] - self.reason = response['reason'].strip() - if '<html>' in str(response['body']): #general html message, discard it - response['body'] = "" - self.body = response['body'].strip() - self.message = "(%(status)s) %(body)s -- %(reason)s" % response - - #return explicit error codes - error_map = { '(12) Not an image':GPHOTOS_NOT_AN_IMAGE, - 'kind: That is not one of the acceptable values': - GPHOTOS_INVALID_KIND, - - } - for msg, code in error_map.iteritems(): - if self.body == msg: - self.error_code = code - break - self.args = [self.error_code, self.reason, self.body] - -class PhotosService(gdata.service.GDataService): - ssl = True - userUri = '/data/feed/api/user/%s' - - def __init__(self, email=None, password=None, source=None, - server='picasaweb.google.com', additional_headers=None, - **kwargs): - """Creates a client for the Google Photos 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: 'picasaweb.google.com'. - **kwargs: The other parameters to pass to gdata.service.GDataService - constructor. - """ - self.email = email - self.client = source - gdata.service.GDataService.__init__( - self, email=email, password=password, service='lh2', source=source, - server=server, additional_headers=additional_headers, **kwargs) - - def GetFeed(self, uri, limit=None, start_index=None): - """Get a feed. - - The results are ordered by the values of their `updated' elements, - with the most recently updated entry appearing first in the feed. - - Arguments: - uri: the uri to fetch - limit (optional): the maximum number of entries to return. Defaults to what - the server returns. - - Returns: - one of gdata.photos.AlbumFeed, - gdata.photos.UserFeed, - gdata.photos.PhotoFeed, - gdata.photos.CommentFeed, - gdata.photos.TagFeed, - depending on the results of the query. - Raises: - GooglePhotosException - - See: - http://code.google.com/apis/picasaweb/gdata.html#Get_Album_Feed_Manual - """ - if limit is not None: - uri += '&max-results=%s' % limit - if start_index is not None: - uri += '&start-index=%s' % start_index - try: - return self.Get(uri, converter=gdata.photos.AnyFeedFromString) - except gdata.service.RequestError, e: - raise GooglePhotosException(e.args[0]) - - def GetEntry(self, uri, limit=None, start_index=None): - """Get an Entry. - - Arguments: - uri: the uri to the entry - limit (optional): the maximum number of entries to return. Defaults to what - the server returns. - - Returns: - one of gdata.photos.AlbumEntry, - gdata.photos.UserEntry, - gdata.photos.PhotoEntry, - gdata.photos.CommentEntry, - gdata.photos.TagEntry, - depending on the results of the query. - Raises: - GooglePhotosException - """ - if limit is not None: - uri += '&max-results=%s' % limit - if start_index is not None: - uri += '&start-index=%s' % start_index - try: - return self.Get(uri, converter=gdata.photos.AnyEntryFromString) - except gdata.service.RequestError, e: - raise GooglePhotosException(e.args[0]) - - def GetUserFeed(self, kind='album', user='default', limit=None): - """Get user-based feed, containing albums, photos, comments or tags; - defaults to albums. - - The entries are ordered by the values of their `updated' elements, - with the most recently updated entry appearing first in the feed. - - Arguments: - kind: the kind of entries to get, either `album', `photo', - `comment' or `tag', or a python list of these. Defaults to `album'. - user (optional): whose albums we're querying. Defaults to current user. - limit (optional): the maximum number of entries to return. - Defaults to everything the server returns. - - - Returns: - gdata.photos.UserFeed, containing appropriate Entry elements - - See: - http://code.google.com/apis/picasaweb/gdata.html#Get_Album_Feed_Manual - http://googledataapis.blogspot.com/2007/07/picasa-web-albums-adds-new-api-features.html - """ - if isinstance(kind, (list, tuple) ): - kind = ",".join(kind) - - uri = '/data/feed/api/user/%s?kind=%s' % (user, kind) - return self.GetFeed(uri, limit=limit) - - def GetTaggedPhotos(self, tag, user='default', limit=None): - """Get all photos belonging to a specific user, tagged by the given keyword - - Arguments: - tag: The tag you're looking for, e.g. `dog' - user (optional): Whose images/videos you want to search, defaults - to current user - limit (optional): the maximum number of entries to return. - Defaults to everything the server returns. - - Returns: - gdata.photos.UserFeed containing PhotoEntry elements - """ - # Lower-casing because of - # http://code.google.com/p/gdata-issues/issues/detail?id=194 - uri = '/data/feed/api/user/%s?kind=photo&tag=%s' % (user, tag.lower()) - return self.GetFeed(uri, limit) - - def SearchUserPhotos(self, query, user='default', limit=100): - """Search through all photos for a specific user and return a feed. - This will look for matches in file names and image tags (a.k.a. keywords) - - Arguments: - query: The string you're looking for, e.g. `vacation' - user (optional): The username of whose photos you want to search, defaults - to current user. - limit (optional): Don't return more than `limit' hits, defaults to 100 - - Only public photos are searched, unless you are authenticated and - searching through your own photos. - - Returns: - gdata.photos.UserFeed with PhotoEntry elements - """ - uri = '/data/feed/api/user/%s?kind=photo&q=%s' % (user, query) - return self.GetFeed(uri, limit=limit) - - def SearchCommunityPhotos(self, query, limit=100): - """Search through all public photos and return a feed. - This will look for matches in file names and image tags (a.k.a. keywords) - - Arguments: - query: The string you're looking for, e.g. `vacation' - limit (optional): Don't return more than `limit' hits, defaults to 100 - - Returns: - gdata.GDataFeed with PhotoEntry elements - """ - uri='/data/feed/api/all?q=%s' % query - return self.GetFeed(uri, limit=limit) - - def GetContacts(self, user='default', limit=None): - """Retrieve a feed that contains a list of your contacts - - Arguments: - user: Username of the user whose contacts you want - - Returns - gdata.photos.UserFeed, with UserEntry entries - - See: - http://groups.google.com/group/Google-Picasa-Data-API/msg/819b0025b5ff5e38 - """ - uri = '/data/feed/api/user/%s/contacts?kind=user' % user - return self.GetFeed(uri, limit=limit) - - def SearchContactsPhotos(self, user='default', search=None, limit=None): - """Search over your contacts' photos and return a feed - - Arguments: - user: Username of the user whose contacts you want - search (optional): What to search for (photo title, description and keywords) - - Returns - gdata.photos.UserFeed, with PhotoEntry elements - - See: - http://groups.google.com/group/Google-Picasa-Data-API/msg/819b0025b5ff5e38 - """ - - uri = '/data/feed/api/user/%s/contacts?kind=photo&q=%s' % (user, search) - return self.GetFeed(uri, limit=limit) - - def InsertAlbum(self, title, summary, location=None, access='public', - commenting_enabled='true', timestamp=None): - """Add an album. - - Needs authentication, see self.ClientLogin() - - Arguments: - title: Album title - summary: Album summary / description - access (optional): `private' or `public'. Public albums are searchable - by everyone on the internet. Defaults to `public' - commenting_enabled (optional): `true' or `false'. Defaults to `true'. - timestamp (optional): A date and time for the album, in milliseconds since - Unix epoch[1] UTC. Defaults to now. - - Returns: - The newly created gdata.photos.AlbumEntry - - See: - http://code.google.com/apis/picasaweb/gdata.html#Add_Album_Manual_Installed - - [1]: http://en.wikipedia.org/wiki/Unix_epoch - """ - album = gdata.photos.AlbumEntry() - album.title = atom.Title(text=title, title_type='text') - album.summary = atom.Summary(text=summary, summary_type='text') - if location is not None: - album.location = gdata.photos.Location(text=location) - album.access = gdata.photos.Access(text=access) - if commenting_enabled in ('true', 'false'): - album.commentingEnabled = gdata.photos.CommentingEnabled(text=commenting_enabled) - if timestamp is None: - timestamp = '%i' % int(time.time() * 1000) - album.timestamp = gdata.photos.Timestamp(text=timestamp) - try: - return self.Post(album, uri=self.userUri % self.email, - converter=gdata.photos.AlbumEntryFromString) - except gdata.service.RequestError, e: - raise GooglePhotosException(e.args[0]) - - def InsertPhoto(self, album_or_uri, photo, filename_or_handle, - content_type='image/jpeg'): - """Add a PhotoEntry - - Needs authentication, see self.ClientLogin() - - Arguments: - album_or_uri: AlbumFeed or uri of the album where the photo should go - photo: PhotoEntry to add - filename_or_handle: A file-like object or file name where the image/video - will be read from - content_type (optional): Internet media type (a.k.a. mime type) of - media object. Currently Google Photos supports these types: - o image/bmp - o image/gif - o image/jpeg - o image/png - - Images will be converted to jpeg on upload. Defaults to `image/jpeg' - - """ - - try: - assert(isinstance(photo, gdata.photos.PhotoEntry)) - except AssertionError: - raise GooglePhotosException({'status':GPHOTOS_INVALID_ARGUMENT, - 'body':'`photo` must be a gdata.photos.PhotoEntry instance', - 'reason':'Found %s, not PhotoEntry' % type(photo) - }) - try: - majtype, mintype = content_type.split('/') - assert(mintype in SUPPORTED_UPLOAD_TYPES) - except (ValueError, AssertionError): - raise GooglePhotosException({'status':GPHOTOS_INVALID_CONTENT_TYPE, - 'body':'This is not a valid content type: %s' % content_type, - 'reason':'Accepted content types: %s' % \ - ['image/'+t for t in SUPPORTED_UPLOAD_TYPES] - }) - if isinstance(filename_or_handle, (str, unicode)) and \ - os.path.exists(filename_or_handle): # it's a file name - mediasource = gdata.MediaSource() - mediasource.setFile(filename_or_handle, content_type) - elif hasattr(filename_or_handle, 'read'):# it's a file-like resource - if hasattr(filename_or_handle, 'seek'): - filename_or_handle.seek(0) # rewind pointer to the start of the file - # gdata.MediaSource needs the content length, so read the whole image - file_handle = StringIO.StringIO(filename_or_handle.read()) - name = 'image' - if hasattr(filename_or_handle, 'name'): - name = filename_or_handle.name - mediasource = gdata.MediaSource(file_handle, content_type, - content_length=file_handle.len, file_name=name) - else: #filename_or_handle is not valid - raise GooglePhotosException({'status':GPHOTOS_INVALID_ARGUMENT, - 'body':'`filename_or_handle` must be a path name or a file-like object', - 'reason':'Found %s, not path name or object with a .read() method' % \ - filename_or_handle - }) - - if isinstance(album_or_uri, (str, unicode)): # it's a uri - feed_uri = album_or_uri - elif hasattr(album_or_uri, 'GetFeedLink'): # it's a AlbumFeed object - feed_uri = album_or_uri.GetFeedLink().href - - try: - return self.Post(photo, uri=feed_uri, media_source=mediasource, - converter=gdata.photos.PhotoEntryFromString) - except gdata.service.RequestError, e: - raise GooglePhotosException(e.args[0]) - - def InsertPhotoSimple(self, album_or_uri, title, summary, filename_or_handle, - content_type='image/jpeg', keywords=None): - """Add a photo without constructing a PhotoEntry. - - Needs authentication, see self.ClientLogin() - - Arguments: - album_or_uri: AlbumFeed or uri of the album where the photo should go - title: Photo title - summary: Photo summary / description - filename_or_handle: A file-like object or file name where the image/video - will be read from - content_type (optional): Internet media type (a.k.a. mime type) of - media object. Currently Google Photos supports these types: - o image/bmp - o image/gif - o image/jpeg - o image/png - - Images will be converted to jpeg on upload. Defaults to `image/jpeg' - keywords (optional): a 1) comma separated string or 2) a python list() of - keywords (a.k.a. tags) to add to the image. - E.g. 1) `dog, vacation, happy' 2) ['dog', 'happy', 'vacation'] - - Returns: - The newly created gdata.photos.PhotoEntry or GooglePhotosException on errors - - See: - http://code.google.com/apis/picasaweb/gdata.html#Add_Album_Manual_Installed - [1]: http://en.wikipedia.org/wiki/Unix_epoch - """ - - metadata = gdata.photos.PhotoEntry() - metadata.title=atom.Title(text=title) - metadata.summary = atom.Summary(text=summary, summary_type='text') - if keywords is not None: - if isinstance(keywords, list): - keywords = ','.join(keywords) - metadata.media.keywords = gdata.media.Keywords(text=keywords) - return self.InsertPhoto(album_or_uri, metadata, filename_or_handle, - content_type) - - def UpdatePhotoMetadata(self, photo): - """Update a photo's metadata. - - Needs authentication, see self.ClientLogin() - - You can update any or all of the following metadata properties: - * <title> - * <media:description> - * <gphoto:checksum> - * <gphoto:client> - * <gphoto:rotation> - * <gphoto:timestamp> - * <gphoto:commentingEnabled> - - Arguments: - photo: a gdata.photos.PhotoEntry object with updated elements - - Returns: - The modified gdata.photos.PhotoEntry - - Example: - p = GetFeed(uri).entry[0] - p.title.text = u'My new text' - p.commentingEnabled.text = 'false' - p = UpdatePhotoMetadata(p) - - It is important that you don't keep the old object around, once - it has been updated. See - http://code.google.com/apis/gdata/reference.html#Optimistic-concurrency - """ - try: - return self.Put(data=photo, uri=photo.GetEditLink().href, - converter=gdata.photos.PhotoEntryFromString) - except gdata.service.RequestError, e: - raise GooglePhotosException(e.args[0]) - - - def UpdatePhotoBlob(self, photo_or_uri, filename_or_handle, - content_type = 'image/jpeg'): - """Update a photo's binary data. - - Needs authentication, see self.ClientLogin() - - Arguments: - photo_or_uri: a gdata.photos.PhotoEntry that will be updated, or a - `edit-media' uri pointing to it - filename_or_handle: A file-like object or file name where the image/video - will be read from - content_type (optional): Internet media type (a.k.a. mime type) of - media object. Currently Google Photos supports these types: - o image/bmp - o image/gif - o image/jpeg - o image/png - Images will be converted to jpeg on upload. Defaults to `image/jpeg' - - Returns: - The modified gdata.photos.PhotoEntry - - Example: - p = GetFeed(PhotoUri) - p = UpdatePhotoBlob(p, '/tmp/newPic.jpg') - - It is important that you don't keep the old object around, once - it has been updated. See - http://code.google.com/apis/gdata/reference.html#Optimistic-concurrency - """ - - try: - majtype, mintype = content_type.split('/') - assert(mintype in SUPPORTED_UPLOAD_TYPES) - except (ValueError, AssertionError): - raise GooglePhotosException({'status':GPHOTOS_INVALID_CONTENT_TYPE, - 'body':'This is not a valid content type: %s' % content_type, - 'reason':'Accepted content types: %s' % \ - ['image/'+t for t in SUPPORTED_UPLOAD_TYPES] - }) - - if isinstance(filename_or_handle, (str, unicode)) and \ - os.path.exists(filename_or_handle): # it's a file name - photoblob = gdata.MediaSource() - photoblob.setFile(filename_or_handle, content_type) - elif hasattr(filename_or_handle, 'read'):# it's a file-like resource - if hasattr(filename_or_handle, 'seek'): - filename_or_handle.seek(0) # rewind pointer to the start of the file - # gdata.MediaSource needs the content length, so read the whole image - file_handle = StringIO.StringIO(filename_or_handle.read()) - name = 'image' - if hasattr(filename_or_handle, 'name'): - name = filename_or_handle.name - mediasource = gdata.MediaSource(file_handle, content_type, - content_length=file_handle.len, file_name=name) - else: #filename_or_handle is not valid - raise GooglePhotosException({'status':GPHOTOS_INVALID_ARGUMENT, - 'body':'`filename_or_handle` must be a path name or a file-like object', - 'reason':'Found %s, not path name or an object with .read() method' % \ - type(filename_or_handle) - }) - - if isinstance(photo_or_uri, (str, unicode)): - entry_uri = photo_or_uri # it's a uri - elif hasattr(photo_or_uri, 'GetEditMediaLink'): - entry_uri = photo_or_uri.GetEditMediaLink().href - try: - return self.Put(photoblob, entry_uri, - converter=gdata.photos.PhotoEntryFromString) - except gdata.service.RequestError, e: - raise GooglePhotosException(e.args[0]) - - def InsertTag(self, photo_or_uri, tag): - """Add a tag (a.k.a. keyword) to a photo. - - Needs authentication, see self.ClientLogin() - - Arguments: - photo_or_uri: a gdata.photos.PhotoEntry that will be tagged, or a - `post' uri pointing to it - (string) tag: The tag/keyword - - Returns: - The new gdata.photos.TagEntry - - Example: - p = GetFeed(PhotoUri) - tag = InsertTag(p, 'Beautiful sunsets') - - """ - tag = gdata.photos.TagEntry(title=atom.Title(text=tag)) - if isinstance(photo_or_uri, (str, unicode)): - post_uri = photo_or_uri # it's a uri - elif hasattr(photo_or_uri, 'GetEditMediaLink'): - post_uri = photo_or_uri.GetPostLink().href - try: - return self.Post(data=tag, uri=post_uri, - converter=gdata.photos.TagEntryFromString) - except gdata.service.RequestError, e: - raise GooglePhotosException(e.args[0]) - - - def InsertComment(self, photo_or_uri, comment): - """Add a comment to a photo. - - Needs authentication, see self.ClientLogin() - - Arguments: - photo_or_uri: a gdata.photos.PhotoEntry that is about to be commented - , or a `post' uri pointing to it - (string) comment: The actual comment - - Returns: - The new gdata.photos.CommentEntry - - Example: - p = GetFeed(PhotoUri) - tag = InsertComment(p, 'OOOH! I would have loved to be there. - Who's that in the back?') - - """ - comment = gdata.photos.CommentEntry(content=atom.Content(text=comment)) - if isinstance(photo_or_uri, (str, unicode)): - post_uri = photo_or_uri # it's a uri - elif hasattr(photo_or_uri, 'GetEditMediaLink'): - post_uri = photo_or_uri.GetPostLink().href - try: - return self.Post(data=comment, uri=post_uri, - converter=gdata.photos.CommentEntryFromString) - except gdata.service.RequestError, e: - raise GooglePhotosException(e.args[0]) - - def Delete(self, object_or_uri, *args, **kwargs): - """Delete an object. - - Re-implementing the GDataService.Delete method, to add some - convenience. - - Arguments: - object_or_uri: Any object that has a GetEditLink() method that - returns a link, or a uri to that object. - - Returns: - ? or GooglePhotosException on errors - """ - try: - uri = object_or_uri.GetEditLink().href - except AttributeError: - uri = object_or_uri - try: - return gdata.service.GDataService.Delete(self, uri, *args, **kwargs) - except gdata.service.RequestError, e: - raise GooglePhotosException(e.args[0]) - -def GetSmallestThumbnail(media_thumbnail_list): - """Helper function to get the smallest thumbnail of a list of - gdata.media.Thumbnail. - Returns gdata.media.Thumbnail """ - r = {} - for thumb in media_thumbnail_list: - r[int(thumb.width)*int(thumb.height)] = thumb - keys = r.keys() - keys.sort() - return r[keys[0]] - -def ConvertAtomTimestampToEpoch(timestamp): - """Helper function to convert a timestamp string, for instance - from atom:updated or atom:published, to milliseconds since Unix epoch - (a.k.a. POSIX time). - - `2007-07-22T00:45:10.000Z' -> """ - return time.mktime(time.strptime(timestamp, '%Y-%m-%dT%H:%M:%S.000Z')) - ## TODO: Timezone aware diff --git a/gdata/analytics/projecthosting/__init__.py b/gdata/analytics/projecthosting/__init__.py deleted file mode 100644 index 8b13789179..0000000000 --- a/gdata/analytics/projecthosting/__init__.py +++ /dev/null @@ -1 +0,0 @@ - diff --git a/gdata/analytics/projecthosting/client.py b/gdata/analytics/projecthosting/client.py deleted file mode 100644 index 512eb32bbb..0000000000 --- a/gdata/analytics/projecthosting/client.py +++ /dev/null @@ -1,201 +0,0 @@ -#!/usr/bin/env python -# -# Copyright 2009 Google 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. - -import atom.data -import gdata.client -import gdata.gauth -import gdata.projecthosting.data - - -class ProjectHostingClient(gdata.client.GDClient): - """Client to interact with the Project Hosting GData API.""" - api_version = '1.0' - auth_service = 'code' - auth_scopes = gdata.gauth.AUTH_SCOPES['code'] - host = 'code.google.com' - ssl = True - - def get_issues(self, project_name, - desired_class=gdata.projecthosting.data.IssuesFeed, **kwargs): - """Get a feed of issues for a particular project. - - Args: - project_name str The name of the project. - query Query Set returned issues parameters. - - Returns: - data.IssuesFeed - """ - return self.get_feed(gdata.projecthosting.data.ISSUES_FULL_FEED % - project_name, desired_class=desired_class, **kwargs) - - def add_issue(self, project_name, title, content, author, - status=None, owner=None, labels=None, ccs=None, **kwargs): - """Create a new issue for the project. - - Args: - project_name str The name of the project. - title str The title of the new issue. - content str The summary of the new issue. - author str The authenticated user's username. - status str The status of the new issue, Accepted, etc. - owner str The username of new issue's owner. - labels [str] Labels to associate with the new issue. - ccs [str] usernames to Cc on the new issue. - Returns: - data.IssueEntry - """ - new_entry = gdata.projecthosting.data.IssueEntry( - title=atom.data.Title(text=title), - content=atom.data.Content(text=content), - author=[atom.data.Author(name=atom.data.Name(text=author))]) - - if status: - new_entry.status = gdata.projecthosting.data.Status(text=status) - - if owner: - owner = [gdata.projecthosting.data.Owner( - username=gdata.projecthosting.data.Username(text=owner))] - - if labels: - new_entry.label = [gdata.projecthosting.data.Label(text=label) - for label in labels] - if ccs: - new_entry.cc = [ - gdata.projecthosting.data.Cc( - username=gdata.projecthosting.data.Username(text=cc)) - for cc in ccs] - - return self.post( - new_entry, - gdata.projecthosting.data.ISSUES_FULL_FEED % project_name, - **kwargs) - - def update_issue(self, project_name, issue_id, author, comment=None, - summary=None, status=None, owner=None, labels=None, ccs=None, - **kwargs): - """Update or comment on one issue for the project. - - Args: - project_name str The name of the issue's project. - issue_id str The issue number needing updated. - author str The authenticated user's username. - comment str A comment to append to the issue - summary str Rewrite the summary of the issue. - status str A new status for the issue. - owner str The username of the new owner. - labels [str] Labels to set on the issue (prepend issue with - to remove a - label). - ccs [str] Ccs to set on th enew issue (prepend cc with - to remove a cc). - - Returns: - data.CommentEntry - """ - updates = gdata.projecthosting.data.Updates() - - if summary: - updates.summary = gdata.projecthosting.data.Summary(text=summary) - - if status: - updates.status = gdata.projecthosting.data.Status(text=status) - - if owner: - updates.ownerUpdate = gdata.projecthosting.data.OwnerUpdate(text=owner) - - if labels: - updates.label = [gdata.projecthosting.data.Label(text=label) - for label in labels] - if ccs: - updates.ccUpdate = [gdata.projecthosting.data.CcUpdate(text=cc) - for cc in ccs] - - update_entry = gdata.projecthosting.data.CommentEntry( - content=atom.data.Content(text=comment), - author=[atom.data.Author(name=atom.data.Name(text=author))], - updates=updates) - - return self.post( - update_entry, - gdata.projecthosting.data.COMMENTS_FULL_FEED % (project_name, issue_id), - **kwargs) - - def get_comments(self, project_name, issue_id, - desired_class=gdata.projecthosting.data.CommentsFeed, - **kwargs): - """Get a feed of all updates to an issue. - - Args: - project_name str The name of the issue's project. - issue_id str The issue number needing updated. - - Returns: - data.CommentsFeed - """ - return self.get_feed( - gdata.projecthosting.data.COMMENTS_FULL_FEED % (project_name, issue_id), - desired_class=desired_class, **kwargs) - - def update(self, entry, auth_token=None, force=False, **kwargs): - """Unsupported GData update method. - - Use update_*() instead. - """ - raise NotImplementedError( - 'GData Update operation unsupported, try update_*') - - def delete(self, entry_or_uri, auth_token=None, force=False, **kwargs): - """Unsupported GData delete method. - - Use update_issue(status='Closed') instead. - """ - raise NotImplementedError( - 'GData Delete API unsupported, try closing the issue instead.') - - -class Query(gdata.client.Query): - - def __init__(self, issue_id=None, label=None, canned_query=None, owner=None, - status=None, **kwargs): - """Constructs a Google Data Query to filter feed contents serverside. - Args: - issue_id: int or str The issue to return based on the issue id. - label: str A label returned issues must have. - canned_query: str Return issues based on a canned query identifier - owner: str Return issues based on the owner of the issue. For Gmail users, - this will be the part of the email preceding the '@' sign. - status: str Return issues based on the status of the issue. - """ - super(Query, self).__init__(**kwargs) - self.label = label - self.issue_id = issue_id - self.canned_query = canned_query - self.owner = owner - self.status = status - - def modify_request(self, http_request): - if self.issue_id: - gdata.client._add_query_param('id', self.issue_id, http_request) - if self.label: - gdata.client._add_query_param('label', self.label, http_request) - if self.canned_query: - gdata.client._add_query_param('can', self.canned_query, http_request) - if self.owner: - gdata.client._add_query_param('owner', self.owner, http_request) - if self.status: - gdata.client._add_query_param('status', self.status, http_request) - super(Query, self).modify_request(http_request) - - ModifyRequest = modify_request diff --git a/gdata/analytics/projecthosting/data.py b/gdata/analytics/projecthosting/data.py deleted file mode 100644 index b0af2f5f71..0000000000 --- a/gdata/analytics/projecthosting/data.py +++ /dev/null @@ -1,134 +0,0 @@ -#!/usr/bin/env python -# -# Copyright 2009 Google 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. - - -# This module is used for version 2 of the Google Data APIs. - - -"""Provides classes and constants for XML in the Google Project Hosting API. - -Canonical documentation for the raw XML which these classes represent can be -found here: http://code.google.com/p/support/wiki/IssueTrackerAPI -""" - - -__author__ = 'jlapenna@google.com (Joe LaPenna)' - -import atom.core -import gdata.data - - -ISSUES_TEMPLATE = '{http://schemas.google.com/projecthosting/issues/2009}%s' - - -ISSUES_FULL_FEED = '/feeds/issues/p/%s/issues/full' -COMMENTS_FULL_FEED = '/feeds/issues/p/%s/issues/%s/comments/full' - - -class Uri(atom.core.XmlElement): - """The issues:uri element.""" - _qname = ISSUES_TEMPLATE % 'uri' - - -class Username(atom.core.XmlElement): - """The issues:username element.""" - _qname = ISSUES_TEMPLATE % 'username' - - -class Cc(atom.core.XmlElement): - """The issues:cc element.""" - _qname = ISSUES_TEMPLATE % 'cc' - uri = Uri - username = Username - - -class Label(atom.core.XmlElement): - """The issues:label element.""" - _qname = ISSUES_TEMPLATE % 'label' - - -class Owner(atom.core.XmlElement): - """The issues:owner element.""" - _qname = ISSUES_TEMPLATE % 'owner' - uri = Uri - username = Username - - -class Stars(atom.core.XmlElement): - """The issues:stars element.""" - _qname = ISSUES_TEMPLATE % 'stars' - - -class State(atom.core.XmlElement): - """The issues:state element.""" - _qname = ISSUES_TEMPLATE % 'state' - - -class Status(atom.core.XmlElement): - """The issues:status element.""" - _qname = ISSUES_TEMPLATE % 'status' - - -class Summary(atom.core.XmlElement): - """The issues:summary element.""" - _qname = ISSUES_TEMPLATE % 'summary' - - -class OwnerUpdate(atom.core.XmlElement): - """The issues:ownerUpdate element.""" - _qname = ISSUES_TEMPLATE % 'ownerUpdate' - - -class CcUpdate(atom.core.XmlElement): - """The issues:ccUpdate element.""" - _qname = ISSUES_TEMPLATE % 'ccUpdate' - - -class Updates(atom.core.XmlElement): - """The issues:updates element.""" - _qname = ISSUES_TEMPLATE % 'updates' - summary = Summary - status = Status - ownerUpdate = OwnerUpdate - label = [Label] - ccUpdate = [CcUpdate] - - -class IssueEntry(gdata.data.GDEntry): - """Represents the information of one issue.""" - _qname = atom.data.ATOM_TEMPLATE % 'entry' - owner = Owner - cc = [Cc] - label = [Label] - stars = Stars - state = State - status = Status - - -class IssuesFeed(gdata.data.GDFeed): - """An Atom feed listing a project's issues.""" - entry = [IssueEntry] - - -class CommentEntry(gdata.data.GDEntry): - """An entry detailing one comment on an issue.""" - _qname = atom.data.ATOM_TEMPLATE % 'entry' - updates = Updates - - -class CommentsFeed(gdata.data.GDFeed): - """An Atom feed listing a project's issue's comments.""" - entry = [CommentEntry] diff --git a/gdata/analytics/sample_util.py b/gdata/analytics/sample_util.py deleted file mode 100644 index aae866e2af..0000000000 --- a/gdata/analytics/sample_util.py +++ /dev/null @@ -1,269 +0,0 @@ -#!/usr/bin/env python -# -# Copyright (C) 2009 Google 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. - - -"""Provides utility functions used with command line samples.""" - -# This module is used for version 2 of the Google Data APIs. - -import sys -import getpass -import urllib -import gdata.gauth - -__author__ = 'j.s@google.com (Jeff Scudder)' - - -CLIENT_LOGIN = 1 -AUTHSUB = 2 -OAUTH = 3 - -HMAC = 1 -RSA = 2 - - -class SettingsUtil(object): - """Gather's user preferences from flags or command prompts. - - An instance of this object stores the choices made by the user. At some - point it might be useful to save the user's preferences so that they do - not need to always set flags or answer preference prompts. - """ - - def __init__(self, prefs=None): - self.prefs = prefs or {} - - def get_param(self, name, prompt='', secret=False, ask=True, reuse=False): - # First, check in this objects stored preferences. - if name in self.prefs: - return self.prefs[name] - # Second, check for a command line parameter. - value = None - for i in xrange(len(sys.argv)): - if sys.argv[i].startswith('--%s=' % name): - value = sys.argv[i].split('=')[1] - elif sys.argv[i] == '--%s' % name: - value = sys.argv[i + 1] - # Third, if it was not on the command line, ask the user to input the - # value. - if value is None and ask: - prompt = '%s: ' % prompt - if secret: - value = getpass.getpass(prompt) - else: - value = raw_input(prompt) - # If we want to save the preference for reuse in future requests, add it - # to this object's prefs. - if value is not None and reuse: - self.prefs[name] = value - return value - - def authorize_client(self, client, auth_type=None, service=None, - source=None, scopes=None, oauth_type=None, - consumer_key=None, consumer_secret=None): - """Uses command line arguments, or prompts user for token values.""" - if 'client_auth_token' in self.prefs: - return - if auth_type is None: - auth_type = int(self.get_param( - 'auth_type', 'Please choose the authorization mechanism you want' - ' to use.\n' - '1. to use your email address and password (ClientLogin)\n' - '2. to use a web browser to visit an auth web page (AuthSub)\n' - '3. if you have registed to use OAuth\n', reuse=True)) - - # Get the scopes for the services we want to access. - if auth_type == AUTHSUB or auth_type == OAUTH: - if scopes is None: - scopes = self.get_param( - 'scopes', 'Enter the URL prefixes (scopes) for the resources you ' - 'would like to access.\nFor multiple scope URLs, place a comma ' - 'between each URL.\n' - 'Example: http://www.google.com/calendar/feeds/,' - 'http://www.google.com/m8/feeds/\n', reuse=True).split(',') - elif isinstance(scopes, (str, unicode)): - scopes = scopes.split(',') - - if auth_type == CLIENT_LOGIN: - email = self.get_param('email', 'Please enter your username', - reuse=False) - password = self.get_param('password', 'Password', True, reuse=False) - if service is None: - service = self.get_param( - 'service', 'What is the name of the service you wish to access?' - '\n(See list:' - ' http://code.google.com/apis/gdata/faq.html#clientlogin)', - reuse=True) - if source is None: - source = self.get_param('source', ask=False, reuse=True) - client.client_login(email, password, source=source, service=service) - elif auth_type == AUTHSUB: - auth_sub_token = self.get_param('auth_sub_token', ask=False, reuse=True) - session_token = self.get_param('session_token', ask=False, reuse=True) - private_key = None - auth_url = None - single_use_token = None - rsa_private_key = self.get_param( - 'rsa_private_key', - 'If you want to use secure mode AuthSub, please provide the\n' - ' location of your RSA private key which corresponds to the\n' - ' certificate you have uploaded for your domain. If you do not\n' - ' have an RSA key, simply press enter', reuse=True) - - if rsa_private_key: - try: - private_key_file = open(rsa_private_key, 'rb') - private_key = private_key_file.read() - private_key_file.close() - except IOError: - print 'Unable to read private key from file' - - if private_key is not None: - if client.auth_token is None: - if session_token: - client.auth_token = gdata.gauth.SecureAuthSubToken( - session_token, private_key, scopes) - self.prefs['client_auth_token'] = gdata.gauth.token_to_blob( - client.auth_token) - return - elif auth_sub_token: - client.auth_token = gdata.gauth.SecureAuthSubToken( - auth_sub_token, private_key, scopes) - client.upgrade_token() - self.prefs['client_auth_token'] = gdata.gauth.token_to_blob( - client.auth_token) - return - - auth_url = gdata.gauth.generate_auth_sub_url( - 'http://gauthmachine.appspot.com/authsub', scopes, True) - print 'with a private key, get ready for this URL', auth_url - - else: - if client.auth_token is None: - if session_token: - client.auth_token = gdata.gauth.AuthSubToken(session_token, - scopes) - self.prefs['client_auth_token'] = gdata.gauth.token_to_blob( - client.auth_token) - return - elif auth_sub_token: - client.auth_token = gdata.gauth.AuthSubToken(auth_sub_token, - scopes) - client.upgrade_token() - self.prefs['client_auth_token'] = gdata.gauth.token_to_blob( - client.auth_token) - return - - auth_url = gdata.gauth.generate_auth_sub_url( - 'http://gauthmachine.appspot.com/authsub', scopes) - - print 'Visit the following URL in your browser to authorize this app:' - print str(auth_url) - print 'After agreeing to authorize the app, copy the token value from' - print ' the URL. Example: "www.google.com/?token=ab12" token value is' - print ' ab12' - token_value = raw_input('Please enter the token value: ') - if private_key is not None: - single_use_token = gdata.gauth.SecureAuthSubToken( - token_value, private_key, scopes) - else: - single_use_token = gdata.gauth.AuthSubToken(token_value, scopes) - client.auth_token = single_use_token - client.upgrade_token() - - elif auth_type == OAUTH: - if oauth_type is None: - oauth_type = int(self.get_param( - 'oauth_type', 'Please choose the authorization mechanism you want' - ' to use.\n' - '1. use an HMAC signature using your consumer key and secret\n' - '2. use RSA with your private key to sign requests\n', - reuse=True)) - - consumer_key = self.get_param( - 'consumer_key', 'Please enter your OAuth conumer key ' - 'which identifies your app', reuse=True) - - if oauth_type == HMAC: - consumer_secret = self.get_param( - 'consumer_secret', 'Please enter your OAuth conumer secret ' - 'which you share with the OAuth provider', True, reuse=False) - # Swap out this code once the client supports requesting an oauth - # token. - # Get a request token. - request_token = client.get_oauth_token( - scopes, 'http://gauthmachine.appspot.com/oauth', consumer_key, - consumer_secret=consumer_secret) - elif oauth_type == RSA: - rsa_private_key = self.get_param( - 'rsa_private_key', - 'Please provide the location of your RSA private key which\n' - ' corresponds to the certificate you have uploaded for your' - ' domain.', - reuse=True) - try: - private_key_file = open(rsa_private_key, 'rb') - private_key = private_key_file.read() - private_key_file.close() - except IOError: - print 'Unable to read private key from file' - - request_token = client.get_oauth_token( - scopes, 'http://gauthmachine.appspot.com/oauth', consumer_key, - rsa_private_key=private_key) - else: - print 'Invalid OAuth signature type' - return None - - # Authorize the request token in the browser. - print 'Visit the following URL in your browser to authorize this app:' - print str(request_token.generate_authorization_url()) - print 'After agreeing to authorize the app, copy URL from the browser\'s' - print ' address bar.' - url = raw_input('Please enter the url: ') - gdata.gauth.authorize_request_token(request_token, url) - # Exchange for an access token. - client.auth_token = client.get_access_token(request_token) - else: - print 'Invalid authorization type.' - return None - if client.auth_token: - self.prefs['client_auth_token'] = gdata.gauth.token_to_blob( - client.auth_token) - - -def get_param(name, prompt='', secret=False, ask=True): - settings = SettingsUtil() - return settings.get_param(name=name, prompt=prompt, secret=secret, ask=ask) - - -def authorize_client(client, auth_type=None, service=None, source=None, - scopes=None, oauth_type=None, consumer_key=None, - consumer_secret=None): - """Uses command line arguments, or prompts user for token values.""" - settings = SettingsUtil() - return settings.authorize_client(client=client, auth_type=auth_type, - service=service, source=source, - scopes=scopes, oauth_type=oauth_type, - consumer_key=consumer_key, - consumer_secret=consumer_secret) - - -def print_options(): - """Displays usage information, available command line params.""" - # TODO: fill in the usage description for authorizing the client. - print '' - diff --git a/gdata/analytics/service.py b/gdata/analytics/service.py deleted file mode 100644 index df05d1f50e..0000000000 --- a/gdata/analytics/service.py +++ /dev/null @@ -1,1718 +0,0 @@ -#!/usr/bin/python -# -# Copyright (C) 2006,2008 Google 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. - - -"""GDataService provides CRUD ops. and programmatic login for GData services. - - Error: A base exception class for all exceptions in the gdata_client - module. - - CaptchaRequired: This exception is thrown when a login attempt results in a - captcha challenge from the ClientLogin service. When this - exception is thrown, the captcha_token and captcha_url are - set to the values provided in the server's response. - - BadAuthentication: Raised when a login attempt is made with an incorrect - username or password. - - NotAuthenticated: Raised if an operation requiring authentication is called - before a user has authenticated. - - NonAuthSubToken: Raised if a method to modify an AuthSub token is used when - the user is either not authenticated or is authenticated - through another authentication mechanism. - - NonOAuthToken: Raised if a method to modify an OAuth token is used when the - user is either not authenticated or is authenticated through - another authentication mechanism. - - RequestError: Raised if a CRUD request returned a non-success code. - - UnexpectedReturnType: Raised if the response from the server was not of the - desired type. For example, this would be raised if the - server sent a feed when the client requested an entry. - - GDataService: Encapsulates user credentials needed to perform insert, update - and delete operations with the GData API. An instance can - perform user authentication, query, insertion, deletion, and - update. - - Query: Eases query URI creation by allowing URI parameters to be set as - dictionary attributes. For example a query with a feed of - '/base/feeds/snippets' and ['bq'] set to 'digital camera' will - produce '/base/feeds/snippets?bq=digital+camera' when .ToUri() is - called on it. -""" - - -__author__ = 'api.jscudder (Jeffrey Scudder)' - -import re -import urllib -import urlparse -try: - from xml.etree import cElementTree as ElementTree -except ImportError: - try: - import cElementTree as ElementTree - except ImportError: - try: - from xml.etree import ElementTree - except ImportError: - from elementtree import ElementTree -import atom.service -import gdata -import atom -import atom.http_interface -import atom.token_store -import gdata.auth -import gdata.gauth - - -AUTH_SERVER_HOST = 'https://www.google.com' - - -# When requesting an AuthSub token, it is often helpful to track the scope -# which is being requested. One way to accomplish this is to add a URL -# parameter to the 'next' URL which contains the requested scope. This -# constant is the default name (AKA key) for the URL parameter. -SCOPE_URL_PARAM_NAME = 'authsub_token_scope' -# When requesting an OAuth access token or authorization of an existing OAuth -# request token, it is often helpful to track the scope(s) which is/are being -# requested. One way to accomplish this is to add a URL parameter to the -# 'callback' URL which contains the requested scope. This constant is the -# default name (AKA key) for the URL parameter. -OAUTH_SCOPE_URL_PARAM_NAME = 'oauth_token_scope' -# Maps the service names used in ClientLogin to scope URLs. -CLIENT_LOGIN_SCOPES = gdata.gauth.AUTH_SCOPES -# Default parameters for GDataService.GetWithRetries method -DEFAULT_NUM_RETRIES = 3 -DEFAULT_DELAY = 1 -DEFAULT_BACKOFF = 2 - - -def lookup_scopes(service_name): - """Finds the scope URLs for the desired service. - - In some cases, an unknown service may be used, and in those cases this - function will return None. - """ - if service_name in CLIENT_LOGIN_SCOPES: - return CLIENT_LOGIN_SCOPES[service_name] - return None - - -# Module level variable specifies which module should be used by GDataService -# objects to make HttpRequests. This setting can be overridden on each -# instance of GDataService. -# This module level variable is deprecated. Reassign the http_client member -# of a GDataService object instead. -http_request_handler = atom.service - - -class Error(Exception): - pass - - -class CaptchaRequired(Error): - pass - - -class BadAuthentication(Error): - pass - - -class NotAuthenticated(Error): - pass - - -class NonAuthSubToken(Error): - pass - - -class NonOAuthToken(Error): - pass - - -class RequestError(Error): - pass - - -class UnexpectedReturnType(Error): - pass - - -class BadAuthenticationServiceURL(Error): - pass - - -class FetchingOAuthRequestTokenFailed(RequestError): - pass - - -class TokenUpgradeFailed(RequestError): - pass - - -class RevokingOAuthTokenFailed(RequestError): - pass - - -class AuthorizationRequired(Error): - pass - - -class TokenHadNoScope(Error): - pass - - -class RanOutOfTries(Error): - pass - - -class GDataService(atom.service.AtomService): - """Contains elements needed for GData login and CRUD request headers. - - Maintains additional headers (tokens for example) needed for the GData - services to allow a user to perform inserts, updates, and deletes. - """ - # The hander member is deprecated, use http_client instead. - handler = None - # The auth_token member is deprecated, use the token_store instead. - auth_token = None - # The tokens dict is deprecated in favor of the token_store. - tokens = None - - def __init__(self, email=None, password=None, account_type='HOSTED_OR_GOOGLE', - service=None, auth_service_url=None, source=None, server=None, - additional_headers=None, handler=None, tokens=None, - http_client=None, token_store=None): - """Creates an object of type GDataService. - - Args: - email: string (optional) The user's email address, used for - authentication. - password: string (optional) The user's password. - account_type: string (optional) The type of account to use. Use - 'GOOGLE' for regular Google accounts or 'HOSTED' for Google - Apps accounts, or 'HOSTED_OR_GOOGLE' to try finding a HOSTED - account first and, if it doesn't exist, try finding a regular - GOOGLE account. Default value: 'HOSTED_OR_GOOGLE'. - service: string (optional) The desired service for which credentials - will be obtained. - auth_service_url: string (optional) User-defined auth token request URL - allows users to explicitly specify where to send auth token requests. - 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: 'base.google.com'. - additional_headers: dictionary (optional) Any additional headers which - should be included with CRUD operations. - handler: module (optional) This parameter is deprecated and has been - replaced by http_client. - tokens: This parameter is deprecated, calls should be made to - token_store instead. - http_client: An object responsible for making HTTP requests using a - request method. If none is provided, a new instance of - atom.http.ProxiedHttpClient will be used. - token_store: Keeps a collection of authorization tokens which can be - applied to requests for a specific URLs. Critical methods are - find_token based on a URL (atom.url.Url or a string), add_token, - and remove_token. - """ - atom.service.AtomService.__init__(self, http_client=http_client, - token_store=token_store) - self.email = email - self.password = password - self.account_type = account_type - self.service = service - self.auth_service_url = auth_service_url - self.server = server - self.additional_headers = additional_headers or {} - self._oauth_input_params = None - self.__SetSource(source) - self.__captcha_token = None - self.__captcha_url = None - self.__gsessionid = None - - if http_request_handler.__name__ == 'gdata.urlfetch': - import gdata.alt.appengine - self.http_client = gdata.alt.appengine.AppEngineHttpClient() - - def _SetSessionId(self, session_id): - """Used in unit tests to simulate a 302 which sets a gsessionid.""" - self.__gsessionid = session_id - - # Define properties for GDataService - def _SetAuthSubToken(self, auth_token, scopes=None): - """Deprecated, use SetAuthSubToken instead.""" - self.SetAuthSubToken(auth_token, scopes=scopes) - - def __SetAuthSubToken(self, auth_token, scopes=None): - """Deprecated, use SetAuthSubToken instead.""" - self._SetAuthSubToken(auth_token, scopes=scopes) - - def _GetAuthToken(self): - """Returns the auth token used for authenticating requests. - - Returns: - string - """ - current_scopes = lookup_scopes(self.service) - if current_scopes: - token = self.token_store.find_token(current_scopes[0]) - if hasattr(token, 'auth_header'): - return token.auth_header - return None - - def _GetCaptchaToken(self): - """Returns a captcha token if the most recent login attempt generated one. - - The captcha token is only set if the Programmatic Login attempt failed - because the Google service issued a captcha challenge. - - Returns: - string - """ - return self.__captcha_token - - def __GetCaptchaToken(self): - return self._GetCaptchaToken() - - captcha_token = property(__GetCaptchaToken, - doc="""Get the captcha token for a login request.""") - - def _GetCaptchaURL(self): - """Returns the URL of the captcha image if a login attempt generated one. - - The captcha URL is only set if the Programmatic Login attempt failed - because the Google service issued a captcha challenge. - - Returns: - string - """ - return self.__captcha_url - - def __GetCaptchaURL(self): - return self._GetCaptchaURL() - - captcha_url = property(__GetCaptchaURL, - doc="""Get the captcha URL for a login request.""") - - def GetGeneratorFromLinkFinder(self, link_finder, func, - num_retries=DEFAULT_NUM_RETRIES, - delay=DEFAULT_DELAY, - backoff=DEFAULT_BACKOFF): - """returns a generator for pagination""" - yield link_finder - next = link_finder.GetNextLink() - while next is not None: - next_feed = func(str(self.GetWithRetries( - next.href, num_retries=num_retries, delay=delay, backoff=backoff))) - yield next_feed - next = next_feed.GetNextLink() - - def _GetElementGeneratorFromLinkFinder(self, link_finder, func, - num_retries=DEFAULT_NUM_RETRIES, - delay=DEFAULT_DELAY, - backoff=DEFAULT_BACKOFF): - for element in self.GetGeneratorFromLinkFinder(link_finder, func, - num_retries=num_retries, - delay=delay, - backoff=backoff).entry: - yield element - - def GetOAuthInputParameters(self): - return self._oauth_input_params - - def SetOAuthInputParameters(self, signature_method, consumer_key, - consumer_secret=None, rsa_key=None, - two_legged_oauth=False, requestor_id=None): - """Sets parameters required for using OAuth authentication mechanism. - - NOTE: Though consumer_secret and rsa_key are optional, either of the two - is required depending on the value of the signature_method. - - Args: - signature_method: class which provides implementation for strategy class - oauth.oauth.OAuthSignatureMethod. Signature method to be used for - signing each request. Valid implementations are provided as the - constants defined by gdata.auth.OAuthSignatureMethod. Currently - they are gdata.auth.OAuthSignatureMethod.RSA_SHA1 and - gdata.auth.OAuthSignatureMethod.HMAC_SHA1 - consumer_key: string Domain identifying third_party web application. - consumer_secret: string (optional) Secret generated during registration. - Required only for HMAC_SHA1 signature method. - rsa_key: string (optional) Private key required for RSA_SHA1 signature - method. - two_legged_oauth: boolean (optional) Enables two-legged OAuth process. - requestor_id: string (optional) User email adress to make requests on - their behalf. This parameter should only be set when two_legged_oauth - is True. - """ - self._oauth_input_params = gdata.auth.OAuthInputParams( - signature_method, consumer_key, consumer_secret=consumer_secret, - rsa_key=rsa_key, requestor_id=requestor_id) - if two_legged_oauth: - oauth_token = gdata.auth.OAuthToken( - oauth_input_params=self._oauth_input_params) - self.SetOAuthToken(oauth_token) - - def FetchOAuthRequestToken(self, scopes=None, extra_parameters=None, - request_url='%s/accounts/OAuthGetRequestToken' % \ - AUTH_SERVER_HOST, oauth_callback=None): - """Fetches and sets the OAuth request token and returns it. - - Args: - scopes: string or list of string base URL(s) of the service(s) to be - accessed. If None, then this method tries to determine the - scope(s) from the current service. - extra_parameters: dict (optional) key-value pairs as any additional - parameters to be included in the URL and signature while making a - request for fetching an OAuth request token. All the OAuth parameters - are added by default. But if provided through this argument, any - default parameters will be overwritten. For e.g. a default parameter - oauth_version 1.0 can be overwritten if - extra_parameters = {'oauth_version': '2.0'} - request_url: Request token URL. The default is - 'https://www.google.com/accounts/OAuthGetRequestToken'. - oauth_callback: str (optional) If set, it is assume the client is using - the OAuth v1.0a protocol where the callback url is sent in the - request token step. If the oauth_callback is also set in - extra_params, this value will override that one. - - Returns: - The fetched request token as a gdata.auth.OAuthToken object. - - Raises: - FetchingOAuthRequestTokenFailed if the server responded to the request - with an error. - """ - if scopes is None: - scopes = lookup_scopes(self.service) - if not isinstance(scopes, (list, tuple)): - scopes = [scopes,] - if oauth_callback: - if extra_parameters is not None: - extra_parameters['oauth_callback'] = oauth_callback - else: - extra_parameters = {'oauth_callback': oauth_callback} - request_token_url = gdata.auth.GenerateOAuthRequestTokenUrl( - self._oauth_input_params, scopes, - request_token_url=request_url, - extra_parameters=extra_parameters) - response = self.http_client.request('GET', str(request_token_url)) - if response.status == 200: - token = gdata.auth.OAuthToken() - token.set_token_string(response.read()) - token.scopes = scopes - token.oauth_input_params = self._oauth_input_params - self.SetOAuthToken(token) - return token - error = { - 'status': response.status, - 'reason': 'Non 200 response on fetch request token', - 'body': response.read() - } - raise FetchingOAuthRequestTokenFailed(error) - - def SetOAuthToken(self, oauth_token): - """Attempts to set the current token and add it to the token store. - - The oauth_token can be any OAuth token i.e. unauthorized request token, - authorized request token or access token. - This method also attempts to add the token to the token store. - Use this method any time you want the current token to point to the - oauth_token passed. For e.g. call this method with the request token - you receive from FetchOAuthRequestToken. - - Args: - request_token: gdata.auth.OAuthToken OAuth request token. - """ - if self.auto_set_current_token: - self.current_token = oauth_token - if self.auto_store_tokens: - self.token_store.add_token(oauth_token) - - def GenerateOAuthAuthorizationURL( - self, request_token=None, callback_url=None, extra_params=None, - include_scopes_in_callback=False, - scopes_param_prefix=OAUTH_SCOPE_URL_PARAM_NAME, - request_url='%s/accounts/OAuthAuthorizeToken' % AUTH_SERVER_HOST): - """Generates URL at which user will login to authorize the request token. - - Args: - request_token: gdata.auth.OAuthToken (optional) OAuth request token. - If not specified, then the current token will be used if it is of - type <gdata.auth.OAuthToken>, else it is found by looking in the - token_store by looking for a token for the current scope. - callback_url: string (optional) The URL user will be sent to after - logging in and granting access. - extra_params: dict (optional) Additional parameters to be sent. - include_scopes_in_callback: Boolean (default=False) if set to True, and - if 'callback_url' is present, the 'callback_url' will be modified to - include the scope(s) from the request token as a URL parameter. The - key for the 'callback' URL's scope parameter will be - OAUTH_SCOPE_URL_PARAM_NAME. The benefit of including the scope URL as - a parameter to the 'callback' URL, is that the page which receives - the OAuth token will be able to tell which URLs the token grants - access to. - scopes_param_prefix: string (default='oauth_token_scope') The URL - parameter key which maps to the list of valid scopes for the token. - This URL parameter will be included in the callback URL along with - the scopes of the token as value if include_scopes_in_callback=True. - request_url: Authorization URL. The default is - 'https://www.google.com/accounts/OAuthAuthorizeToken'. - Returns: - A string URL at which the user is required to login. - - Raises: - NonOAuthToken if the user's request token is not an OAuth token or if a - request token was not available. - """ - if request_token and not isinstance(request_token, gdata.auth.OAuthToken): - raise NonOAuthToken - if not request_token: - if isinstance(self.current_token, gdata.auth.OAuthToken): - request_token = self.current_token - else: - current_scopes = lookup_scopes(self.service) - if current_scopes: - token = self.token_store.find_token(current_scopes[0]) - if isinstance(token, gdata.auth.OAuthToken): - request_token = token - if not request_token: - raise NonOAuthToken - return str(gdata.auth.GenerateOAuthAuthorizationUrl( - request_token, - authorization_url=request_url, - callback_url=callback_url, extra_params=extra_params, - include_scopes_in_callback=include_scopes_in_callback, - scopes_param_prefix=scopes_param_prefix)) - - def UpgradeToOAuthAccessToken(self, authorized_request_token=None, - request_url='%s/accounts/OAuthGetAccessToken' \ - % AUTH_SERVER_HOST, oauth_version='1.0', - oauth_verifier=None): - """Upgrades the authorized request token to an access token and returns it - - Args: - authorized_request_token: gdata.auth.OAuthToken (optional) OAuth request - token. If not specified, then the current token will be used if it is - of type <gdata.auth.OAuthToken>, else it is found by looking in the - token_store by looking for a token for the current scope. - request_url: Access token URL. The default is - 'https://www.google.com/accounts/OAuthGetAccessToken'. - oauth_version: str (default='1.0') oauth_version parameter. All other - 'oauth_' parameters are added by default. This parameter too, is - added by default but here you can override it's value. - oauth_verifier: str (optional) If present, it is assumed that the client - will use the OAuth v1.0a protocol which includes passing the - oauth_verifier (as returned by the SP) in the access token step. - - Returns: - Access token - - Raises: - NonOAuthToken if the user's authorized request token is not an OAuth - token or if an authorized request token was not available. - TokenUpgradeFailed if the server responded to the request with an - error. - """ - if (authorized_request_token and - not isinstance(authorized_request_token, gdata.auth.OAuthToken)): - raise NonOAuthToken - if not authorized_request_token: - if isinstance(self.current_token, gdata.auth.OAuthToken): - authorized_request_token = self.current_token - else: - current_scopes = lookup_scopes(self.service) - if current_scopes: - token = self.token_store.find_token(current_scopes[0]) - if isinstance(token, gdata.auth.OAuthToken): - authorized_request_token = token - if not authorized_request_token: - raise NonOAuthToken - access_token_url = gdata.auth.GenerateOAuthAccessTokenUrl( - authorized_request_token, - self._oauth_input_params, - access_token_url=request_url, - oauth_version=oauth_version, - oauth_verifier=oauth_verifier) - response = self.http_client.request('GET', str(access_token_url)) - if response.status == 200: - token = gdata.auth.OAuthTokenFromHttpBody(response.read()) - token.scopes = authorized_request_token.scopes - token.oauth_input_params = authorized_request_token.oauth_input_params - self.SetOAuthToken(token) - return token - else: - raise TokenUpgradeFailed({'status': response.status, - 'reason': 'Non 200 response on upgrade', - 'body': response.read()}) - - def RevokeOAuthToken(self, request_url='%s/accounts/AuthSubRevokeToken' % \ - AUTH_SERVER_HOST): - """Revokes an existing OAuth token. - - request_url: Token revoke URL. The default is - 'https://www.google.com/accounts/AuthSubRevokeToken'. - Raises: - NonOAuthToken if the user's auth token is not an OAuth token. - RevokingOAuthTokenFailed if request for revoking an OAuth token failed. - """ - scopes = lookup_scopes(self.service) - token = self.token_store.find_token(scopes[0]) - if not isinstance(token, gdata.auth.OAuthToken): - raise NonOAuthToken - - response = token.perform_request(self.http_client, 'GET', request_url, - headers={'Content-Type':'application/x-www-form-urlencoded'}) - if response.status == 200: - self.token_store.remove_token(token) - else: - raise RevokingOAuthTokenFailed - - def GetAuthSubToken(self): - """Returns the AuthSub token as a string. - - If the token is an gdta.auth.AuthSubToken, the Authorization Label - ("AuthSub token") is removed. - - This method examines the current_token to see if it is an AuthSubToken - or SecureAuthSubToken. If not, it searches the token_store for a token - which matches the current scope. - - The current scope is determined by the service name string member. - - Returns: - If the current_token is set to an AuthSubToken/SecureAuthSubToken, - return the token string. If there is no current_token, a token string - for a token which matches the service object's default scope is returned. - If there are no tokens valid for the scope, returns None. - """ - if isinstance(self.current_token, gdata.auth.AuthSubToken): - return self.current_token.get_token_string() - current_scopes = lookup_scopes(self.service) - if current_scopes: - token = self.token_store.find_token(current_scopes[0]) - if isinstance(token, gdata.auth.AuthSubToken): - return token.get_token_string() - else: - token = self.token_store.find_token(atom.token_store.SCOPE_ALL) - if isinstance(token, gdata.auth.ClientLoginToken): - return token.get_token_string() - return None - - def SetAuthSubToken(self, token, scopes=None, rsa_key=None): - """Sets the token sent in requests to an AuthSub token. - - Sets the current_token and attempts to add the token to the token_store. - - Only use this method if you have received a token from the AuthSub - service. The auth token is set automatically when UpgradeToSessionToken() - is used. See documentation for Google AuthSub here: - http://code.google.com/apis/accounts/AuthForWebApps.html - - Args: - token: gdata.auth.AuthSubToken or gdata.auth.SecureAuthSubToken or string - The token returned by the AuthSub service. If the token is an - AuthSubToken or SecureAuthSubToken, the scope information stored in - the token is used. If the token is a string, the scopes parameter is - used to determine the valid scopes. - scopes: list of URLs for which the token is valid. This is only used - if the token parameter is a string. - rsa_key: string (optional) Private key required for RSA_SHA1 signature - method. This parameter is necessary if the token is a string - representing a secure token. - """ - if not isinstance(token, gdata.auth.AuthSubToken): - token_string = token - if rsa_key: - token = gdata.auth.SecureAuthSubToken(rsa_key) - else: - token = gdata.auth.AuthSubToken() - - token.set_token_string(token_string) - - # If no scopes were set for the token, use the scopes passed in, or - # try to determine the scopes based on the current service name. If - # all else fails, set the token to match all requests. - if not token.scopes: - if scopes is None: - scopes = lookup_scopes(self.service) - if scopes is None: - scopes = [atom.token_store.SCOPE_ALL] - token.scopes = scopes - if self.auto_set_current_token: - self.current_token = token - if self.auto_store_tokens: - self.token_store.add_token(token) - - def GetClientLoginToken(self): - """Returns the token string for the current token or a token matching the - service scope. - - If the current_token is a ClientLoginToken, the token string for - the current token is returned. If the current_token is not set, this method - searches for a token in the token_store which is valid for the service - object's current scope. - - The current scope is determined by the service name string member. - The token string is the end of the Authorization header, it doesn not - include the ClientLogin label. - """ - if isinstance(self.current_token, gdata.auth.ClientLoginToken): - return self.current_token.get_token_string() - current_scopes = lookup_scopes(self.service) - if current_scopes: - token = self.token_store.find_token(current_scopes[0]) - if isinstance(token, gdata.auth.ClientLoginToken): - return token.get_token_string() - else: - token = self.token_store.find_token(atom.token_store.SCOPE_ALL) - if isinstance(token, gdata.auth.ClientLoginToken): - return token.get_token_string() - return None - - def SetClientLoginToken(self, token, scopes=None): - """Sets the token sent in requests to a ClientLogin token. - - This method sets the current_token to a new ClientLoginToken and it - also attempts to add the ClientLoginToken to the token_store. - - Only use this method if you have received a token from the ClientLogin - service. The auth_token is set automatically when ProgrammaticLogin() - is used. See documentation for Google ClientLogin here: - http://code.google.com/apis/accounts/docs/AuthForInstalledApps.html - - Args: - token: string or instance of a ClientLoginToken. - """ - if not isinstance(token, gdata.auth.ClientLoginToken): - token_string = token - token = gdata.auth.ClientLoginToken() - token.set_token_string(token_string) - - if not token.scopes: - if scopes is None: - scopes = lookup_scopes(self.service) - if scopes is None: - scopes = [atom.token_store.SCOPE_ALL] - token.scopes = scopes - if self.auto_set_current_token: - self.current_token = token - if self.auto_store_tokens: - self.token_store.add_token(token) - - # Private methods to create the source property. - def __GetSource(self): - return self.__source - - def __SetSource(self, new_source): - self.__source = new_source - # Update the UserAgent header to include the new application name. - self.additional_headers['User-Agent'] = atom.http_interface.USER_AGENT % ( - self.__source,) - - source = property(__GetSource, __SetSource, - doc="""The source is the name of the application making the request. - It should be in the form company_id-app_name-app_version""") - - # Authentication operations - - def ProgrammaticLogin(self, captcha_token=None, captcha_response=None): - """Authenticates the user and sets the GData Auth token. - - Login retreives a temporary auth token which must be used with all - requests to GData services. The auth token is stored in the GData client - object. - - Login is also used to respond to a captcha challenge. If the user's login - attempt failed with a CaptchaRequired error, the user can respond by - calling Login with the captcha token and the answer to the challenge. - - Args: - captcha_token: string (optional) The identifier for the captcha challenge - which was presented to the user. - captcha_response: string (optional) The user's answer to the captch - challenge. - - Raises: - CaptchaRequired if the login service will require a captcha response - BadAuthentication if the login service rejected the username or password - Error if the login service responded with a 403 different from the above - """ - request_body = gdata.auth.generate_client_login_request_body(self.email, - self.password, self.service, self.source, self.account_type, - captcha_token, captcha_response) - - # If the user has defined their own authentication service URL, - # send the ClientLogin requests to this URL: - if not self.auth_service_url: - auth_request_url = AUTH_SERVER_HOST + '/accounts/ClientLogin' - else: - auth_request_url = self.auth_service_url - - auth_response = self.http_client.request('POST', auth_request_url, - data=request_body, - headers={'Content-Type':'application/x-www-form-urlencoded'}) - response_body = auth_response.read() - - if auth_response.status == 200: - # TODO: insert the token into the token_store directly. - self.SetClientLoginToken( - gdata.auth.get_client_login_token(response_body)) - self.__captcha_token = None - self.__captcha_url = None - - elif auth_response.status == 403: - # Examine each line to find the error type and the captcha token and - # captch URL if they are present. - captcha_parameters = gdata.auth.get_captcha_challenge(response_body, - captcha_base_url='%s/accounts/' % AUTH_SERVER_HOST) - if captcha_parameters: - self.__captcha_token = captcha_parameters['token'] - self.__captcha_url = captcha_parameters['url'] - raise CaptchaRequired, 'Captcha Required' - elif response_body.splitlines()[0] == 'Error=BadAuthentication': - self.__captcha_token = None - self.__captcha_url = None - raise BadAuthentication, 'Incorrect username or password' - else: - self.__captcha_token = None - self.__captcha_url = None - raise Error, 'Server responded with a 403 code' - elif auth_response.status == 302: - self.__captcha_token = None - self.__captcha_url = None - # Google tries to redirect all bad URLs back to - # http://www.google.<locale>. If a redirect - # attempt is made, assume the user has supplied an incorrect authentication URL - raise BadAuthenticationServiceURL, 'Server responded with a 302 code.' - - def ClientLogin(self, username, password, account_type=None, service=None, - auth_service_url=None, source=None, captcha_token=None, - captcha_response=None): - """Convenience method for authenticating using ProgrammaticLogin. - - Sets values for email, password, and other optional members. - - Args: - username: - password: - account_type: string (optional) - service: string (optional) - auth_service_url: string (optional) - captcha_token: string (optional) - captcha_response: string (optional) - """ - self.email = username - self.password = password - - if account_type: - self.account_type = account_type - if service: - self.service = service - if source: - self.source = source - if auth_service_url: - self.auth_service_url = auth_service_url - - self.ProgrammaticLogin(captcha_token, captcha_response) - - def GenerateAuthSubURL(self, next, scope, secure=False, session=True, - domain='default'): - """Generate a URL at which the user will login and be redirected back. - - Users enter their credentials on a Google login page and a token is sent - to the URL specified in next. See documentation for AuthSub login at: - http://code.google.com/apis/accounts/docs/AuthSub.html - - Args: - next: string The URL user will be sent to after logging in. - scope: string or list of strings. The URLs of the services to be - accessed. - secure: boolean (optional) Determines whether or not the issued token - is a secure token. - session: boolean (optional) Determines whether or not the issued token - can be upgraded to a session token. - """ - if not isinstance(scope, (list, tuple)): - scope = (scope,) - return gdata.auth.generate_auth_sub_url(next, scope, secure=secure, - session=session, - request_url='%s/accounts/AuthSubRequest' % AUTH_SERVER_HOST, - domain=domain) - - def UpgradeToSessionToken(self, token=None): - """Upgrades a single use AuthSub token to a session token. - - Args: - token: A gdata.auth.AuthSubToken or gdata.auth.SecureAuthSubToken - (optional) which is good for a single use but can be upgraded - to a session token. If no token is passed in, the token - is found by looking in the token_store by looking for a token - for the current scope. - - Raises: - NonAuthSubToken if the user's auth token is not an AuthSub token - TokenUpgradeFailed if the server responded to the request with an - error. - """ - if token is None: - scopes = lookup_scopes(self.service) - if scopes: - token = self.token_store.find_token(scopes[0]) - else: - token = self.token_store.find_token(atom.token_store.SCOPE_ALL) - if not isinstance(token, gdata.auth.AuthSubToken): - raise NonAuthSubToken - - self.SetAuthSubToken(self.upgrade_to_session_token(token)) - - def upgrade_to_session_token(self, token): - """Upgrades a single use AuthSub token to a session token. - - Args: - token: A gdata.auth.AuthSubToken or gdata.auth.SecureAuthSubToken - which is good for a single use but can be upgraded to a - session token. - - Returns: - The upgraded token as a gdata.auth.AuthSubToken object. - - Raises: - TokenUpgradeFailed if the server responded to the request with an - error. - """ - response = token.perform_request(self.http_client, 'GET', - AUTH_SERVER_HOST + '/accounts/AuthSubSessionToken', - headers={'Content-Type':'application/x-www-form-urlencoded'}) - response_body = response.read() - if response.status == 200: - token.set_token_string( - gdata.auth.token_from_http_body(response_body)) - return token - else: - raise TokenUpgradeFailed({'status': response.status, - 'reason': 'Non 200 response on upgrade', - 'body': response_body}) - - def RevokeAuthSubToken(self): - """Revokes an existing AuthSub token. - - Raises: - NonAuthSubToken if the user's auth token is not an AuthSub token - """ - scopes = lookup_scopes(self.service) - token = self.token_store.find_token(scopes[0]) - if not isinstance(token, gdata.auth.AuthSubToken): - raise NonAuthSubToken - - response = token.perform_request(self.http_client, 'GET', - AUTH_SERVER_HOST + '/accounts/AuthSubRevokeToken', - headers={'Content-Type':'application/x-www-form-urlencoded'}) - if response.status == 200: - self.token_store.remove_token(token) - - def AuthSubTokenInfo(self): - """Fetches the AuthSub token's metadata from the server. - - Raises: - NonAuthSubToken if the user's auth token is not an AuthSub token - """ - scopes = lookup_scopes(self.service) - token = self.token_store.find_token(scopes[0]) - if not isinstance(token, gdata.auth.AuthSubToken): - raise NonAuthSubToken - - response = token.perform_request(self.http_client, 'GET', - AUTH_SERVER_HOST + '/accounts/AuthSubTokenInfo', - headers={'Content-Type':'application/x-www-form-urlencoded'}) - result_body = response.read() - if response.status == 200: - return result_body - else: - raise RequestError, {'status': response.status, - 'body': result_body} - - def GetWithRetries(self, uri, extra_headers=None, redirects_remaining=4, - encoding='UTF-8', converter=None, num_retries=DEFAULT_NUM_RETRIES, - delay=DEFAULT_DELAY, backoff=DEFAULT_BACKOFF, logger=None): - """This is a wrapper method for Get with retrying capability. - - To avoid various errors while retrieving bulk entities by retrying - specified times. - - Note this method relies on the time module and so may not be usable - by default in Python2.2. - - Args: - num_retries: Integer; the retry count. - delay: Integer; the initial delay for retrying. - backoff: Integer; how much the delay should lengthen after each failure. - logger: An object which has a debug(str) method to receive logging - messages. Recommended that you pass in the logging module. - Raises: - ValueError if any of the parameters has an invalid value. - RanOutOfTries on failure after number of retries. - """ - # Moved import for time module inside this method since time is not a - # default module in Python2.2. This method will not be usable in - # Python2.2. - import time - if backoff <= 1: - raise ValueError("backoff must be greater than 1") - num_retries = int(num_retries) - - if num_retries < 0: - raise ValueError("num_retries must be 0 or greater") - - if delay <= 0: - raise ValueError("delay must be greater than 0") - - # Let's start - mtries, mdelay = num_retries, delay - while mtries > 0: - if mtries != num_retries: - if logger: - logger.debug("Retrying: %s" % uri) - try: - rv = self.Get(uri, extra_headers=extra_headers, - redirects_remaining=redirects_remaining, - encoding=encoding, converter=converter) - except SystemExit: - # Allow this error - raise - except RequestError, e: - # Error 500 is 'internal server error' and warrants a retry - # Error 503 is 'service unavailable' and warrants a retry - if e[0]['status'] not in [500, 503]: - raise e - # Else, fall through to the retry code... - except Exception, e: - if logger: - logger.debug(e) - # Fall through to the retry code... - else: - # This is the right path. - return rv - mtries -= 1 - time.sleep(mdelay) - mdelay *= backoff - raise RanOutOfTries('Ran out of tries.') - - # CRUD operations - def Get(self, uri, extra_headers=None, redirects_remaining=4, - encoding='UTF-8', converter=None): - """Query the GData API with the given URI - - The uri is the portion of the URI after the server value - (ex: www.google.com). - - To perform a query against Google Base, set the server to - 'base.google.com' and set the uri to '/base/feeds/...', where ... is - your query. For example, to find snippets for all digital cameras uri - should be set to: '/base/feeds/snippets?bq=digital+camera' - - Args: - uri: string The query in the form of a URI. Example: - '/base/feeds/snippets?bq=digital+camera'. - extra_headers: dictionary (optional) Extra HTTP headers to be included - in the GET request. These headers are in addition to - those stored in the client's additional_headers property. - The client automatically sets the Content-Type and - Authorization headers. - redirects_remaining: int (optional) Tracks the number of additional - redirects this method will allow. If the service object receives - a redirect and remaining is 0, it will not follow the redirect. - This was added to avoid infinite redirect loops. - encoding: string (optional) The character encoding for the server's - response. Default is UTF-8 - converter: func (optional) A function which will transform - the server's results before it is returned. Example: use - GDataFeedFromString to parse the server response as if it - were a GDataFeed. - - Returns: - If there is no ResultsTransformer specified in the call, a GDataFeed - or GDataEntry depending on which is sent from the server. If the - response is niether a feed or entry and there is no ResultsTransformer, - return a string. If there is a ResultsTransformer, the returned value - will be that of the ResultsTransformer function. - """ - - if extra_headers is None: - extra_headers = {} - - if self.__gsessionid is not None: - if uri.find('gsessionid=') < 0: - if uri.find('?') > -1: - uri += '&gsessionid=%s' % (self.__gsessionid,) - else: - uri += '?gsessionid=%s' % (self.__gsessionid,) - - server_response = self.request('GET', uri, - headers=extra_headers) - result_body = server_response.read() - - if server_response.status == 200: - if converter: - return converter(result_body) - # There was no ResultsTransformer specified, so try to convert the - # server's response into a GDataFeed. - feed = gdata.GDataFeedFromString(result_body) - if not feed: - # If conversion to a GDataFeed failed, try to convert the server's - # response to a GDataEntry. - entry = gdata.GDataEntryFromString(result_body) - if not entry: - # The server's response wasn't a feed, or an entry, so return the - # response body as a string. - return result_body - return entry - return feed - elif server_response.status == 302: - if redirects_remaining > 0: - location = (server_response.getheader('Location') - or server_response.getheader('location')) - if location is not None: - m = re.compile('[\?\&]gsessionid=(\w*\-)').search(location) - if m is not None: - self.__gsessionid = m.group(1) - return GDataService.Get(self, location, extra_headers, redirects_remaining - 1, - encoding=encoding, converter=converter) - else: - raise RequestError, {'status': server_response.status, - 'reason': '302 received without Location header', - 'body': result_body} - else: - raise RequestError, {'status': server_response.status, - 'reason': 'Redirect received, but redirects_remaining <= 0', - 'body': result_body} - else: - raise RequestError, {'status': server_response.status, - 'reason': server_response.reason, 'body': result_body} - - def GetMedia(self, uri, extra_headers=None): - """Returns a MediaSource containing media and its metadata from the given - URI string. - """ - response_handle = self.request('GET', uri, - headers=extra_headers) - return gdata.MediaSource(response_handle, response_handle.getheader( - 'Content-Type'), - response_handle.getheader('Content-Length')) - - def GetEntry(self, uri, extra_headers=None): - """Query the GData API with the given URI and receive an Entry. - - See also documentation for gdata.service.Get - - Args: - uri: string The query in the form of a URI. Example: - '/base/feeds/snippets?bq=digital+camera'. - extra_headers: dictionary (optional) Extra HTTP headers to be included - in the GET request. These headers are in addition to - those stored in the client's additional_headers property. - The client automatically sets the Content-Type and - Authorization headers. - - Returns: - A GDataEntry built from the XML in the server's response. - """ - - result = GDataService.Get(self, uri, extra_headers, - converter=atom.EntryFromString) - if isinstance(result, atom.Entry): - return result - else: - raise UnexpectedReturnType, 'Server did not send an entry' - - def GetFeed(self, uri, extra_headers=None, - converter=gdata.GDataFeedFromString): - """Query the GData API with the given URI and receive a Feed. - - See also documentation for gdata.service.Get - - Args: - uri: string The query in the form of a URI. Example: - '/base/feeds/snippets?bq=digital+camera'. - extra_headers: dictionary (optional) Extra HTTP headers to be included - in the GET request. These headers are in addition to - those stored in the client's additional_headers property. - The client automatically sets the Content-Type and - Authorization headers. - - Returns: - A GDataFeed built from the XML in the server's response. - """ - - result = GDataService.Get(self, uri, extra_headers, converter=converter) - if isinstance(result, atom.Feed): - return result - else: - raise UnexpectedReturnType, 'Server did not send a feed' - - def GetNext(self, feed): - """Requests the next 'page' of results in the feed. - - This method uses the feed's next link to request an additional feed - and uses the class of the feed to convert the results of the GET request. - - Args: - feed: atom.Feed or a subclass. The feed should contain a next link and - the type of the feed will be applied to the results from the - server. The new feed which is returned will be of the same class - as this feed which was passed in. - - Returns: - A new feed representing the next set of results in the server's feed. - The type of this feed will match that of the feed argument. - """ - next_link = feed.GetNextLink() - # Create a closure which will convert an XML string to the class of - # the feed object passed in. - def ConvertToFeedClass(xml_string): - return atom.CreateClassFromXMLString(feed.__class__, xml_string) - # Make a GET request on the next link and use the above closure for the - # converted which processes the XML string from the server. - if next_link and next_link.href: - return GDataService.Get(self, next_link.href, - converter=ConvertToFeedClass) - else: - return None - - def Post(self, data, uri, extra_headers=None, url_params=None, - escape_params=True, redirects_remaining=4, media_source=None, - converter=None): - """Insert or update data into a GData service at the given URI. - - Args: - data: string, ElementTree._Element, atom.Entry, or gdata.GDataEntry The - XML to be sent to the uri. - uri: string The location (feed) to which the data should be inserted. - Example: '/base/feeds/items'. - extra_headers: dict (optional) HTTP headers which are to be included. - The client automatically sets the Content-Type, - Authorization, and Content-Length headers. - url_params: dict (optional) Additional URL parameters to be included - in the URI. These are translated into query arguments - in the form '&dict_key=value&...'. - Example: {'max-results': '250'} becomes &max-results=250 - escape_params: boolean (optional) If false, the calling code has already - ensured that the query will form a valid URL (all - reserved characters have been escaped). If true, this - method will escape the query and any URL parameters - provided. - media_source: MediaSource (optional) Container for the media to be sent - along with the entry, if provided. - converter: func (optional) A function which will be executed on the - server's response. Often this is a function like - GDataEntryFromString which will parse the body of the server's - response and return a GDataEntry. - - Returns: - If the post succeeded, this method will return a GDataFeed, GDataEntry, - or the results of running converter on the server's result body (if - converter was specified). - """ - return GDataService.PostOrPut(self, 'POST', data, uri, - extra_headers=extra_headers, url_params=url_params, - escape_params=escape_params, redirects_remaining=redirects_remaining, - media_source=media_source, converter=converter) - - def PostOrPut(self, verb, data, uri, extra_headers=None, url_params=None, - escape_params=True, redirects_remaining=4, media_source=None, - converter=None): - """Insert data into a GData service at the given URI. - - Args: - verb: string, either 'POST' or 'PUT' - data: string, ElementTree._Element, atom.Entry, or gdata.GDataEntry The - XML to be sent to the uri. - uri: string The location (feed) to which the data should be inserted. - Example: '/base/feeds/items'. - extra_headers: dict (optional) HTTP headers which are to be included. - The client automatically sets the Content-Type, - Authorization, and Content-Length headers. - url_params: dict (optional) Additional URL parameters to be included - in the URI. These are translated into query arguments - in the form '&dict_key=value&...'. - Example: {'max-results': '250'} becomes &max-results=250 - escape_params: boolean (optional) If false, the calling code has already - ensured that the query will form a valid URL (all - reserved characters have been escaped). If true, this - method will escape the query and any URL parameters - provided. - media_source: MediaSource (optional) Container for the media to be sent - along with the entry, if provided. - converter: func (optional) A function which will be executed on the - server's response. Often this is a function like - GDataEntryFromString which will parse the body of the server's - response and return a GDataEntry. - - Returns: - If the post succeeded, this method will return a GDataFeed, GDataEntry, - or the results of running converter on the server's result body (if - converter was specified). - """ - if extra_headers is None: - extra_headers = {} - - if self.__gsessionid is not None: - if uri.find('gsessionid=') < 0: - if url_params is None: - url_params = {} - url_params['gsessionid'] = self.__gsessionid - - if data and media_source: - if ElementTree.iselement(data): - data_str = ElementTree.tostring(data) - else: - data_str = str(data) - - multipart = [] - multipart.append('Media multipart posting\r\n--END_OF_PART\r\n' + \ - 'Content-Type: application/atom+xml\r\n\r\n') - multipart.append('\r\n--END_OF_PART\r\nContent-Type: ' + \ - media_source.content_type+'\r\n\r\n') - multipart.append('\r\n--END_OF_PART--\r\n') - - extra_headers['MIME-version'] = '1.0' - extra_headers['Content-Length'] = str(len(multipart[0]) + - len(multipart[1]) + len(multipart[2]) + - len(data_str) + media_source.content_length) - - extra_headers['Content-Type'] = 'multipart/related; boundary=END_OF_PART' - server_response = self.request(verb, uri, - data=[multipart[0], data_str, multipart[1], media_source.file_handle, - multipart[2]], headers=extra_headers, url_params=url_params) - result_body = server_response.read() - - elif media_source or isinstance(data, gdata.MediaSource): - if isinstance(data, gdata.MediaSource): - media_source = data - extra_headers['Content-Length'] = str(media_source.content_length) - extra_headers['Content-Type'] = media_source.content_type - server_response = self.request(verb, uri, - data=media_source.file_handle, headers=extra_headers, - url_params=url_params) - result_body = server_response.read() - - else: - http_data = data - if 'Content-Type' not in extra_headers: - content_type = 'application/atom+xml' - extra_headers['Content-Type'] = content_type - server_response = self.request(verb, uri, data=http_data, - headers=extra_headers, url_params=url_params) - result_body = server_response.read() - - # Server returns 201 for most post requests, but when performing a batch - # request the server responds with a 200 on success. - if server_response.status == 201 or server_response.status == 200: - if converter: - return converter(result_body) - feed = gdata.GDataFeedFromString(result_body) - if not feed: - entry = gdata.GDataEntryFromString(result_body) - if not entry: - return result_body - return entry - return feed - elif server_response.status == 302: - if redirects_remaining > 0: - location = (server_response.getheader('Location') - or server_response.getheader('location')) - if location is not None: - m = re.compile('[\?\&]gsessionid=(\w*\-)').search(location) - if m is not None: - self.__gsessionid = m.group(1) - return GDataService.PostOrPut(self, verb, data, location, - extra_headers, url_params, escape_params, - redirects_remaining - 1, media_source, converter=converter) - else: - raise RequestError, {'status': server_response.status, - 'reason': '302 received without Location header', - 'body': result_body} - else: - raise RequestError, {'status': server_response.status, - 'reason': 'Redirect received, but redirects_remaining <= 0', - 'body': result_body} - else: - raise RequestError, {'status': server_response.status, - 'reason': server_response.reason, 'body': result_body} - - def Put(self, data, uri, extra_headers=None, url_params=None, - escape_params=True, redirects_remaining=3, media_source=None, - converter=None): - """Updates an entry at the given URI. - - Args: - data: string, ElementTree._Element, or xml_wrapper.ElementWrapper The - XML containing the updated data. - uri: string A URI indicating entry to which the update will be applied. - Example: '/base/feeds/items/ITEM-ID' - extra_headers: dict (optional) HTTP headers which are to be included. - The client automatically sets the Content-Type, - Authorization, and Content-Length headers. - url_params: dict (optional) Additional URL parameters to be included - in the URI. These are translated into query arguments - in the form '&dict_key=value&...'. - Example: {'max-results': '250'} becomes &max-results=250 - escape_params: boolean (optional) If false, the calling code has already - ensured that the query will form a valid URL (all - reserved characters have been escaped). If true, this - method will escape the query and any URL parameters - provided. - converter: func (optional) A function which will be executed on the - server's response. Often this is a function like - GDataEntryFromString which will parse the body of the server's - response and return a GDataEntry. - - Returns: - If the put succeeded, this method will return a GDataFeed, GDataEntry, - or the results of running converter on the server's result body (if - converter was specified). - """ - return GDataService.PostOrPut(self, 'PUT', data, uri, - extra_headers=extra_headers, url_params=url_params, - escape_params=escape_params, redirects_remaining=redirects_remaining, - media_source=media_source, converter=converter) - - def Delete(self, uri, extra_headers=None, url_params=None, - escape_params=True, redirects_remaining=4): - """Deletes the entry at the given URI. - - Args: - uri: string The URI of the entry to be deleted. Example: - '/base/feeds/items/ITEM-ID' - extra_headers: dict (optional) HTTP headers which are to be included. - The client automatically sets the Content-Type and - Authorization headers. - url_params: dict (optional) Additional URL parameters to be included - in the URI. These are translated into query arguments - in the form '&dict_key=value&...'. - Example: {'max-results': '250'} becomes &max-results=250 - escape_params: boolean (optional) If false, the calling code has already - ensured that the query will form a valid URL (all - reserved characters have been escaped). If true, this - method will escape the query and any URL parameters - provided. - - Returns: - True if the entry was deleted. - """ - if extra_headers is None: - extra_headers = {} - - if self.__gsessionid is not None: - if uri.find('gsessionid=') < 0: - if url_params is None: - url_params = {} - url_params['gsessionid'] = self.__gsessionid - - server_response = self.request('DELETE', uri, - headers=extra_headers, url_params=url_params) - result_body = server_response.read() - - if server_response.status == 200: - return True - elif server_response.status == 302: - if redirects_remaining > 0: - location = (server_response.getheader('Location') - or server_response.getheader('location')) - if location is not None: - m = re.compile('[\?\&]gsessionid=(\w*\-)').search(location) - if m is not None: - self.__gsessionid = m.group(1) - return GDataService.Delete(self, location, extra_headers, - url_params, escape_params, redirects_remaining - 1) - else: - raise RequestError, {'status': server_response.status, - 'reason': '302 received without Location header', - 'body': result_body} - else: - raise RequestError, {'status': server_response.status, - 'reason': 'Redirect received, but redirects_remaining <= 0', - 'body': result_body} - else: - raise RequestError, {'status': server_response.status, - 'reason': server_response.reason, 'body': result_body} - - -def ExtractToken(url, scopes_included_in_next=True): - """Gets the AuthSub token from the current page's URL. - - Designed to be used on the URL that the browser is sent to after the user - authorizes this application at the page given by GenerateAuthSubRequestUrl. - - Args: - url: The current page's URL. It should contain the token as a URL - parameter. Example: 'http://example.com/?...&token=abcd435' - scopes_included_in_next: If True, this function looks for a scope value - associated with the token. The scope is a URL parameter with the - key set to SCOPE_URL_PARAM_NAME. This parameter should be present - if the AuthSub request URL was generated using - GenerateAuthSubRequestUrl with include_scope_in_next set to True. - - Returns: - A tuple containing the token string and a list of scope strings for which - this token should be valid. If the scope was not included in the URL, the - tuple will contain (token, None). - """ - parsed = urlparse.urlparse(url) - token = gdata.auth.AuthSubTokenFromUrl(parsed[4]) - scopes = '' - if scopes_included_in_next: - for pair in parsed[4].split('&'): - if pair.startswith('%s=' % SCOPE_URL_PARAM_NAME): - scopes = urllib.unquote_plus(pair.split('=')[1]) - return (token, scopes.split(' ')) - - -def GenerateAuthSubRequestUrl(next, scopes, hd='default', secure=False, - session=True, request_url='https://www.google.com/accounts/AuthSubRequest', - include_scopes_in_next=True): - """Creates a URL to request an AuthSub token to access Google services. - - For more details on AuthSub, see the documentation here: - http://code.google.com/apis/accounts/docs/AuthSub.html - - Args: - next: The URL where the browser should be sent after the user authorizes - the application. This page is responsible for receiving the token - which is embeded in the URL as a parameter. - scopes: The base URL to which access will be granted. Example: - 'http://www.google.com/calendar/feeds' will grant access to all - URLs in the Google Calendar data API. If you would like a token for - multiple scopes, pass in a list of URL strings. - hd: The domain to which the user's account belongs. This is set to the - domain name if you are using Google Apps. Example: 'example.org' - Defaults to 'default' - secure: If set to True, all requests should be signed. The default is - False. - session: If set to True, the token received by the 'next' URL can be - upgraded to a multiuse session token. If session is set to False, the - token may only be used once and cannot be upgraded. Default is True. - request_url: The base of the URL to which the user will be sent to - authorize this application to access their data. The default is - 'https://www.google.com/accounts/AuthSubRequest'. - include_scopes_in_next: Boolean if set to true, the 'next' parameter will - be modified to include the requested scope as a URL parameter. The - key for the next's scope parameter will be SCOPE_URL_PARAM_NAME. The - benefit of including the scope URL as a parameter to the next URL, is - that the page which receives the AuthSub token will be able to tell - which URLs the token grants access to. - - Returns: - A URL string to which the browser should be sent. - """ - if isinstance(scopes, list): - scope = ' '.join(scopes) - else: - scope = scopes - if include_scopes_in_next: - if next.find('?') > -1: - next += '&%s' % urllib.urlencode({SCOPE_URL_PARAM_NAME:scope}) - else: - next += '?%s' % urllib.urlencode({SCOPE_URL_PARAM_NAME:scope}) - return gdata.auth.GenerateAuthSubUrl(next=next, scope=scope, secure=secure, - session=session, request_url=request_url, domain=hd) - - -class Query(dict): - """Constructs a query URL to be used in GET requests - - Url parameters are created by adding key-value pairs to this object as a - dict. For example, to add &max-results=25 to the URL do - my_query['max-results'] = 25 - - Category queries are created by adding category strings to the categories - member. All items in the categories list will be concatenated with the / - symbol (symbolizing a category x AND y restriction). If you would like to OR - 2 categories, append them as one string with a | between the categories. - For example, do query.categories.append('Fritz|Laurie') to create a query - like this feed/-/Fritz%7CLaurie . This query will look for results in both - categories. - """ - - def __init__(self, feed=None, text_query=None, params=None, - categories=None): - """Constructor for Query - - Args: - feed: str (optional) The path for the feed (Examples: - '/base/feeds/snippets' or 'calendar/feeds/jo@gmail.com/private/full' - text_query: str (optional) The contents of the q query parameter. The - contents of the text_query are 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 (key-value pairs). - categories: list (optional) List of category strings which should be - included as query categories. See - http://code.google.com/apis/gdata/reference.html#Queries for - details. If you want to get results from category A or B (both - categories), specify a single list item 'A|B'. - """ - - self.feed = feed - self.categories = [] - if text_query: - self.text_query = text_query - if isinstance(params, dict): - for param in params: - self[param] = params[param] - if isinstance(categories, list): - for category in categories: - self.categories.append(category) - - def _GetTextQuery(self): - if 'q' in self.keys(): - return self['q'] - else: - return None - - def _SetTextQuery(self, query): - self['q'] = query - - text_query = property(_GetTextQuery, _SetTextQuery, - doc="""The feed query's q parameter""") - - def _GetAuthor(self): - if 'author' in self.keys(): - return self['author'] - else: - return None - - def _SetAuthor(self, query): - self['author'] = query - - author = property(_GetAuthor, _SetAuthor, - doc="""The feed query's author parameter""") - - def _GetAlt(self): - if 'alt' in self.keys(): - return self['alt'] - else: - return None - - def _SetAlt(self, query): - self['alt'] = query - - alt = property(_GetAlt, _SetAlt, - doc="""The feed query's alt parameter""") - - def _GetUpdatedMin(self): - if 'updated-min' in self.keys(): - return self['updated-min'] - else: - return None - - def _SetUpdatedMin(self, query): - self['updated-min'] = query - - updated_min = property(_GetUpdatedMin, _SetUpdatedMin, - doc="""The feed query's updated-min parameter""") - - def _GetUpdatedMax(self): - if 'updated-max' in self.keys(): - return self['updated-max'] - else: - return None - - def _SetUpdatedMax(self, query): - self['updated-max'] = query - - updated_max = property(_GetUpdatedMax, _SetUpdatedMax, - doc="""The feed query's updated-max parameter""") - - def _GetPublishedMin(self): - if 'published-min' in self.keys(): - return self['published-min'] - else: - return None - - def _SetPublishedMin(self, query): - self['published-min'] = query - - published_min = property(_GetPublishedMin, _SetPublishedMin, - doc="""The feed query's published-min parameter""") - - def _GetPublishedMax(self): - if 'published-max' in self.keys(): - return self['published-max'] - else: - return None - - def _SetPublishedMax(self, query): - self['published-max'] = query - - published_max = property(_GetPublishedMax, _SetPublishedMax, - doc="""The feed query's published-max parameter""") - - def _GetStartIndex(self): - if 'start-index' in self.keys(): - return self['start-index'] - else: - return None - - def _SetStartIndex(self, query): - if not isinstance(query, str): - query = str(query) - self['start-index'] = query - - start_index = property(_GetStartIndex, _SetStartIndex, - doc="""The feed query's start-index parameter""") - - def _GetMaxResults(self): - if 'max-results' in self.keys(): - return self['max-results'] - else: - return None - - def _SetMaxResults(self, query): - if not isinstance(query, str): - query = str(query) - self['max-results'] = query - - max_results = property(_GetMaxResults, _SetMaxResults, - doc="""The feed query's max-results parameter""") - - def _GetOrderBy(self): - if 'orderby' in self.keys(): - return self['orderby'] - else: - return None - - def _SetOrderBy(self, query): - self['orderby'] = query - - orderby = property(_GetOrderBy, _SetOrderBy, - doc="""The feed query's orderby parameter""") - - def ToUri(self): - q_feed = self.feed or '' - category_string = '/'.join( - [urllib.quote_plus(c) for c in self.categories]) - # Add categories to the feed if there are any. - if len(self.categories) > 0: - q_feed = q_feed + '/-/' + category_string - return atom.service.BuildUri(q_feed, self) - - def __str__(self): - return self.ToUri() diff --git a/gdata/analytics/sites/__init__.py b/gdata/analytics/sites/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/gdata/analytics/sites/client.py b/gdata/analytics/sites/client.py deleted file mode 100644 index 2915fc58c6..0000000000 --- a/gdata/analytics/sites/client.py +++ /dev/null @@ -1,462 +0,0 @@ -#!/usr/bin/python -# -# Copyright 2009 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. - -"""SitesClient extends gdata.client.GDClient to streamline Sites API calls.""" - - -__author__ = 'e.bidelman (Eric Bidelman)' - -import atom.data -import gdata.client -import gdata.sites.data -import gdata.gauth - - -# Feed URI templates -CONTENT_FEED_TEMPLATE = '/feeds/content/%s/%s/' -REVISION_FEED_TEMPLATE = '/feeds/revision/%s/%s/' -ACTIVITY_FEED_TEMPLATE = '/feeds/activity/%s/%s/' -SITE_FEED_TEMPLATE = '/feeds/site/%s/' -ACL_FEED_TEMPLATE = '/feeds/acl/site/%s/%s/' - - -class SitesClient(gdata.client.GDClient): - - """Client extension for the Google Sites API service.""" - - host = 'sites.google.com' # default server for the API - domain = 'site' # default site domain name - api_version = '1.1' # default major version for the service. - auth_service = 'jotspot' - auth_scopes = gdata.gauth.AUTH_SCOPES['jotspot'] - ssl = True - - def __init__(self, site=None, domain=None, auth_token=None, **kwargs): - """Constructs a new client for the Sites API. - - Args: - site: string (optional) Name (webspace) of the Google Site - domain: string (optional) Domain of the (Google Apps hosted) Site. - If no domain is given, the Site is assumed to be a consumer Google - Site, in which case the value 'site' is used. - auth_token: (optional) gdata.gauth.ClientLoginToken, AuthSubToken, or - OAuthToken which 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) - self.site = site - if domain is not None: - self.domain = domain - - def __make_kind_category(self, label): - if label is None: - return None - return atom.data.Category( - scheme=gdata.sites.data.SITES_KIND_SCHEME, - term='%s#%s' % (gdata.sites.data.SITES_NAMESPACE, label), label=label) - - __MakeKindCategory = __make_kind_category - - def __upload(self, entry, media_source, auth_token=None, **kwargs): - """Uploads an attachment file to the Sites API. - - Args: - entry: gdata.sites.data.ContentEntry The Atom XML to include. - media_source: gdata.data.MediaSource The file payload to be uploaded. - auth_token: (optional) gdata.gauth.ClientLoginToken, AuthSubToken, or - OAuthToken which authorizes this client to edit the user's data. - kwargs: Other parameters to pass to gdata.client.post(). - - Returns: - The created entry. - """ - uri = self.make_content_feed_uri() - return self.post(entry, uri, media_source=media_source, - auth_token=auth_token, **kwargs) - - def _get_file_content(self, uri): - """Fetches the file content from the specified URI. - - Args: - uri: string The full URL to fetch the file contents from. - - Returns: - The binary file content. - - Raises: - gdata.client.RequestError: on error response from server. - """ - server_response = self.request('GET', uri) - if server_response.status != 200: - raise gdata.client.RequestError, {'status': server_response.status, - 'reason': server_response.reason, - 'body': server_response.read()} - return server_response.read() - - _GetFileContent = _get_file_content - - def make_content_feed_uri(self): - return CONTENT_FEED_TEMPLATE % (self.domain, self.site) - - MakeContentFeedUri = make_content_feed_uri - - def make_revision_feed_uri(self): - return REVISION_FEED_TEMPLATE % (self.domain, self.site) - - MakeRevisionFeedUri = make_revision_feed_uri - - def make_activity_feed_uri(self): - return ACTIVITY_FEED_TEMPLATE % (self.domain, self.site) - - MakeActivityFeedUri = make_activity_feed_uri - - def make_site_feed_uri(self, site_name=None): - if site_name is not None: - return (SITE_FEED_TEMPLATE % self.domain) + site_name - else: - return SITE_FEED_TEMPLATE % self.domain - - MakeSiteFeedUri = make_site_feed_uri - - def make_acl_feed_uri(self): - return ACL_FEED_TEMPLATE % (self.domain, self.site) - - MakeAclFeedUri = make_acl_feed_uri - - def get_content_feed(self, uri=None, auth_token=None, **kwargs): - """Retrieves the content feed containing the current state of site. - - Args: - uri: string (optional) A full URI to query the Content feed with. - auth_token: (optional) gdata.gauth.ClientLoginToken, AuthSubToken, or - OAuthToken which authorizes this client to edit the user's data. - kwargs: Other parameters to pass to self.get_feed(). - - Returns: - gdata.sites.data.ContentFeed - """ - if uri is None: - uri = self.make_content_feed_uri() - return self.get_feed(uri, desired_class=gdata.sites.data.ContentFeed, - auth_token=auth_token, **kwargs) - - GetContentFeed = get_content_feed - - def get_revision_feed(self, entry_or_uri_or_id, auth_token=None, **kwargs): - """Retrieves the revision feed containing the revision history for a node. - - Args: - entry_or_uri_or_id: string or gdata.sites.data.ContentEntry A full URI, - content entry node ID, or a content entry object of the entry to - retrieve revision information for. - auth_token: (optional) gdata.gauth.ClientLoginToken, AuthSubToken, or - OAuthToken which authorizes this client to edit the user's data. - kwargs: Other parameters to pass to self.get_feed(). - - Returns: - gdata.sites.data.RevisionFeed - """ - uri = self.make_revision_feed_uri() - if isinstance(entry_or_uri_or_id, gdata.sites.data.ContentEntry): - uri = entry_or_uri_or_id.FindRevisionLink() - elif entry_or_uri_or_id.find('/') == -1: - uri += entry_or_uri_or_id - else: - uri = entry_or_uri_or_id - return self.get_feed(uri, desired_class=gdata.sites.data.RevisionFeed, - auth_token=auth_token, **kwargs) - - GetRevisionFeed = get_revision_feed - - def get_activity_feed(self, uri=None, auth_token=None, **kwargs): - """Retrieves the activity feed containing recent Site activity. - - Args: - uri: string (optional) A full URI to query the Activity feed. - auth_token: (optional) gdata.gauth.ClientLoginToken, AuthSubToken, or - OAuthToken which authorizes this client to edit the user's data. - kwargs: Other parameters to pass to self.get_feed(). - - Returns: - gdata.sites.data.ActivityFeed - """ - if uri is None: - uri = self.make_activity_feed_uri() - return self.get_feed(uri, desired_class=gdata.sites.data.ActivityFeed, - auth_token=auth_token, **kwargs) - - GetActivityFeed = get_activity_feed - - def get_site_feed(self, uri=None, auth_token=None, **kwargs): - """Retrieves the site feed containing a list of sites a user has access to. - - Args: - uri: string (optional) A full URI to query the site feed. - auth_token: (optional) gdata.gauth.ClientLoginToken, AuthSubToken, or - OAuthToken which authorizes this client to edit the user's data. - kwargs: Other parameters to pass to self.get_feed(). - - Returns: - gdata.sites.data.SiteFeed - """ - if uri is None: - uri = self.make_site_feed_uri() - return self.get_feed(uri, desired_class=gdata.sites.data.SiteFeed, - auth_token=auth_token, **kwargs) - - GetSiteFeed = get_site_feed - - def get_acl_feed(self, uri=None, auth_token=None, **kwargs): - """Retrieves the acl feed containing a site's sharing permissions. - - Args: - uri: string (optional) A full URI to query the acl feed. - auth_token: (optional) gdata.gauth.ClientLoginToken, AuthSubToken, or - OAuthToken which authorizes this client to edit the user's data. - kwargs: Other parameters to pass to self.get_feed(). - - Returns: - gdata.sites.data.AclFeed - """ - if uri is None: - uri = self.make_acl_feed_uri() - return self.get_feed(uri, desired_class=gdata.sites.data.AclFeed, - auth_token=auth_token, **kwargs) - - GetAclFeed = get_acl_feed - - def create_site(self, title, description=None, source_site=None, - theme=None, uri=None, auth_token=None, **kwargs): - """Creates a new Google Site. - - Note: This feature is only available to Google Apps domains. - - Args: - title: string Title for the site. - description: string (optional) A description/summary for the site. - source_site: string (optional) The site feed URI of the site to copy. - This parameter should only be specified when copying a site. - theme: string (optional) The name of the theme to create the site with. - uri: string (optional) A full site feed URI to override where the site - is created/copied. By default, the site will be created under - the currently set domain (e.g. self.domain). - auth_token: (optional) gdata.gauth.ClientLoginToken, AuthSubToken, or - OAuthToken which authorizes this client to edit the user's data. - kwargs: Other parameters to pass to gdata.client.post(). - - Returns: - gdata.sites.data.SiteEntry of the created site. - """ - new_entry = gdata.sites.data.SiteEntry(title=atom.data.Title(text=title)) - - if description is not None: - new_entry.summary = gdata.sites.data.Summary(text=description) - - # Add the source link if we're making a copy of a site. - if source_site is not None: - source_link = atom.data.Link(rel=gdata.sites.data.SITES_SOURCE_LINK_REL, - type='application/atom+xml', - href=source_site) - new_entry.link.append(source_link) - - if theme is not None: - new_entry.theme = gdata.sites.data.Theme(text=theme) - - if uri is None: - uri = self.make_site_feed_uri() - - return self.post(new_entry, uri, auth_token=auth_token, **kwargs) - - CreateSite = create_site - - def create_page(self, kind, title, html='', page_name=None, parent=None, - auth_token=None, **kwargs): - """Creates a new page (specified by kind) on a Google Site. - - Args: - kind: string The type of page/item to create. For example, webpage, - listpage, comment, announcementspage, filecabinet, etc. The full list - of supported kinds can be found in gdata.sites.gdata.SUPPORT_KINDS. - title: string Title for the page. - html: string (optional) XHTML for the page's content body. - page_name: string (optional) The URL page name to set. If not set, the - title will be normalized and used as the page's URL path. - parent: string or gdata.sites.data.ContentEntry (optional) The parent - entry or parent link url to create the page under. - auth_token: (optional) gdata.gauth.ClientLoginToken, AuthSubToken, or - OAuthToken which authorizes this client to edit the user's data. - kwargs: Other parameters to pass to gdata.client.post(). - - Returns: - gdata.sites.data.ContentEntry of the created page. - """ - new_entry = gdata.sites.data.ContentEntry( - title=atom.data.Title(text=title), kind=kind, - content=gdata.sites.data.Content(text=html)) - - if page_name is not None: - new_entry.page_name = gdata.sites.data.PageName(text=page_name) - - # Add parent link to entry if it should be uploaded as a subpage. - if isinstance(parent, gdata.sites.data.ContentEntry): - parent_link = atom.data.Link(rel=gdata.sites.data.SITES_PARENT_LINK_REL, - type='application/atom+xml', - href=parent.GetSelfLink().href) - new_entry.link.append(parent_link) - elif parent is not None: - parent_link = atom.data.Link(rel=gdata.sites.data.SITES_PARENT_LINK_REL, - type='application/atom+xml', - href=parent) - new_entry.link.append(parent_link) - - return self.post(new_entry, self.make_content_feed_uri(), - auth_token=auth_token, **kwargs) - - CreatePage = create_page - - def create_webattachment(self, src, content_type, title, parent, - description=None, auth_token=None, **kwargs): - """Creates a new webattachment within a filecabinet. - - Args: - src: string The url of the web attachment. - content_type: string The MIME type of the web attachment. - title: string The title to name the web attachment. - parent: string or gdata.sites.data.ContentEntry (optional) The - parent entry or url of the filecabinet to create the attachment under. - description: string (optional) A summary/description for the attachment. - auth_token: (optional) gdata.gauth.ClientLoginToken, AuthSubToken, or - OAuthToken which authorizes this client to edit the user's data. - kwargs: Other parameters to pass to gdata.client.post(). - - Returns: - gdata.sites.data.ContentEntry of the created page. - """ - new_entry = gdata.sites.data.ContentEntry( - title=atom.data.Title(text=title), kind='webattachment', - content=gdata.sites.data.Content(src=src, type=content_type)) - - if isinstance(parent, gdata.sites.data.ContentEntry): - link = atom.data.Link(rel=gdata.sites.data.SITES_PARENT_LINK_REL, - type='application/atom+xml', - href=parent.GetSelfLink().href) - elif parent is not None: - link = atom.data.Link(rel=gdata.sites.data.SITES_PARENT_LINK_REL, - type='application/atom+xml', href=parent) - - new_entry.link.append(link) - - # Add file decription if it was specified - if description is not None: - new_entry.summary = gdata.sites.data.Summary(type='text', - text=description) - - return self.post(new_entry, self.make_content_feed_uri(), - auth_token=auth_token, **kwargs) - - CreateWebAttachment = create_webattachment - - def upload_attachment(self, file_handle, parent, content_type=None, - title=None, description=None, folder_name=None, - auth_token=None, **kwargs): - """Uploads an attachment to a parent page. - - Args: - file_handle: MediaSource or string A gdata.data.MediaSource object - containing the file to be uploaded or the full path name to the - file on disk. - parent: gdata.sites.data.ContentEntry or string The parent page to - upload the file to or the full URI of the entry's self link. - content_type: string (optional) The MIME type of the file - (e.g 'application/pdf'). This should be provided if file is not a - MediaSource object. - title: string (optional) The title to name the attachment. If not - included, the filepath or media source's filename is used. - description: string (optional) A summary/description for the attachment. - folder_name: string (optional) The name of an existing folder to upload - the attachment to. This only applies when the parent parameter points - to a filecabinet entry. - auth_token: (optional) gdata.gauth.ClientLoginToken, AuthSubToken, or - OAuthToken which authorizes this client to edit the user's data. - kwargs: Other parameters to pass to self.__upload(). - - Returns: - A gdata.sites.data.ContentEntry containing information about the created - attachment. - """ - if isinstance(parent, gdata.sites.data.ContentEntry): - link = atom.data.Link(rel=gdata.sites.data.SITES_PARENT_LINK_REL, - type='application/atom+xml', - href=parent.GetSelfLink().href) - else: - link = atom.data.Link(rel=gdata.sites.data.SITES_PARENT_LINK_REL, - type='application/atom+xml', - href=parent) - - if not isinstance(file_handle, gdata.data.MediaSource): - ms = gdata.data.MediaSource(file_path=file_handle, - content_type=content_type) - else: - ms = file_handle - - # If no title specified, use the file name - if title is None: - title = ms.file_name - - new_entry = gdata.sites.data.ContentEntry(kind='attachment') - new_entry.title = atom.data.Title(text=title) - new_entry.link.append(link) - - # Add file decription if it was specified - if description is not None: - new_entry.summary = gdata.sites.data.Summary(type='text', - text=description) - - # Upload the attachment to a filecabinet folder? - if parent.Kind() == 'filecabinet' and folder_name is not None: - folder_category = atom.data.Category( - scheme=gdata.sites.data.FOLDER_KIND_TERM, term=folder_name) - new_entry.category.append(folder_category) - - return self.__upload(new_entry, ms, auth_token=auth_token, **kwargs) - - UploadAttachment = upload_attachment - - def download_attachment(self, uri_or_entry, file_path): - """Downloads an attachment file to disk. - - Args: - uri_or_entry: string The full URL to download the file from. - file_path: string The full path to save the file to. - - Raises: - gdata.client.RequestError: on error response from server. - """ - uri = uri_or_entry - if isinstance(uri_or_entry, gdata.sites.data.ContentEntry): - uri = uri_or_entry.content.src - - f = open(file_path, 'wb') - try: - f.write(self._get_file_content(uri)) - except gdata.client.RequestError, e: - f.close() - raise e - f.flush() - f.close() - - DownloadAttachment = download_attachment diff --git a/gdata/analytics/sites/data.py b/gdata/analytics/sites/data.py deleted file mode 100644 index ff09ac7b95..0000000000 --- a/gdata/analytics/sites/data.py +++ /dev/null @@ -1,377 +0,0 @@ -#!/usr/bin/python -# -# Copyright 2009 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 the Sites Data API.""" - -__author__ = 'e.bidelman (Eric Bidelman)' - - -import atom.core -import atom.data -import gdata.acl.data -import gdata.data - -# XML Namespaces used in Google Sites entities. -SITES_NAMESPACE = 'http://schemas.google.com/sites/2008' -SITES_TEMPLATE = '{http://schemas.google.com/sites/2008}%s' -SPREADSHEETS_NAMESPACE = 'http://schemas.google.com/spreadsheets/2006' -SPREADSHEETS_TEMPLATE = '{http://schemas.google.com/spreadsheets/2006}%s' -DC_TERMS_TEMPLATE = '{http://purl.org/dc/terms}%s' -THR_TERMS_TEMPLATE = '{http://purl.org/syndication/thread/1.0}%s' -XHTML_NAMESPACE = 'http://www.w3.org/1999/xhtml' -XHTML_TEMPLATE = '{http://www.w3.org/1999/xhtml}%s' - -SITES_PARENT_LINK_REL = SITES_NAMESPACE + '#parent' -SITES_REVISION_LINK_REL = SITES_NAMESPACE + '#revision' -SITES_SOURCE_LINK_REL = SITES_NAMESPACE + '#source' - -SITES_KIND_SCHEME = 'http://schemas.google.com/g/2005#kind' -ANNOUNCEMENT_KIND_TERM = SITES_NAMESPACE + '#announcement' -ANNOUNCEMENT_PAGE_KIND_TERM = SITES_NAMESPACE + '#announcementspage' -ATTACHMENT_KIND_TERM = SITES_NAMESPACE + '#attachment' -COMMENT_KIND_TERM = SITES_NAMESPACE + '#comment' -FILECABINET_KIND_TERM = SITES_NAMESPACE + '#filecabinet' -LISTITEM_KIND_TERM = SITES_NAMESPACE + '#listitem' -LISTPAGE_KIND_TERM = SITES_NAMESPACE + '#listpage' -WEBPAGE_KIND_TERM = SITES_NAMESPACE + '#webpage' -WEBATTACHMENT_KIND_TERM = SITES_NAMESPACE + '#webattachment' -FOLDER_KIND_TERM = SITES_NAMESPACE + '#folder' -TAG_KIND_TERM = SITES_NAMESPACE + '#tag' - -SUPPORT_KINDS = [ - 'announcement', 'announcementspage', 'attachment', 'comment', 'filecabinet', - 'listitem', 'listpage', 'webpage', 'webattachment', 'tag' - ] - - -class Revision(atom.core.XmlElement): - """Google Sites <sites:revision>.""" - _qname = SITES_TEMPLATE % 'revision' - - -class PageName(atom.core.XmlElement): - """Google Sites <sites:pageName>.""" - _qname = SITES_TEMPLATE % 'pageName' - - -class SiteName(atom.core.XmlElement): - """Google Sites <sites:siteName>.""" - _qname = SITES_TEMPLATE % 'siteName' - - -class Theme(atom.core.XmlElement): - """Google Sites <sites:theme>.""" - _qname = SITES_TEMPLATE % 'theme' - - -class Deleted(atom.core.XmlElement): - """Google Sites <gd:deleted>.""" - _qname = gdata.data.GDATA_TEMPLATE % 'deleted' - - -class Publisher(atom.core.XmlElement): - """Google Sites <dc:pulisher>.""" - _qname = DC_TERMS_TEMPLATE % 'publisher' - - -class Worksheet(atom.core.XmlElement): - """Google Sites List Page <gs:worksheet>.""" - - _qname = SPREADSHEETS_TEMPLATE % 'worksheet' - name = 'name' - - -class Header(atom.core.XmlElement): - """Google Sites List Page <gs:header>.""" - - _qname = SPREADSHEETS_TEMPLATE % 'header' - row = 'row' - - -class Column(atom.core.XmlElement): - """Google Sites List Page <gs:column>.""" - - _qname = SPREADSHEETS_TEMPLATE % 'column' - index = 'index' - name = 'name' - - -class Data(atom.core.XmlElement): - """Google Sites List Page <gs:data>.""" - - _qname = SPREADSHEETS_TEMPLATE % 'data' - startRow = 'startRow' - column = [Column] - - -class Field(atom.core.XmlElement): - """Google Sites List Item <gs:field>.""" - - _qname = SPREADSHEETS_TEMPLATE % 'field' - index = 'index' - name = 'name' - - -class InReplyTo(atom.core.XmlElement): - """Google Sites List Item <thr:in-reply-to>.""" - - _qname = THR_TERMS_TEMPLATE % 'in-reply-to' - href = 'href' - ref = 'ref' - source = 'source' - type = 'type' - - -class Content(atom.data.Content): - """Google Sites version of <atom:content> that encapsulates XHTML.""" - - def __init__(self, html=None, type=None, **kwargs): - if type is None and html: - type = 'xhtml' - super(Content, self).__init__(type=type, **kwargs) - if html is not None: - self.html = html - - def _get_html(self): - if self.children: - return self.children[0] - else: - return '' - - def _set_html(self, html): - if not html: - self.children = [] - return - - if type(html) == str: - html = atom.core.parse(html) - if not html.namespace: - html.namespace = XHTML_NAMESPACE - - self.children = [html] - - html = property(_get_html, _set_html) - - -class Summary(atom.data.Summary): - """Google Sites version of <atom:summary>.""" - - def __init__(self, html=None, type=None, text=None, **kwargs): - if type is None and html: - type = 'xhtml' - - super(Summary, self).__init__(type=type, text=text, **kwargs) - if html is not None: - self.html = html - - def _get_html(self): - if self.children: - return self.children[0] - else: - return '' - - def _set_html(self, html): - if not html: - self.children = [] - return - - if type(html) == str: - html = atom.core.parse(html) - if not html.namespace: - html.namespace = XHTML_NAMESPACE - - self.children = [html] - - html = property(_get_html, _set_html) - - -class BaseSiteEntry(gdata.data.GDEntry): - """Google Sites Entry.""" - - def __init__(self, kind=None, **kwargs): - super(BaseSiteEntry, self).__init__(**kwargs) - if kind is not None: - self.category.append( - atom.data.Category(scheme=SITES_KIND_SCHEME, - term='%s#%s' % (SITES_NAMESPACE, kind), - label=kind)) - - def __find_category_scheme(self, scheme): - for category in self.category: - if category.scheme == scheme: - return category - return None - - def kind(self): - kind = self.__find_category_scheme(SITES_KIND_SCHEME) - if kind is not None: - return kind.term[len(SITES_NAMESPACE) + 1:] - else: - return None - - Kind = kind - - def get_node_id(self): - return self.id.text[self.id.text.rfind('/') + 1:] - - GetNodeId = get_node_id - - def find_parent_link(self): - return self.find_url(SITES_PARENT_LINK_REL) - - FindParentLink = find_parent_link - - def is_deleted(self): - return self.deleted is not None - - IsDeleted = is_deleted - - -class ContentEntry(BaseSiteEntry): - """Google Sites Content Entry.""" - content = Content - deleted = Deleted - publisher = Publisher - in_reply_to = InReplyTo - worksheet = Worksheet - header = Header - data = Data - field = [Field] - revision = Revision - page_name = PageName - feed_link = gdata.data.FeedLink - - def find_revison_link(self): - return self.find_url(SITES_REVISION_LINK_REL) - - FindRevisionLink = find_revison_link - - -class ContentFeed(gdata.data.GDFeed): - """Google Sites Content Feed. - - The Content feed is a feed containing the current, editable site content. - """ - entry = [ContentEntry] - - def __get_entry_type(self, kind): - matches = [] - for entry in self.entry: - if entry.Kind() == kind: - matches.append(entry) - return matches - - def get_announcements(self): - return self.__get_entry_type('announcement') - - GetAnnouncements = get_announcements - - def get_announcement_pages(self): - return self.__get_entry_type('announcementspage') - - GetAnnouncementPages = get_announcement_pages - - def get_attachments(self): - return self.__get_entry_type('attachment') - - GetAttachments = get_attachments - - def get_comments(self): - return self.__get_entry_type('comment') - - GetComments = get_comments - - def get_file_cabinets(self): - return self.__get_entry_type('filecabinet') - - GetFileCabinets = get_file_cabinets - - def get_list_items(self): - return self.__get_entry_type('listitem') - - GetListItems = get_list_items - - def get_list_pages(self): - return self.__get_entry_type('listpage') - - GetListPages = get_list_pages - - def get_webpages(self): - return self.__get_entry_type('webpage') - - GetWebpages = get_webpages - - def get_webattachments(self): - return self.__get_entry_type('webattachment') - - GetWebattachments = get_webattachments - - -class ActivityEntry(BaseSiteEntry): - """Google Sites Activity Entry.""" - summary = Summary - - -class ActivityFeed(gdata.data.GDFeed): - """Google Sites Activity Feed. - - The Activity feed is a feed containing recent Site activity. - """ - entry = [ActivityEntry] - - -class RevisionEntry(BaseSiteEntry): - """Google Sites Revision Entry.""" - content = Content - - -class RevisionFeed(gdata.data.GDFeed): - """Google Sites Revision Feed. - - The Activity feed is a feed containing recent Site activity. - """ - entry = [RevisionEntry] - - -class SiteEntry(gdata.data.GDEntry): - """Google Sites Site Feed Entry.""" - site_name = SiteName - theme = Theme - - def find_source_link(self): - return self.find_url(SITES_SOURCE_LINK_REL) - - FindSourceLink = find_source_link - - -class SiteFeed(gdata.data.GDFeed): - """Google Sites Site Feed. - - The Site feed can be used to list a user's sites and create new sites. - """ - entry = [SiteEntry] - - -class AclEntry(gdata.acl.data.AclEntry): - """Google Sites ACL Entry.""" - - -class AclFeed(gdata.acl.data.AclFeed): - """Google Sites ACL Feed. - - The ACL feed can be used to modify the sharing permissions of a Site. - """ - entry = [AclEntry] diff --git a/gdata/analytics/spreadsheet/__init__.py b/gdata/analytics/spreadsheet/__init__.py deleted file mode 100644 index e9a0fb3dc7..0000000000 --- a/gdata/analytics/spreadsheet/__init__.py +++ /dev/null @@ -1,474 +0,0 @@ -#!/usr/bin/python -# -# Copyright (C) 2007 Google 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. - -"""Contains extensions to Atom objects used with Google Spreadsheets. -""" - -__author__ = 'api.laurabeth@gmail.com (Laura Beth Lincoln)' - - -try: - from xml.etree import cElementTree as ElementTree -except ImportError: - try: - import cElementTree as ElementTree - except ImportError: - try: - from xml.etree import ElementTree - except ImportError: - from elementtree import ElementTree -import atom -import gdata -import re -import string - - -# XML namespaces which are often used in Google Spreadsheets entities. -GSPREADSHEETS_NAMESPACE = 'http://schemas.google.com/spreadsheets/2006' -GSPREADSHEETS_TEMPLATE = '{http://schemas.google.com/spreadsheets/2006}%s' - -GSPREADSHEETS_EXTENDED_NAMESPACE = ('http://schemas.google.com/spreadsheets' - '/2006/extended') -GSPREADSHEETS_EXTENDED_TEMPLATE = ('{http://schemas.google.com/spreadsheets' - '/2006/extended}%s') - - -class ColCount(atom.AtomBase): - """The Google Spreadsheets colCount element """ - - _tag = 'colCount' - _namespace = GSPREADSHEETS_NAMESPACE - _children = atom.AtomBase._children.copy() - _attributes = atom.AtomBase._attributes.copy() - - def __init__(self, text=None, extension_elements=None, - extension_attributes=None): - self.text = text - self.extension_elements = extension_elements or [] - self.extension_attributes = extension_attributes or {} - - -def ColCountFromString(xml_string): - return atom.CreateClassFromXMLString(ColCount, xml_string) - - -class RowCount(atom.AtomBase): - """The Google Spreadsheets rowCount element """ - - _tag = 'rowCount' - _namespace = GSPREADSHEETS_NAMESPACE - _children = atom.AtomBase._children.copy() - _attributes = atom.AtomBase._attributes.copy() - - def __init__(self, text=None, extension_elements=None, - extension_attributes=None): - self.text = text - self.extension_elements = extension_elements or [] - self.extension_attributes = extension_attributes or {} - -def RowCountFromString(xml_string): - return atom.CreateClassFromXMLString(RowCount, xml_string) - - -class Cell(atom.AtomBase): - """The Google Spreadsheets cell element """ - - _tag = 'cell' - _namespace = GSPREADSHEETS_NAMESPACE - _children = atom.AtomBase._children.copy() - _attributes = atom.AtomBase._attributes.copy() - _attributes['row'] = 'row' - _attributes['col'] = 'col' - _attributes['inputValue'] = 'inputValue' - _attributes['numericValue'] = 'numericValue' - - def __init__(self, text=None, row=None, col=None, inputValue=None, - numericValue=None, extension_elements=None, extension_attributes=None): - self.text = text - self.row = row - self.col = col - self.inputValue = inputValue - self.numericValue = numericValue - self.extension_elements = extension_elements or [] - self.extension_attributes = extension_attributes or {} - - -def CellFromString(xml_string): - return atom.CreateClassFromXMLString(Cell, xml_string) - - -class Custom(atom.AtomBase): - """The Google Spreadsheets custom element""" - - _namespace = GSPREADSHEETS_EXTENDED_NAMESPACE - _children = atom.AtomBase._children.copy() - _attributes = atom.AtomBase._attributes.copy() - - def __init__(self, column=None, text=None, extension_elements=None, - extension_attributes=None): - self.column = column # The name of the column - self.text = text - self.extension_elements = extension_elements or [] - self.extension_attributes = extension_attributes or {} - - def _BecomeChildElement(self, tree): - new_child = ElementTree.Element('') - tree.append(new_child) - new_child.tag = '{%s}%s' % (self.__class__._namespace, - self.column) - self._AddMembersToElementTree(new_child) - - def _ToElementTree(self): - new_tree = ElementTree.Element('{%s}%s' % (self.__class__._namespace, - self.column)) - self._AddMembersToElementTree(new_tree) - return new_tree - - def _HarvestElementTree(self, tree): - namespace_uri, local_tag = string.split(tree.tag[1:], "}", 1) - self.column = local_tag - # Fill in the instance members from the contents of the XML tree. - for child in tree: - self._ConvertElementTreeToMember(child) - for attribute, value in tree.attrib.iteritems(): - self._ConvertElementAttributeToMember(attribute, value) - self.text = tree.text - - -def CustomFromString(xml_string): - element_tree = ElementTree.fromstring(xml_string) - return _CustomFromElementTree(element_tree) - - -def _CustomFromElementTree(element_tree): - namespace_uri, local_tag = string.split(element_tree.tag[1:], "}", 1) - if namespace_uri == GSPREADSHEETS_EXTENDED_NAMESPACE: - new_custom = Custom() - new_custom._HarvestElementTree(element_tree) - new_custom.column = local_tag - return new_custom - return None - - - - - -class SpreadsheetsSpreadsheet(gdata.GDataEntry): - """A Google Spreadsheets flavor of a Spreadsheet Atom Entry """ - - _tag = 'entry' - _namespace = atom.ATOM_NAMESPACE - _children = gdata.GDataEntry._children.copy() - _attributes = gdata.GDataEntry._attributes.copy() - - def __init__(self, author=None, category=None, content=None, - contributor=None, atom_id=None, link=None, published=None, rights=None, - source=None, summary=None, title=None, control=None, updated=None, - text=None, extension_elements=None, extension_attributes=None): - self.author = author or [] - self.category = category or [] - self.content = content - self.contributor = contributor or [] - self.id = atom_id - self.link = link or [] - self.published = published - self.rights = rights - self.source = source - self.summary = summary - self.control = control - self.title = title - self.updated = updated - self.text = text - self.extension_elements = extension_elements or [] - self.extension_attributes = extension_attributes or {} - - -def SpreadsheetsSpreadsheetFromString(xml_string): - return atom.CreateClassFromXMLString(SpreadsheetsSpreadsheet, - xml_string) - - -class SpreadsheetsWorksheet(gdata.GDataEntry): - """A Google Spreadsheets flavor of a Worksheet Atom Entry """ - - _tag = 'entry' - _namespace = atom.ATOM_NAMESPACE - _children = gdata.GDataEntry._children.copy() - _attributes = gdata.GDataEntry._attributes.copy() - _children['{%s}rowCount' % GSPREADSHEETS_NAMESPACE] = ('row_count', - RowCount) - _children['{%s}colCount' % GSPREADSHEETS_NAMESPACE] = ('col_count', - ColCount) - - def __init__(self, author=None, category=None, content=None, - contributor=None, atom_id=None, link=None, published=None, rights=None, - source=None, summary=None, title=None, control=None, updated=None, - row_count=None, col_count=None, text=None, extension_elements=None, - extension_attributes=None): - self.author = author or [] - self.category = category or [] - self.content = content - self.contributor = contributor or [] - self.id = atom_id - self.link = link or [] - self.published = published - self.rights = rights - self.source = source - self.summary = summary - self.control = control - self.title = title - self.updated = updated - self.row_count = row_count - self.col_count = col_count - self.text = text - self.extension_elements = extension_elements or [] - self.extension_attributes = extension_attributes or {} - - -def SpreadsheetsWorksheetFromString(xml_string): - return atom.CreateClassFromXMLString(SpreadsheetsWorksheet, - xml_string) - - -class SpreadsheetsCell(gdata.BatchEntry): - """A Google Spreadsheets flavor of a Cell Atom Entry """ - - _tag = 'entry' - _namespace = atom.ATOM_NAMESPACE - _children = gdata.BatchEntry._children.copy() - _attributes = gdata.BatchEntry._attributes.copy() - _children['{%s}cell' % GSPREADSHEETS_NAMESPACE] = ('cell', Cell) - - def __init__(self, author=None, category=None, content=None, - contributor=None, atom_id=None, link=None, published=None, rights=None, - source=None, summary=None, title=None, control=None, updated=None, - cell=None, batch_operation=None, batch_id=None, batch_status=None, - text=None, extension_elements=None, extension_attributes=None): - self.author = author or [] - self.category = category or [] - self.content = content - self.contributor = contributor or [] - self.id = atom_id - self.link = link or [] - self.published = published - self.rights = rights - self.source = source - self.summary = summary - self.control = control - self.title = title - self.batch_operation = batch_operation - self.batch_id = batch_id - self.batch_status = batch_status - self.updated = updated - self.cell = cell - self.text = text - self.extension_elements = extension_elements or [] - self.extension_attributes = extension_attributes or {} - - -def SpreadsheetsCellFromString(xml_string): - return atom.CreateClassFromXMLString(SpreadsheetsCell, - xml_string) - - -class SpreadsheetsList(gdata.GDataEntry): - """A Google Spreadsheets flavor of a List Atom Entry """ - - _tag = 'entry' - _namespace = atom.ATOM_NAMESPACE - _children = gdata.GDataEntry._children.copy() - _attributes = gdata.GDataEntry._attributes.copy() - - def __init__(self, author=None, category=None, content=None, - contributor=None, atom_id=None, link=None, published=None, rights=None, - source=None, summary=None, title=None, control=None, updated=None, - custom=None, - text=None, extension_elements=None, extension_attributes=None): - self.author = author or [] - self.category = category or [] - self.content = content - self.contributor = contributor or [] - self.id = atom_id - self.link = link or [] - self.published = published - self.rights = rights - self.source = source - self.summary = summary - self.control = control - self.title = title - self.updated = updated - self.custom = custom or {} - self.text = text - self.extension_elements = extension_elements or [] - self.extension_attributes = extension_attributes or {} - - # We need to overwrite _ConvertElementTreeToMember to add special logic to - # convert custom attributes to members - def _ConvertElementTreeToMember(self, child_tree): - # Find the element's tag in this class's list of child members - if self.__class__._children.has_key(child_tree.tag): - member_name = self.__class__._children[child_tree.tag][0] - member_class = self.__class__._children[child_tree.tag][1] - # If the class member is supposed to contain a list, make sure the - # matching member is set to a list, then append the new member - # instance to the list. - if isinstance(member_class, list): - if getattr(self, member_name) is None: - setattr(self, member_name, []) - getattr(self, member_name).append(atom._CreateClassFromElementTree( - member_class[0], child_tree)) - else: - setattr(self, member_name, - atom._CreateClassFromElementTree(member_class, child_tree)) - elif child_tree.tag.find('{%s}' % GSPREADSHEETS_EXTENDED_NAMESPACE) == 0: - # If this is in the custom namespace, make add it to the custom dict. - name = child_tree.tag[child_tree.tag.index('}')+1:] - custom = _CustomFromElementTree(child_tree) - if custom: - self.custom[name] = custom - else: - atom.ExtensionContainer._ConvertElementTreeToMember(self, child_tree) - - # We need to overwtite _AddMembersToElementTree to add special logic to - # convert custom members to XML nodes. - def _AddMembersToElementTree(self, tree): - # Convert the members of this class which are XML child nodes. - # This uses the class's _children dictionary to find the members which - # should become XML child nodes. - member_node_names = [values[0] for tag, values in - self.__class__._children.iteritems()] - for member_name in member_node_names: - member = getattr(self, member_name) - if member is None: - pass - elif isinstance(member, list): - for instance in member: - instance._BecomeChildElement(tree) - else: - member._BecomeChildElement(tree) - # Convert the members of this class which are XML attributes. - for xml_attribute, member_name in self.__class__._attributes.iteritems(): - member = getattr(self, member_name) - if member is not None: - tree.attrib[xml_attribute] = member - # Convert all special custom item attributes to nodes - for name, custom in self.custom.iteritems(): - custom._BecomeChildElement(tree) - # Lastly, call the ExtensionContainers's _AddMembersToElementTree to - # convert any extension attributes. - atom.ExtensionContainer._AddMembersToElementTree(self, tree) - - -def SpreadsheetsListFromString(xml_string): - return atom.CreateClassFromXMLString(SpreadsheetsList, - xml_string) - element_tree = ElementTree.fromstring(xml_string) - return _SpreadsheetsListFromElementTree(element_tree) - - -class SpreadsheetsSpreadsheetsFeed(gdata.GDataFeed): - """A feed containing Google Spreadsheets Spreadsheets""" - - _tag = 'feed' - _namespace = atom.ATOM_NAMESPACE - _children = gdata.GDataFeed._children.copy() - _attributes = gdata.GDataFeed._attributes.copy() - _children['{%s}entry' % atom.ATOM_NAMESPACE] = ('entry', - [SpreadsheetsSpreadsheet]) - - -def SpreadsheetsSpreadsheetsFeedFromString(xml_string): - return atom.CreateClassFromXMLString(SpreadsheetsSpreadsheetsFeed, - xml_string) - - -class SpreadsheetsWorksheetsFeed(gdata.GDataFeed): - """A feed containing Google Spreadsheets Spreadsheets""" - - _tag = 'feed' - _namespace = atom.ATOM_NAMESPACE - _children = gdata.GDataFeed._children.copy() - _attributes = gdata.GDataFeed._attributes.copy() - _children['{%s}entry' % atom.ATOM_NAMESPACE] = ('entry', - [SpreadsheetsWorksheet]) - - -def SpreadsheetsWorksheetsFeedFromString(xml_string): - return atom.CreateClassFromXMLString(SpreadsheetsWorksheetsFeed, - xml_string) - - -class SpreadsheetsCellsFeed(gdata.BatchFeed): - """A feed containing Google Spreadsheets Cells""" - - _tag = 'feed' - _namespace = atom.ATOM_NAMESPACE - _children = gdata.BatchFeed._children.copy() - _attributes = gdata.BatchFeed._attributes.copy() - _children['{%s}entry' % atom.ATOM_NAMESPACE] = ('entry', - [SpreadsheetsCell]) - _children['{%s}rowCount' % GSPREADSHEETS_NAMESPACE] = ('row_count', - RowCount) - _children['{%s}colCount' % GSPREADSHEETS_NAMESPACE] = ('col_count', - ColCount) - - def __init__(self, author=None, category=None, contributor=None, - generator=None, icon=None, atom_id=None, link=None, logo=None, - rights=None, subtitle=None, title=None, updated=None, - entry=None, total_results=None, start_index=None, - items_per_page=None, extension_elements=None, - extension_attributes=None, text=None, row_count=None, - col_count=None, interrupted=None): - gdata.BatchFeed.__init__(self, author=author, category=category, - contributor=contributor, generator=generator, - icon=icon, atom_id=atom_id, link=link, - logo=logo, rights=rights, subtitle=subtitle, - title=title, updated=updated, entry=entry, - total_results=total_results, - start_index=start_index, - items_per_page=items_per_page, - extension_elements=extension_elements, - extension_attributes=extension_attributes, - text=text, interrupted=interrupted) - self.row_count = row_count - self.col_count = col_count - - def GetBatchLink(self): - for link in self.link: - if link.rel == 'http://schemas.google.com/g/2005#batch': - return link - return None - - -def SpreadsheetsCellsFeedFromString(xml_string): - return atom.CreateClassFromXMLString(SpreadsheetsCellsFeed, - xml_string) - - -class SpreadsheetsListFeed(gdata.GDataFeed): - """A feed containing Google Spreadsheets Spreadsheets""" - - _tag = 'feed' - _namespace = atom.ATOM_NAMESPACE - _children = gdata.GDataFeed._children.copy() - _attributes = gdata.GDataFeed._attributes.copy() - _children['{%s}entry' % atom.ATOM_NAMESPACE] = ('entry', - [SpreadsheetsList]) - - -def SpreadsheetsListFeedFromString(xml_string): - return atom.CreateClassFromXMLString(SpreadsheetsListFeed, - xml_string) diff --git a/gdata/analytics/spreadsheet/service.py b/gdata/analytics/spreadsheet/service.py deleted file mode 100644 index 1fe3eb7d72..0000000000 --- a/gdata/analytics/spreadsheet/service.py +++ /dev/null @@ -1,487 +0,0 @@ -#!/usr/bin/python -# -# Copyright (C) 2007 Google 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. - -"""SpreadsheetsService extends the GDataService to streamline Google -Spreadsheets operations. - - SpreadsheetService: Provides methods to query feeds and manipulate items. - Extends GDataService. - - DictionaryToParamList: Function which converts a dictionary into a list of - URL arguments (represented as strings). This is a - utility function used in CRUD operations. -""" - -__author__ = 'api.laurabeth@gmail.com (Laura Beth Lincoln)' - - -import gdata -import atom.service -import gdata.service -import gdata.spreadsheet -import atom - - -class Error(Exception): - """Base class for exceptions in this module.""" - pass - - -class RequestError(Error): - pass - - -class SpreadsheetsService(gdata.service.GDataService): - """Client for the Google Spreadsheets service.""" - - def __init__(self, email=None, password=None, source=None, - server='spreadsheets.google.com', additional_headers=None, - **kwargs): - """Creates a client for the Google Spreadsheets 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: 'spreadsheets.google.com'. - **kwargs: The other parameters to pass to gdata.service.GDataService - constructor. - """ - gdata.service.GDataService.__init__( - self, email=email, password=password, service='wise', source=source, - server=server, additional_headers=additional_headers, **kwargs) - - def GetSpreadsheetsFeed(self, key=None, query=None, visibility='private', - projection='full'): - """Gets a spreadsheets feed or a specific entry if a key is defined - Args: - key: string (optional) The spreadsheet key defined in /ccc?key= - query: DocumentQuery (optional) Query parameters - - Returns: - If there is no key, then a SpreadsheetsSpreadsheetsFeed. - If there is a key, then a SpreadsheetsSpreadsheet. - """ - - base_uri = 'https://%s/feeds/spreadsheets' % self.server - uri = ('%s/%s/%s' - % (base_uri, visibility, projection)) - - if key is not None: - uri = '%s/%s' % (uri, key) - - if query != None: - query.feed = base_uri - query.visibility = visibility - query.projection = projection - uri = query.ToUri() - - if key: - return self.Get(uri, - converter=gdata.spreadsheet.SpreadsheetsSpreadsheetFromString) - else: - return self.Get(uri, - converter=gdata.spreadsheet.SpreadsheetsSpreadsheetsFeedFromString) - - def GetWorksheetsFeed(self, key, wksht_id=None, query=None, - visibility='private', projection='full'): - """Gets a worksheets feed or a specific entry if a wksht is defined - Args: - key: string The spreadsheet key defined in /ccc?key= - wksht_id: string (optional) The id for a specific worksheet entry - query: DocumentQuery (optional) Query parameters - - Returns: - If there is no wksht_id, then a SpreadsheetsWorksheetsFeed. - If there is a wksht_id, then a SpreadsheetsWorksheet. - """ - - uri = ('https://%s/feeds/worksheets/%s/%s/%s' - % (self.server, key, visibility, projection)) - - if wksht_id != None: - uri = '%s/%s' % (uri, wksht_id) - - if query != None: - query.feed = uri - uri = query.ToUri() - - if wksht_id: - return self.Get(uri, - converter=gdata.spreadsheet.SpreadsheetsWorksheetFromString) - else: - return self.Get(uri, - converter=gdata.spreadsheet.SpreadsheetsWorksheetsFeedFromString) - - def AddWorksheet(self, title, row_count, col_count, key): - """Creates a new worksheet in the desired spreadsheet. - - The new worksheet is appended to the end of the list of worksheets. The - new worksheet will only have the available number of columns and cells - specified. - - Args: - title: str The title which will be displayed in the list of worksheets. - row_count: int or str The number of rows in the new worksheet. - col_count: int or str The number of columns in the new worksheet. - key: str The spreadsheet key to the spreadsheet to which the new - worksheet should be added. - - Returns: - A SpreadsheetsWorksheet if the new worksheet was created succesfully. - """ - new_worksheet = gdata.spreadsheet.SpreadsheetsWorksheet( - title=atom.Title(text=title), - row_count=gdata.spreadsheet.RowCount(text=str(row_count)), - col_count=gdata.spreadsheet.ColCount(text=str(col_count))) - return self.Post(new_worksheet, - 'https://%s/feeds/worksheets/%s/private/full' % (self.server, key), - converter=gdata.spreadsheet.SpreadsheetsWorksheetFromString) - - def UpdateWorksheet(self, worksheet_entry, url=None): - """Changes the size and/or title of the desired worksheet. - - Args: - worksheet_entry: SpreadsheetWorksheet The new contents of the - worksheet. - url: str (optional) The URL to which the edited worksheet entry should - be sent. If the url is None, the edit URL from the worksheet will - be used. - - Returns: - A SpreadsheetsWorksheet with the new information about the worksheet. - """ - target_url = url or worksheet_entry.GetEditLink().href - return self.Put(worksheet_entry, target_url, - converter=gdata.spreadsheet.SpreadsheetsWorksheetFromString) - - def DeleteWorksheet(self, worksheet_entry=None, url=None): - """Removes the desired worksheet from the spreadsheet - - Args: - worksheet_entry: SpreadsheetWorksheet (optional) The worksheet to - be deleted. If this is none, then the DELETE reqest is sent to - the url specified in the url parameter. - url: str (optaional) The URL to which the DELETE request should be - sent. If left as None, the worksheet's edit URL is used. - - Returns: - True if the worksheet was deleted successfully. - """ - if url: - target_url = url - else: - target_url = worksheet_entry.GetEditLink().href - return self.Delete(target_url) - - def GetCellsFeed(self, key, wksht_id='default', cell=None, query=None, - visibility='private', projection='full'): - """Gets a cells feed or a specific entry if a cell is defined - Args: - key: string The spreadsheet key defined in /ccc?key= - wksht_id: string The id for a specific worksheet entry - cell: string (optional) The R1C1 address of the cell - query: DocumentQuery (optional) Query parameters - - Returns: - If there is no cell, then a SpreadsheetsCellsFeed. - If there is a cell, then a SpreadsheetsCell. - """ - - uri = ('https://%s/feeds/cells/%s/%s/%s/%s' - % (self.server, key, wksht_id, visibility, projection)) - - if cell != None: - uri = '%s/%s' % (uri, cell) - - if query != None: - query.feed = uri - uri = query.ToUri() - - if cell: - return self.Get(uri, - converter=gdata.spreadsheet.SpreadsheetsCellFromString) - else: - return self.Get(uri, - converter=gdata.spreadsheet.SpreadsheetsCellsFeedFromString) - - def GetListFeed(self, key, wksht_id='default', row_id=None, query=None, - visibility='private', projection='full'): - """Gets a list feed or a specific entry if a row_id is defined - Args: - key: string The spreadsheet key defined in /ccc?key= - wksht_id: string The id for a specific worksheet entry - row_id: string (optional) The row_id of a row in the list - query: DocumentQuery (optional) Query parameters - - Returns: - If there is no row_id, then a SpreadsheetsListFeed. - If there is a row_id, then a SpreadsheetsList. - """ - - uri = ('https://%s/feeds/list/%s/%s/%s/%s' - % (self.server, key, wksht_id, visibility, projection)) - - if row_id is not None: - uri = '%s/%s' % (uri, row_id) - - if query is not None: - query.feed = uri - uri = query.ToUri() - - if row_id: - return self.Get(uri, - converter=gdata.spreadsheet.SpreadsheetsListFromString) - else: - return self.Get(uri, - converter=gdata.spreadsheet.SpreadsheetsListFeedFromString) - - def UpdateCell(self, row, col, inputValue, key, wksht_id='default'): - """Updates an existing cell. - - Args: - row: int The row the cell to be editted is in - col: int The column the cell to be editted is in - inputValue: str the new value of the cell - key: str The key of the spreadsheet in which this cell resides. - wksht_id: str The ID of the worksheet which holds this cell. - - Returns: - The updated cell entry - """ - row = str(row) - col = str(col) - # make the new cell - new_cell = gdata.spreadsheet.Cell(row=row, col=col, inputValue=inputValue) - # get the edit uri and PUT - cell = 'R%sC%s' % (row, col) - entry = self.GetCellsFeed(key, wksht_id, cell) - for a_link in entry.link: - if a_link.rel == 'edit': - entry.cell = new_cell - return self.Put(entry, a_link.href, - converter=gdata.spreadsheet.SpreadsheetsCellFromString) - - def _GenerateCellsBatchUrl(self, spreadsheet_key, worksheet_id): - return ('https://spreadsheets.google.com/feeds/cells/%s/%s/' - 'private/full/batch' % (spreadsheet_key, worksheet_id)) - - def ExecuteBatch(self, batch_feed, url=None, spreadsheet_key=None, - worksheet_id=None, - converter=gdata.spreadsheet.SpreadsheetsCellsFeedFromString): - """Sends a batch request feed to the server. - - The batch request needs to be sent to the batch URL for a particular - worksheet. You can specify the worksheet by providing the spreadsheet_key - and worksheet_id, or by sending the URL from the cells feed's batch link. - - Args: - batch_feed: gdata.spreadsheet.SpreadsheetsCellFeed A feed containing - BatchEntry elements which contain the desired CRUD operation and - any necessary data to modify a cell. - url: str (optional) The batch URL for the cells feed to which these - changes should be applied. This can be found by calling - cells_feed.GetBatchLink().href. - spreadsheet_key: str (optional) Used to generate the batch request URL - if the url argument is None. If using the spreadsheet key to - generate the URL, the worksheet id is also required. - worksheet_id: str (optional) Used if the url is not provided, it is - oart of the batch feed target URL. This is used with the spreadsheet - key. - converter: Function (optional) Function to be executed on the server's - response. This function should take one string as a parameter. The - default value is SpreadsheetsCellsFeedFromString which will turn the result - into a gdata.spreadsheet.SpreadsheetsCellsFeed object. - - Returns: - A gdata.BatchFeed containing the results. - """ - - if url is None: - url = self._GenerateCellsBatchUrl(spreadsheet_key, worksheet_id) - return self.Post(batch_feed, url, converter=converter) - - def InsertRow(self, row_data, key, wksht_id='default'): - """Inserts a new row with the provided data - - Args: - uri: string The post uri of the list feed - row_data: dict A dictionary of column header to row data - - Returns: - The inserted row - """ - new_entry = gdata.spreadsheet.SpreadsheetsList() - for k, v in row_data.iteritems(): - new_custom = gdata.spreadsheet.Custom() - new_custom.column = k - new_custom.text = v - new_entry.custom[new_custom.column] = new_custom - # Generate the post URL for the worksheet which will receive the new entry. - post_url = 'https://spreadsheets.google.com/feeds/list/%s/%s/private/full'%( - key, wksht_id) - return self.Post(new_entry, post_url, - converter=gdata.spreadsheet.SpreadsheetsListFromString) - - def UpdateRow(self, entry, new_row_data): - """Updates a row with the provided data - - If you want to add additional information to a row, it is often - easier to change the values in entry.custom, then use the Put - method instead of UpdateRow. This UpdateRow method will replace - the contents of the row with new_row_data - it will change all columns - not just the columns specified in the new_row_data dict. - - Args: - entry: gdata.spreadsheet.SpreadsheetsList The entry to be updated - new_row_data: dict A dictionary of column header to row data - - Returns: - The updated row - """ - entry.custom = {} - for k, v in new_row_data.iteritems(): - new_custom = gdata.spreadsheet.Custom() - new_custom.column = k - new_custom.text = v - entry.custom[k] = new_custom - for a_link in entry.link: - if a_link.rel == 'edit': - return self.Put(entry, a_link.href, - converter=gdata.spreadsheet.SpreadsheetsListFromString) - - def DeleteRow(self, entry): - """Deletes a row, the provided entry - - Args: - entry: gdata.spreadsheet.SpreadsheetsList The row to be deleted - - Returns: - The delete response - """ - for a_link in entry.link: - if a_link.rel == 'edit': - return self.Delete(a_link.href) - - -class DocumentQuery(gdata.service.Query): - - def _GetTitleQuery(self): - return self['title'] - - def _SetTitleQuery(self, document_query): - self['title'] = document_query - - title = property(_GetTitleQuery, _SetTitleQuery, - doc="""The title query parameter""") - - def _GetTitleExactQuery(self): - return self['title-exact'] - - def _SetTitleExactQuery(self, document_query): - self['title-exact'] = document_query - - title_exact = property(_GetTitleExactQuery, _SetTitleExactQuery, - doc="""The title-exact query parameter""") - - -class CellQuery(gdata.service.Query): - - def _GetMinRowQuery(self): - return self['min-row'] - - def _SetMinRowQuery(self, cell_query): - self['min-row'] = cell_query - - min_row = property(_GetMinRowQuery, _SetMinRowQuery, - doc="""The min-row query parameter""") - - def _GetMaxRowQuery(self): - return self['max-row'] - - def _SetMaxRowQuery(self, cell_query): - self['max-row'] = cell_query - - max_row = property(_GetMaxRowQuery, _SetMaxRowQuery, - doc="""The max-row query parameter""") - - def _GetMinColQuery(self): - return self['min-col'] - - def _SetMinColQuery(self, cell_query): - self['min-col'] = cell_query - - min_col = property(_GetMinColQuery, _SetMinColQuery, - doc="""The min-col query parameter""") - - def _GetMaxColQuery(self): - return self['max-col'] - - def _SetMaxColQuery(self, cell_query): - self['max-col'] = cell_query - - max_col = property(_GetMaxColQuery, _SetMaxColQuery, - doc="""The max-col query parameter""") - - def _GetRangeQuery(self): - return self['range'] - - def _SetRangeQuery(self, cell_query): - self['range'] = cell_query - - range = property(_GetRangeQuery, _SetRangeQuery, - doc="""The range query parameter""") - - def _GetReturnEmptyQuery(self): - return self['return-empty'] - - def _SetReturnEmptyQuery(self, cell_query): - self['return-empty'] = cell_query - - return_empty = property(_GetReturnEmptyQuery, _SetReturnEmptyQuery, - doc="""The return-empty query parameter""") - - -class ListQuery(gdata.service.Query): - - def _GetSpreadsheetQuery(self): - return self['sq'] - - def _SetSpreadsheetQuery(self, list_query): - self['sq'] = list_query - - sq = property(_GetSpreadsheetQuery, _SetSpreadsheetQuery, - doc="""The sq query parameter""") - - def _GetOrderByQuery(self): - return self['orderby'] - - def _SetOrderByQuery(self, list_query): - self['orderby'] = list_query - - orderby = property(_GetOrderByQuery, _SetOrderByQuery, - doc="""The orderby query parameter""") - - def _GetReverseQuery(self): - return self['reverse'] - - def _SetReverseQuery(self, list_query): - self['reverse'] = list_query - - reverse = property(_GetReverseQuery, _SetReverseQuery, - doc="""The reverse query parameter""") diff --git a/gdata/analytics/spreadsheet/text_db.py b/gdata/analytics/spreadsheet/text_db.py deleted file mode 100644 index a8de5463c2..0000000000 --- a/gdata/analytics/spreadsheet/text_db.py +++ /dev/null @@ -1,559 +0,0 @@ -#!/usr/bin/python -# -# Copyright Google 2007-2008, 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. - - -import StringIO -import gdata -import gdata.service -import gdata.spreadsheet -import gdata.spreadsheet.service -import gdata.docs -import gdata.docs.service - - -"""Make the Google Documents API feel more like using a database. - -This module contains a client and other classes which make working with the -Google Documents List Data API and the Google Spreadsheets Data API look a -bit more like working with a heirarchical database. Using the DatabaseClient, -you can create or find spreadsheets and use them like a database, with -worksheets representing tables and rows representing records. - -Example Usage: -# Create a new database, a new table, and add records. -client = gdata.spreadsheet.text_db.DatabaseClient(username='jo@example.com', - password='12345') -database = client.CreateDatabase('My Text Database') -table = database.CreateTable('addresses', ['name','email', - 'phonenumber', 'mailingaddress']) -record = table.AddRecord({'name':'Bob', 'email':'bob@example.com', - 'phonenumber':'555-555-1234', 'mailingaddress':'900 Imaginary St.'}) - -# Edit a record -record.content['email'] = 'bob2@example.com' -record.Push() - -# Delete a table -table.Delete - -Warnings: -Care should be exercised when using this module on spreadsheets -which contain formulas. This module treats all rows as containing text and -updating a row will overwrite any formula with the output of the formula. -The intended use case is to allow easy storage of text data in a spreadsheet. - - Error: Domain specific extension of Exception. - BadCredentials: Error raised is username or password was incorrect. - CaptchaRequired: Raised if a login attempt failed and a CAPTCHA challenge - was issued. - DatabaseClient: Communicates with Google Docs APIs servers. - Database: Represents a spreadsheet and interacts with tables. - Table: Represents a worksheet and interacts with records. - RecordResultSet: A list of records in a table. - Record: Represents a row in a worksheet allows manipulation of text data. -""" - - -__author__ = 'api.jscudder (Jeffrey Scudder)' - - -class Error(Exception): - pass - - -class BadCredentials(Error): - pass - - -class CaptchaRequired(Error): - pass - - -class DatabaseClient(object): - """Allows creation and finding of Google Spreadsheets databases. - - The DatabaseClient simplifies the process of creating and finding Google - Spreadsheets and will talk to both the Google Spreadsheets API and the - Google Documents List API. - """ - - def __init__(self, username=None, password=None): - """Constructor for a Database Client. - - If the username and password are present, the constructor will contact - the Google servers to authenticate. - - Args: - username: str (optional) Example: jo@example.com - password: str (optional) - """ - self.__docs_client = gdata.docs.service.DocsService() - self.__spreadsheets_client = ( - gdata.spreadsheet.service.SpreadsheetsService()) - self.SetCredentials(username, password) - - def SetCredentials(self, username, password): - """Attempts to log in to Google APIs using the provided credentials. - - If the username or password are None, the client will not request auth - tokens. - - Args: - username: str (optional) Example: jo@example.com - password: str (optional) - """ - self.__docs_client.email = username - self.__docs_client.password = password - self.__spreadsheets_client.email = username - self.__spreadsheets_client.password = password - if username and password: - try: - self.__docs_client.ProgrammaticLogin() - self.__spreadsheets_client.ProgrammaticLogin() - except gdata.service.CaptchaRequired: - raise CaptchaRequired('Please visit https://www.google.com/accounts/' - 'DisplayUnlockCaptcha to unlock your account.') - except gdata.service.BadAuthentication: - raise BadCredentials('Username or password incorrect.') - - def CreateDatabase(self, name): - """Creates a new Google Spreadsheet with the desired name. - - Args: - name: str The title for the spreadsheet. - - Returns: - A Database instance representing the new spreadsheet. - """ - # Create a Google Spreadsheet to form the foundation of this database. - # Spreadsheet is created by uploading a file to the Google Documents - # List API. - virtual_csv_file = StringIO.StringIO(',,,') - virtual_media_source = gdata.MediaSource(file_handle=virtual_csv_file, content_type='text/csv', content_length=3) - db_entry = self.__docs_client.UploadSpreadsheet(virtual_media_source, name) - return Database(spreadsheet_entry=db_entry, database_client=self) - - def GetDatabases(self, spreadsheet_key=None, name=None): - """Finds spreadsheets which have the unique key or title. - - If querying on the spreadsheet_key there will be at most one result, but - searching by name could yield multiple results. - - Args: - spreadsheet_key: str The unique key for the spreadsheet, this - usually in the the form 'pk23...We' or 'o23...423.12,,,3'. - name: str The title of the spreadsheets. - - Returns: - A list of Database objects representing the desired spreadsheets. - """ - if spreadsheet_key: - db_entry = self.__docs_client.GetDocumentListEntry( - r'/feeds/documents/private/full/spreadsheet%3A' + spreadsheet_key) - return [Database(spreadsheet_entry=db_entry, database_client=self)] - else: - title_query = gdata.docs.service.DocumentQuery() - title_query['title'] = name - db_feed = self.__docs_client.QueryDocumentListFeed(title_query.ToUri()) - matching_databases = [] - for entry in db_feed.entry: - matching_databases.append(Database(spreadsheet_entry=entry, - database_client=self)) - return matching_databases - - def _GetDocsClient(self): - return self.__docs_client - - def _GetSpreadsheetsClient(self): - return self.__spreadsheets_client - - -class Database(object): - """Provides interface to find and create tables. - - The database represents a Google Spreadsheet. - """ - - def __init__(self, spreadsheet_entry=None, database_client=None): - """Constructor for a database object. - - Args: - spreadsheet_entry: gdata.docs.DocumentListEntry The - Atom entry which represents the Google Spreadsheet. The - spreadsheet's key is extracted from the entry and stored as a - member. - database_client: DatabaseClient A client which can talk to the - Google Spreadsheets servers to perform operations on worksheets - within this spreadsheet. - """ - self.entry = spreadsheet_entry - if self.entry: - id_parts = spreadsheet_entry.id.text.split('/') - self.spreadsheet_key = id_parts[-1].replace('spreadsheet%3A', '') - self.client = database_client - - def CreateTable(self, name, fields=None): - """Add a new worksheet to this spreadsheet and fill in column names. - - Args: - name: str The title of the new worksheet. - fields: list of strings The column names which are placed in the - first row of this worksheet. These names are converted into XML - tags by the server. To avoid changes during the translation - process I recommend using all lowercase alphabetic names. For - example ['somelongname', 'theothername'] - - Returns: - Table representing the newly created worksheet. - """ - worksheet = self.client._GetSpreadsheetsClient().AddWorksheet(title=name, - row_count=1, col_count=len(fields), key=self.spreadsheet_key) - return Table(name=name, worksheet_entry=worksheet, - database_client=self.client, - spreadsheet_key=self.spreadsheet_key, fields=fields) - - def GetTables(self, worksheet_id=None, name=None): - """Searches for a worksheet with the specified ID or name. - - The list of results should have one table at most, or no results - if the id or name were not found. - - Args: - worksheet_id: str The ID of the worksheet, example: 'od6' - name: str The title of the worksheet. - - Returns: - A list of length 0 or 1 containing the desired Table. A list is returned - to make this method feel like GetDatabases and GetRecords. - """ - if worksheet_id: - worksheet_entry = self.client._GetSpreadsheetsClient().GetWorksheetsFeed( - self.spreadsheet_key, wksht_id=worksheet_id) - return [Table(name=worksheet_entry.title.text, - worksheet_entry=worksheet_entry, database_client=self.client, - spreadsheet_key=self.spreadsheet_key)] - else: - matching_tables = [] - query = None - if name: - query = gdata.spreadsheet.service.DocumentQuery() - query.title = name - - worksheet_feed = self.client._GetSpreadsheetsClient().GetWorksheetsFeed( - self.spreadsheet_key, query=query) - for entry in worksheet_feed.entry: - matching_tables.append(Table(name=entry.title.text, - worksheet_entry=entry, database_client=self.client, - spreadsheet_key=self.spreadsheet_key)) - return matching_tables - - def Delete(self): - """Deletes the entire database spreadsheet from Google Spreadsheets.""" - entry = self.client._GetDocsClient().Get( - r'http://docs.google.com/feeds/documents/private/full/spreadsheet%3A' + - self.spreadsheet_key) - self.client._GetDocsClient().Delete(entry.GetEditLink().href) - - -class Table(object): - - def __init__(self, name=None, worksheet_entry=None, database_client=None, - spreadsheet_key=None, fields=None): - self.name = name - self.entry = worksheet_entry - id_parts = worksheet_entry.id.text.split('/') - self.worksheet_id = id_parts[-1] - self.spreadsheet_key = spreadsheet_key - self.client = database_client - self.fields = fields or [] - if fields: - self.SetFields(fields) - - def LookupFields(self): - """Queries to find the column names in the first row of the worksheet. - - Useful when you have retrieved the table from the server and you don't - know the column names. - """ - if self.entry: - first_row_contents = [] - query = gdata.spreadsheet.service.CellQuery() - query.max_row = '1' - query.min_row = '1' - feed = self.client._GetSpreadsheetsClient().GetCellsFeed( - self.spreadsheet_key, wksht_id=self.worksheet_id, query=query) - for entry in feed.entry: - first_row_contents.append(entry.content.text) - # Get the next set of cells if needed. - next_link = feed.GetNextLink() - while next_link: - feed = self.client._GetSpreadsheetsClient().Get(next_link.href, - converter=gdata.spreadsheet.SpreadsheetsCellsFeedFromString) - for entry in feed.entry: - first_row_contents.append(entry.content.text) - next_link = feed.GetNextLink() - # Convert the contents of the cells to valid headers. - self.fields = ConvertStringsToColumnHeaders(first_row_contents) - - def SetFields(self, fields): - """Changes the contents of the cells in the first row of this worksheet. - - Args: - fields: list of strings The names in the list comprise the - first row of the worksheet. These names are converted into XML - tags by the server. To avoid changes during the translation - process I recommend using all lowercase alphabetic names. For - example ['somelongname', 'theothername'] - """ - # TODO: If the table already had fields, we might want to clear out the, - # current column headers. - self.fields = fields - i = 0 - for column_name in fields: - i = i + 1 - # TODO: speed this up by using a batch request to update cells. - self.client._GetSpreadsheetsClient().UpdateCell(1, i, column_name, - self.spreadsheet_key, self.worksheet_id) - - def Delete(self): - """Deletes this worksheet from the spreadsheet.""" - worksheet = self.client._GetSpreadsheetsClient().GetWorksheetsFeed( - self.spreadsheet_key, wksht_id=self.worksheet_id) - self.client._GetSpreadsheetsClient().DeleteWorksheet( - worksheet_entry=worksheet) - - def AddRecord(self, data): - """Adds a new row to this worksheet. - - Args: - data: dict of strings Mapping of string values to column names. - - Returns: - Record which represents this row of the spreadsheet. - """ - new_row = self.client._GetSpreadsheetsClient().InsertRow(data, - self.spreadsheet_key, wksht_id=self.worksheet_id) - return Record(content=data, row_entry=new_row, - spreadsheet_key=self.spreadsheet_key, worksheet_id=self.worksheet_id, - database_client=self.client) - - def GetRecord(self, row_id=None, row_number=None): - """Gets a single record from the worksheet based on row ID or number. - - Args: - row_id: The ID for the individual row. - row_number: str or int The position of the desired row. Numbering - begins at 1, which refers to the second row in the worksheet since - the first row is used for column names. - - Returns: - Record for the desired row. - """ - if row_id: - row_entry = self.client._GetSpreadsheetsClient().GetListFeed( - self.spreadsheet_key, wksht_id=self.worksheet_id, row_id=row_id) - return Record(content=None, row_entry=row_entry, - spreadsheet_key=self.spreadsheet_key, - worksheet_id=self.worksheet_id, database_client=self.client) - else: - row_query = gdata.spreadsheet.service.ListQuery() - row_query.start_index = str(row_number) - row_query.max_results = '1' - row_feed = self.client._GetSpreadsheetsClient().GetListFeed( - self.spreadsheet_key, wksht_id=self.worksheet_id, query=row_query) - if len(row_feed.entry) >= 1: - return Record(content=None, row_entry=row_feed.entry[0], - spreadsheet_key=self.spreadsheet_key, - worksheet_id=self.worksheet_id, database_client=self.client) - else: - return None - - def GetRecords(self, start_row, end_row): - """Gets all rows between the start and end row numbers inclusive. - - Args: - start_row: str or int - end_row: str or int - - Returns: - RecordResultSet for the desired rows. - """ - start_row = int(start_row) - end_row = int(end_row) - max_rows = end_row - start_row + 1 - row_query = gdata.spreadsheet.service.ListQuery() - row_query.start_index = str(start_row) - row_query.max_results = str(max_rows) - rows_feed = self.client._GetSpreadsheetsClient().GetListFeed( - self.spreadsheet_key, wksht_id=self.worksheet_id, query=row_query) - return RecordResultSet(rows_feed, self.client, self.spreadsheet_key, - self.worksheet_id) - - def FindRecords(self, query_string): - """Performs a query against the worksheet to find rows which match. - - For details on query string syntax see the section on sq under - http://code.google.com/apis/spreadsheets/reference.html#list_Parameters - - Args: - query_string: str Examples: 'name == john' to find all rows with john - in the name column, '(cost < 19.50 and name != toy) or cost > 500' - - Returns: - RecordResultSet with the first group of matches. - """ - row_query = gdata.spreadsheet.service.ListQuery() - row_query.sq = query_string - matching_feed = self.client._GetSpreadsheetsClient().GetListFeed( - self.spreadsheet_key, wksht_id=self.worksheet_id, query=row_query) - return RecordResultSet(matching_feed, self.client, - self.spreadsheet_key, self.worksheet_id) - - -class RecordResultSet(list): - """A collection of rows which allows fetching of the next set of results. - - The server may not send all rows in the requested range because there are - too many. Using this result set you can access the first set of results - as if it is a list, then get the next batch (if there are more results) by - calling GetNext(). - """ - - def __init__(self, feed, client, spreadsheet_key, worksheet_id): - self.client = client - self.spreadsheet_key = spreadsheet_key - self.worksheet_id = worksheet_id - self.feed = feed - list(self) - for entry in self.feed.entry: - self.append(Record(content=None, row_entry=entry, - spreadsheet_key=spreadsheet_key, worksheet_id=worksheet_id, - database_client=client)) - - def GetNext(self): - """Fetches the next batch of rows in the result set. - - Returns: - A new RecordResultSet. - """ - next_link = self.feed.GetNextLink() - if next_link and next_link.href: - new_feed = self.client._GetSpreadsheetsClient().Get(next_link.href, - converter=gdata.spreadsheet.SpreadsheetsListFeedFromString) - return RecordResultSet(new_feed, self.client, self.spreadsheet_key, - self.worksheet_id) - - -class Record(object): - """Represents one row in a worksheet and provides a dictionary of values. - - Attributes: - custom: dict Represents the contents of the row with cell values mapped - to column headers. - """ - - def __init__(self, content=None, row_entry=None, spreadsheet_key=None, - worksheet_id=None, database_client=None): - """Constructor for a record. - - Args: - content: dict of strings Mapping of string values to column names. - row_entry: gdata.spreadsheet.SpreadsheetsList The Atom entry - representing this row in the worksheet. - spreadsheet_key: str The ID of the spreadsheet in which this row - belongs. - worksheet_id: str The ID of the worksheet in which this row belongs. - database_client: DatabaseClient The client which can be used to talk - the Google Spreadsheets server to edit this row. - """ - self.entry = row_entry - self.spreadsheet_key = spreadsheet_key - self.worksheet_id = worksheet_id - if row_entry: - self.row_id = row_entry.id.text.split('/')[-1] - else: - self.row_id = None - self.client = database_client - self.content = content or {} - if not content: - self.ExtractContentFromEntry(row_entry) - - def ExtractContentFromEntry(self, entry): - """Populates the content and row_id based on content of the entry. - - This method is used in the Record's contructor. - - Args: - entry: gdata.spreadsheet.SpreadsheetsList The Atom entry - representing this row in the worksheet. - """ - self.content = {} - if entry: - self.row_id = entry.id.text.split('/')[-1] - for label, custom in entry.custom.iteritems(): - self.content[label] = custom.text - - def Push(self): - """Send the content of the record to spreadsheets to edit the row. - - All items in the content dictionary will be sent. Items which have been - removed from the content may remain in the row. The content member - of the record will not be modified so additional fields in the row - might be absent from this local copy. - """ - self.entry = self.client._GetSpreadsheetsClient().UpdateRow(self.entry, self.content) - - def Pull(self): - """Query Google Spreadsheets to get the latest data from the server. - - Fetches the entry for this row and repopulates the content dictionary - with the data found in the row. - """ - if self.row_id: - self.entry = self.client._GetSpreadsheetsClient().GetListFeed( - self.spreadsheet_key, wksht_id=self.worksheet_id, row_id=self.row_id) - self.ExtractContentFromEntry(self.entry) - - def Delete(self): - self.client._GetSpreadsheetsClient().DeleteRow(self.entry) - - -def ConvertStringsToColumnHeaders(proposed_headers): - """Converts a list of strings to column names which spreadsheets accepts. - - When setting values in a record, the keys which represent column names must - fit certain rules. They are all lower case, contain no spaces or special - characters. If two columns have the same name after being sanitized, the - columns further to the right have _2, _3 _4, etc. appended to them. - - If there are column names which consist of all special characters, or if - the column header is blank, an obfuscated value will be used for a column - name. This method does not handle blank column names or column names with - only special characters. - """ - headers = [] - for input_string in proposed_headers: - # TODO: probably a more efficient way to do this. Perhaps regex. - sanitized = input_string.lower().replace('_', '').replace( - ':', '').replace(' ', '') - # When the same sanitized header appears multiple times in the first row - # of a spreadsheet, _n is appended to the name to make it unique. - header_count = headers.count(sanitized) - if header_count > 0: - headers.append('%s_%i' % (sanitized, header_count+1)) - else: - headers.append(sanitized) - return headers diff --git a/gdata/analytics/spreadsheets/__init__.py b/gdata/analytics/spreadsheets/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/gdata/analytics/spreadsheets/client.py b/gdata/analytics/spreadsheets/client.py deleted file mode 100644 index 872ec13025..0000000000 --- a/gdata/analytics/spreadsheets/client.py +++ /dev/null @@ -1,592 +0,0 @@ -#!/usr/bin/env python -# -# Copyright (C) 2009 Google 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. - - -"""Contains a client to communicate with the Google Spreadsheets servers. - -For documentation on the Spreadsheets API, see: -http://code.google.com/apis/spreadsheets/ -""" - - -__author__ = 'j.s@google.com (Jeff Scudder)' - - -import gdata.client -import gdata.gauth -import gdata.spreadsheets.data -import atom.data -import atom.http_core - - -SPREADSHEETS_URL = ('https://spreadsheets.google.com/feeds/spreadsheets' - '/private/full') -WORKSHEETS_URL = ('https://spreadsheets.google.com/feeds/worksheets/' - '%s/private/full') -WORKSHEET_URL = ('https://spreadsheets.google.com/feeds/worksheets/' - '%s/private/full/%s') -TABLES_URL = 'https://spreadsheets.google.com/feeds/%s/tables' -RECORDS_URL = 'https://spreadsheets.google.com/feeds/%s/records/%s' -RECORD_URL = 'https://spreadsheets.google.com/feeds/%s/records/%s/%s' -CELLS_URL = 'https://spreadsheets.google.com/feeds/cells/%s/%s/private/full' -CELL_URL = ('https://spreadsheets.google.com/feeds/cells/%s/%s/private/full/' - 'R%sC%s') -LISTS_URL = 'https://spreadsheets.google.com/feeds/list/%s/%s/private/full' - - -class SpreadsheetsClient(gdata.client.GDClient): - api_version = '3' - auth_service = 'wise' - auth_scopes = gdata.gauth.AUTH_SCOPES['wise'] - ssl = True - - def get_spreadsheets(self, auth_token=None, - desired_class=gdata.spreadsheets.data.SpreadsheetsFeed, - **kwargs): - """Obtains a feed with the spreadsheets belonging to the current user. - - Args: - auth_token: An object which sets the Authorization HTTP header in its - modify_request method. Recommended classes include - gdata.gauth.ClientLoginToken and gdata.gauth.AuthSubToken - among others. Represents the current user. Defaults to None - and if None, this method will look for a value in the - auth_token member of SpreadsheetsClient. - desired_class: class descended from atom.core.XmlElement to which a - successful response should be converted. If there is no - converter function specified (converter=None) then the - desired_class will be used in calling the - atom.core.parse function. If neither - the desired_class nor the converter is specified, an - HTTP reponse object will be returned. Defaults to - gdata.spreadsheets.data.SpreadsheetsFeed. - """ - return self.get_feed(SPREADSHEETS_URL, auth_token=auth_token, - desired_class=desired_class, **kwargs) - - GetSpreadsheets = get_spreadsheets - - def get_worksheets(self, spreadsheet_key, auth_token=None, - desired_class=gdata.spreadsheets.data.WorksheetsFeed, - **kwargs): - """Finds the worksheets within a given spreadsheet. - - Args: - spreadsheet_key: str, The unique ID of this containing spreadsheet. This - can be the ID from the URL or as provided in a - Spreadsheet entry. - auth_token: An object which sets the Authorization HTTP header in its - modify_request method. Recommended classes include - gdata.gauth.ClientLoginToken and gdata.gauth.AuthSubToken - among others. Represents the current user. Defaults to None - and if None, this method will look for a value in the - auth_token member of SpreadsheetsClient. - desired_class: class descended from atom.core.XmlElement to which a - successful response should be converted. If there is no - converter function specified (converter=None) then the - desired_class will be used in calling the - atom.core.parse function. If neither - the desired_class nor the converter is specified, an - HTTP reponse object will be returned. Defaults to - gdata.spreadsheets.data.WorksheetsFeed. - """ - return self.get_feed(WORKSHEETS_URL % spreadsheet_key, - auth_token=auth_token, desired_class=desired_class, - **kwargs) - - GetWorksheets = get_worksheets - - def add_worksheet(self, spreadsheet_key, title, rows, cols, - auth_token=None, **kwargs): - """Creates a new worksheet entry in the spreadsheet. - - Args: - spreadsheet_key: str, The unique ID of this containing spreadsheet. This - can be the ID from the URL or as provided in a - Spreadsheet entry. - title: str, The title to be used in for the worksheet. - rows: str or int, The number of rows this worksheet should start with. - cols: str or int, The number of columns this worksheet should start with. - auth_token: An object which sets the Authorization HTTP header in its - modify_request method. Recommended classes include - gdata.gauth.ClientLoginToken and gdata.gauth.AuthSubToken - among others. Represents the current user. Defaults to None - and if None, this method will look for a value in the - auth_token member of SpreadsheetsClient. - """ - new_worksheet = gdata.spreadsheets.data.WorksheetEntry( - title=atom.data.Title(text=title), - row_count=gdata.spreadsheets.data.RowCount(text=str(rows)), - col_count=gdata.spreadsheets.data.ColCount(text=str(cols))) - return self.post(new_worksheet, WORKSHEETS_URL % spreadsheet_key, - auth_token=auth_token, **kwargs) - - AddWorksheet = add_worksheet - - def get_worksheet(self, spreadsheet_key, worksheet_id, - desired_class=gdata.spreadsheets.data.WorksheetEntry, - auth_token=None, **kwargs): - """Retrieves a single worksheet. - - Args: - spreadsheet_key: str, The unique ID of this containing spreadsheet. This - can be the ID from the URL or as provided in a - Spreadsheet entry. - worksheet_id: str, The unique ID for the worksheet withing the desired - spreadsheet. - auth_token: An object which sets the Authorization HTTP header in its - modify_request method. Recommended classes include - gdata.gauth.ClientLoginToken and gdata.gauth.AuthSubToken - among others. Represents the current user. Defaults to None - and if None, this method will look for a value in the - auth_token member of SpreadsheetsClient. - desired_class: class descended from atom.core.XmlElement to which a - successful response should be converted. If there is no - converter function specified (converter=None) then the - desired_class will be used in calling the - atom.core.parse function. If neither - the desired_class nor the converter is specified, an - HTTP reponse object will be returned. Defaults to - gdata.spreadsheets.data.WorksheetEntry. - - """ - return self.get_entry(WORKSHEET_URL % (spreadsheet_key, worksheet_id,), - auth_token=auth_token, desired_class=desired_class, - **kwargs) - - GetWorksheet = get_worksheet - - def add_table(self, spreadsheet_key, title, summary, worksheet_name, - header_row, num_rows, start_row, insertion_mode, - column_headers, auth_token=None, **kwargs): - """Creates a new table within the worksheet. - - Args: - spreadsheet_key: str, The unique ID of this containing spreadsheet. This - can be the ID from the URL or as provided in a - Spreadsheet entry. - title: str, The title for the new table within a worksheet. - summary: str, A description of the table. - worksheet_name: str The name of the worksheet in which this table - should live. - header_row: int or str, The number of the row in the worksheet which - will contain the column names for the data in this table. - num_rows: int or str, The number of adjacent rows in this table. - start_row: int or str, The number of the row at which the data begins. - insertion_mode: str - column_headers: dict of strings, maps the column letters (A, B, C) to - the desired name which will be viewable in the - worksheet. - auth_token: An object which sets the Authorization HTTP header in its - modify_request method. Recommended classes include - gdata.gauth.ClientLoginToken and gdata.gauth.AuthSubToken - among others. Represents the current user. Defaults to None - and if None, this method will look for a value in the - auth_token member of SpreadsheetsClient. - """ - data = gdata.spreadsheets.data.Data( - insertion_mode=insertion_mode, num_rows=str(num_rows), - start_row=str(start_row)) - for index, name in column_headers.iteritems(): - data.column.append(gdata.spreadsheets.data.Column( - index=index, name=name)) - new_table = gdata.spreadsheets.data.Table( - title=atom.data.Title(text=title), summary=atom.data.Summary(summary), - worksheet=gdata.spreadsheets.data.Worksheet(name=worksheet_name), - header=gdata.spreadsheets.data.Header(row=str(header_row)), data=data) - return self.post(new_table, TABLES_URL % spreadsheet_key, - auth_token=auth_token, **kwargs) - - AddTable = add_table - - def get_tables(self, spreadsheet_key, - desired_class=gdata.spreadsheets.data.TablesFeed, - auth_token=None, **kwargs): - """Retrieves a feed listing the tables in this spreadsheet. - - Args: - spreadsheet_key: str, The unique ID of this containing spreadsheet. This - can be the ID from the URL or as provided in a - Spreadsheet entry. - desired_class: class descended from atom.core.XmlElement to which a - successful response should be converted. If there is no - converter function specified (converter=None) then the - desired_class will be used in calling the - atom.core.parse function. If neither - the desired_class nor the converter is specified, an - HTTP reponse object will be returned. Defaults to - gdata.spreadsheets.data.TablesFeed. - auth_token: An object which sets the Authorization HTTP header in its - modify_request method. Recommended classes include - gdata.gauth.ClientLoginToken and gdata.gauth.AuthSubToken - among others. Represents the current user. Defaults to None - and if None, this method will look for a value in the - auth_token member of SpreadsheetsClient. - """ - return self.get_feed(TABLES_URL % spreadsheet_key, - desired_class=desired_class, auth_token=auth_token, - **kwargs) - - GetTables = get_tables - - def add_record(self, spreadsheet_key, table_id, fields, - title=None, auth_token=None, **kwargs): - """Adds a new row to the table. - - Args: - spreadsheet_key: str, The unique ID of this containing spreadsheet. This - can be the ID from the URL or as provided in a - Spreadsheet entry. - table_id: str, The ID of the table within the worksheet which should - receive this new record. The table ID can be found using the - get_table_id method of a gdata.spreadsheets.data.Table. - fields: dict of strings mapping column names to values. - title: str, optional The title for this row. - auth_token: An object which sets the Authorization HTTP header in its - modify_request method. Recommended classes include - gdata.gauth.ClientLoginToken and gdata.gauth.AuthSubToken - among others. Represents the current user. Defaults to None - and if None, this method will look for a value in the - auth_token member of SpreadsheetsClient. - """ - new_record = gdata.spreadsheets.data.Record() - if title is not None: - new_record.title = atom.data.Title(text=title) - for name, value in fields.iteritems(): - new_record.field.append(gdata.spreadsheets.data.Field( - name=name, text=value)) - return self.post(new_record, RECORDS_URL % (spreadsheet_key, table_id), - auth_token=auth_token, **kwargs) - - AddRecord = add_record - - def get_records(self, spreadsheet_key, table_id, - desired_class=gdata.spreadsheets.data.RecordsFeed, - auth_token=None, **kwargs): - """Retrieves the records in a table. - - Args: - spreadsheet_key: str, The unique ID of this containing spreadsheet. This - can be the ID from the URL or as provided in a - Spreadsheet entry. - table_id: str, The ID of the table within the worksheet whose records - we would like to fetch. The table ID can be found using the - get_table_id method of a gdata.spreadsheets.data.Table. - desired_class: class descended from atom.core.XmlElement to which a - successful response should be converted. If there is no - converter function specified (converter=None) then the - desired_class will be used in calling the - atom.core.parse function. If neither - the desired_class nor the converter is specified, an - HTTP reponse object will be returned. Defaults to - gdata.spreadsheets.data.RecordsFeed. - auth_token: An object which sets the Authorization HTTP header in its - modify_request method. Recommended classes include - gdata.gauth.ClientLoginToken and gdata.gauth.AuthSubToken - among others. Represents the current user. Defaults to None - and if None, this method will look for a value in the - auth_token member of SpreadsheetsClient. - """ - return self.get_feed(RECORDS_URL % (spreadsheet_key, table_id), - desired_class=desired_class, auth_token=auth_token, - **kwargs) - - GetRecords = get_records - - def get_record(self, spreadsheet_key, table_id, record_id, - desired_class=gdata.spreadsheets.data.Record, - auth_token=None, **kwargs): - """Retrieves a single record from the table. - - Args: - spreadsheet_key: str, The unique ID of this containing spreadsheet. This - can be the ID from the URL or as provided in a - Spreadsheet entry. - table_id: str, The ID of the table within the worksheet whose records - we would like to fetch. The table ID can be found using the - get_table_id method of a gdata.spreadsheets.data.Table. - record_id: str, The ID of the record within this table which we want to - fetch. You can find the record ID using get_record_id() on - an instance of the gdata.spreadsheets.data.Record class. - desired_class: class descended from atom.core.XmlElement to which a - successful response should be converted. If there is no - converter function specified (converter=None) then the - desired_class will be used in calling the - atom.core.parse function. If neither - the desired_class nor the converter is specified, an - HTTP reponse object will be returned. Defaults to - gdata.spreadsheets.data.RecordsFeed. - auth_token: An object which sets the Authorization HTTP header in its - modify_request method. Recommended classes include - gdata.gauth.ClientLoginToken and gdata.gauth.AuthSubToken - among others. Represents the current user. Defaults to None - and if None, this method will look for a value in the - auth_token member of SpreadsheetsClient. - """ - return self.get_entry(RECORD_URL % (spreadsheet_key, table_id, record_id), - desired_class=desired_class, auth_token=auth_token, - **kwargs) - - GetRecord = get_record - - def get_cells(self, spreadsheet_key, worksheet_id, - desired_class=gdata.spreadsheets.data.CellsFeed, - auth_token=None, **kwargs): - """Retrieves the cells which have values in this spreadsheet. - - Blank cells are not included. - - Args: - spreadsheet_key: str, The unique ID of this containing spreadsheet. This - can be the ID from the URL or as provided in a - Spreadsheet entry. - worksheet_id: str, The unique ID of the worksheet in this spreadsheet - whose cells we want. This can be obtained using - WorksheetEntry's get_worksheet_id method. - desired_class: class descended from atom.core.XmlElement to which a - successful response should be converted. If there is no - converter function specified (converter=None) then the - desired_class will be used in calling the - atom.core.parse function. If neither - the desired_class nor the converter is specified, an - HTTP reponse object will be returned. Defaults to - gdata.spreadsheets.data.CellsFeed. - auth_token: An object which sets the Authorization HTTP header in its - modify_request method. Recommended classes include - gdata.gauth.ClientLoginToken and gdata.gauth.AuthSubToken - among others. Represents the current user. Defaults to None - and if None, this method will look for a value in the - auth_token member of SpreadsheetsClient. - """ - return self.get_feed(CELLS_URL % (spreadsheet_key, worksheet_id), - auth_token=auth_token, desired_class=desired_class, - **kwargs) - - GetCells = get_cells - - def get_cell(self, spreadsheet_key, worksheet_id, row_num, col_num, - desired_class=gdata.spreadsheets.data.CellEntry, - auth_token=None, **kwargs): - """Retrieves a single cell from the worksheet. - - Indexes are 1 based so the first cell in the worksheet is 1, 1. - - Args: - spreadsheet_key: str, The unique ID of this containing spreadsheet. This - can be the ID from the URL or as provided in a - Spreadsheet entry. - worksheet_id: str, The unique ID of the worksheet in this spreadsheet - whose cells we want. This can be obtained using - WorksheetEntry's get_worksheet_id method. - row_num: int, The row of the cell that we want. Numbering starts with 1. - col_num: int, The column of the cell we want. Numbering starts with 1. - desired_class: class descended from atom.core.XmlElement to which a - successful response should be converted. If there is no - converter function specified (converter=None) then the - desired_class will be used in calling the - atom.core.parse function. If neither - the desired_class nor the converter is specified, an - HTTP reponse object will be returned. Defaults to - gdata.spreadsheets.data.CellEntry. - auth_token: An object which sets the Authorization HTTP header in its - modify_request method. Recommended classes include - gdata.gauth.ClientLoginToken and gdata.gauth.AuthSubToken - among others. Represents the current user. Defaults to None - and if None, this method will look for a value in the - auth_token member of SpreadsheetsClient. - """ - return self.get_entry( - CELL_URL % (spreadsheet_key, worksheet_id, row_num, col_num), - auth_token=auth_token, desired_class=desired_class, **kwargs) - - GetCell = get_cell - - def get_list_feed(self, spreadsheet_key, worksheet_id, - desired_class=gdata.spreadsheets.data.ListsFeed, - auth_token=None, **kwargs): - """Retrieves the value rows from the worksheet's list feed. - - The list feed is a view of the spreadsheet in which the first row is used - for column names and subsequent rows up to the first blank line are - records. - - Args: - spreadsheet_key: str, The unique ID of this containing spreadsheet. This - can be the ID from the URL or as provided in a - Spreadsheet entry. - worksheet_id: str, The unique ID of the worksheet in this spreadsheet - whose cells we want. This can be obtained using - WorksheetEntry's get_worksheet_id method. - desired_class: class descended from atom.core.XmlElement to which a - successful response should be converted. If there is no - converter function specified (converter=None) then the - desired_class will be used in calling the - atom.core.parse function. If neither - the desired_class nor the converter is specified, an - HTTP reponse object will be returned. Defaults to - gdata.spreadsheets.data.ListsFeed. - auth_token: An object which sets the Authorization HTTP header in its - modify_request method. Recommended classes include - gdata.gauth.ClientLoginToken and gdata.gauth.AuthSubToken - among others. Represents the current user. Defaults to None - and if None, this method will look for a value in the - auth_token member of SpreadsheetsClient. - """ - return self.get_feed(LISTS_URL % (spreadsheet_key, worksheet_id), - auth_token=auth_token, desired_class=desired_class, - **kwargs) - - GetListFeed = get_list_feed - - def add_list_entry(self, list_entry, spreadsheet_key, worksheet_id, - auth_token=None, **kwargs): - """Adds a new row to the worksheet's list feed. - - Args: - list_entry: gdata.spreadsheets.data.ListsEntry An entry which contains - the values which should be set for the columns in this - record. - spreadsheet_key: str, The unique ID of this containing spreadsheet. This - can be the ID from the URL or as provided in a - Spreadsheet entry. - worksheet_id: str, The unique ID of the worksheet in this spreadsheet - whose cells we want. This can be obtained using - WorksheetEntry's get_worksheet_id method. - auth_token: An object which sets the Authorization HTTP header in its - modify_request method. Recommended classes include - gdata.gauth.ClientLoginToken and gdata.gauth.AuthSubToken - among others. Represents the current user. Defaults to None - and if None, this method will look for a value in the - auth_token member of SpreadsheetsClient. - """ - return self.post(list_entry, LISTS_URL % (spreadsheet_key, worksheet_id), - auth_token=auth_token, **kwargs) - - AddListEntry = add_list_entry - - -class SpreadsheetQuery(gdata.client.Query): - - def __init__(self, title=None, title_exact=None, **kwargs): - """Adds Spreadsheets feed query parameters to a request. - - Args: - title: str Specifies the search terms for the title of a document. - This parameter used without title-exact will only submit partial - queries, not exact queries. - title_exact: str Specifies whether the title query should be taken as an - exact string. Meaningless without title. Possible values are - 'true' and 'false'. - """ - gdata.client.Query.__init__(self, **kwargs) - self.title = title - self.title_exact = title_exact - - def modify_request(self, http_request): - gdata.client._add_query_param('title', self.title, http_request) - gdata.client._add_query_param('title-exact', self.title_exact, - http_request) - gdata.client.Query.modify_request(self, http_request) - - ModifyRequest = modify_request - - -class WorksheetQuery(SpreadsheetQuery): - pass - - -class ListQuery(gdata.client.Query): - - def __init__(self, order_by=None, reverse=None, sq=None, **kwargs): - """Adds List-feed specific query parameters to a request. - - Args: - order_by: str Specifies what column to use in ordering the entries in - the feed. By position (the default): 'position' returns - rows in the order in which they appear in the GUI. Row 1, then - row 2, then row 3, and so on. By column: - 'column:columnName' sorts rows in ascending order based on the - values in the column with the given columnName, where - columnName is the value in the header row for that column. - reverse: str Specifies whether to sort in descending or ascending order. - Reverses default sort order: 'true' results in a descending - sort; 'false' (the default) results in an ascending sort. - sq: str Structured query on the full text in the worksheet. - [columnName][binaryOperator][value] - Supported binaryOperators are: - - (), for overriding order of operations - - = or ==, for strict equality - - <> or !=, for strict inequality - - and or &&, for boolean and - - or or ||, for boolean or - """ - gdata.client.Query.__init__(self, **kwargs) - self.order_by = order_by - self.reverse = reverse - self.sq = sq - - def modify_request(self, http_request): - gdata.client._add_query_param('orderby', self.order_by, http_request) - gdata.client._add_query_param('reverse', self.reverse, http_request) - gdata.client._add_query_param('sq', self.sq, http_request) - gdata.client.Query.modify_request(self, http_request) - - ModifyRequest = modify_request - - -class TableQuery(ListQuery): - pass - - -class CellQuery(gdata.client.Query): - - def __init__(self, min_row=None, max_row=None, min_col=None, max_col=None, - range=None, return_empty=None, **kwargs): - """Adds Cells-feed specific query parameters to a request. - - Args: - min_row: str or int Positional number of minimum row returned in query. - max_row: str or int Positional number of maximum row returned in query. - min_col: str or int Positional number of minimum column returned in query. - max_col: str or int Positional number of maximum column returned in query. - range: str A single cell or a range of cells. Use standard spreadsheet - cell-range notations, using a colon to separate start and end of - range. Examples: - - 'A1' and 'R1C1' both specify only cell A1. - - 'D1:F3' and 'R1C4:R3C6' both specify the rectangle of cells with - corners at D1 and F3. - return_empty: str If 'true' then empty cells will be returned in the feed. - If omitted, the default is 'false'. - """ - gdata.client.Query.__init__(self, **kwargs) - self.min_row = min_row - self.max_row = max_row - self.min_col = min_col - self.max_col = max_col - self.range = range - self.return_empty = return_empty - - def modify_request(self, http_request): - gdata.client._add_query_param('min-row', self.min_row, http_request) - gdata.client._add_query_param('max-row', self.max_row, http_request) - gdata.client._add_query_param('min-col', self.min_col, http_request) - gdata.client._add_query_param('max-col', self.max_col, http_request) - gdata.client._add_query_param('range', self.range, http_request) - gdata.client._add_query_param('return-empty', self.return_empty, - http_request) - gdata.client.Query.modify_request(self, http_request) - - ModifyRequest = modify_request diff --git a/gdata/analytics/spreadsheets/data.py b/gdata/analytics/spreadsheets/data.py deleted file mode 100644 index 93e67a8b4d..0000000000 --- a/gdata/analytics/spreadsheets/data.py +++ /dev/null @@ -1,346 +0,0 @@ -#!/usr/bin/env python -# -# Copyright (C) 2009 Google 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. - - -# This module is used for version 2 of the Google Data APIs. - - -"""Provides classes and constants for the XML in the Google Spreadsheets API. - -Documentation for the raw XML which these classes represent can be found here: -http://code.google.com/apis/spreadsheets/docs/3.0/reference.html#Elements -""" - - -__author__ = 'j.s@google.com (Jeff Scudder)' - - -import atom.core -import gdata.data - - -GS_TEMPLATE = '{http://schemas.google.com/spreadsheets/2006}%s' -GSX_NAMESPACE = 'http://schemas.google.com/spreadsheets/2006/extended' - - -INSERT_MODE = 'insert' -OVERWRITE_MODE = 'overwrite' - - -WORKSHEETS_REL = 'http://schemas.google.com/spreadsheets/2006#worksheetsfeed' - - -class Error(Exception): - pass - - -class FieldMissing(Exception): - pass - - -class HeaderNotSet(Error): - """The desired column header had no value for the row in the list feed.""" - - -class Cell(atom.core.XmlElement): - """The gs:cell element. - - A cell in the worksheet. The <gs:cell> element can appear only as a child - of <atom:entry>. - """ - _qname = GS_TEMPLATE % 'cell' - col = 'col' - input_value = 'inputValue' - numeric_value = 'numericValue' - row = 'row' - - -class ColCount(atom.core.XmlElement): - """The gs:colCount element. - - Indicates the number of columns in the worksheet, including columns that - contain only empty cells. The <gs:colCount> element can appear as a child - of <atom:entry> or <atom:feed> - """ - _qname = GS_TEMPLATE % 'colCount' - - -class Field(atom.core.XmlElement): - """The gs:field element. - - A field single cell within a record. Contained in an <atom:entry>. - """ - _qname = GS_TEMPLATE % 'field' - index = 'index' - name = 'name' - - -class Column(Field): - """The gs:column element.""" - _qname = GS_TEMPLATE % 'column' - - -class Data(atom.core.XmlElement): - """The gs:data element. - - A data region of a table. Contained in an <atom:entry> element. - """ - _qname = GS_TEMPLATE % 'data' - column = [Column] - insertion_mode = 'insertionMode' - num_rows = 'numRows' - start_row = 'startRow' - - -class Header(atom.core.XmlElement): - """The gs:header element. - - Indicates which row is the header row. Contained in an <atom:entry>. - """ - _qname = GS_TEMPLATE % 'header' - row = 'row' - - -class RowCount(atom.core.XmlElement): - """The gs:rowCount element. - - Indicates the number of total rows in the worksheet, including rows that - contain only empty cells. The <gs:rowCount> element can appear as a - child of <atom:entry> or <atom:feed>. - """ - _qname = GS_TEMPLATE % 'rowCount' - - -class Worksheet(atom.core.XmlElement): - """The gs:worksheet element. - - The worksheet where the table lives.Contained in an <atom:entry>. - """ - _qname = GS_TEMPLATE % 'worksheet' - name = 'name' - - -class Spreadsheet(gdata.data.GDEntry): - """An Atom entry which represents a Google Spreadsheet.""" - - def find_worksheets_feed(self): - return self.find_url(WORKSHEETS_REL) - - FindWorksheetsFeed = find_worksheets_feed - - def get_spreadsheet_key(self): - """Extracts the spreadsheet key unique to this spreadsheet.""" - return self.get_id().split('/')[-1] - - GetSpreadsheetKey = get_spreadsheet_key - - -class SpreadsheetsFeed(gdata.data.GDFeed): - """An Atom feed listing a user's Google Spreadsheets.""" - entry = [Spreadsheet] - - -class WorksheetEntry(gdata.data.GDEntry): - """An Atom entry representing a single worksheet in a spreadsheet.""" - row_count = RowCount - col_count = ColCount - - def get_worksheet_id(self): - """The worksheet ID identifies this worksheet in its spreadsheet.""" - return self.get_id().split('/')[-1] - - GetWorksheetId = get_worksheet_id - - -class WorksheetsFeed(gdata.data.GDFeed): - """A feed containing the worksheets in a single spreadsheet.""" - entry = [WorksheetEntry] - - -class Table(gdata.data.GDEntry): - """An Atom entry that represents a subsection of a worksheet. - - A table allows you to treat part or all of a worksheet somewhat like a - table in a database that is, as a set of structured data items. Tables - don't exist until you explicitly create them before you can use a table - feed, you have to explicitly define where the table data comes from. - """ - data = Data - header = Header - worksheet = Worksheet - - def get_table_id(self): - if self.id.text: - return self.id.text.split('/')[-1] - return None - - GetTableId = get_table_id - - -class TablesFeed(gdata.data.GDFeed): - """An Atom feed containing the tables defined within a worksheet.""" - entry = [Table] - - -class Record(gdata.data.GDEntry): - """An Atom entry representing a single record in a table. - - Note that the order of items in each record is the same as the order of - columns in the table definition, which may not match the order of - columns in the GUI. - """ - field = [Field] - - def value_for_index(self, column_index): - for field in self.field: - if field.index == column_index: - return field.text - raise FieldMissing('There is no field for %s' % column_index) - - ValueForIndex = value_for_index - - def value_for_name(self, name): - for field in self.field: - if field.name == name: - return field.text - raise FieldMissing('There is no field for %s' % name) - - ValueForName = value_for_name - - def get_record_id(self): - if self.id.text: - return self.id.text.split('/')[-1] - return None - - -class RecordsFeed(gdata.data.GDFeed): - """An Atom feed containing the individuals records in a table.""" - entry = [Record] - - -class ListRow(atom.core.XmlElement): - """A gsx column value within a row. - - The local tag in the _qname is blank and must be set to the column - name. For example, when adding to a ListEntry, do: - col_value = ListRow(text='something') - col_value._qname = col_value._qname % 'mycolumnname' - """ - _qname = '{http://schemas.google.com/spreadsheets/2006/extended}%s' - - -class ListEntry(gdata.data.GDEntry): - """An Atom entry representing a worksheet row in the list feed. - - The values for a particular column can be get and set using - x.get_value('columnheader') and x.set_value('columnheader', 'value'). - See also the explanation of column names in the ListFeed class. - """ - - def get_value(self, column_name): - """Returns the displayed text for the desired column in this row. - - The formula or input which generated the displayed value is not accessible - through the list feed, to see the user's input, use the cells feed. - - If a column is not present in this spreadsheet, or there is no value - for a column in this row, this method will return None. - """ - values = self.get_elements(column_name, GSX_NAMESPACE) - if len(values) == 0: - return None - return values[0].text - - def set_value(self, column_name, value): - """Changes the value of cell in this row under the desired column name. - - Warning: if the cell contained a formula, it will be wiped out by setting - the value using the list feed since the list feed only works with - displayed values. - - No client side checking is performed on the column_name, you need to - ensure that the column_name is the local tag name in the gsx tag for the - column. For example, the column_name will not contain special characters, - spaces, uppercase letters, etc. - """ - # Try to find the column in this row to change an existing value. - values = self.get_elements(column_name, GSX_NAMESPACE) - if len(values) > 0: - values[0].text = value - else: - # There is no value in this row for the desired column, so add a new - # gsx:column_name element. - new_value = ListRow(text=value) - new_value._qname = new_value._qname % (column_name,) - self._other_elements.append(new_value) - - def to_dict(self): - """Converts this row to a mapping of column names to their values.""" - result = {} - values = self.get_elements(namespace=GSX_NAMESPACE) - for item in values: - result[item._get_tag()] = item.text - return result - - def from_dict(self, values): - """Sets values for this row from the dictionary. - - Old values which are already in the entry will not be removed unless - they are overwritten with new values from the dict. - """ - for column, value in values.iteritems(): - self.set_value(column, value) - - -class ListsFeed(gdata.data.GDFeed): - """An Atom feed in which each entry represents a row in a worksheet. - - The first row in the worksheet is used as the column names for the values - in each row. If a header cell is empty, then a unique column ID is used - for the gsx element name. - - Spaces in a column name are removed from the name of the corresponding - gsx element. - - Caution: The columnNames are case-insensitive. For example, if you see - a <gsx:e-mail> element in a feed, you can't know whether the column - heading in the original worksheet was "e-mail" or "E-Mail". - - Note: If two or more columns have the same name, then subsequent columns - of the same name have _n appended to the columnName. For example, if the - first column name is "e-mail", followed by columns named "E-Mail" and - "E-mail", then the columnNames will be gsx:e-mail, gsx:e-mail_2, and - gsx:e-mail_3 respectively. - """ - entry = [ListEntry] - - -class CellEntry(gdata.data.BatchEntry): - """An Atom entry representing a single cell in a worksheet.""" - cell = Cell - - -class CellsFeed(gdata.data.BatchFeed): - """An Atom feed contains one entry per cell in a worksheet. - - The cell feed supports batch operations, you can send multiple cell - operations in one HTTP request. - """ - entry = [CellEntry] - - def batch_set_cell(row, col, input): - pass - diff --git a/gdata/analytics/test_config.py b/gdata/analytics/test_config.py deleted file mode 100644 index e07ce63e6b..0000000000 --- a/gdata/analytics/test_config.py +++ /dev/null @@ -1,434 +0,0 @@ -#!/usr/bin/env python - -# Copyright (C) 2009 Google 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. - - -import sys -import unittest -import getpass -import inspect -import atom.mock_http_core -import gdata.gauth - - -"""Loads configuration for tests which connect to Google servers. - -Settings used in tests are stored in a ConfigCollection instance in this -module called options. If your test needs to get a test related setting, -use - -import gdata.test_config -option_value = gdata.test_config.options.get_value('x') - -The above will check the command line for an '--x' argument, and if not -found will either use the default value for 'x' or prompt the user to enter -one. - -Your test can override the value specified by the user by performing: - -gdata.test_config.options.set_value('x', 'y') - -If your test uses a new option which you would like to allow the user to -specify on the command line or via a prompt, you can use the register_option -method as follows: - -gdata.test_config.options.register( - 'option_name', 'Prompt shown to the user', secret=False #As for password. - 'This is the description of the option, shown when help is requested.', - 'default value, provide only if you do not want the user to be prompted') -""" - - -class Option(object): - - def __init__(self, name, prompt, secret=False, description=None, default=None): - self.name = name - self.prompt = prompt - self.secret = secret - self.description = description - self.default = default - - def get(self): - value = self.default - # Check for a command line parameter. - for i in xrange(len(sys.argv)): - if sys.argv[i].startswith('--%s=' % self.name): - value = sys.argv[i].split('=')[1] - elif sys.argv[i] == '--%s' % self.name: - value = sys.argv[i + 1] - # If the param was not on the command line, ask the user to input the - # value. - # In order for this to prompt the user, the default value for the option - # must be None. - if value is None: - prompt = '%s: ' % self.prompt - if self.secret: - value = getpass.getpass(prompt) - else: - print 'You can specify this on the command line using --%s' % self.name - value = raw_input(prompt) - return value - - -class ConfigCollection(object): - - def __init__(self, options=None): - self.options = options or {} - self.values = {} - - def register_option(self, option): - self.options[option.name] = option - - def register(self, *args, **kwargs): - self.register_option(Option(*args, **kwargs)) - - def get_value(self, option_name): - if option_name in self.values: - return self.values[option_name] - value = self.options[option_name].get() - if value is not None: - self.values[option_name] = value - return value - - def set_value(self, option_name, value): - self.values[option_name] = value - - def render_usage(self): - message_parts = [] - for opt_name, option in self.options.iteritems(): - message_parts.append('--%s: %s' % (opt_name, option.description)) - return '\n'.join(message_parts) - - -options = ConfigCollection() - - -# Register the default options. -options.register( - 'username', - 'Please enter the email address of your test account', - description=('The email address you want to sign in with. ' - 'Make sure this is a test account as these tests may edit' - ' or delete data.')) -options.register( - 'password', - 'Please enter the password for your test account', - secret=True, description='The test account password.') -options.register( - 'clearcache', - 'Delete cached data? (enter true or false)', - description=('If set to true, any temporary files which cache test' - ' requests and responses will be deleted.'), - default='true') -options.register( - 'savecache', - 'Save requests and responses in a temporary file? (enter true or false)', - description=('If set to true, requests to the server and responses will' - ' be saved in temporary files.'), - default='false') -options.register( - 'runlive', - 'Run the live tests which contact the server? (enter true or false)', - description=('If set to true, the tests will make real HTTP requests to' - ' the servers. This slows down test execution and may' - ' modify the users data, be sure to use a test account.'), - default='true') -options.register( - 'ssl', - 'Run the live tests over SSL (enter true or false)', - description='If set to true, all tests will be performed over HTTPS (SSL)', - default='false') -options.register( - 'clean', - 'Clean ALL data first before and after each test (enter true or false)', - description='If set to true, all tests will remove all data (DANGEROUS)', - default='false') -options.register( - 'appsusername', - 'Please enter the email address of your test Apps domain account', - description=('The email address you want to sign in with. ' - 'Make sure this is a test account on your Apps domain as ' - 'these tests may edit or delete data.')) -options.register( - 'appspassword', - 'Please enter the password for your test Apps domain account', - secret=True, description='The test Apps account password.') - -# Other options which may be used if needed. -BLOG_ID_OPTION = Option( - 'blogid', - 'Please enter the ID of your test blog', - description=('The blog ID for the blog which should have test posts added' - ' to it. Example 7682659670455539811')) -TEST_IMAGE_LOCATION_OPTION = Option( - 'imgpath', - 'Please enter the full path to a test image to upload', - description=('This test image will be uploaded to a service which' - ' accepts a media file, it must be a jpeg.')) -SPREADSHEET_ID_OPTION = Option( - 'spreadsheetid', - 'Please enter the ID of a spreadsheet to use in these tests', - description=('The spreadsheet ID for the spreadsheet which should be' - ' modified by theses tests.')) -APPS_DOMAIN_OPTION = Option( - 'appsdomain', - 'Please enter your Google Apps domain', - description=('The domain the Google Apps is hosted on or leave blank' - ' if n/a')) -SITES_NAME_OPTION = Option( - 'sitename', - 'Please enter name of your Google Site', - description='The webspace name of the Site found in its URL.') -PROJECT_NAME_OPTION = Option( - 'project_name', - 'Please enter the name of your project hosting project', - description=('The name of the project which should have test issues added' - ' to it. Example gdata-python-client')) -ISSUE_ASSIGNEE_OPTION = Option( - 'issue_assignee', - 'Enter the email address of the target owner of the updated issue.', - description=('The email address of the user a created issue\'s owner will ' - ' become. Example testuser2@gmail.com')) -GA_TABLE_ID = Option( - 'table_id', - 'Enter the Table ID of the Google Analytics profile to test', - description=('The Table ID of the Google Analytics profile to test.' - ' Example ga:1174')) -TARGET_USERNAME_OPTION = Option( - 'targetusername', - 'Please enter the username (without domain) of the user which will be' - ' affected by the tests', - description=('The username of the user to be tested')) -YT_DEVELOPER_KEY_OPTION = Option( - 'developerkey', - 'Please enter your YouTube developer key', - description=('The YouTube developer key for your account')) -YT_CLIENT_ID_OPTION = Option( - 'clientid', - 'Please enter your YouTube client ID', - description=('The YouTube client ID for your account')) -YT_VIDEO_ID_OPTION= Option( - 'videoid', - 'Please enter the ID of a YouTube video you uploaded', - description=('The video ID of a YouTube video uploaded to your account')) - - -# Functions to inject a cachable HTTP client into a service client. -def configure_client(client, case_name, service_name, use_apps_auth=False): - """Sets up a mock client which will reuse a saved session. - - Should be called during setUp of each unit test. - - Handles authentication to allow the GDClient to make requests which - require an auth header. - - Args: - client: a gdata.GDClient whose http_client member should be replaced - with a atom.mock_http_core.MockHttpClient so that repeated - executions can used cached responses instead of contacting - the server. - case_name: str The name of the test case class. Examples: 'BloggerTest', - 'ContactsTest'. Used to save a session - for the ClientLogin auth token request, so the case_name - should be reused if and only if the same username, password, - and service are being used. - service_name: str The service name as used for ClientLogin to identify - the Google Data API being accessed. Example: 'blogger', - 'wise', etc. - use_apps_auth: bool (optional) If set to True, use appsusername and - appspassword command-line args instead of username and - password respectively. - """ - # Use a mock HTTP client which will record and replay the HTTP traffic - # from these tests. - client.http_client = atom.mock_http_core.MockHttpClient() - client.http_client.cache_case_name = case_name - # Getting the auth token only needs to be done once in the course of test - # runs. - auth_token_key = '%s_auth_token' % service_name - if (auth_token_key not in options.values - and options.get_value('runlive') == 'true'): - client.http_client.cache_test_name = 'client_login' - cache_name = client.http_client.get_cache_file_name() - if options.get_value('clearcache') == 'true': - client.http_client.delete_session(cache_name) - client.http_client.use_cached_session(cache_name) - if not use_apps_auth: - username = options.get_value('username') - password = options.get_value('password') - else: - username = options.get_value('appsusername') - password = options.get_value('appspassword') - auth_token = client.client_login(username, password, case_name, - service=service_name) - options.values[auth_token_key] = gdata.gauth.token_to_blob(auth_token) - if client.alt_auth_service is not None: - options.values[client.alt_auth_service] = gdata.gauth.token_to_blob( - client.alt_auth_token) - client.http_client.close_session() - # Allow a config auth_token of False to prevent the client's auth header - # from being modified. - if auth_token_key in options.values: - client.auth_token = gdata.gauth.token_from_blob( - options.values[auth_token_key]) - if client.alt_auth_service is not None: - client.alt_auth_token = gdata.gauth.token_from_blob( - options.values[client.alt_auth_service]) - - -def configure_cache(client, test_name): - """Loads or begins a cached session to record HTTP traffic. - - Should be called at the beginning of each test method. - - Args: - client: a gdata.GDClient whose http_client member has been replaced - with a atom.mock_http_core.MockHttpClient so that repeated - executions can used cached responses instead of contacting - the server. - test_name: str The name of this test method. Examples: - 'TestClass.test_x_works', 'TestClass.test_crud_operations'. - This is used to name the recording of the HTTP requests and - responses, so it should be unique to each test method in the - test case. - """ - # Auth token is obtained in configure_client which is called as part of - # setUp. - client.http_client.cache_test_name = test_name - cache_name = client.http_client.get_cache_file_name() - if options.get_value('clearcache') == 'true': - client.http_client.delete_session(cache_name) - client.http_client.use_cached_session(cache_name) - - -def close_client(client): - """Saves the recoded responses to a temp file if the config file allows. - - This should be called in the unit test's tearDown method. - - Checks to see if the 'savecache' option is set to 'true', to make sure we - only save sessions to repeat if the user desires. - """ - if client and options.get_value('savecache') == 'true': - # If this was a live request, save the recording. - client.http_client.close_session() - - -def configure_service(service, case_name, service_name): - """Sets up a mock GDataService v1 client to reuse recorded sessions. - - Should be called during setUp of each unit test. This is a duplicate of - configure_client, modified to handle old v1 service classes. - """ - service.http_client.v2_http_client = atom.mock_http_core.MockHttpClient() - service.http_client.v2_http_client.cache_case_name = case_name - # Getting the auth token only needs to be done once in the course of test - # runs. - auth_token_key = 'service_%s_auth_token' % service_name - if (auth_token_key not in options.values - and options.get_value('runlive') == 'true'): - service.http_client.v2_http_client.cache_test_name = 'client_login' - cache_name = service.http_client.v2_http_client.get_cache_file_name() - if options.get_value('clearcache') == 'true': - service.http_client.v2_http_client.delete_session(cache_name) - service.http_client.v2_http_client.use_cached_session(cache_name) - service.ClientLogin(options.get_value('username'), - options.get_value('password'), - service=service_name, source=case_name) - options.values[auth_token_key] = service.GetClientLoginToken() - service.http_client.v2_http_client.close_session() - if auth_token_key in options.values: - service.SetClientLoginToken(options.values[auth_token_key]) - - -def configure_service_cache(service, test_name): - """Loads or starts a session recording for a v1 Service object. - - Duplicates the behavior of configure_cache, but the target for this - function is a v1 Service object instead of a v2 Client. - """ - service.http_client.v2_http_client.cache_test_name = test_name - cache_name = service.http_client.v2_http_client.get_cache_file_name() - if options.get_value('clearcache') == 'true': - service.http_client.v2_http_client.delete_session(cache_name) - service.http_client.v2_http_client.use_cached_session(cache_name) - - -def close_service(service): - if service and options.get_value('savecache') == 'true': - # If this was a live request, save the recording. - service.http_client.v2_http_client.close_session() - - -def build_suite(classes): - """Creates a TestSuite for all unit test classes in the list. - - Assumes that each of the classes in the list has unit test methods which - begin with 'test'. Calls unittest.makeSuite. - - Returns: - A new unittest.TestSuite containing a test suite for all classes. - """ - suites = [unittest.makeSuite(a_class, 'test') for a_class in classes] - return unittest.TestSuite(suites) - - -def check_data_classes(test, classes): - import inspect - for data_class in classes: - test.assert_(data_class.__doc__ is not None, - 'The class %s should have a docstring' % data_class) - if hasattr(data_class, '_qname'): - qname_versions = None - if isinstance(data_class._qname, tuple): - qname_versions = data_class._qname - else: - qname_versions = (data_class._qname,) - for versioned_qname in qname_versions: - test.assert_(isinstance(versioned_qname, str), - 'The class %s has a non-string _qname' % data_class) - test.assert_(not versioned_qname.endswith('}'), - 'The _qname for class %s is only a namespace' % ( - data_class)) - - for attribute_name, value in data_class.__dict__.iteritems(): - # Ignore all elements that start with _ (private members) - if not attribute_name.startswith('_'): - try: - if not (isinstance(value, str) or inspect.isfunction(value) - or (isinstance(value, list) - and issubclass(value[0], atom.core.XmlElement)) - or type(value) == property # Allow properties. - or inspect.ismethod(value) # Allow methods. - or inspect.ismethoddescriptor(value) # Allow method descriptors. - # staticmethod et al. - or issubclass(value, atom.core.XmlElement)): - test.fail( - 'XmlElement member should have an attribute, XML class,' - ' or list of XML classes as attributes.') - - except TypeError: - test.fail('Element %s in %s was of type %s' % ( - attribute_name, data_class._qname, type(value))) - - -def check_clients_with_auth(test, classes): - for client_class in classes: - test.assert_(hasattr(client_class, 'api_version')) - test.assert_(isinstance(client_class.auth_service, (str, unicode, int))) - test.assert_(hasattr(client_class, 'auth_service')) - test.assert_(isinstance(client_class.auth_service, (str, unicode))) - test.assert_(hasattr(client_class, 'auth_scopes')) - test.assert_(isinstance(client_class.auth_scopes, (list, tuple))) diff --git a/gdata/analytics/test_data.py b/gdata/analytics/test_data.py deleted file mode 100644 index d75a5c96f9..0000000000 --- a/gdata/analytics/test_data.py +++ /dev/null @@ -1,5616 +0,0 @@ -#!/usr/bin/python -# -# Copyright (C) 2006 Google 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. - - - -XML_ENTRY_1 = """<?xml version='1.0'?> -<entry xmlns='http://www.w3.org/2005/Atom' - xmlns:g='http://base.google.com/ns/1.0'> - <category scheme="http://base.google.com/categories/itemtypes" - term="products"/> - <id> http://www.google.com/test/id/url </id> - <title type='text'>Testing 2000 series laptop - -
A Testing Laptop
-
- - - Computer - Laptop - testing laptop - products -
""" - - -TEST_BASE_ENTRY = """ - - - Testing 2000 series laptop - -
A Testing Laptop
-
- - yes - - - - Computer - Laptop - testing laptop - products -
""" - - -BIG_FEED = """ - - dive into mark - - A <em>lot</em> of effort - went into making this effortless - - 2005-07-31T12:29:29Z - tag:example.org,2003:3 - - - Copyright (c) 2003, Mark Pilgrim - - Example Toolkit - - - Atom draft-07 snapshot - - - tag:example.org,2003:3.2397 - 2005-07-31T12:29:29Z - 2003-12-13T08:29:29-04:00 - - Mark Pilgrim - http://example.org/ - f8dy@example.com - - - Sam Ruby - - - Joe Gregorio - - -
-

[Update: The Atom draft is finished.]

-
-
-
-
-""" - -SMALL_FEED = """ - - Example Feed - - 2003-12-13T18:30:02Z - - John Doe - - urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6 - - Atom-Powered Robots Run Amok - - urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a - 2003-12-13T18:30:02Z - Some text. - - -""" - -GBASE_FEED = """ - -http://www.google.com/base/feeds/snippets -2007-02-08T23:18:21.935Z -Items matching query: digital camera - - - - - - - - -GoogleBase -2171885 -1 -25 - -http://www.google.com/base/feeds/snippets/13246453826751927533 -2007-02-08T13:23:27.000Z -2007-02-08T16:40:57.000Z - - -Digital Camera Battery Notebook Computer 12v DC Power Cable - 5.5mm x 2.5mm (Center +) Camera Connecting Cables -Notebook Computer 12v DC Power Cable - 5.5mm x 2.1mm (Center +) This connection cable will allow any Digital Pursuits battery pack to power portable computers that operate with 12v power and have a 2.1mm power connector (center +) Digital ... - - - - - -B&H Photo-Video -anon-szot0wdsq0at@base.google.com - -PayPal & Bill Me Later credit available online only. -new -420 9th Ave. 10001 -305668-REG -Products -Digital Camera Battery -2007-03-10T13:23:27.000Z -1172711 -34.95 usd -Digital Photography>Camera Connecting Cables -EN -DCB5092 -US -1.0 -http://base.google.com/base_image?q=http%3A%2F%2Fwww.bhphotovideo.com%2Fimages%2Fitems%2F305668.jpg&dhm=ffffffff84c9a95e&size=6 - - -http://www.google.com/base/feeds/snippets/10145771037331858608 -2007-02-08T13:23:27.000Z -2007-02-08T16:40:57.000Z - - -Digital Camera Battery Electronic Device 5v DC Power Cable - 5.5mm x 2.5mm (Center +) Camera Connecting Cables -Electronic Device 5v DC Power Cable - 5.5mm x 2.5mm (Center +) This connection cable will allow any Digital Pursuits battery pack to power any electronic device that operates with 5v power and has a 2.5mm power connector (center +) Digital ... - - - - - -B&H Photo-Video -anon-szot0wdsq0at@base.google.com - -420 9th Ave. 10001 -new -0.18 -US -Digital Photography>Camera Connecting Cables -PayPal & Bill Me Later credit available online only. -305656-REG -http://base.google.com/base_image?q=http%3A%2F%2Fwww.bhphotovideo.com%2Fimages%2Fitems%2F305656.jpg&dhm=7315bdc8&size=6 -DCB5108 -838098005108 -34.95 usd -EN -Digital Camera Battery -1172711 -Products -2007-03-10T13:23:27.000Z - - -http://www.google.com/base/feeds/snippets/3128608193804768644 -2007-02-08T02:21:27.000Z -2007-02-08T15:40:13.000Z - - -Digital Camera Battery Power Cable for Kodak 645 Pro-Back ProBack & DCS-300 Series Camera Connecting Cables -Camera Connection Cable - to Power Kodak 645 Pro-Back DCS-300 Series Digital Cameras This connection cable will allow any Digital Pursuits battery pack to power the following digital cameras: Kodak DCS Pro Back 645 DCS-300 series Digital Photography ... - - - - - -B&H Photo-Video -anon-szot0wdsq0at@base.google.com - -0.3 -DCB6006 -http://base.google.com/base_image?q=http%3A%2F%2Fwww.bhphotovideo.com%2Fimages%2Fitems%2F305685.jpg&dhm=72f0ca0a&size=6 -420 9th Ave. 10001 -PayPal & Bill Me Later credit available online only. -Products -US -digital kodak camera -Digital Camera Battery -2007-03-10T02:21:27.000Z -EN -new -34.95 usd -1172711 -Digital Photography>Camera Connecting Cables -305685-REG - -""" - -EXTENSION_TREE = """ - - - John Doe - Bar - - - -""" - -TEST_AUTHOR = """ - - John Doe - johndoes@someemailadress.com - http://www.google.com - -""" - -TEST_LINK = """ - -""" - -TEST_GBASE_ATTRIBUTE = """ - Digital Camera Battery -""" - - -CALENDAR_FEED = """ - - http://www.google.com/calendar/feeds/default - 2007-03-20T22:48:57.833Z - GData Ops Demo's Calendar List - - - - - GData Ops Demo - gdata.ops.demo@gmail.com - - - Google Calendar - 1 - - - http://www.google.com/calendar/feeds/default/gdata.ops.demo%40gmail.com - 2007-03-20T22:48:57.837Z - 2007-03-20T22:48:52.000Z - GData Ops Demo - - - - - - GData Ops Demo - gdata.ops.demo@gmail.com - - - - - - - - - http://www.google.com/calendar/feeds/default/jnh21ovnjgfph21h32gvms2758%40group.calendar.google.com - 2007-03-20T22:48:57.837Z - 2007-03-20T22:48:53.000Z - GData Ops Demo Secondary Calendar - - - - - - - GData Ops Demo Secondary Calendar - - - - - - - - -""" - -CALENDAR_FULL_EVENT_FEED = """ - - - http://www.google.com/calendar/feeds/default/private/full - 2007-03-20T21:29:57.000Z - - GData Ops Demo - GData Ops Demo - - - - - - - - GData Ops Demo - gdata.ops.demo@gmail.com - - - Google Calendar - 10 - 1 - 25 - - - - http://www.google.com/calendar/feeds/default/private/full/o99flmgmkfkfrr8u745ghr3100 - 2007-03-20T21:29:52.000Z - 2007-03-20T21:29:57.000Z - - test deleted - - - - - - - - GData Ops Demo - gdata.ops.demo@gmail.com - - - - - - - - - - - - - - - - - - - - - http://www.google.com/calendar/feeds/default/private/full/2qt3ao5hbaq7m9igr5ak9esjo0 - 2007-03-20T21:26:04.000Z - 2007-03-20T21:28:46.000Z - - Afternoon at Dolores Park with Kim - - - - - - - - GData Ops Demo - gdata.ops.demo@gmail.com - - - - - - - - - - - - - - - - - - - - - - - - - - - - - http://www.google.com/calendar/feeds/default/private/full/uvsqhg7klnae40v50vihr1pvos - 2007-03-20T21:28:37.000Z - 2007-03-20T21:28:37.000Z - - Team meeting - - - - - - - - GData Ops Demo - gdata.ops.demo@gmail.com - - DTSTART;TZID=America/Los_Angeles:20070323T090000 - DTEND;TZID=America/Los_Angeles:20070323T100000 - RRULE:FREQ=WEEKLY;BYDAY=FR;UNTIL=20070817T160000Z;WKST=SU - BEGIN:VTIMEZONE TZID:America/Los_Angeles - X-LIC-LOCATION:America/Los_Angeles BEGIN:STANDARD - TZOFFSETFROM:-0700 TZOFFSETTO:-0800 TZNAME:PST - DTSTART:19701025T020000 RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU - END:STANDARD BEGIN:DAYLIGHT TZOFFSETFROM:-0800 TZOFFSETTO:-0700 - TZNAME:PDT DTSTART:19700405T020000 - RRULE:FREQ=YEARLY;BYMONTH=4;BYDAY=1SU END:DAYLIGHT - END:VTIMEZONE - - - - - - - - - - - - - - http://www.google.com/calendar/feeds/default/private/full/st4vk9kiffs6rasrl32e4a7alo - 2007-03-20T21:25:46.000Z - 2007-03-20T21:25:46.000Z - - Movie with Kim and danah - - - - - - - - GData Ops Demo - gdata.ops.demo@gmail.com - - - - - - - - - - - - - - - - - - - - - http://www.google.com/calendar/feeds/default/private/full/ofl1e45ubtsoh6gtu127cls2oo - 2007-03-20T21:24:43.000Z - 2007-03-20T21:25:08.000Z - - Dinner with Kim and Sarah - - - - - - - - GData Ops Demo - gdata.ops.demo@gmail.com - - - - - - - - - - - - - - - - - - - - - http://www.google.com/calendar/feeds/default/private/full/b69s2avfi2joigsclecvjlc91g - 2007-03-20T21:24:19.000Z - 2007-03-20T21:25:05.000Z - - Dinner with Jane and John - - - - - - - - GData Ops Demo - gdata.ops.demo@gmail.com - - - - - - - - - - - - - - - - - - - - - http://www.google.com/calendar/feeds/default/private/full/u9p66kkiotn8bqh9k7j4rcnjjc - 2007-03-20T21:24:33.000Z - 2007-03-20T21:24:33.000Z - - Tennis with Elizabeth - - - - - - - - GData Ops Demo - gdata.ops.demo@gmail.com - - - - - - - - - - - - - - - - - - - - - http://www.google.com/calendar/feeds/default/private/full/76oj2kceidob3s708tvfnuaq3c - 2007-03-20T21:24:00.000Z - 2007-03-20T21:24:00.000Z - - Lunch with Jenn - - - - - - - - GData Ops Demo - gdata.ops.demo@gmail.com - - - - - - - - - - - - - - - - - - - - - http://www.google.com/calendar/feeds/default/private/full/5np9ec8m7uoauk1vedh5mhodco - 2007-03-20T07:50:02.000Z - 2007-03-20T20:39:26.000Z - - test entry - test desc - - - - - - - GData Ops Demo - gdata.ops.demo@gmail.com - - - - - - - - - - - - - - - - - - - - - - - - - - - - - http://www.google.com/calendar/feeds/default/private/full/fu6sl0rqakf3o0a13oo1i1a1mg - 2007-02-14T23:23:37.000Z - 2007-02-14T23:25:30.000Z - - test - - - - - - - - - - - GData Ops Demo - gdata.ops.demo@gmail.com - - - - - - - - - - - - - - - - - - - - - http://www.google.com/calendar/feeds/default/private/full/h7a0haa4da8sil3rr19ia6luvc - 2007-07-16T22:13:28.000Z - 2007-07-16T22:13:29.000Z - - - - - - - - - - - - - GData Ops Demo - gdata.ops.demo@gmail.com - - - - - - - - - - - - - -""" - -CALENDAR_BATCH_REQUEST = """ - - - - 1 - - - Event inserted via batch - - - 2 - - http://www.google.com/calendar/feeds/default/private/full/glcs0kv2qqa0gf52qi1jo018gc - - Event queried via batch - - - 3 - - http://www.google.com/calendar/feeds/default/private/full/ujm0go5dtngdkr6u91dcqvj0qs - - Event updated via batch - - - - - - 4 - - http://www.google.com/calendar/feeds/default/private/full/d8qbg9egk1n6lhsgq1sjbqffqc - - Event deleted via batch - - - - - -""" - -CALENDAR_BATCH_RESPONSE = """ - - http://www.google.com/calendar/feeds/default/private/full - 2007-09-21T23:01:00.380Z - - Batch Feed - - - - - 1 - - - http://www.google.com/calendar/feeds/default/private/full/n9ug78gd9tv53ppn4hdjvk68ek - - Event inserted via batch - - - - - - 2 - - - http://www.google.com/calendar/feeds/default/private/full/glsc0kv2aqa0ff52qi1jo018gc - - Event queried via batch - - - - - - 3 - - - http://www.google.com/calendar/feeds/default/private/full/ujm0go5dtngdkr6u91dcqvj0qs - - Event updated via batch - - - - 3 - - - - - 4 - - - http://www.google.com/calendar/feeds/default/private/full/d8qbg9egk1n6lhsgq1sjbqffqc - - Event deleted via batch - Deleted - - -""" - -GBASE_ATTRIBUTE_FEED = """ - - http://www.google.com/base/feeds/attributes - 2006-11-01T20:35:59.578Z - - - Attribute histogram for query: [item type:jobs] - - - - GoogleBase - 16 - 1 - 16 - - http://www.google.com/base/feeds/attributes/job+industry%28text%29N%5Bitem+type%3Ajobs%5D - 2006-11-01T20:36:00.100Z - job industry(text) - Attribute"job industry" of type text. - - - - it internet - healthcare - information technology - accounting - clerical and administrative - other - sales and sales management - information systems - engineering and architecture - sales - - - -""" - - -GBASE_ATTRIBUTE_ENTRY = """ - - http://www.google.com/base/feeds/attributes/job+industry%28text%29N%5Bitem+type%3Ajobs%5D - 2006-11-01T20:36:00.100Z - job industry(text) - Attribute"job industry" of type text. - - - - it internet - healthcare - information technology - accounting - clerical and administrative - other - sales and sales management - information systems - engineering and architecture - sales - - -""" - -GBASE_LOCALES_FEED = """ - - http://www.google.com/base/feeds/locales/ - 2006-06-13T18:11:40.120Z - Locales - - - - - Google Inc. - base@google.com - - GoogleBase - 3 - 25 - - - http://www.google.com/base/feeds/locales/en_US - 2006-03-27T22:27:36.658Z - - - en_US - en_US - - - - - - http://www.google.com/base/feeds/locales/en_GB - 2006-06-13T18:14:18.601Z - - en_GB - en_GB - - - - - http://www.google.com/base/feeds/locales/de_DE - 2006-06-13T18:14:18.601Z - - de_DE - de_DE - - - -""" - -GBASE_STRING_ENCODING_ENTRY = """ - - http://www.google.com/base/feeds/snippets/17495780256183230088 - 2007-12-09T03:13:07.000Z - 2008-01-07T03:26:46.000Z - - Digital Camera Cord Fits SONY Cybershot DSC-R1 S40 - SONY \xC2\xB7 Cybershot Digital Camera Usb Cable DESCRIPTION - This is a 2.5 USB 2.0 A to Mini B (5 Pin) high quality digital camera - cable used for connecting your Sony Digital Cameras and Camcoders. Backward - Compatible with USB 2.0, 1.0 and 1.1. Fully ... - - - - eBay - - Products - EN - US - 0.99 usd - http://thumbs.ebaystatic.com/pict/270195049057_1.jpg - Cameras & Photo>Digital Camera Accessories>Cables - Cords & Connectors>USB Cables>For Other Brands - 11729 - 270195049057 - 2008-02-06T03:26:46Z -""" - - -RECURRENCE_EXCEPTION_ENTRY = """ - - http://www.google.com/calendar/feeds/default/private/composite/i7lgfj69mjqjgnodklif3vbm7g - 2007-04-05T21:51:49.000Z - 2007-04-05T21:51:49.000Z - - testDavid - - - - - - gdata ops - gdata.ops.test@gmail.com - - - - - - - - - - DTSTART;TZID=America/Anchorage:20070403T100000 - DTEND;TZID=America/Anchorage:20070403T110000 - RRULE:FREQ=DAILY;UNTIL=20070408T180000Z;WKST=SU - EXDATE;TZID=America/Anchorage:20070407T100000 - EXDATE;TZID=America/Anchorage:20070405T100000 - EXDATE;TZID=America/Anchorage:20070404T100000 BEGIN:VTIMEZONE - TZID:America/Anchorage X-LIC-LOCATION:America/Anchorage - BEGIN:STANDARD TZOFFSETFROM:-0800 TZOFFSETTO:-0900 TZNAME:AKST - DTSTART:19701025T020000 RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU - END:STANDARD BEGIN:DAYLIGHT TZOFFSETFROM:-0900 TZOFFSETTO:-0800 - TZNAME:AKDT DTSTART:19700405T020000 - RRULE:FREQ=YEARLY;BYMONTH=4;BYDAY=1SU END:DAYLIGHT - END:VTIMEZONE - - - - - - i7lgfj69mjqjgnodklif3vbm7g_20070407T180000Z - 2007-04-05T21:51:49.000Z - 2007-04-05T21:52:58.000Z - - testDavid - - - - gdata ops - gdata.ops.test@gmail.com - - - - - - - - - - - - - - - - - - - 2007-04-05T21:54:09.285Z - - - Comments for: testDavid - - - - - - - - - - - - """ - -NICK_ENTRY = """ - - https://apps-apis.google.com/a/feeds/example.com/nickname/2.0/Foo - 1970-01-01T00:00:00.000Z - - Foo - - - - -""" - -NICK_FEED = """ - - - http://apps-apis.google.com/a/feeds/example.com/nickname/2.0 - - 1970-01-01T00:00:00.000Z - - Nicknames for user SusanJones - - - - 1 - 2 - - - http://apps-apis.google.com/a/feeds/example.com/nickname/2.0/Foo - - - Foo - - - - - - - - http://apps-apis.google.com/a/feeds/example.com/nickname/2.0/suse - - - suse - - - - - -""" - -USER_ENTRY = """ - - https://apps-apis.google.com/a/feeds/example.com/user/2.0/TestUser - 1970-01-01T00:00:00.000Z - - TestUser - - - - - - - -""" - -USER_FEED = """ - - - http://apps-apis.google.com/a/feeds/example.com/user/2.0 - - 1970-01-01T00:00:00.000Z - - Users - """ - -EMAIL_LIST_ENTRY = """ - - - https://apps-apis.google.com/a/feeds/example.com/emailList/2.0/testlist - - 1970-01-01T00:00:00.000Z - - testlist - - - - -""" - -EMAIL_LIST_FEED = """ - - - http://apps-apis.google.com/a/feeds/example.com/emailList/2.0 - - 1970-01-01T00:00:00.000Z - - EmailLists - """ - -EMAIL_LIST_RECIPIENT_ENTRY = """ - - https://apps-apis.google.com/a/feeds/example.com/emailList/2.0/us-sales/recipient/TestUser%40example.com - 1970-01-01T00:00:00.000Z - - TestUser - - - -""" - -EMAIL_LIST_RECIPIENT_FEED = """ - - - http://apps-apis.google.com/a/feeds/example.com/emailList/2.0/us-sales/recipient - - 1970-01-01T00:00:00.000Z - - Recipients for email list us-sales - """ - -ACL_FEED = """ - - http://www.google.com/calendar/feeds/liz%40gmail.com/acl/full - 2007-04-21T00:52:04.000Z - Elizabeth Bennet's access control list - - - - - - - - - Google Calendar - 2 - 1 - - http://www.google.com/calendar/feeds/liz%40gmail.com/acl/full/user%3Aliz%40gmail.com - 2007-04-21T00:52:04.000Z - - - owner - - - - - - - Elizabeth Bennet - liz@gmail.com - - - - - - - http://www.google.com/calendar/feeds/liz%40gmail.com/acl/full/default - 2007-04-21T00:52:04.000Z - - - read - - - - - - - Elizabeth Bennet - liz@gmail.com - - - - - - """ - -ACL_ENTRY = """ - - http://www.google.com/calendar/feeds/liz%40gmail.com/acl/full/user%3Aliz%40gmail.com - 2007-04-21T00:52:04.000Z - - - owner - - - - - - - Elizabeth Bennet - liz@gmail.com - - - - - """ - -DOCUMENT_LIST_FEED = """ -21test.usertest.user@gmail.comhttps://docs.google.com/feeds/documents/private/full/spreadsheet%3AsupercalifragilisticexpeadociousTest Spreadsheet2007-07-03T18:03:32.045Z - -document:dfrkj84g_3348jbxpxcd - - test.user - test.user@gmail.com - -2009-03-05T07:48:21.493Z - -test.usertest.user@gmail.comhttp://docs.google.com/feeds/documents/private/full/document%3Agr00vyTest Document2007-07-03T18:02:50.338Z - - - test.user - test.user@gmail.com - - - 2009-03-05T07:48:21.493Z -http://docs.google.com/feeds/documents/private/fullAvailable -Documents - -test.user@gmail.com2007-07-09T23:07:21.898Z - -""" - -DOCUMENT_LIST_ENTRY = """ - -test.usertest.user@gmail.com -https://docs.google.com/feeds/documents/private/full/spreadsheet%3Asupercalifragilisticexpealidocious - -Test Spreadsheet2007-07-03T18:03:32.045Z -spreadsheet:supercalifragilisticexpealidocious - - test.user - test.user@gmail.com - -2009-03-05T07:48:21.493Z - - -""" - -DOCUMENT_LIST_ENTRY_V3 = """ - -test.usertest.user@gmail.com -https://docs.google.com/feeds/documents/private/full/spreadsheet%3Asupercalifragilisticexpealidocious - - -Test Spreadsheet2007-07-03T18:03:32.045Z -spreadsheet:supercalifragilisticexpealidocious - - test.user - test.user@gmail.com - -2009-03-05T07:48:21.493Z - -1000 - - - - - - - - - - - -""" - -DOCUMENT_LIST_ACL_ENTRY = """ - - - - -""" - -DOCUMENT_LIST_ACL_WITHKEY_ENTRY = """ - - - - -""" - -DOCUMENT_LIST_ACL_FEED = """ - -http://docs.google.com/feeds/acl/private/full/spreadsheet%3ApFrmMi8feTQYCgZpwUQ -2009-02-22T03:48:25.895Z - -Document Permissions - - - - -2 -1 - - http://docs.google.com/feeds/acl/private/full/spreadsheet%3ApFrmMi8feTQp4pwUwUQ/user%3Auser%40gmail.com - 2009-02-22T03:48:25.896Z - - Document Permission - user@gmail.com - - - - - - - http://docs.google.com/feeds/acl/private/full/spreadsheet%3ApFrmMi8fCgZp4pwUwUQ/user%3Auser2%40google.com - 2009-02-22T03:48:26.257Z - - Document Permission - user2@google.com - - - - - -""" - -DOCUMENT_LIST_REVISION_FEED = """ - -https://docs.google.com/feeds/default/private/full/resource_id/revisions -2009-08-17T04:22:10.378Z -Document Revisions - - - -6 -1 - - https://docs.google.com/feeds/id/resource_id/revisions/2 - 2009-08-17T04:22:10.440Z - 2009-08-14T07:11:34.197Z - Revision 2 - - - - - - another_user - another_user@gmail.com - - - - - - -""" - -BATCH_ENTRY = """ - - http://www.google.com/base/feeds/items/2173859253842813008 - 2006-07-11T14:51:43.560Z - 2006-07-11T14:51: 43.560Z - title - content - - - recipes - - itemB - -""" - -BATCH_FEED_REQUEST = """ - - My Batch Feed - - http://www.google.com/base/feeds/items/13308004346459454600 - - - - http://www.google.com/base/feeds/items/17437536661927313949 - - - - ... - ... - itemA - - recipes - - - ... - ... - itemB - - recipes - -""" - -BATCH_FEED_RESULT = """ - - http://www.google.com/base/feeds/items - 2006-07-11T14:51:42.894Z - My Batch - - - - - http://www.google.com/base/feeds/items/2173859253842813008 - 2006-07-11T14:51:43.560Z - 2006-07-11T14:51: 43.560Z - ... - ... - - - recipes - - itemB - - - - http://www.google.com/base/feeds/items/11974645606383737963 - 2006-07-11T14:51:43.247Z - 2006-07-11T14:51: 43.247Z - ... - ... - - - recipes - - itemA - - - - http://www.google.com/base/feeds/items/13308004346459454600 - 2006-07-11T14:51:42.894Z - Error - Bad request - - - - - - - - http://www.google.com/base/feeds/items/17437536661927313949 - 2006-07-11T14:51:43.246Z - Deleted - - - -""" - -ALBUM_FEED = """ - - http://picasaweb.google.com/data/feed/api/user/sample.user/albumid/1 - 2007-09-21T18:23:05.000Z - - Test - - public - http://lh6.google.com/sample.user/Rt8WNoDZEJE/AAAAAAAAABk/HQGlDhpIgWo/s160-c/Test.jpg - - - - - - sample - http://picasaweb.google.com/sample.user - - Picasaweb 4 - 1 - 500 - 1 - Test - - public 1188975600000 - 2 - sample.user - sample - true - 0 - http://picasaweb.google.com/data/entry/api/user/sample.user/albumid/1/photoid/2 - 2007-09-05T20:49:23.000Z - 2007-09-21T18:23:05.000Z - - Aqua Blue.jpg - Blue - - - - 2 - 1190398985145172 - 0.0 - 1 2560 - 1600 - 883405 - - - 1189025362000 - true - c041ce17aaa637eb656c81d9cf526c24 - - true - 1 - - Aqua Blue.jpg Blue - tag, test - - - - - sample - - - - http://picasaweb.google.com/data/entry/api/user/sample.user/albumid/1/photoid/3 - 2007-09-05T20:49:24.000Z - 2007-09-21T18:19:38.000Z - - Aqua Graphite.jpg - Gray - - - - - 3 - 1190398778006402 - 1.0 - 1 - 2560 - 1600 - 798334 - - - 1189025363000 - - true - a5ce2e36b9df7d3cb081511c72e73926 - - true - 0 - - Aqua Graphite.jpg - Gray - - - - - - sample - - - - http://picasaweb.google.com/data/entry/api/user/sample.user/albumid/1/tag/tag - 2007-09-05T20:49:24.000Z - - tag - tag - - - - sample - http://picasaweb.google.com/sample.user - - - - http://picasaweb.google.com/data/entry/api/user/sample.user/albumid/1/tag/test - 2007-09-05T20:49:24.000Z - - test - test - - - - sample - http://picasaweb.google.com/sample.user - - -""" - -CODE_SEARCH_FEED = """ - -http://www.google.com/codesearch/feeds/search?q=malloc -2007-12-19T16:08:04Z -Google Code Search -Google Code Search -2530000 -1 - -Google Code Search - -http://www.google.com/codesearch - - - - - -http://www.google.com/codesearch?hl=en&q=+malloc+show:LDjwp-Iqc7U:84hEYaYsZk8:xDGReDhvNi0&sa=N&ct=rx&cd=1&cs_p=http://www.gnu.org&cs_f=software/autoconf/manual/autoconf-2.60/autoconf.html-002&cs_p=http://www.gnu.org&cs_f=software/autoconf/manual/autoconf-2.60/autoconf.html-002#first2007-12-19T16:08:04ZCode owned by external author.software/autoconf/manual/autoconf-2.60/autoconf.html<pre> 8: void *<b>malloc</b> (); - - -</pre><pre> #undef <b>malloc</b> -</pre><pre> void *<b>malloc</b> (); - -</pre><pre> rpl_<b>malloc</b> (size_t n) -</pre><pre> return <b>malloc</b> (n); - -</pre> -http://www.google.com/codesearch?hl=en&q=+malloc+show:h4hfh-fV-jI:niBq_bwWZNs:H0OhClf0HWQ&sa=N&ct=rx&cd=2&cs_p=ftp://ftp.gnu.org/gnu/guile/guile-1.6.8.tar.gz&cs_f=guile-1.6.8/libguile/mallocs.c&cs_p=ftp://ftp.gnu.org/gnu/guile/guile-1.6.8.tar.gz&cs_f=guile-1.6.8/libguile/mallocs.c#first2007-12-19T16:08:04ZCode owned by external author.guile-1.6.8/libguile/mallocs.c<pre> 86: { - scm_t_bits mem = n ? (scm_t_bits) <b>malloc</b> (n) : 0; - if (n &amp;&amp; !mem) - -</pre><pre>#include &lt;<b>malloc</b>.h&gt; -</pre><pre>scm_t_bits scm_tc16_<b>malloc</b>; - -</pre><pre><b>malloc</b>_free (SCM ptr) -</pre><pre><b>malloc</b>_print (SCM exp, SCM port, scm_print_state *pstate SCM_UNUSED) - -</pre><pre> scm_puts(&quot;#&lt;<b>malloc</b> &quot;, port); -</pre><pre> scm_t_bits mem = n ? (scm_t_bits) <b>malloc</b> (n) : 0; - -</pre><pre> SCM_RETURN_NEWSMOB (scm_tc16_<b>malloc</b>, mem); -</pre><pre> scm_tc16_<b>malloc</b> = scm_make_smob_type (&quot;<b>malloc</b>&quot;, 0); - -</pre><pre> scm_set_smob_free (scm_tc16_<b>malloc</b>, <b>malloc</b>_free); -</pre>GPL - -http://www.google.com/codesearch?hl=en&q=+malloc+show:9wyZUG-N_30:7_dFxoC1ZrY:C0_iYbFj90M&sa=N&ct=rx&cd=3&cs_p=http://ftp.gnu.org/gnu/bash/bash-3.0.tar.gz&cs_f=bash-3.0/lib/malloc/alloca.c&cs_p=http://ftp.gnu.org/gnu/bash/bash-3.0.tar.gz&cs_f=bash-3.0/lib/malloc/alloca.c#first2007-12-19T16:08:04ZCode owned by external author.bash-3.0/lib/malloc/alloca.c<pre> 78: #ifndef emacs - #define <b>malloc</b> x<b>malloc</b> - extern pointer x<b>malloc</b> (); - -</pre><pre> <b>malloc</b>. The Emacs executable needs alloca to call x<b>malloc</b>, because -</pre><pre> ordinary <b>malloc</b> isn&#39;t protected from input signals. On the other - -</pre><pre> hand, the utilities in lib-src need alloca to call <b>malloc</b>; some of -</pre><pre> them are very simple, and don&#39;t have an x<b>malloc</b> routine. - -</pre><pre> Callers below should use <b>malloc</b>. */ -</pre><pre>#define <b>malloc</b> x<b>malloc</b> - -</pre><pre>extern pointer x<b>malloc</b> (); -</pre><pre> It is very important that sizeof(header) agree with <b>malloc</b> - -</pre><pre> register pointer new = <b>malloc</b> (sizeof (header) + size); -</pre>GPL -http://www.google.com/codesearch?hl=en&q=+malloc+show:uhVCKyPcT6k:8juMxxzmUJw:H7_IDsTB2L4&sa=N&ct=rx&cd=4&cs_p=http://ftp.mozilla.org/pub/mozilla.org/mozilla/releases/mozilla1.7b/src/mozilla-source-1.7b-source.tar.bz2&cs_f=mozilla/xpcom/build/malloc.c&cs_p=http://ftp.mozilla.org/pub/mozilla.org/mozilla/releases/mozilla1.7b/src/mozilla-source-1.7b-source.tar.bz2&cs_f=mozilla/xpcom/build/malloc.c#first2007-12-19T16:08:04ZCode owned by external author.mozilla/xpcom/build/malloc.c<pre> 54: http://gee.cs.oswego.edu/dl/html/<b>malloc</b>.html - - You may already by default be using a c library containing a <b>malloc</b> - -</pre><pre>/* ---------- To make a <b>malloc</b>.h, start cutting here ------------ */ -</pre><pre> Note: There may be an updated version of this <b>malloc</b> obtainable at - -</pre><pre> ftp://gee.cs.oswego.edu/pub/misc/<b>malloc</b>.c -</pre><pre>* Why use this <b>malloc</b>? - -</pre><pre> most tunable <b>malloc</b> ever written. However it is among the fastest -</pre><pre> allocator for <b>malloc</b>-intensive programs. - -</pre><pre> http://gee.cs.oswego.edu/dl/html/<b>malloc</b>.html -</pre><pre> You may already by default be using a c library containing a <b>malloc</b> - -</pre><pre> that is somehow based on some version of this <b>malloc</b> (for example in -</pre>Mozilla -http://www.google.com/codesearch?hl=en&q=+malloc+show:4n1P2HVOISs:Ybbpph0wR2M:OhIN_sDrG0U&sa=N&ct=rx&cd=5&cs_p=http://regexps.srparish.net/src/hackerlab/hackerlab-1.0pre2.tar.gz&cs_f=hackerlab-1.0pre2/src/hackerlab/tests/mem-tests/unit-must-malloc.sh&cs_p=http://regexps.srparish.net/src/hackerlab/hackerlab-1.0pre2.tar.gz&cs_f=hackerlab-1.0pre2/src/hackerlab/tests/mem-tests/unit-must-malloc.sh#first2007-12-19T16:08:04ZCode owned by external author.hackerlab-1.0pre2/src/hackerlab/tests/mem-tests/unit-must-malloc.sh<pre> 11: echo ================ unit-must-<b>malloc</b> tests ================ - ./unit-must-<b>malloc</b> - echo ...passed - -</pre><pre># tag: Tom Lord Tue Dec 4 14:54:29 2001 (mem-tests/unit-must-<b>malloc</b>.sh) -</pre><pre>echo ================ unit-must-<b>malloc</b> tests ================ - -</pre><pre>./unit-must-<b>malloc</b> -</pre>GPL -http://www.google.com/codesearch?hl=en&q=+malloc+show:GzkwiWG266M:ykuz3bG00ws:2sTvVSif08g&sa=N&ct=rx&cd=6&cs_p=http://ftp.gnu.org/gnu/tar/tar-1.14.tar.bz2&cs_f=tar-1.14/lib/malloc.c&cs_p=http://ftp.gnu.org/gnu/tar/tar-1.14.tar.bz2&cs_f=tar-1.14/lib/malloc.c#first2007-12-19T16:08:04ZCode owned by external author.tar-1.14/lib/malloc.c<pre> 22: #endif - #undef <b>malloc</b> - - -</pre><pre>/* Work around bug on some systems where <b>malloc</b> (0) fails. -</pre><pre>#undef <b>malloc</b> - -</pre><pre>rpl_<b>malloc</b> (size_t n) -</pre><pre> return <b>malloc</b> (n); - -</pre>GPL -http://www.google.com/codesearch?hl=en&q=+malloc+show:o_TFIeBY6dY:ktI_dt8wPao:AI03BD1Dz0Y&sa=N&ct=rx&cd=7&cs_p=http://ftp.gnu.org/gnu/tar/tar-1.16.1.tar.gz&cs_f=tar-1.16.1/lib/malloc.c&cs_p=http://ftp.gnu.org/gnu/tar/tar-1.16.1.tar.gz&cs_f=tar-1.16.1/lib/malloc.c#first2007-12-19T16:08:04ZCode owned by external author.tar-1.16.1/lib/malloc.c<pre> 21: #include &lt;config.h&gt; - #undef <b>malloc</b> - - -</pre><pre>/* <b>malloc</b>() function that is glibc compatible. -</pre><pre>#undef <b>malloc</b> - -</pre><pre>rpl_<b>malloc</b> (size_t n) -</pre><pre> return <b>malloc</b> (n); - -</pre>GPL -http://www.google.com/codesearch?hl=en&q=+malloc+show:_ibw-VLkMoI:jBOtIJSmFd4:-0NUEVeCwfY&sa=N&ct=rx&cd=8&cs_p=http://freshmeat.net/redir/uclibc/20616/url_bz2/uClibc-0.9.28.1.tar.bz2&cs_f=uClibc-0.9.29/include/malloc.h&cs_p=http://freshmeat.net/redir/uclibc/20616/url_bz2/uClibc-0.9.28.1.tar.bz2&cs_f=uClibc-0.9.29/include/malloc.h#first2007-12-19T16:08:04ZCode owned by external author.uClibc-0.9.29/include/malloc.h<pre> 1: /* Prototypes and definition for <b>malloc</b> implementation. - Copyright (C) 1996, 1997, 1999, 2000 Free Software Foundation, Inc. - -</pre><pre>/* Prototypes and definition for <b>malloc</b> implementation. -</pre><pre> `pt<b>malloc</b>&#39;, a <b>malloc</b> implementation for multiple threads without - -</pre><pre> See the files `pt<b>malloc</b>.c&#39; or `COPYRIGHT&#39; for copying conditions. -</pre><pre> This work is mainly derived from <b>malloc</b>-2.6.4 by Doug Lea - -</pre><pre> ftp://g.oswego.edu/pub/misc/<b>malloc</b>.c -</pre><pre> `pt<b>malloc</b>.c&#39;. - -</pre><pre># define __<b>malloc</b>_ptr_t void * -</pre><pre># define __<b>malloc</b>_ptr_t char * - -</pre><pre># define __<b>malloc</b>_size_t size_t -</pre>LGPL -http://www.google.com/codesearch?hl=en&q=+malloc+show:F6qHcZ9vefo:bTX7o9gKfks:hECF4r_eKC0&sa=N&ct=rx&cd=9&cs_p=http://ftp.gnu.org/gnu/glibc/glibc-2.0.1.tar.gz&cs_f=glibc-2.0.1/hurd/hurdmalloc.h&cs_p=http://ftp.gnu.org/gnu/glibc/glibc-2.0.1.tar.gz&cs_f=glibc-2.0.1/hurd/hurdmalloc.h#first2007-12-19T16:08:04ZCode owned by external author.glibc-2.0.1/hurd/hurdmalloc.h<pre> 15: #define <b>malloc</b> _hurd_<b>malloc</b> - #define realloc _hurd_realloc - -</pre><pre> All hurd-internal code which uses <b>malloc</b> et al includes this file so it -</pre><pre> will use the internal <b>malloc</b> routines _hurd_{<b>malloc</b>,realloc,free} - -</pre><pre> of <b>malloc</b> et al is the unixoid one using sbrk. -</pre><pre>extern void *_hurd_<b>malloc</b> (size_t); - -</pre><pre>#define <b>malloc</b> _hurd_<b>malloc</b> -</pre>GPL - -http://www.google.com/codesearch?hl=en&q=+malloc+show:CHUvHYzyLc8:pdcAfzDA6lY:wjofHuNLTHg&sa=N&ct=rx&cd=10&cs_p=ftp://apache.mirrors.pair.com/httpd/httpd-2.2.4.tar.bz2&cs_f=httpd-2.2.4/srclib/apr/include/arch/netware/apr_private.h&cs_p=ftp://apache.mirrors.pair.com/httpd/httpd-2.2.4.tar.bz2&cs_f=httpd-2.2.4/srclib/apr/include/arch/netware/apr_private.h#first2007-12-19T16:08:04ZCode owned by external author.httpd-2.2.4/srclib/apr/include/arch/netware/apr_private.h<pre> 173: #undef <b>malloc</b> - #define <b>malloc</b>(x) library_<b>malloc</b>(gLibHandle,x) - -</pre><pre>/* Redefine <b>malloc</b> to use the library <b>malloc</b> call so -</pre><pre>#undef <b>malloc</b> - -</pre><pre>#define <b>malloc</b>(x) library_<b>malloc</b>(gLibHandle,x) -</pre>Apache - -""" - -YOUTUBE_VIDEO_FEED = """http://gdata.youtube.com/feeds/api/standardfeeds/top_rated2008-05-14T02:24:07.000-07:00Top Ratedhttp://www.youtube.com/img/pic_youtubelogo_123x63.gifYouTubehttp://www.youtube.com/YouTube data API100125 -http://gdata.youtube.com/feeds/api/videos/C71ypXYGho82008-03-20T10:17:27.000-07:002008-05-14T04:26:37.000-07:00Me odeio por te amar - KARYN GARCIAhttp://www.karyngarcia.com.brTvKarynGarciahttp://gdata.youtube.com/feeds/api/users/tvkaryngarciaMe odeio por te amar - KARYN GARCIAhttp://www.karyngarcia.com.bramar, boyfriend, garcia, karyn, me, odeio, por, teMusictest111test222 -http://gdata.youtube.com/feeds/api/videos/gsVaTyb1tBw2008-02-15T04:31:45.000-08:002008-05-14T05:09:42.000-07:00extreme helmet cam Kani, Keil and Patotrimmedperaltamagichttp://gdata.youtube.com/feeds/api/users/peraltamagicextreme helmet cam Kani, Keil and Patotrimmedalcala, cam, campillo, dirt, extreme, helmet, kani, patoSports -""" - -YOUTUBE_ENTRY_PRIVATE = """ - - http://gdata.youtube.com/feeds/videos/UMFI1hdm96E - 2007-01-07T01:50:15.000Z - 2007-01-07T01:50:15.000Z - - - - - - - - - "Crazy (Gnarles Barkley)" - Acoustic Cover - <div style="color: #000000;font-family: - Arial, Helvetica, sans-serif; font-size:12px; font-size: 12px; - width: 555px;"><table cellspacing="0" cellpadding="0" - border="0"><tbody><tr><td width="140" - valign="top" rowspan="2"><div style="border: 1px solid - #999999; margin: 0px 10px 5px 0px;"><a - href="http://www.youtube.com/watch?v=UMFI1hdm96E"><img - alt="" - src="http://img.youtube.com/vi/UMFI1hdm96E/2.jpg"></a></div></td> - <td width="256" valign="top"><div style="font-size: - 12px; font-weight: bold;"><a style="font-size: 15px; - font-weight: bold; font-decoration: none;" - href="http://www.youtube.com/watch?v=UMFI1hdm96E">&quot;Crazy - (Gnarles Barkley)&quot; - Acoustic Cover</a> - <br></div> <div style="font-size: 12px; margin: - 3px 0px;"><span>Gnarles Barkley acoustic cover - http://www.myspace.com/davidchoimusic</span></div></td> - <td style="font-size: 11px; line-height: 1.4em; padding-left: - 20px; padding-top: 1px;" width="146" - valign="top"><div><span style="color: #666666; - font-size: 11px;">From:</span> <a - href="http://www.youtube.com/profile?user=davidchoimusic">davidchoimusic</a></div> - <div><span style="color: #666666; font-size: - 11px;">Views:</span> 113321</div> <div - style="white-space: nowrap;text-align: left"><img - style="border: 0px none; margin: 0px; padding: 0px; - vertical-align: middle; font-size: 11px;" align="top" alt="" - src="http://gdata.youtube.com/static/images/icn_star_full_11x11.gif"> - <img style="border: 0px none; margin: 0px; padding: 0px; - vertical-align: middle; font-size: 11px;" align="top" alt="" - src="http://gdata.youtube.com/static/images/icn_star_full_11x11.gif"> - <img style="border: 0px none; margin: 0px; padding: 0px; - vertical-align: middle; font-size: 11px;" align="top" alt="" - src="http://gdata.youtube.com/static/images/icn_star_full_11x11.gif"> - <img style="border: 0px none; margin: 0px; padding: 0px; - vertical-align: middle; font-size: 11px;" align="top" alt="" - src="http://gdata.youtube.com/static/images/icn_star_full_11x11.gif"> - <img style="border: 0px none; margin: 0px; padding: 0px; - vertical-align: middle; font-size: 11px;" align="top" alt="" - src="http://gdata.youtube.com/static/images/icn_star_half_11x11.gif"></div> - <div style="font-size: 11px;">1005 <span style="color: - #666666; font-size: - 11px;">ratings</span></div></td></tr> - <tr><td><span style="color: #666666; font-size: - 11px;">Time:</span> <span style="color: #000000; - font-size: 11px; font-weight: - bold;">04:15</span></td> <td style="font-size: - 11px; padding-left: 20px;"><span style="color: #666666; - font-size: 11px;">More in</span> <a - href="http://www.youtube.com/categories_portal?c=10">Music</a></td></tr></tbody></table></div> - - - - - - davidchoimusic - http://gdata.youtube.com/feeds/users/davidchoimusic - - - "Crazy (Gnarles Barkley)" - Acoustic Cover - Gnarles Barkley acoustic cover http://www.myspace.com/davidchoimusic - music, singing, gnarls, barkley, acoustic, cover - - - Music - - DeveloperTag1 - - - - - - - - - - - - - 37.398529052734375 -122.0635986328125 - - - - - - - - yes - - The content of this video may violate the terms of use. - -""" - -YOUTUBE_COMMENT_FEED = """ -http://gdata.youtube.com/feeds/videos/2Idhz9ef5oU/comments2008-05-19T21:45:45.261ZCommentshttp://www.youtube.com/img/pic_youtubelogo_123x63.gifYouTubehttp://www.youtube.com/YouTube data API0125 - - http://gdata.youtube.com/feeds/videos/2Idhz9ef5oU/comments/91F809A3DE2EB81B - 2008-02-22T15:27:15.000-08:002008-02-22T15:27:15.000-08:00 - - test66 - test66 - - - - apitestjhartmannhttp://gdata.youtube.com/feeds/users/apitestjhartmann - - - http://gdata.youtube.com/feeds/videos/2Idhz9ef5oU/comments/A261AEEFD23674AA - 2008-02-22T15:27:01.000-08:002008-02-22T15:27:01.000-08:00 - - test333 - test333 - - - - apitestjhartmannhttp://gdata.youtube.com/feeds/users/apitestjhartmann - - - http://gdata.youtube.com/feeds/videos/2Idhz9ef5oU/comments/0DCF1E3531B3FF85 - 2008-02-22T15:11:06.000-08:002008-02-22T15:11:06.000-08:00 - - test2 - test2 - - - - apitestjhartmannhttp://gdata.youtube.com/feeds/users/apitestjhartmann - -""" - -YOUTUBE_PLAYLIST_FEED = """ - - http://gdata.youtube.com/feeds/users/andyland74/playlists?start-index=1&max-results=25 - 2008-02-26T00:26:15.635Z - - andyland74's Playlists - http://www.youtube.com/img/pic_youtubelogo_123x63.gif - - - - - - andyland74 - http://gdata.youtube.com/feeds/users/andyland74 - - YouTube data API - 1 - 1 - 25 - - My new playlist Description - - http://gdata.youtube.com/feeds/users/andyland74/playlists/8BCDD04DE8F771B2 - 2007-11-04T17:30:27.000-08:00 - 2008-02-22T09:55:14.000-08:00 - - My New Playlist Title - My new playlist Description - - - - - andyland74 - http://gdata.youtube.com/feeds/users/andyland74 - - -""" - -YOUTUBE_PLAYLIST_VIDEO_FEED = """http://gdata.youtube.com/feeds/api/playlists/BCB3BB96DF51B5052008-05-16T12:03:17.000-07:00Test PlaylistTest playlist 1http://www.youtube.com/img/pic_youtubelogo_123x63.gifgdpythonhttp://gdata.youtube.com/feeds/api/users/gdpythonYouTube data API1125Test PlaylistTest playlist 1http://gdata.youtube.com/feeds/api/playlists/BCB3BB96DF51B505/B0F29389E537F8882008-05-16T20:54:08.520ZUploading YouTube Videos with the PHP Client LibraryJochen Hartmann demonstrates the basics of how to use the PHP Client Library with the YouTube Data API. - -PHP Developer's Guide: -http://code.google.com/apis/youtube/developers_guide_php.html - -Other documentation: -http://code.google.com/apis/youtube/GoogleDevelopershttp://gdata.youtube.com/feeds/api/users/googledevelopersUploading YouTube Videos with the PHP Client LibraryJochen Hartmann demonstrates the basics of how to use the PHP Client Library with the YouTube Data API. - -PHP Developer's Guide: -http://code.google.com/apis/youtube/developers_guide_php.html - -Other documentation: -http://code.google.com/apis/youtube/api, data, demo, php, screencast, tutorial, uploading, walkthrough, youtubeEducationundefined1""" - -YOUTUBE_SUBSCRIPTION_FEED = """ - - http://gdata.youtube.com/feeds/users/andyland74/subscriptions?start-index=1&max-results=25 - 2008-02-26T00:26:15.635Z - - andyland74's Subscriptions - http://www.youtube.com/img/pic_youtubelogo_123x63.gif - - - - - - andyland74 - http://gdata.youtube.com/feeds/users/andyland74 - - YouTube data API - 1 - 1 - 25 - - http://gdata.youtube.com/feeds/users/andyland74/subscriptions/d411759045e2ad8c - 2007-11-04T17:30:27.000-08:00 - 2008-02-22T09:55:14.000-08:00 - - - Videos published by : NBC - - - - - andyland74 - http://gdata.youtube.com/feeds/users/andyland74 - - NBC - - -""" - -YOUTUBE_VIDEO_RESPONSE_FEED = """ - - http://gdata.youtube.com/feeds/videos/2c3q9K4cHzY/responses2008-05-19T22:37:34.076ZVideos responses to 'Giant NES controller coffee table'http://www.youtube.com/img/pic_youtubelogo_123x63.gifYouTubehttp://www.youtube.com/YouTube data API8125 - - http://gdata.youtube.com/feeds/videos/7b9EnRI9VbY2008-03-11T19:08:53.000-07:002008-05-18T21:33:10.000-07:00 - - - - - - - - - - - - Catnip Partysnipped - - - - - PismoBeachhttp://gdata.youtube.com/feeds/users/pismobeach - - Catnip Party - Uncle, Hillary, Hankette, and B4 all but overdose on the patioBrattman, cat, catmint, catnip, cats, chat, drug, gato, gatto, kat, kato, katt, Katze, kedi, kissa, OD, overdose, party, sex, Uncle - - Animals - - - - - - - - - - - - - - - - -""" - - -YOUTUBE_PROFILE = """ - - http://gdata.youtube.com/feeds/users/andyland74 - 2006-10-16T00:09:45.000-07:00 - 2008-02-26T11:48:21.000-08:00 - - - andyland74 Channel - - - - andyland74 - http://gdata.youtube.com/feeds/users/andyland74 - - 33 - andyland74 - andy - example - Catch-22 - m - Google - Testing YouTube APIs - Somewhere - US - Aqua Teen Hungerforce - Elliott Smith - Technical Writer - University of North Carolina - - - - - - - - -""" - -YOUTUBE_CONTACTS_FEED = """ - http://gdata.youtube.com/feeds/users/apitestjhartmann/contacts2008-05-16T19:24:34.916Zapitestjhartmann's Contactshttp://www.youtube.com/img/pic_youtubelogo_123x63.gifapitestjhartmannhttp://gdata.youtube.com/feeds/users/apitestjhartmannYouTube data API2125 - - http://gdata.youtube.com/feeds/users/apitestjhartmann/contacts/test898990902008-02-04T11:27:54.000-08:002008-05-16T19:24:34.916Ztest89899090apitestjhartmannhttp://gdata.youtube.com/feeds/users/apitestjhartmanntest89899090requested - - http://gdata.youtube.com/feeds/users/apitestjhartmann/contacts/testjfisher2008-02-26T14:13:03.000-08:002008-05-16T19:24:34.916Ztestjfisherapitestjhartmannhttp://gdata.youtube.com/feeds/users/apitestjhartmanntestjfisherpending -""" - -NEW_CONTACT = """ - - http://www.google.com/m8/feeds/contacts/liz%40gmail.com/base/8411573 - 2008-02-28T18:47:02.303Z - - Fitzgerald - Notes - - - - - (206)555-1212 - 456-123-2133 - (206)555-1213 - - - - - - - 1600 Amphitheatre Pkwy Mountain View -""" - -CONTACTS_FEED = """ - - http://www.google.com/m8/feeds/contacts/liz%40gmail.com/base - 2008-03-05T12:36:38.836Z - - Contacts - - - - - - Elizabeth Bennet - liz@gmail.com - - - Contacts - - 1 - 1 - 25 - - - http://www.google.com/m8/feeds/contacts/liz%40gmail.com/base/c9012de - - 2008-03-05T12:36:38.835Z - - Fitzgerald - - - - - - 456 - - - - -""" - - -CONTACT_GROUPS_FEED = """ - - jo@gmail.com - 2008-05-21T21:11:25.237Z - - Jo's Contact Groups - - - - - - - Jo Brown - jo@gmail.com - - Contacts - 3 - 1 - 25 - - http://google.com/m8/feeds/groups/jo%40gmail.com/base/270f - 2008-05-14T13:10:19.070Z - - joggers - joggers - - - -""" - -CONTACT_GROUP_ENTRY = """ - - - http://www.google.com/feeds/groups/jo%40gmail.com/base/1234 - 2005-01-18T21:00:00Z - 2006-01-01T00:00:00Z - Salsa group - Salsa group - - - - Very nice people. - -""" - -CALENDAR_RESOURCE_ENTRY = """ - - - - - -""" - -CALENDAR_RESOURCES_FEED = """ - - https://apps-apis.google.com/a/feeds/calendar/resource/2.0/yourdomain.com - 2008-10-17T15:29:21.064Z - - - - - 1 - - https://apps-apis.google.com/a/feeds/calendar/resource/2.0/yourdomain.com/CR-NYC-14-12-BR - 2008-10-17T15:29:21.064Z - - - - - - - - - - https://apps-apis.google.com/a/feeds/calendar/resource/2.0/yourdomain.com/?start=(Bike)-London-43-Lobby-Bike-1 - 2008-10-17T15:29:21.064Z - - - - - - - - -""" - -BLOG_ENTRY = """ - tag:blogger.com,1999:blog-blogID.post-postID - 2006-08-02T18:44:43.089-07:00 - 2006-11-08T18:10:23.020-08:00 - Lizzy's Diary - Being the journal of Elizabeth Bennet - - - - - - - - - - - - Elizabeth Bennet - liz@gmail.com - -""" - -BLOG_POST = """ - Marriage! - -
-

Mr. Darcy has proposed marriage to me!

-

He is the last man on earth I would ever desire to marry.

-

Whatever shall I do?

-
-
- - Elizabeth Bennet - liz@gmail.com - -
""" - -BLOG_POSTS_FEED = """ - tag:blogger.com,1999:blog-blogID - 2006-11-08T18:10:23.020-08:00 - Lizzy's Diary - - - - - - - - Elizabeth Bennet - liz@gmail.com - - Blogger - - tag:blogger.com,1999:blog-blogID.post-postID - 2006-11-08T18:10:00.000-08:00 - 2006-11-08T18:10:14.954-08:00 - Quite disagreeable - <p>I met Mr. Bingley's friend Mr. Darcy - this evening. I found him quite disagreeable.</p> - - - - - - - - Elizabeth Bennet - liz@gmail.com - - -""" - -BLOG_COMMENTS_FEED = """ - tag:blogger.com,1999:blog-blogID.postpostID..comments - 2007-04-04T21:56:29.803-07:00 - My Blog : Time to relax - - - - - Blog Author name - - Blogger - 1 - 1 - - tag:blogger.com,1999:blog-blogID.post-commentID - 2007-04-04T21:56:00.000-07:00 - 2007-04-04T21:56:29.803-07:00 - This is my first comment - This is my first comment - - - - - Blog Author name - - - -""" - - -SITES_FEED = """ - https://www.google.com/webmasters/tools/feeds/sites - Sites - 1 - - - - - 2008-10-02T07:26:51.833Z - - http://www.example.com - http://www.example.com - - - - 2007-11-17T18:27:32.543Z - - - - true - 2008-09-14T08:59:28.000 - US - none - normal - true - false - - - 456456-google.html - -""" - - -SITEMAPS_FEED = """ - http://www.example.com - http://www.example.com/ - 2006-11-17T18:27:32.543Z - - - - HTML - WAP - - - Value1 - Value2 - Value3 - - - http://www.example.com/sitemap-index.xml - http://www.example.com/sitemap-index.xml - - 2006-11-17T18:27:32.543Z - WEB - StatusValue - 2006-11-18T19:27:32.543Z - 102 - - - http://www.example.com/mobile/sitemap-index.xml - http://www.example.com/mobile/sitemap-index.xml - - 2006-11-17T18:27:32.543Z - StatusValue - 2006-11-18T19:27:32.543Z - 102 - HTML - - - http://www.example.com/news/sitemap-index.xml - http://www.example.com/news/sitemap-index.xml - - 2006-11-17T18:27:32.543Z - StatusValue - 2006-11-18T19:27:32.543Z - 102 - LabelValue - -""" - -HEALTH_CCR_NOTICE_PAYLOAD = """ - - - - - Start date - 2007-04-04T07:00:00Z - - - Aortic valve disorders - - 410.10 - ICD9 - 2004 - - - Active - - - -""" - -HEALTH_PROFILE_ENTRY_DIGEST = """ - - https://www.google.com/health/feeds/profile/default/vneCn5qdEIY_digest - 2008-09-29T07:52:17.176Z - - - - - - vneCn5qdEIY - - English - - en - ISO-639-1 - - - V1.0 - - 2008-09-29T07:52:17.176Z - - - Google Health Profile - - - - - - Pregnancy status - - - Not pregnant - - - - - user@google.com - - Patient - - - - - - - Breastfeeding status - - - Not breastfeeding - - - - - user@gmail.com - - Patient - - - - - - - - Hn0FE0IlcY-FMFFgSTxkvA/CONDITION/0 - - - Start date - - 2007-04-04T07:00:00Z - - - Aortic valve disorders - - 410.10 - ICD9 - 2004 - - - - Active - - - - example.com - - Information Provider - - - - - - - - Malaria - - 136.9 - ICD9_Broader - - - 084.6 - ICD9 - - - - ACTIVE - - - - user@gmail.com - - Patient - - - - - - - - - - - - Race - - S15814 - HL7 - - - - White - - - - - user@gmail.com - - Patient - - - - - - - - - - - - - - Allergy - - - A-Fil - - - ACTIVE - - - - user@gmail.com - - Patient - - - - - - - Severe - - - - - - Allergy - - - A.E.R Traveler - - - ACTIVE - - - - user@gmail.com - - Patient - - - - - - - Severe - - - - - - - - - - ACTIVE - - - - user@gmail.com - - Patient - - - - - - A& D - - - - 0 - - - - - - - - - - 0 - - - - To skin - - C38305 - FDA - - 0 - - - - - - - - - - - ACTIVE - - - - user@gmail.com - - Patient - - - - - - A-Fil - - - - 0 - - - - - - - - - - 0 - - - - To skin - - C38305 - FDA - - 0 - - - - - - - - - - - ACTIVE - - - - user@gmail.com - - Patient - - - - - - Lipitor - - - - 0 - - - - - - - - - - 0 - - - - By mouth - - C38288 - FDA - - 0 - - - - - - - - - - - - - - - user@gmail.com - - Patient - - - - - - Chickenpox Vaccine - - 21 - HL7 - - - - - - - - - - - - - - - - - - - - user@gmail.com - - Patient - - - - - - - - Height - - - - 0 - - 70 - - inches - - - - - - - - - - - - user@gmail.com - - Patient - - - - - - - - Weight - - - - 0 - - 2480 - - ounces - - - - - - - - - - - - user@gmail.com - - Patient - - - - - - - - Blood Type - - - - 0 - - O+ - - - - - - - - - - - - - - user@gmail.com - - Patient - - - - - - - - Collection start date - - 2008-09-03 - - - - Acetaldehyde - Blood - - - - 0 - - - - - - - - - - - - Abdominal Ultrasound - - - - - user@gmail.com - - Patient - - - - - - - - Abdominoplasty - - - - - user@gmail.com - - Patient - - - - - - - - - Google Health Profile - - - - - - - - 1984-07-22 - - - Male - - - - - - user@gmail.com - - Patient - - - - - - -""" - -HEALTH_PROFILE_FEED = """ -https://www.google.com/health/feeds/profile/default -2008-09-30T01:07:17.888Z - -Profile Feed - - - - -1 - - https://www.google.com/health/feeds/profile/default/DysasdfARnFAao - 2008-09-29T03:12:50.850Z - 2008-09-29T03:12:50.850Z - - - - - <content type="html"/> - <link rel="http://schemas.google.com/health/data#complete" type="application/atom+xml" href="https://www.google.com/health/feeds/profile/default/-/MEDICATION/%7Bhttp%3A%2F%2Fschemas.google.com%2Fg%2F2005%23kind%7Dhttp%3A%2F%2Fschemas.google.com%2Fhealth%2Fkinds%23profile/%7Bhttp%3A%2F%2Fschemas.google.com%2Fhealth%2Fitem%7DA%26+D"/> - <link rel="self" type="application/atom+xml" href="https://www.google.com/health/feeds/profile/default/DysasdfARnFAao"/> - <link rel="edit" type="application/atom+xml" href="https://www.google.com/health/feeds/profile/default/DysasdfARnFAao"/> - <author> - <name>User Name</name> - <email>user@gmail.com</email> - </author> - <ContinuityOfCareRecord xmlns="urn:astm-org:CCR"> - <CCRDocumentObjectID>hiD9sEigSzdk8nNT0evR4g</CCRDocumentObjectID> - <Language/> - <DateTime> - <Type/> - </DateTime> - <Patient/> - <Body> - <Medications> - <Medication> - <Type/> - <Description/> - <Status> - <Text>ACTIVE</Text> - </Status> - <Source> - <Actor> - <ActorID>user@gmail.com</ActorID> - <ActorRole> - <Text>Patient</Text> - </ActorRole> - </Actor> - </Source> - <Product> - <ProductName> - <Text>A& D</Text> - </ProductName> - <Strength> - <Units/> - <StrengthSequencePosition>0</StrengthSequencePosition> - <VariableStrengthModifier/> - </Strength> - </Product> - <Directions> - <Direction> - <Description/> - <DeliveryMethod/> - <Dose> - <Units/> - <DoseSequencePosition>0</DoseSequencePosition> - <VariableDoseModifier/> - </Dose> - <Route> - <Text>To skin</Text> - <Code> - <Value>C38305</Value> - <CodingSystem>FDA</CodingSystem> - </Code> - <RouteSequencePosition>0</RouteSequencePosition> - <MultipleRouteModifier/> - </Route> - </Direction> - </Directions> - <Refills/> - </Medication> - </Medications> - </Body> - </ContinuityOfCareRecord> -</entry> -<entry> - <id>https://www.google.com/health/feeds/profile/default/7I1WQzZrgp4</id> - <published>2008-09-29T03:27:14.909Z</published> - <updated>2008-09-29T03:27:14.909Z</updated> - <category scheme="http://schemas.google.com/g/2005#kind" term="http://schemas.google.com/health/kinds#profile"/> - <category scheme="http://schemas.google.com/health/item" term="A-Fil"/> - <category term="ALLERGY"/> - <title type="text"/> - <content type="html"/> - <link rel="http://schemas.google.com/health/data#complete" type="application/atom+xml" href="https://www.google.com/health/feeds/profile/default/-/%7Bhttp%3A%2F%2Fschemas.google.com%2Fg%2F2005%23kind%7Dhttp%3A%2F%2Fschemas.google.com%2Fhealth%2Fkinds%23profile/%7Bhttp%3A%2F%2Fschemas.google.com%2Fhealth%2Fitem%7DA-Fil/ALLERGY"/> - <link rel="self" type="application/atom+xml" href="https://www.google.com/health/feeds/profile/default/7I1WQzZrgp4"/> - <link rel="edit" type="application/atom+xml" href="https://www.google.com/health/feeds/profile/default/7I1WQzZrgp4"/> - <author> - <name>User Name</name> - <email>user@gmail.com</email> - </author> - <ContinuityOfCareRecord xmlns="urn:astm-org:CCR"> - <CCRDocumentObjectID>YOyHDxQUiECCPgnsjV8SlQ</CCRDocumentObjectID> - <Language/> - <DateTime> - <Type/> - </DateTime> - <Patient/> - <Body> - <Alerts> - <Alert> - <Type> - <Text>Allergy</Text> - </Type> - <Description> - <Text>A-Fil</Text> - </Description> - <Status> - <Text>ACTIVE</Text> - </Status> - <Source> - <Actor> - <ActorID>user@gmail.com</ActorID> - <ActorRole> - <Text>Patient</Text> - </ActorRole> - </Actor> - </Source> - <Reaction> - <Description/> - <Severity> - <Text>Severe</Text> - </Severity> - </Reaction> - </Alert> - </Alerts> - </Body> - </ContinuityOfCareRecord> -</entry> -<entry> - <id>https://www.google.com/health/feeds/profile/default/Dz9wV83sKFg</id> - <published>2008-09-29T03:12:52.166Z</published> - <updated>2008-09-29T03:12:52.167Z</updated> - <category term="MEDICATION"/> - <category scheme="http://schemas.google.com/g/2005#kind" term="http://schemas.google.com/health/kinds#profile"/> - <category scheme="http://schemas.google.com/health/item" term="A-Fil"/> - <title type="text"/> - <content type="html"/> - <link rel="http://schemas.google.com/health/data#complete" type="application/atom+xml" href="https://www.google.com/health/feeds/profile/default/-/MEDICATION/%7Bhttp%3A%2F%2Fschemas.google.com%2Fg%2F2005%23kind%7Dhttp%3A%2F%2Fschemas.google.com%2Fhealth%2Fkinds%23profile/%7Bhttp%3A%2F%2Fschemas.google.com%2Fhealth%2Fitem%7DA-Fil"/> - <link rel="self" type="application/atom+xml" href="https://www.google.com/health/feeds/profile/default/Dz9wV83sKFg"/> - <link rel="edit" type="application/atom+xml" href="https://www.google.com/health/feeds/profile/default/Dz9wV83sKFg"/> - <author> - <name>User Name</name> - <email>user@gmail.com</email> - </author> - <ContinuityOfCareRecord xmlns="urn:astm-org:CCR"> - <CCRDocumentObjectID>7w.XFEPeuIYN3Rn32pUiUw</CCRDocumentObjectID> - <Language/> - <DateTime> - <Type/> - </DateTime> - <Patient/> - <Body> - <Medications> - <Medication> - <Type/> - <Description/> - <Status> - <Text>ACTIVE</Text> - </Status> - <Source> - <Actor> - <ActorID>user@gmail.com</ActorID> - <ActorRole> - <Text>Patient</Text> - </ActorRole> - </Actor> - </Source> - <Product> - <ProductName> - <Text>A-Fil</Text> - </ProductName> - <Strength> - <Units/> - <StrengthSequencePosition>0</StrengthSequencePosition> - <VariableStrengthModifier/> - </Strength> - </Product> - <Directions> - <Direction> - <Description/> - <DeliveryMethod/> - <Dose> - <Units/> - <DoseSequencePosition>0</DoseSequencePosition> - <VariableDoseModifier/> - </Dose> - <Route> - <Text>To skin</Text> - <Code> - <Value>C38305</Value> - <CodingSystem>FDA</CodingSystem> - </Code> - <RouteSequencePosition>0</RouteSequencePosition> - <MultipleRouteModifier/> - </Route> - </Direction> - </Directions> - <Refills/> - </Medication> - </Medications> - </Body> - </ContinuityOfCareRecord> -</entry> -<entry> - <id>https://www.google.com/health/feeds/profile/default/lzsxVzqZUyw</id> - <published>2008-09-29T03:13:07.496Z</published> - <updated>2008-09-29T03:13:07.497Z</updated> - <category scheme="http://schemas.google.com/health/item" term="A.E.R Traveler"/> - <category scheme="http://schemas.google.com/g/2005#kind" term="http://schemas.google.com/health/kinds#profile"/> - <category term="ALLERGY"/> - <title type="text"/> - <content type="html"/> - <link rel="http://schemas.google.com/health/data#complete" type="application/atom+xml" href="https://www.google.com/health/feeds/profile/default/-/%7Bhttp%3A%2F%2Fschemas.google.com%2Fhealth%2Fitem%7DA.E.R+Traveler/%7Bhttp%3A%2F%2Fschemas.google.com%2Fg%2F2005%23kind%7Dhttp%3A%2F%2Fschemas.google.com%2Fhealth%2Fkinds%23profile/ALLERGY"/> - <link rel="self" type="application/atom+xml" href="https://www.google.com/health/feeds/profile/default/lzsxVzqZUyw"/> - <link rel="edit" type="application/atom+xml" href="https://www.google.com/health/feeds/profile/default/lzsxVzqZUyw"/> - <author> - <name>User Name</name> - <email>user@gmail.com</email> - </author> - <ContinuityOfCareRecord xmlns="urn:astm-org:CCR"> - <CCRDocumentObjectID>5efFB0J2WgEHNUvk2z3A1A</CCRDocumentObjectID> - <Language/> - <DateTime> - <Type/> - </DateTime> - <Patient/> - <Body> - <Alerts> - <Alert> - <Type> - <Text>Allergy</Text> - </Type> - <Description> - <Text>A.E.R Traveler</Text> - </Description> - <Status> - <Text>ACTIVE</Text> - </Status> - <Source> - <Actor> - <ActorID>user@gmail.com</ActorID> - <ActorRole> - <Text>Patient</Text> - </ActorRole> - </Actor> - </Source> - <Reaction> - <Description/> - <Severity> - <Text>Severe</Text> - </Severity> - </Reaction> - </Alert> - </Alerts> - </Body> - </ContinuityOfCareRecord> -</entry> -<entry> - <id>https://www.google.com/health/feeds/profile/default/6PvhfKAXyYw</id> - <published>2008-09-29T03:13:02.123Z</published> - <updated>2008-09-29T03:13:02.124Z</updated> - <category scheme="http://schemas.google.com/g/2005#kind" term="http://schemas.google.com/health/kinds#profile"/> - <category term="PROCEDURE"/> - <category scheme="http://schemas.google.com/health/item" term="Abdominal Ultrasound"/> - <title type="text"/> - <content type="html"/> - <link rel="http://schemas.google.com/health/data#complete" type="application/atom+xml" href="https://www.google.com/health/feeds/profile/default/-/%7Bhttp%3A%2F%2Fschemas.google.com%2Fg%2F2005%23kind%7Dhttp%3A%2F%2Fschemas.google.com%2Fhealth%2Fkinds%23profile/PROCEDURE/%7Bhttp%3A%2F%2Fschemas.google.com%2Fhealth%2Fitem%7DAbdominal+Ultrasound"/> - <link rel="self" type="application/atom+xml" href="https://www.google.com/health/feeds/profile/default/6PvhfKAXyYw"/> - <link rel="edit" type="application/atom+xml" href="https://www.google.com/health/feeds/profile/default/6PvhfKAXyYw"/> - <author> - <name>User Name</name> - <email>user@gmail.com</email> - </author> - <ContinuityOfCareRecord xmlns="urn:astm-org:CCR"> - <CCRDocumentObjectID>W3Wbvx_QHwG5pxVchpuF1A</CCRDocumentObjectID> - <Language/> - <DateTime> - <Type/> - </DateTime> - <Patient/> - <Body> - <Procedures> - <Procedure> - <Type/> - <Description> - <Text>Abdominal Ultrasound</Text> - </Description> - <Status/> - <Source> - <Actor> - <ActorID>user@gmail.com</ActorID> - <ActorRole> - <Text>Patient</Text> - </ActorRole> - </Actor> - </Source> - </Procedure> - </Procedures> - </Body> - </ContinuityOfCareRecord> -</entry> -<entry> - <id>https://www.google.com/health/feeds/profile/default/r2zGPGewCeU</id> - <published>2008-09-29T03:13:03.434Z</published> - <updated>2008-09-29T03:13:03.435Z</updated> - <category scheme="http://schemas.google.com/health/item" term="Abdominoplasty"/> - <category scheme="http://schemas.google.com/g/2005#kind" term="http://schemas.google.com/health/kinds#profile"/> - <category term="PROCEDURE"/> - <title type="text"/> - <content type="html"/> - <link rel="http://schemas.google.com/health/data#complete" type="application/atom+xml" href="https://www.google.com/health/feeds/profile/default/-/%7Bhttp%3A%2F%2Fschemas.google.com%2Fhealth%2Fitem%7DAbdominoplasty/%7Bhttp%3A%2F%2Fschemas.google.com%2Fg%2F2005%23kind%7Dhttp%3A%2F%2Fschemas.google.com%2Fhealth%2Fkinds%23profile/PROCEDURE"/> - <link rel="self" type="application/atom+xml" href="https://www.google.com/health/feeds/profile/default/r2zGPGewCeU"/> - <link rel="edit" type="application/atom+xml" href="https://www.google.com/health/feeds/profile/default/r2zGPGewCeU"/> - <author> - <name>User Name</name> - <email>user@gmail.com</email> - </author> - <ContinuityOfCareRecord xmlns="urn:astm-org:CCR"> - <CCRDocumentObjectID>OUKgj5X0KMnbkC5sDL.yHA</CCRDocumentObjectID> - <Language/> - <DateTime> - <Type/> - </DateTime> - <Patient/> - <Body> - <Procedures> - <Procedure> - <Type/> - <Description> - <Text>Abdominoplasty</Text> - </Description> - <Status/> - <Source> - <Actor> - <ActorID>user@gmail.com</ActorID> - <ActorRole> - <Text>Patient</Text> - </ActorRole> - </Actor> - </Source> - </Procedure> - </Procedures> - </Body> - </ContinuityOfCareRecord> -</entry> -<entry> - <id>https://www.google.com/health/feeds/profile/default/_cCCbQ0O3ug</id> - <published>2008-09-29T03:13:29.041Z</published> - <updated>2008-09-29T03:13:29.042Z</updated> - <category scheme="http://schemas.google.com/g/2005#kind" term="http://schemas.google.com/health/kinds#profile"/> - <category scheme="http://schemas.google.com/health/item" term="Acetaldehyde - Blood"/> - <category term="LABTEST"/> - <title type="text"/> - <content type="html"/> - <link rel="http://schemas.google.com/health/data#complete" type="application/atom+xml" href="https://www.google.com/health/feeds/profile/default/-/%7Bhttp%3A%2F%2Fschemas.google.com%2Fg%2F2005%23kind%7Dhttp%3A%2F%2Fschemas.google.com%2Fhealth%2Fkinds%23profile/%7Bhttp%3A%2F%2Fschemas.google.com%2Fhealth%2Fitem%7DAcetaldehyde+-+Blood/LABTEST"/> - <link rel="self" type="application/atom+xml" href="https://www.google.com/health/feeds/profile/default/_cCCbQ0O3ug"/> - <link rel="edit" type="application/atom+xml" href="https://www.google.com/health/feeds/profile/default/_cCCbQ0O3ug"/> - <author> - <name>User Name</name> - <email>user@gmail.com</email> - </author> - <ContinuityOfCareRecord xmlns="urn:astm-org:CCR"> - <CCRDocumentObjectID>YWtomFb8aG.DueZ7z7fyug</CCRDocumentObjectID> - <Language/> - <DateTime> - <Type/> - </DateTime> - <Patient/> - <Body> - <Results> - <Result> - <Type/> - <Description/> - <Status/> - <Source> - <Actor> - <ActorID>user@gmail.com</ActorID> - <ActorRole> - <Text>Patient</Text> - </ActorRole> - </Actor> - </Source> - <Substance/> - <Test> - <DateTime> - <Type> - <Text>Collection start date</Text> - </Type> - <ExactDateTime>2008-09-03</ExactDateTime> - </DateTime> - <Type/> - <Description> - <Text>Acetaldehyde - Blood</Text> - </Description> - <Status/> - <TestResult> - <ResultSequencePosition>0</ResultSequencePosition> - <VariableResultModifier/> - <Units/> - </TestResult> - <ConfidenceValue/> - </Test> - </Result> - </Results> - </Body> - </ContinuityOfCareRecord> -</entry> -<entry> - <id>https://www.google.com/health/feeds/profile/default/BdyA3iJZyCc</id> - <published>2008-09-29T03:00:45.915Z</published> - <updated>2008-09-29T03:00:45.915Z</updated> - <category scheme="http://schemas.google.com/health/item" term="Aortic valve disorders"/> - <category scheme="http://schemas.google.com/g/2005#kind" term="http://schemas.google.com/health/kinds#profile"/> - <category term="CONDITION"/> - <title type="text">Aortic valve disorders - - - - - example.com - example.com - - - h1ljpoeKJ85li.1FHsG9Gw - - - - Hn0FE0IlcY-FMFFgSTxkvA/CONDITION/0 - - - Start date - - 2007-04-04T07:00:00Z - - - Aortic valve disorders - - 410.10 - ICD9 - 2004 - - - - Active - - - - example.com - - Information Provider - - - - - - - - - - https://www.google.com/health/feeds/profile/default/Cl.aMWIH5VA - 2008-09-29T03:13:34.996Z - 2008-09-29T03:13:34.997Z - - - - - <content type="html"/> - <link rel="http://schemas.google.com/health/data#complete" type="application/atom+xml" href="https://www.google.com/health/feeds/profile/default/-/%7Bhttp%3A%2F%2Fschemas.google.com%2Fg%2F2005%23kind%7Dhttp%3A%2F%2Fschemas.google.com%2Fhealth%2Fkinds%23profile/%7Bhttp%3A%2F%2Fschemas.google.com%2Fhealth%2Fitem%7DChickenpox+Vaccine/IMMUNIZATION"/> - <link rel="self" type="application/atom+xml" href="https://www.google.com/health/feeds/profile/default/Cl.aMWIH5VA"/> - <link rel="edit" type="application/atom+xml" href="https://www.google.com/health/feeds/profile/default/Cl.aMWIH5VA"/> - <author> - <name>User Name</name> - <email>user@gmail.com</email> - </author> - <ContinuityOfCareRecord xmlns="urn:astm-org:CCR"> - <CCRDocumentObjectID>KlhUqfftgELIitpKbqYalw</CCRDocumentObjectID> - <Language/> - <DateTime> - <Type/> - </DateTime> - <Patient/> - <Body> - <Immunizations> - <Immunization> - <Type/> - <Description/> - <Status/> - <Source> - <Actor> - <ActorID>user@gmail.com</ActorID> - <ActorRole> - <Text>Patient</Text> - </ActorRole> - </Actor> - </Source> - <Product> - <ProductName> - <Text>Chickenpox Vaccine</Text> - <Code> - <Value>21</Value> - <CodingSystem>HL7</CodingSystem> - </Code> - </ProductName> - </Product> - <Directions> - <Direction> - <Description/> - <DeliveryMethod/> - </Direction> - </Directions> - <Refills/> - </Immunization> - </Immunizations> - </Body> - </ContinuityOfCareRecord> -</entry> -<entry> - <id>https://www.google.com/health/feeds/profile/default/l0a7.FlX3_0</id> - <published>2008-09-29T03:14:47.461Z</published> - <updated>2008-09-29T03:14:47.461Z</updated> - <category scheme="http://schemas.google.com/g/2005#kind" term="http://schemas.google.com/health/kinds#profile"/> - <category term="DEMOGRAPHICS"/> - <category scheme="http://schemas.google.com/health/item" term="Demographics"/> - <title type="text">Demographics - - - - - - User Name - user@gmail.com - - - U5GDAVOxFbexQw3iyvqPYg - - - - - - - - - - - - - - - - 1984-07-22 - - - Male - - - - - - user@gmail.com - - Patient - - - - - - - - - https://www.google.com/health/feeds/profile/default/oIBDdgwFLyo - 2008-09-29T03:14:47.690Z - 2008-09-29T03:14:47.691Z - - - - FunctionalStatus - - - - - - User Name - user@gmail.com - - - W.EJcnhxb7W5M4eR4Tr1YA - - - - - - - - - - Pregnancy status - - - Not pregnant - - - - - user@gmail.com - - Patient - - - - - - - Breastfeeding status - - - Not breastfeeding - - - - - user@gmail.com - - Patient - - - - - - - - - - https://www.google.com/health/feeds/profile/default/wwljIlXuTVg - 2008-09-29T03:26:10.080Z - 2008-09-29T03:26:10.081Z - - - - - <content type="html"/> - <link rel="http://schemas.google.com/health/data#complete" type="application/atom+xml" href="https://www.google.com/health/feeds/profile/default/-/MEDICATION/%7Bhttp%3A%2F%2Fschemas.google.com%2Fg%2F2005%23kind%7Dhttp%3A%2F%2Fschemas.google.com%2Fhealth%2Fkinds%23profile/%7Bhttp%3A%2F%2Fschemas.google.com%2Fhealth%2Fitem%7DLipitor"/> - <link rel="self" type="application/atom+xml" href="https://www.google.com/health/feeds/profile/default/wwljIlXuTVg"/> - <link rel="edit" type="application/atom+xml" href="https://www.google.com/health/feeds/profile/default/wwljIlXuTVg"/> - <author> - <name>User Name</name> - <email>user@gmail.com</email> - </author> - <ContinuityOfCareRecord xmlns="urn:astm-org:CCR"> - <CCRDocumentObjectID>OrpghzvvbG_YaO5koqT2ug</CCRDocumentObjectID> - <Language/> - <DateTime> - <Type/> - </DateTime> - <Patient/> - <Body> - <Medications> - <Medication> - <Type/> - <Description/> - <Status> - <Text>ACTIVE</Text> - </Status> - <Source> - <Actor> - <ActorID>user@gmail.com</ActorID> - <ActorRole> - <Text>Patient</Text> - </ActorRole> - </Actor> - </Source> - <Product> - <ProductName> - <Text>Lipitor</Text> - </ProductName> - <Strength> - <Units/> - <StrengthSequencePosition>0</StrengthSequencePosition> - <VariableStrengthModifier/> - </Strength> - </Product> - <Directions> - <Direction> - <Description/> - <DeliveryMethod/> - <Dose> - <Units/> - <DoseSequencePosition>0</DoseSequencePosition> - <VariableDoseModifier/> - </Dose> - <Route> - <Text>By mouth</Text> - <Code> - <Value>C38288</Value> - <CodingSystem>FDA</CodingSystem> - </Code> - <RouteSequencePosition>0</RouteSequencePosition> - <MultipleRouteModifier/> - </Route> - </Direction> - </Directions> - <Refills/> - </Medication> - </Medications> - </Body> - </ContinuityOfCareRecord> -</entry> -<entry> - <id>https://www.google.com/health/feeds/profile/default/dd09TR12SiY</id> - <published>2008-09-29T07:52:17.175Z</published> - <updated>2008-09-29T07:52:17.176Z</updated> - <category scheme="http://schemas.google.com/g/2005#kind" term="http://schemas.google.com/health/kinds#profile"/> - <category scheme="http://schemas.google.com/health/item" term="Malaria"/> - <category term="CONDITION"/> - <title type="text"/> - <content type="html"/> - <link rel="http://schemas.google.com/health/data#complete" type="application/atom+xml" href="https://www.google.com/health/feeds/profile/default/-/%7Bhttp%3A%2F%2Fschemas.google.com%2Fg%2F2005%23kind%7Dhttp%3A%2F%2Fschemas.google.com%2Fhealth%2Fkinds%23profile/%7Bhttp%3A%2F%2Fschemas.google.com%2Fhealth%2Fitem%7DMalaria/CONDITION"/> - <link rel="self" type="application/atom+xml" href="https://www.google.com/health/feeds/profile/default/dd09TR12SiY"/> - <link rel="edit" type="application/atom+xml" href="https://www.google.com/health/feeds/profile/default/dd09TR12SiY"/> - <author> - <name>User Name</name> - <email>user@gmail.com</email> - </author> - <ContinuityOfCareRecord xmlns="urn:astm-org:CCR"> - <CCRDocumentObjectID>XF99N6X4lpy.jfPUPLMMSQ</CCRDocumentObjectID> - <Language/> - <DateTime> - <Type/> - </DateTime> - <Patient/> - <Body> - <Problems> - <Problem> - <Type/> - <Description> - <Text>Malaria</Text> - <Code> - <Value>136.9</Value> - <CodingSystem>ICD9_Broader</CodingSystem> - </Code> - <Code> - <Value>084.6</Value> - <CodingSystem>ICD9</CodingSystem> - </Code> - </Description> - <Status> - <Text>ACTIVE</Text> - </Status> - <Source> - <Actor> - <ActorID>user@gmail.com</ActorID> - <ActorRole> - <Text>Patient</Text> - </ActorRole> - </Actor> - </Source> - <HealthStatus> - <Description/> - </HealthStatus> - </Problem> - </Problems> - </Body> - </ContinuityOfCareRecord> -</entry> -<entry> - <id>https://www.google.com/health/feeds/profile/default/aS0Cf964DPs</id> - <published>2008-09-29T03:14:47.463Z</published> - <updated>2008-09-29T03:14:47.463Z</updated> - <category scheme="http://schemas.google.com/g/2005#kind" term="http://schemas.google.com/health/kinds#profile"/> - <category term="DEMOGRAPHICS"/> - <category scheme="http://schemas.google.com/health/item" term="SocialHistory (Drinking, Smoking)"/> - <title type="text">SocialHistory (Drinking, Smoking) - - - - - - User Name - user@gmail.com - - - kXylGU5YXLBzriv61xPGZQ - - - - - - - - - - Race - - S15814 - HL7 - - - - White - - - - - user@gmail.com - - Patient - - - - - - - - - - - - - - - https://www.google.com/health/feeds/profile/default/s5lII5xfj_g - 2008-09-29T03:14:47.544Z - 2008-09-29T03:14:47.545Z - - - - VitalSigns - - - - - - User Name - user@gmail.com - - - FTTIiY0TVVj35kZqFFjPjQ - - - - - - - - - - - - - - user@gmail.com - - Patient - - - - - - - - Height - - - - 0 - - 70 - - inches - - - - - - - - - - - - user@gmail.com - - Patient - - - - - - - - Weight - - - - 0 - - 2480 - - ounces - - - - - - - - - - - - user@gmail.com - - Patient - - - - - - - - Blood Type - - - - 0 - - O+ - - - - - - - - - -""" - -HEALTH_PROFILE_LIST_ENTRY = """ - - https://www.google.com/health/feeds/profile/list/vndCn5sdfwdEIY - 1970-01-01T00:00:00.000Z - profile name - vndCn5sdfwdEIY - - - - user@gmail.com - -""" - -BOOK_ENTRY = """"""\ - """"""\ - """http://www.google.com/books/feeds/volumes/b7GZr5Btp30C"""\ - """2009-04-24T23:35:16.000Z"""\ - """"""\ - """A theory of justice"""\ - """"""\ - """"""\ - """"""\ - """"""\ - """"""\ - """"""\ - """"""\ - """"""\ - """"""\ - """John Rawls"""\ - """1999"""\ - """p Since it appeared in 1971, John Rawls's i A Theory of Justice /i has become a classic. The author has now revised the original edition to clear up a number of difficulties he and others have found in the original book. /p p Rawls aims to express an essential part of the common core of the democratic tradition--justice as fairness--and to provide an alternative to utilitarianism, which had dominated the Anglo-Saxon tradition of political thought since the nineteenth century. Rawls substitutes the ideal of the social contract as a more satisfactory account of the basic rights and liberties of citizens as free and equal persons. "Each person," writes Rawls, "possesses an inviolability founded on justice that even the welfare of society as a whole cannot override." Advancing the ideas of Rousseau, Kant, Emerson, and Lincoln, Rawls's theory is as powerful today as it was when first published. /p"""\ - """538 pages"""\ - """b7GZr5Btp30C"""\ - """ISBN:0198250541"""\ - """ISBN:9780198250548"""\ - """en"""\ - """Oxford University Press"""\ - """A theory of justice"""\ -"""""" - -BOOK_FEED = """"""\ - """"""\ - """http://www.google.com/books/feeds/volumes"""\ - """2009-04-24T23:39:47.000Z"""\ - """"""\ - """Search results for 9780198250548"""\ - """"""\ - """"""\ - """"""\ - """"""\ - """Google Books Search"""\ - """http://www.google.com"""\ - """"""\ - """Google Book Search data API"""\ - """1"""\ - """1"""\ - """20"""\ - """"""\ - """http://www.google.com/books/feeds/volumes/b7GZr5Btp30C"""\ - """2009-04-24T23:39:47.000Z"""\ - """"""\ - """A theory of justice"""\ - """"""\ - """"""\ - """"""\ - """"""\ - """"""\ - """"""\ - """"""\ - """"""\ - """"""\ - """John Rawls"""\ - """1999"""\ - """... 9780198250548 ..."""\ - """538 pages"""\ - """b7GZr5Btp30C"""\ - """ISBN:0198250541"""\ - """ISBN:9780198250548"""\ - """Law"""\ - """A theory of justice"""\ - """"""\ -"""""" - -MAP_FEED = """ - - http://maps.google.com/maps/feeds/maps/208825816854482607313 - 2009-07-27T18:48:29.631Z - - My maps - - - - - - - Roman - - 1 - 1 - 1 - - http://maps.google.com/maps/feeds/maps/208825816854482607313/00046fb45f88fa910bcea - 2009-07-27T18:46:34.451Z - 2009-07-27T18:48:29.631Z - 2009-07-27T18:48:29.631Z - - yes - - - Untitled - - - - - - - Roman - - - -""" - -MAP_ENTRY = """ - - http://maps.google.com/maps/feeds/maps/208825816854482607313/00046fb45f88fa910bcea - 2009-07-27T18:46:34.451Z - 2009-07-27T18:48:29.631Z - 2009-07-27T18:48:29.631Z - - yes - - - Untitled - - - - - - - Roman - - -""" - -MAP_FEATURE_FEED = """ - - http://maps.google.com/maps/feeds/features/208825816854482607313/00046fb45f88fa910bcea - 2009-07-27T18:48:29.631Z - - Untitled - - - - - 4 - 1 - 4 - - http://maps.google.com/maps/feeds/features/208825816854482607313/00046fb45f88fa910bcea/00046fb4632573b19e0b7 - 2009-07-27T18:47:35.037Z - 2009-07-27T18:47:35.037Z - 2009-07-27T18:47:35.037Z - - Some feature title - - - Some feature title - Some feature content]]> - - - -113.818359,41.442726,0.0 - - - - - - - Roman - - - Roman - - - - http://maps.google.com/maps/feeds/features/208825816854482607313/00046fb45f88fa910bcea/00046fb46325e839a11e6 - 2009-07-27T18:47:35.067Z - 2009-07-27T18:48:22.184Z - 2009-07-27T18:48:22.184Z - - A cool poly! - - - A cool poly! - And a description]]> - - - - - 1 - -109.775391,47.457809,0.0 -99.755859,51.508742,0.0 -92.900391,48.04871,0.0 -92.8125,44.339565,0.0 -95.273437,44.402392,0.0 -97.207031,46.619261,0.0 -100.898437,46.073231,0.0 -102.480469,43.068888,0.0 -110.742187,45.274886,0.0 -109.775391,47.457809,0.0 - - - - - - - - - Roman - - - Roman - - - - http://maps.google.com/maps/feeds/features/208825816854482607313/00046fb45f88fa910bcea/00046fb465f5002e56b7a - 2009-07-27T18:48:22.194Z - 2009-07-27T18:48:22.194Z - 2009-07-27T18:48:22.194Z - - New Mexico - - - New Mexico - Word.]]> - - - 1 - -110.039062,37.788081,0.0 -103.183594,37.926868,0.0 -103.183594,32.472695,0.0 -108.896484,32.026706,0.0 -109.863281,31.203405,0.0 -110.039062,37.788081,0.0 - - - - - - - Roman - - - Roman - - - -""" - -MAP_FEATURE_ENTRY = """ - - http://maps.google.com/maps/feeds/features/208825816854482607313/00046fb45f88fa910bcea/00046fb4632573b19e0b7 - 2009-07-27T18:47:35.037Z - 2009-07-27T18:47:35.037Z - 2009-07-27T18:47:35.037Z - - Some feature title - - - Some feature title - Some feature content]]> - - - -113.818359,41.442726,0.0 - - - - - - - Roman - - - Roman - - -""" - -MAP_FEATURE_KML = """ - Some feature title - Some feature content]]> - - - -113.818359,41.442726,0.0 - - -""" - -SITES_LISTPAGE_ENTRY = ''' - - http:///sites.google.com/feeds/content/site/gdatatestsite/1712987567114738703 - 2009-06-16T00:37:37.393Z - - ListPagesTitle - -
- -
stuff go here
asdf
-
sdf
-
-
-
-
-
-
-
- - - - Test User - test@gmail.com - - - - - - - - - - - -
''' - -SITES_COMMENT_ENTRY = ''' - - http://sites.google.com/feeds/content/site/gdatatestsite/abc123 - 2009-06-15T18:40:22.407Z - - - <content type="xhtml"> - <div xmlns="http://www.w3.org/1999/xhtml">first comment</div> - </content> - <link rel="http://schemas.google.com/sites/2008#parent" type="application/atom+xml" href="http://sites.google.com/feeds/content/site/gdatatestsite/abc123parent"/> - <link rel="self" type="application/atom+xml" href="http://sites.google.com/feeds/content/site/gdatatestsite/abc123"/> - <link rel="edit" type="application/atom+xml" href="http://sites.google.com/feeds/content/site/gdatatestsite/abc123"/> - <author> - <name>Test User</name> - <email>test@gmail.com</email> - </author> - <thr:in-reply-to xmlns:thr="http://purl.org/syndication/thread/1.0" href="http://sites.google.com/site/gdatatestsite/annoucment/testpost" ref="http://sites.google.com/feeds/content/site/gdatatestsite/abc123" source="http://sites.google.com/feeds/content/site/gdatatestsite" type="text/html"/> -</entry>''' - -SITES_LISTITEM_ENTRY = '''<?xml version="1.0" encoding="UTF-8"?> -<entry xmlns="http://www.w3.org/2005/Atom"> - <id>http://sites.google.com/feeds/content/site/gdatatestsite/abc123</id> - <updated>2009-06-16T00:34:55.633Z</updated> - <category scheme="http://schemas.google.com/g/2005#kind" term="http://schemas.google.com/sites/2008#listitem"/> - <title type="text"/> - <link rel="http://schemas.google.com/sites/2008#parent" type="application/atom+xml" href="http://sites.google.com/feeds/content/site/gdatatestsite/abc123def"/> - <link rel="self" type="application/atom+xml" href="http://sites.google.com/feeds/content/site/gdatatestsite/abc123"/> - <link rel="edit" type="application/atom+xml" href="http://sites.google.com/feeds/content/site/gdatatestsite/abc123"/> - <author> - <name>Test User</name> - <email>test@gmail.com</email> - </author> - <gs:field xmlns:gs="http://schemas.google.com/spreadsheets/2006" index="A" name="Owner">test value</gs:field> - <gs:field xmlns:gs="http://schemas.google.com/spreadsheets/2006" index="B" name="Description">test</gs:field> - <gs:field xmlns:gs="http://schemas.google.com/spreadsheets/2006" index="C" name="Resolution">90</gs:field> - <gs:field xmlns:gs="http://schemas.google.com/spreadsheets/2006" index="D" name="Complete"/> - <gs:field xmlns:gs="http://schemas.google.com/spreadsheets/2006" index="E" name="MyCo">2009-05-31</gs:field> -</entry>''' - -SITES_CONTENT_FEED = '''<?xml version="1.0" encoding="UTF-8"?> -<feed xmlns="http://www.w3.org/2005/Atom" xmlns:openSearch="http://a9.com/-/spec/opensearch/1.1/" -xmlns:sites="http://schemas.google.com/sites/2008" xmlns:gs="http://schemas.google.com/spreadsheets/2006" -xmlns:dc="http://purl.org/dc/terms" xmlns:batch="http://schemas.google.com/gdata/batch" -xmlns:gd="http://schemas.google.com/g/2005" xmlns:thr="http://purl.org/syndication/thread/1.0"> -<id>http://sites.google.com/feeds/content/site/gdatatestsite</id> -<updated>2009-06-15T21:35:43.282Z</updated> -<link rel="http://schemas.google.com/g/2005#feed" type="application/atom+xml" href="http://sites.google.com/feeds/content/site/gdatatestsite"/> -<link rel="http://schemas.google.com/g/2005#post" type="application/atom+xml" href="http://sites.google.com/feeds/content/site/gdatatestsite"/> -<link rel="self" type="application/atom+xml" href="http://sites.google.com/feeds/content/site/gdatatestsite"/> -<generator version="1" uri="http://sites.google.com">Google Sites</generator> -<openSearch:startIndex>1</openSearch:startIndex> -<entry> - <id>http:///sites.google.com/feeds/content/site/gdatatestsite/1712987567114738703</id> - <updated>2009-06-16T00:37:37.393Z</updated> - <category scheme="http://schemas.google.com/g/2005#kind" term="http://schemas.google.com/sites/2008#listpage"/> - <title type="text">ListPagesTitle - -
- -
stuff go here
asdf
-
sdf
-
-
-
-
-
-
-
- - - - - - Test User - test@gmail.com - - - - - - - - - - - 2 - - home - -
- - http://sites.google.com/feeds/content/site/gdatatestsite/abc123 - 2009-06-17T00:40:37.082Z - - filecabinet - -
- -
sdf
-
-
-
- - - - - - Test User - test@gmail.com - - -
- - http://sites.google.com/feeds/content/site/gdatatestsite/abc123 - 2009-06-16T00:34:55.633Z - - - <link rel="http://schemas.google.com/sites/2008#parent" type="application/atom+xml" href="http://sites.google.com/feeds/content/site/gdatatestsite/abc123def"/> - <link rel="self" type="application/atom+xml" href="http://sites.google.com/feeds/content/site/gdatatestsite/abc123"/> - <link rel="edit" type="application/atom+xml" href="http://sites.google.com/feeds/content/site/gdatatestsite/abc123"/> - <link rel="http://schemas.google.com/sites/2008#revision" type="application/atom+xml" href="http:///sites.google.com/feeds/content/site/gdatatestsite/abc123"/> - <author> - <name>Test User</name> - <email>test@gmail.com</email> - </author> - <gs:field xmlns:gs="http://schemas.google.com/spreadsheets/2006" index="A" name="Owner">test value</gs:field> - <gs:field xmlns:gs="http://schemas.google.com/spreadsheets/2006" index="B" name="Description">test</gs:field> - <gs:field xmlns:gs="http://schemas.google.com/spreadsheets/2006" index="C" name="Resolution">90</gs:field> - <gs:field xmlns:gs="http://schemas.google.com/spreadsheets/2006" index="D" name="Complete"/> - <gs:field xmlns:gs="http://schemas.google.com/spreadsheets/2006" index="E" name="MyCo">2009-05-31</gs:field> -</entry> -<entry> - <id>http://sites.google.com/feeds/content/site/gdatatestsite/abc123</id> - <updated>2009-06-15T18:40:32.922Z</updated> - <category scheme="http://schemas.google.com/g/2005#kind" term="http://schemas.google.com/sites/2008#attachment"/> - <title type="text">testFile.ods - - - - - - - Test User - test@gmail.com - - - something else - - - http://sites.google.com/feeds/content/site/gdatatestsite/abc123 - 2009-06-15T18:40:22.407Z - - - <content type="xhtml"> - <div xmlns="http://www.w3.org/1999/xhtml">first comment</div> - </content> - <link rel="http://schemas.google.com/sites/2008#parent" type="application/atom+xml" href="http://sites.google.com/feeds/content/site/gdatatestsite/abc123"/> - <link rel="self" type="application/atom+xml" href="http://sites.google.com/feeds/content/site/gdatatestsite/abc123"/> - <link rel="edit" type="application/atom+xml" href="http://sites.google.com/feeds/content/site/gdatatestsite/abc123"/> - <link rel="http://schemas.google.com/sites/2008#revision" type="application/atom+xml" href="http:///sites.google.com/feeds/content/site/gdatatestsite/abc123"/> - <author> - <name>Test User</name> - <email>test@gmail.com</email> - </author> - <thr:in-reply-to xmlns:thr="http://purl.org/syndication/thread/1.0" href="http://sites.google.com/site/gdatatestsite/annoucment/testpost" ref="http://sites.google.com/feeds/content/site/gdatatestsite/abc123" source="http://sites.google.com/feeds/content/site/gdatatestsite" type="text/html"/> -</entry> -<entry> - <id>http://sites.google.com/feeds/content/site/gdatatestsite/abc123</id> - <updated>2009-06-15T18:40:16.388Z</updated> - <category scheme="http://schemas.google.com/g/2005#kind" term="http://schemas.google.com/sites/2008#announcement"/> - <title type="text">TestPost - -
- -
content goes here
-
-
-
- - - - - - Test User - test@gmail.com - -
- - http://sites.google.com/feeds/content/site/gdatatestsite/abc123 - 2009-06-12T23:37:59.417Z - - Home - -
- -
Some Content goes here
-
-
-
- -
-
-
-
-
-
- - - - - Test User - test@gmail.com - -
- - http://sites.google.com/feeds/content/site/gdatatestsite/2639323850129333500 - 2009-06-12T23:32:09.191Z - - annoucment - -
-
-
- - - - - Test User - test@gmail.com - - -
-''' - -SITES_ACTIVITY_FEED = ''' - -http://sites.google.com/feeds/activity/site/siteName -2009-08-19T05:46:01.503Z -Activity - - - -Google Sites -1 - -http://sites.google.com/feeds/activity/site/siteName/197441951793148343 -2009-08-17T00:08:19.387Z - -NewWebpage3 - -
User deleted NewWebpage3 -
-
- - - - - User - user@gmail.com - -
- -http://sites.google.com/feeds/activity/site/siteName/7299542210274956360 -2009-08-17T00:08:03.711Z - -NewWebpage3 - -
User edited NewWebpage3 -
-
- - - - - User - user@gmail.com - -
-
''' - -SITES_REVISION_FEED = ''' - -http://sites.google.com/feeds/revision/site/siteName/2947510322163358574 -2009-08-19T06:20:18.151Z -Revisions - - -Google Sites -1 - -http://sites.google.com/feeds/revision/site/siteName/2947510322163358574/1 -2009-08-19T04:33:14.856Z - - -<content type="xhtml"> - <div xmlns="http://www.w3.org/1999/xhtml"> - <table cellspacing="0" class="sites-layout-name-one-column sites-layout-hbox"> - <tbody> - <tr> - <td class="sites-layout-tile sites-tile-name-content-1">testcomment</td> - </tr> - </tbody> - </table> -</div> -</content> -<link rel="http://schemas.google.com/sites/2008#parent" type="application/atom+xml" href="http://sites.google.com/feeds/content/site/siteName/54395424125706119"/> -<link rel="alternate" type="text" href="http://sites.google.com/site/system/app/pages/admin/compare?wuid=wuid%3Agx%3A28e7a9057c581b6e&rev1=1"/> -<link rel="self" type="application/atom+xml" href="http://sites.google.com/feeds/revision/site/siteName/2947510322163358574/1"/> -<author> - <name>User</name> - <email>user@gmail.com</email> -</author> -<thr:in-reply-to href="http://sites.google.com/site/siteName/code/js" ref="http://sites.google.com/feeds/content/site/siteName/54395424125706119" source="http://sites.google.com/feeds/content/google.com/siteName" type="text/html;charset=UTF-8"/> -<sites:revision>1</sites:revision> -</entry> -</feed>''' - -SITES_SITE_FEED = ''' -<feed xmlns="http://www.w3.org/2005/Atom" xmlns:openSearch="http://a9.com/-/spec/opensearch/1.1/" xmlns:gAcl="http://schemas.google.com/acl/2007" xmlns:sites="http://schemas.google.com/sites/2008" xmlns:gs="http://schemas.google.com/spreadsheets/2006" xmlns:dc="http://purl.org/dc/terms" xmlns:batch="http://schemas.google.com/gdata/batch" xmlns:gd="http://schemas.google.com/g/2005" xmlns:thr="http://purl.org/syndication/thread/1.0"> -<id>https://sites.google.com/feeds/site/example.com</id> -<updated>2009-12-09T01:05:54.631Z</updated> -<title>Site - - - -Google Sites -1 - -https://sites.google.com/feeds/site/example.com/new-test-site -2009-12-02T22:55:31.040Z -2009-12-02T22:55:31.040Z -New Test Site -A new site to hold memories - - - - - -new-test-site -iceberg - - -https://sites.google.com/feeds/site/example.com/newautosite2 -2009-12-05T00:28:01.077Z -2009-12-05T00:28:01.077Z -newAutoSite3 -A new site to hold memories2 - - - - -newautosite2 -default - -''' - -SITES_ACL_FEED = ''' - -https://sites.google.comsites.google.com/feeds/acl/site/example.com/new-test-site -2009-12-09T01:24:59.080Z - -Acl - - - -Google Sites -1 - - https://sites.google.com/feeds/acl/site/google.com/new-test-site/user%3Auser%40example.com - 2009-12-09T01:24:59.080Z - 2009-12-09T01:24:59.080Z - - - - - - -''' - -ANALYTICS_ACCOUNT_FEED_old = ''' - -http://www.google.com/analytics/feeds/accounts/abc@test.com -2009-06-25T03:55:22.000-07:00 -Profile list for abc@test.com - - -Google Analytics - -Google Analytics -12 -1 -12 - -http://www.google.com/analytics/feeds/accounts/ga:1174 -2009-06-25T03:55:22.000-07:00 -www.googlestore.com - -ga:1174 - - - - - - - -''' - -ANALYTICS_ACCOUNT_FEED = ''' - - http://www.google.com/analytics/feeds/accounts/api.nickm@google.com - 2009-10-14T09:14:25.000-07:00 - Profile list for abc@test.com - - - Google Analytics - - Google Analytics - 37 - 1 - 37 - - ga:operatingSystem==iPhone - - - http://www.google.com/analytics/feeds/accounts/ga:1174 - 2009-10-14T09:14:25.000-07:00 - www.googlestore.com - - - - - - - - - - - - - - - - - - - - - - ga:1174 - -''' - -ANALYTICS_DATA_FEED = ''' - - http://www.google.com/analytics/feeds/data?ids=ga:1174&dimensions=ga:medium,ga:source&metrics=ga:bounces,ga:visits&filters=ga:medium%3D%3Dreferral&start-date=2008-10-01&end-date=2008-10-31 - 2008-10-31T16:59:59.999-07:00 - Google Analytics Data for Profile 1174 - - - - Google Analytics - - Google Analytics - 6451 - 1 - 2 - 2008-10-01 - 2008-10-31 - - ga:operatingSystem==iPhone - - - - - - true - - ga:1174 - www.googlestore.com - - - - - - http://www.google.com/analytics/feeds/data?ids=ga:1174&ga:medium=referral&ga:source=blogger.com&filters=ga:medium%3D%3Dreferral&start-date=2008-10-01&end-date=2008-10-31 - 2008-10-30T17:00:00.001-07:00 - ga:source=blogger.com | ga:medium=referral - - - - - - -''' - - -ANALYTICS_MGMT_PROFILE_FEED = ''' - - https://www.google.com/analytics/feeds/datasources/ga/accounts/~all/webproperties/~all/profiles - 2010-06-14T22:18:48.676Z - Google Analytics Profiles for superman@gmail.com - - - Google Analytics - - Google Analytics - 1 - 1 - 1000 - - https://www.google.com/analytics/feeds/datasources/ga/accounts/30481/webproperties/UA-30481-1/profiles/1174 - 2010-06-09T05:58:15.436-07:00 - Google Analytics Profile www.googlestore.com - - - - - - - - - - - - -''' - -ANALYTICS_MGMT_GOAL_FEED = ''' - - https://www.google.com/analytics/feeds/datasources/ga/accounts/~all/webproperties/~all/profiles/~all/goals - 2010-06-14T22:21:18.485Z - Google Analytics Goals for superman@gmail.com - - - Google Analytics - - Google Analytics - 3 - 1 - 1000 - - https://www.google.com/analytics/feeds/datasources/ga/accounts/30481/webproperties/UA-30481-1/profiles/1174/goals/1 - 2010-02-07T13:12:43.377-08:00 - Google Analytics Goal 1 - - - - - - - - - - - https://www.google.com/analytics/feeds/datasources/ga/accounts/30481/webproperties/UA-30481-1/profiles/1174/goals/2 - 2010-02-07T13:12:43.376-08:00 - Google Analytics Goal 2 - - - - - - - - -''' - -ANALYTICS_MGMT_ADV_SEGMENT_FEED = ''' - - https://www.google.com/analytics/feeds/datasources/ga/segments - 2010-06-14T22:22:02.728Z - Google Analytics Advanced Segments for superman@gmail.com - - - Google Analytics - - Google Analytics - 2 - 1 - 1000 - - https://www.google.com/analytics/feeds/datasources/ga/segments/gaid::0 - 2009-10-26T13:00:44.915-07:00 - Google Analytics Advanced Segment Sources Form Google - - - ga:source=~^\Qgoogle\E - - - -''' - -MULTIDOMAIN_USER_ENTRY = """ - - - - - - - -""" - -MULTIDOMAIN_USER_FEED = """ - - https://apps-apis.google.com/a/feeds/user/2.0/example.com - 2010-01-26T23:38:13.215Z - - - - 1 - - https://apps-apis.google.com/a/feeds/user/2.0/example.com/admin%40example.com - 2010-01-26T23:38:13.210Z - - - - - - - - - - - - https://apps-apis.google.com/a/feeds/user/2.0/example.com/liz%40example.com - 2010-01-26T23:38:13.210Z - - - - - - - - - - -""" - -MULTIDOMAIN_USER_RENAME_REQUEST = """ - - -""" - -MULTIDOMAIN_ALIAS_ENTRY = """ - - https://apps-apis.google.com/a/feeds/alias/2.0/gethelp_example.com/helpdesk%40gethelp_example.com - 2008-10-17T15:02:45.646Z - - - - -""" - -MULTIDOMAIN_ALIAS_FEED = """ - - https://apps-apis.google.com/a/feeds/alias/2.0/gethelp_example.com - 2010-01-26T23:38:13.215Z - - - - 1 - - https://apps-apis.google.com/a/feeds/alias/2.0/gethelp_example.com/helpdesk%40gethelp_example.com - 2010-01-26T23:38:13.210Z - - - - - - - https://apps-apis.google.com/a/feeds/alias/2.0/gethelp_example.com/support%40gethelp_example.com - 2010-01-26T23:38:13.210Z - - - - - -""" diff --git a/gdata/analytics/tlslite/BaseDB.py b/gdata/analytics/tlslite/BaseDB.py deleted file mode 100644 index ca8dff6b40..0000000000 --- a/gdata/analytics/tlslite/BaseDB.py +++ /dev/null @@ -1,120 +0,0 @@ -"""Base class for SharedKeyDB and VerifierDB.""" - -import anydbm -import thread - -class BaseDB: - def __init__(self, filename, type): - self.type = type - self.filename = filename - if self.filename: - self.db = None - else: - self.db = {} - self.lock = thread.allocate_lock() - - def create(self): - """Create a new on-disk database. - - @raise anydbm.error: If there's a problem creating the database. - """ - if self.filename: - self.db = anydbm.open(self.filename, "n") #raises anydbm.error - self.db["--Reserved--type"] = self.type - self.db.sync() - else: - self.db = {} - - def open(self): - """Open a pre-existing on-disk database. - - @raise anydbm.error: If there's a problem opening the database. - @raise ValueError: If the database is not of the right type. - """ - if not self.filename: - raise ValueError("Can only open on-disk databases") - self.db = anydbm.open(self.filename, "w") #raises anydbm.error - try: - if self.db["--Reserved--type"] != self.type: - raise ValueError("Not a %s database" % self.type) - except KeyError: - raise ValueError("Not a recognized database") - - def __getitem__(self, username): - if self.db == None: - raise AssertionError("DB not open") - - self.lock.acquire() - try: - valueStr = self.db[username] - finally: - self.lock.release() - - return self._getItem(username, valueStr) - - def __setitem__(self, username, value): - if self.db == None: - raise AssertionError("DB not open") - - valueStr = self._setItem(username, value) - - self.lock.acquire() - try: - self.db[username] = valueStr - if self.filename: - self.db.sync() - finally: - self.lock.release() - - def __delitem__(self, username): - if self.db == None: - raise AssertionError("DB not open") - - self.lock.acquire() - try: - del(self.db[username]) - if self.filename: - self.db.sync() - finally: - self.lock.release() - - def __contains__(self, username): - """Check if the database contains the specified username. - - @type username: str - @param username: The username to check for. - - @rtype: bool - @return: True if the database contains the username, False - otherwise. - - """ - if self.db == None: - raise AssertionError("DB not open") - - self.lock.acquire() - try: - return self.db.has_key(username) - finally: - self.lock.release() - - def check(self, username, param): - value = self.__getitem__(username) - return self._checkItem(value, username, param) - - def keys(self): - """Return a list of usernames in the database. - - @rtype: list - @return: The usernames in the database. - """ - if self.db == None: - raise AssertionError("DB not open") - - self.lock.acquire() - try: - usernames = self.db.keys() - finally: - self.lock.release() - usernames = [u for u in usernames if not u.startswith("--Reserved--")] - return usernames \ No newline at end of file diff --git a/gdata/analytics/tlslite/Checker.py b/gdata/analytics/tlslite/Checker.py deleted file mode 100644 index f978697628..0000000000 --- a/gdata/analytics/tlslite/Checker.py +++ /dev/null @@ -1,146 +0,0 @@ -"""Class for post-handshake certificate checking.""" - -from utils.cryptomath import hashAndBase64 -from X509 import X509 -from X509CertChain import X509CertChain -from errors import * - - -class Checker: - """This class is passed to a handshake function to check the other - party's certificate chain. - - If a handshake function completes successfully, but the Checker - judges the other party's certificate chain to be missing or - inadequate, a subclass of - L{tlslite.errors.TLSAuthenticationError} will be raised. - - Currently, the Checker can check either an X.509 or a cryptoID - chain (for the latter, cryptoIDlib must be installed). - """ - - def __init__(self, cryptoID=None, protocol=None, - x509Fingerprint=None, - x509TrustList=None, x509CommonName=None, - checkResumedSession=False): - """Create a new Checker instance. - - You must pass in one of these argument combinations: - - cryptoID[, protocol] (requires cryptoIDlib) - - x509Fingerprint - - x509TrustList[, x509CommonName] (requires cryptlib_py) - - @type cryptoID: str - @param cryptoID: A cryptoID which the other party's certificate - chain must match. The cryptoIDlib module must be installed. - Mutually exclusive with all of the 'x509...' arguments. - - @type protocol: str - @param protocol: A cryptoID protocol URI which the other - party's certificate chain must match. Requires the 'cryptoID' - argument. - - @type x509Fingerprint: str - @param x509Fingerprint: A hex-encoded X.509 end-entity - fingerprint which the other party's end-entity certificate must - match. Mutually exclusive with the 'cryptoID' and - 'x509TrustList' arguments. - - @type x509TrustList: list of L{tlslite.X509.X509} - @param x509TrustList: A list of trusted root certificates. The - other party must present a certificate chain which extends to - one of these root certificates. The cryptlib_py module must be - installed. Mutually exclusive with the 'cryptoID' and - 'x509Fingerprint' arguments. - - @type x509CommonName: str - @param x509CommonName: The end-entity certificate's 'CN' field - must match this value. For a web server, this is typically a - server name such as 'www.amazon.com'. Mutually exclusive with - the 'cryptoID' and 'x509Fingerprint' arguments. Requires the - 'x509TrustList' argument. - - @type checkResumedSession: bool - @param checkResumedSession: If resumed sessions should be - checked. This defaults to False, on the theory that if the - session was checked once, we don't need to bother - re-checking it. - """ - - if cryptoID and (x509Fingerprint or x509TrustList): - raise ValueError() - if x509Fingerprint and x509TrustList: - raise ValueError() - if x509CommonName and not x509TrustList: - raise ValueError() - if protocol and not cryptoID: - raise ValueError() - if cryptoID: - import cryptoIDlib #So we raise an error here - if x509TrustList: - import cryptlib_py #So we raise an error here - self.cryptoID = cryptoID - self.protocol = protocol - self.x509Fingerprint = x509Fingerprint - self.x509TrustList = x509TrustList - self.x509CommonName = x509CommonName - self.checkResumedSession = checkResumedSession - - def __call__(self, connection): - """Check a TLSConnection. - - When a Checker is passed to a handshake function, this will - be called at the end of the function. - - @type connection: L{tlslite.TLSConnection.TLSConnection} - @param connection: The TLSConnection to examine. - - @raise tlslite.errors.TLSAuthenticationError: If the other - party's certificate chain is missing or bad. - """ - if not self.checkResumedSession and connection.resumed: - return - - if self.cryptoID or self.x509Fingerprint or self.x509TrustList: - if connection._client: - chain = connection.session.serverCertChain - else: - chain = connection.session.clientCertChain - - if self.x509Fingerprint or self.x509TrustList: - if isinstance(chain, X509CertChain): - if self.x509Fingerprint: - if chain.getFingerprint() != self.x509Fingerprint: - raise TLSFingerprintError(\ - "X.509 fingerprint mismatch: %s, %s" % \ - (chain.getFingerprint(), self.x509Fingerprint)) - else: #self.x509TrustList - if not chain.validate(self.x509TrustList): - raise TLSValidationError("X.509 validation failure") - if self.x509CommonName and \ - (chain.getCommonName() != self.x509CommonName): - raise TLSAuthorizationError(\ - "X.509 Common Name mismatch: %s, %s" % \ - (chain.getCommonName(), self.x509CommonName)) - elif chain: - raise TLSAuthenticationTypeError() - else: - raise TLSNoAuthenticationError() - elif self.cryptoID: - import cryptoIDlib.CertChain - if isinstance(chain, cryptoIDlib.CertChain.CertChain): - if chain.cryptoID != self.cryptoID: - raise TLSFingerprintError(\ - "cryptoID mismatch: %s, %s" % \ - (chain.cryptoID, self.cryptoID)) - if self.protocol: - if not chain.checkProtocol(self.protocol): - raise TLSAuthorizationError(\ - "cryptoID protocol mismatch") - if not chain.validate(): - raise TLSValidationError("cryptoID validation failure") - elif chain: - raise TLSAuthenticationTypeError() - else: - raise TLSNoAuthenticationError() - diff --git a/gdata/analytics/tlslite/FileObject.py b/gdata/analytics/tlslite/FileObject.py deleted file mode 100644 index 6ee02b2436..0000000000 --- a/gdata/analytics/tlslite/FileObject.py +++ /dev/null @@ -1,220 +0,0 @@ -"""Class returned by TLSConnection.makefile().""" - -class FileObject: - """This class provides a file object interface to a - L{tlslite.TLSConnection.TLSConnection}. - - Call makefile() on a TLSConnection to create a FileObject instance. - - This class was copied, with minor modifications, from the - _fileobject class in socket.py. Note that fileno() is not - implemented.""" - - default_bufsize = 16384 #TREV: changed from 8192 - - def __init__(self, sock, mode='rb', bufsize=-1): - self._sock = sock - self.mode = mode # Not actually used in this version - if bufsize < 0: - bufsize = self.default_bufsize - self.bufsize = bufsize - self.softspace = False - if bufsize == 0: - self._rbufsize = 1 - elif bufsize == 1: - self._rbufsize = self.default_bufsize - else: - self._rbufsize = bufsize - self._wbufsize = bufsize - self._rbuf = "" # A string - self._wbuf = [] # A list of strings - - def _getclosed(self): - return self._sock is not None - closed = property(_getclosed, doc="True if the file is closed") - - def close(self): - try: - if self._sock: - for result in self._sock._decrefAsync(): #TREV - pass - finally: - self._sock = None - - def __del__(self): - try: - self.close() - except: - # close() may fail if __init__ didn't complete - pass - - def flush(self): - if self._wbuf: - buffer = "".join(self._wbuf) - self._wbuf = [] - self._sock.sendall(buffer) - - #def fileno(self): - # raise NotImplementedError() #TREV - - def write(self, data): - data = str(data) # XXX Should really reject non-string non-buffers - if not data: - return - self._wbuf.append(data) - if (self._wbufsize == 0 or - self._wbufsize == 1 and '\n' in data or - self._get_wbuf_len() >= self._wbufsize): - self.flush() - - def writelines(self, list): - # XXX We could do better here for very long lists - # XXX Should really reject non-string non-buffers - self._wbuf.extend(filter(None, map(str, list))) - if (self._wbufsize <= 1 or - self._get_wbuf_len() >= self._wbufsize): - self.flush() - - def _get_wbuf_len(self): - buf_len = 0 - for x in self._wbuf: - buf_len += len(x) - return buf_len - - def read(self, size=-1): - data = self._rbuf - if size < 0: - # Read until EOF - buffers = [] - if data: - buffers.append(data) - self._rbuf = "" - if self._rbufsize <= 1: - recv_size = self.default_bufsize - else: - recv_size = self._rbufsize - while True: - data = self._sock.recv(recv_size) - if not data: - break - buffers.append(data) - return "".join(buffers) - else: - # Read until size bytes or EOF seen, whichever comes first - buf_len = len(data) - if buf_len >= size: - self._rbuf = data[size:] - return data[:size] - buffers = [] - if data: - buffers.append(data) - self._rbuf = "" - while True: - left = size - buf_len - recv_size = max(self._rbufsize, left) - data = self._sock.recv(recv_size) - if not data: - break - buffers.append(data) - n = len(data) - if n >= left: - self._rbuf = data[left:] - buffers[-1] = data[:left] - break - buf_len += n - return "".join(buffers) - - def readline(self, size=-1): - data = self._rbuf - if size < 0: - # Read until \n or EOF, whichever comes first - if self._rbufsize <= 1: - # Speed up unbuffered case - assert data == "" - buffers = [] - recv = self._sock.recv - while data != "\n": - data = recv(1) - if not data: - break - buffers.append(data) - return "".join(buffers) - nl = data.find('\n') - if nl >= 0: - nl += 1 - self._rbuf = data[nl:] - return data[:nl] - buffers = [] - if data: - buffers.append(data) - self._rbuf = "" - while True: - data = self._sock.recv(self._rbufsize) - if not data: - break - buffers.append(data) - nl = data.find('\n') - if nl >= 0: - nl += 1 - self._rbuf = data[nl:] - buffers[-1] = data[:nl] - break - return "".join(buffers) - else: - # Read until size bytes or \n or EOF seen, whichever comes first - nl = data.find('\n', 0, size) - if nl >= 0: - nl += 1 - self._rbuf = data[nl:] - return data[:nl] - buf_len = len(data) - if buf_len >= size: - self._rbuf = data[size:] - return data[:size] - buffers = [] - if data: - buffers.append(data) - self._rbuf = "" - while True: - data = self._sock.recv(self._rbufsize) - if not data: - break - buffers.append(data) - left = size - buf_len - nl = data.find('\n', 0, left) - if nl >= 0: - nl += 1 - self._rbuf = data[nl:] - buffers[-1] = data[:nl] - break - n = len(data) - if n >= left: - self._rbuf = data[left:] - buffers[-1] = data[:left] - break - buf_len += n - return "".join(buffers) - - def readlines(self, sizehint=0): - total = 0 - list = [] - while True: - line = self.readline() - if not line: - break - list.append(line) - total += len(line) - if sizehint and total >= sizehint: - break - return list - - # Iterator protocols - - def __iter__(self): - return self - - def next(self): - line = self.readline() - if not line: - raise StopIteration - return line diff --git a/gdata/analytics/tlslite/HandshakeSettings.py b/gdata/analytics/tlslite/HandshakeSettings.py deleted file mode 100644 index c7c3223e51..0000000000 --- a/gdata/analytics/tlslite/HandshakeSettings.py +++ /dev/null @@ -1,159 +0,0 @@ -"""Class for setting handshake parameters.""" - -from constants import CertificateType -from utils import cryptomath -from utils import cipherfactory - -class HandshakeSettings: - """This class encapsulates various parameters that can be used with - a TLS handshake. - @sort: minKeySize, maxKeySize, cipherNames, certificateTypes, - minVersion, maxVersion - - @type minKeySize: int - @ivar minKeySize: The minimum bit length for asymmetric keys. - - If the other party tries to use SRP, RSA, or Diffie-Hellman - parameters smaller than this length, an alert will be - signalled. The default is 1023. - - @type maxKeySize: int - @ivar maxKeySize: The maximum bit length for asymmetric keys. - - If the other party tries to use SRP, RSA, or Diffie-Hellman - parameters larger than this length, an alert will be signalled. - The default is 8193. - - @type cipherNames: list - @ivar cipherNames: The allowed ciphers, in order of preference. - - The allowed values in this list are 'aes256', 'aes128', '3des', and - 'rc4'. If these settings are used with a client handshake, they - determine the order of the ciphersuites offered in the ClientHello - message. - - If these settings are used with a server handshake, the server will - choose whichever ciphersuite matches the earliest entry in this - list. - - NOTE: If '3des' is used in this list, but TLS Lite can't find an - add-on library that supports 3DES, then '3des' will be silently - removed. - - The default value is ['aes256', 'aes128', '3des', 'rc4']. - - @type certificateTypes: list - @ivar certificateTypes: The allowed certificate types, in order of - preference. - - The allowed values in this list are 'x509' and 'cryptoID'. This - list is only used with a client handshake. The client will - advertise to the server which certificate types are supported, and - will check that the server uses one of the appropriate types. - - NOTE: If 'cryptoID' is used in this list, but cryptoIDlib is not - installed, then 'cryptoID' will be silently removed. - - @type minVersion: tuple - @ivar minVersion: The minimum allowed SSL/TLS version. - - This variable can be set to (3,0) for SSL 3.0, (3,1) for - TLS 1.0, or (3,2) for TLS 1.1. If the other party wishes to - use a lower version, a protocol_version alert will be signalled. - The default is (3,0). - - @type maxVersion: tuple - @ivar maxVersion: The maximum allowed SSL/TLS version. - - This variable can be set to (3,0) for SSL 3.0, (3,1) for - TLS 1.0, or (3,2) for TLS 1.1. If the other party wishes to - use a higher version, a protocol_version alert will be signalled. - The default is (3,2). (WARNING: Some servers may (improperly) - reject clients which offer support for TLS 1.1. In this case, - try lowering maxVersion to (3,1)). - """ - def __init__(self): - self.minKeySize = 1023 - self.maxKeySize = 8193 - self.cipherNames = ["aes256", "aes128", "3des", "rc4"] - self.cipherImplementations = ["cryptlib", "openssl", "pycrypto", - "python"] - self.certificateTypes = ["x509", "cryptoID"] - self.minVersion = (3,0) - self.maxVersion = (3,2) - - #Filters out options that are not supported - def _filter(self): - other = HandshakeSettings() - other.minKeySize = self.minKeySize - other.maxKeySize = self.maxKeySize - other.cipherNames = self.cipherNames - other.cipherImplementations = self.cipherImplementations - other.certificateTypes = self.certificateTypes - other.minVersion = self.minVersion - other.maxVersion = self.maxVersion - - if not cipherfactory.tripleDESPresent: - other.cipherNames = [e for e in self.cipherNames if e != "3des"] - if len(other.cipherNames)==0: - raise ValueError("No supported ciphers") - - try: - import cryptoIDlib - except ImportError: - other.certificateTypes = [e for e in self.certificateTypes \ - if e != "cryptoID"] - if len(other.certificateTypes)==0: - raise ValueError("No supported certificate types") - - if not cryptomath.cryptlibpyLoaded: - other.cipherImplementations = [e for e in \ - self.cipherImplementations if e != "cryptlib"] - if not cryptomath.m2cryptoLoaded: - other.cipherImplementations = [e for e in \ - other.cipherImplementations if e != "openssl"] - if not cryptomath.pycryptoLoaded: - other.cipherImplementations = [e for e in \ - other.cipherImplementations if e != "pycrypto"] - if len(other.cipherImplementations)==0: - raise ValueError("No supported cipher implementations") - - if other.minKeySize<512: - raise ValueError("minKeySize too small") - if other.minKeySize>16384: - raise ValueError("minKeySize too large") - if other.maxKeySize<512: - raise ValueError("maxKeySize too small") - if other.maxKeySize>16384: - raise ValueError("maxKeySize too large") - for s in other.cipherNames: - if s not in ("aes256", "aes128", "rc4", "3des"): - raise ValueError("Unknown cipher name: '%s'" % s) - for s in other.cipherImplementations: - if s not in ("cryptlib", "openssl", "python", "pycrypto"): - raise ValueError("Unknown cipher implementation: '%s'" % s) - for s in other.certificateTypes: - if s not in ("x509", "cryptoID"): - raise ValueError("Unknown certificate type: '%s'" % s) - - if other.minVersion > other.maxVersion: - raise ValueError("Versions set incorrectly") - - if not other.minVersion in ((3,0), (3,1), (3,2)): - raise ValueError("minVersion set incorrectly") - - if not other.maxVersion in ((3,0), (3,1), (3,2)): - raise ValueError("maxVersion set incorrectly") - - return other - - def _getCertificateTypes(self): - l = [] - for ct in self.certificateTypes: - if ct == "x509": - l.append(CertificateType.x509) - elif ct == "cryptoID": - l.append(CertificateType.cryptoID) - else: - raise AssertionError() - return l diff --git a/gdata/analytics/tlslite/Session.py b/gdata/analytics/tlslite/Session.py deleted file mode 100644 index a951f45894..0000000000 --- a/gdata/analytics/tlslite/Session.py +++ /dev/null @@ -1,131 +0,0 @@ -"""Class representing a TLS session.""" - -from utils.compat import * -from mathtls import * -from constants import * - -class Session: - """ - This class represents a TLS session. - - TLS distinguishes between connections and sessions. A new - handshake creates both a connection and a session. Data is - transmitted over the connection. - - The session contains a more permanent record of the handshake. The - session can be inspected to determine handshake results. The - session can also be used to create a new connection through - "session resumption". If the client and server both support this, - they can create a new connection based on an old session without - the overhead of a full handshake. - - The session for a L{tlslite.TLSConnection.TLSConnection} can be - retrieved from the connection's 'session' attribute. - - @type srpUsername: str - @ivar srpUsername: The client's SRP username (or None). - - @type sharedKeyUsername: str - @ivar sharedKeyUsername: The client's shared-key username (or - None). - - @type clientCertChain: L{tlslite.X509CertChain.X509CertChain} or - L{cryptoIDlib.CertChain.CertChain} - @ivar clientCertChain: The client's certificate chain (or None). - - @type serverCertChain: L{tlslite.X509CertChain.X509CertChain} or - L{cryptoIDlib.CertChain.CertChain} - @ivar serverCertChain: The server's certificate chain (or None). - """ - - def __init__(self): - self.masterSecret = createByteArraySequence([]) - self.sessionID = createByteArraySequence([]) - self.cipherSuite = 0 - self.srpUsername = None - self.sharedKeyUsername = None - self.clientCertChain = None - self.serverCertChain = None - self.resumable = False - self.sharedKey = False - - def _clone(self): - other = Session() - other.masterSecret = self.masterSecret - other.sessionID = self.sessionID - other.cipherSuite = self.cipherSuite - other.srpUsername = self.srpUsername - other.sharedKeyUsername = self.sharedKeyUsername - other.clientCertChain = self.clientCertChain - other.serverCertChain = self.serverCertChain - other.resumable = self.resumable - other.sharedKey = self.sharedKey - return other - - def _calcMasterSecret(self, version, premasterSecret, clientRandom, - serverRandom): - if version == (3,0): - self.masterSecret = PRF_SSL(premasterSecret, - concatArrays(clientRandom, serverRandom), 48) - elif version in ((3,1), (3,2)): - self.masterSecret = PRF(premasterSecret, "master secret", - concatArrays(clientRandom, serverRandom), 48) - else: - raise AssertionError() - - def valid(self): - """If this session can be used for session resumption. - - @rtype: bool - @return: If this session can be used for session resumption. - """ - return self.resumable or self.sharedKey - - def _setResumable(self, boolean): - #Only let it be set if this isn't a shared key - if not self.sharedKey: - #Only let it be set to True if the sessionID is non-null - if (not boolean) or (boolean and self.sessionID): - self.resumable = boolean - - def getCipherName(self): - """Get the name of the cipher used with this connection. - - @rtype: str - @return: The name of the cipher used with this connection. - Either 'aes128', 'aes256', 'rc4', or '3des'. - """ - if self.cipherSuite in CipherSuite.aes128Suites: - return "aes128" - elif self.cipherSuite in CipherSuite.aes256Suites: - return "aes256" - elif self.cipherSuite in CipherSuite.rc4Suites: - return "rc4" - elif self.cipherSuite in CipherSuite.tripleDESSuites: - return "3des" - else: - return None - - def _createSharedKey(self, sharedKeyUsername, sharedKey): - if len(sharedKeyUsername)>16: - raise ValueError() - if len(sharedKey)>47: - raise ValueError() - - self.sharedKeyUsername = sharedKeyUsername - - self.sessionID = createByteArrayZeros(16) - for x in range(len(sharedKeyUsername)): - self.sessionID[x] = ord(sharedKeyUsername[x]) - - premasterSecret = createByteArrayZeros(48) - sharedKey = chr(len(sharedKey)) + sharedKey - for x in range(48): - premasterSecret[x] = ord(sharedKey[x % len(sharedKey)]) - - self.masterSecret = PRF(premasterSecret, "shared secret", - createByteArraySequence([]), 48) - self.sharedKey = True - return self - - diff --git a/gdata/analytics/tlslite/SessionCache.py b/gdata/analytics/tlslite/SessionCache.py deleted file mode 100644 index 34cf0b0ec4..0000000000 --- a/gdata/analytics/tlslite/SessionCache.py +++ /dev/null @@ -1,103 +0,0 @@ -"""Class for caching TLS sessions.""" - -import thread -import time - -class SessionCache: - """This class is used by the server to cache TLS sessions. - - Caching sessions allows the client to use TLS session resumption - and avoid the expense of a full handshake. To use this class, - simply pass a SessionCache instance into the server handshake - function. - - This class is thread-safe. - """ - - #References to these instances - #are also held by the caller, who may change the 'resumable' - #flag, so the SessionCache must return the same instances - #it was passed in. - - def __init__(self, maxEntries=10000, maxAge=14400): - """Create a new SessionCache. - - @type maxEntries: int - @param maxEntries: The maximum size of the cache. When this - limit is reached, the oldest sessions will be deleted as - necessary to make room for new ones. The default is 10000. - - @type maxAge: int - @param maxAge: The number of seconds before a session expires - from the cache. The default is 14400 (i.e. 4 hours).""" - - self.lock = thread.allocate_lock() - - # Maps sessionIDs to sessions - self.entriesDict = {} - - #Circular list of (sessionID, timestamp) pairs - self.entriesList = [(None,None)] * maxEntries - - self.firstIndex = 0 - self.lastIndex = 0 - self.maxAge = maxAge - - def __getitem__(self, sessionID): - self.lock.acquire() - try: - self._purge() #Delete old items, so we're assured of a new one - session = self.entriesDict[sessionID] - - #When we add sessions they're resumable, but it's possible - #for the session to be invalidated later on (if a fatal alert - #is returned), so we have to check for resumability before - #returning the session. - - if session.valid(): - return session - else: - raise KeyError() - finally: - self.lock.release() - - - def __setitem__(self, sessionID, session): - self.lock.acquire() - try: - #Add the new element - self.entriesDict[sessionID] = session - self.entriesList[self.lastIndex] = (sessionID, time.time()) - self.lastIndex = (self.lastIndex+1) % len(self.entriesList) - - #If the cache is full, we delete the oldest element to make an - #empty space - if self.lastIndex == self.firstIndex: - del(self.entriesDict[self.entriesList[self.firstIndex][0]]) - self.firstIndex = (self.firstIndex+1) % len(self.entriesList) - finally: - self.lock.release() - - #Delete expired items - def _purge(self): - currentTime = time.time() - - #Search through the circular list, deleting expired elements until - #we reach a non-expired element. Since elements in list are - #ordered in time, we can break once we reach the first non-expired - #element - index = self.firstIndex - while index != self.lastIndex: - if currentTime - self.entriesList[index][1] > self.maxAge: - del(self.entriesDict[self.entriesList[index][0]]) - index = (index+1) % len(self.entriesList) - else: - break - self.firstIndex = index - -def _test(): - import doctest, SessionCache - return doctest.testmod(SessionCache) - -if __name__ == "__main__": - _test() diff --git a/gdata/analytics/tlslite/SharedKeyDB.py b/gdata/analytics/tlslite/SharedKeyDB.py deleted file mode 100644 index 3246ec7f15..0000000000 --- a/gdata/analytics/tlslite/SharedKeyDB.py +++ /dev/null @@ -1,58 +0,0 @@ -"""Class for storing shared keys.""" - -from utils.cryptomath import * -from utils.compat import * -from mathtls import * -from Session import Session -from BaseDB import BaseDB - -class SharedKeyDB(BaseDB): - """This class represent an in-memory or on-disk database of shared - keys. - - A SharedKeyDB can be passed to a server handshake function to - authenticate a client based on one of the shared keys. - - This class is thread-safe. - """ - - def __init__(self, filename=None): - """Create a new SharedKeyDB. - - @type filename: str - @param filename: Filename for an on-disk database, or None for - an in-memory database. If the filename already exists, follow - this with a call to open(). To create a new on-disk database, - follow this with a call to create(). - """ - BaseDB.__init__(self, filename, "shared key") - - def _getItem(self, username, valueStr): - session = Session() - session._createSharedKey(username, valueStr) - return session - - def __setitem__(self, username, sharedKey): - """Add a shared key to the database. - - @type username: str - @param username: The username to associate the shared key with. - Must be less than or equal to 16 characters in length, and must - not already be in the database. - - @type sharedKey: str - @param sharedKey: The shared key to add. Must be less than 48 - characters in length. - """ - BaseDB.__setitem__(self, username, sharedKey) - - def _setItem(self, username, value): - if len(username)>16: - raise ValueError("username too long") - if len(value)>=48: - raise ValueError("shared key too long") - return value - - def _checkItem(self, value, username, param): - newSession = self._getItem(username, param) - return value.masterSecret == newSession.masterSecret \ No newline at end of file diff --git a/gdata/analytics/tlslite/TLSConnection.py b/gdata/analytics/tlslite/TLSConnection.py deleted file mode 100644 index d125f8f0a4..0000000000 --- a/gdata/analytics/tlslite/TLSConnection.py +++ /dev/null @@ -1,1600 +0,0 @@ -""" -MAIN CLASS FOR TLS LITE (START HERE!). -""" -from __future__ import generators - -import socket -from utils.compat import formatExceptionTrace -from TLSRecordLayer import TLSRecordLayer -from Session import Session -from constants import * -from utils.cryptomath import getRandomBytes -from errors import * -from messages import * -from mathtls import * -from HandshakeSettings import HandshakeSettings - - -class TLSConnection(TLSRecordLayer): - """ - This class wraps a socket and provides TLS handshaking and data - transfer. - - To use this class, create a new instance, passing a connected - socket into the constructor. Then call some handshake function. - If the handshake completes without raising an exception, then a TLS - connection has been negotiated. You can transfer data over this - connection as if it were a socket. - - This class provides both synchronous and asynchronous versions of - its key functions. The synchronous versions should be used when - writing single-or multi-threaded code using blocking sockets. The - asynchronous versions should be used when performing asynchronous, - event-based I/O with non-blocking sockets. - - Asynchronous I/O is a complicated subject; typically, you should - not use the asynchronous functions directly, but should use some - framework like asyncore or Twisted which TLS Lite integrates with - (see - L{tlslite.integration.TLSAsyncDispatcherMixIn.TLSAsyncDispatcherMixIn} or - L{tlslite.integration.TLSTwistedProtocolWrapper.TLSTwistedProtocolWrapper}). - """ - - - def __init__(self, sock): - """Create a new TLSConnection instance. - - @param sock: The socket data will be transmitted on. The - socket should already be connected. It may be in blocking or - non-blocking mode. - - @type sock: L{socket.socket} - """ - TLSRecordLayer.__init__(self, sock) - - def handshakeClientSRP(self, username, password, session=None, - settings=None, checker=None, async=False): - """Perform an SRP handshake in the role of client. - - This function performs a TLS/SRP handshake. SRP mutually - authenticates both parties to each other using only a - username and password. This function may also perform a - combined SRP and server-certificate handshake, if the server - chooses to authenticate itself with a certificate chain in - addition to doing SRP. - - TLS/SRP is non-standard. Most TLS implementations don't - support it. See - U{http://www.ietf.org/html.charters/tls-charter.html} or - U{http://trevp.net/tlssrp/} for the latest information on - TLS/SRP. - - Like any handshake function, this can be called on a closed - TLS connection, or on a TLS connection that is already open. - If called on an open connection it performs a re-handshake. - - If the function completes without raising an exception, the - TLS connection will be open and available for data transfer. - - If an exception is raised, the connection will have been - automatically closed (if it was ever open). - - @type username: str - @param username: The SRP username. - - @type password: str - @param password: The SRP password. - - @type session: L{tlslite.Session.Session} - @param session: A TLS session to attempt to resume. This - session must be an SRP session performed with the same username - and password as were passed in. If the resumption does not - succeed, a full SRP handshake will be performed. - - @type settings: L{tlslite.HandshakeSettings.HandshakeSettings} - @param settings: Various settings which can be used to control - the ciphersuites, certificate types, and SSL/TLS versions - offered by the client. - - @type checker: L{tlslite.Checker.Checker} - @param checker: A Checker instance. This instance will be - invoked to examine the other party's authentication - credentials, if the handshake completes succesfully. - - @type async: bool - @param async: If False, this function will block until the - handshake is completed. If True, this function will return a - generator. Successive invocations of the generator will - return 0 if it is waiting to read from the socket, 1 if it is - waiting to write to the socket, or will raise StopIteration if - the handshake operation is completed. - - @rtype: None or an iterable - @return: If 'async' is True, a generator object will be - returned. - - @raise socket.error: If a socket error occurs. - @raise tlslite.errors.TLSAbruptCloseError: If the socket is closed - without a preceding alert. - @raise tlslite.errors.TLSAlert: If a TLS alert is signalled. - @raise tlslite.errors.TLSAuthenticationError: If the checker - doesn't like the other party's authentication credentials. - """ - handshaker = self._handshakeClientAsync(srpParams=(username, password), - session=session, settings=settings, checker=checker) - if async: - return handshaker - for result in handshaker: - pass - - def handshakeClientCert(self, certChain=None, privateKey=None, - session=None, settings=None, checker=None, - async=False): - """Perform a certificate-based handshake in the role of client. - - This function performs an SSL or TLS handshake. The server - will authenticate itself using an X.509 or cryptoID certificate - chain. If the handshake succeeds, the server's certificate - chain will be stored in the session's serverCertChain attribute. - Unless a checker object is passed in, this function does no - validation or checking of the server's certificate chain. - - If the server requests client authentication, the - client will send the passed-in certificate chain, and use the - passed-in private key to authenticate itself. If no - certificate chain and private key were passed in, the client - will attempt to proceed without client authentication. The - server may or may not allow this. - - Like any handshake function, this can be called on a closed - TLS connection, or on a TLS connection that is already open. - If called on an open connection it performs a re-handshake. - - If the function completes without raising an exception, the - TLS connection will be open and available for data transfer. - - If an exception is raised, the connection will have been - automatically closed (if it was ever open). - - @type certChain: L{tlslite.X509CertChain.X509CertChain} or - L{cryptoIDlib.CertChain.CertChain} - @param certChain: The certificate chain to be used if the - server requests client authentication. - - @type privateKey: L{tlslite.utils.RSAKey.RSAKey} - @param privateKey: The private key to be used if the server - requests client authentication. - - @type session: L{tlslite.Session.Session} - @param session: A TLS session to attempt to resume. If the - resumption does not succeed, a full handshake will be - performed. - - @type settings: L{tlslite.HandshakeSettings.HandshakeSettings} - @param settings: Various settings which can be used to control - the ciphersuites, certificate types, and SSL/TLS versions - offered by the client. - - @type checker: L{tlslite.Checker.Checker} - @param checker: A Checker instance. This instance will be - invoked to examine the other party's authentication - credentials, if the handshake completes succesfully. - - @type async: bool - @param async: If False, this function will block until the - handshake is completed. If True, this function will return a - generator. Successive invocations of the generator will - return 0 if it is waiting to read from the socket, 1 if it is - waiting to write to the socket, or will raise StopIteration if - the handshake operation is completed. - - @rtype: None or an iterable - @return: If 'async' is True, a generator object will be - returned. - - @raise socket.error: If a socket error occurs. - @raise tlslite.errors.TLSAbruptCloseError: If the socket is closed - without a preceding alert. - @raise tlslite.errors.TLSAlert: If a TLS alert is signalled. - @raise tlslite.errors.TLSAuthenticationError: If the checker - doesn't like the other party's authentication credentials. - """ - handshaker = self._handshakeClientAsync(certParams=(certChain, - privateKey), session=session, settings=settings, - checker=checker) - if async: - return handshaker - for result in handshaker: - pass - - def handshakeClientUnknown(self, srpCallback=None, certCallback=None, - session=None, settings=None, checker=None, - async=False): - """Perform a to-be-determined type of handshake in the role of client. - - This function performs an SSL or TLS handshake. If the server - requests client certificate authentication, the - certCallback will be invoked and should return a (certChain, - privateKey) pair. If the callback returns None, the library - will attempt to proceed without client authentication. The - server may or may not allow this. - - If the server requests SRP authentication, the srpCallback - will be invoked and should return a (username, password) pair. - If the callback returns None, the local implementation will - signal a user_canceled error alert. - - After the handshake completes, the client can inspect the - connection's session attribute to determine what type of - authentication was performed. - - Like any handshake function, this can be called on a closed - TLS connection, or on a TLS connection that is already open. - If called on an open connection it performs a re-handshake. - - If the function completes without raising an exception, the - TLS connection will be open and available for data transfer. - - If an exception is raised, the connection will have been - automatically closed (if it was ever open). - - @type srpCallback: callable - @param srpCallback: The callback to be used if the server - requests SRP authentication. If None, the client will not - offer support for SRP ciphersuites. - - @type certCallback: callable - @param certCallback: The callback to be used if the server - requests client certificate authentication. - - @type session: L{tlslite.Session.Session} - @param session: A TLS session to attempt to resume. If the - resumption does not succeed, a full handshake will be - performed. - - @type settings: L{tlslite.HandshakeSettings.HandshakeSettings} - @param settings: Various settings which can be used to control - the ciphersuites, certificate types, and SSL/TLS versions - offered by the client. - - @type checker: L{tlslite.Checker.Checker} - @param checker: A Checker instance. This instance will be - invoked to examine the other party's authentication - credentials, if the handshake completes succesfully. - - @type async: bool - @param async: If False, this function will block until the - handshake is completed. If True, this function will return a - generator. Successive invocations of the generator will - return 0 if it is waiting to read from the socket, 1 if it is - waiting to write to the socket, or will raise StopIteration if - the handshake operation is completed. - - @rtype: None or an iterable - @return: If 'async' is True, a generator object will be - returned. - - @raise socket.error: If a socket error occurs. - @raise tlslite.errors.TLSAbruptCloseError: If the socket is closed - without a preceding alert. - @raise tlslite.errors.TLSAlert: If a TLS alert is signalled. - @raise tlslite.errors.TLSAuthenticationError: If the checker - doesn't like the other party's authentication credentials. - """ - handshaker = self._handshakeClientAsync(unknownParams=(srpCallback, - certCallback), session=session, settings=settings, - checker=checker) - if async: - return handshaker - for result in handshaker: - pass - - def handshakeClientSharedKey(self, username, sharedKey, settings=None, - checker=None, async=False): - """Perform a shared-key handshake in the role of client. - - This function performs a shared-key handshake. Using shared - symmetric keys of high entropy (128 bits or greater) mutually - authenticates both parties to each other. - - TLS with shared-keys is non-standard. Most TLS - implementations don't support it. See - U{http://www.ietf.org/html.charters/tls-charter.html} for the - latest information on TLS with shared-keys. If the shared-keys - Internet-Draft changes or is superceded, TLS Lite will track - those changes, so the shared-key support in later versions of - TLS Lite may become incompatible with this version. - - Like any handshake function, this can be called on a closed - TLS connection, or on a TLS connection that is already open. - If called on an open connection it performs a re-handshake. - - If the function completes without raising an exception, the - TLS connection will be open and available for data transfer. - - If an exception is raised, the connection will have been - automatically closed (if it was ever open). - - @type username: str - @param username: The shared-key username. - - @type sharedKey: str - @param sharedKey: The shared key. - - @type settings: L{tlslite.HandshakeSettings.HandshakeSettings} - @param settings: Various settings which can be used to control - the ciphersuites, certificate types, and SSL/TLS versions - offered by the client. - - @type checker: L{tlslite.Checker.Checker} - @param checker: A Checker instance. This instance will be - invoked to examine the other party's authentication - credentials, if the handshake completes succesfully. - - @type async: bool - @param async: If False, this function will block until the - handshake is completed. If True, this function will return a - generator. Successive invocations of the generator will - return 0 if it is waiting to read from the socket, 1 if it is - waiting to write to the socket, or will raise StopIteration if - the handshake operation is completed. - - @rtype: None or an iterable - @return: If 'async' is True, a generator object will be - returned. - - @raise socket.error: If a socket error occurs. - @raise tlslite.errors.TLSAbruptCloseError: If the socket is closed - without a preceding alert. - @raise tlslite.errors.TLSAlert: If a TLS alert is signalled. - @raise tlslite.errors.TLSAuthenticationError: If the checker - doesn't like the other party's authentication credentials. - """ - handshaker = self._handshakeClientAsync(sharedKeyParams=(username, - sharedKey), settings=settings, checker=checker) - if async: - return handshaker - for result in handshaker: - pass - - def _handshakeClientAsync(self, srpParams=(), certParams=(), - unknownParams=(), sharedKeyParams=(), - session=None, settings=None, checker=None, - recursive=False): - - handshaker = self._handshakeClientAsyncHelper(srpParams=srpParams, - certParams=certParams, unknownParams=unknownParams, - sharedKeyParams=sharedKeyParams, session=session, - settings=settings, recursive=recursive) - for result in self._handshakeWrapperAsync(handshaker, checker): - yield result - - - def _handshakeClientAsyncHelper(self, srpParams, certParams, unknownParams, - sharedKeyParams, session, settings, recursive): - if not recursive: - self._handshakeStart(client=True) - - #Unpack parameters - srpUsername = None # srpParams - password = None # srpParams - clientCertChain = None # certParams - privateKey = None # certParams - srpCallback = None # unknownParams - certCallback = None # unknownParams - #session # sharedKeyParams (or session) - #settings # settings - - if srpParams: - srpUsername, password = srpParams - elif certParams: - clientCertChain, privateKey = certParams - elif unknownParams: - srpCallback, certCallback = unknownParams - elif sharedKeyParams: - session = Session()._createSharedKey(*sharedKeyParams) - - if not settings: - settings = HandshakeSettings() - settings = settings._filter() - - #Validate parameters - if srpUsername and not password: - raise ValueError("Caller passed a username but no password") - if password and not srpUsername: - raise ValueError("Caller passed a password but no username") - - if clientCertChain and not privateKey: - raise ValueError("Caller passed a certChain but no privateKey") - if privateKey and not clientCertChain: - raise ValueError("Caller passed a privateKey but no certChain") - - if clientCertChain: - foundType = False - try: - import cryptoIDlib.CertChain - if isinstance(clientCertChain, cryptoIDlib.CertChain.CertChain): - if "cryptoID" not in settings.certificateTypes: - raise ValueError("Client certificate doesn't "\ - "match Handshake Settings") - settings.certificateTypes = ["cryptoID"] - foundType = True - except ImportError: - pass - if not foundType and isinstance(clientCertChain, - X509CertChain): - if "x509" not in settings.certificateTypes: - raise ValueError("Client certificate doesn't match "\ - "Handshake Settings") - settings.certificateTypes = ["x509"] - foundType = True - if not foundType: - raise ValueError("Unrecognized certificate type") - - - if session: - if not session.valid(): - session = None #ignore non-resumable sessions... - elif session.resumable and \ - (session.srpUsername != srpUsername): - raise ValueError("Session username doesn't match") - - #Add Faults to parameters - if srpUsername and self.fault == Fault.badUsername: - srpUsername += "GARBAGE" - if password and self.fault == Fault.badPassword: - password += "GARBAGE" - if sharedKeyParams: - identifier = sharedKeyParams[0] - sharedKey = sharedKeyParams[1] - if self.fault == Fault.badIdentifier: - identifier += "GARBAGE" - session = Session()._createSharedKey(identifier, sharedKey) - elif self.fault == Fault.badSharedKey: - sharedKey += "GARBAGE" - session = Session()._createSharedKey(identifier, sharedKey) - - - #Initialize locals - serverCertChain = None - cipherSuite = 0 - certificateType = CertificateType.x509 - premasterSecret = None - - #Get client nonce - clientRandom = getRandomBytes(32) - - #Initialize acceptable ciphersuites - cipherSuites = [] - if srpParams: - cipherSuites += CipherSuite.getSrpRsaSuites(settings.cipherNames) - cipherSuites += CipherSuite.getSrpSuites(settings.cipherNames) - elif certParams: - cipherSuites += CipherSuite.getRsaSuites(settings.cipherNames) - elif unknownParams: - if srpCallback: - cipherSuites += \ - CipherSuite.getSrpRsaSuites(settings.cipherNames) - cipherSuites += \ - CipherSuite.getSrpSuites(settings.cipherNames) - cipherSuites += CipherSuite.getRsaSuites(settings.cipherNames) - elif sharedKeyParams: - cipherSuites += CipherSuite.getRsaSuites(settings.cipherNames) - else: - cipherSuites += CipherSuite.getRsaSuites(settings.cipherNames) - - #Initialize acceptable certificate types - certificateTypes = settings._getCertificateTypes() - - #Tentatively set the version to the client's minimum version. - #We'll use this for the ClientHello, and if an error occurs - #parsing the Server Hello, we'll use this version for the response - self.version = settings.maxVersion - - #Either send ClientHello (with a resumable session)... - if session: - #If it's a resumable (i.e. not a shared-key session), then its - #ciphersuite must be one of the acceptable ciphersuites - if (not sharedKeyParams) and \ - session.cipherSuite not in cipherSuites: - raise ValueError("Session's cipher suite not consistent "\ - "with parameters") - else: - clientHello = ClientHello() - clientHello.create(settings.maxVersion, clientRandom, - session.sessionID, cipherSuites, - certificateTypes, session.srpUsername) - - #Or send ClientHello (without) - else: - clientHello = ClientHello() - clientHello.create(settings.maxVersion, clientRandom, - createByteArraySequence([]), cipherSuites, - certificateTypes, srpUsername) - for result in self._sendMsg(clientHello): - yield result - - #Get ServerHello (or missing_srp_username) - for result in self._getMsg((ContentType.handshake, - ContentType.alert), - HandshakeType.server_hello): - if result in (0,1): - yield result - else: - break - msg = result - - if isinstance(msg, ServerHello): - serverHello = msg - elif isinstance(msg, Alert): - alert = msg - - #If it's not a missing_srp_username, re-raise - if alert.description != AlertDescription.missing_srp_username: - self._shutdown(False) - raise TLSRemoteAlert(alert) - - #If we're not in SRP callback mode, we won't have offered SRP - #without a username, so we shouldn't get this alert - if not srpCallback: - for result in self._sendError(\ - AlertDescription.unexpected_message): - yield result - srpParams = srpCallback() - #If the callback returns None, cancel the handshake - if srpParams == None: - for result in self._sendError(AlertDescription.user_canceled): - yield result - - #Recursively perform handshake - for result in self._handshakeClientAsyncHelper(srpParams, - None, None, None, None, settings, True): - yield result - return - - #Get the server version. Do this before anything else, so any - #error alerts will use the server's version - self.version = serverHello.server_version - - #Future responses from server must use this version - self._versionCheck = True - - #Check ServerHello - if serverHello.server_version < settings.minVersion: - for result in self._sendError(\ - AlertDescription.protocol_version, - "Too old version: %s" % str(serverHello.server_version)): - yield result - if serverHello.server_version > settings.maxVersion: - for result in self._sendError(\ - AlertDescription.protocol_version, - "Too new version: %s" % str(serverHello.server_version)): - yield result - if serverHello.cipher_suite not in cipherSuites: - for result in self._sendError(\ - AlertDescription.illegal_parameter, - "Server responded with incorrect ciphersuite"): - yield result - if serverHello.certificate_type not in certificateTypes: - for result in self._sendError(\ - AlertDescription.illegal_parameter, - "Server responded with incorrect certificate type"): - yield result - if serverHello.compression_method != 0: - for result in self._sendError(\ - AlertDescription.illegal_parameter, - "Server responded with incorrect compression method"): - yield result - - #Get the server nonce - serverRandom = serverHello.random - - #If the server agrees to resume - if session and session.sessionID and \ - serverHello.session_id == session.sessionID: - - #If a shared-key, we're flexible about suites; otherwise the - #server-chosen suite has to match the session's suite - if sharedKeyParams: - session.cipherSuite = serverHello.cipher_suite - elif serverHello.cipher_suite != session.cipherSuite: - for result in self._sendError(\ - AlertDescription.illegal_parameter,\ - "Server's ciphersuite doesn't match session"): - yield result - - #Set the session for this connection - self.session = session - - #Calculate pending connection states - self._calcPendingStates(clientRandom, serverRandom, - settings.cipherImplementations) - - #Exchange ChangeCipherSpec and Finished messages - for result in self._getFinished(): - yield result - for result in self._sendFinished(): - yield result - - #Mark the connection as open - self._handshakeDone(resumed=True) - - #If server DOES NOT agree to resume - else: - - if sharedKeyParams: - for result in self._sendError(\ - AlertDescription.user_canceled, - "Was expecting a shared-key resumption"): - yield result - - #We've already validated these - cipherSuite = serverHello.cipher_suite - certificateType = serverHello.certificate_type - - #If the server chose an SRP suite... - if cipherSuite in CipherSuite.srpSuites: - #Get ServerKeyExchange, ServerHelloDone - for result in self._getMsg(ContentType.handshake, - HandshakeType.server_key_exchange, cipherSuite): - if result in (0,1): - yield result - else: - break - serverKeyExchange = result - - for result in self._getMsg(ContentType.handshake, - HandshakeType.server_hello_done): - if result in (0,1): - yield result - else: - break - serverHelloDone = result - - #If the server chose an SRP+RSA suite... - elif cipherSuite in CipherSuite.srpRsaSuites: - #Get Certificate, ServerKeyExchange, ServerHelloDone - for result in self._getMsg(ContentType.handshake, - HandshakeType.certificate, certificateType): - if result in (0,1): - yield result - else: - break - serverCertificate = result - - for result in self._getMsg(ContentType.handshake, - HandshakeType.server_key_exchange, cipherSuite): - if result in (0,1): - yield result - else: - break - serverKeyExchange = result - - for result in self._getMsg(ContentType.handshake, - HandshakeType.server_hello_done): - if result in (0,1): - yield result - else: - break - serverHelloDone = result - - #If the server chose an RSA suite... - elif cipherSuite in CipherSuite.rsaSuites: - #Get Certificate[, CertificateRequest], ServerHelloDone - for result in self._getMsg(ContentType.handshake, - HandshakeType.certificate, certificateType): - if result in (0,1): - yield result - else: - break - serverCertificate = result - - for result in self._getMsg(ContentType.handshake, - (HandshakeType.server_hello_done, - HandshakeType.certificate_request)): - if result in (0,1): - yield result - else: - break - msg = result - - certificateRequest = None - if isinstance(msg, CertificateRequest): - certificateRequest = msg - for result in self._getMsg(ContentType.handshake, - HandshakeType.server_hello_done): - if result in (0,1): - yield result - else: - break - serverHelloDone = result - elif isinstance(msg, ServerHelloDone): - serverHelloDone = msg - else: - raise AssertionError() - - - #Calculate SRP premaster secret, if server chose an SRP or - #SRP+RSA suite - if cipherSuite in CipherSuite.srpSuites + \ - CipherSuite.srpRsaSuites: - #Get and check the server's group parameters and B value - N = serverKeyExchange.srp_N - g = serverKeyExchange.srp_g - s = serverKeyExchange.srp_s - B = serverKeyExchange.srp_B - - if (g,N) not in goodGroupParameters: - for result in self._sendError(\ - AlertDescription.untrusted_srp_parameters, - "Unknown group parameters"): - yield result - if numBits(N) < settings.minKeySize: - for result in self._sendError(\ - AlertDescription.untrusted_srp_parameters, - "N value is too small: %d" % numBits(N)): - yield result - if numBits(N) > settings.maxKeySize: - for result in self._sendError(\ - AlertDescription.untrusted_srp_parameters, - "N value is too large: %d" % numBits(N)): - yield result - if B % N == 0: - for result in self._sendError(\ - AlertDescription.illegal_parameter, - "Suspicious B value"): - yield result - - #Check the server's signature, if server chose an - #SRP+RSA suite - if cipherSuite in CipherSuite.srpRsaSuites: - #Hash ServerKeyExchange/ServerSRPParams - hashBytes = serverKeyExchange.hash(clientRandom, - serverRandom) - - #Extract signature bytes from ServerKeyExchange - sigBytes = serverKeyExchange.signature - if len(sigBytes) == 0: - for result in self._sendError(\ - AlertDescription.illegal_parameter, - "Server sent an SRP ServerKeyExchange "\ - "message without a signature"): - yield result - - #Get server's public key from the Certificate message - for result in self._getKeyFromChain(serverCertificate, - settings): - if result in (0,1): - yield result - else: - break - publicKey, serverCertChain = result - - #Verify signature - if not publicKey.verify(sigBytes, hashBytes): - for result in self._sendError(\ - AlertDescription.decrypt_error, - "Signature failed to verify"): - yield result - - - #Calculate client's ephemeral DH values (a, A) - a = bytesToNumber(getRandomBytes(32)) - A = powMod(g, a, N) - - #Calculate client's static DH values (x, v) - x = makeX(bytesToString(s), srpUsername, password) - v = powMod(g, x, N) - - #Calculate u - u = makeU(N, A, B) - - #Calculate premaster secret - k = makeK(N, g) - S = powMod((B - (k*v)) % N, a+(u*x), N) - - if self.fault == Fault.badA: - A = N - S = 0 - premasterSecret = numberToBytes(S) - - #Send ClientKeyExchange - for result in self._sendMsg(\ - ClientKeyExchange(cipherSuite).createSRP(A)): - yield result - - - #Calculate RSA premaster secret, if server chose an RSA suite - elif cipherSuite in CipherSuite.rsaSuites: - - #Handle the presence of a CertificateRequest - if certificateRequest: - if unknownParams and certCallback: - certParamsNew = certCallback() - if certParamsNew: - clientCertChain, privateKey = certParamsNew - - #Get server's public key from the Certificate message - for result in self._getKeyFromChain(serverCertificate, - settings): - if result in (0,1): - yield result - else: - break - publicKey, serverCertChain = result - - - #Calculate premaster secret - premasterSecret = getRandomBytes(48) - premasterSecret[0] = settings.maxVersion[0] - premasterSecret[1] = settings.maxVersion[1] - - if self.fault == Fault.badPremasterPadding: - premasterSecret[0] = 5 - if self.fault == Fault.shortPremasterSecret: - premasterSecret = premasterSecret[:-1] - - #Encrypt premaster secret to server's public key - encryptedPreMasterSecret = publicKey.encrypt(premasterSecret) - - #If client authentication was requested, send Certificate - #message, either with certificates or empty - if certificateRequest: - clientCertificate = Certificate(certificateType) - - if clientCertChain: - #Check to make sure we have the same type of - #certificates the server requested - wrongType = False - if certificateType == CertificateType.x509: - if not isinstance(clientCertChain, X509CertChain): - wrongType = True - elif certificateType == CertificateType.cryptoID: - if not isinstance(clientCertChain, - cryptoIDlib.CertChain.CertChain): - wrongType = True - if wrongType: - for result in self._sendError(\ - AlertDescription.handshake_failure, - "Client certificate is of wrong type"): - yield result - - clientCertificate.create(clientCertChain) - - for result in self._sendMsg(clientCertificate): - yield result - else: - #The server didn't request client auth, so we - #zeroize these so the clientCertChain won't be - #stored in the session. - privateKey = None - clientCertChain = None - - #Send ClientKeyExchange - clientKeyExchange = ClientKeyExchange(cipherSuite, - self.version) - clientKeyExchange.createRSA(encryptedPreMasterSecret) - for result in self._sendMsg(clientKeyExchange): - yield result - - #If client authentication was requested and we have a - #private key, send CertificateVerify - if certificateRequest and privateKey: - if self.version == (3,0): - #Create a temporary session object, just for the - #purpose of creating the CertificateVerify - session = Session() - session._calcMasterSecret(self.version, - premasterSecret, - clientRandom, - serverRandom) - verifyBytes = self._calcSSLHandshakeHash(\ - session.masterSecret, "") - elif self.version in ((3,1), (3,2)): - verifyBytes = stringToBytes(\ - self._handshake_md5.digest() + \ - self._handshake_sha.digest()) - if self.fault == Fault.badVerifyMessage: - verifyBytes[0] = ((verifyBytes[0]+1) % 256) - signedBytes = privateKey.sign(verifyBytes) - certificateVerify = CertificateVerify() - certificateVerify.create(signedBytes) - for result in self._sendMsg(certificateVerify): - yield result - - - #Create the session object - self.session = Session() - self.session._calcMasterSecret(self.version, premasterSecret, - clientRandom, serverRandom) - self.session.sessionID = serverHello.session_id - self.session.cipherSuite = cipherSuite - self.session.srpUsername = srpUsername - self.session.clientCertChain = clientCertChain - self.session.serverCertChain = serverCertChain - - #Calculate pending connection states - self._calcPendingStates(clientRandom, serverRandom, - settings.cipherImplementations) - - #Exchange ChangeCipherSpec and Finished messages - for result in self._sendFinished(): - yield result - for result in self._getFinished(): - yield result - - #Mark the connection as open - self.session._setResumable(True) - self._handshakeDone(resumed=False) - - - - def handshakeServer(self, sharedKeyDB=None, verifierDB=None, - certChain=None, privateKey=None, reqCert=False, - sessionCache=None, settings=None, checker=None): - """Perform a handshake in the role of server. - - This function performs an SSL or TLS handshake. Depending on - the arguments and the behavior of the client, this function can - perform a shared-key, SRP, or certificate-based handshake. It - can also perform a combined SRP and server-certificate - handshake. - - Like any handshake function, this can be called on a closed - TLS connection, or on a TLS connection that is already open. - If called on an open connection it performs a re-handshake. - This function does not send a Hello Request message before - performing the handshake, so if re-handshaking is required, - the server must signal the client to begin the re-handshake - through some other means. - - If the function completes without raising an exception, the - TLS connection will be open and available for data transfer. - - If an exception is raised, the connection will have been - automatically closed (if it was ever open). - - @type sharedKeyDB: L{tlslite.SharedKeyDB.SharedKeyDB} - @param sharedKeyDB: A database of shared symmetric keys - associated with usernames. If the client performs a - shared-key handshake, the session's sharedKeyUsername - attribute will be set. - - @type verifierDB: L{tlslite.VerifierDB.VerifierDB} - @param verifierDB: A database of SRP password verifiers - associated with usernames. If the client performs an SRP - handshake, the session's srpUsername attribute will be set. - - @type certChain: L{tlslite.X509CertChain.X509CertChain} or - L{cryptoIDlib.CertChain.CertChain} - @param certChain: The certificate chain to be used if the - client requests server certificate authentication. - - @type privateKey: L{tlslite.utils.RSAKey.RSAKey} - @param privateKey: The private key to be used if the client - requests server certificate authentication. - - @type reqCert: bool - @param reqCert: Whether to request client certificate - authentication. This only applies if the client chooses server - certificate authentication; if the client chooses SRP or - shared-key authentication, this will be ignored. If the client - performs a client certificate authentication, the sessions's - clientCertChain attribute will be set. - - @type sessionCache: L{tlslite.SessionCache.SessionCache} - @param sessionCache: An in-memory cache of resumable sessions. - The client can resume sessions from this cache. Alternatively, - if the client performs a full handshake, a new session will be - added to the cache. - - @type settings: L{tlslite.HandshakeSettings.HandshakeSettings} - @param settings: Various settings which can be used to control - the ciphersuites and SSL/TLS version chosen by the server. - - @type checker: L{tlslite.Checker.Checker} - @param checker: A Checker instance. This instance will be - invoked to examine the other party's authentication - credentials, if the handshake completes succesfully. - - @raise socket.error: If a socket error occurs. - @raise tlslite.errors.TLSAbruptCloseError: If the socket is closed - without a preceding alert. - @raise tlslite.errors.TLSAlert: If a TLS alert is signalled. - @raise tlslite.errors.TLSAuthenticationError: If the checker - doesn't like the other party's authentication credentials. - """ - for result in self.handshakeServerAsync(sharedKeyDB, verifierDB, - certChain, privateKey, reqCert, sessionCache, settings, - checker): - pass - - - def handshakeServerAsync(self, sharedKeyDB=None, verifierDB=None, - certChain=None, privateKey=None, reqCert=False, - sessionCache=None, settings=None, checker=None): - """Start a server handshake operation on the TLS connection. - - This function returns a generator which behaves similarly to - handshakeServer(). Successive invocations of the generator - will return 0 if it is waiting to read from the socket, 1 if it is - waiting to write to the socket, or it will raise StopIteration - if the handshake operation is complete. - - @rtype: iterable - @return: A generator; see above for details. - """ - handshaker = self._handshakeServerAsyncHelper(\ - sharedKeyDB=sharedKeyDB, - verifierDB=verifierDB, certChain=certChain, - privateKey=privateKey, reqCert=reqCert, - sessionCache=sessionCache, settings=settings) - for result in self._handshakeWrapperAsync(handshaker, checker): - yield result - - - def _handshakeServerAsyncHelper(self, sharedKeyDB, verifierDB, - certChain, privateKey, reqCert, sessionCache, - settings): - - self._handshakeStart(client=False) - - if (not sharedKeyDB) and (not verifierDB) and (not certChain): - raise ValueError("Caller passed no authentication credentials") - if certChain and not privateKey: - raise ValueError("Caller passed a certChain but no privateKey") - if privateKey and not certChain: - raise ValueError("Caller passed a privateKey but no certChain") - - if not settings: - settings = HandshakeSettings() - settings = settings._filter() - - #Initialize acceptable cipher suites - cipherSuites = [] - if verifierDB: - if certChain: - cipherSuites += \ - CipherSuite.getSrpRsaSuites(settings.cipherNames) - cipherSuites += CipherSuite.getSrpSuites(settings.cipherNames) - if sharedKeyDB or certChain: - cipherSuites += CipherSuite.getRsaSuites(settings.cipherNames) - - #Initialize acceptable certificate type - certificateType = None - if certChain: - try: - import cryptoIDlib.CertChain - if isinstance(certChain, cryptoIDlib.CertChain.CertChain): - certificateType = CertificateType.cryptoID - except ImportError: - pass - if isinstance(certChain, X509CertChain): - certificateType = CertificateType.x509 - if certificateType == None: - raise ValueError("Unrecognized certificate type") - - #Initialize locals - clientCertChain = None - serverCertChain = None #We may set certChain to this later - postFinishedError = None - - #Tentatively set version to most-desirable version, so if an error - #occurs parsing the ClientHello, this is what we'll use for the - #error alert - self.version = settings.maxVersion - - #Get ClientHello - for result in self._getMsg(ContentType.handshake, - HandshakeType.client_hello): - if result in (0,1): - yield result - else: - break - clientHello = result - - #If client's version is too low, reject it - if clientHello.client_version < settings.minVersion: - self.version = settings.minVersion - for result in self._sendError(\ - AlertDescription.protocol_version, - "Too old version: %s" % str(clientHello.client_version)): - yield result - - #If client's version is too high, propose my highest version - elif clientHello.client_version > settings.maxVersion: - self.version = settings.maxVersion - - else: - #Set the version to the client's version - self.version = clientHello.client_version - - #Get the client nonce; create server nonce - clientRandom = clientHello.random - serverRandom = getRandomBytes(32) - - #Calculate the first cipher suite intersection. - #This is the 'privileged' ciphersuite. We'll use it if we're - #doing a shared-key resumption or a new negotiation. In fact, - #the only time we won't use it is if we're resuming a non-sharedkey - #session, in which case we use the ciphersuite from the session. - # - #Given the current ciphersuite ordering, this means we prefer SRP - #over non-SRP. - for cipherSuite in cipherSuites: - if cipherSuite in clientHello.cipher_suites: - break - else: - for result in self._sendError(\ - AlertDescription.handshake_failure): - yield result - - #If resumption was requested... - if clientHello.session_id and (sharedKeyDB or sessionCache): - session = None - - #Check in the sharedKeys container - if sharedKeyDB and len(clientHello.session_id)==16: - try: - #Trim off zero padding, if any - for x in range(16): - if clientHello.session_id[x]==0: - break - self.allegedSharedKeyUsername = bytesToString(\ - clientHello.session_id[:x]) - session = sharedKeyDB[self.allegedSharedKeyUsername] - if not session.sharedKey: - raise AssertionError() - #use privileged ciphersuite - session.cipherSuite = cipherSuite - except KeyError: - pass - - #Then check in the session cache - if sessionCache and not session: - try: - session = sessionCache[bytesToString(\ - clientHello.session_id)] - if session.sharedKey: - raise AssertionError() - if not session.resumable: - raise AssertionError() - #Check for consistency with ClientHello - if session.cipherSuite not in cipherSuites: - for result in self._sendError(\ - AlertDescription.handshake_failure): - yield result - if session.cipherSuite not in clientHello.cipher_suites: - for result in self._sendError(\ - AlertDescription.handshake_failure): - yield result - if clientHello.srp_username: - if clientHello.srp_username != session.srpUsername: - for result in self._sendError(\ - AlertDescription.handshake_failure): - yield result - except KeyError: - pass - - #If a session is found.. - if session: - #Set the session - self.session = session - - #Send ServerHello - serverHello = ServerHello() - serverHello.create(self.version, serverRandom, - session.sessionID, session.cipherSuite, - certificateType) - for result in self._sendMsg(serverHello): - yield result - - #From here on, the client's messages must have the right version - self._versionCheck = True - - #Calculate pending connection states - self._calcPendingStates(clientRandom, serverRandom, - settings.cipherImplementations) - - #Exchange ChangeCipherSpec and Finished messages - for result in self._sendFinished(): - yield result - for result in self._getFinished(): - yield result - - #Mark the connection as open - self._handshakeDone(resumed=True) - return - - - #If not a resumption... - - #TRICKY: we might have chosen an RSA suite that was only deemed - #acceptable because of the shared-key resumption. If the shared- - #key resumption failed, because the identifier wasn't recognized, - #we might fall through to here, where we have an RSA suite - #chosen, but no certificate. - if cipherSuite in CipherSuite.rsaSuites and not certChain: - for result in self._sendError(\ - AlertDescription.handshake_failure): - yield result - - #If an RSA suite is chosen, check for certificate type intersection - #(We do this check down here because if the mismatch occurs but the - # client is using a shared-key session, it's okay) - if cipherSuite in CipherSuite.rsaSuites + \ - CipherSuite.srpRsaSuites: - if certificateType not in clientHello.certificate_types: - for result in self._sendError(\ - AlertDescription.handshake_failure, - "the client doesn't support my certificate type"): - yield result - - #Move certChain -> serverCertChain, now that we're using it - serverCertChain = certChain - - - #Create sessionID - if sessionCache: - sessionID = getRandomBytes(32) - else: - sessionID = createByteArraySequence([]) - - #If we've selected an SRP suite, exchange keys and calculate - #premaster secret: - if cipherSuite in CipherSuite.srpSuites + CipherSuite.srpRsaSuites: - - #If there's no SRP username... - if not clientHello.srp_username: - - #Ask the client to re-send ClientHello with one - for result in self._sendMsg(Alert().create(\ - AlertDescription.missing_srp_username, - AlertLevel.warning)): - yield result - - #Get ClientHello - for result in self._getMsg(ContentType.handshake, - HandshakeType.client_hello): - if result in (0,1): - yield result - else: - break - clientHello = result - - #Check ClientHello - #If client's version is too low, reject it (COPIED CODE; BAD!) - if clientHello.client_version < settings.minVersion: - self.version = settings.minVersion - for result in self._sendError(\ - AlertDescription.protocol_version, - "Too old version: %s" % str(clientHello.client_version)): - yield result - - #If client's version is too high, propose my highest version - elif clientHello.client_version > settings.maxVersion: - self.version = settings.maxVersion - - else: - #Set the version to the client's version - self.version = clientHello.client_version - - #Recalculate the privileged cipher suite, making sure to - #pick an SRP suite - cipherSuites = [c for c in cipherSuites if c in \ - CipherSuite.srpSuites + \ - CipherSuite.srpRsaSuites] - for cipherSuite in cipherSuites: - if cipherSuite in clientHello.cipher_suites: - break - else: - for result in self._sendError(\ - AlertDescription.handshake_failure): - yield result - - #Get the client nonce; create server nonce - clientRandom = clientHello.random - serverRandom = getRandomBytes(32) - - #The username better be there, this time - if not clientHello.srp_username: - for result in self._sendError(\ - AlertDescription.illegal_parameter, - "Client resent a hello, but without the SRP"\ - " username"): - yield result - - - #Get username - self.allegedSrpUsername = clientHello.srp_username - - #Get parameters from username - try: - entry = verifierDB[self.allegedSrpUsername] - except KeyError: - for result in self._sendError(\ - AlertDescription.unknown_srp_username): - yield result - (N, g, s, v) = entry - - #Calculate server's ephemeral DH values (b, B) - b = bytesToNumber(getRandomBytes(32)) - k = makeK(N, g) - B = (powMod(g, b, N) + (k*v)) % N - - #Create ServerKeyExchange, signing it if necessary - serverKeyExchange = ServerKeyExchange(cipherSuite) - serverKeyExchange.createSRP(N, g, stringToBytes(s), B) - if cipherSuite in CipherSuite.srpRsaSuites: - hashBytes = serverKeyExchange.hash(clientRandom, - serverRandom) - serverKeyExchange.signature = privateKey.sign(hashBytes) - - #Send ServerHello[, Certificate], ServerKeyExchange, - #ServerHelloDone - msgs = [] - serverHello = ServerHello() - serverHello.create(self.version, serverRandom, sessionID, - cipherSuite, certificateType) - msgs.append(serverHello) - if cipherSuite in CipherSuite.srpRsaSuites: - certificateMsg = Certificate(certificateType) - certificateMsg.create(serverCertChain) - msgs.append(certificateMsg) - msgs.append(serverKeyExchange) - msgs.append(ServerHelloDone()) - for result in self._sendMsgs(msgs): - yield result - - #From here on, the client's messages must have the right version - self._versionCheck = True - - #Get and check ClientKeyExchange - for result in self._getMsg(ContentType.handshake, - HandshakeType.client_key_exchange, - cipherSuite): - if result in (0,1): - yield result - else: - break - clientKeyExchange = result - A = clientKeyExchange.srp_A - if A % N == 0: - postFinishedError = (AlertDescription.illegal_parameter, - "Suspicious A value") - #Calculate u - u = makeU(N, A, B) - - #Calculate premaster secret - S = powMod((A * powMod(v,u,N)) % N, b, N) - premasterSecret = numberToBytes(S) - - - #If we've selected an RSA suite, exchange keys and calculate - #premaster secret: - elif cipherSuite in CipherSuite.rsaSuites: - - #Send ServerHello, Certificate[, CertificateRequest], - #ServerHelloDone - msgs = [] - msgs.append(ServerHello().create(self.version, serverRandom, - sessionID, cipherSuite, certificateType)) - msgs.append(Certificate(certificateType).create(serverCertChain)) - if reqCert: - msgs.append(CertificateRequest()) - msgs.append(ServerHelloDone()) - for result in self._sendMsgs(msgs): - yield result - - #From here on, the client's messages must have the right version - self._versionCheck = True - - #Get [Certificate,] (if was requested) - if reqCert: - if self.version == (3,0): - for result in self._getMsg((ContentType.handshake, - ContentType.alert), - HandshakeType.certificate, - certificateType): - if result in (0,1): - yield result - else: - break - msg = result - - if isinstance(msg, Alert): - #If it's not a no_certificate alert, re-raise - alert = msg - if alert.description != \ - AlertDescription.no_certificate: - self._shutdown(False) - raise TLSRemoteAlert(alert) - elif isinstance(msg, Certificate): - clientCertificate = msg - if clientCertificate.certChain and \ - clientCertificate.certChain.getNumCerts()!=0: - clientCertChain = clientCertificate.certChain - else: - raise AssertionError() - elif self.version in ((3,1), (3,2)): - for result in self._getMsg(ContentType.handshake, - HandshakeType.certificate, - certificateType): - if result in (0,1): - yield result - else: - break - clientCertificate = result - if clientCertificate.certChain and \ - clientCertificate.certChain.getNumCerts()!=0: - clientCertChain = clientCertificate.certChain - else: - raise AssertionError() - - #Get ClientKeyExchange - for result in self._getMsg(ContentType.handshake, - HandshakeType.client_key_exchange, - cipherSuite): - if result in (0,1): - yield result - else: - break - clientKeyExchange = result - - #Decrypt ClientKeyExchange - premasterSecret = privateKey.decrypt(\ - clientKeyExchange.encryptedPreMasterSecret) - - randomPreMasterSecret = getRandomBytes(48) - versionCheck = (premasterSecret[0], premasterSecret[1]) - if not premasterSecret: - premasterSecret = randomPreMasterSecret - elif len(premasterSecret)!=48: - premasterSecret = randomPreMasterSecret - elif versionCheck != clientHello.client_version: - if versionCheck != self.version: #Tolerate buggy IE clients - premasterSecret = randomPreMasterSecret - - #Get and check CertificateVerify, if relevant - if clientCertChain: - if self.version == (3,0): - #Create a temporary session object, just for the purpose - #of checking the CertificateVerify - session = Session() - session._calcMasterSecret(self.version, premasterSecret, - clientRandom, serverRandom) - verifyBytes = self._calcSSLHandshakeHash(\ - session.masterSecret, "") - elif self.version in ((3,1), (3,2)): - verifyBytes = stringToBytes(self._handshake_md5.digest() +\ - self._handshake_sha.digest()) - for result in self._getMsg(ContentType.handshake, - HandshakeType.certificate_verify): - if result in (0,1): - yield result - else: - break - certificateVerify = result - publicKey = clientCertChain.getEndEntityPublicKey() - if len(publicKey) < settings.minKeySize: - postFinishedError = (AlertDescription.handshake_failure, - "Client's public key too small: %d" % len(publicKey)) - if len(publicKey) > settings.maxKeySize: - postFinishedError = (AlertDescription.handshake_failure, - "Client's public key too large: %d" % len(publicKey)) - - if not publicKey.verify(certificateVerify.signature, - verifyBytes): - postFinishedError = (AlertDescription.decrypt_error, - "Signature failed to verify") - - - #Create the session object - self.session = Session() - self.session._calcMasterSecret(self.version, premasterSecret, - clientRandom, serverRandom) - self.session.sessionID = sessionID - self.session.cipherSuite = cipherSuite - self.session.srpUsername = self.allegedSrpUsername - self.session.clientCertChain = clientCertChain - self.session.serverCertChain = serverCertChain - - #Calculate pending connection states - self._calcPendingStates(clientRandom, serverRandom, - settings.cipherImplementations) - - #Exchange ChangeCipherSpec and Finished messages - for result in self._getFinished(): - yield result - - #If we were holding a post-finished error until receiving the client - #finished message, send it now. We delay the call until this point - #because calling sendError() throws an exception, and our caller might - #shut down the socket upon receiving the exception. If he did, and the - #client was still sending its ChangeCipherSpec or Finished messages, it - #would cause a socket error on the client side. This is a lot of - #consideration to show to misbehaving clients, but this would also - #cause problems with fault-testing. - if postFinishedError: - for result in self._sendError(*postFinishedError): - yield result - - for result in self._sendFinished(): - yield result - - #Add the session object to the session cache - if sessionCache and sessionID: - sessionCache[bytesToString(sessionID)] = self.session - - #Mark the connection as open - self.session._setResumable(True) - self._handshakeDone(resumed=False) - - - def _handshakeWrapperAsync(self, handshaker, checker): - if not self.fault: - try: - for result in handshaker: - yield result - if checker: - try: - checker(self) - except TLSAuthenticationError: - alert = Alert().create(AlertDescription.close_notify, - AlertLevel.fatal) - for result in self._sendMsg(alert): - yield result - raise - except: - self._shutdown(False) - raise - else: - try: - for result in handshaker: - yield result - if checker: - try: - checker(self) - except TLSAuthenticationError: - alert = Alert().create(AlertDescription.close_notify, - AlertLevel.fatal) - for result in self._sendMsg(alert): - yield result - raise - except socket.error, e: - raise TLSFaultError("socket error!") - except TLSAbruptCloseError, e: - raise TLSFaultError("abrupt close error!") - except TLSAlert, alert: - if alert.description not in Fault.faultAlerts[self.fault]: - raise TLSFaultError(str(alert)) - else: - pass - except: - self._shutdown(False) - raise - else: - raise TLSFaultError("No error!") - - - def _getKeyFromChain(self, certificate, settings): - #Get and check cert chain from the Certificate message - certChain = certificate.certChain - if not certChain or certChain.getNumCerts() == 0: - for result in self._sendError(AlertDescription.illegal_parameter, - "Other party sent a Certificate message without "\ - "certificates"): - yield result - - #Get and check public key from the cert chain - publicKey = certChain.getEndEntityPublicKey() - if len(publicKey) < settings.minKeySize: - for result in self._sendError(AlertDescription.handshake_failure, - "Other party's public key too small: %d" % len(publicKey)): - yield result - if len(publicKey) > settings.maxKeySize: - for result in self._sendError(AlertDescription.handshake_failure, - "Other party's public key too large: %d" % len(publicKey)): - yield result - - yield publicKey, certChain diff --git a/gdata/analytics/tlslite/TLSRecordLayer.py b/gdata/analytics/tlslite/TLSRecordLayer.py deleted file mode 100644 index 875ce80070..0000000000 --- a/gdata/analytics/tlslite/TLSRecordLayer.py +++ /dev/null @@ -1,1123 +0,0 @@ -"""Helper class for TLSConnection.""" -from __future__ import generators - -from utils.compat import * -from utils.cryptomath import * -from utils.cipherfactory import createAES, createRC4, createTripleDES -from utils.codec import * -from errors import * -from messages import * -from mathtls import * -from constants import * -from utils.cryptomath import getRandomBytes -from utils import hmac -from FileObject import FileObject -import sha -import md5 -import socket -import errno -import traceback - -class _ConnectionState: - def __init__(self): - self.macContext = None - self.encContext = None - self.seqnum = 0 - - def getSeqNumStr(self): - w = Writer(8) - w.add(self.seqnum, 8) - seqnumStr = bytesToString(w.bytes) - self.seqnum += 1 - return seqnumStr - - -class TLSRecordLayer: - """ - This class handles data transmission for a TLS connection. - - Its only subclass is L{tlslite.TLSConnection.TLSConnection}. We've - separated the code in this class from TLSConnection to make things - more readable. - - - @type sock: socket.socket - @ivar sock: The underlying socket object. - - @type session: L{tlslite.Session.Session} - @ivar session: The session corresponding to this connection. - - Due to TLS session resumption, multiple connections can correspond - to the same underlying session. - - @type version: tuple - @ivar version: The TLS version being used for this connection. - - (3,0) means SSL 3.0, and (3,1) means TLS 1.0. - - @type closed: bool - @ivar closed: If this connection is closed. - - @type resumed: bool - @ivar resumed: If this connection is based on a resumed session. - - @type allegedSharedKeyUsername: str or None - @ivar allegedSharedKeyUsername: This is set to the shared-key - username asserted by the client, whether the handshake succeeded or - not. If the handshake fails, this can be inspected to - determine if a guessing attack is in progress against a particular - user account. - - @type allegedSrpUsername: str or None - @ivar allegedSrpUsername: This is set to the SRP username - asserted by the client, whether the handshake succeeded or not. - If the handshake fails, this can be inspected to determine - if a guessing attack is in progress against a particular user - account. - - @type closeSocket: bool - @ivar closeSocket: If the socket should be closed when the - connection is closed (writable). - - If you set this to True, TLS Lite will assume the responsibility of - closing the socket when the TLS Connection is shutdown (either - through an error or through the user calling close()). The default - is False. - - @type ignoreAbruptClose: bool - @ivar ignoreAbruptClose: If an abrupt close of the socket should - raise an error (writable). - - If you set this to True, TLS Lite will not raise a - L{tlslite.errors.TLSAbruptCloseError} exception if the underlying - socket is unexpectedly closed. Such an unexpected closure could be - caused by an attacker. However, it also occurs with some incorrect - TLS implementations. - - You should set this to True only if you're not worried about an - attacker truncating the connection, and only if necessary to avoid - spurious errors. The default is False. - - @sort: __init__, read, readAsync, write, writeAsync, close, closeAsync, - getCipherImplementation, getCipherName - """ - - def __init__(self, sock): - self.sock = sock - - #My session object (Session instance; read-only) - self.session = None - - #Am I a client or server? - self._client = None - - #Buffers for processing messages - self._handshakeBuffer = [] - self._readBuffer = "" - - #Handshake digests - self._handshake_md5 = md5.md5() - self._handshake_sha = sha.sha() - - #TLS Protocol Version - self.version = (0,0) #read-only - self._versionCheck = False #Once we choose a version, this is True - - #Current and Pending connection states - self._writeState = _ConnectionState() - self._readState = _ConnectionState() - self._pendingWriteState = _ConnectionState() - self._pendingReadState = _ConnectionState() - - #Is the connection open? - self.closed = True #read-only - self._refCount = 0 #Used to trigger closure - - #Is this a resumed (or shared-key) session? - self.resumed = False #read-only - - #What username did the client claim in his handshake? - self.allegedSharedKeyUsername = None - self.allegedSrpUsername = None - - #On a call to close(), do we close the socket? (writeable) - self.closeSocket = False - - #If the socket is abruptly closed, do we ignore it - #and pretend the connection was shut down properly? (writeable) - self.ignoreAbruptClose = False - - #Fault we will induce, for testing purposes - self.fault = None - - #********************************************************* - # Public Functions START - #********************************************************* - - def read(self, max=None, min=1): - """Read some data from the TLS connection. - - This function will block until at least 'min' bytes are - available (or the connection is closed). - - If an exception is raised, the connection will have been - automatically closed. - - @type max: int - @param max: The maximum number of bytes to return. - - @type min: int - @param min: The minimum number of bytes to return - - @rtype: str - @return: A string of no more than 'max' bytes, and no fewer - than 'min' (unless the connection has been closed, in which - case fewer than 'min' bytes may be returned). - - @raise socket.error: If a socket error occurs. - @raise tlslite.errors.TLSAbruptCloseError: If the socket is closed - without a preceding alert. - @raise tlslite.errors.TLSAlert: If a TLS alert is signalled. - """ - for result in self.readAsync(max, min): - pass - return result - - def readAsync(self, max=None, min=1): - """Start a read operation on the TLS connection. - - This function returns a generator which behaves similarly to - read(). Successive invocations of the generator will return 0 - if it is waiting to read from the socket, 1 if it is waiting - to write to the socket, or a string if the read operation has - completed. - - @rtype: iterable - @return: A generator; see above for details. - """ - try: - while len(self._readBuffer)= len(s): - break - if endIndex > len(s): - endIndex = len(s) - block = stringToBytes(s[startIndex : endIndex]) - applicationData = ApplicationData().create(block) - for result in self._sendMsg(applicationData, skipEmptyFrag): - yield result - skipEmptyFrag = True #only send an empy fragment on 1st message - index += 1 - except: - self._shutdown(False) - raise - - def close(self): - """Close the TLS connection. - - This function will block until it has exchanged close_notify - alerts with the other party. After doing so, it will shut down the - TLS connection. Further attempts to read through this connection - will return "". Further attempts to write through this connection - will raise ValueError. - - If makefile() has been called on this connection, the connection - will be not be closed until the connection object and all file - objects have been closed. - - Even if an exception is raised, the connection will have been - closed. - - @raise socket.error: If a socket error occurs. - @raise tlslite.errors.TLSAbruptCloseError: If the socket is closed - without a preceding alert. - @raise tlslite.errors.TLSAlert: If a TLS alert is signalled. - """ - if not self.closed: - for result in self._decrefAsync(): - pass - - def closeAsync(self): - """Start a close operation on the TLS connection. - - This function returns a generator which behaves similarly to - close(). Successive invocations of the generator will return 0 - if it is waiting to read from the socket, 1 if it is waiting - to write to the socket, or will raise StopIteration if the - close operation has completed. - - @rtype: iterable - @return: A generator; see above for details. - """ - if not self.closed: - for result in self._decrefAsync(): - yield result - - def _decrefAsync(self): - self._refCount -= 1 - if self._refCount == 0 and not self.closed: - try: - for result in self._sendMsg(Alert().create(\ - AlertDescription.close_notify, AlertLevel.warning)): - yield result - alert = None - while not alert: - for result in self._getMsg((ContentType.alert, \ - ContentType.application_data)): - if result in (0,1): - yield result - if result.contentType == ContentType.alert: - alert = result - if alert.description == AlertDescription.close_notify: - self._shutdown(True) - else: - raise TLSRemoteAlert(alert) - except (socket.error, TLSAbruptCloseError): - #If the other side closes the socket, that's okay - self._shutdown(True) - except: - self._shutdown(False) - raise - - def getCipherName(self): - """Get the name of the cipher used with this connection. - - @rtype: str - @return: The name of the cipher used with this connection. - Either 'aes128', 'aes256', 'rc4', or '3des'. - """ - if not self._writeState.encContext: - return None - return self._writeState.encContext.name - - def getCipherImplementation(self): - """Get the name of the cipher implementation used with - this connection. - - @rtype: str - @return: The name of the cipher implementation used with - this connection. Either 'python', 'cryptlib', 'openssl', - or 'pycrypto'. - """ - if not self._writeState.encContext: - return None - return self._writeState.encContext.implementation - - - - #Emulate a socket, somewhat - - def send(self, s): - """Send data to the TLS connection (socket emulation). - - @raise socket.error: If a socket error occurs. - """ - self.write(s) - return len(s) - - def sendall(self, s): - """Send data to the TLS connection (socket emulation). - - @raise socket.error: If a socket error occurs. - """ - self.write(s) - - def recv(self, bufsize): - """Get some data from the TLS connection (socket emulation). - - @raise socket.error: If a socket error occurs. - @raise tlslite.errors.TLSAbruptCloseError: If the socket is closed - without a preceding alert. - @raise tlslite.errors.TLSAlert: If a TLS alert is signalled. - """ - return self.read(bufsize) - - def makefile(self, mode='r', bufsize=-1): - """Create a file object for the TLS connection (socket emulation). - - @rtype: L{tlslite.FileObject.FileObject} - """ - self._refCount += 1 - return FileObject(self, mode, bufsize) - - def getsockname(self): - """Return the socket's own address (socket emulation).""" - return self.sock.getsockname() - - def getpeername(self): - """Return the remote address to which the socket is connected - (socket emulation).""" - return self.sock.getpeername() - - def settimeout(self, value): - """Set a timeout on blocking socket operations (socket emulation).""" - return self.sock.settimeout(value) - - def gettimeout(self): - """Return the timeout associated with socket operations (socket - emulation).""" - return self.sock.gettimeout() - - def setsockopt(self, level, optname, value): - """Set the value of the given socket option (socket emulation).""" - return self.sock.setsockopt(level, optname, value) - - - #********************************************************* - # Public Functions END - #********************************************************* - - def _shutdown(self, resumable): - self._writeState = _ConnectionState() - self._readState = _ConnectionState() - #Don't do this: self._readBuffer = "" - self.version = (0,0) - self._versionCheck = False - self.closed = True - if self.closeSocket: - self.sock.close() - - #Even if resumable is False, we'll never toggle this on - if not resumable and self.session: - self.session.resumable = False - - - def _sendError(self, alertDescription, errorStr=None): - alert = Alert().create(alertDescription, AlertLevel.fatal) - for result in self._sendMsg(alert): - yield result - self._shutdown(False) - raise TLSLocalAlert(alert, errorStr) - - def _sendMsgs(self, msgs): - skipEmptyFrag = False - for msg in msgs: - for result in self._sendMsg(msg, skipEmptyFrag): - yield result - skipEmptyFrag = True - - def _sendMsg(self, msg, skipEmptyFrag=False): - bytes = msg.write() - contentType = msg.contentType - - #Whenever we're connected and asked to send a message, - #we first send an empty Application Data message. This prevents - #an attacker from launching a chosen-plaintext attack based on - #knowing the next IV. - if not self.closed and not skipEmptyFrag and self.version == (3,1): - if self._writeState.encContext: - if self._writeState.encContext.isBlockCipher: - for result in self._sendMsg(ApplicationData(), - skipEmptyFrag=True): - yield result - - #Update handshake hashes - if contentType == ContentType.handshake: - bytesStr = bytesToString(bytes) - self._handshake_md5.update(bytesStr) - self._handshake_sha.update(bytesStr) - - #Calculate MAC - if self._writeState.macContext: - seqnumStr = self._writeState.getSeqNumStr() - bytesStr = bytesToString(bytes) - mac = self._writeState.macContext.copy() - mac.update(seqnumStr) - mac.update(chr(contentType)) - if self.version == (3,0): - mac.update( chr( int(len(bytes)/256) ) ) - mac.update( chr( int(len(bytes)%256) ) ) - elif self.version in ((3,1), (3,2)): - mac.update(chr(self.version[0])) - mac.update(chr(self.version[1])) - mac.update( chr( int(len(bytes)/256) ) ) - mac.update( chr( int(len(bytes)%256) ) ) - else: - raise AssertionError() - mac.update(bytesStr) - macString = mac.digest() - macBytes = stringToBytes(macString) - if self.fault == Fault.badMAC: - macBytes[0] = (macBytes[0]+1) % 256 - - #Encrypt for Block or Stream Cipher - if self._writeState.encContext: - #Add padding and encrypt (for Block Cipher): - if self._writeState.encContext.isBlockCipher: - - #Add TLS 1.1 fixed block - if self.version == (3,2): - bytes = self.fixedIVBlock + bytes - - #Add padding: bytes = bytes + (macBytes + paddingBytes) - currentLength = len(bytes) + len(macBytes) + 1 - blockLength = self._writeState.encContext.block_size - paddingLength = blockLength-(currentLength % blockLength) - - paddingBytes = createByteArraySequence([paddingLength] * \ - (paddingLength+1)) - if self.fault == Fault.badPadding: - paddingBytes[0] = (paddingBytes[0]+1) % 256 - endBytes = concatArrays(macBytes, paddingBytes) - bytes = concatArrays(bytes, endBytes) - #Encrypt - plaintext = stringToBytes(bytes) - ciphertext = self._writeState.encContext.encrypt(plaintext) - bytes = stringToBytes(ciphertext) - - #Encrypt (for Stream Cipher) - else: - bytes = concatArrays(bytes, macBytes) - plaintext = bytesToString(bytes) - ciphertext = self._writeState.encContext.encrypt(plaintext) - bytes = stringToBytes(ciphertext) - - #Add record header and send - r = RecordHeader3().create(self.version, contentType, len(bytes)) - s = bytesToString(concatArrays(r.write(), bytes)) - while 1: - try: - bytesSent = self.sock.send(s) #Might raise socket.error - except socket.error, why: - if why[0] == errno.EWOULDBLOCK: - yield 1 - continue - else: - raise - if bytesSent == len(s): - return - s = s[bytesSent:] - yield 1 - - - def _getMsg(self, expectedType, secondaryType=None, constructorType=None): - try: - if not isinstance(expectedType, tuple): - expectedType = (expectedType,) - - #Spin in a loop, until we've got a non-empty record of a type we - #expect. The loop will be repeated if: - # - we receive a renegotiation attempt; we send no_renegotiation, - # then try again - # - we receive an empty application-data fragment; we try again - while 1: - for result in self._getNextRecord(): - if result in (0,1): - yield result - recordHeader, p = result - - #If this is an empty application-data fragment, try again - if recordHeader.type == ContentType.application_data: - if p.index == len(p.bytes): - continue - - #If we received an unexpected record type... - if recordHeader.type not in expectedType: - - #If we received an alert... - if recordHeader.type == ContentType.alert: - alert = Alert().parse(p) - - #We either received a fatal error, a warning, or a - #close_notify. In any case, we're going to close the - #connection. In the latter two cases we respond with - #a close_notify, but ignore any socket errors, since - #the other side might have already closed the socket. - if alert.level == AlertLevel.warning or \ - alert.description == AlertDescription.close_notify: - - #If the sendMsg() call fails because the socket has - #already been closed, we will be forgiving and not - #report the error nor invalidate the "resumability" - #of the session. - try: - alertMsg = Alert() - alertMsg.create(AlertDescription.close_notify, - AlertLevel.warning) - for result in self._sendMsg(alertMsg): - yield result - except socket.error: - pass - - if alert.description == \ - AlertDescription.close_notify: - self._shutdown(True) - elif alert.level == AlertLevel.warning: - self._shutdown(False) - - else: #Fatal alert: - self._shutdown(False) - - #Raise the alert as an exception - raise TLSRemoteAlert(alert) - - #If we received a renegotiation attempt... - if recordHeader.type == ContentType.handshake: - subType = p.get(1) - reneg = False - if self._client: - if subType == HandshakeType.hello_request: - reneg = True - else: - if subType == HandshakeType.client_hello: - reneg = True - #Send no_renegotiation, then try again - if reneg: - alertMsg = Alert() - alertMsg.create(AlertDescription.no_renegotiation, - AlertLevel.warning) - for result in self._sendMsg(alertMsg): - yield result - continue - - #Otherwise: this is an unexpected record, but neither an - #alert nor renegotiation - for result in self._sendError(\ - AlertDescription.unexpected_message, - "received type=%d" % recordHeader.type): - yield result - - break - - #Parse based on content_type - if recordHeader.type == ContentType.change_cipher_spec: - yield ChangeCipherSpec().parse(p) - elif recordHeader.type == ContentType.alert: - yield Alert().parse(p) - elif recordHeader.type == ContentType.application_data: - yield ApplicationData().parse(p) - elif recordHeader.type == ContentType.handshake: - #Convert secondaryType to tuple, if it isn't already - if not isinstance(secondaryType, tuple): - secondaryType = (secondaryType,) - - #If it's a handshake message, check handshake header - if recordHeader.ssl2: - subType = p.get(1) - if subType != HandshakeType.client_hello: - for result in self._sendError(\ - AlertDescription.unexpected_message, - "Can only handle SSLv2 ClientHello messages"): - yield result - if HandshakeType.client_hello not in secondaryType: - for result in self._sendError(\ - AlertDescription.unexpected_message): - yield result - subType = HandshakeType.client_hello - else: - subType = p.get(1) - if subType not in secondaryType: - for result in self._sendError(\ - AlertDescription.unexpected_message, - "Expecting %s, got %s" % (str(secondaryType), subType)): - yield result - - #Update handshake hashes - sToHash = bytesToString(p.bytes) - self._handshake_md5.update(sToHash) - self._handshake_sha.update(sToHash) - - #Parse based on handshake type - if subType == HandshakeType.client_hello: - yield ClientHello(recordHeader.ssl2).parse(p) - elif subType == HandshakeType.server_hello: - yield ServerHello().parse(p) - elif subType == HandshakeType.certificate: - yield Certificate(constructorType).parse(p) - elif subType == HandshakeType.certificate_request: - yield CertificateRequest().parse(p) - elif subType == HandshakeType.certificate_verify: - yield CertificateVerify().parse(p) - elif subType == HandshakeType.server_key_exchange: - yield ServerKeyExchange(constructorType).parse(p) - elif subType == HandshakeType.server_hello_done: - yield ServerHelloDone().parse(p) - elif subType == HandshakeType.client_key_exchange: - yield ClientKeyExchange(constructorType, \ - self.version).parse(p) - elif subType == HandshakeType.finished: - yield Finished(self.version).parse(p) - else: - raise AssertionError() - - #If an exception was raised by a Parser or Message instance: - except SyntaxError, e: - for result in self._sendError(AlertDescription.decode_error, - formatExceptionTrace(e)): - yield result - - - #Returns next record or next handshake message - def _getNextRecord(self): - - #If there's a handshake message waiting, return it - if self._handshakeBuffer: - recordHeader, bytes = self._handshakeBuffer[0] - self._handshakeBuffer = self._handshakeBuffer[1:] - yield (recordHeader, Parser(bytes)) - return - - #Otherwise... - #Read the next record header - bytes = createByteArraySequence([]) - recordHeaderLength = 1 - ssl2 = False - while 1: - try: - s = self.sock.recv(recordHeaderLength-len(bytes)) - except socket.error, why: - if why[0] == errno.EWOULDBLOCK: - yield 0 - continue - else: - raise - - #If the connection was abruptly closed, raise an error - if len(s)==0: - raise TLSAbruptCloseError() - - bytes += stringToBytes(s) - if len(bytes)==1: - if bytes[0] in ContentType.all: - ssl2 = False - recordHeaderLength = 5 - elif bytes[0] == 128: - ssl2 = True - recordHeaderLength = 2 - else: - raise SyntaxError() - if len(bytes) == recordHeaderLength: - break - - #Parse the record header - if ssl2: - r = RecordHeader2().parse(Parser(bytes)) - else: - r = RecordHeader3().parse(Parser(bytes)) - - #Check the record header fields - if r.length > 18432: - for result in self._sendError(AlertDescription.record_overflow): - yield result - - #Read the record contents - bytes = createByteArraySequence([]) - while 1: - try: - s = self.sock.recv(r.length - len(bytes)) - except socket.error, why: - if why[0] == errno.EWOULDBLOCK: - yield 0 - continue - else: - raise - - #If the connection is closed, raise a socket error - if len(s)==0: - raise TLSAbruptCloseError() - - bytes += stringToBytes(s) - if len(bytes) == r.length: - break - - #Check the record header fields (2) - #We do this after reading the contents from the socket, so that - #if there's an error, we at least don't leave extra bytes in the - #socket.. - # - # THIS CHECK HAS NO SECURITY RELEVANCE (?), BUT COULD HURT INTEROP. - # SO WE LEAVE IT OUT FOR NOW. - # - #if self._versionCheck and r.version != self.version: - # for result in self._sendError(AlertDescription.protocol_version, - # "Version in header field: %s, should be %s" % (str(r.version), - # str(self.version))): - # yield result - - #Decrypt the record - for result in self._decryptRecord(r.type, bytes): - if result in (0,1): - yield result - else: - break - bytes = result - p = Parser(bytes) - - #If it doesn't contain handshake messages, we can just return it - if r.type != ContentType.handshake: - yield (r, p) - #If it's an SSLv2 ClientHello, we can return it as well - elif r.ssl2: - yield (r, p) - else: - #Otherwise, we loop through and add the handshake messages to the - #handshake buffer - while 1: - if p.index == len(bytes): #If we're at the end - if not self._handshakeBuffer: - for result in self._sendError(\ - AlertDescription.decode_error, \ - "Received empty handshake record"): - yield result - break - #There needs to be at least 4 bytes to get a header - if p.index+4 > len(bytes): - for result in self._sendError(\ - AlertDescription.decode_error, - "A record has a partial handshake message (1)"): - yield result - p.get(1) # skip handshake type - msgLength = p.get(3) - if p.index+msgLength > len(bytes): - for result in self._sendError(\ - AlertDescription.decode_error, - "A record has a partial handshake message (2)"): - yield result - - handshakePair = (r, bytes[p.index-4 : p.index+msgLength]) - self._handshakeBuffer.append(handshakePair) - p.index += msgLength - - #We've moved at least one handshake message into the - #handshakeBuffer, return the first one - recordHeader, bytes = self._handshakeBuffer[0] - self._handshakeBuffer = self._handshakeBuffer[1:] - yield (recordHeader, Parser(bytes)) - - - def _decryptRecord(self, recordType, bytes): - if self._readState.encContext: - - #Decrypt if it's a block cipher - if self._readState.encContext.isBlockCipher: - blockLength = self._readState.encContext.block_size - if len(bytes) % blockLength != 0: - for result in self._sendError(\ - AlertDescription.decryption_failed, - "Encrypted data not a multiple of blocksize"): - yield result - ciphertext = bytesToString(bytes) - plaintext = self._readState.encContext.decrypt(ciphertext) - if self.version == (3,2): #For TLS 1.1, remove explicit IV - plaintext = plaintext[self._readState.encContext.block_size : ] - bytes = stringToBytes(plaintext) - - #Check padding - paddingGood = True - paddingLength = bytes[-1] - if (paddingLength+1) > len(bytes): - paddingGood=False - totalPaddingLength = 0 - else: - if self.version == (3,0): - totalPaddingLength = paddingLength+1 - elif self.version in ((3,1), (3,2)): - totalPaddingLength = paddingLength+1 - paddingBytes = bytes[-totalPaddingLength:-1] - for byte in paddingBytes: - if byte != paddingLength: - paddingGood = False - totalPaddingLength = 0 - else: - raise AssertionError() - - #Decrypt if it's a stream cipher - else: - paddingGood = True - ciphertext = bytesToString(bytes) - plaintext = self._readState.encContext.decrypt(ciphertext) - bytes = stringToBytes(plaintext) - totalPaddingLength = 0 - - #Check MAC - macGood = True - macLength = self._readState.macContext.digest_size - endLength = macLength + totalPaddingLength - if endLength > len(bytes): - macGood = False - else: - #Read MAC - startIndex = len(bytes) - endLength - endIndex = startIndex + macLength - checkBytes = bytes[startIndex : endIndex] - - #Calculate MAC - seqnumStr = self._readState.getSeqNumStr() - bytes = bytes[:-endLength] - bytesStr = bytesToString(bytes) - mac = self._readState.macContext.copy() - mac.update(seqnumStr) - mac.update(chr(recordType)) - if self.version == (3,0): - mac.update( chr( int(len(bytes)/256) ) ) - mac.update( chr( int(len(bytes)%256) ) ) - elif self.version in ((3,1), (3,2)): - mac.update(chr(self.version[0])) - mac.update(chr(self.version[1])) - mac.update( chr( int(len(bytes)/256) ) ) - mac.update( chr( int(len(bytes)%256) ) ) - else: - raise AssertionError() - mac.update(bytesStr) - macString = mac.digest() - macBytes = stringToBytes(macString) - - #Compare MACs - if macBytes != checkBytes: - macGood = False - - if not (paddingGood and macGood): - for result in self._sendError(AlertDescription.bad_record_mac, - "MAC failure (or padding failure)"): - yield result - - yield bytes - - def _handshakeStart(self, client): - self._client = client - self._handshake_md5 = md5.md5() - self._handshake_sha = sha.sha() - self._handshakeBuffer = [] - self.allegedSharedKeyUsername = None - self.allegedSrpUsername = None - self._refCount = 1 - - def _handshakeDone(self, resumed): - self.resumed = resumed - self.closed = False - - def _calcPendingStates(self, clientRandom, serverRandom, implementations): - if self.session.cipherSuite in CipherSuite.aes128Suites: - macLength = 20 - keyLength = 16 - ivLength = 16 - createCipherFunc = createAES - elif self.session.cipherSuite in CipherSuite.aes256Suites: - macLength = 20 - keyLength = 32 - ivLength = 16 - createCipherFunc = createAES - elif self.session.cipherSuite in CipherSuite.rc4Suites: - macLength = 20 - keyLength = 16 - ivLength = 0 - createCipherFunc = createRC4 - elif self.session.cipherSuite in CipherSuite.tripleDESSuites: - macLength = 20 - keyLength = 24 - ivLength = 8 - createCipherFunc = createTripleDES - else: - raise AssertionError() - - if self.version == (3,0): - createMACFunc = MAC_SSL - elif self.version in ((3,1), (3,2)): - createMACFunc = hmac.HMAC - - outputLength = (macLength*2) + (keyLength*2) + (ivLength*2) - - #Calculate Keying Material from Master Secret - if self.version == (3,0): - keyBlock = PRF_SSL(self.session.masterSecret, - concatArrays(serverRandom, clientRandom), - outputLength) - elif self.version in ((3,1), (3,2)): - keyBlock = PRF(self.session.masterSecret, - "key expansion", - concatArrays(serverRandom,clientRandom), - outputLength) - else: - raise AssertionError() - - #Slice up Keying Material - clientPendingState = _ConnectionState() - serverPendingState = _ConnectionState() - p = Parser(keyBlock) - clientMACBlock = bytesToString(p.getFixBytes(macLength)) - serverMACBlock = bytesToString(p.getFixBytes(macLength)) - clientKeyBlock = bytesToString(p.getFixBytes(keyLength)) - serverKeyBlock = bytesToString(p.getFixBytes(keyLength)) - clientIVBlock = bytesToString(p.getFixBytes(ivLength)) - serverIVBlock = bytesToString(p.getFixBytes(ivLength)) - clientPendingState.macContext = createMACFunc(clientMACBlock, - digestmod=sha) - serverPendingState.macContext = createMACFunc(serverMACBlock, - digestmod=sha) - clientPendingState.encContext = createCipherFunc(clientKeyBlock, - clientIVBlock, - implementations) - serverPendingState.encContext = createCipherFunc(serverKeyBlock, - serverIVBlock, - implementations) - - #Assign new connection states to pending states - if self._client: - self._pendingWriteState = clientPendingState - self._pendingReadState = serverPendingState - else: - self._pendingWriteState = serverPendingState - self._pendingReadState = clientPendingState - - if self.version == (3,2) and ivLength: - #Choose fixedIVBlock for TLS 1.1 (this is encrypted with the CBC - #residue to create the IV for each sent block) - self.fixedIVBlock = getRandomBytes(ivLength) - - def _changeWriteState(self): - self._writeState = self._pendingWriteState - self._pendingWriteState = _ConnectionState() - - def _changeReadState(self): - self._readState = self._pendingReadState - self._pendingReadState = _ConnectionState() - - def _sendFinished(self): - #Send ChangeCipherSpec - for result in self._sendMsg(ChangeCipherSpec()): - yield result - - #Switch to pending write state - self._changeWriteState() - - #Calculate verification data - verifyData = self._calcFinished(True) - if self.fault == Fault.badFinished: - verifyData[0] = (verifyData[0]+1)%256 - - #Send Finished message under new state - finished = Finished(self.version).create(verifyData) - for result in self._sendMsg(finished): - yield result - - def _getFinished(self): - #Get and check ChangeCipherSpec - for result in self._getMsg(ContentType.change_cipher_spec): - if result in (0,1): - yield result - changeCipherSpec = result - - if changeCipherSpec.type != 1: - for result in self._sendError(AlertDescription.illegal_parameter, - "ChangeCipherSpec type incorrect"): - yield result - - #Switch to pending read state - self._changeReadState() - - #Calculate verification data - verifyData = self._calcFinished(False) - - #Get and check Finished message under new state - for result in self._getMsg(ContentType.handshake, - HandshakeType.finished): - if result in (0,1): - yield result - finished = result - if finished.verify_data != verifyData: - for result in self._sendError(AlertDescription.decrypt_error, - "Finished message is incorrect"): - yield result - - def _calcFinished(self, send=True): - if self.version == (3,0): - if (self._client and send) or (not self._client and not send): - senderStr = "\x43\x4C\x4E\x54" - else: - senderStr = "\x53\x52\x56\x52" - - verifyData = self._calcSSLHandshakeHash(self.session.masterSecret, - senderStr) - return verifyData - - elif self.version in ((3,1), (3,2)): - if (self._client and send) or (not self._client and not send): - label = "client finished" - else: - label = "server finished" - - handshakeHashes = stringToBytes(self._handshake_md5.digest() + \ - self._handshake_sha.digest()) - verifyData = PRF(self.session.masterSecret, label, handshakeHashes, - 12) - return verifyData - else: - raise AssertionError() - - #Used for Finished messages and CertificateVerify messages in SSL v3 - def _calcSSLHandshakeHash(self, masterSecret, label): - masterSecretStr = bytesToString(masterSecret) - - imac_md5 = self._handshake_md5.copy() - imac_sha = self._handshake_sha.copy() - - imac_md5.update(label + masterSecretStr + '\x36'*48) - imac_sha.update(label + masterSecretStr + '\x36'*40) - - md5Str = md5.md5(masterSecretStr + ('\x5c'*48) + \ - imac_md5.digest()).digest() - shaStr = sha.sha(masterSecretStr + ('\x5c'*40) + \ - imac_sha.digest()).digest() - - return stringToBytes(md5Str + shaStr) - diff --git a/gdata/analytics/tlslite/VerifierDB.py b/gdata/analytics/tlslite/VerifierDB.py deleted file mode 100644 index f706b17967..0000000000 --- a/gdata/analytics/tlslite/VerifierDB.py +++ /dev/null @@ -1,90 +0,0 @@ -"""Class for storing SRP password verifiers.""" - -from utils.cryptomath import * -from utils.compat import * -import mathtls -from BaseDB import BaseDB - -class VerifierDB(BaseDB): - """This class represent an in-memory or on-disk database of SRP - password verifiers. - - A VerifierDB can be passed to a server handshake to authenticate - a client based on one of the verifiers. - - This class is thread-safe. - """ - def __init__(self, filename=None): - """Create a new VerifierDB instance. - - @type filename: str - @param filename: Filename for an on-disk database, or None for - an in-memory database. If the filename already exists, follow - this with a call to open(). To create a new on-disk database, - follow this with a call to create(). - """ - BaseDB.__init__(self, filename, "verifier") - - def _getItem(self, username, valueStr): - (N, g, salt, verifier) = valueStr.split(" ") - N = base64ToNumber(N) - g = base64ToNumber(g) - salt = base64ToString(salt) - verifier = base64ToNumber(verifier) - return (N, g, salt, verifier) - - def __setitem__(self, username, verifierEntry): - """Add a verifier entry to the database. - - @type username: str - @param username: The username to associate the verifier with. - Must be less than 256 characters in length. Must not already - be in the database. - - @type verifierEntry: tuple - @param verifierEntry: The verifier entry to add. Use - L{tlslite.VerifierDB.VerifierDB.makeVerifier} to create a - verifier entry. - """ - BaseDB.__setitem__(self, username, verifierEntry) - - - def _setItem(self, username, value): - if len(username)>=256: - raise ValueError("username too long") - N, g, salt, verifier = value - N = numberToBase64(N) - g = numberToBase64(g) - salt = stringToBase64(salt) - verifier = numberToBase64(verifier) - valueStr = " ".join( (N, g, salt, verifier) ) - return valueStr - - def _checkItem(self, value, username, param): - (N, g, salt, verifier) = value - x = mathtls.makeX(salt, username, param) - v = powMod(g, x, N) - return (verifier == v) - - - def makeVerifier(username, password, bits): - """Create a verifier entry which can be stored in a VerifierDB. - - @type username: str - @param username: The username for this verifier. Must be less - than 256 characters in length. - - @type password: str - @param password: The password for this verifier. - - @type bits: int - @param bits: This values specifies which SRP group parameters - to use. It must be one of (1024, 1536, 2048, 3072, 4096, 6144, - 8192). Larger values are more secure but slower. 2048 is a - good compromise between safety and speed. - - @rtype: tuple - @return: A tuple which may be stored in a VerifierDB. - """ - return mathtls.makeVerifier(username, password, bits) - makeVerifier = staticmethod(makeVerifier) \ No newline at end of file diff --git a/gdata/analytics/tlslite/X509.py b/gdata/analytics/tlslite/X509.py deleted file mode 100644 index a47ddcfa2a..0000000000 --- a/gdata/analytics/tlslite/X509.py +++ /dev/null @@ -1,133 +0,0 @@ -"""Class representing an X.509 certificate.""" - -from utils.ASN1Parser import ASN1Parser -from utils.cryptomath import * -from utils.keyfactory import _createPublicRSAKey - - -class X509: - """This class represents an X.509 certificate. - - @type bytes: L{array.array} of unsigned bytes - @ivar bytes: The DER-encoded ASN.1 certificate - - @type publicKey: L{tlslite.utils.RSAKey.RSAKey} - @ivar publicKey: The subject public key from the certificate. - """ - - def __init__(self): - self.bytes = createByteArraySequence([]) - self.publicKey = None - - def parse(self, s): - """Parse a PEM-encoded X.509 certificate. - - @type s: str - @param s: A PEM-encoded X.509 certificate (i.e. a base64-encoded - certificate wrapped with "-----BEGIN CERTIFICATE-----" and - "-----END CERTIFICATE-----" tags). - """ - - start = s.find("-----BEGIN CERTIFICATE-----") - end = s.find("-----END CERTIFICATE-----") - if start == -1: - raise SyntaxError("Missing PEM prefix") - if end == -1: - raise SyntaxError("Missing PEM postfix") - s = s[start+len("-----BEGIN CERTIFICATE-----") : end] - - bytes = base64ToBytes(s) - self.parseBinary(bytes) - return self - - def parseBinary(self, bytes): - """Parse a DER-encoded X.509 certificate. - - @type bytes: str or L{array.array} of unsigned bytes - @param bytes: A DER-encoded X.509 certificate. - """ - - if isinstance(bytes, type("")): - bytes = stringToBytes(bytes) - - self.bytes = bytes - p = ASN1Parser(bytes) - - #Get the tbsCertificate - tbsCertificateP = p.getChild(0) - - #Is the optional version field present? - #This determines which index the key is at. - if tbsCertificateP.value[0]==0xA0: - subjectPublicKeyInfoIndex = 6 - else: - subjectPublicKeyInfoIndex = 5 - - #Get the subjectPublicKeyInfo - subjectPublicKeyInfoP = tbsCertificateP.getChild(\ - subjectPublicKeyInfoIndex) - - #Get the algorithm - algorithmP = subjectPublicKeyInfoP.getChild(0) - rsaOID = algorithmP.value - if list(rsaOID) != [6, 9, 42, 134, 72, 134, 247, 13, 1, 1, 1, 5, 0]: - raise SyntaxError("Unrecognized AlgorithmIdentifier") - - #Get the subjectPublicKey - subjectPublicKeyP = subjectPublicKeyInfoP.getChild(1) - - #Adjust for BIT STRING encapsulation - if (subjectPublicKeyP.value[0] !=0): - raise SyntaxError() - subjectPublicKeyP = ASN1Parser(subjectPublicKeyP.value[1:]) - - #Get the modulus and exponent - modulusP = subjectPublicKeyP.getChild(0) - publicExponentP = subjectPublicKeyP.getChild(1) - - #Decode them into numbers - n = bytesToNumber(modulusP.value) - e = bytesToNumber(publicExponentP.value) - - #Create a public key instance - self.publicKey = _createPublicRSAKey(n, e) - - def getFingerprint(self): - """Get the hex-encoded fingerprint of this certificate. - - @rtype: str - @return: A hex-encoded fingerprint. - """ - return sha.sha(self.bytes).hexdigest() - - def getCommonName(self): - """Get the Subject's Common Name from the certificate. - - The cryptlib_py module must be installed in order to use this - function. - - @rtype: str or None - @return: The CN component of the certificate's subject DN, if - present. - """ - import cryptlib_py - import array - c = cryptlib_py.cryptImportCert(self.bytes, cryptlib_py.CRYPT_UNUSED) - name = cryptlib_py.CRYPT_CERTINFO_COMMONNAME - try: - try: - length = cryptlib_py.cryptGetAttributeString(c, name, None) - returnVal = array.array('B', [0] * length) - cryptlib_py.cryptGetAttributeString(c, name, returnVal) - returnVal = returnVal.tostring() - except cryptlib_py.CryptException, e: - if e[0] == cryptlib_py.CRYPT_ERROR_NOTFOUND: - returnVal = None - return returnVal - finally: - cryptlib_py.cryptDestroyCert(c) - - def writeBytes(self): - return self.bytes - - diff --git a/gdata/analytics/tlslite/X509CertChain.py b/gdata/analytics/tlslite/X509CertChain.py deleted file mode 100644 index d5f0b4d42a..0000000000 --- a/gdata/analytics/tlslite/X509CertChain.py +++ /dev/null @@ -1,181 +0,0 @@ -"""Class representing an X.509 certificate chain.""" - -from utils import cryptomath - -class X509CertChain: - """This class represents a chain of X.509 certificates. - - @type x509List: list - @ivar x509List: A list of L{tlslite.X509.X509} instances, - starting with the end-entity certificate and with every - subsequent certificate certifying the previous. - """ - - def __init__(self, x509List=None): - """Create a new X509CertChain. - - @type x509List: list - @param x509List: A list of L{tlslite.X509.X509} instances, - starting with the end-entity certificate and with every - subsequent certificate certifying the previous. - """ - if x509List: - self.x509List = x509List - else: - self.x509List = [] - - def getNumCerts(self): - """Get the number of certificates in this chain. - - @rtype: int - """ - return len(self.x509List) - - def getEndEntityPublicKey(self): - """Get the public key from the end-entity certificate. - - @rtype: L{tlslite.utils.RSAKey.RSAKey} - """ - if self.getNumCerts() == 0: - raise AssertionError() - return self.x509List[0].publicKey - - def getFingerprint(self): - """Get the hex-encoded fingerprint of the end-entity certificate. - - @rtype: str - @return: A hex-encoded fingerprint. - """ - if self.getNumCerts() == 0: - raise AssertionError() - return self.x509List[0].getFingerprint() - - def getCommonName(self): - """Get the Subject's Common Name from the end-entity certificate. - - The cryptlib_py module must be installed in order to use this - function. - - @rtype: str or None - @return: The CN component of the certificate's subject DN, if - present. - """ - if self.getNumCerts() == 0: - raise AssertionError() - return self.x509List[0].getCommonName() - - def validate(self, x509TrustList): - """Check the validity of the certificate chain. - - This checks that every certificate in the chain validates with - the subsequent one, until some certificate validates with (or - is identical to) one of the passed-in root certificates. - - The cryptlib_py module must be installed in order to use this - function. - - @type x509TrustList: list of L{tlslite.X509.X509} - @param x509TrustList: A list of trusted root certificates. The - certificate chain must extend to one of these certificates to - be considered valid. - """ - - import cryptlib_py - c1 = None - c2 = None - lastC = None - rootC = None - - try: - rootFingerprints = [c.getFingerprint() for c in x509TrustList] - - #Check that every certificate in the chain validates with the - #next one - for cert1, cert2 in zip(self.x509List, self.x509List[1:]): - - #If we come upon a root certificate, we're done. - if cert1.getFingerprint() in rootFingerprints: - return True - - c1 = cryptlib_py.cryptImportCert(cert1.writeBytes(), - cryptlib_py.CRYPT_UNUSED) - c2 = cryptlib_py.cryptImportCert(cert2.writeBytes(), - cryptlib_py.CRYPT_UNUSED) - try: - cryptlib_py.cryptCheckCert(c1, c2) - except: - return False - cryptlib_py.cryptDestroyCert(c1) - c1 = None - cryptlib_py.cryptDestroyCert(c2) - c2 = None - - #If the last certificate is one of the root certificates, we're - #done. - if self.x509List[-1].getFingerprint() in rootFingerprints: - return True - - #Otherwise, find a root certificate that the last certificate - #chains to, and validate them. - lastC = cryptlib_py.cryptImportCert(self.x509List[-1].writeBytes(), - cryptlib_py.CRYPT_UNUSED) - for rootCert in x509TrustList: - rootC = cryptlib_py.cryptImportCert(rootCert.writeBytes(), - cryptlib_py.CRYPT_UNUSED) - if self._checkChaining(lastC, rootC): - try: - cryptlib_py.cryptCheckCert(lastC, rootC) - return True - except: - return False - return False - finally: - if not (c1 is None): - cryptlib_py.cryptDestroyCert(c1) - if not (c2 is None): - cryptlib_py.cryptDestroyCert(c2) - if not (lastC is None): - cryptlib_py.cryptDestroyCert(lastC) - if not (rootC is None): - cryptlib_py.cryptDestroyCert(rootC) - - - - def _checkChaining(self, lastC, rootC): - import cryptlib_py - import array - def compareNames(name): - try: - length = cryptlib_py.cryptGetAttributeString(lastC, name, None) - lastName = array.array('B', [0] * length) - cryptlib_py.cryptGetAttributeString(lastC, name, lastName) - lastName = lastName.tostring() - except cryptlib_py.CryptException, e: - if e[0] == cryptlib_py.CRYPT_ERROR_NOTFOUND: - lastName = None - try: - length = cryptlib_py.cryptGetAttributeString(rootC, name, None) - rootName = array.array('B', [0] * length) - cryptlib_py.cryptGetAttributeString(rootC, name, rootName) - rootName = rootName.tostring() - except cryptlib_py.CryptException, e: - if e[0] == cryptlib_py.CRYPT_ERROR_NOTFOUND: - rootName = None - - return lastName == rootName - - cryptlib_py.cryptSetAttribute(lastC, - cryptlib_py.CRYPT_CERTINFO_ISSUERNAME, - cryptlib_py.CRYPT_UNUSED) - - if not compareNames(cryptlib_py.CRYPT_CERTINFO_COUNTRYNAME): - return False - if not compareNames(cryptlib_py.CRYPT_CERTINFO_LOCALITYNAME): - return False - if not compareNames(cryptlib_py.CRYPT_CERTINFO_ORGANIZATIONNAME): - return False - if not compareNames(cryptlib_py.CRYPT_CERTINFO_ORGANIZATIONALUNITNAME): - return False - if not compareNames(cryptlib_py.CRYPT_CERTINFO_COMMONNAME): - return False - return True \ No newline at end of file diff --git a/gdata/analytics/tlslite/__init__.py b/gdata/analytics/tlslite/__init__.py deleted file mode 100644 index 47cfd1c6f1..0000000000 --- a/gdata/analytics/tlslite/__init__.py +++ /dev/null @@ -1,39 +0,0 @@ -""" -TLS Lite is a free python library that implements SSL v3, TLS v1, and -TLS v1.1. TLS Lite supports non-traditional authentication methods -such as SRP, shared keys, and cryptoIDs, in addition to X.509 -certificates. TLS Lite is pure python, however it can access OpenSSL, -cryptlib, pycrypto, and GMPY for faster crypto operations. TLS Lite -integrates with httplib, xmlrpclib, poplib, imaplib, smtplib, -SocketServer, asyncore, and Twisted. - -To use, do:: - - from tlslite.api import * - -Then use the L{tlslite.TLSConnection.TLSConnection} class with a socket, -or use one of the integration classes in L{tlslite.integration}. - -@version: 0.3.8 -""" -__version__ = "0.3.8" - -__all__ = ["api", - "BaseDB", - "Checker", - "constants", - "errors", - "FileObject", - "HandshakeSettings", - "mathtls", - "messages", - "Session", - "SessionCache", - "SharedKeyDB", - "TLSConnection", - "TLSRecordLayer", - "VerifierDB", - "X509", - "X509CertChain", - "integration", - "utils"] diff --git a/gdata/analytics/tlslite/api.py b/gdata/analytics/tlslite/api.py deleted file mode 100644 index eebfbc6091..0000000000 --- a/gdata/analytics/tlslite/api.py +++ /dev/null @@ -1,75 +0,0 @@ -"""Import this module for easy access to TLS Lite objects. - -The TLS Lite API consists of classes, functions, and variables spread -throughout this package. Instead of importing them individually with:: - - from tlslite.TLSConnection import TLSConnection - from tlslite.HandshakeSettings import HandshakeSettings - from tlslite.errors import * - . - . - -It's easier to do:: - - from tlslite.api import * - -This imports all the important objects (TLSConnection, Checker, -HandshakeSettings, etc.) into the global namespace. In particular, it -imports:: - - from constants import AlertLevel, AlertDescription, Fault - from errors import * - from Checker import Checker - from HandshakeSettings import HandshakeSettings - from Session import Session - from SessionCache import SessionCache - from SharedKeyDB import SharedKeyDB - from TLSConnection import TLSConnection - from VerifierDB import VerifierDB - from X509 import X509 - from X509CertChain import X509CertChain - - from integration.HTTPTLSConnection import HTTPTLSConnection - from integration.POP3_TLS import POP3_TLS - from integration.IMAP4_TLS import IMAP4_TLS - from integration.SMTP_TLS import SMTP_TLS - from integration.XMLRPCTransport import XMLRPCTransport - from integration.TLSSocketServerMixIn import TLSSocketServerMixIn - from integration.TLSAsyncDispatcherMixIn import TLSAsyncDispatcherMixIn - from integration.TLSTwistedProtocolWrapper import TLSTwistedProtocolWrapper - from utils.cryptomath import cryptlibpyLoaded, m2cryptoLoaded, - gmpyLoaded, pycryptoLoaded, prngName - from utils.keyfactory import generateRSAKey, parsePEMKey, parseXMLKey, - parseAsPublicKey, parsePrivateKey -""" - -from constants import AlertLevel, AlertDescription, Fault -from errors import * -from Checker import Checker -from HandshakeSettings import HandshakeSettings -from Session import Session -from SessionCache import SessionCache -from SharedKeyDB import SharedKeyDB -from TLSConnection import TLSConnection -from VerifierDB import VerifierDB -from X509 import X509 -from X509CertChain import X509CertChain - -from integration.HTTPTLSConnection import HTTPTLSConnection -from integration.TLSSocketServerMixIn import TLSSocketServerMixIn -from integration.TLSAsyncDispatcherMixIn import TLSAsyncDispatcherMixIn -from integration.POP3_TLS import POP3_TLS -from integration.IMAP4_TLS import IMAP4_TLS -from integration.SMTP_TLS import SMTP_TLS -from integration.XMLRPCTransport import XMLRPCTransport -try: - import twisted - del(twisted) - from integration.TLSTwistedProtocolWrapper import TLSTwistedProtocolWrapper -except ImportError: - pass - -from utils.cryptomath import cryptlibpyLoaded, m2cryptoLoaded, gmpyLoaded, \ - pycryptoLoaded, prngName -from utils.keyfactory import generateRSAKey, parsePEMKey, parseXMLKey, \ - parseAsPublicKey, parsePrivateKey diff --git a/gdata/analytics/tlslite/constants.py b/gdata/analytics/tlslite/constants.py deleted file mode 100644 index 8f2d5590e9..0000000000 --- a/gdata/analytics/tlslite/constants.py +++ /dev/null @@ -1,225 +0,0 @@ -"""Constants used in various places.""" - -class CertificateType: - x509 = 0 - openpgp = 1 - cryptoID = 2 - -class HandshakeType: - hello_request = 0 - client_hello = 1 - server_hello = 2 - certificate = 11 - server_key_exchange = 12 - certificate_request = 13 - server_hello_done = 14 - certificate_verify = 15 - client_key_exchange = 16 - finished = 20 - -class ContentType: - change_cipher_spec = 20 - alert = 21 - handshake = 22 - application_data = 23 - all = (20,21,22,23) - -class AlertLevel: - warning = 1 - fatal = 2 - -class AlertDescription: - """ - @cvar bad_record_mac: A TLS record failed to decrypt properly. - - If this occurs during a shared-key or SRP handshake it most likely - indicates a bad password. It may also indicate an implementation - error, or some tampering with the data in transit. - - This alert will be signalled by the server if the SRP password is bad. It - may also be signalled by the server if the SRP username is unknown to the - server, but it doesn't wish to reveal that fact. - - This alert will be signalled by the client if the shared-key username is - bad. - - @cvar handshake_failure: A problem occurred while handshaking. - - This typically indicates a lack of common ciphersuites between client and - server, or some other disagreement (about SRP parameters or key sizes, - for example). - - @cvar protocol_version: The other party's SSL/TLS version was unacceptable. - - This indicates that the client and server couldn't agree on which version - of SSL or TLS to use. - - @cvar user_canceled: The handshake is being cancelled for some reason. - - """ - - close_notify = 0 - unexpected_message = 10 - bad_record_mac = 20 - decryption_failed = 21 - record_overflow = 22 - decompression_failure = 30 - handshake_failure = 40 - no_certificate = 41 #SSLv3 - bad_certificate = 42 - unsupported_certificate = 43 - certificate_revoked = 44 - certificate_expired = 45 - certificate_unknown = 46 - illegal_parameter = 47 - unknown_ca = 48 - access_denied = 49 - decode_error = 50 - decrypt_error = 51 - export_restriction = 60 - protocol_version = 70 - insufficient_security = 71 - internal_error = 80 - user_canceled = 90 - no_renegotiation = 100 - unknown_srp_username = 120 - missing_srp_username = 121 - untrusted_srp_parameters = 122 - -class CipherSuite: - TLS_SRP_SHA_WITH_3DES_EDE_CBC_SHA = 0x0050 - TLS_SRP_SHA_WITH_AES_128_CBC_SHA = 0x0053 - TLS_SRP_SHA_WITH_AES_256_CBC_SHA = 0x0056 - - TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA = 0x0051 - TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA = 0x0054 - TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA = 0x0057 - - TLS_RSA_WITH_3DES_EDE_CBC_SHA = 0x000A - TLS_RSA_WITH_AES_128_CBC_SHA = 0x002F - TLS_RSA_WITH_AES_256_CBC_SHA = 0x0035 - TLS_RSA_WITH_RC4_128_SHA = 0x0005 - - srpSuites = [] - srpSuites.append(TLS_SRP_SHA_WITH_3DES_EDE_CBC_SHA) - srpSuites.append(TLS_SRP_SHA_WITH_AES_128_CBC_SHA) - srpSuites.append(TLS_SRP_SHA_WITH_AES_256_CBC_SHA) - def getSrpSuites(ciphers): - suites = [] - for cipher in ciphers: - if cipher == "aes128": - suites.append(CipherSuite.TLS_SRP_SHA_WITH_AES_128_CBC_SHA) - elif cipher == "aes256": - suites.append(CipherSuite.TLS_SRP_SHA_WITH_AES_256_CBC_SHA) - elif cipher == "3des": - suites.append(CipherSuite.TLS_SRP_SHA_WITH_3DES_EDE_CBC_SHA) - return suites - getSrpSuites = staticmethod(getSrpSuites) - - srpRsaSuites = [] - srpRsaSuites.append(TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA) - srpRsaSuites.append(TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA) - srpRsaSuites.append(TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA) - def getSrpRsaSuites(ciphers): - suites = [] - for cipher in ciphers: - if cipher == "aes128": - suites.append(CipherSuite.TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA) - elif cipher == "aes256": - suites.append(CipherSuite.TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA) - elif cipher == "3des": - suites.append(CipherSuite.TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA) - return suites - getSrpRsaSuites = staticmethod(getSrpRsaSuites) - - rsaSuites = [] - rsaSuites.append(TLS_RSA_WITH_3DES_EDE_CBC_SHA) - rsaSuites.append(TLS_RSA_WITH_AES_128_CBC_SHA) - rsaSuites.append(TLS_RSA_WITH_AES_256_CBC_SHA) - rsaSuites.append(TLS_RSA_WITH_RC4_128_SHA) - def getRsaSuites(ciphers): - suites = [] - for cipher in ciphers: - if cipher == "aes128": - suites.append(CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA) - elif cipher == "aes256": - suites.append(CipherSuite.TLS_RSA_WITH_AES_256_CBC_SHA) - elif cipher == "rc4": - suites.append(CipherSuite.TLS_RSA_WITH_RC4_128_SHA) - elif cipher == "3des": - suites.append(CipherSuite.TLS_RSA_WITH_3DES_EDE_CBC_SHA) - return suites - getRsaSuites = staticmethod(getRsaSuites) - - tripleDESSuites = [] - tripleDESSuites.append(TLS_SRP_SHA_WITH_3DES_EDE_CBC_SHA) - tripleDESSuites.append(TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA) - tripleDESSuites.append(TLS_RSA_WITH_3DES_EDE_CBC_SHA) - - aes128Suites = [] - aes128Suites.append(TLS_SRP_SHA_WITH_AES_128_CBC_SHA) - aes128Suites.append(TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA) - aes128Suites.append(TLS_RSA_WITH_AES_128_CBC_SHA) - - aes256Suites = [] - aes256Suites.append(TLS_SRP_SHA_WITH_AES_256_CBC_SHA) - aes256Suites.append(TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA) - aes256Suites.append(TLS_RSA_WITH_AES_256_CBC_SHA) - - rc4Suites = [] - rc4Suites.append(TLS_RSA_WITH_RC4_128_SHA) - - -class Fault: - badUsername = 101 - badPassword = 102 - badA = 103 - clientSrpFaults = range(101,104) - - badVerifyMessage = 601 - clientCertFaults = range(601,602) - - badPremasterPadding = 501 - shortPremasterSecret = 502 - clientNoAuthFaults = range(501,503) - - badIdentifier = 401 - badSharedKey = 402 - clientSharedKeyFaults = range(401,403) - - badB = 201 - serverFaults = range(201,202) - - badFinished = 300 - badMAC = 301 - badPadding = 302 - genericFaults = range(300,303) - - faultAlerts = {\ - badUsername: (AlertDescription.unknown_srp_username, \ - AlertDescription.bad_record_mac),\ - badPassword: (AlertDescription.bad_record_mac,),\ - badA: (AlertDescription.illegal_parameter,),\ - badIdentifier: (AlertDescription.handshake_failure,),\ - badSharedKey: (AlertDescription.bad_record_mac,),\ - badPremasterPadding: (AlertDescription.bad_record_mac,),\ - shortPremasterSecret: (AlertDescription.bad_record_mac,),\ - badVerifyMessage: (AlertDescription.decrypt_error,),\ - badFinished: (AlertDescription.decrypt_error,),\ - badMAC: (AlertDescription.bad_record_mac,),\ - badPadding: (AlertDescription.bad_record_mac,) - } - - faultNames = {\ - badUsername: "bad username",\ - badPassword: "bad password",\ - badA: "bad A",\ - badIdentifier: "bad identifier",\ - badSharedKey: "bad sharedkey",\ - badPremasterPadding: "bad premaster padding",\ - shortPremasterSecret: "short premaster secret",\ - badVerifyMessage: "bad verify message",\ - badFinished: "bad finished message",\ - badMAC: "bad MAC",\ - badPadding: "bad padding" - } diff --git a/gdata/analytics/tlslite/errors.py b/gdata/analytics/tlslite/errors.py deleted file mode 100644 index c7f7ba81d4..0000000000 --- a/gdata/analytics/tlslite/errors.py +++ /dev/null @@ -1,149 +0,0 @@ -"""Exception classes. -@sort: TLSError, TLSAbruptCloseError, TLSAlert, TLSLocalAlert, TLSRemoteAlert, -TLSAuthenticationError, TLSNoAuthenticationError, TLSAuthenticationTypeError, -TLSFingerprintError, TLSAuthorizationError, TLSValidationError, TLSFaultError -""" - -from constants import AlertDescription, AlertLevel - -class TLSError(Exception): - """Base class for all TLS Lite exceptions.""" - pass - -class TLSAbruptCloseError(TLSError): - """The socket was closed without a proper TLS shutdown. - - The TLS specification mandates that an alert of some sort - must be sent before the underlying socket is closed. If the socket - is closed without this, it could signify that an attacker is trying - to truncate the connection. It could also signify a misbehaving - TLS implementation, or a random network failure. - """ - pass - -class TLSAlert(TLSError): - """A TLS alert has been signalled.""" - pass - - _descriptionStr = {\ - AlertDescription.close_notify: "close_notify",\ - AlertDescription.unexpected_message: "unexpected_message",\ - AlertDescription.bad_record_mac: "bad_record_mac",\ - AlertDescription.decryption_failed: "decryption_failed",\ - AlertDescription.record_overflow: "record_overflow",\ - AlertDescription.decompression_failure: "decompression_failure",\ - AlertDescription.handshake_failure: "handshake_failure",\ - AlertDescription.no_certificate: "no certificate",\ - AlertDescription.bad_certificate: "bad_certificate",\ - AlertDescription.unsupported_certificate: "unsupported_certificate",\ - AlertDescription.certificate_revoked: "certificate_revoked",\ - AlertDescription.certificate_expired: "certificate_expired",\ - AlertDescription.certificate_unknown: "certificate_unknown",\ - AlertDescription.illegal_parameter: "illegal_parameter",\ - AlertDescription.unknown_ca: "unknown_ca",\ - AlertDescription.access_denied: "access_denied",\ - AlertDescription.decode_error: "decode_error",\ - AlertDescription.decrypt_error: "decrypt_error",\ - AlertDescription.export_restriction: "export_restriction",\ - AlertDescription.protocol_version: "protocol_version",\ - AlertDescription.insufficient_security: "insufficient_security",\ - AlertDescription.internal_error: "internal_error",\ - AlertDescription.user_canceled: "user_canceled",\ - AlertDescription.no_renegotiation: "no_renegotiation",\ - AlertDescription.unknown_srp_username: "unknown_srp_username",\ - AlertDescription.missing_srp_username: "missing_srp_username"} - -class TLSLocalAlert(TLSAlert): - """A TLS alert has been signalled by the local implementation. - - @type description: int - @ivar description: Set to one of the constants in - L{tlslite.constants.AlertDescription} - - @type level: int - @ivar level: Set to one of the constants in - L{tlslite.constants.AlertLevel} - - @type message: str - @ivar message: Description of what went wrong. - """ - def __init__(self, alert, message=None): - self.description = alert.description - self.level = alert.level - self.message = message - - def __str__(self): - alertStr = TLSAlert._descriptionStr.get(self.description) - if alertStr == None: - alertStr = str(self.description) - if self.message: - return alertStr + ": " + self.message - else: - return alertStr - -class TLSRemoteAlert(TLSAlert): - """A TLS alert has been signalled by the remote implementation. - - @type description: int - @ivar description: Set to one of the constants in - L{tlslite.constants.AlertDescription} - - @type level: int - @ivar level: Set to one of the constants in - L{tlslite.constants.AlertLevel} - """ - def __init__(self, alert): - self.description = alert.description - self.level = alert.level - - def __str__(self): - alertStr = TLSAlert._descriptionStr.get(self.description) - if alertStr == None: - alertStr = str(self.description) - return alertStr - -class TLSAuthenticationError(TLSError): - """The handshake succeeded, but the other party's authentication - was inadequate. - - This exception will only be raised when a - L{tlslite.Checker.Checker} has been passed to a handshake function. - The Checker will be invoked once the handshake completes, and if - the Checker objects to how the other party authenticated, a - subclass of this exception will be raised. - """ - pass - -class TLSNoAuthenticationError(TLSAuthenticationError): - """The Checker was expecting the other party to authenticate with a - certificate chain, but this did not occur.""" - pass - -class TLSAuthenticationTypeError(TLSAuthenticationError): - """The Checker was expecting the other party to authenticate with a - different type of certificate chain.""" - pass - -class TLSFingerprintError(TLSAuthenticationError): - """The Checker was expecting the other party to authenticate with a - certificate chain that matches a different fingerprint.""" - pass - -class TLSAuthorizationError(TLSAuthenticationError): - """The Checker was expecting the other party to authenticate with a - certificate chain that has a different authorization.""" - pass - -class TLSValidationError(TLSAuthenticationError): - """The Checker has determined that the other party's certificate - chain is invalid.""" - pass - -class TLSFaultError(TLSError): - """The other party responded incorrectly to an induced fault. - - This exception will only occur during fault testing, when a - TLSConnection's fault variable is set to induce some sort of - faulty behavior, and the other party doesn't respond appropriately. - """ - pass diff --git a/gdata/analytics/tlslite/integration/AsyncStateMachine.py b/gdata/analytics/tlslite/integration/AsyncStateMachine.py deleted file mode 100644 index abed604321..0000000000 --- a/gdata/analytics/tlslite/integration/AsyncStateMachine.py +++ /dev/null @@ -1,235 +0,0 @@ -""" -A state machine for using TLS Lite with asynchronous I/O. -""" - -class AsyncStateMachine: - """ - This is an abstract class that's used to integrate TLS Lite with - asyncore and Twisted. - - This class signals wantsReadsEvent() and wantsWriteEvent(). When - the underlying socket has become readable or writeable, the event - should be passed to this class by calling inReadEvent() or - inWriteEvent(). This class will then try to read or write through - the socket, and will update its state appropriately. - - This class will forward higher-level events to its subclass. For - example, when a complete TLS record has been received, - outReadEvent() will be called with the decrypted data. - """ - - def __init__(self): - self._clear() - - def _clear(self): - #These store the various asynchronous operations (i.e. - #generators). Only one of them, at most, is ever active at a - #time. - self.handshaker = None - self.closer = None - self.reader = None - self.writer = None - - #This stores the result from the last call to the - #currently active operation. If 0 it indicates that the - #operation wants to read, if 1 it indicates that the - #operation wants to write. If None, there is no active - #operation. - self.result = None - - def _checkAssert(self, maxActive=1): - #This checks that only one operation, at most, is - #active, and that self.result is set appropriately. - activeOps = 0 - if self.handshaker: - activeOps += 1 - if self.closer: - activeOps += 1 - if self.reader: - activeOps += 1 - if self.writer: - activeOps += 1 - - if self.result == None: - if activeOps != 0: - raise AssertionError() - elif self.result in (0,1): - if activeOps != 1: - raise AssertionError() - else: - raise AssertionError() - if activeOps > maxActive: - raise AssertionError() - - def wantsReadEvent(self): - """If the state machine wants to read. - - If an operation is active, this returns whether or not the - operation wants to read from the socket. If an operation is - not active, this returns None. - - @rtype: bool or None - @return: If the state machine wants to read. - """ - if self.result != None: - return self.result == 0 - return None - - def wantsWriteEvent(self): - """If the state machine wants to write. - - If an operation is active, this returns whether or not the - operation wants to write to the socket. If an operation is - not active, this returns None. - - @rtype: bool or None - @return: If the state machine wants to write. - """ - if self.result != None: - return self.result == 1 - return None - - def outConnectEvent(self): - """Called when a handshake operation completes. - - May be overridden in subclass. - """ - pass - - def outCloseEvent(self): - """Called when a close operation completes. - - May be overridden in subclass. - """ - pass - - def outReadEvent(self, readBuffer): - """Called when a read operation completes. - - May be overridden in subclass.""" - pass - - def outWriteEvent(self): - """Called when a write operation completes. - - May be overridden in subclass.""" - pass - - def inReadEvent(self): - """Tell the state machine it can read from the socket.""" - try: - self._checkAssert() - if self.handshaker: - self._doHandshakeOp() - elif self.closer: - self._doCloseOp() - elif self.reader: - self._doReadOp() - elif self.writer: - self._doWriteOp() - else: - self.reader = self.tlsConnection.readAsync(16384) - self._doReadOp() - except: - self._clear() - raise - - def inWriteEvent(self): - """Tell the state machine it can write to the socket.""" - try: - self._checkAssert() - if self.handshaker: - self._doHandshakeOp() - elif self.closer: - self._doCloseOp() - elif self.reader: - self._doReadOp() - elif self.writer: - self._doWriteOp() - else: - self.outWriteEvent() - except: - self._clear() - raise - - def _doHandshakeOp(self): - try: - self.result = self.handshaker.next() - except StopIteration: - self.handshaker = None - self.result = None - self.outConnectEvent() - - def _doCloseOp(self): - try: - self.result = self.closer.next() - except StopIteration: - self.closer = None - self.result = None - self.outCloseEvent() - - def _doReadOp(self): - self.result = self.reader.next() - if not self.result in (0,1): - readBuffer = self.result - self.reader = None - self.result = None - self.outReadEvent(readBuffer) - - def _doWriteOp(self): - try: - self.result = self.writer.next() - except StopIteration: - self.writer = None - self.result = None - - def setHandshakeOp(self, handshaker): - """Start a handshake operation. - - @type handshaker: generator - @param handshaker: A generator created by using one of the - asynchronous handshake functions (i.e. handshakeServerAsync, or - handshakeClientxxx(..., async=True). - """ - try: - self._checkAssert(0) - self.handshaker = handshaker - self._doHandshakeOp() - except: - self._clear() - raise - - def setServerHandshakeOp(self, **args): - """Start a handshake operation. - - The arguments passed to this function will be forwarded to - L{tlslite.TLSConnection.TLSConnection.handshakeServerAsync}. - """ - handshaker = self.tlsConnection.handshakeServerAsync(**args) - self.setHandshakeOp(handshaker) - - def setCloseOp(self): - """Start a close operation. - """ - try: - self._checkAssert(0) - self.closer = self.tlsConnection.closeAsync() - self._doCloseOp() - except: - self._clear() - raise - - def setWriteOp(self, writeBuffer): - """Start a write operation. - - @type writeBuffer: str - @param writeBuffer: The string to transmit. - """ - try: - self._checkAssert(0) - self.writer = self.tlsConnection.writeAsync(writeBuffer) - self._doWriteOp() - except: - self._clear() - raise - diff --git a/gdata/analytics/tlslite/integration/ClientHelper.py b/gdata/analytics/tlslite/integration/ClientHelper.py deleted file mode 100644 index 58e0152f9f..0000000000 --- a/gdata/analytics/tlslite/integration/ClientHelper.py +++ /dev/null @@ -1,163 +0,0 @@ -""" -A helper class for using TLS Lite with stdlib clients -(httplib, xmlrpclib, imaplib, poplib). -""" - -from gdata.tlslite.Checker import Checker - -class ClientHelper: - """This is a helper class used to integrate TLS Lite with various - TLS clients (e.g. poplib, smtplib, httplib, etc.)""" - - def __init__(self, - username=None, password=None, sharedKey=None, - certChain=None, privateKey=None, - cryptoID=None, protocol=None, - x509Fingerprint=None, - x509TrustList=None, x509CommonName=None, - settings = None): - """ - For client authentication, use one of these argument - combinations: - - username, password (SRP) - - username, sharedKey (shared-key) - - certChain, privateKey (certificate) - - For server authentication, you can either rely on the - implicit mutual authentication performed by SRP or - shared-keys, or you can do certificate-based server - authentication with one of these argument combinations: - - cryptoID[, protocol] (requires cryptoIDlib) - - x509Fingerprint - - x509TrustList[, x509CommonName] (requires cryptlib_py) - - Certificate-based server authentication is compatible with - SRP or certificate-based client authentication. It is - not compatible with shared-keys. - - The constructor does not perform the TLS handshake itself, but - simply stores these arguments for later. The handshake is - performed only when this class needs to connect with the - server. Then you should be prepared to handle TLS-specific - exceptions. See the client handshake functions in - L{tlslite.TLSConnection.TLSConnection} for details on which - exceptions might be raised. - - @type username: str - @param username: SRP or shared-key username. Requires the - 'password' or 'sharedKey' argument. - - @type password: str - @param password: SRP password for mutual authentication. - Requires the 'username' argument. - - @type sharedKey: str - @param sharedKey: Shared key for mutual authentication. - Requires the 'username' argument. - - @type certChain: L{tlslite.X509CertChain.X509CertChain} or - L{cryptoIDlib.CertChain.CertChain} - @param certChain: Certificate chain for client authentication. - Requires the 'privateKey' argument. Excludes the SRP or - shared-key related arguments. - - @type privateKey: L{tlslite.utils.RSAKey.RSAKey} - @param privateKey: Private key for client authentication. - Requires the 'certChain' argument. Excludes the SRP or - shared-key related arguments. - - @type cryptoID: str - @param cryptoID: cryptoID for server authentication. Mutually - exclusive with the 'x509...' arguments. - - @type protocol: str - @param protocol: cryptoID protocol URI for server - authentication. Requires the 'cryptoID' argument. - - @type x509Fingerprint: str - @param x509Fingerprint: Hex-encoded X.509 fingerprint for - server authentication. Mutually exclusive with the 'cryptoID' - and 'x509TrustList' arguments. - - @type x509TrustList: list of L{tlslite.X509.X509} - @param x509TrustList: A list of trusted root certificates. The - other party must present a certificate chain which extends to - one of these root certificates. The cryptlib_py module must be - installed to use this parameter. Mutually exclusive with the - 'cryptoID' and 'x509Fingerprint' arguments. - - @type x509CommonName: str - @param x509CommonName: The end-entity certificate's 'CN' field - must match this value. For a web server, this is typically a - server name such as 'www.amazon.com'. Mutually exclusive with - the 'cryptoID' and 'x509Fingerprint' arguments. Requires the - 'x509TrustList' argument. - - @type settings: L{tlslite.HandshakeSettings.HandshakeSettings} - @param settings: Various settings which can be used to control - the ciphersuites, certificate types, and SSL/TLS versions - offered by the client. - """ - - self.username = None - self.password = None - self.sharedKey = None - self.certChain = None - self.privateKey = None - self.checker = None - - #SRP Authentication - if username and password and not \ - (sharedKey or certChain or privateKey): - self.username = username - self.password = password - - #Shared Key Authentication - elif username and sharedKey and not \ - (password or certChain or privateKey): - self.username = username - self.sharedKey = sharedKey - - #Certificate Chain Authentication - elif certChain and privateKey and not \ - (username or password or sharedKey): - self.certChain = certChain - self.privateKey = privateKey - - #No Authentication - elif not password and not username and not \ - sharedKey and not certChain and not privateKey: - pass - - else: - raise ValueError("Bad parameters") - - #Authenticate the server based on its cryptoID or fingerprint - if sharedKey and (cryptoID or protocol or x509Fingerprint): - raise ValueError("Can't use shared keys with other forms of"\ - "authentication") - - self.checker = Checker(cryptoID, protocol, x509Fingerprint, - x509TrustList, x509CommonName) - self.settings = settings - - self.tlsSession = None - - def _handshake(self, tlsConnection): - if self.username and self.password: - tlsConnection.handshakeClientSRP(username=self.username, - password=self.password, - checker=self.checker, - settings=self.settings, - session=self.tlsSession) - elif self.username and self.sharedKey: - tlsConnection.handshakeClientSharedKey(username=self.username, - sharedKey=self.sharedKey, - settings=self.settings) - else: - tlsConnection.handshakeClientCert(certChain=self.certChain, - privateKey=self.privateKey, - checker=self.checker, - settings=self.settings, - session=self.tlsSession) - self.tlsSession = tlsConnection.session diff --git a/gdata/analytics/tlslite/integration/HTTPTLSConnection.py b/gdata/analytics/tlslite/integration/HTTPTLSConnection.py deleted file mode 100644 index 58e31a1080..0000000000 --- a/gdata/analytics/tlslite/integration/HTTPTLSConnection.py +++ /dev/null @@ -1,169 +0,0 @@ -"""TLS Lite + httplib.""" - -import socket -import httplib -from gdata.tlslite.TLSConnection import TLSConnection -from gdata.tlslite.integration.ClientHelper import ClientHelper - - -class HTTPBaseTLSConnection(httplib.HTTPConnection): - """This abstract class provides a framework for adding TLS support - to httplib.""" - - default_port = 443 - - def __init__(self, host, port=None, strict=None): - if strict == None: - #Python 2.2 doesn't support strict - httplib.HTTPConnection.__init__(self, host, port) - else: - httplib.HTTPConnection.__init__(self, host, port, strict) - - def connect(self): - sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - if hasattr(sock, 'settimeout'): - sock.settimeout(10) - sock.connect((self.host, self.port)) - - #Use a TLSConnection to emulate a socket - self.sock = TLSConnection(sock) - - #When httplib closes this, close the socket - self.sock.closeSocket = True - self._handshake(self.sock) - - def _handshake(self, tlsConnection): - """Called to perform some sort of handshake. - - This method must be overridden in a subclass to do some type of - handshake. This method will be called after the socket has - been connected but before any data has been sent. If this - method does not raise an exception, the TLS connection will be - considered valid. - - This method may (or may not) be called every time an HTTP - request is performed, depending on whether the underlying HTTP - connection is persistent. - - @type tlsConnection: L{tlslite.TLSConnection.TLSConnection} - @param tlsConnection: The connection to perform the handshake - on. - """ - raise NotImplementedError() - - -class HTTPTLSConnection(HTTPBaseTLSConnection, ClientHelper): - """This class extends L{HTTPBaseTLSConnection} to support the - common types of handshaking.""" - - def __init__(self, host, port=None, - username=None, password=None, sharedKey=None, - certChain=None, privateKey=None, - cryptoID=None, protocol=None, - x509Fingerprint=None, - x509TrustList=None, x509CommonName=None, - settings = None): - """Create a new HTTPTLSConnection. - - For client authentication, use one of these argument - combinations: - - username, password (SRP) - - username, sharedKey (shared-key) - - certChain, privateKey (certificate) - - For server authentication, you can either rely on the - implicit mutual authentication performed by SRP or - shared-keys, or you can do certificate-based server - authentication with one of these argument combinations: - - cryptoID[, protocol] (requires cryptoIDlib) - - x509Fingerprint - - x509TrustList[, x509CommonName] (requires cryptlib_py) - - Certificate-based server authentication is compatible with - SRP or certificate-based client authentication. It is - not compatible with shared-keys. - - The constructor does not perform the TLS handshake itself, but - simply stores these arguments for later. The handshake is - performed only when this class needs to connect with the - server. Thus you should be prepared to handle TLS-specific - exceptions when calling methods inherited from - L{httplib.HTTPConnection} such as request(), connect(), and - send(). See the client handshake functions in - L{tlslite.TLSConnection.TLSConnection} for details on which - exceptions might be raised. - - @type host: str - @param host: Server to connect to. - - @type port: int - @param port: Port to connect to. - - @type username: str - @param username: SRP or shared-key username. Requires the - 'password' or 'sharedKey' argument. - - @type password: str - @param password: SRP password for mutual authentication. - Requires the 'username' argument. - - @type sharedKey: str - @param sharedKey: Shared key for mutual authentication. - Requires the 'username' argument. - - @type certChain: L{tlslite.X509CertChain.X509CertChain} or - L{cryptoIDlib.CertChain.CertChain} - @param certChain: Certificate chain for client authentication. - Requires the 'privateKey' argument. Excludes the SRP or - shared-key related arguments. - - @type privateKey: L{tlslite.utils.RSAKey.RSAKey} - @param privateKey: Private key for client authentication. - Requires the 'certChain' argument. Excludes the SRP or - shared-key related arguments. - - @type cryptoID: str - @param cryptoID: cryptoID for server authentication. Mutually - exclusive with the 'x509...' arguments. - - @type protocol: str - @param protocol: cryptoID protocol URI for server - authentication. Requires the 'cryptoID' argument. - - @type x509Fingerprint: str - @param x509Fingerprint: Hex-encoded X.509 fingerprint for - server authentication. Mutually exclusive with the 'cryptoID' - and 'x509TrustList' arguments. - - @type x509TrustList: list of L{tlslite.X509.X509} - @param x509TrustList: A list of trusted root certificates. The - other party must present a certificate chain which extends to - one of these root certificates. The cryptlib_py module must be - installed to use this parameter. Mutually exclusive with the - 'cryptoID' and 'x509Fingerprint' arguments. - - @type x509CommonName: str - @param x509CommonName: The end-entity certificate's 'CN' field - must match this value. For a web server, this is typically a - server name such as 'www.amazon.com'. Mutually exclusive with - the 'cryptoID' and 'x509Fingerprint' arguments. Requires the - 'x509TrustList' argument. - - @type settings: L{tlslite.HandshakeSettings.HandshakeSettings} - @param settings: Various settings which can be used to control - the ciphersuites, certificate types, and SSL/TLS versions - offered by the client. - """ - - HTTPBaseTLSConnection.__init__(self, host, port) - - ClientHelper.__init__(self, - username, password, sharedKey, - certChain, privateKey, - cryptoID, protocol, - x509Fingerprint, - x509TrustList, x509CommonName, - settings) - - def _handshake(self, tlsConnection): - ClientHelper._handshake(self, tlsConnection) diff --git a/gdata/analytics/tlslite/integration/IMAP4_TLS.py b/gdata/analytics/tlslite/integration/IMAP4_TLS.py deleted file mode 100644 index e47076ccc8..0000000000 --- a/gdata/analytics/tlslite/integration/IMAP4_TLS.py +++ /dev/null @@ -1,132 +0,0 @@ -"""TLS Lite + imaplib.""" - -import socket -from imaplib import IMAP4 -from gdata.tlslite.TLSConnection import TLSConnection -from gdata.tlslite.integration.ClientHelper import ClientHelper - -# IMAP TLS PORT -IMAP4_TLS_PORT = 993 - -class IMAP4_TLS(IMAP4, ClientHelper): - """This class extends L{imaplib.IMAP4} with TLS support.""" - - def __init__(self, host = '', port = IMAP4_TLS_PORT, - username=None, password=None, sharedKey=None, - certChain=None, privateKey=None, - cryptoID=None, protocol=None, - x509Fingerprint=None, - x509TrustList=None, x509CommonName=None, - settings=None): - """Create a new IMAP4_TLS. - - For client authentication, use one of these argument - combinations: - - username, password (SRP) - - username, sharedKey (shared-key) - - certChain, privateKey (certificate) - - For server authentication, you can either rely on the - implicit mutual authentication performed by SRP or - shared-keys, or you can do certificate-based server - authentication with one of these argument combinations: - - cryptoID[, protocol] (requires cryptoIDlib) - - x509Fingerprint - - x509TrustList[, x509CommonName] (requires cryptlib_py) - - Certificate-based server authentication is compatible with - SRP or certificate-based client authentication. It is - not compatible with shared-keys. - - The caller should be prepared to handle TLS-specific - exceptions. See the client handshake functions in - L{tlslite.TLSConnection.TLSConnection} for details on which - exceptions might be raised. - - @type host: str - @param host: Server to connect to. - - @type port: int - @param port: Port to connect to. - - @type username: str - @param username: SRP or shared-key username. Requires the - 'password' or 'sharedKey' argument. - - @type password: str - @param password: SRP password for mutual authentication. - Requires the 'username' argument. - - @type sharedKey: str - @param sharedKey: Shared key for mutual authentication. - Requires the 'username' argument. - - @type certChain: L{tlslite.X509CertChain.X509CertChain} or - L{cryptoIDlib.CertChain.CertChain} - @param certChain: Certificate chain for client authentication. - Requires the 'privateKey' argument. Excludes the SRP or - shared-key related arguments. - - @type privateKey: L{tlslite.utils.RSAKey.RSAKey} - @param privateKey: Private key for client authentication. - Requires the 'certChain' argument. Excludes the SRP or - shared-key related arguments. - - @type cryptoID: str - @param cryptoID: cryptoID for server authentication. Mutually - exclusive with the 'x509...' arguments. - - @type protocol: str - @param protocol: cryptoID protocol URI for server - authentication. Requires the 'cryptoID' argument. - - @type x509Fingerprint: str - @param x509Fingerprint: Hex-encoded X.509 fingerprint for - server authentication. Mutually exclusive with the 'cryptoID' - and 'x509TrustList' arguments. - - @type x509TrustList: list of L{tlslite.X509.X509} - @param x509TrustList: A list of trusted root certificates. The - other party must present a certificate chain which extends to - one of these root certificates. The cryptlib_py module must be - installed to use this parameter. Mutually exclusive with the - 'cryptoID' and 'x509Fingerprint' arguments. - - @type x509CommonName: str - @param x509CommonName: The end-entity certificate's 'CN' field - must match this value. For a web server, this is typically a - server name such as 'www.amazon.com'. Mutually exclusive with - the 'cryptoID' and 'x509Fingerprint' arguments. Requires the - 'x509TrustList' argument. - - @type settings: L{tlslite.HandshakeSettings.HandshakeSettings} - @param settings: Various settings which can be used to control - the ciphersuites, certificate types, and SSL/TLS versions - offered by the client. - """ - - ClientHelper.__init__(self, - username, password, sharedKey, - certChain, privateKey, - cryptoID, protocol, - x509Fingerprint, - x509TrustList, x509CommonName, - settings) - - IMAP4.__init__(self, host, port) - - - def open(self, host = '', port = IMAP4_TLS_PORT): - """Setup connection to remote server on "host:port". - - This connection will be used by the routines: - read, readline, send, shutdown. - """ - self.host = host - self.port = port - self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - self.sock.connect((host, port)) - self.sock = TLSConnection(self.sock) - self.sock.closeSocket = True - ClientHelper._handshake(self, self.sock) - self.file = self.sock.makefile('rb') diff --git a/gdata/analytics/tlslite/integration/IntegrationHelper.py b/gdata/analytics/tlslite/integration/IntegrationHelper.py deleted file mode 100644 index af5193b480..0000000000 --- a/gdata/analytics/tlslite/integration/IntegrationHelper.py +++ /dev/null @@ -1,52 +0,0 @@ - -class IntegrationHelper: - - def __init__(self, - username=None, password=None, sharedKey=None, - certChain=None, privateKey=None, - cryptoID=None, protocol=None, - x509Fingerprint=None, - x509TrustList=None, x509CommonName=None, - settings = None): - - self.username = None - self.password = None - self.sharedKey = None - self.certChain = None - self.privateKey = None - self.checker = None - - #SRP Authentication - if username and password and not \ - (sharedKey or certChain or privateKey): - self.username = username - self.password = password - - #Shared Key Authentication - elif username and sharedKey and not \ - (password or certChain or privateKey): - self.username = username - self.sharedKey = sharedKey - - #Certificate Chain Authentication - elif certChain and privateKey and not \ - (username or password or sharedKey): - self.certChain = certChain - self.privateKey = privateKey - - #No Authentication - elif not password and not username and not \ - sharedKey and not certChain and not privateKey: - pass - - else: - raise ValueError("Bad parameters") - - #Authenticate the server based on its cryptoID or fingerprint - if sharedKey and (cryptoID or protocol or x509Fingerprint): - raise ValueError("Can't use shared keys with other forms of"\ - "authentication") - - self.checker = Checker(cryptoID, protocol, x509Fingerprint, - x509TrustList, x509CommonName) - self.settings = settings \ No newline at end of file diff --git a/gdata/analytics/tlslite/integration/POP3_TLS.py b/gdata/analytics/tlslite/integration/POP3_TLS.py deleted file mode 100644 index 26b37fdd84..0000000000 --- a/gdata/analytics/tlslite/integration/POP3_TLS.py +++ /dev/null @@ -1,142 +0,0 @@ -"""TLS Lite + poplib.""" - -import socket -from poplib import POP3 -from gdata.tlslite.TLSConnection import TLSConnection -from gdata.tlslite.integration.ClientHelper import ClientHelper - -# POP TLS PORT -POP3_TLS_PORT = 995 - -class POP3_TLS(POP3, ClientHelper): - """This class extends L{poplib.POP3} with TLS support.""" - - def __init__(self, host, port = POP3_TLS_PORT, - username=None, password=None, sharedKey=None, - certChain=None, privateKey=None, - cryptoID=None, protocol=None, - x509Fingerprint=None, - x509TrustList=None, x509CommonName=None, - settings=None): - """Create a new POP3_TLS. - - For client authentication, use one of these argument - combinations: - - username, password (SRP) - - username, sharedKey (shared-key) - - certChain, privateKey (certificate) - - For server authentication, you can either rely on the - implicit mutual authentication performed by SRP or - shared-keys, or you can do certificate-based server - authentication with one of these argument combinations: - - cryptoID[, protocol] (requires cryptoIDlib) - - x509Fingerprint - - x509TrustList[, x509CommonName] (requires cryptlib_py) - - Certificate-based server authentication is compatible with - SRP or certificate-based client authentication. It is - not compatible with shared-keys. - - The caller should be prepared to handle TLS-specific - exceptions. See the client handshake functions in - L{tlslite.TLSConnection.TLSConnection} for details on which - exceptions might be raised. - - @type host: str - @param host: Server to connect to. - - @type port: int - @param port: Port to connect to. - - @type username: str - @param username: SRP or shared-key username. Requires the - 'password' or 'sharedKey' argument. - - @type password: str - @param password: SRP password for mutual authentication. - Requires the 'username' argument. - - @type sharedKey: str - @param sharedKey: Shared key for mutual authentication. - Requires the 'username' argument. - - @type certChain: L{tlslite.X509CertChain.X509CertChain} or - L{cryptoIDlib.CertChain.CertChain} - @param certChain: Certificate chain for client authentication. - Requires the 'privateKey' argument. Excludes the SRP or - shared-key related arguments. - - @type privateKey: L{tlslite.utils.RSAKey.RSAKey} - @param privateKey: Private key for client authentication. - Requires the 'certChain' argument. Excludes the SRP or - shared-key related arguments. - - @type cryptoID: str - @param cryptoID: cryptoID for server authentication. Mutually - exclusive with the 'x509...' arguments. - - @type protocol: str - @param protocol: cryptoID protocol URI for server - authentication. Requires the 'cryptoID' argument. - - @type x509Fingerprint: str - @param x509Fingerprint: Hex-encoded X.509 fingerprint for - server authentication. Mutually exclusive with the 'cryptoID' - and 'x509TrustList' arguments. - - @type x509TrustList: list of L{tlslite.X509.X509} - @param x509TrustList: A list of trusted root certificates. The - other party must present a certificate chain which extends to - one of these root certificates. The cryptlib_py module must be - installed to use this parameter. Mutually exclusive with the - 'cryptoID' and 'x509Fingerprint' arguments. - - @type x509CommonName: str - @param x509CommonName: The end-entity certificate's 'CN' field - must match this value. For a web server, this is typically a - server name such as 'www.amazon.com'. Mutually exclusive with - the 'cryptoID' and 'x509Fingerprint' arguments. Requires the - 'x509TrustList' argument. - - @type settings: L{tlslite.HandshakeSettings.HandshakeSettings} - @param settings: Various settings which can be used to control - the ciphersuites, certificate types, and SSL/TLS versions - offered by the client. - """ - - self.host = host - self.port = port - msg = "getaddrinfo returns an empty list" - self.sock = None - for res in socket.getaddrinfo(self.host, self.port, 0, socket.SOCK_STREAM): - af, socktype, proto, canonname, sa = res - try: - self.sock = socket.socket(af, socktype, proto) - self.sock.connect(sa) - except socket.error, msg: - if self.sock: - self.sock.close() - self.sock = None - continue - break - if not self.sock: - raise socket.error, msg - - ### New code below (all else copied from poplib) - ClientHelper.__init__(self, - username, password, sharedKey, - certChain, privateKey, - cryptoID, protocol, - x509Fingerprint, - x509TrustList, x509CommonName, - settings) - - self.sock = TLSConnection(self.sock) - self.sock.closeSocket = True - ClientHelper._handshake(self, self.sock) - ### - - self.file = self.sock.makefile('rb') - self._debugging = 0 - self.welcome = self._getresp() diff --git a/gdata/analytics/tlslite/integration/SMTP_TLS.py b/gdata/analytics/tlslite/integration/SMTP_TLS.py deleted file mode 100644 index 67e0febed6..0000000000 --- a/gdata/analytics/tlslite/integration/SMTP_TLS.py +++ /dev/null @@ -1,114 +0,0 @@ -"""TLS Lite + smtplib.""" - -from smtplib import SMTP -from gdata.tlslite.TLSConnection import TLSConnection -from gdata.tlslite.integration.ClientHelper import ClientHelper - -class SMTP_TLS(SMTP): - """This class extends L{smtplib.SMTP} with TLS support.""" - - def starttls(self, - username=None, password=None, sharedKey=None, - certChain=None, privateKey=None, - cryptoID=None, protocol=None, - x509Fingerprint=None, - x509TrustList=None, x509CommonName=None, - settings=None): - """Puts the connection to the SMTP server into TLS mode. - - If the server supports TLS, this will encrypt the rest of the SMTP - session. - - For client authentication, use one of these argument - combinations: - - username, password (SRP) - - username, sharedKey (shared-key) - - certChain, privateKey (certificate) - - For server authentication, you can either rely on the - implicit mutual authentication performed by SRP or - shared-keys, or you can do certificate-based server - authentication with one of these argument combinations: - - cryptoID[, protocol] (requires cryptoIDlib) - - x509Fingerprint - - x509TrustList[, x509CommonName] (requires cryptlib_py) - - Certificate-based server authentication is compatible with - SRP or certificate-based client authentication. It is - not compatible with shared-keys. - - The caller should be prepared to handle TLS-specific - exceptions. See the client handshake functions in - L{tlslite.TLSConnection.TLSConnection} for details on which - exceptions might be raised. - - @type username: str - @param username: SRP or shared-key username. Requires the - 'password' or 'sharedKey' argument. - - @type password: str - @param password: SRP password for mutual authentication. - Requires the 'username' argument. - - @type sharedKey: str - @param sharedKey: Shared key for mutual authentication. - Requires the 'username' argument. - - @type certChain: L{tlslite.X509CertChain.X509CertChain} or - L{cryptoIDlib.CertChain.CertChain} - @param certChain: Certificate chain for client authentication. - Requires the 'privateKey' argument. Excludes the SRP or - shared-key related arguments. - - @type privateKey: L{tlslite.utils.RSAKey.RSAKey} - @param privateKey: Private key for client authentication. - Requires the 'certChain' argument. Excludes the SRP or - shared-key related arguments. - - @type cryptoID: str - @param cryptoID: cryptoID for server authentication. Mutually - exclusive with the 'x509...' arguments. - - @type protocol: str - @param protocol: cryptoID protocol URI for server - authentication. Requires the 'cryptoID' argument. - - @type x509Fingerprint: str - @param x509Fingerprint: Hex-encoded X.509 fingerprint for - server authentication. Mutually exclusive with the 'cryptoID' - and 'x509TrustList' arguments. - - @type x509TrustList: list of L{tlslite.X509.X509} - @param x509TrustList: A list of trusted root certificates. The - other party must present a certificate chain which extends to - one of these root certificates. The cryptlib_py module must be - installed to use this parameter. Mutually exclusive with the - 'cryptoID' and 'x509Fingerprint' arguments. - - @type x509CommonName: str - @param x509CommonName: The end-entity certificate's 'CN' field - must match this value. For a web server, this is typically a - server name such as 'www.amazon.com'. Mutually exclusive with - the 'cryptoID' and 'x509Fingerprint' arguments. Requires the - 'x509TrustList' argument. - - @type settings: L{tlslite.HandshakeSettings.HandshakeSettings} - @param settings: Various settings which can be used to control - the ciphersuites, certificate types, and SSL/TLS versions - offered by the client. - """ - (resp, reply) = self.docmd("STARTTLS") - if resp == 220: - helper = ClientHelper( - username, password, sharedKey, - certChain, privateKey, - cryptoID, protocol, - x509Fingerprint, - x509TrustList, x509CommonName, - settings) - conn = TLSConnection(self.sock) - conn.closeSocket = True - helper._handshake(conn) - self.sock = conn - self.file = conn.makefile('rb') - return (resp, reply) diff --git a/gdata/analytics/tlslite/integration/TLSAsyncDispatcherMixIn.py b/gdata/analytics/tlslite/integration/TLSAsyncDispatcherMixIn.py deleted file mode 100644 index f732f62e66..0000000000 --- a/gdata/analytics/tlslite/integration/TLSAsyncDispatcherMixIn.py +++ /dev/null @@ -1,139 +0,0 @@ -"""TLS Lite + asyncore.""" - - -import asyncore -from gdata.tlslite.TLSConnection import TLSConnection -from AsyncStateMachine import AsyncStateMachine - - -class TLSAsyncDispatcherMixIn(AsyncStateMachine): - """This class can be "mixed in" with an - L{asyncore.dispatcher} to add TLS support. - - This class essentially sits between the dispatcher and the select - loop, intercepting events and only calling the dispatcher when - applicable. - - In the case of handle_read(), a read operation will be activated, - and when it completes, the bytes will be placed in a buffer where - the dispatcher can retrieve them by calling recv(), and the - dispatcher's handle_read() will be called. - - In the case of handle_write(), the dispatcher's handle_write() will - be called, and when it calls send(), a write operation will be - activated. - - To use this class, you must combine it with an asyncore.dispatcher, - and pass in a handshake operation with setServerHandshakeOp(). - - Below is an example of using this class with medusa. This class is - mixed in with http_channel to create http_tls_channel. Note: - 1. the mix-in is listed first in the inheritance list - - 2. the input buffer size must be at least 16K, otherwise the - dispatcher might not read all the bytes from the TLS layer, - leaving some bytes in limbo. - - 3. IE seems to have a problem receiving a whole HTTP response in a - single TLS record, so HTML pages containing '\\r\\n\\r\\n' won't - be displayed on IE. - - Add the following text into 'start_medusa.py', in the 'HTTP Server' - section:: - - from tlslite.api import * - s = open("./serverX509Cert.pem").read() - x509 = X509() - x509.parse(s) - certChain = X509CertChain([x509]) - - s = open("./serverX509Key.pem").read() - privateKey = parsePEMKey(s, private=True) - - class http_tls_channel(TLSAsyncDispatcherMixIn, - http_server.http_channel): - ac_in_buffer_size = 16384 - - def __init__ (self, server, conn, addr): - http_server.http_channel.__init__(self, server, conn, addr) - TLSAsyncDispatcherMixIn.__init__(self, conn) - self.tlsConnection.ignoreAbruptClose = True - self.setServerHandshakeOp(certChain=certChain, - privateKey=privateKey) - - hs.channel_class = http_tls_channel - - If the TLS layer raises an exception, the exception will be caught - in asyncore.dispatcher, which will call close() on this class. The - TLS layer always closes the TLS connection before raising an - exception, so the close operation will complete right away, causing - asyncore.dispatcher.close() to be called, which closes the socket - and removes this instance from the asyncore loop. - - """ - - - def __init__(self, sock=None): - AsyncStateMachine.__init__(self) - - if sock: - self.tlsConnection = TLSConnection(sock) - - #Calculate the sibling I'm being mixed in with. - #This is necessary since we override functions - #like readable(), handle_read(), etc., but we - #also want to call the sibling's versions. - for cl in self.__class__.__bases__: - if cl != TLSAsyncDispatcherMixIn and cl != AsyncStateMachine: - self.siblingClass = cl - break - else: - raise AssertionError() - - def readable(self): - result = self.wantsReadEvent() - if result != None: - return result - return self.siblingClass.readable(self) - - def writable(self): - result = self.wantsWriteEvent() - if result != None: - return result - return self.siblingClass.writable(self) - - def handle_read(self): - self.inReadEvent() - - def handle_write(self): - self.inWriteEvent() - - def outConnectEvent(self): - self.siblingClass.handle_connect(self) - - def outCloseEvent(self): - asyncore.dispatcher.close(self) - - def outReadEvent(self, readBuffer): - self.readBuffer = readBuffer - self.siblingClass.handle_read(self) - - def outWriteEvent(self): - self.siblingClass.handle_write(self) - - def recv(self, bufferSize=16384): - if bufferSize < 16384 or self.readBuffer == None: - raise AssertionError() - returnValue = self.readBuffer - self.readBuffer = None - return returnValue - - def send(self, writeBuffer): - self.setWriteOp(writeBuffer) - return len(writeBuffer) - - def close(self): - if hasattr(self, "tlsConnection"): - self.setCloseOp() - else: - asyncore.dispatcher.close(self) diff --git a/gdata/analytics/tlslite/integration/TLSSocketServerMixIn.py b/gdata/analytics/tlslite/integration/TLSSocketServerMixIn.py deleted file mode 100644 index 10224b688b..0000000000 --- a/gdata/analytics/tlslite/integration/TLSSocketServerMixIn.py +++ /dev/null @@ -1,59 +0,0 @@ -"""TLS Lite + SocketServer.""" - -from gdata.tlslite.TLSConnection import TLSConnection - -class TLSSocketServerMixIn: - """ - This class can be mixed in with any L{SocketServer.TCPServer} to - add TLS support. - - To use this class, define a new class that inherits from it and - some L{SocketServer.TCPServer} (with the mix-in first). Then - implement the handshake() method, doing some sort of server - handshake on the connection argument. If the handshake method - returns True, the RequestHandler will be triggered. Below is a - complete example of a threaded HTTPS server:: - - from SocketServer import * - from BaseHTTPServer import * - from SimpleHTTPServer import * - from tlslite.api import * - - s = open("./serverX509Cert.pem").read() - x509 = X509() - x509.parse(s) - certChain = X509CertChain([x509]) - - s = open("./serverX509Key.pem").read() - privateKey = parsePEMKey(s, private=True) - - sessionCache = SessionCache() - - class MyHTTPServer(ThreadingMixIn, TLSSocketServerMixIn, - HTTPServer): - def handshake(self, tlsConnection): - try: - tlsConnection.handshakeServer(certChain=certChain, - privateKey=privateKey, - sessionCache=sessionCache) - tlsConnection.ignoreAbruptClose = True - return True - except TLSError, error: - print "Handshake failure:", str(error) - return False - - httpd = MyHTTPServer(('localhost', 443), SimpleHTTPRequestHandler) - httpd.serve_forever() - """ - - - def finish_request(self, sock, client_address): - tlsConnection = TLSConnection(sock) - if self.handshake(tlsConnection) == True: - self.RequestHandlerClass(tlsConnection, client_address, self) - tlsConnection.close() - - #Implement this method to do some form of handshaking. Return True - #if the handshake finishes properly and the request is authorized. - def handshake(self, tlsConnection): - raise NotImplementedError() diff --git a/gdata/analytics/tlslite/integration/TLSTwistedProtocolWrapper.py b/gdata/analytics/tlslite/integration/TLSTwistedProtocolWrapper.py deleted file mode 100644 index c88703cacf..0000000000 --- a/gdata/analytics/tlslite/integration/TLSTwistedProtocolWrapper.py +++ /dev/null @@ -1,196 +0,0 @@ -"""TLS Lite + Twisted.""" - -from twisted.protocols.policies import ProtocolWrapper, WrappingFactory -from twisted.python.failure import Failure - -from AsyncStateMachine import AsyncStateMachine -from gdata.tlslite.TLSConnection import TLSConnection -from gdata.tlslite.errors import * - -import socket -import errno - - -#The TLSConnection is created around a "fake socket" that -#plugs it into the underlying Twisted transport -class _FakeSocket: - def __init__(self, wrapper): - self.wrapper = wrapper - self.data = "" - - def send(self, data): - ProtocolWrapper.write(self.wrapper, data) - return len(data) - - def recv(self, numBytes): - if self.data == "": - raise socket.error, (errno.EWOULDBLOCK, "") - returnData = self.data[:numBytes] - self.data = self.data[numBytes:] - return returnData - -class TLSTwistedProtocolWrapper(ProtocolWrapper, AsyncStateMachine): - """This class can wrap Twisted protocols to add TLS support. - - Below is a complete example of using TLS Lite with a Twisted echo - server. - - There are two server implementations below. Echo is the original - protocol, which is oblivious to TLS. Echo1 subclasses Echo and - negotiates TLS when the client connects. Echo2 subclasses Echo and - negotiates TLS when the client sends "STARTTLS":: - - from twisted.internet.protocol import Protocol, Factory - from twisted.internet import reactor - from twisted.protocols.policies import WrappingFactory - from twisted.protocols.basic import LineReceiver - from twisted.python import log - from twisted.python.failure import Failure - import sys - from tlslite.api import * - - s = open("./serverX509Cert.pem").read() - x509 = X509() - x509.parse(s) - certChain = X509CertChain([x509]) - - s = open("./serverX509Key.pem").read() - privateKey = parsePEMKey(s, private=True) - - verifierDB = VerifierDB("verifierDB") - verifierDB.open() - - class Echo(LineReceiver): - def connectionMade(self): - self.transport.write("Welcome to the echo server!\\r\\n") - - def lineReceived(self, line): - self.transport.write(line + "\\r\\n") - - class Echo1(Echo): - def connectionMade(self): - if not self.transport.tlsStarted: - self.transport.setServerHandshakeOp(certChain=certChain, - privateKey=privateKey, - verifierDB=verifierDB) - else: - Echo.connectionMade(self) - - def connectionLost(self, reason): - pass #Handle any TLS exceptions here - - class Echo2(Echo): - def lineReceived(self, data): - if data == "STARTTLS": - self.transport.setServerHandshakeOp(certChain=certChain, - privateKey=privateKey, - verifierDB=verifierDB) - else: - Echo.lineReceived(self, data) - - def connectionLost(self, reason): - pass #Handle any TLS exceptions here - - factory = Factory() - factory.protocol = Echo1 - #factory.protocol = Echo2 - - wrappingFactory = WrappingFactory(factory) - wrappingFactory.protocol = TLSTwistedProtocolWrapper - - log.startLogging(sys.stdout) - reactor.listenTCP(1079, wrappingFactory) - reactor.run() - - This class works as follows: - - Data comes in and is given to the AsyncStateMachine for handling. - AsyncStateMachine will forward events to this class, and we'll - pass them on to the ProtocolHandler, which will proxy them to the - wrapped protocol. The wrapped protocol may then call back into - this class, and these calls will be proxied into the - AsyncStateMachine. - - The call graph looks like this: - - self.dataReceived - - AsyncStateMachine.inReadEvent - - self.out(Connect|Close|Read)Event - - ProtocolWrapper.(connectionMade|loseConnection|dataReceived) - - self.(loseConnection|write|writeSequence) - - AsyncStateMachine.(setCloseOp|setWriteOp) - """ - - #WARNING: IF YOU COPY-AND-PASTE THE ABOVE CODE, BE SURE TO REMOVE - #THE EXTRA ESCAPING AROUND "\\r\\n" - - def __init__(self, factory, wrappedProtocol): - ProtocolWrapper.__init__(self, factory, wrappedProtocol) - AsyncStateMachine.__init__(self) - self.fakeSocket = _FakeSocket(self) - self.tlsConnection = TLSConnection(self.fakeSocket) - self.tlsStarted = False - self.connectionLostCalled = False - - def connectionMade(self): - try: - ProtocolWrapper.connectionMade(self) - except TLSError, e: - self.connectionLost(Failure(e)) - ProtocolWrapper.loseConnection(self) - - def dataReceived(self, data): - try: - if not self.tlsStarted: - ProtocolWrapper.dataReceived(self, data) - else: - self.fakeSocket.data += data - while self.fakeSocket.data: - AsyncStateMachine.inReadEvent(self) - except TLSError, e: - self.connectionLost(Failure(e)) - ProtocolWrapper.loseConnection(self) - - def connectionLost(self, reason): - if not self.connectionLostCalled: - ProtocolWrapper.connectionLost(self, reason) - self.connectionLostCalled = True - - - def outConnectEvent(self): - ProtocolWrapper.connectionMade(self) - - def outCloseEvent(self): - ProtocolWrapper.loseConnection(self) - - def outReadEvent(self, data): - if data == "": - ProtocolWrapper.loseConnection(self) - else: - ProtocolWrapper.dataReceived(self, data) - - - def setServerHandshakeOp(self, **args): - self.tlsStarted = True - AsyncStateMachine.setServerHandshakeOp(self, **args) - - def loseConnection(self): - if not self.tlsStarted: - ProtocolWrapper.loseConnection(self) - else: - AsyncStateMachine.setCloseOp(self) - - def write(self, data): - if not self.tlsStarted: - ProtocolWrapper.write(self, data) - else: - #Because of the FakeSocket, write operations are guaranteed to - #terminate immediately. - AsyncStateMachine.setWriteOp(self, data) - - def writeSequence(self, seq): - if not self.tlsStarted: - ProtocolWrapper.writeSequence(self, seq) - else: - #Because of the FakeSocket, write operations are guaranteed to - #terminate immediately. - AsyncStateMachine.setWriteOp(self, "".join(seq)) diff --git a/gdata/analytics/tlslite/integration/XMLRPCTransport.py b/gdata/analytics/tlslite/integration/XMLRPCTransport.py deleted file mode 100644 index 3f025e46e7..0000000000 --- a/gdata/analytics/tlslite/integration/XMLRPCTransport.py +++ /dev/null @@ -1,137 +0,0 @@ -"""TLS Lite + xmlrpclib.""" - -import xmlrpclib -import httplib -from gdata.tlslite.integration.HTTPTLSConnection import HTTPTLSConnection -from gdata.tlslite.integration.ClientHelper import ClientHelper - - -class XMLRPCTransport(xmlrpclib.Transport, ClientHelper): - """Handles an HTTPS transaction to an XML-RPC server.""" - - def __init__(self, - username=None, password=None, sharedKey=None, - certChain=None, privateKey=None, - cryptoID=None, protocol=None, - x509Fingerprint=None, - x509TrustList=None, x509CommonName=None, - settings=None): - """Create a new XMLRPCTransport. - - An instance of this class can be passed to L{xmlrpclib.ServerProxy} - to use TLS with XML-RPC calls:: - - from tlslite.api import XMLRPCTransport - from xmlrpclib import ServerProxy - - transport = XMLRPCTransport(user="alice", password="abra123") - server = ServerProxy("https://localhost", transport) - - For client authentication, use one of these argument - combinations: - - username, password (SRP) - - username, sharedKey (shared-key) - - certChain, privateKey (certificate) - - For server authentication, you can either rely on the - implicit mutual authentication performed by SRP or - shared-keys, or you can do certificate-based server - authentication with one of these argument combinations: - - cryptoID[, protocol] (requires cryptoIDlib) - - x509Fingerprint - - x509TrustList[, x509CommonName] (requires cryptlib_py) - - Certificate-based server authentication is compatible with - SRP or certificate-based client authentication. It is - not compatible with shared-keys. - - The constructor does not perform the TLS handshake itself, but - simply stores these arguments for later. The handshake is - performed only when this class needs to connect with the - server. Thus you should be prepared to handle TLS-specific - exceptions when calling methods of L{xmlrpclib.ServerProxy}. See the - client handshake functions in - L{tlslite.TLSConnection.TLSConnection} for details on which - exceptions might be raised. - - @type username: str - @param username: SRP or shared-key username. Requires the - 'password' or 'sharedKey' argument. - - @type password: str - @param password: SRP password for mutual authentication. - Requires the 'username' argument. - - @type sharedKey: str - @param sharedKey: Shared key for mutual authentication. - Requires the 'username' argument. - - @type certChain: L{tlslite.X509CertChain.X509CertChain} or - L{cryptoIDlib.CertChain.CertChain} - @param certChain: Certificate chain for client authentication. - Requires the 'privateKey' argument. Excludes the SRP or - shared-key related arguments. - - @type privateKey: L{tlslite.utils.RSAKey.RSAKey} - @param privateKey: Private key for client authentication. - Requires the 'certChain' argument. Excludes the SRP or - shared-key related arguments. - - @type cryptoID: str - @param cryptoID: cryptoID for server authentication. Mutually - exclusive with the 'x509...' arguments. - - @type protocol: str - @param protocol: cryptoID protocol URI for server - authentication. Requires the 'cryptoID' argument. - - @type x509Fingerprint: str - @param x509Fingerprint: Hex-encoded X.509 fingerprint for - server authentication. Mutually exclusive with the 'cryptoID' - and 'x509TrustList' arguments. - - @type x509TrustList: list of L{tlslite.X509.X509} - @param x509TrustList: A list of trusted root certificates. The - other party must present a certificate chain which extends to - one of these root certificates. The cryptlib_py module must be - installed to use this parameter. Mutually exclusive with the - 'cryptoID' and 'x509Fingerprint' arguments. - - @type x509CommonName: str - @param x509CommonName: The end-entity certificate's 'CN' field - must match this value. For a web server, this is typically a - server name such as 'www.amazon.com'. Mutually exclusive with - the 'cryptoID' and 'x509Fingerprint' arguments. Requires the - 'x509TrustList' argument. - - @type settings: L{tlslite.HandshakeSettings.HandshakeSettings} - @param settings: Various settings which can be used to control - the ciphersuites, certificate types, and SSL/TLS versions - offered by the client. - """ - - ClientHelper.__init__(self, - username, password, sharedKey, - certChain, privateKey, - cryptoID, protocol, - x509Fingerprint, - x509TrustList, x509CommonName, - settings) - - - def make_connection(self, host): - # create a HTTPS connection object from a host descriptor - host, extra_headers, x509 = self.get_host_info(host) - http = HTTPTLSConnection(host, None, - self.username, self.password, - self.sharedKey, - self.certChain, self.privateKey, - self.checker.cryptoID, - self.checker.protocol, - self.checker.x509Fingerprint, - self.checker.x509TrustList, - self.checker.x509CommonName, - self.settings) - http2 = httplib.HTTP() - http2._setup(http) - return http2 diff --git a/gdata/analytics/tlslite/integration/__init__.py b/gdata/analytics/tlslite/integration/__init__.py deleted file mode 100644 index 960f4065f2..0000000000 --- a/gdata/analytics/tlslite/integration/__init__.py +++ /dev/null @@ -1,17 +0,0 @@ -"""Classes for integrating TLS Lite with other packages.""" - -__all__ = ["AsyncStateMachine", - "HTTPTLSConnection", - "POP3_TLS", - "IMAP4_TLS", - "SMTP_TLS", - "XMLRPCTransport", - "TLSSocketServerMixIn", - "TLSAsyncDispatcherMixIn", - "TLSTwistedProtocolWrapper"] - -try: - import twisted - del twisted -except ImportError: - del __all__[__all__.index("TLSTwistedProtocolWrapper")] diff --git a/gdata/analytics/tlslite/mathtls.py b/gdata/analytics/tlslite/mathtls.py deleted file mode 100644 index 3b8ede6012..0000000000 --- a/gdata/analytics/tlslite/mathtls.py +++ /dev/null @@ -1,170 +0,0 @@ -"""Miscellaneous helper functions.""" - -from utils.compat import * -from utils.cryptomath import * - -import hmac -import md5 -import sha - -#1024, 1536, 2048, 3072, 4096, 6144, and 8192 bit groups] -goodGroupParameters = [(2,0xEEAF0AB9ADB38DD69C33F80AFA8FC5E86072618775FF3C0B9EA2314C9C256576D674DF7496EA81D3383B4813D692C6E0E0D5D8E250B98BE48E495C1D6089DAD15DC7D7B46154D6B6CE8EF4AD69B15D4982559B297BCF1885C529F566660E57EC68EDBC3C05726CC02FD4CBF4976EAA9AFD5138FE8376435B9FC61D2FC0EB06E3),\ - (2,0x9DEF3CAFB939277AB1F12A8617A47BBBDBA51DF499AC4C80BEEEA9614B19CC4D5F4F5F556E27CBDE51C6A94BE4607A291558903BA0D0F84380B655BB9A22E8DCDF028A7CEC67F0D08134B1C8B97989149B609E0BE3BAB63D47548381DBC5B1FC764E3F4B53DD9DA1158BFD3E2B9C8CF56EDF019539349627DB2FD53D24B7C48665772E437D6C7F8CE442734AF7CCB7AE837C264AE3A9BEB87F8A2FE9B8B5292E5A021FFF5E91479E8CE7A28C2442C6F315180F93499A234DCF76E3FED135F9BB),\ - (2,0xAC6BDB41324A9A9BF166DE5E1389582FAF72B6651987EE07FC3192943DB56050A37329CBB4A099ED8193E0757767A13DD52312AB4B03310DCD7F48A9DA04FD50E8083969EDB767B0CF6095179A163AB3661A05FBD5FAAAE82918A9962F0B93B855F97993EC975EEAA80D740ADBF4FF747359D041D5C33EA71D281E446B14773BCA97B43A23FB801676BD207A436C6481F1D2B9078717461A5B9D32E688F87748544523B524B0D57D5EA77A2775D2ECFA032CFBDBF52FB3786160279004E57AE6AF874E7303CE53299CCC041C7BC308D82A5698F3A8D0C38271AE35F8E9DBFBB694B5C803D89F7AE435DE236D525F54759B65E372FCD68EF20FA7111F9E4AFF73),\ - (2,0xFFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6BF12FFA06D98A0864D87602733EC86A64521F2B18177B200CBBE117577A615D6C770988C0BAD946E208E24FA074E5AB3143DB5BFCE0FD108E4B82D120A93AD2CAFFFFFFFFFFFFFFFF),\ - (5,0xFFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6BF12FFA06D98A0864D87602733EC86A64521F2B18177B200CBBE117577A615D6C770988C0BAD946E208E24FA074E5AB3143DB5BFCE0FD108E4B82D120A92108011A723C12A787E6D788719A10BDBA5B2699C327186AF4E23C1A946834B6150BDA2583E9CA2AD44CE8DBBBC2DB04DE8EF92E8EFC141FBECAA6287C59474E6BC05D99B2964FA090C3A2233BA186515BE7ED1F612970CEE2D7AFB81BDD762170481CD0069127D5B05AA993B4EA988D8FDDC186FFB7DC90A6C08F4DF435C934063199FFFFFFFFFFFFFFFF),\ - (5,0xFFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6BF12FFA06D98A0864D87602733EC86A64521F2B18177B200CBBE117577A615D6C770988C0BAD946E208E24FA074E5AB3143DB5BFCE0FD108E4B82D120A92108011A723C12A787E6D788719A10BDBA5B2699C327186AF4E23C1A946834B6150BDA2583E9CA2AD44CE8DBBBC2DB04DE8EF92E8EFC141FBECAA6287C59474E6BC05D99B2964FA090C3A2233BA186515BE7ED1F612970CEE2D7AFB81BDD762170481CD0069127D5B05AA993B4EA988D8FDDC186FFB7DC90A6C08F4DF435C93402849236C3FAB4D27C7026C1D4DCB2602646DEC9751E763DBA37BDF8FF9406AD9E530EE5DB382F413001AEB06A53ED9027D831179727B0865A8918DA3EDBEBCF9B14ED44CE6CBACED4BB1BDB7F1447E6CC254B332051512BD7AF426FB8F401378CD2BF5983CA01C64B92ECF032EA15D1721D03F482D7CE6E74FEF6D55E702F46980C82B5A84031900B1C9E59E7C97FBEC7E8F323A97A7E36CC88BE0F1D45B7FF585AC54BD407B22B4154AACC8F6D7EBF48E1D814CC5ED20F8037E0A79715EEF29BE32806A1D58BB7C5DA76F550AA3D8A1FBFF0EB19CCB1A313D55CDA56C9EC2EF29632387FE8D76E3C0468043E8F663F4860EE12BF2D5B0B7474D6E694F91E6DCC4024FFFFFFFFFFFFFFFF),\ - (5,0xFFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6BF12FFA06D98A0864D87602733EC86A64521F2B18177B200CBBE117577A615D6C770988C0BAD946E208E24FA074E5AB3143DB5BFCE0FD108E4B82D120A92108011A723C12A787E6D788719A10BDBA5B2699C327186AF4E23C1A946834B6150BDA2583E9CA2AD44CE8DBBBC2DB04DE8EF92E8EFC141FBECAA6287C59474E6BC05D99B2964FA090C3A2233BA186515BE7ED1F612970CEE2D7AFB81BDD762170481CD0069127D5B05AA993B4EA988D8FDDC186FFB7DC90A6C08F4DF435C93402849236C3FAB4D27C7026C1D4DCB2602646DEC9751E763DBA37BDF8FF9406AD9E530EE5DB382F413001AEB06A53ED9027D831179727B0865A8918DA3EDBEBCF9B14ED44CE6CBACED4BB1BDB7F1447E6CC254B332051512BD7AF426FB8F401378CD2BF5983CA01C64B92ECF032EA15D1721D03F482D7CE6E74FEF6D55E702F46980C82B5A84031900B1C9E59E7C97FBEC7E8F323A97A7E36CC88BE0F1D45B7FF585AC54BD407B22B4154AACC8F6D7EBF48E1D814CC5ED20F8037E0A79715EEF29BE32806A1D58BB7C5DA76F550AA3D8A1FBFF0EB19CCB1A313D55CDA56C9EC2EF29632387FE8D76E3C0468043E8F663F4860EE12BF2D5B0B7474D6E694F91E6DBE115974A3926F12FEE5E438777CB6A932DF8CD8BEC4D073B931BA3BC832B68D9DD300741FA7BF8AFC47ED2576F6936BA424663AAB639C5AE4F5683423B4742BF1C978238F16CBE39D652DE3FDB8BEFC848AD922222E04A4037C0713EB57A81A23F0C73473FC646CEA306B4BCBC8862F8385DDFA9D4B7FA2C087E879683303ED5BDD3A062B3CF5B3A278A66D2A13F83F44F82DDF310EE074AB6A364597E899A0255DC164F31CC50846851DF9AB48195DED7EA1B1D510BD7EE74D73FAF36BC31ECFA268359046F4EB879F924009438B481C6CD7889A002ED5EE382BC9190DA6FC026E479558E4475677E9AA9E3050E2765694DFC81F56E880B96E7160C980DD98EDD3DFFFFFFFFFFFFFFFFF)] - -def P_hash(hashModule, secret, seed, length): - bytes = createByteArrayZeros(length) - secret = bytesToString(secret) - seed = bytesToString(seed) - A = seed - index = 0 - while 1: - A = hmac.HMAC(secret, A, hashModule).digest() - output = hmac.HMAC(secret, A+seed, hashModule).digest() - for c in output: - if index >= length: - return bytes - bytes[index] = ord(c) - index += 1 - return bytes - -def PRF(secret, label, seed, length): - #Split the secret into left and right halves - S1 = secret[ : int(math.ceil(len(secret)/2.0))] - S2 = secret[ int(math.floor(len(secret)/2.0)) : ] - - #Run the left half through P_MD5 and the right half through P_SHA1 - p_md5 = P_hash(md5, S1, concatArrays(stringToBytes(label), seed), length) - p_sha1 = P_hash(sha, S2, concatArrays(stringToBytes(label), seed), length) - - #XOR the output values and return the result - for x in range(length): - p_md5[x] ^= p_sha1[x] - return p_md5 - - -def PRF_SSL(secret, seed, length): - secretStr = bytesToString(secret) - seedStr = bytesToString(seed) - bytes = createByteArrayZeros(length) - index = 0 - for x in range(26): - A = chr(ord('A')+x) * (x+1) # 'A', 'BB', 'CCC', etc.. - input = secretStr + sha.sha(A + secretStr + seedStr).digest() - output = md5.md5(input).digest() - for c in output: - if index >= length: - return bytes - bytes[index] = ord(c) - index += 1 - return bytes - -def makeX(salt, username, password): - if len(username)>=256: - raise ValueError("username too long") - if len(salt)>=256: - raise ValueError("salt too long") - return stringToNumber(sha.sha(salt + sha.sha(username + ":" + password)\ - .digest()).digest()) - -#This function is used by VerifierDB.makeVerifier -def makeVerifier(username, password, bits): - bitsIndex = {1024:0, 1536:1, 2048:2, 3072:3, 4096:4, 6144:5, 8192:6}[bits] - g,N = goodGroupParameters[bitsIndex] - salt = bytesToString(getRandomBytes(16)) - x = makeX(salt, username, password) - verifier = powMod(g, x, N) - return N, g, salt, verifier - -def PAD(n, x): - nLength = len(numberToString(n)) - s = numberToString(x) - if len(s) < nLength: - s = ("\0" * (nLength-len(s))) + s - return s - -def makeU(N, A, B): - return stringToNumber(sha.sha(PAD(N, A) + PAD(N, B)).digest()) - -def makeK(N, g): - return stringToNumber(sha.sha(numberToString(N) + PAD(N, g)).digest()) - - -""" -MAC_SSL -Modified from Python HMAC by Trevor -""" - -class MAC_SSL: - """MAC_SSL class. - - This supports the API for Cryptographic Hash Functions (PEP 247). - """ - - def __init__(self, key, msg = None, digestmod = None): - """Create a new MAC_SSL object. - - key: key for the keyed hash object. - msg: Initial input for the hash, if provided. - digestmod: A module supporting PEP 247. Defaults to the md5 module. - """ - if digestmod is None: - import md5 - digestmod = md5 - - if key == None: #TREVNEW - for faster copying - return #TREVNEW - - self.digestmod = digestmod - self.outer = digestmod.new() - self.inner = digestmod.new() - self.digest_size = digestmod.digest_size - - ipad = "\x36" * 40 - opad = "\x5C" * 40 - - self.inner.update(key) - self.inner.update(ipad) - self.outer.update(key) - self.outer.update(opad) - if msg is not None: - self.update(msg) - - - def update(self, msg): - """Update this hashing object with the string msg. - """ - self.inner.update(msg) - - def copy(self): - """Return a separate copy of this hashing object. - - An update to this copy won't affect the original object. - """ - other = MAC_SSL(None) #TREVNEW - for faster copying - other.digest_size = self.digest_size #TREVNEW - other.digestmod = self.digestmod - other.inner = self.inner.copy() - other.outer = self.outer.copy() - return other - - def digest(self): - """Return the hash value of this hashing object. - - This returns a string containing 8-bit data. The object is - not altered in any way by this function; you can continue - updating the object after calling this function. - """ - h = self.outer.copy() - h.update(self.inner.digest()) - return h.digest() - - def hexdigest(self): - """Like digest(), but returns a string of hexadecimal digits instead. - """ - return "".join([hex(ord(x))[2:].zfill(2) - for x in tuple(self.digest())]) diff --git a/gdata/analytics/tlslite/messages.py b/gdata/analytics/tlslite/messages.py deleted file mode 100644 index afccc793a5..0000000000 --- a/gdata/analytics/tlslite/messages.py +++ /dev/null @@ -1,561 +0,0 @@ -"""Classes representing TLS messages.""" - -from utils.compat import * -from utils.cryptomath import * -from errors import * -from utils.codec import * -from constants import * -from X509 import X509 -from X509CertChain import X509CertChain - -import sha -import md5 - -class RecordHeader3: - def __init__(self): - self.type = 0 - self.version = (0,0) - self.length = 0 - self.ssl2 = False - - def create(self, version, type, length): - self.type = type - self.version = version - self.length = length - return self - - def write(self): - w = Writer(5) - w.add(self.type, 1) - w.add(self.version[0], 1) - w.add(self.version[1], 1) - w.add(self.length, 2) - return w.bytes - - def parse(self, p): - self.type = p.get(1) - self.version = (p.get(1), p.get(1)) - self.length = p.get(2) - self.ssl2 = False - return self - -class RecordHeader2: - def __init__(self): - self.type = 0 - self.version = (0,0) - self.length = 0 - self.ssl2 = True - - def parse(self, p): - if p.get(1)!=128: - raise SyntaxError() - self.type = ContentType.handshake - self.version = (2,0) - #We don't support 2-byte-length-headers; could be a problem - self.length = p.get(1) - return self - - -class Msg: - def preWrite(self, trial): - if trial: - w = Writer() - else: - length = self.write(True) - w = Writer(length) - return w - - def postWrite(self, w, trial): - if trial: - return w.index - else: - return w.bytes - -class Alert(Msg): - def __init__(self): - self.contentType = ContentType.alert - self.level = 0 - self.description = 0 - - def create(self, description, level=AlertLevel.fatal): - self.level = level - self.description = description - return self - - def parse(self, p): - p.setLengthCheck(2) - self.level = p.get(1) - self.description = p.get(1) - p.stopLengthCheck() - return self - - def write(self): - w = Writer(2) - w.add(self.level, 1) - w.add(self.description, 1) - return w.bytes - - -class HandshakeMsg(Msg): - def preWrite(self, handshakeType, trial): - if trial: - w = Writer() - w.add(handshakeType, 1) - w.add(0, 3) - else: - length = self.write(True) - w = Writer(length) - w.add(handshakeType, 1) - w.add(length-4, 3) - return w - - -class ClientHello(HandshakeMsg): - def __init__(self, ssl2=False): - self.contentType = ContentType.handshake - self.ssl2 = ssl2 - self.client_version = (0,0) - self.random = createByteArrayZeros(32) - self.session_id = createByteArraySequence([]) - self.cipher_suites = [] # a list of 16-bit values - self.certificate_types = [CertificateType.x509] - self.compression_methods = [] # a list of 8-bit values - self.srp_username = None # a string - - def create(self, version, random, session_id, cipher_suites, - certificate_types=None, srp_username=None): - self.client_version = version - self.random = random - self.session_id = session_id - self.cipher_suites = cipher_suites - self.certificate_types = certificate_types - self.compression_methods = [0] - self.srp_username = srp_username - return self - - def parse(self, p): - if self.ssl2: - self.client_version = (p.get(1), p.get(1)) - cipherSpecsLength = p.get(2) - sessionIDLength = p.get(2) - randomLength = p.get(2) - self.cipher_suites = p.getFixList(3, int(cipherSpecsLength/3)) - self.session_id = p.getFixBytes(sessionIDLength) - self.random = p.getFixBytes(randomLength) - if len(self.random) < 32: - zeroBytes = 32-len(self.random) - self.random = createByteArrayZeros(zeroBytes) + self.random - self.compression_methods = [0]#Fake this value - - #We're not doing a stopLengthCheck() for SSLv2, oh well.. - else: - p.startLengthCheck(3) - self.client_version = (p.get(1), p.get(1)) - self.random = p.getFixBytes(32) - self.session_id = p.getVarBytes(1) - self.cipher_suites = p.getVarList(2, 2) - self.compression_methods = p.getVarList(1, 1) - if not p.atLengthCheck(): - totalExtLength = p.get(2) - soFar = 0 - while soFar != totalExtLength: - extType = p.get(2) - extLength = p.get(2) - if extType == 6: - self.srp_username = bytesToString(p.getVarBytes(1)) - elif extType == 7: - self.certificate_types = p.getVarList(1, 1) - else: - p.getFixBytes(extLength) - soFar += 4 + extLength - p.stopLengthCheck() - return self - - def write(self, trial=False): - w = HandshakeMsg.preWrite(self, HandshakeType.client_hello, trial) - w.add(self.client_version[0], 1) - w.add(self.client_version[1], 1) - w.addFixSeq(self.random, 1) - w.addVarSeq(self.session_id, 1, 1) - w.addVarSeq(self.cipher_suites, 2, 2) - w.addVarSeq(self.compression_methods, 1, 1) - - extLength = 0 - if self.certificate_types and self.certificate_types != \ - [CertificateType.x509]: - extLength += 5 + len(self.certificate_types) - if self.srp_username: - extLength += 5 + len(self.srp_username) - if extLength > 0: - w.add(extLength, 2) - - if self.certificate_types and self.certificate_types != \ - [CertificateType.x509]: - w.add(7, 2) - w.add(len(self.certificate_types)+1, 2) - w.addVarSeq(self.certificate_types, 1, 1) - if self.srp_username: - w.add(6, 2) - w.add(len(self.srp_username)+1, 2) - w.addVarSeq(stringToBytes(self.srp_username), 1, 1) - - return HandshakeMsg.postWrite(self, w, trial) - - -class ServerHello(HandshakeMsg): - def __init__(self): - self.contentType = ContentType.handshake - self.server_version = (0,0) - self.random = createByteArrayZeros(32) - self.session_id = createByteArraySequence([]) - self.cipher_suite = 0 - self.certificate_type = CertificateType.x509 - self.compression_method = 0 - - def create(self, version, random, session_id, cipher_suite, - certificate_type): - self.server_version = version - self.random = random - self.session_id = session_id - self.cipher_suite = cipher_suite - self.certificate_type = certificate_type - self.compression_method = 0 - return self - - def parse(self, p): - p.startLengthCheck(3) - self.server_version = (p.get(1), p.get(1)) - self.random = p.getFixBytes(32) - self.session_id = p.getVarBytes(1) - self.cipher_suite = p.get(2) - self.compression_method = p.get(1) - if not p.atLengthCheck(): - totalExtLength = p.get(2) - soFar = 0 - while soFar != totalExtLength: - extType = p.get(2) - extLength = p.get(2) - if extType == 7: - self.certificate_type = p.get(1) - else: - p.getFixBytes(extLength) - soFar += 4 + extLength - p.stopLengthCheck() - return self - - def write(self, trial=False): - w = HandshakeMsg.preWrite(self, HandshakeType.server_hello, trial) - w.add(self.server_version[0], 1) - w.add(self.server_version[1], 1) - w.addFixSeq(self.random, 1) - w.addVarSeq(self.session_id, 1, 1) - w.add(self.cipher_suite, 2) - w.add(self.compression_method, 1) - - extLength = 0 - if self.certificate_type and self.certificate_type != \ - CertificateType.x509: - extLength += 5 - - if extLength != 0: - w.add(extLength, 2) - - if self.certificate_type and self.certificate_type != \ - CertificateType.x509: - w.add(7, 2) - w.add(1, 2) - w.add(self.certificate_type, 1) - - return HandshakeMsg.postWrite(self, w, trial) - -class Certificate(HandshakeMsg): - def __init__(self, certificateType): - self.certificateType = certificateType - self.contentType = ContentType.handshake - self.certChain = None - - def create(self, certChain): - self.certChain = certChain - return self - - def parse(self, p): - p.startLengthCheck(3) - if self.certificateType == CertificateType.x509: - chainLength = p.get(3) - index = 0 - certificate_list = [] - while index != chainLength: - certBytes = p.getVarBytes(3) - x509 = X509() - x509.parseBinary(certBytes) - certificate_list.append(x509) - index += len(certBytes)+3 - if certificate_list: - self.certChain = X509CertChain(certificate_list) - elif self.certificateType == CertificateType.cryptoID: - s = bytesToString(p.getVarBytes(2)) - if s: - try: - import cryptoIDlib.CertChain - except ImportError: - raise SyntaxError(\ - "cryptoID cert chain received, cryptoIDlib not present") - self.certChain = cryptoIDlib.CertChain.CertChain().parse(s) - else: - raise AssertionError() - - p.stopLengthCheck() - return self - - def write(self, trial=False): - w = HandshakeMsg.preWrite(self, HandshakeType.certificate, trial) - if self.certificateType == CertificateType.x509: - chainLength = 0 - if self.certChain: - certificate_list = self.certChain.x509List - else: - certificate_list = [] - #determine length - for cert in certificate_list: - bytes = cert.writeBytes() - chainLength += len(bytes)+3 - #add bytes - w.add(chainLength, 3) - for cert in certificate_list: - bytes = cert.writeBytes() - w.addVarSeq(bytes, 1, 3) - elif self.certificateType == CertificateType.cryptoID: - if self.certChain: - bytes = stringToBytes(self.certChain.write()) - else: - bytes = createByteArraySequence([]) - w.addVarSeq(bytes, 1, 2) - else: - raise AssertionError() - return HandshakeMsg.postWrite(self, w, trial) - -class CertificateRequest(HandshakeMsg): - def __init__(self): - self.contentType = ContentType.handshake - self.certificate_types = [] - #treat as opaque bytes for now - self.certificate_authorities = createByteArraySequence([]) - - def create(self, certificate_types, certificate_authorities): - self.certificate_types = certificate_types - self.certificate_authorities = certificate_authorities - return self - - def parse(self, p): - p.startLengthCheck(3) - self.certificate_types = p.getVarList(1, 1) - self.certificate_authorities = p.getVarBytes(2) - p.stopLengthCheck() - return self - - def write(self, trial=False): - w = HandshakeMsg.preWrite(self, HandshakeType.certificate_request, - trial) - w.addVarSeq(self.certificate_types, 1, 1) - w.addVarSeq(self.certificate_authorities, 1, 2) - return HandshakeMsg.postWrite(self, w, trial) - -class ServerKeyExchange(HandshakeMsg): - def __init__(self, cipherSuite): - self.cipherSuite = cipherSuite - self.contentType = ContentType.handshake - self.srp_N = 0L - self.srp_g = 0L - self.srp_s = createByteArraySequence([]) - self.srp_B = 0L - self.signature = createByteArraySequence([]) - - def createSRP(self, srp_N, srp_g, srp_s, srp_B): - self.srp_N = srp_N - self.srp_g = srp_g - self.srp_s = srp_s - self.srp_B = srp_B - return self - - def parse(self, p): - p.startLengthCheck(3) - self.srp_N = bytesToNumber(p.getVarBytes(2)) - self.srp_g = bytesToNumber(p.getVarBytes(2)) - self.srp_s = p.getVarBytes(1) - self.srp_B = bytesToNumber(p.getVarBytes(2)) - if self.cipherSuite in CipherSuite.srpRsaSuites: - self.signature = p.getVarBytes(2) - p.stopLengthCheck() - return self - - def write(self, trial=False): - w = HandshakeMsg.preWrite(self, HandshakeType.server_key_exchange, - trial) - w.addVarSeq(numberToBytes(self.srp_N), 1, 2) - w.addVarSeq(numberToBytes(self.srp_g), 1, 2) - w.addVarSeq(self.srp_s, 1, 1) - w.addVarSeq(numberToBytes(self.srp_B), 1, 2) - if self.cipherSuite in CipherSuite.srpRsaSuites: - w.addVarSeq(self.signature, 1, 2) - return HandshakeMsg.postWrite(self, w, trial) - - def hash(self, clientRandom, serverRandom): - oldCipherSuite = self.cipherSuite - self.cipherSuite = None - try: - bytes = clientRandom + serverRandom + self.write()[4:] - s = bytesToString(bytes) - return stringToBytes(md5.md5(s).digest() + sha.sha(s).digest()) - finally: - self.cipherSuite = oldCipherSuite - -class ServerHelloDone(HandshakeMsg): - def __init__(self): - self.contentType = ContentType.handshake - - def create(self): - return self - - def parse(self, p): - p.startLengthCheck(3) - p.stopLengthCheck() - return self - - def write(self, trial=False): - w = HandshakeMsg.preWrite(self, HandshakeType.server_hello_done, trial) - return HandshakeMsg.postWrite(self, w, trial) - -class ClientKeyExchange(HandshakeMsg): - def __init__(self, cipherSuite, version=None): - self.cipherSuite = cipherSuite - self.version = version - self.contentType = ContentType.handshake - self.srp_A = 0 - self.encryptedPreMasterSecret = createByteArraySequence([]) - - def createSRP(self, srp_A): - self.srp_A = srp_A - return self - - def createRSA(self, encryptedPreMasterSecret): - self.encryptedPreMasterSecret = encryptedPreMasterSecret - return self - - def parse(self, p): - p.startLengthCheck(3) - if self.cipherSuite in CipherSuite.srpSuites + \ - CipherSuite.srpRsaSuites: - self.srp_A = bytesToNumber(p.getVarBytes(2)) - elif self.cipherSuite in CipherSuite.rsaSuites: - if self.version in ((3,1), (3,2)): - self.encryptedPreMasterSecret = p.getVarBytes(2) - elif self.version == (3,0): - self.encryptedPreMasterSecret = \ - p.getFixBytes(len(p.bytes)-p.index) - else: - raise AssertionError() - else: - raise AssertionError() - p.stopLengthCheck() - return self - - def write(self, trial=False): - w = HandshakeMsg.preWrite(self, HandshakeType.client_key_exchange, - trial) - if self.cipherSuite in CipherSuite.srpSuites + \ - CipherSuite.srpRsaSuites: - w.addVarSeq(numberToBytes(self.srp_A), 1, 2) - elif self.cipherSuite in CipherSuite.rsaSuites: - if self.version in ((3,1), (3,2)): - w.addVarSeq(self.encryptedPreMasterSecret, 1, 2) - elif self.version == (3,0): - w.addFixSeq(self.encryptedPreMasterSecret, 1) - else: - raise AssertionError() - else: - raise AssertionError() - return HandshakeMsg.postWrite(self, w, trial) - -class CertificateVerify(HandshakeMsg): - def __init__(self): - self.contentType = ContentType.handshake - self.signature = createByteArraySequence([]) - - def create(self, signature): - self.signature = signature - return self - - def parse(self, p): - p.startLengthCheck(3) - self.signature = p.getVarBytes(2) - p.stopLengthCheck() - return self - - def write(self, trial=False): - w = HandshakeMsg.preWrite(self, HandshakeType.certificate_verify, - trial) - w.addVarSeq(self.signature, 1, 2) - return HandshakeMsg.postWrite(self, w, trial) - -class ChangeCipherSpec(Msg): - def __init__(self): - self.contentType = ContentType.change_cipher_spec - self.type = 1 - - def create(self): - self.type = 1 - return self - - def parse(self, p): - p.setLengthCheck(1) - self.type = p.get(1) - p.stopLengthCheck() - return self - - def write(self, trial=False): - w = Msg.preWrite(self, trial) - w.add(self.type,1) - return Msg.postWrite(self, w, trial) - - -class Finished(HandshakeMsg): - def __init__(self, version): - self.contentType = ContentType.handshake - self.version = version - self.verify_data = createByteArraySequence([]) - - def create(self, verify_data): - self.verify_data = verify_data - return self - - def parse(self, p): - p.startLengthCheck(3) - if self.version == (3,0): - self.verify_data = p.getFixBytes(36) - elif self.version in ((3,1), (3,2)): - self.verify_data = p.getFixBytes(12) - else: - raise AssertionError() - p.stopLengthCheck() - return self - - def write(self, trial=False): - w = HandshakeMsg.preWrite(self, HandshakeType.finished, trial) - w.addFixSeq(self.verify_data, 1) - return HandshakeMsg.postWrite(self, w, trial) - -class ApplicationData(Msg): - def __init__(self): - self.contentType = ContentType.application_data - self.bytes = createByteArraySequence([]) - - def create(self, bytes): - self.bytes = bytes - return self - - def parse(self, p): - self.bytes = p.bytes - return self - - def write(self): - return self.bytes \ No newline at end of file diff --git a/gdata/analytics/tlslite/utils/AES.py b/gdata/analytics/tlslite/utils/AES.py deleted file mode 100644 index 8413f4c109..0000000000 --- a/gdata/analytics/tlslite/utils/AES.py +++ /dev/null @@ -1,31 +0,0 @@ -"""Abstract class for AES.""" - -class AES: - def __init__(self, key, mode, IV, implementation): - if len(key) not in (16, 24, 32): - raise AssertionError() - if mode != 2: - raise AssertionError() - if len(IV) != 16: - raise AssertionError() - self.isBlockCipher = True - self.block_size = 16 - self.implementation = implementation - if len(key)==16: - self.name = "aes128" - elif len(key)==24: - self.name = "aes192" - elif len(key)==32: - self.name = "aes256" - else: - raise AssertionError() - - #CBC-Mode encryption, returns ciphertext - #WARNING: *MAY* modify the input as well - def encrypt(self, plaintext): - assert(len(plaintext) % 16 == 0) - - #CBC-Mode decryption, returns plaintext - #WARNING: *MAY* modify the input as well - def decrypt(self, ciphertext): - assert(len(ciphertext) % 16 == 0) \ No newline at end of file diff --git a/gdata/analytics/tlslite/utils/ASN1Parser.py b/gdata/analytics/tlslite/utils/ASN1Parser.py deleted file mode 100644 index 16b50f29cd..0000000000 --- a/gdata/analytics/tlslite/utils/ASN1Parser.py +++ /dev/null @@ -1,34 +0,0 @@ -"""Class for parsing ASN.1""" -from compat import * -from codec import * - -#Takes a byte array which has a DER TLV field at its head -class ASN1Parser: - def __init__(self, bytes): - p = Parser(bytes) - p.get(1) #skip Type - - #Get Length - self.length = self._getASN1Length(p) - - #Get Value - self.value = p.getFixBytes(self.length) - - #Assuming this is a sequence... - def getChild(self, which): - p = Parser(self.value) - for x in range(which+1): - markIndex = p.index - p.get(1) #skip Type - length = self._getASN1Length(p) - p.getFixBytes(length) - return ASN1Parser(p.bytes[markIndex : p.index]) - - #Decode the ASN.1 DER length field - def _getASN1Length(self, p): - firstLength = p.get(1) - if firstLength<=127: - return firstLength - else: - lengthLength = firstLength & 0x7F - return p.get(lengthLength) diff --git a/gdata/analytics/tlslite/utils/Cryptlib_AES.py b/gdata/analytics/tlslite/utils/Cryptlib_AES.py deleted file mode 100644 index 9e101fc626..0000000000 --- a/gdata/analytics/tlslite/utils/Cryptlib_AES.py +++ /dev/null @@ -1,34 +0,0 @@ -"""Cryptlib AES implementation.""" - -from cryptomath import * -from AES import * - -if cryptlibpyLoaded: - - def new(key, mode, IV): - return Cryptlib_AES(key, mode, IV) - - class Cryptlib_AES(AES): - - def __init__(self, key, mode, IV): - AES.__init__(self, key, mode, IV, "cryptlib") - self.context = cryptlib_py.cryptCreateContext(cryptlib_py.CRYPT_UNUSED, cryptlib_py.CRYPT_ALGO_AES) - cryptlib_py.cryptSetAttribute(self.context, cryptlib_py.CRYPT_CTXINFO_MODE, cryptlib_py.CRYPT_MODE_CBC) - cryptlib_py.cryptSetAttribute(self.context, cryptlib_py.CRYPT_CTXINFO_KEYSIZE, len(key)) - cryptlib_py.cryptSetAttributeString(self.context, cryptlib_py.CRYPT_CTXINFO_KEY, key) - cryptlib_py.cryptSetAttributeString(self.context, cryptlib_py.CRYPT_CTXINFO_IV, IV) - - def __del__(self): - cryptlib_py.cryptDestroyContext(self.context) - - def encrypt(self, plaintext): - AES.encrypt(self, plaintext) - bytes = stringToBytes(plaintext) - cryptlib_py.cryptEncrypt(self.context, bytes) - return bytesToString(bytes) - - def decrypt(self, ciphertext): - AES.decrypt(self, ciphertext) - bytes = stringToBytes(ciphertext) - cryptlib_py.cryptDecrypt(self.context, bytes) - return bytesToString(bytes) diff --git a/gdata/analytics/tlslite/utils/Cryptlib_RC4.py b/gdata/analytics/tlslite/utils/Cryptlib_RC4.py deleted file mode 100644 index 7c6d087b8d..0000000000 --- a/gdata/analytics/tlslite/utils/Cryptlib_RC4.py +++ /dev/null @@ -1,28 +0,0 @@ -"""Cryptlib RC4 implementation.""" - -from cryptomath import * -from RC4 import RC4 - -if cryptlibpyLoaded: - - def new(key): - return Cryptlib_RC4(key) - - class Cryptlib_RC4(RC4): - - def __init__(self, key): - RC4.__init__(self, key, "cryptlib") - self.context = cryptlib_py.cryptCreateContext(cryptlib_py.CRYPT_UNUSED, cryptlib_py.CRYPT_ALGO_RC4) - cryptlib_py.cryptSetAttribute(self.context, cryptlib_py.CRYPT_CTXINFO_KEYSIZE, len(key)) - cryptlib_py.cryptSetAttributeString(self.context, cryptlib_py.CRYPT_CTXINFO_KEY, key) - - def __del__(self): - cryptlib_py.cryptDestroyContext(self.context) - - def encrypt(self, plaintext): - bytes = stringToBytes(plaintext) - cryptlib_py.cryptEncrypt(self.context, bytes) - return bytesToString(bytes) - - def decrypt(self, ciphertext): - return self.encrypt(ciphertext) \ No newline at end of file diff --git a/gdata/analytics/tlslite/utils/Cryptlib_TripleDES.py b/gdata/analytics/tlslite/utils/Cryptlib_TripleDES.py deleted file mode 100644 index a4f8155a09..0000000000 --- a/gdata/analytics/tlslite/utils/Cryptlib_TripleDES.py +++ /dev/null @@ -1,35 +0,0 @@ -"""Cryptlib 3DES implementation.""" - -from cryptomath import * - -from TripleDES import * - -if cryptlibpyLoaded: - - def new(key, mode, IV): - return Cryptlib_TripleDES(key, mode, IV) - - class Cryptlib_TripleDES(TripleDES): - - def __init__(self, key, mode, IV): - TripleDES.__init__(self, key, mode, IV, "cryptlib") - self.context = cryptlib_py.cryptCreateContext(cryptlib_py.CRYPT_UNUSED, cryptlib_py.CRYPT_ALGO_3DES) - cryptlib_py.cryptSetAttribute(self.context, cryptlib_py.CRYPT_CTXINFO_MODE, cryptlib_py.CRYPT_MODE_CBC) - cryptlib_py.cryptSetAttribute(self.context, cryptlib_py.CRYPT_CTXINFO_KEYSIZE, len(key)) - cryptlib_py.cryptSetAttributeString(self.context, cryptlib_py.CRYPT_CTXINFO_KEY, key) - cryptlib_py.cryptSetAttributeString(self.context, cryptlib_py.CRYPT_CTXINFO_IV, IV) - - def __del__(self): - cryptlib_py.cryptDestroyContext(self.context) - - def encrypt(self, plaintext): - TripleDES.encrypt(self, plaintext) - bytes = stringToBytes(plaintext) - cryptlib_py.cryptEncrypt(self.context, bytes) - return bytesToString(bytes) - - def decrypt(self, ciphertext): - TripleDES.decrypt(self, ciphertext) - bytes = stringToBytes(ciphertext) - cryptlib_py.cryptDecrypt(self.context, bytes) - return bytesToString(bytes) \ No newline at end of file diff --git a/gdata/analytics/tlslite/utils/OpenSSL_AES.py b/gdata/analytics/tlslite/utils/OpenSSL_AES.py deleted file mode 100644 index e60679bf50..0000000000 --- a/gdata/analytics/tlslite/utils/OpenSSL_AES.py +++ /dev/null @@ -1,49 +0,0 @@ -"""OpenSSL/M2Crypto AES implementation.""" - -from cryptomath import * -from AES import * - -if m2cryptoLoaded: - - def new(key, mode, IV): - return OpenSSL_AES(key, mode, IV) - - class OpenSSL_AES(AES): - - def __init__(self, key, mode, IV): - AES.__init__(self, key, mode, IV, "openssl") - self.key = key - self.IV = IV - - def _createContext(self, encrypt): - context = m2.cipher_ctx_new() - if len(self.key)==16: - cipherType = m2.aes_128_cbc() - if len(self.key)==24: - cipherType = m2.aes_192_cbc() - if len(self.key)==32: - cipherType = m2.aes_256_cbc() - m2.cipher_init(context, cipherType, self.key, self.IV, encrypt) - return context - - def encrypt(self, plaintext): - AES.encrypt(self, plaintext) - context = self._createContext(1) - ciphertext = m2.cipher_update(context, plaintext) - m2.cipher_ctx_free(context) - self.IV = ciphertext[-self.block_size:] - return ciphertext - - def decrypt(self, ciphertext): - AES.decrypt(self, ciphertext) - context = self._createContext(0) - #I think M2Crypto has a bug - it fails to decrypt and return the last block passed in. - #To work around this, we append sixteen zeros to the string, below: - plaintext = m2.cipher_update(context, ciphertext+('\0'*16)) - - #If this bug is ever fixed, then plaintext will end up having a garbage - #plaintext block on the end. That's okay - the below code will discard it. - plaintext = plaintext[:len(ciphertext)] - m2.cipher_ctx_free(context) - self.IV = ciphertext[-self.block_size:] - return plaintext diff --git a/gdata/analytics/tlslite/utils/OpenSSL_RC4.py b/gdata/analytics/tlslite/utils/OpenSSL_RC4.py deleted file mode 100644 index ac433aad76..0000000000 --- a/gdata/analytics/tlslite/utils/OpenSSL_RC4.py +++ /dev/null @@ -1,25 +0,0 @@ -"""OpenSSL/M2Crypto RC4 implementation.""" - -from cryptomath import * -from RC4 import RC4 - -if m2cryptoLoaded: - - def new(key): - return OpenSSL_RC4(key) - - class OpenSSL_RC4(RC4): - - def __init__(self, key): - RC4.__init__(self, key, "openssl") - self.rc4 = m2.rc4_new() - m2.rc4_set_key(self.rc4, key) - - def __del__(self): - m2.rc4_free(self.rc4) - - def encrypt(self, plaintext): - return m2.rc4_update(self.rc4, plaintext) - - def decrypt(self, ciphertext): - return self.encrypt(ciphertext) diff --git a/gdata/analytics/tlslite/utils/OpenSSL_RSAKey.py b/gdata/analytics/tlslite/utils/OpenSSL_RSAKey.py deleted file mode 100644 index fe1a3cd74d..0000000000 --- a/gdata/analytics/tlslite/utils/OpenSSL_RSAKey.py +++ /dev/null @@ -1,148 +0,0 @@ -"""OpenSSL/M2Crypto RSA implementation.""" - -from cryptomath import * - -from RSAKey import * -from Python_RSAKey import Python_RSAKey - -#copied from M2Crypto.util.py, so when we load the local copy of m2 -#we can still use it -def password_callback(v, prompt1='Enter private key passphrase:', - prompt2='Verify passphrase:'): - from getpass import getpass - while 1: - try: - p1=getpass(prompt1) - if v: - p2=getpass(prompt2) - if p1==p2: - break - else: - break - except KeyboardInterrupt: - return None - return p1 - - -if m2cryptoLoaded: - class OpenSSL_RSAKey(RSAKey): - def __init__(self, n=0, e=0): - self.rsa = None - self._hasPrivateKey = False - if (n and not e) or (e and not n): - raise AssertionError() - if n and e: - self.rsa = m2.rsa_new() - m2.rsa_set_n(self.rsa, numberToMPI(n)) - m2.rsa_set_e(self.rsa, numberToMPI(e)) - - def __del__(self): - if self.rsa: - m2.rsa_free(self.rsa) - - def __getattr__(self, name): - if name == 'e': - if not self.rsa: - return 0 - return mpiToNumber(m2.rsa_get_e(self.rsa)) - elif name == 'n': - if not self.rsa: - return 0 - return mpiToNumber(m2.rsa_get_n(self.rsa)) - else: - raise AttributeError - - def hasPrivateKey(self): - return self._hasPrivateKey - - def hash(self): - return Python_RSAKey(self.n, self.e).hash() - - def _rawPrivateKeyOp(self, m): - s = numberToString(m) - byteLength = numBytes(self.n) - if len(s)== byteLength: - pass - elif len(s) == byteLength-1: - s = '\0' + s - else: - raise AssertionError() - c = stringToNumber(m2.rsa_private_encrypt(self.rsa, s, - m2.no_padding)) - return c - - def _rawPublicKeyOp(self, c): - s = numberToString(c) - byteLength = numBytes(self.n) - if len(s)== byteLength: - pass - elif len(s) == byteLength-1: - s = '\0' + s - else: - raise AssertionError() - m = stringToNumber(m2.rsa_public_decrypt(self.rsa, s, - m2.no_padding)) - return m - - def acceptsPassword(self): return True - - def write(self, password=None): - bio = m2.bio_new(m2.bio_s_mem()) - if self._hasPrivateKey: - if password: - def f(v): return password - m2.rsa_write_key(self.rsa, bio, m2.des_ede_cbc(), f) - else: - def f(): pass - m2.rsa_write_key_no_cipher(self.rsa, bio, f) - else: - if password: - raise AssertionError() - m2.rsa_write_pub_key(self.rsa, bio) - s = m2.bio_read(bio, m2.bio_ctrl_pending(bio)) - m2.bio_free(bio) - return s - - def writeXMLPublicKey(self, indent=''): - return Python_RSAKey(self.n, self.e).write(indent) - - def generate(bits): - key = OpenSSL_RSAKey() - def f():pass - key.rsa = m2.rsa_generate_key(bits, 3, f) - key._hasPrivateKey = True - return key - generate = staticmethod(generate) - - def parse(s, passwordCallback=None): - if s.startswith("-----BEGIN "): - if passwordCallback==None: - callback = password_callback - else: - def f(v, prompt1=None, prompt2=None): - return passwordCallback() - callback = f - bio = m2.bio_new(m2.bio_s_mem()) - try: - m2.bio_write(bio, s) - key = OpenSSL_RSAKey() - if s.startswith("-----BEGIN RSA PRIVATE KEY-----"): - def f():pass - key.rsa = m2.rsa_read_key(bio, callback) - if key.rsa == None: - raise SyntaxError() - key._hasPrivateKey = True - elif s.startswith("-----BEGIN PUBLIC KEY-----"): - key.rsa = m2.rsa_read_pub_key(bio) - if key.rsa == None: - raise SyntaxError() - key._hasPrivateKey = False - else: - raise SyntaxError() - return key - finally: - m2.bio_free(bio) - else: - raise SyntaxError() - - parse = staticmethod(parse) diff --git a/gdata/analytics/tlslite/utils/OpenSSL_TripleDES.py b/gdata/analytics/tlslite/utils/OpenSSL_TripleDES.py deleted file mode 100644 index f5ba165652..0000000000 --- a/gdata/analytics/tlslite/utils/OpenSSL_TripleDES.py +++ /dev/null @@ -1,44 +0,0 @@ -"""OpenSSL/M2Crypto 3DES implementation.""" - -from cryptomath import * -from TripleDES import * - -if m2cryptoLoaded: - - def new(key, mode, IV): - return OpenSSL_TripleDES(key, mode, IV) - - class OpenSSL_TripleDES(TripleDES): - - def __init__(self, key, mode, IV): - TripleDES.__init__(self, key, mode, IV, "openssl") - self.key = key - self.IV = IV - - def _createContext(self, encrypt): - context = m2.cipher_ctx_new() - cipherType = m2.des_ede3_cbc() - m2.cipher_init(context, cipherType, self.key, self.IV, encrypt) - return context - - def encrypt(self, plaintext): - TripleDES.encrypt(self, plaintext) - context = self._createContext(1) - ciphertext = m2.cipher_update(context, plaintext) - m2.cipher_ctx_free(context) - self.IV = ciphertext[-self.block_size:] - return ciphertext - - def decrypt(self, ciphertext): - TripleDES.decrypt(self, ciphertext) - context = self._createContext(0) - #I think M2Crypto has a bug - it fails to decrypt and return the last block passed in. - #To work around this, we append sixteen zeros to the string, below: - plaintext = m2.cipher_update(context, ciphertext+('\0'*16)) - - #If this bug is ever fixed, then plaintext will end up having a garbage - #plaintext block on the end. That's okay - the below code will ignore it. - plaintext = plaintext[:len(ciphertext)] - m2.cipher_ctx_free(context) - self.IV = ciphertext[-self.block_size:] - return plaintext \ No newline at end of file diff --git a/gdata/analytics/tlslite/utils/PyCrypto_AES.py b/gdata/analytics/tlslite/utils/PyCrypto_AES.py deleted file mode 100644 index e38b19d6fb..0000000000 --- a/gdata/analytics/tlslite/utils/PyCrypto_AES.py +++ /dev/null @@ -1,22 +0,0 @@ -"""PyCrypto AES implementation.""" - -from cryptomath import * -from AES import * - -if pycryptoLoaded: - import Crypto.Cipher.AES - - def new(key, mode, IV): - return PyCrypto_AES(key, mode, IV) - - class PyCrypto_AES(AES): - - def __init__(self, key, mode, IV): - AES.__init__(self, key, mode, IV, "pycrypto") - self.context = Crypto.Cipher.AES.new(key, mode, IV) - - def encrypt(self, plaintext): - return self.context.encrypt(plaintext) - - def decrypt(self, ciphertext): - return self.context.decrypt(ciphertext) \ No newline at end of file diff --git a/gdata/analytics/tlslite/utils/PyCrypto_RC4.py b/gdata/analytics/tlslite/utils/PyCrypto_RC4.py deleted file mode 100644 index 6c6d86afde..0000000000 --- a/gdata/analytics/tlslite/utils/PyCrypto_RC4.py +++ /dev/null @@ -1,22 +0,0 @@ -"""PyCrypto RC4 implementation.""" - -from cryptomath import * -from RC4 import * - -if pycryptoLoaded: - import Crypto.Cipher.ARC4 - - def new(key): - return PyCrypto_RC4(key) - - class PyCrypto_RC4(RC4): - - def __init__(self, key): - RC4.__init__(self, key, "pycrypto") - self.context = Crypto.Cipher.ARC4.new(key) - - def encrypt(self, plaintext): - return self.context.encrypt(plaintext) - - def decrypt(self, ciphertext): - return self.context.decrypt(ciphertext) \ No newline at end of file diff --git a/gdata/analytics/tlslite/utils/PyCrypto_RSAKey.py b/gdata/analytics/tlslite/utils/PyCrypto_RSAKey.py deleted file mode 100644 index 48b5cef03f..0000000000 --- a/gdata/analytics/tlslite/utils/PyCrypto_RSAKey.py +++ /dev/null @@ -1,61 +0,0 @@ -"""PyCrypto RSA implementation.""" - -from cryptomath import * - -from RSAKey import * -from Python_RSAKey import Python_RSAKey - -if pycryptoLoaded: - - from Crypto.PublicKey import RSA - - class PyCrypto_RSAKey(RSAKey): - def __init__(self, n=0, e=0, d=0, p=0, q=0, dP=0, dQ=0, qInv=0): - if not d: - self.rsa = RSA.construct( (n, e) ) - else: - self.rsa = RSA.construct( (n, e, d, p, q) ) - - def __getattr__(self, name): - return getattr(self.rsa, name) - - def hasPrivateKey(self): - return self.rsa.has_private() - - def hash(self): - return Python_RSAKey(self.n, self.e).hash() - - def _rawPrivateKeyOp(self, m): - s = numberToString(m) - byteLength = numBytes(self.n) - if len(s)== byteLength: - pass - elif len(s) == byteLength-1: - s = '\0' + s - else: - raise AssertionError() - c = stringToNumber(self.rsa.decrypt((s,))) - return c - - def _rawPublicKeyOp(self, c): - s = numberToString(c) - byteLength = numBytes(self.n) - if len(s)== byteLength: - pass - elif len(s) == byteLength-1: - s = '\0' + s - else: - raise AssertionError() - m = stringToNumber(self.rsa.encrypt(s, None)[0]) - return m - - def writeXMLPublicKey(self, indent=''): - return Python_RSAKey(self.n, self.e).write(indent) - - def generate(bits): - key = PyCrypto_RSAKey() - def f(numBytes): - return bytesToString(getRandomBytes(numBytes)) - key.rsa = RSA.generate(bits, f) - return key - generate = staticmethod(generate) diff --git a/gdata/analytics/tlslite/utils/PyCrypto_TripleDES.py b/gdata/analytics/tlslite/utils/PyCrypto_TripleDES.py deleted file mode 100644 index 8c22bb80a5..0000000000 --- a/gdata/analytics/tlslite/utils/PyCrypto_TripleDES.py +++ /dev/null @@ -1,22 +0,0 @@ -"""PyCrypto 3DES implementation.""" - -from cryptomath import * -from TripleDES import * - -if pycryptoLoaded: - import Crypto.Cipher.DES3 - - def new(key, mode, IV): - return PyCrypto_TripleDES(key, mode, IV) - - class PyCrypto_TripleDES(TripleDES): - - def __init__(self, key, mode, IV): - TripleDES.__init__(self, key, mode, IV, "pycrypto") - self.context = Crypto.Cipher.DES3.new(key, mode, IV) - - def encrypt(self, plaintext): - return self.context.encrypt(plaintext) - - def decrypt(self, ciphertext): - return self.context.decrypt(ciphertext) \ No newline at end of file diff --git a/gdata/analytics/tlslite/utils/Python_AES.py b/gdata/analytics/tlslite/utils/Python_AES.py deleted file mode 100644 index 657152f892..0000000000 --- a/gdata/analytics/tlslite/utils/Python_AES.py +++ /dev/null @@ -1,68 +0,0 @@ -"""Pure-Python AES implementation.""" - -from cryptomath import * - -from AES import * -from rijndael import rijndael - -def new(key, mode, IV): - return Python_AES(key, mode, IV) - -class Python_AES(AES): - def __init__(self, key, mode, IV): - AES.__init__(self, key, mode, IV, "python") - self.rijndael = rijndael(key, 16) - self.IV = IV - - def encrypt(self, plaintext): - AES.encrypt(self, plaintext) - - plaintextBytes = stringToBytes(plaintext) - chainBytes = stringToBytes(self.IV) - - #CBC Mode: For each block... - for x in range(len(plaintextBytes)/16): - - #XOR with the chaining block - blockBytes = plaintextBytes[x*16 : (x*16)+16] - for y in range(16): - blockBytes[y] ^= chainBytes[y] - blockString = bytesToString(blockBytes) - - #Encrypt it - encryptedBytes = stringToBytes(self.rijndael.encrypt(blockString)) - - #Overwrite the input with the output - for y in range(16): - plaintextBytes[(x*16)+y] = encryptedBytes[y] - - #Set the next chaining block - chainBytes = encryptedBytes - - self.IV = bytesToString(chainBytes) - return bytesToString(plaintextBytes) - - def decrypt(self, ciphertext): - AES.decrypt(self, ciphertext) - - ciphertextBytes = stringToBytes(ciphertext) - chainBytes = stringToBytes(self.IV) - - #CBC Mode: For each block... - for x in range(len(ciphertextBytes)/16): - - #Decrypt it - blockBytes = ciphertextBytes[x*16 : (x*16)+16] - blockString = bytesToString(blockBytes) - decryptedBytes = stringToBytes(self.rijndael.decrypt(blockString)) - - #XOR with the chaining block and overwrite the input with output - for y in range(16): - decryptedBytes[y] ^= chainBytes[y] - ciphertextBytes[(x*16)+y] = decryptedBytes[y] - - #Set the next chaining block - chainBytes = blockBytes - - self.IV = bytesToString(chainBytes) - return bytesToString(ciphertextBytes) diff --git a/gdata/analytics/tlslite/utils/Python_RC4.py b/gdata/analytics/tlslite/utils/Python_RC4.py deleted file mode 100644 index 56ce5fb2fc..0000000000 --- a/gdata/analytics/tlslite/utils/Python_RC4.py +++ /dev/null @@ -1,39 +0,0 @@ -"""Pure-Python RC4 implementation.""" - -from RC4 import RC4 -from cryptomath import * - -def new(key): - return Python_RC4(key) - -class Python_RC4(RC4): - def __init__(self, key): - RC4.__init__(self, key, "python") - keyBytes = stringToBytes(key) - S = [i for i in range(256)] - j = 0 - for i in range(256): - j = (j + S[i] + keyBytes[i % len(keyBytes)]) % 256 - S[i], S[j] = S[j], S[i] - - self.S = S - self.i = 0 - self.j = 0 - - def encrypt(self, plaintext): - plaintextBytes = stringToBytes(plaintext) - S = self.S - i = self.i - j = self.j - for x in range(len(plaintextBytes)): - i = (i + 1) % 256 - j = (j + S[i]) % 256 - S[i], S[j] = S[j], S[i] - t = (S[i] + S[j]) % 256 - plaintextBytes[x] ^= S[t] - self.i = i - self.j = j - return bytesToString(plaintextBytes) - - def decrypt(self, ciphertext): - return self.encrypt(ciphertext) diff --git a/gdata/analytics/tlslite/utils/Python_RSAKey.py b/gdata/analytics/tlslite/utils/Python_RSAKey.py deleted file mode 100644 index 2c469b572c..0000000000 --- a/gdata/analytics/tlslite/utils/Python_RSAKey.py +++ /dev/null @@ -1,209 +0,0 @@ -"""Pure-Python RSA implementation.""" - -from cryptomath import * -import xmltools -from ASN1Parser import ASN1Parser -from RSAKey import * - -class Python_RSAKey(RSAKey): - def __init__(self, n=0, e=0, d=0, p=0, q=0, dP=0, dQ=0, qInv=0): - if (n and not e) or (e and not n): - raise AssertionError() - self.n = n - self.e = e - self.d = d - self.p = p - self.q = q - self.dP = dP - self.dQ = dQ - self.qInv = qInv - self.blinder = 0 - self.unblinder = 0 - - def hasPrivateKey(self): - return self.d != 0 - - def hash(self): - s = self.writeXMLPublicKey('\t\t') - return hashAndBase64(s.strip()) - - def _rawPrivateKeyOp(self, m): - #Create blinding values, on the first pass: - if not self.blinder: - self.unblinder = getRandomNumber(2, self.n) - self.blinder = powMod(invMod(self.unblinder, self.n), self.e, - self.n) - - #Blind the input - m = (m * self.blinder) % self.n - - #Perform the RSA operation - c = self._rawPrivateKeyOpHelper(m) - - #Unblind the output - c = (c * self.unblinder) % self.n - - #Update blinding values - self.blinder = (self.blinder * self.blinder) % self.n - self.unblinder = (self.unblinder * self.unblinder) % self.n - - #Return the output - return c - - - def _rawPrivateKeyOpHelper(self, m): - #Non-CRT version - #c = powMod(m, self.d, self.n) - - #CRT version (~3x faster) - s1 = powMod(m, self.dP, self.p) - s2 = powMod(m, self.dQ, self.q) - h = ((s1 - s2) * self.qInv) % self.p - c = s2 + self.q * h - return c - - def _rawPublicKeyOp(self, c): - m = powMod(c, self.e, self.n) - return m - - def acceptsPassword(self): return False - - def write(self, indent=''): - if self.d: - s = indent+'\n' - else: - s = indent+'\n' - s += indent+'\t%s\n' % numberToBase64(self.n) - s += indent+'\t%s\n' % numberToBase64(self.e) - if self.d: - s += indent+'\t%s\n' % numberToBase64(self.d) - s += indent+'\t

%s

\n' % numberToBase64(self.p) - s += indent+'\t%s\n' % numberToBase64(self.q) - s += indent+'\t%s\n' % numberToBase64(self.dP) - s += indent+'\t%s\n' % numberToBase64(self.dQ) - s += indent+'\t%s\n' % numberToBase64(self.qInv) - s += indent+'
' - else: - s += indent+'' - #Only add \n if part of a larger structure - if indent != '': - s += '\n' - return s - - def writeXMLPublicKey(self, indent=''): - return Python_RSAKey(self.n, self.e).write(indent) - - def generate(bits): - key = Python_RSAKey() - p = getRandomPrime(bits/2, False) - q = getRandomPrime(bits/2, False) - t = lcm(p-1, q-1) - key.n = p * q - key.e = 3L #Needed to be long, for Java - key.d = invMod(key.e, t) - key.p = p - key.q = q - key.dP = key.d % (p-1) - key.dQ = key.d % (q-1) - key.qInv = invMod(q, p) - return key - generate = staticmethod(generate) - - def parsePEM(s, passwordCallback=None): - """Parse a string containing a or , or - PEM-encoded key.""" - - start = s.find("-----BEGIN PRIVATE KEY-----") - if start != -1: - end = s.find("-----END PRIVATE KEY-----") - if end == -1: - raise SyntaxError("Missing PEM Postfix") - s = s[start+len("-----BEGIN PRIVATE KEY -----") : end] - bytes = base64ToBytes(s) - return Python_RSAKey._parsePKCS8(bytes) - else: - start = s.find("-----BEGIN RSA PRIVATE KEY-----") - if start != -1: - end = s.find("-----END RSA PRIVATE KEY-----") - if end == -1: - raise SyntaxError("Missing PEM Postfix") - s = s[start+len("-----BEGIN RSA PRIVATE KEY -----") : end] - bytes = base64ToBytes(s) - return Python_RSAKey._parseSSLeay(bytes) - raise SyntaxError("Missing PEM Prefix") - parsePEM = staticmethod(parsePEM) - - def parseXML(s): - element = xmltools.parseAndStripWhitespace(s) - return Python_RSAKey._parseXML(element) - parseXML = staticmethod(parseXML) - - def _parsePKCS8(bytes): - p = ASN1Parser(bytes) - - version = p.getChild(0).value[0] - if version != 0: - raise SyntaxError("Unrecognized PKCS8 version") - - rsaOID = p.getChild(1).value - if list(rsaOID) != [6, 9, 42, 134, 72, 134, 247, 13, 1, 1, 1, 5, 0]: - raise SyntaxError("Unrecognized AlgorithmIdentifier") - - #Get the privateKey - privateKeyP = p.getChild(2) - - #Adjust for OCTET STRING encapsulation - privateKeyP = ASN1Parser(privateKeyP.value) - - return Python_RSAKey._parseASN1PrivateKey(privateKeyP) - _parsePKCS8 = staticmethod(_parsePKCS8) - - def _parseSSLeay(bytes): - privateKeyP = ASN1Parser(bytes) - return Python_RSAKey._parseASN1PrivateKey(privateKeyP) - _parseSSLeay = staticmethod(_parseSSLeay) - - def _parseASN1PrivateKey(privateKeyP): - version = privateKeyP.getChild(0).value[0] - if version != 0: - raise SyntaxError("Unrecognized RSAPrivateKey version") - n = bytesToNumber(privateKeyP.getChild(1).value) - e = bytesToNumber(privateKeyP.getChild(2).value) - d = bytesToNumber(privateKeyP.getChild(3).value) - p = bytesToNumber(privateKeyP.getChild(4).value) - q = bytesToNumber(privateKeyP.getChild(5).value) - dP = bytesToNumber(privateKeyP.getChild(6).value) - dQ = bytesToNumber(privateKeyP.getChild(7).value) - qInv = bytesToNumber(privateKeyP.getChild(8).value) - return Python_RSAKey(n, e, d, p, q, dP, dQ, qInv) - _parseASN1PrivateKey = staticmethod(_parseASN1PrivateKey) - - def _parseXML(element): - try: - xmltools.checkName(element, "privateKey") - except SyntaxError: - xmltools.checkName(element, "publicKey") - - #Parse attributes - xmltools.getReqAttribute(element, "xmlns", "http://trevp.net/rsa\Z") - xmltools.checkNoMoreAttributes(element) - - #Parse public values ( and ) - n = base64ToNumber(xmltools.getText(xmltools.getChild(element, 0, "n"), xmltools.base64RegEx)) - e = base64ToNumber(xmltools.getText(xmltools.getChild(element, 1, "e"), xmltools.base64RegEx)) - d = 0 - p = 0 - q = 0 - dP = 0 - dQ = 0 - qInv = 0 - #Parse private values, if present - if element.childNodes.length>=3: - d = base64ToNumber(xmltools.getText(xmltools.getChild(element, 2, "d"), xmltools.base64RegEx)) - p = base64ToNumber(xmltools.getText(xmltools.getChild(element, 3, "p"), xmltools.base64RegEx)) - q = base64ToNumber(xmltools.getText(xmltools.getChild(element, 4, "q"), xmltools.base64RegEx)) - dP = base64ToNumber(xmltools.getText(xmltools.getChild(element, 5, "dP"), xmltools.base64RegEx)) - dQ = base64ToNumber(xmltools.getText(xmltools.getChild(element, 6, "dQ"), xmltools.base64RegEx)) - qInv = base64ToNumber(xmltools.getText(xmltools.getLastChild(element, 7, "qInv"), xmltools.base64RegEx)) - return Python_RSAKey(n, e, d, p, q, dP, dQ, qInv) - _parseXML = staticmethod(_parseXML) diff --git a/gdata/analytics/tlslite/utils/RC4.py b/gdata/analytics/tlslite/utils/RC4.py deleted file mode 100644 index 550692327c..0000000000 --- a/gdata/analytics/tlslite/utils/RC4.py +++ /dev/null @@ -1,17 +0,0 @@ -"""Abstract class for RC4.""" - -from compat import * #For False - -class RC4: - def __init__(self, keyBytes, implementation): - if len(keyBytes) < 16 or len(keyBytes) > 256: - raise ValueError() - self.isBlockCipher = False - self.name = "rc4" - self.implementation = implementation - - def encrypt(self, plaintext): - raise NotImplementedError() - - def decrypt(self, ciphertext): - raise NotImplementedError() \ No newline at end of file diff --git a/gdata/analytics/tlslite/utils/RSAKey.py b/gdata/analytics/tlslite/utils/RSAKey.py deleted file mode 100644 index 2f5d28680a..0000000000 --- a/gdata/analytics/tlslite/utils/RSAKey.py +++ /dev/null @@ -1,264 +0,0 @@ -"""Abstract class for RSA.""" - -from cryptomath import * - - -class RSAKey: - """This is an abstract base class for RSA keys. - - Particular implementations of RSA keys, such as - L{OpenSSL_RSAKey.OpenSSL_RSAKey}, - L{Python_RSAKey.Python_RSAKey}, and - L{PyCrypto_RSAKey.PyCrypto_RSAKey}, - inherit from this. - - To create or parse an RSA key, don't use one of these classes - directly. Instead, use the factory functions in - L{tlslite.utils.keyfactory}. - """ - - def __init__(self, n=0, e=0): - """Create a new RSA key. - - If n and e are passed in, the new key will be initialized. - - @type n: int - @param n: RSA modulus. - - @type e: int - @param e: RSA public exponent. - """ - raise NotImplementedError() - - def __len__(self): - """Return the length of this key in bits. - - @rtype: int - """ - return numBits(self.n) - - def hasPrivateKey(self): - """Return whether or not this key has a private component. - - @rtype: bool - """ - raise NotImplementedError() - - def hash(self): - """Return the cryptoID value corresponding to this - key. - - @rtype: str - """ - raise NotImplementedError() - - def getSigningAlgorithm(self): - """Return the cryptoID sigAlgo value corresponding to this key. - - @rtype: str - """ - return "pkcs1-sha1" - - def hashAndSign(self, bytes): - """Hash and sign the passed-in bytes. - - This requires the key to have a private component. It performs - a PKCS1-SHA1 signature on the passed-in data. - - @type bytes: str or L{array.array} of unsigned bytes - @param bytes: The value which will be hashed and signed. - - @rtype: L{array.array} of unsigned bytes. - @return: A PKCS1-SHA1 signature on the passed-in data. - """ - if not isinstance(bytes, type("")): - bytes = bytesToString(bytes) - hashBytes = stringToBytes(sha1(bytes).digest()) - prefixedHashBytes = self._addPKCS1SHA1Prefix(hashBytes) - sigBytes = self.sign(prefixedHashBytes) - return sigBytes - - def hashAndVerify(self, sigBytes, bytes): - """Hash and verify the passed-in bytes with the signature. - - This verifies a PKCS1-SHA1 signature on the passed-in data. - - @type sigBytes: L{array.array} of unsigned bytes - @param sigBytes: A PKCS1-SHA1 signature. - - @type bytes: str or L{array.array} of unsigned bytes - @param bytes: The value which will be hashed and verified. - - @rtype: bool - @return: Whether the signature matches the passed-in data. - """ - if not isinstance(bytes, type("")): - bytes = bytesToString(bytes) - hashBytes = stringToBytes(sha1(bytes).digest()) - prefixedHashBytes = self._addPKCS1SHA1Prefix(hashBytes) - return self.verify(sigBytes, prefixedHashBytes) - - def sign(self, bytes): - """Sign the passed-in bytes. - - This requires the key to have a private component. It performs - a PKCS1 signature on the passed-in data. - - @type bytes: L{array.array} of unsigned bytes - @param bytes: The value which will be signed. - - @rtype: L{array.array} of unsigned bytes. - @return: A PKCS1 signature on the passed-in data. - """ - if not self.hasPrivateKey(): - raise AssertionError() - paddedBytes = self._addPKCS1Padding(bytes, 1) - m = bytesToNumber(paddedBytes) - if m >= self.n: - raise ValueError() - c = self._rawPrivateKeyOp(m) - sigBytes = numberToBytes(c) - return sigBytes - - def verify(self, sigBytes, bytes): - """Verify the passed-in bytes with the signature. - - This verifies a PKCS1 signature on the passed-in data. - - @type sigBytes: L{array.array} of unsigned bytes - @param sigBytes: A PKCS1 signature. - - @type bytes: L{array.array} of unsigned bytes - @param bytes: The value which will be verified. - - @rtype: bool - @return: Whether the signature matches the passed-in data. - """ - paddedBytes = self._addPKCS1Padding(bytes, 1) - c = bytesToNumber(sigBytes) - if c >= self.n: - return False - m = self._rawPublicKeyOp(c) - checkBytes = numberToBytes(m) - return checkBytes == paddedBytes - - def encrypt(self, bytes): - """Encrypt the passed-in bytes. - - This performs PKCS1 encryption of the passed-in data. - - @type bytes: L{array.array} of unsigned bytes - @param bytes: The value which will be encrypted. - - @rtype: L{array.array} of unsigned bytes. - @return: A PKCS1 encryption of the passed-in data. - """ - paddedBytes = self._addPKCS1Padding(bytes, 2) - m = bytesToNumber(paddedBytes) - if m >= self.n: - raise ValueError() - c = self._rawPublicKeyOp(m) - encBytes = numberToBytes(c) - return encBytes - - def decrypt(self, encBytes): - """Decrypt the passed-in bytes. - - This requires the key to have a private component. It performs - PKCS1 decryption of the passed-in data. - - @type encBytes: L{array.array} of unsigned bytes - @param encBytes: The value which will be decrypted. - - @rtype: L{array.array} of unsigned bytes or None. - @return: A PKCS1 decryption of the passed-in data or None if - the data is not properly formatted. - """ - if not self.hasPrivateKey(): - raise AssertionError() - c = bytesToNumber(encBytes) - if c >= self.n: - return None - m = self._rawPrivateKeyOp(c) - decBytes = numberToBytes(m) - if (len(decBytes) != numBytes(self.n)-1): #Check first byte - return None - if decBytes[0] != 2: #Check second byte - return None - for x in range(len(decBytes)-1): #Scan through for zero separator - if decBytes[x]== 0: - break - else: - return None - return decBytes[x+1:] #Return everything after the separator - - def _rawPrivateKeyOp(self, m): - raise NotImplementedError() - - def _rawPublicKeyOp(self, c): - raise NotImplementedError() - - def acceptsPassword(self): - """Return True if the write() method accepts a password for use - in encrypting the private key. - - @rtype: bool - """ - raise NotImplementedError() - - def write(self, password=None): - """Return a string containing the key. - - @rtype: str - @return: A string describing the key, in whichever format (PEM - or XML) is native to the implementation. - """ - raise NotImplementedError() - - def writeXMLPublicKey(self, indent=''): - """Return a string containing the key. - - @rtype: str - @return: A string describing the public key, in XML format. - """ - return Python_RSAKey(self.n, self.e).write(indent) - - def generate(bits): - """Generate a new key with the specified bit length. - - @rtype: L{tlslite.utils.RSAKey.RSAKey} - """ - raise NotImplementedError() - generate = staticmethod(generate) - - - # ************************************************************************** - # Helper Functions for RSA Keys - # ************************************************************************** - - def _addPKCS1SHA1Prefix(self, bytes): - prefixBytes = createByteArraySequence(\ - [48,33,48,9,6,5,43,14,3,2,26,5,0,4,20]) - prefixedBytes = prefixBytes + bytes - return prefixedBytes - - def _addPKCS1Padding(self, bytes, blockType): - padLength = (numBytes(self.n) - (len(bytes)+3)) - if blockType == 1: #Signature padding - pad = [0xFF] * padLength - elif blockType == 2: #Encryption padding - pad = createByteArraySequence([]) - while len(pad) < padLength: - padBytes = getRandomBytes(padLength * 2) - pad = [b for b in padBytes if b != 0] - pad = pad[:padLength] - else: - raise AssertionError() - - #NOTE: To be proper, we should add [0,blockType]. However, - #the zero is lost when the returned padding is converted - #to a number, so we don't even bother with it. Also, - #adding it would cause a misalignment in verify() - padding = createByteArraySequence([blockType] + pad + [0]) - paddedBytes = padding + bytes - return paddedBytes diff --git a/gdata/analytics/tlslite/utils/TripleDES.py b/gdata/analytics/tlslite/utils/TripleDES.py deleted file mode 100644 index 2db45888bd..0000000000 --- a/gdata/analytics/tlslite/utils/TripleDES.py +++ /dev/null @@ -1,26 +0,0 @@ -"""Abstract class for 3DES.""" - -from compat import * #For True - -class TripleDES: - def __init__(self, key, mode, IV, implementation): - if len(key) != 24: - raise ValueError() - if mode != 2: - raise ValueError() - if len(IV) != 8: - raise ValueError() - self.isBlockCipher = True - self.block_size = 8 - self.implementation = implementation - self.name = "3des" - - #CBC-Mode encryption, returns ciphertext - #WARNING: *MAY* modify the input as well - def encrypt(self, plaintext): - assert(len(plaintext) % 8 == 0) - - #CBC-Mode decryption, returns plaintext - #WARNING: *MAY* modify the input as well - def decrypt(self, ciphertext): - assert(len(ciphertext) % 8 == 0) diff --git a/gdata/analytics/tlslite/utils/__init__.py b/gdata/analytics/tlslite/utils/__init__.py deleted file mode 100644 index e96b4bef8a..0000000000 --- a/gdata/analytics/tlslite/utils/__init__.py +++ /dev/null @@ -1,31 +0,0 @@ -"""Toolkit for crypto and other stuff.""" - -__all__ = ["AES", - "ASN1Parser", - "cipherfactory", - "codec", - "Cryptlib_AES", - "Cryptlib_RC4", - "Cryptlib_TripleDES", - "cryptomath: cryptomath module", - "dateFuncs", - "hmac", - "JCE_RSAKey", - "compat", - "keyfactory", - "OpenSSL_AES", - "OpenSSL_RC4", - "OpenSSL_RSAKey", - "OpenSSL_TripleDES", - "PyCrypto_AES", - "PyCrypto_RC4", - "PyCrypto_RSAKey", - "PyCrypto_TripleDES", - "Python_AES", - "Python_RC4", - "Python_RSAKey", - "RC4", - "rijndael", - "RSAKey", - "TripleDES", - "xmltools"] diff --git a/gdata/analytics/tlslite/utils/cipherfactory.py b/gdata/analytics/tlslite/utils/cipherfactory.py deleted file mode 100644 index ccbb6b5ff9..0000000000 --- a/gdata/analytics/tlslite/utils/cipherfactory.py +++ /dev/null @@ -1,111 +0,0 @@ -"""Factory functions for symmetric cryptography.""" - -import os - -import Python_AES -import Python_RC4 - -import cryptomath - -tripleDESPresent = False - -if cryptomath.m2cryptoLoaded: - import OpenSSL_AES - import OpenSSL_RC4 - import OpenSSL_TripleDES - tripleDESPresent = True - -if cryptomath.cryptlibpyLoaded: - import Cryptlib_AES - import Cryptlib_RC4 - import Cryptlib_TripleDES - tripleDESPresent = True - -if cryptomath.pycryptoLoaded: - import PyCrypto_AES - import PyCrypto_RC4 - import PyCrypto_TripleDES - tripleDESPresent = True - -# ************************************************************************** -# Factory Functions for AES -# ************************************************************************** - -def createAES(key, IV, implList=None): - """Create a new AES object. - - @type key: str - @param key: A 16, 24, or 32 byte string. - - @type IV: str - @param IV: A 16 byte string - - @rtype: L{tlslite.utils.AES} - @return: An AES object. - """ - if implList == None: - implList = ["cryptlib", "openssl", "pycrypto", "python"] - - for impl in implList: - if impl == "cryptlib" and cryptomath.cryptlibpyLoaded: - return Cryptlib_AES.new(key, 2, IV) - elif impl == "openssl" and cryptomath.m2cryptoLoaded: - return OpenSSL_AES.new(key, 2, IV) - elif impl == "pycrypto" and cryptomath.pycryptoLoaded: - return PyCrypto_AES.new(key, 2, IV) - elif impl == "python": - return Python_AES.new(key, 2, IV) - raise NotImplementedError() - -def createRC4(key, IV, implList=None): - """Create a new RC4 object. - - @type key: str - @param key: A 16 to 32 byte string. - - @type IV: object - @param IV: Ignored, whatever it is. - - @rtype: L{tlslite.utils.RC4} - @return: An RC4 object. - """ - if implList == None: - implList = ["cryptlib", "openssl", "pycrypto", "python"] - - if len(IV) != 0: - raise AssertionError() - for impl in implList: - if impl == "cryptlib" and cryptomath.cryptlibpyLoaded: - return Cryptlib_RC4.new(key) - elif impl == "openssl" and cryptomath.m2cryptoLoaded: - return OpenSSL_RC4.new(key) - elif impl == "pycrypto" and cryptomath.pycryptoLoaded: - return PyCrypto_RC4.new(key) - elif impl == "python": - return Python_RC4.new(key) - raise NotImplementedError() - -#Create a new TripleDES instance -def createTripleDES(key, IV, implList=None): - """Create a new 3DES object. - - @type key: str - @param key: A 24 byte string. - - @type IV: str - @param IV: An 8 byte string - - @rtype: L{tlslite.utils.TripleDES} - @return: A 3DES object. - """ - if implList == None: - implList = ["cryptlib", "openssl", "pycrypto"] - - for impl in implList: - if impl == "cryptlib" and cryptomath.cryptlibpyLoaded: - return Cryptlib_TripleDES.new(key, 2, IV) - elif impl == "openssl" and cryptomath.m2cryptoLoaded: - return OpenSSL_TripleDES.new(key, 2, IV) - elif impl == "pycrypto" and cryptomath.pycryptoLoaded: - return PyCrypto_TripleDES.new(key, 2, IV) - raise NotImplementedError() \ No newline at end of file diff --git a/gdata/analytics/tlslite/utils/codec.py b/gdata/analytics/tlslite/utils/codec.py deleted file mode 100644 index 13022a0b93..0000000000 --- a/gdata/analytics/tlslite/utils/codec.py +++ /dev/null @@ -1,94 +0,0 @@ -"""Classes for reading/writing binary data (such as TLS records).""" - -from compat import * - -class Writer: - def __init__(self, length=0): - #If length is zero, then this is just a "trial run" to determine length - self.index = 0 - self.bytes = createByteArrayZeros(length) - - def add(self, x, length): - if self.bytes: - newIndex = self.index+length-1 - while newIndex >= self.index: - self.bytes[newIndex] = x & 0xFF - x >>= 8 - newIndex -= 1 - self.index += length - - def addFixSeq(self, seq, length): - if self.bytes: - for e in seq: - self.add(e, length) - else: - self.index += len(seq)*length - - def addVarSeq(self, seq, length, lengthLength): - if self.bytes: - self.add(len(seq)*length, lengthLength) - for e in seq: - self.add(e, length) - else: - self.index += lengthLength + (len(seq)*length) - - -class Parser: - def __init__(self, bytes): - self.bytes = bytes - self.index = 0 - - def get(self, length): - if self.index + length > len(self.bytes): - raise SyntaxError() - x = 0 - for count in range(length): - x <<= 8 - x |= self.bytes[self.index] - self.index += 1 - return x - - def getFixBytes(self, lengthBytes): - bytes = self.bytes[self.index : self.index+lengthBytes] - self.index += lengthBytes - return bytes - - def getVarBytes(self, lengthLength): - lengthBytes = self.get(lengthLength) - return self.getFixBytes(lengthBytes) - - def getFixList(self, length, lengthList): - l = [0] * lengthList - for x in range(lengthList): - l[x] = self.get(length) - return l - - def getVarList(self, length, lengthLength): - lengthList = self.get(lengthLength) - if lengthList % length != 0: - raise SyntaxError() - lengthList = int(lengthList/length) - l = [0] * lengthList - for x in range(lengthList): - l[x] = self.get(length) - return l - - def startLengthCheck(self, lengthLength): - self.lengthCheck = self.get(lengthLength) - self.indexCheck = self.index - - def setLengthCheck(self, length): - self.lengthCheck = length - self.indexCheck = self.index - - def stopLengthCheck(self): - if (self.index - self.indexCheck) != self.lengthCheck: - raise SyntaxError() - - def atLengthCheck(self): - if (self.index - self.indexCheck) < self.lengthCheck: - return False - elif (self.index - self.indexCheck) == self.lengthCheck: - return True - else: - raise SyntaxError() \ No newline at end of file diff --git a/gdata/analytics/tlslite/utils/compat.py b/gdata/analytics/tlslite/utils/compat.py deleted file mode 100644 index 7d2d9250d8..0000000000 --- a/gdata/analytics/tlslite/utils/compat.py +++ /dev/null @@ -1,140 +0,0 @@ -"""Miscellaneous functions to mask Python version differences.""" - -import sys -import os - -if sys.version_info < (2,2): - raise AssertionError("Python 2.2 or later required") - -if sys.version_info < (2,3): - - def enumerate(collection): - return zip(range(len(collection)), collection) - - class Set: - def __init__(self, seq=None): - self.values = {} - if seq: - for e in seq: - self.values[e] = None - - def add(self, e): - self.values[e] = None - - def discard(self, e): - if e in self.values.keys(): - del(self.values[e]) - - def union(self, s): - ret = Set() - for e in self.values.keys(): - ret.values[e] = None - for e in s.values.keys(): - ret.values[e] = None - return ret - - def issubset(self, other): - for e in self.values.keys(): - if e not in other.values.keys(): - return False - return True - - def __nonzero__( self): - return len(self.values.keys()) - - def __contains__(self, e): - return e in self.values.keys() - - def __iter__(self): - return iter(set.values.keys()) - - -if os.name != "java": - - import array - def createByteArraySequence(seq): - return array.array('B', seq) - def createByteArrayZeros(howMany): - return array.array('B', [0] * howMany) - def concatArrays(a1, a2): - return a1+a2 - - def bytesToString(bytes): - return bytes.tostring() - def stringToBytes(s): - bytes = createByteArrayZeros(0) - bytes.fromstring(s) - return bytes - - import math - def numBits(n): - if n==0: - return 0 - s = "%x" % n - return ((len(s)-1)*4) + \ - {'0':0, '1':1, '2':2, '3':2, - '4':3, '5':3, '6':3, '7':3, - '8':4, '9':4, 'a':4, 'b':4, - 'c':4, 'd':4, 'e':4, 'f':4, - }[s[0]] - return int(math.floor(math.log(n, 2))+1) - - BaseException = Exception - import sys - import traceback - def formatExceptionTrace(e): - newStr = "".join(traceback.format_exception(sys.exc_type, sys.exc_value, sys.exc_traceback)) - return newStr - -else: - #Jython 2.1 is missing lots of python 2.3 stuff, - #which we have to emulate here: - #NOTE: JYTHON SUPPORT NO LONGER WORKS, DUE TO USE OF GENERATORS. - #THIS CODE IS LEFT IN SO THAT ONE JYTHON UPDATES TO 2.2, IT HAS A - #CHANCE OF WORKING AGAIN. - - import java - import jarray - - def createByteArraySequence(seq): - if isinstance(seq, type("")): #If it's a string, convert - seq = [ord(c) for c in seq] - return jarray.array(seq, 'h') #use short instead of bytes, cause bytes are signed - def createByteArrayZeros(howMany): - return jarray.zeros(howMany, 'h') #use short instead of bytes, cause bytes are signed - def concatArrays(a1, a2): - l = list(a1)+list(a2) - return createByteArraySequence(l) - - #WAY TOO SLOW - MUST BE REPLACED------------ - def bytesToString(bytes): - return "".join([chr(b) for b in bytes]) - - def stringToBytes(s): - bytes = createByteArrayZeros(len(s)) - for count, c in enumerate(s): - bytes[count] = ord(c) - return bytes - #WAY TOO SLOW - MUST BE REPLACED------------ - - def numBits(n): - if n==0: - return 0 - n= 1L * n; #convert to long, if it isn't already - return n.__tojava__(java.math.BigInteger).bitLength() - - #Adjust the string to an array of bytes - def stringToJavaByteArray(s): - bytes = jarray.zeros(len(s), 'b') - for count, c in enumerate(s): - x = ord(c) - if x >= 128: x -= 256 - bytes[count] = x - return bytes - - BaseException = java.lang.Exception - import sys - import traceback - def formatExceptionTrace(e): - newStr = "".join(traceback.format_exception(sys.exc_type, sys.exc_value, sys.exc_traceback)) - return newStr \ No newline at end of file diff --git a/gdata/analytics/tlslite/utils/cryptomath.py b/gdata/analytics/tlslite/utils/cryptomath.py deleted file mode 100644 index 92fb77437b..0000000000 --- a/gdata/analytics/tlslite/utils/cryptomath.py +++ /dev/null @@ -1,404 +0,0 @@ -"""cryptomath module - -This module has basic math/crypto code.""" - -import os -import sys -import math -import base64 -import binascii -if sys.version_info[:2] <= (2, 4): - from sha import sha as sha1 -else: - from hashlib import sha1 - -from compat import * - - -# ************************************************************************** -# Load Optional Modules -# ************************************************************************** - -# Try to load M2Crypto/OpenSSL -try: - from M2Crypto import m2 - m2cryptoLoaded = True - -except ImportError: - m2cryptoLoaded = False - - -# Try to load cryptlib -try: - import cryptlib_py - try: - cryptlib_py.cryptInit() - except cryptlib_py.CryptException, e: - #If tlslite and cryptoIDlib are both present, - #they might each try to re-initialize this, - #so we're tolerant of that. - if e[0] != cryptlib_py.CRYPT_ERROR_INITED: - raise - cryptlibpyLoaded = True - -except ImportError: - cryptlibpyLoaded = False - -#Try to load GMPY -try: - import gmpy - gmpyLoaded = True -except ImportError: - gmpyLoaded = False - -#Try to load pycrypto -try: - import Crypto.Cipher.AES - pycryptoLoaded = True -except ImportError: - pycryptoLoaded = False - - -# ************************************************************************** -# PRNG Functions -# ************************************************************************** - -# Get os.urandom PRNG -try: - os.urandom(1) - def getRandomBytes(howMany): - return stringToBytes(os.urandom(howMany)) - prngName = "os.urandom" - -except: - # Else get cryptlib PRNG - if cryptlibpyLoaded: - def getRandomBytes(howMany): - randomKey = cryptlib_py.cryptCreateContext(cryptlib_py.CRYPT_UNUSED, - cryptlib_py.CRYPT_ALGO_AES) - cryptlib_py.cryptSetAttribute(randomKey, - cryptlib_py.CRYPT_CTXINFO_MODE, - cryptlib_py.CRYPT_MODE_OFB) - cryptlib_py.cryptGenerateKey(randomKey) - bytes = createByteArrayZeros(howMany) - cryptlib_py.cryptEncrypt(randomKey, bytes) - return bytes - prngName = "cryptlib" - - else: - #Else get UNIX /dev/urandom PRNG - try: - devRandomFile = open("/dev/urandom", "rb") - def getRandomBytes(howMany): - return stringToBytes(devRandomFile.read(howMany)) - prngName = "/dev/urandom" - except IOError: - #Else get Win32 CryptoAPI PRNG - try: - import win32prng - def getRandomBytes(howMany): - s = win32prng.getRandomBytes(howMany) - if len(s) != howMany: - raise AssertionError() - return stringToBytes(s) - prngName ="CryptoAPI" - except ImportError: - #Else no PRNG :-( - def getRandomBytes(howMany): - raise NotImplementedError("No Random Number Generator "\ - "available.") - prngName = "None" - -# ************************************************************************** -# Converter Functions -# ************************************************************************** - -def bytesToNumber(bytes): - total = 0L - multiplier = 1L - for count in range(len(bytes)-1, -1, -1): - byte = bytes[count] - total += multiplier * byte - multiplier *= 256 - return total - -def numberToBytes(n): - howManyBytes = numBytes(n) - bytes = createByteArrayZeros(howManyBytes) - for count in range(howManyBytes-1, -1, -1): - bytes[count] = int(n % 256) - n >>= 8 - return bytes - -def bytesToBase64(bytes): - s = bytesToString(bytes) - return stringToBase64(s) - -def base64ToBytes(s): - s = base64ToString(s) - return stringToBytes(s) - -def numberToBase64(n): - bytes = numberToBytes(n) - return bytesToBase64(bytes) - -def base64ToNumber(s): - bytes = base64ToBytes(s) - return bytesToNumber(bytes) - -def stringToNumber(s): - bytes = stringToBytes(s) - return bytesToNumber(bytes) - -def numberToString(s): - bytes = numberToBytes(s) - return bytesToString(bytes) - -def base64ToString(s): - try: - return base64.decodestring(s) - except binascii.Error, e: - raise SyntaxError(e) - except binascii.Incomplete, e: - raise SyntaxError(e) - -def stringToBase64(s): - return base64.encodestring(s).replace("\n", "") - -def mpiToNumber(mpi): #mpi is an openssl-format bignum string - if (ord(mpi[4]) & 0x80) !=0: #Make sure this is a positive number - raise AssertionError() - bytes = stringToBytes(mpi[4:]) - return bytesToNumber(bytes) - -def numberToMPI(n): - bytes = numberToBytes(n) - ext = 0 - #If the high-order bit is going to be set, - #add an extra byte of zeros - if (numBits(n) & 0x7)==0: - ext = 1 - length = numBytes(n) + ext - bytes = concatArrays(createByteArrayZeros(4+ext), bytes) - bytes[0] = (length >> 24) & 0xFF - bytes[1] = (length >> 16) & 0xFF - bytes[2] = (length >> 8) & 0xFF - bytes[3] = length & 0xFF - return bytesToString(bytes) - - - -# ************************************************************************** -# Misc. Utility Functions -# ************************************************************************** - -def numBytes(n): - if n==0: - return 0 - bits = numBits(n) - return int(math.ceil(bits / 8.0)) - -def hashAndBase64(s): - return stringToBase64(sha1(s).digest()) - -def getBase64Nonce(numChars=22): #defaults to an 132 bit nonce - bytes = getRandomBytes(numChars) - bytesStr = "".join([chr(b) for b in bytes]) - return stringToBase64(bytesStr)[:numChars] - - -# ************************************************************************** -# Big Number Math -# ************************************************************************** - -def getRandomNumber(low, high): - if low >= high: - raise AssertionError() - howManyBits = numBits(high) - howManyBytes = numBytes(high) - lastBits = howManyBits % 8 - while 1: - bytes = getRandomBytes(howManyBytes) - if lastBits: - bytes[0] = bytes[0] % (1 << lastBits) - n = bytesToNumber(bytes) - if n >= low and n < high: - return n - -def gcd(a,b): - a, b = max(a,b), min(a,b) - while b: - a, b = b, a % b - return a - -def lcm(a, b): - #This will break when python division changes, but we can't use // cause - #of Jython - return (a * b) / gcd(a, b) - -#Returns inverse of a mod b, zero if none -#Uses Extended Euclidean Algorithm -def invMod(a, b): - c, d = a, b - uc, ud = 1, 0 - while c != 0: - #This will break when python division changes, but we can't use // - #cause of Jython - q = d / c - c, d = d-(q*c), c - uc, ud = ud - (q * uc), uc - if d == 1: - return ud % b - return 0 - - -if gmpyLoaded: - def powMod(base, power, modulus): - base = gmpy.mpz(base) - power = gmpy.mpz(power) - modulus = gmpy.mpz(modulus) - result = pow(base, power, modulus) - return long(result) - -else: - #Copied from Bryan G. Olson's post to comp.lang.python - #Does left-to-right instead of pow()'s right-to-left, - #thus about 30% faster than the python built-in with small bases - def powMod(base, power, modulus): - nBitScan = 5 - - """ Return base**power mod modulus, using multi bit scanning - with nBitScan bits at a time.""" - - #TREV - Added support for negative exponents - negativeResult = False - if (power < 0): - power *= -1 - negativeResult = True - - exp2 = 2**nBitScan - mask = exp2 - 1 - - # Break power into a list of digits of nBitScan bits. - # The list is recursive so easy to read in reverse direction. - nibbles = None - while power: - nibbles = int(power & mask), nibbles - power = power >> nBitScan - - # Make a table of powers of base up to 2**nBitScan - 1 - lowPowers = [1] - for i in xrange(1, exp2): - lowPowers.append((lowPowers[i-1] * base) % modulus) - - # To exponentiate by the first nibble, look it up in the table - nib, nibbles = nibbles - prod = lowPowers[nib] - - # For the rest, square nBitScan times, then multiply by - # base^nibble - while nibbles: - nib, nibbles = nibbles - for i in xrange(nBitScan): - prod = (prod * prod) % modulus - if nib: prod = (prod * lowPowers[nib]) % modulus - - #TREV - Added support for negative exponents - if negativeResult: - prodInv = invMod(prod, modulus) - #Check to make sure the inverse is correct - if (prod * prodInv) % modulus != 1: - raise AssertionError() - return prodInv - return prod - - -#Pre-calculate a sieve of the ~100 primes < 1000: -def makeSieve(n): - sieve = range(n) - for count in range(2, int(math.sqrt(n))): - if sieve[count] == 0: - continue - x = sieve[count] * 2 - while x < len(sieve): - sieve[x] = 0 - x += sieve[count] - sieve = [x for x in sieve[2:] if x] - return sieve - -sieve = makeSieve(1000) - -def isPrime(n, iterations=5, display=False): - #Trial division with sieve - for x in sieve: - if x >= n: return True - if n % x == 0: return False - #Passed trial division, proceed to Rabin-Miller - #Rabin-Miller implemented per Ferguson & Schneier - #Compute s, t for Rabin-Miller - if display: print "*", - s, t = n-1, 0 - while s % 2 == 0: - s, t = s/2, t+1 - #Repeat Rabin-Miller x times - a = 2 #Use 2 as a base for first iteration speedup, per HAC - for count in range(iterations): - v = powMod(a, s, n) - if v==1: - continue - i = 0 - while v != n-1: - if i == t-1: - return False - else: - v, i = powMod(v, 2, n), i+1 - a = getRandomNumber(2, n) - return True - -def getRandomPrime(bits, display=False): - if bits < 10: - raise AssertionError() - #The 1.5 ensures the 2 MSBs are set - #Thus, when used for p,q in RSA, n will have its MSB set - # - #Since 30 is lcm(2,3,5), we'll set our test numbers to - #29 % 30 and keep them there - low = (2L ** (bits-1)) * 3/2 - high = 2L ** bits - 30 - p = getRandomNumber(low, high) - p += 29 - (p % 30) - while 1: - if display: print ".", - p += 30 - if p >= high: - p = getRandomNumber(low, high) - p += 29 - (p % 30) - if isPrime(p, display=display): - return p - -#Unused at the moment... -def getRandomSafePrime(bits, display=False): - if bits < 10: - raise AssertionError() - #The 1.5 ensures the 2 MSBs are set - #Thus, when used for p,q in RSA, n will have its MSB set - # - #Since 30 is lcm(2,3,5), we'll set our test numbers to - #29 % 30 and keep them there - low = (2 ** (bits-2)) * 3/2 - high = (2 ** (bits-1)) - 30 - q = getRandomNumber(low, high) - q += 29 - (q % 30) - while 1: - if display: print ".", - q += 30 - if (q >= high): - q = getRandomNumber(low, high) - q += 29 - (q % 30) - #Ideas from Tom Wu's SRP code - #Do trial division on p and q before Rabin-Miller - if isPrime(q, 0, display=display): - p = (2 * q) + 1 - if isPrime(p, display=display): - if isPrime(q, display=display): - return p diff --git a/gdata/analytics/tlslite/utils/dateFuncs.py b/gdata/analytics/tlslite/utils/dateFuncs.py deleted file mode 100644 index 38812ebf85..0000000000 --- a/gdata/analytics/tlslite/utils/dateFuncs.py +++ /dev/null @@ -1,75 +0,0 @@ - -import os - -#Functions for manipulating datetime objects -#CCYY-MM-DDThh:mm:ssZ -def parseDateClass(s): - year, month, day = s.split("-") - day, tail = day[:2], day[2:] - hour, minute, second = tail[1:].split(":") - second = second[:2] - year, month, day = int(year), int(month), int(day) - hour, minute, second = int(hour), int(minute), int(second) - return createDateClass(year, month, day, hour, minute, second) - - -if os.name != "java": - from datetime import datetime, timedelta - - #Helper functions for working with a date/time class - def createDateClass(year, month, day, hour, minute, second): - return datetime(year, month, day, hour, minute, second) - - def printDateClass(d): - #Split off fractional seconds, append 'Z' - return d.isoformat().split(".")[0]+"Z" - - def getNow(): - return datetime.utcnow() - - def getHoursFromNow(hours): - return datetime.utcnow() + timedelta(hours=hours) - - def getMinutesFromNow(minutes): - return datetime.utcnow() + timedelta(minutes=minutes) - - def isDateClassExpired(d): - return d < datetime.utcnow() - - def isDateClassBefore(d1, d2): - return d1 < d2 - -else: - #Jython 2.1 is missing lots of python 2.3 stuff, - #which we have to emulate here: - import java - import jarray - - def createDateClass(year, month, day, hour, minute, second): - c = java.util.Calendar.getInstance() - c.setTimeZone(java.util.TimeZone.getTimeZone("UTC")) - c.set(year, month-1, day, hour, minute, second) - return c - - def printDateClass(d): - return "%04d-%02d-%02dT%02d:%02d:%02dZ" % \ - (d.get(d.YEAR), d.get(d.MONTH)+1, d.get(d.DATE), \ - d.get(d.HOUR_OF_DAY), d.get(d.MINUTE), d.get(d.SECOND)) - - def getNow(): - c = java.util.Calendar.getInstance() - c.setTimeZone(java.util.TimeZone.getTimeZone("UTC")) - c.get(c.HOUR) #force refresh? - return c - - def getHoursFromNow(hours): - d = getNow() - d.add(d.HOUR, hours) - return d - - def isDateClassExpired(d): - n = getNow() - return d.before(n) - - def isDateClassBefore(d1, d2): - return d1.before(d2) diff --git a/gdata/analytics/tlslite/utils/entropy.c b/gdata/analytics/tlslite/utils/entropy.c deleted file mode 100644 index c627794d2d..0000000000 --- a/gdata/analytics/tlslite/utils/entropy.c +++ /dev/null @@ -1,173 +0,0 @@ - -#include "Python.h" - - -#ifdef MS_WINDOWS - -/* The following #define is not needed on VC6 with the Platform SDK, and it -may not be needed on VC7, I'm not sure. I don't think it hurts anything.*/ -#define _WIN32_WINNT 0x0400 - -#include - - -typedef BOOL (WINAPI *CRYPTACQUIRECONTEXTA)(HCRYPTPROV *phProv,\ - LPCSTR pszContainer, LPCSTR pszProvider, DWORD dwProvType,\ - DWORD dwFlags ); -typedef BOOL (WINAPI *CRYPTGENRANDOM)(HCRYPTPROV hProv, DWORD dwLen,\ - BYTE *pbBuffer ); -typedef BOOL (WINAPI *CRYPTRELEASECONTEXT)(HCRYPTPROV hProv,\ - DWORD dwFlags); - - -static PyObject* entropy(PyObject *self, PyObject *args) -{ - int howMany = 0; - HINSTANCE hAdvAPI32 = NULL; - CRYPTACQUIRECONTEXTA pCryptAcquireContextA = NULL; - CRYPTGENRANDOM pCryptGenRandom = NULL; - CRYPTRELEASECONTEXT pCryptReleaseContext = NULL; - HCRYPTPROV hCryptProv = 0; - unsigned char* bytes = NULL; - PyObject* returnVal = NULL; - - - /* Read arguments */ - if (!PyArg_ParseTuple(args, "i", &howMany)) - return(NULL); - - /* Obtain handle to the DLL containing CryptoAPI - This should not fail */ - if( (hAdvAPI32 = GetModuleHandle("advapi32.dll")) == NULL) { - PyErr_Format(PyExc_SystemError, - "Advapi32.dll not found"); - return NULL; - } - - /* Obtain pointers to the CryptoAPI functions - This will fail on some early version of Win95 */ - pCryptAcquireContextA = (CRYPTACQUIRECONTEXTA)GetProcAddress(hAdvAPI32,\ - "CryptAcquireContextA"); - pCryptGenRandom = (CRYPTGENRANDOM)GetProcAddress(hAdvAPI32,\ - "CryptGenRandom"); - pCryptReleaseContext = (CRYPTRELEASECONTEXT) GetProcAddress(hAdvAPI32,\ - "CryptReleaseContext"); - if (pCryptAcquireContextA == NULL || pCryptGenRandom == NULL || - pCryptReleaseContext == NULL) { - PyErr_Format(PyExc_NotImplementedError, - "CryptoAPI not available on this version of Windows"); - return NULL; - } - - /* Allocate bytes */ - if ((bytes = (unsigned char*)PyMem_Malloc(howMany)) == NULL) - return PyErr_NoMemory(); - - - /* Acquire context */ - if(!pCryptAcquireContextA(&hCryptProv, NULL, NULL, PROV_RSA_FULL, - CRYPT_VERIFYCONTEXT)) { - PyErr_Format(PyExc_SystemError, - "CryptAcquireContext failed, error %d", GetLastError()); - PyMem_Free(bytes); - return NULL; - } - - /* Get random data */ - if(!pCryptGenRandom(hCryptProv, howMany, bytes)) { - PyErr_Format(PyExc_SystemError, - "CryptGenRandom failed, error %d", GetLastError()); - PyMem_Free(bytes); - CryptReleaseContext(hCryptProv, 0); - return NULL; - } - - /* Build return value */ - returnVal = Py_BuildValue("s#", bytes, howMany); - PyMem_Free(bytes); - - /* Release context */ - if (!pCryptReleaseContext(hCryptProv, 0)) { - PyErr_Format(PyExc_SystemError, - "CryptReleaseContext failed, error %d", GetLastError()); - return NULL; - } - - return returnVal; -} - -#elif defined(HAVE_UNISTD_H) && defined(HAVE_FCNTL_H) - -#include -#include - -static PyObject* entropy(PyObject *self, PyObject *args) -{ - int howMany; - int fd; - unsigned char* bytes = NULL; - PyObject* returnVal = NULL; - - - /* Read arguments */ - if (!PyArg_ParseTuple(args, "i", &howMany)) - return(NULL); - - /* Allocate bytes */ - if ((bytes = (unsigned char*)PyMem_Malloc(howMany)) == NULL) - return PyErr_NoMemory(); - - /* Open device */ - if ((fd = open("/dev/urandom", O_RDONLY, 0)) == -1) { - PyErr_Format(PyExc_NotImplementedError, - "No entropy source found"); - PyMem_Free(bytes); - return NULL; - } - - /* Get random data */ - if (read(fd, bytes, howMany) < howMany) { - PyErr_Format(PyExc_SystemError, - "Reading from /dev/urandom failed"); - PyMem_Free(bytes); - close(fd); - return NULL; - } - - /* Build return value */ - returnVal = Py_BuildValue("s#", bytes, howMany); - PyMem_Free(bytes); - - /* Close device */ - close(fd); - - return returnVal; -} - -#else - -static PyObject* entropy(PyObject *self, PyObject *args) -{ - PyErr_Format(PyExc_NotImplementedError, - "Function not supported"); - return NULL; -} - -#endif - - - -/* List of functions exported by this module */ - -static struct PyMethodDef entropy_functions[] = { - {"entropy", (PyCFunction)entropy, METH_VARARGS, "Return a string of random bytes produced by a platform-specific\nentropy source."}, - {NULL, NULL} /* Sentinel */ -}; - - -/* Initialize this module. */ - -PyMODINIT_FUNC initentropy(void) -{ - Py_InitModule("entropy", entropy_functions); -} \ No newline at end of file diff --git a/gdata/analytics/tlslite/utils/hmac.py b/gdata/analytics/tlslite/utils/hmac.py deleted file mode 100644 index fe8feec219..0000000000 --- a/gdata/analytics/tlslite/utils/hmac.py +++ /dev/null @@ -1,104 +0,0 @@ -"""HMAC (Keyed-Hashing for Message Authentication) Python module. - -Implements the HMAC algorithm as described by RFC 2104. - -(This file is modified from the standard library version to do faster -copying) -""" - -def _strxor(s1, s2): - """Utility method. XOR the two strings s1 and s2 (must have same length). - """ - return "".join(map(lambda x, y: chr(ord(x) ^ ord(y)), s1, s2)) - -# The size of the digests returned by HMAC depends on the underlying -# hashing module used. -digest_size = None - -class HMAC: - """RFC2104 HMAC class. - - This supports the API for Cryptographic Hash Functions (PEP 247). - """ - - def __init__(self, key, msg = None, digestmod = None): - """Create a new HMAC object. - - key: key for the keyed hash object. - msg: Initial input for the hash, if provided. - digestmod: A module supporting PEP 247. Defaults to the md5 module. - """ - if digestmod is None: - import md5 - digestmod = md5 - - if key == None: #TREVNEW - for faster copying - return #TREVNEW - - self.digestmod = digestmod - self.outer = digestmod.new() - self.inner = digestmod.new() - self.digest_size = digestmod.digest_size - - blocksize = 64 - ipad = "\x36" * blocksize - opad = "\x5C" * blocksize - - if len(key) > blocksize: - key = digestmod.new(key).digest() - - key = key + chr(0) * (blocksize - len(key)) - self.outer.update(_strxor(key, opad)) - self.inner.update(_strxor(key, ipad)) - if msg is not None: - self.update(msg) - -## def clear(self): -## raise NotImplementedError, "clear() method not available in HMAC." - - def update(self, msg): - """Update this hashing object with the string msg. - """ - self.inner.update(msg) - - def copy(self): - """Return a separate copy of this hashing object. - - An update to this copy won't affect the original object. - """ - other = HMAC(None) #TREVNEW - for faster copying - other.digest_size = self.digest_size #TREVNEW - other.digestmod = self.digestmod - other.inner = self.inner.copy() - other.outer = self.outer.copy() - return other - - def digest(self): - """Return the hash value of this hashing object. - - This returns a string containing 8-bit data. The object is - not altered in any way by this function; you can continue - updating the object after calling this function. - """ - h = self.outer.copy() - h.update(self.inner.digest()) - return h.digest() - - def hexdigest(self): - """Like digest(), but returns a string of hexadecimal digits instead. - """ - return "".join([hex(ord(x))[2:].zfill(2) - for x in tuple(self.digest())]) - -def new(key, msg = None, digestmod = None): - """Create a new hashing object and return it. - - key: The starting key for the hash. - msg: if available, will immediately be hashed into the object's starting - state. - - You can now feed arbitrary strings into the object using its update() - method, and can ask for the hash value at any time by calling its digest() - method. - """ - return HMAC(key, msg, digestmod) diff --git a/gdata/analytics/tlslite/utils/jython_compat.py b/gdata/analytics/tlslite/utils/jython_compat.py deleted file mode 100644 index 1245183a99..0000000000 --- a/gdata/analytics/tlslite/utils/jython_compat.py +++ /dev/null @@ -1,195 +0,0 @@ -"""Miscellaneous functions to mask Python/Jython differences.""" - -import os -import sha - -if os.name != "java": - BaseException = Exception - - from sets import Set - import array - import math - - def createByteArraySequence(seq): - return array.array('B', seq) - def createByteArrayZeros(howMany): - return array.array('B', [0] * howMany) - def concatArrays(a1, a2): - return a1+a2 - - def bytesToString(bytes): - return bytes.tostring() - - def stringToBytes(s): - bytes = createByteArrayZeros(0) - bytes.fromstring(s) - return bytes - - def numBits(n): - if n==0: - return 0 - return int(math.floor(math.log(n, 2))+1) - - class CertChainBase: pass - class SelfTestBase: pass - class ReportFuncBase: pass - - #Helper functions for working with sets (from Python 2.3) - def iterSet(set): - return iter(set) - - def getListFromSet(set): - return list(set) - - #Factory function for getting a SHA1 object - def getSHA1(s): - return sha.sha(s) - - import sys - import traceback - - def formatExceptionTrace(e): - newStr = "".join(traceback.format_exception(sys.exc_type, sys.exc_value, sys.exc_traceback)) - return newStr - -else: - #Jython 2.1 is missing lots of python 2.3 stuff, - #which we have to emulate here: - import java - import jarray - - BaseException = java.lang.Exception - - def createByteArraySequence(seq): - if isinstance(seq, type("")): #If it's a string, convert - seq = [ord(c) for c in seq] - return jarray.array(seq, 'h') #use short instead of bytes, cause bytes are signed - def createByteArrayZeros(howMany): - return jarray.zeros(howMany, 'h') #use short instead of bytes, cause bytes are signed - def concatArrays(a1, a2): - l = list(a1)+list(a2) - return createByteArraySequence(l) - - #WAY TOO SLOW - MUST BE REPLACED------------ - def bytesToString(bytes): - return "".join([chr(b) for b in bytes]) - - def stringToBytes(s): - bytes = createByteArrayZeros(len(s)) - for count, c in enumerate(s): - bytes[count] = ord(c) - return bytes - #WAY TOO SLOW - MUST BE REPLACED------------ - - def numBits(n): - if n==0: - return 0 - n= 1L * n; #convert to long, if it isn't already - return n.__tojava__(java.math.BigInteger).bitLength() - - #This properly creates static methods for Jython - class staticmethod: - def __init__(self, anycallable): self.__call__ = anycallable - - #Properties are not supported for Jython - class property: - def __init__(self, anycallable): pass - - #True and False have to be specially defined - False = 0 - True = 1 - - class StopIteration(Exception): pass - - def enumerate(collection): - return zip(range(len(collection)), collection) - - class Set: - def __init__(self, seq=None): - self.values = {} - if seq: - for e in seq: - self.values[e] = None - - def add(self, e): - self.values[e] = None - - def discard(self, e): - if e in self.values.keys(): - del(self.values[e]) - - def union(self, s): - ret = Set() - for e in self.values.keys(): - ret.values[e] = None - for e in s.values.keys(): - ret.values[e] = None - return ret - - def issubset(self, other): - for e in self.values.keys(): - if e not in other.values.keys(): - return False - return True - - def __nonzero__( self): - return len(self.values.keys()) - - def __contains__(self, e): - return e in self.values.keys() - - def iterSet(set): - return set.values.keys() - - def getListFromSet(set): - return set.values.keys() - - """ - class JCE_SHA1: - def __init__(self, s=None): - self.md = java.security.MessageDigest.getInstance("SHA1") - if s: - self.update(s) - - def update(self, s): - self.md.update(s) - - def copy(self): - sha1 = JCE_SHA1() - sha1.md = self.md.clone() - return sha1 - - def digest(self): - digest = self.md.digest() - bytes = jarray.zeros(20, 'h') - for count in xrange(20): - x = digest[count] - if x < 0: x += 256 - bytes[count] = x - return bytes - """ - - #Factory function for getting a SHA1 object - #The JCE_SHA1 class is way too slow... - #the sha.sha object we use instead is broken in the jython 2.1 - #release, and needs to be patched - def getSHA1(s): - #return JCE_SHA1(s) - return sha.sha(s) - - - #Adjust the string to an array of bytes - def stringToJavaByteArray(s): - bytes = jarray.zeros(len(s), 'b') - for count, c in enumerate(s): - x = ord(c) - if x >= 128: x -= 256 - bytes[count] = x - return bytes - - import sys - import traceback - - def formatExceptionTrace(e): - newStr = "".join(traceback.format_exception(sys.exc_type, sys.exc_value, sys.exc_traceback)) - return newStr diff --git a/gdata/analytics/tlslite/utils/keyfactory.py b/gdata/analytics/tlslite/utils/keyfactory.py deleted file mode 100644 index 5005af7f5b..0000000000 --- a/gdata/analytics/tlslite/utils/keyfactory.py +++ /dev/null @@ -1,243 +0,0 @@ -"""Factory functions for asymmetric cryptography. -@sort: generateRSAKey, parseXMLKey, parsePEMKey, parseAsPublicKey, -parseAsPrivateKey -""" - -from compat import * - -from RSAKey import RSAKey -from Python_RSAKey import Python_RSAKey -import cryptomath - -if cryptomath.m2cryptoLoaded: - from OpenSSL_RSAKey import OpenSSL_RSAKey - -if cryptomath.pycryptoLoaded: - from PyCrypto_RSAKey import PyCrypto_RSAKey - -# ************************************************************************** -# Factory Functions for RSA Keys -# ************************************************************************** - -def generateRSAKey(bits, implementations=["openssl", "python"]): - """Generate an RSA key with the specified bit length. - - @type bits: int - @param bits: Desired bit length of the new key's modulus. - - @rtype: L{tlslite.utils.RSAKey.RSAKey} - @return: A new RSA private key. - """ - for implementation in implementations: - if implementation == "openssl" and cryptomath.m2cryptoLoaded: - return OpenSSL_RSAKey.generate(bits) - elif implementation == "python": - return Python_RSAKey.generate(bits) - raise ValueError("No acceptable implementations") - -def parseXMLKey(s, private=False, public=False, implementations=["python"]): - """Parse an XML-format key. - - The XML format used here is specific to tlslite and cryptoIDlib. The - format can store the public component of a key, or the public and - private components. For example:: - - - 4a5yzB8oGNlHo866CAspAC47M4Fvx58zwK8pou... - Aw== - - - - 4a5yzB8oGNlHo866CAspAC47M4Fvx58zwK8pou... - Aw== - JZ0TIgUxWXmL8KJ0VqyG1V0J3ern9pqIoB0xmy... -

5PreIj6z6ldIGL1V4+1C36dQFHNCQHJvW52GXc... - /E/wDit8YXPCxx126zTq2ilQ3IcW54NJYyNjiZ... - mKc+wX8inDowEH45Qp4slRo1YveBgExKPROu6... - qDVKtBz9lk0shL5PR3ickXDgkwS576zbl2ztB... - j6E8EA7dNsTImaXexAmLA1DoeArsYeFAInr... - - - @type s: str - @param s: A string containing an XML public or private key. - - @type private: bool - @param private: If True, a L{SyntaxError} will be raised if the private - key component is not present. - - @type public: bool - @param public: If True, the private key component (if present) will be - discarded, so this function will always return a public key. - - @rtype: L{tlslite.utils.RSAKey.RSAKey} - @return: An RSA key. - - @raise SyntaxError: If the key is not properly formatted. - """ - for implementation in implementations: - if implementation == "python": - key = Python_RSAKey.parseXML(s) - break - else: - raise ValueError("No acceptable implementations") - - return _parseKeyHelper(key, private, public) - -#Parse as an OpenSSL or Python key -def parsePEMKey(s, private=False, public=False, passwordCallback=None, - implementations=["openssl", "python"]): - """Parse a PEM-format key. - - The PEM format is used by OpenSSL and other tools. The - format is typically used to store both the public and private - components of a key. For example:: - - -----BEGIN RSA PRIVATE KEY----- - MIICXQIBAAKBgQDYscuoMzsGmW0pAYsmyHltxB2TdwHS0dImfjCMfaSDkfLdZY5+ - dOWORVns9etWnr194mSGA1F0Pls/VJW8+cX9+3vtJV8zSdANPYUoQf0TP7VlJxkH - dSRkUbEoz5bAAs/+970uos7n7iXQIni+3erUTdYEk2iWnMBjTljfgbK/dQIDAQAB - AoGAJHoJZk75aKr7DSQNYIHuruOMdv5ZeDuJvKERWxTrVJqE32/xBKh42/IgqRrc - esBN9ZregRCd7YtxoL+EVUNWaJNVx2mNmezEznrc9zhcYUrgeaVdFO2yBF1889zO - gCOVwrO8uDgeyj6IKa25H6c1N13ih/o7ZzEgWbGG+ylU1yECQQDv4ZSJ4EjSh/Fl - aHdz3wbBa/HKGTjC8iRy476Cyg2Fm8MZUe9Yy3udOrb5ZnS2MTpIXt5AF3h2TfYV - VoFXIorjAkEA50FcJmzT8sNMrPaV8vn+9W2Lu4U7C+K/O2g1iXMaZms5PC5zV5aV - CKXZWUX1fq2RaOzlbQrpgiolhXpeh8FjxwJBAOFHzSQfSsTNfttp3KUpU0LbiVvv - i+spVSnA0O4rq79KpVNmK44Mq67hsW1P11QzrzTAQ6GVaUBRv0YS061td1kCQHnP - wtN2tboFR6lABkJDjxoGRvlSt4SOPr7zKGgrWjeiuTZLHXSAnCY+/hr5L9Q3ZwXG - 6x6iBdgLjVIe4BZQNtcCQQDXGv/gWinCNTN3MPWfTW/RGzuMYVmyBFais0/VrgdH - h1dLpztmpQqfyH/zrBXQ9qL/zR4ojS6XYneO/U18WpEe - -----END RSA PRIVATE KEY----- - - To generate a key like this with OpenSSL, run:: - - openssl genrsa 2048 > key.pem - - This format also supports password-encrypted private keys. TLS - Lite can only handle password-encrypted private keys when OpenSSL - and M2Crypto are installed. In this case, passwordCallback will be - invoked to query the user for the password. - - @type s: str - @param s: A string containing a PEM-encoded public or private key. - - @type private: bool - @param private: If True, a L{SyntaxError} will be raised if the - private key component is not present. - - @type public: bool - @param public: If True, the private key component (if present) will - be discarded, so this function will always return a public key. - - @type passwordCallback: callable - @param passwordCallback: This function will be called, with no - arguments, if the PEM-encoded private key is password-encrypted. - The callback should return the password string. If the password is - incorrect, SyntaxError will be raised. If no callback is passed - and the key is password-encrypted, a prompt will be displayed at - the console. - - @rtype: L{tlslite.utils.RSAKey.RSAKey} - @return: An RSA key. - - @raise SyntaxError: If the key is not properly formatted. - """ - for implementation in implementations: - if implementation == "openssl" and cryptomath.m2cryptoLoaded: - key = OpenSSL_RSAKey.parse(s, passwordCallback) - break - elif implementation == "python": - key = Python_RSAKey.parsePEM(s) - break - else: - raise ValueError("No acceptable implementations") - - return _parseKeyHelper(key, private, public) - - -def _parseKeyHelper(key, private, public): - if private: - if not key.hasPrivateKey(): - raise SyntaxError("Not a private key!") - - if public: - return _createPublicKey(key) - - if private: - if hasattr(key, "d"): - return _createPrivateKey(key) - else: - return key - - return key - -def parseAsPublicKey(s): - """Parse an XML or PEM-formatted public key. - - @type s: str - @param s: A string containing an XML or PEM-encoded public or private key. - - @rtype: L{tlslite.utils.RSAKey.RSAKey} - @return: An RSA public key. - - @raise SyntaxError: If the key is not properly formatted. - """ - try: - return parsePEMKey(s, public=True) - except: - return parseXMLKey(s, public=True) - -def parsePrivateKey(s): - """Parse an XML or PEM-formatted private key. - - @type s: str - @param s: A string containing an XML or PEM-encoded private key. - - @rtype: L{tlslite.utils.RSAKey.RSAKey} - @return: An RSA private key. - - @raise SyntaxError: If the key is not properly formatted. - """ - try: - return parsePEMKey(s, private=True) - except: - return parseXMLKey(s, private=True) - -def _createPublicKey(key): - """ - Create a new public key. Discard any private component, - and return the most efficient key possible. - """ - if not isinstance(key, RSAKey): - raise AssertionError() - return _createPublicRSAKey(key.n, key.e) - -def _createPrivateKey(key): - """ - Create a new private key. Return the most efficient key possible. - """ - if not isinstance(key, RSAKey): - raise AssertionError() - if not key.hasPrivateKey(): - raise AssertionError() - return _createPrivateRSAKey(key.n, key.e, key.d, key.p, key.q, key.dP, - key.dQ, key.qInv) - -def _createPublicRSAKey(n, e, implementations = ["openssl", "pycrypto", - "python"]): - for implementation in implementations: - if implementation == "openssl" and cryptomath.m2cryptoLoaded: - return OpenSSL_RSAKey(n, e) - elif implementation == "pycrypto" and cryptomath.pycryptoLoaded: - return PyCrypto_RSAKey(n, e) - elif implementation == "python": - return Python_RSAKey(n, e) - raise ValueError("No acceptable implementations") - -def _createPrivateRSAKey(n, e, d, p, q, dP, dQ, qInv, - implementations = ["pycrypto", "python"]): - for implementation in implementations: - if implementation == "pycrypto" and cryptomath.pycryptoLoaded: - return PyCrypto_RSAKey(n, e, d, p, q, dP, dQ, qInv) - elif implementation == "python": - return Python_RSAKey(n, e, d, p, q, dP, dQ, qInv) - raise ValueError("No acceptable implementations") diff --git a/gdata/analytics/tlslite/utils/rijndael.py b/gdata/analytics/tlslite/utils/rijndael.py deleted file mode 100644 index cb2f547346..0000000000 --- a/gdata/analytics/tlslite/utils/rijndael.py +++ /dev/null @@ -1,392 +0,0 @@ -""" -A pure python (slow) implementation of rijndael with a decent interface - -To include - - -from rijndael import rijndael - -To do a key setup - - -r = rijndael(key, block_size = 16) - -key must be a string of length 16, 24, or 32 -blocksize must be 16, 24, or 32. Default is 16 - -To use - - -ciphertext = r.encrypt(plaintext) -plaintext = r.decrypt(ciphertext) - -If any strings are of the wrong length a ValueError is thrown -""" - -# ported from the Java reference code by Bram Cohen, bram@gawth.com, April 2001 -# this code is public domain, unless someone makes -# an intellectual property claim against the reference -# code, in which case it can be made public domain by -# deleting all the comments and renaming all the variables - -import copy -import string - - - -#----------------------- -#TREV - ADDED BECAUSE THERE'S WARNINGS ABOUT INT OVERFLOW BEHAVIOR CHANGING IN -#2.4..... -import os -if os.name != "java": - import exceptions - if hasattr(exceptions, "FutureWarning"): - import warnings - warnings.filterwarnings("ignore", category=FutureWarning, append=1) -#----------------------- - - - -shifts = [[[0, 0], [1, 3], [2, 2], [3, 1]], - [[0, 0], [1, 5], [2, 4], [3, 3]], - [[0, 0], [1, 7], [3, 5], [4, 4]]] - -# [keysize][block_size] -num_rounds = {16: {16: 10, 24: 12, 32: 14}, 24: {16: 12, 24: 12, 32: 14}, 32: {16: 14, 24: 14, 32: 14}} - -A = [[1, 1, 1, 1, 1, 0, 0, 0], - [0, 1, 1, 1, 1, 1, 0, 0], - [0, 0, 1, 1, 1, 1, 1, 0], - [0, 0, 0, 1, 1, 1, 1, 1], - [1, 0, 0, 0, 1, 1, 1, 1], - [1, 1, 0, 0, 0, 1, 1, 1], - [1, 1, 1, 0, 0, 0, 1, 1], - [1, 1, 1, 1, 0, 0, 0, 1]] - -# produce log and alog tables, needed for multiplying in the -# field GF(2^m) (generator = 3) -alog = [1] -for i in xrange(255): - j = (alog[-1] << 1) ^ alog[-1] - if j & 0x100 != 0: - j ^= 0x11B - alog.append(j) - -log = [0] * 256 -for i in xrange(1, 255): - log[alog[i]] = i - -# multiply two elements of GF(2^m) -def mul(a, b): - if a == 0 or b == 0: - return 0 - return alog[(log[a & 0xFF] + log[b & 0xFF]) % 255] - -# substitution box based on F^{-1}(x) -box = [[0] * 8 for i in xrange(256)] -box[1][7] = 1 -for i in xrange(2, 256): - j = alog[255 - log[i]] - for t in xrange(8): - box[i][t] = (j >> (7 - t)) & 0x01 - -B = [0, 1, 1, 0, 0, 0, 1, 1] - -# affine transform: box[i] <- B + A*box[i] -cox = [[0] * 8 for i in xrange(256)] -for i in xrange(256): - for t in xrange(8): - cox[i][t] = B[t] - for j in xrange(8): - cox[i][t] ^= A[t][j] * box[i][j] - -# S-boxes and inverse S-boxes -S = [0] * 256 -Si = [0] * 256 -for i in xrange(256): - S[i] = cox[i][0] << 7 - for t in xrange(1, 8): - S[i] ^= cox[i][t] << (7-t) - Si[S[i] & 0xFF] = i - -# T-boxes -G = [[2, 1, 1, 3], - [3, 2, 1, 1], - [1, 3, 2, 1], - [1, 1, 3, 2]] - -AA = [[0] * 8 for i in xrange(4)] - -for i in xrange(4): - for j in xrange(4): - AA[i][j] = G[i][j] - AA[i][i+4] = 1 - -for i in xrange(4): - pivot = AA[i][i] - if pivot == 0: - t = i + 1 - while AA[t][i] == 0 and t < 4: - t += 1 - assert t != 4, 'G matrix must be invertible' - for j in xrange(8): - AA[i][j], AA[t][j] = AA[t][j], AA[i][j] - pivot = AA[i][i] - for j in xrange(8): - if AA[i][j] != 0: - AA[i][j] = alog[(255 + log[AA[i][j] & 0xFF] - log[pivot & 0xFF]) % 255] - for t in xrange(4): - if i != t: - for j in xrange(i+1, 8): - AA[t][j] ^= mul(AA[i][j], AA[t][i]) - AA[t][i] = 0 - -iG = [[0] * 4 for i in xrange(4)] - -for i in xrange(4): - for j in xrange(4): - iG[i][j] = AA[i][j + 4] - -def mul4(a, bs): - if a == 0: - return 0 - r = 0 - for b in bs: - r <<= 8 - if b != 0: - r = r | mul(a, b) - return r - -T1 = [] -T2 = [] -T3 = [] -T4 = [] -T5 = [] -T6 = [] -T7 = [] -T8 = [] -U1 = [] -U2 = [] -U3 = [] -U4 = [] - -for t in xrange(256): - s = S[t] - T1.append(mul4(s, G[0])) - T2.append(mul4(s, G[1])) - T3.append(mul4(s, G[2])) - T4.append(mul4(s, G[3])) - - s = Si[t] - T5.append(mul4(s, iG[0])) - T6.append(mul4(s, iG[1])) - T7.append(mul4(s, iG[2])) - T8.append(mul4(s, iG[3])) - - U1.append(mul4(t, iG[0])) - U2.append(mul4(t, iG[1])) - U3.append(mul4(t, iG[2])) - U4.append(mul4(t, iG[3])) - -# round constants -rcon = [1] -r = 1 -for t in xrange(1, 30): - r = mul(2, r) - rcon.append(r) - -del A -del AA -del pivot -del B -del G -del box -del log -del alog -del i -del j -del r -del s -del t -del mul -del mul4 -del cox -del iG - -class rijndael: - def __init__(self, key, block_size = 16): - if block_size != 16 and block_size != 24 and block_size != 32: - raise ValueError('Invalid block size: ' + str(block_size)) - if len(key) != 16 and len(key) != 24 and len(key) != 32: - raise ValueError('Invalid key size: ' + str(len(key))) - self.block_size = block_size - - ROUNDS = num_rounds[len(key)][block_size] - BC = block_size / 4 - # encryption round keys - Ke = [[0] * BC for i in xrange(ROUNDS + 1)] - # decryption round keys - Kd = [[0] * BC for i in xrange(ROUNDS + 1)] - ROUND_KEY_COUNT = (ROUNDS + 1) * BC - KC = len(key) / 4 - - # copy user material bytes into temporary ints - tk = [] - for i in xrange(0, KC): - tk.append((ord(key[i * 4]) << 24) | (ord(key[i * 4 + 1]) << 16) | - (ord(key[i * 4 + 2]) << 8) | ord(key[i * 4 + 3])) - - # copy values into round key arrays - t = 0 - j = 0 - while j < KC and t < ROUND_KEY_COUNT: - Ke[t / BC][t % BC] = tk[j] - Kd[ROUNDS - (t / BC)][t % BC] = tk[j] - j += 1 - t += 1 - tt = 0 - rconpointer = 0 - while t < ROUND_KEY_COUNT: - # extrapolate using phi (the round key evolution function) - tt = tk[KC - 1] - tk[0] ^= (S[(tt >> 16) & 0xFF] & 0xFF) << 24 ^ \ - (S[(tt >> 8) & 0xFF] & 0xFF) << 16 ^ \ - (S[ tt & 0xFF] & 0xFF) << 8 ^ \ - (S[(tt >> 24) & 0xFF] & 0xFF) ^ \ - (rcon[rconpointer] & 0xFF) << 24 - rconpointer += 1 - if KC != 8: - for i in xrange(1, KC): - tk[i] ^= tk[i-1] - else: - for i in xrange(1, KC / 2): - tk[i] ^= tk[i-1] - tt = tk[KC / 2 - 1] - tk[KC / 2] ^= (S[ tt & 0xFF] & 0xFF) ^ \ - (S[(tt >> 8) & 0xFF] & 0xFF) << 8 ^ \ - (S[(tt >> 16) & 0xFF] & 0xFF) << 16 ^ \ - (S[(tt >> 24) & 0xFF] & 0xFF) << 24 - for i in xrange(KC / 2 + 1, KC): - tk[i] ^= tk[i-1] - # copy values into round key arrays - j = 0 - while j < KC and t < ROUND_KEY_COUNT: - Ke[t / BC][t % BC] = tk[j] - Kd[ROUNDS - (t / BC)][t % BC] = tk[j] - j += 1 - t += 1 - # inverse MixColumn where needed - for r in xrange(1, ROUNDS): - for j in xrange(BC): - tt = Kd[r][j] - Kd[r][j] = U1[(tt >> 24) & 0xFF] ^ \ - U2[(tt >> 16) & 0xFF] ^ \ - U3[(tt >> 8) & 0xFF] ^ \ - U4[ tt & 0xFF] - self.Ke = Ke - self.Kd = Kd - - def encrypt(self, plaintext): - if len(plaintext) != self.block_size: - raise ValueError('wrong block length, expected ' + str(self.block_size) + ' got ' + str(len(plaintext))) - Ke = self.Ke - - BC = self.block_size / 4 - ROUNDS = len(Ke) - 1 - if BC == 4: - SC = 0 - elif BC == 6: - SC = 1 - else: - SC = 2 - s1 = shifts[SC][1][0] - s2 = shifts[SC][2][0] - s3 = shifts[SC][3][0] - a = [0] * BC - # temporary work array - t = [] - # plaintext to ints + key - for i in xrange(BC): - t.append((ord(plaintext[i * 4 ]) << 24 | - ord(plaintext[i * 4 + 1]) << 16 | - ord(plaintext[i * 4 + 2]) << 8 | - ord(plaintext[i * 4 + 3]) ) ^ Ke[0][i]) - # apply round transforms - for r in xrange(1, ROUNDS): - for i in xrange(BC): - a[i] = (T1[(t[ i ] >> 24) & 0xFF] ^ - T2[(t[(i + s1) % BC] >> 16) & 0xFF] ^ - T3[(t[(i + s2) % BC] >> 8) & 0xFF] ^ - T4[ t[(i + s3) % BC] & 0xFF] ) ^ Ke[r][i] - t = copy.copy(a) - # last round is special - result = [] - for i in xrange(BC): - tt = Ke[ROUNDS][i] - result.append((S[(t[ i ] >> 24) & 0xFF] ^ (tt >> 24)) & 0xFF) - result.append((S[(t[(i + s1) % BC] >> 16) & 0xFF] ^ (tt >> 16)) & 0xFF) - result.append((S[(t[(i + s2) % BC] >> 8) & 0xFF] ^ (tt >> 8)) & 0xFF) - result.append((S[ t[(i + s3) % BC] & 0xFF] ^ tt ) & 0xFF) - return string.join(map(chr, result), '') - - def decrypt(self, ciphertext): - if len(ciphertext) != self.block_size: - raise ValueError('wrong block length, expected ' + str(self.block_size) + ' got ' + str(len(plaintext))) - Kd = self.Kd - - BC = self.block_size / 4 - ROUNDS = len(Kd) - 1 - if BC == 4: - SC = 0 - elif BC == 6: - SC = 1 - else: - SC = 2 - s1 = shifts[SC][1][1] - s2 = shifts[SC][2][1] - s3 = shifts[SC][3][1] - a = [0] * BC - # temporary work array - t = [0] * BC - # ciphertext to ints + key - for i in xrange(BC): - t[i] = (ord(ciphertext[i * 4 ]) << 24 | - ord(ciphertext[i * 4 + 1]) << 16 | - ord(ciphertext[i * 4 + 2]) << 8 | - ord(ciphertext[i * 4 + 3]) ) ^ Kd[0][i] - # apply round transforms - for r in xrange(1, ROUNDS): - for i in xrange(BC): - a[i] = (T5[(t[ i ] >> 24) & 0xFF] ^ - T6[(t[(i + s1) % BC] >> 16) & 0xFF] ^ - T7[(t[(i + s2) % BC] >> 8) & 0xFF] ^ - T8[ t[(i + s3) % BC] & 0xFF] ) ^ Kd[r][i] - t = copy.copy(a) - # last round is special - result = [] - for i in xrange(BC): - tt = Kd[ROUNDS][i] - result.append((Si[(t[ i ] >> 24) & 0xFF] ^ (tt >> 24)) & 0xFF) - result.append((Si[(t[(i + s1) % BC] >> 16) & 0xFF] ^ (tt >> 16)) & 0xFF) - result.append((Si[(t[(i + s2) % BC] >> 8) & 0xFF] ^ (tt >> 8)) & 0xFF) - result.append((Si[ t[(i + s3) % BC] & 0xFF] ^ tt ) & 0xFF) - return string.join(map(chr, result), '') - -def encrypt(key, block): - return rijndael(key, len(block)).encrypt(block) - -def decrypt(key, block): - return rijndael(key, len(block)).decrypt(block) - -def test(): - def t(kl, bl): - b = 'b' * bl - r = rijndael('a' * kl, bl) - assert r.decrypt(r.encrypt(b)) == b - t(16, 16) - t(16, 24) - t(16, 32) - t(24, 16) - t(24, 24) - t(24, 32) - t(32, 16) - t(32, 24) - t(32, 32) - diff --git a/gdata/analytics/tlslite/utils/win32prng.c b/gdata/analytics/tlslite/utils/win32prng.c deleted file mode 100644 index de08b3b3b9..0000000000 --- a/gdata/analytics/tlslite/utils/win32prng.c +++ /dev/null @@ -1,63 +0,0 @@ - -#include "Python.h" -#define _WIN32_WINNT 0x0400 /* Needed for CryptoAPI on some systems */ -#include - - -static PyObject* getRandomBytes(PyObject *self, PyObject *args) -{ - int howMany; - HCRYPTPROV hCryptProv; - unsigned char* bytes = NULL; - PyObject* returnVal = NULL; - - - /* Read Arguments */ - if (!PyArg_ParseTuple(args, "i", &howMany)) - return(NULL); - - /* Get Context */ - if(CryptAcquireContext( - &hCryptProv, - NULL, - NULL, - PROV_RSA_FULL, - CRYPT_VERIFYCONTEXT) == 0) - return Py_BuildValue("s#", NULL, 0); - - - /* Allocate bytes */ - bytes = malloc(howMany); - - - /* Get random data */ - if(CryptGenRandom( - hCryptProv, - howMany, - bytes) == 0) - returnVal = Py_BuildValue("s#", NULL, 0); - else - returnVal = Py_BuildValue("s#", bytes, howMany); - - free(bytes); - CryptReleaseContext(hCryptProv, 0); - - return returnVal; -} - - - -/* List of functions exported by this module */ - -static struct PyMethodDef win32prng_functions[] = { - {"getRandomBytes", (PyCFunction)getRandomBytes, METH_VARARGS}, - {NULL, NULL} /* Sentinel */ -}; - - -/* Initialize this module. */ - -DL_EXPORT(void) initwin32prng(void) -{ - Py_InitModule("win32prng", win32prng_functions); -} diff --git a/gdata/analytics/tlslite/utils/xmltools.py b/gdata/analytics/tlslite/utils/xmltools.py deleted file mode 100644 index c1e8c4d950..0000000000 --- a/gdata/analytics/tlslite/utils/xmltools.py +++ /dev/null @@ -1,202 +0,0 @@ -"""Helper functions for XML. - -This module has misc. helper functions for working with XML DOM nodes.""" - -from compat import * -import os -import re - -if os.name == "java": - # Only for Jython - from javax.xml.parsers import * - import java - - builder = DocumentBuilderFactory.newInstance().newDocumentBuilder() - - def parseDocument(s): - stream = java.io.ByteArrayInputStream(java.lang.String(s).getBytes()) - return builder.parse(stream) -else: - from xml.dom import minidom - from xml.sax import saxutils - - def parseDocument(s): - return minidom.parseString(s) - -def parseAndStripWhitespace(s): - try: - element = parseDocument(s).documentElement - except BaseException, e: - raise SyntaxError(str(e)) - stripWhitespace(element) - return element - -#Goes through a DOM tree and removes whitespace besides child elements, -#as long as this whitespace is correctly tab-ified -def stripWhitespace(element, tab=0): - element.normalize() - - lastSpacer = "\n" + ("\t"*tab) - spacer = lastSpacer + "\t" - - #Zero children aren't allowed (i.e. ) - #This makes writing output simpler, and matches Canonical XML - if element.childNodes.length==0: #DON'T DO len(element.childNodes) - doesn't work in Jython - raise SyntaxError("Empty XML elements not allowed") - - #If there's a single child, it must be text context - if element.childNodes.length==1: - if element.firstChild.nodeType == element.firstChild.TEXT_NODE: - #If it's an empty element, remove - if element.firstChild.data == lastSpacer: - element.removeChild(element.firstChild) - return - #If not text content, give an error - elif element.firstChild.nodeType == element.firstChild.ELEMENT_NODE: - raise SyntaxError("Bad whitespace under '%s'" % element.tagName) - else: - raise SyntaxError("Unexpected node type in XML document") - - #Otherwise there's multiple child element - child = element.firstChild - while child: - if child.nodeType == child.ELEMENT_NODE: - stripWhitespace(child, tab+1) - child = child.nextSibling - elif child.nodeType == child.TEXT_NODE: - if child == element.lastChild: - if child.data != lastSpacer: - raise SyntaxError("Bad whitespace under '%s'" % element.tagName) - elif child.data != spacer: - raise SyntaxError("Bad whitespace under '%s'" % element.tagName) - next = child.nextSibling - element.removeChild(child) - child = next - else: - raise SyntaxError("Unexpected node type in XML document") - - -def checkName(element, name): - if element.nodeType != element.ELEMENT_NODE: - raise SyntaxError("Missing element: '%s'" % name) - - if name == None: - return - - if element.tagName != name: - raise SyntaxError("Wrong element name: should be '%s', is '%s'" % (name, element.tagName)) - -def getChild(element, index, name=None): - if element.nodeType != element.ELEMENT_NODE: - raise SyntaxError("Wrong node type in getChild()") - - child = element.childNodes.item(index) - if child == None: - raise SyntaxError("Missing child: '%s'" % name) - checkName(child, name) - return child - -def getChildIter(element, index): - class ChildIter: - def __init__(self, element, index): - self.element = element - self.index = index - - def next(self): - if self.index < len(self.element.childNodes): - retVal = self.element.childNodes.item(self.index) - self.index += 1 - else: - retVal = None - return retVal - - def checkEnd(self): - if self.index != len(self.element.childNodes): - raise SyntaxError("Too many elements under: '%s'" % self.element.tagName) - return ChildIter(element, index) - -def getChildOrNone(element, index): - if element.nodeType != element.ELEMENT_NODE: - raise SyntaxError("Wrong node type in getChild()") - child = element.childNodes.item(index) - return child - -def getLastChild(element, index, name=None): - if element.nodeType != element.ELEMENT_NODE: - raise SyntaxError("Wrong node type in getLastChild()") - - child = element.childNodes.item(index) - if child == None: - raise SyntaxError("Missing child: '%s'" % name) - if child != element.lastChild: - raise SyntaxError("Too many elements under: '%s'" % element.tagName) - checkName(child, name) - return child - -#Regular expressions for syntax-checking attribute and element content -nsRegEx = "http://trevp.net/cryptoID\Z" -cryptoIDRegEx = "([a-km-z3-9]{5}\.){3}[a-km-z3-9]{5}\Z" -urlRegEx = "http(s)?://.{1,100}\Z" -sha1Base64RegEx = "[A-Za-z0-9+/]{27}=\Z" -base64RegEx = "[A-Za-z0-9+/]+={0,4}\Z" -certsListRegEx = "(0)?(1)?(2)?(3)?(4)?(5)?(6)?(7)?(8)?(9)?\Z" -keyRegEx = "[A-Z]\Z" -keysListRegEx = "(A)?(B)?(C)?(D)?(E)?(F)?(G)?(H)?(I)?(J)?(K)?(L)?(M)?(N)?(O)?(P)?(Q)?(R)?(S)?(T)?(U)?(V)?(W)?(X)?(Y)?(Z)?\Z" -dateTimeRegEx = "\d\d\d\d-\d\d-\d\dT\d\d:\d\d:\d\dZ\Z" -shortStringRegEx = ".{1,100}\Z" -exprRegEx = "[a-zA-Z0-9 ,()]{1,200}\Z" -notAfterDeltaRegEx = "0|([1-9][0-9]{0,8})\Z" #A number from 0 to (1 billion)-1 -booleanRegEx = "(true)|(false)" - -def getReqAttribute(element, attrName, regEx=""): - if element.nodeType != element.ELEMENT_NODE: - raise SyntaxError("Wrong node type in getReqAttribute()") - - value = element.getAttribute(attrName) - if not value: - raise SyntaxError("Missing Attribute: " + attrName) - if not re.match(regEx, value): - raise SyntaxError("Bad Attribute Value for '%s': '%s' " % (attrName, value)) - element.removeAttribute(attrName) - return str(value) #de-unicode it; this is needed for bsddb, for example - -def getAttribute(element, attrName, regEx=""): - if element.nodeType != element.ELEMENT_NODE: - raise SyntaxError("Wrong node type in getAttribute()") - - value = element.getAttribute(attrName) - if value: - if not re.match(regEx, value): - raise SyntaxError("Bad Attribute Value for '%s': '%s' " % (attrName, value)) - element.removeAttribute(attrName) - return str(value) #de-unicode it; this is needed for bsddb, for example - -def checkNoMoreAttributes(element): - if element.nodeType != element.ELEMENT_NODE: - raise SyntaxError("Wrong node type in checkNoMoreAttributes()") - - if element.attributes.length!=0: - raise SyntaxError("Extra attributes on '%s'" % element.tagName) - -def getText(element, regEx=""): - textNode = element.firstChild - if textNode == None: - raise SyntaxError("Empty element '%s'" % element.tagName) - if textNode.nodeType != textNode.TEXT_NODE: - raise SyntaxError("Non-text node: '%s'" % element.tagName) - if not re.match(regEx, textNode.data): - raise SyntaxError("Bad Text Value for '%s': '%s' " % (element.tagName, textNode.data)) - return str(textNode.data) #de-unicode it; this is needed for bsddb, for example - -#Function for adding tabs to a string -def indent(s, steps, ch="\t"): - tabs = ch*steps - if s[-1] != "\n": - s = tabs + s.replace("\n", "\n"+tabs) - else: - s = tabs + s.replace("\n", "\n"+tabs) - s = s[ : -len(tabs)] - return s - -def escape(s): - return saxutils.escape(s) diff --git a/gdata/analytics/urlfetch.py b/gdata/analytics/urlfetch.py deleted file mode 100644 index 890b257d67..0000000000 --- a/gdata/analytics/urlfetch.py +++ /dev/null @@ -1,247 +0,0 @@ -#!/usr/bin/python -# -# Copyright (C) 2008 Google 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. - - -"""Provides HTTP functions for gdata.service to use on Google App Engine - -AppEngineHttpClient: Provides an HTTP request method which uses App Engine's - urlfetch API. Set the http_client member of a GDataService object to an - instance of an AppEngineHttpClient to allow the gdata library to run on - Google App Engine. - -run_on_appengine: Function which will modify an existing GDataService object - to allow it to run on App Engine. It works by creating a new instance of - the AppEngineHttpClient and replacing the GDataService object's - http_client. - -HttpRequest: Function that wraps google.appengine.api.urlfetch.Fetch in a - common interface which is used by gdata.service.GDataService. In other - words, this module can be used as the gdata service request handler so - that all HTTP requests will be performed by the hosting Google App Engine - server. -""" - - -__author__ = 'api.jscudder (Jeff Scudder)' - - -import StringIO -import atom.service -import atom.http_interface -from google.appengine.api import urlfetch - - -def run_on_appengine(gdata_service): - """Modifies a GDataService object to allow it to run on App Engine. - - Args: - gdata_service: An instance of AtomService, GDataService, or any - of their subclasses which has an http_client member. - """ - gdata_service.http_client = AppEngineHttpClient() - - -class AppEngineHttpClient(atom.http_interface.GenericHttpClient): - def __init__(self, headers=None): - self.debug = False - self.headers = headers or {} - - def request(self, operation, url, data=None, headers=None): - """Performs an HTTP call to the server, supports GET, POST, PUT, and - DELETE. - - Usage example, perform and HTTP GET on http://www.google.com/: - import atom.http - client = atom.http.HttpClient() - http_response = client.request('GET', 'http://www.google.com/') - - Args: - operation: str The HTTP operation to be performed. This is usually one - of 'GET', 'POST', 'PUT', or 'DELETE' - data: filestream, list of parts, or other object which can be converted - to a string. Should be set to None when performing a GET or DELETE. - If data is a file-like object which can be read, this method will - read a chunk of 100K bytes at a time and send them. - If the data is a list of parts to be sent, each part will be - evaluated and sent. - url: The full URL to which the request should be sent. Can be a string - or atom.url.Url. - headers: dict of strings. HTTP headers which should be sent - in the request. - """ - all_headers = self.headers.copy() - if headers: - all_headers.update(headers) - - # Construct the full payload. - # Assume that data is None or a string. - data_str = data - if data: - if isinstance(data, list): - # If data is a list of different objects, convert them all to strings - # and join them together. - converted_parts = [__ConvertDataPart(x) for x in data] - data_str = ''.join(converted_parts) - else: - data_str = __ConvertDataPart(data) - - # If the list of headers does not include a Content-Length, attempt to - # calculate it based on the data object. - if data and 'Content-Length' not in all_headers: - all_headers['Content-Length'] = len(data_str) - - # Set the content type to the default value if none was set. - if 'Content-Type' not in all_headers: - all_headers['Content-Type'] = 'application/atom+xml' - - # Lookup the urlfetch operation which corresponds to the desired HTTP verb. - if operation == 'GET': - method = urlfetch.GET - elif operation == 'POST': - method = urlfetch.POST - elif operation == 'PUT': - method = urlfetch.PUT - elif operation == 'DELETE': - method = urlfetch.DELETE - else: - method = None - return HttpResponse(urlfetch.Fetch(url=str(url), payload=data_str, - method=method, headers=all_headers)) - - -def HttpRequest(service, operation, data, uri, extra_headers=None, - url_params=None, escape_params=True, content_type='application/atom+xml'): - """Performs an HTTP call to the server, supports GET, POST, PUT, and DELETE. - - This function is deprecated, use AppEngineHttpClient.request instead. - - To use this module with gdata.service, you can set this module to be the - http_request_handler so that HTTP requests use Google App Engine's urlfetch. - import gdata.service - import gdata.urlfetch - gdata.service.http_request_handler = gdata.urlfetch - - Args: - service: atom.AtomService object which contains some of the parameters - needed to make the request. The following members are used to - construct the HTTP call: server (str), additional_headers (dict), - port (int), and ssl (bool). - operation: str The HTTP operation to be performed. This is usually one of - 'GET', 'POST', 'PUT', or 'DELETE' - data: filestream, list of parts, or other object which can be - converted to a string. - Should be set to None when performing a GET or PUT. - If data is a file-like object which can be read, this method will read - a chunk of 100K bytes at a time and send them. - If the data is a list of parts to be sent, each part will be evaluated - and sent. - uri: The beginning of the URL to which the request should be sent. - Examples: '/', '/base/feeds/snippets', - '/m8/feeds/contacts/default/base' - extra_headers: dict of strings. HTTP headers which should be sent - in the request. These headers are in addition to those stored in - service.additional_headers. - url_params: dict of strings. Key value pairs to be added to the URL as - URL parameters. For example {'foo':'bar', 'test':'param'} will - become ?foo=bar&test=param. - escape_params: bool default True. If true, the keys and values in - url_params will be URL escaped when the form is constructed - (Special characters converted to %XX form.) - content_type: str The MIME type for the data being sent. Defaults to - 'application/atom+xml', this is only used if data is set. - """ - full_uri = atom.service.BuildUri(uri, url_params, escape_params) - (server, port, ssl, partial_uri) = atom.service.ProcessUrl(service, full_uri) - # Construct the full URL for the request. - if ssl: - full_url = 'https://%s%s' % (server, partial_uri) - else: - full_url = 'http://%s%s' % (server, partial_uri) - - # Construct the full payload. - # Assume that data is None or a string. - data_str = data - if data: - if isinstance(data, list): - # If data is a list of different objects, convert them all to strings - # and join them together. - converted_parts = [__ConvertDataPart(x) for x in data] - data_str = ''.join(converted_parts) - else: - data_str = __ConvertDataPart(data) - - # Construct the dictionary of HTTP headers. - headers = {} - if isinstance(service.additional_headers, dict): - headers = service.additional_headers.copy() - if isinstance(extra_headers, dict): - for header, value in extra_headers.iteritems(): - headers[header] = value - # Add the content type header (we don't need to calculate content length, - # since urlfetch.Fetch will calculate for us). - if content_type: - headers['Content-Type'] = content_type - - # Lookup the urlfetch operation which corresponds to the desired HTTP verb. - if operation == 'GET': - method = urlfetch.GET - elif operation == 'POST': - method = urlfetch.POST - elif operation == 'PUT': - method = urlfetch.PUT - elif operation == 'DELETE': - method = urlfetch.DELETE - else: - method = None - return HttpResponse(urlfetch.Fetch(url=full_url, payload=data_str, - method=method, headers=headers)) - - -def __ConvertDataPart(data): - if not data or isinstance(data, str): - return data - elif hasattr(data, 'read'): - # data is a file like object, so read it completely. - return data.read() - # The data object was not a file. - # Try to convert to a string and send the data. - return str(data) - - -class HttpResponse(object): - """Translates a urlfetch resoinse to look like an hhtplib resoinse. - - Used to allow the resoinse from HttpRequest to be usable by gdata.service - methods. - """ - - def __init__(self, urlfetch_response): - self.body = StringIO.StringIO(urlfetch_response.content) - self.headers = urlfetch_response.headers - self.status = urlfetch_response.status_code - self.reason = '' - - def read(self, length=None): - if not length: - return self.body.read() - else: - return self.body.read(length) - - def getheader(self, name): - if not self.headers.has_key(name): - return self.headers[name.lower()] - return self.headers[name] - diff --git a/gdata/analytics/webmastertools/__init__.py b/gdata/analytics/webmastertools/__init__.py deleted file mode 100644 index 7ad20ff35e..0000000000 --- a/gdata/analytics/webmastertools/__init__.py +++ /dev/null @@ -1,544 +0,0 @@ -#!/usr/bin/python -# -# Copyright (C) 2008 Yu-Jie Lin -# -# 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. - -"""Contains extensions to Atom objects used with Google Webmaster Tools.""" - - -__author__ = 'livibetter (Yu-Jie Lin)' - - -try: - from xml.etree import cElementTree as ElementTree -except ImportError: - try: - import cElementTree as ElementTree - except ImportError: - try: - from xml.etree import ElementTree - except ImportError: - from elementtree import ElementTree -import atom -import gdata - - -# XML namespaces which are often used in Google Webmaster Tools entities. -GWEBMASTERTOOLS_NAMESPACE = 'http://schemas.google.com/webmasters/tools/2007' -GWEBMASTERTOOLS_TEMPLATE = '{http://schemas.google.com/webmasters/tools/2007}%s' - - -class Indexed(atom.AtomBase): - _tag = 'indexed' - _namespace = GWEBMASTERTOOLS_NAMESPACE - - -def IndexedFromString(xml_string): - return atom.CreateClassFromXMLString(Indexed, xml_string) - - -class Crawled(atom.Date): - _tag = 'crawled' - _namespace = GWEBMASTERTOOLS_NAMESPACE - - -def CrawledFromString(xml_string): - return atom.CreateClassFromXMLString(Crawled, xml_string) - - -class GeoLocation(atom.AtomBase): - _tag = 'geolocation' - _namespace = GWEBMASTERTOOLS_NAMESPACE - - -def GeoLocationFromString(xml_string): - return atom.CreateClassFromXMLString(GeoLocation, xml_string) - - -class PreferredDomain(atom.AtomBase): - _tag = 'preferred-domain' - _namespace = GWEBMASTERTOOLS_NAMESPACE - - -def PreferredDomainFromString(xml_string): - return atom.CreateClassFromXMLString(PreferredDomain, xml_string) - - -class CrawlRate(atom.AtomBase): - _tag = 'crawl-rate' - _namespace = GWEBMASTERTOOLS_NAMESPACE - - -def CrawlRateFromString(xml_string): - return atom.CreateClassFromXMLString(CrawlRate, xml_string) - - -class EnhancedImageSearch(atom.AtomBase): - _tag = 'enhanced-image-search' - _namespace = GWEBMASTERTOOLS_NAMESPACE - - -def EnhancedImageSearchFromString(xml_string): - return atom.CreateClassFromXMLString(EnhancedImageSearch, xml_string) - - -class Verified(atom.AtomBase): - _tag = 'verified' - _namespace = GWEBMASTERTOOLS_NAMESPACE - - -def VerifiedFromString(xml_string): - return atom.CreateClassFromXMLString(Verified, xml_string) - - -class VerificationMethodMeta(atom.AtomBase): - _tag = 'meta' - _namespace = atom.ATOM_NAMESPACE - _children = atom.AtomBase._children.copy() - _attributes = atom.AtomBase._attributes.copy() - _attributes['name'] = 'name' - _attributes['content'] = 'content' - - def __init__(self, text=None, name=None, content=None, - extension_elements=None, extension_attributes=None): - self.text = text - self.name = name - self.content = content - self.extension_elements = extension_elements or [] - self.extension_attributes = extension_attributes or {} - - -def VerificationMethodMetaFromString(xml_string): - return atom.CreateClassFromXMLString(VerificationMethodMeta, xml_string) - - -class VerificationMethod(atom.AtomBase): - _tag = 'verification-method' - _namespace = GWEBMASTERTOOLS_NAMESPACE - _children = atom.Text._children.copy() - _attributes = atom.Text._attributes.copy() - _children['{%s}meta' % atom.ATOM_NAMESPACE] = ( - 'meta', VerificationMethodMeta) - _attributes['in-use'] = 'in_use' - _attributes['type'] = 'type' - - def __init__(self, text=None, in_use=None, meta=None, type=None, - extension_elements=None, extension_attributes=None): - self.text = text - self.in_use = in_use - self.meta = meta - self.type = type - self.extension_elements = extension_elements or [] - self.extension_attributes = extension_attributes or {} - - -def VerificationMethodFromString(xml_string): - return atom.CreateClassFromXMLString(VerificationMethod, xml_string) - - -class MarkupLanguage(atom.AtomBase): - _tag = 'markup-language' - _namespace = GWEBMASTERTOOLS_NAMESPACE - - -def MarkupLanguageFromString(xml_string): - return atom.CreateClassFromXMLString(MarkupLanguage, xml_string) - - -class SitemapMobile(atom.AtomBase): - _tag = 'sitemap-mobile' - _namespace = GWEBMASTERTOOLS_NAMESPACE - _children = atom.AtomBase._children.copy() - _attributes = atom.AtomBase._attributes.copy() - _children['{%s}markup-language' % GWEBMASTERTOOLS_NAMESPACE] = ( - 'markup_language', [MarkupLanguage]) - - def __init__(self, markup_language=None, - extension_elements=None, extension_attributes=None, text=None): - - self.markup_language = markup_language or [] - self.text = text - self.extension_elements = extension_elements or [] - self.extension_attributes = extension_attributes or {} - - -def SitemapMobileFromString(xml_string): - return atom.CreateClassFromXMLString(SitemapMobile, xml_string) - - -class SitemapMobileMarkupLanguage(atom.AtomBase): - _tag = 'sitemap-mobile-markup-language' - _namespace = GWEBMASTERTOOLS_NAMESPACE - - -def SitemapMobileMarkupLanguageFromString(xml_string): - return atom.CreateClassFromXMLString(SitemapMobileMarkupLanguage, xml_string) - - -class PublicationLabel(atom.AtomBase): - _tag = 'publication-label' - _namespace = GWEBMASTERTOOLS_NAMESPACE - - -def PublicationLabelFromString(xml_string): - return atom.CreateClassFromXMLString(PublicationLabel, xml_string) - - -class SitemapNews(atom.AtomBase): - _tag = 'sitemap-news' - _namespace = GWEBMASTERTOOLS_NAMESPACE - _children = atom.AtomBase._children.copy() - _attributes = atom.AtomBase._attributes.copy() - _children['{%s}publication-label' % GWEBMASTERTOOLS_NAMESPACE] = ( - 'publication_label', [PublicationLabel]) - - def __init__(self, publication_label=None, - extension_elements=None, extension_attributes=None, text=None): - - self.publication_label = publication_label or [] - self.text = text - self.extension_elements = extension_elements or [] - self.extension_attributes = extension_attributes or {} - - -def SitemapNewsFromString(xml_string): - return atom.CreateClassFromXMLString(SitemapNews, xml_string) - - -class SitemapNewsPublicationLabel(atom.AtomBase): - _tag = 'sitemap-news-publication-label' - _namespace = GWEBMASTERTOOLS_NAMESPACE - - -def SitemapNewsPublicationLabelFromString(xml_string): - return atom.CreateClassFromXMLString(SitemapNewsPublicationLabel, xml_string) - - -class SitemapLastDownloaded(atom.Date): - _tag = 'sitemap-last-downloaded' - _namespace = GWEBMASTERTOOLS_NAMESPACE - - -def SitemapLastDownloadedFromString(xml_string): - return atom.CreateClassFromXMLString(SitemapLastDownloaded, xml_string) - - -class SitemapType(atom.AtomBase): - _tag = 'sitemap-type' - _namespace = GWEBMASTERTOOLS_NAMESPACE - - -def SitemapTypeFromString(xml_string): - return atom.CreateClassFromXMLString(SitemapType, xml_string) - - -class SitemapStatus(atom.AtomBase): - _tag = 'sitemap-status' - _namespace = GWEBMASTERTOOLS_NAMESPACE - - -def SitemapStatusFromString(xml_string): - return atom.CreateClassFromXMLString(SitemapStatus, xml_string) - - -class SitemapUrlCount(atom.AtomBase): - _tag = 'sitemap-url-count' - _namespace = GWEBMASTERTOOLS_NAMESPACE - - -def SitemapUrlCountFromString(xml_string): - return atom.CreateClassFromXMLString(SitemapUrlCount, xml_string) - - -class LinkFinder(atom.LinkFinder): - """An "interface" providing methods to find link elements - - SitesEntry elements often contain multiple links which differ in the rel - attribute or content type. Often, developers are interested in a specific - type of link so this class provides methods to find specific classes of links. - - This class is used as a mixin in SitesEntry. - """ - - def GetSelfLink(self): - """Find the first link with rel set to 'self' - - Returns: - An atom.Link or none if none of the links had rel equal to 'self' - """ - - for a_link in self.link: - if a_link.rel == 'self': - return a_link - return None - - def GetEditLink(self): - for a_link in self.link: - if a_link.rel == 'edit': - return a_link - return None - - def GetPostLink(self): - """Get a link containing the POST target URL. - - The POST target URL is used to insert new entries. - - Returns: - A link object with a rel matching the POST type. - """ - for a_link in self.link: - if a_link.rel == 'http://schemas.google.com/g/2005#post': - return a_link - return None - - def GetFeedLink(self): - for a_link in self.link: - if a_link.rel == 'http://schemas.google.com/g/2005#feed': - return a_link - return None - - -class SitesEntry(atom.Entry, LinkFinder): - """A Google Webmaster Tools meta Entry flavor of an Atom Entry """ - - _tag = atom.Entry._tag - _namespace = atom.Entry._namespace - _children = atom.Entry._children.copy() - _attributes = atom.Entry._attributes.copy() - _children['{%s}entryLink' % gdata.GDATA_NAMESPACE] = ( - 'entry_link', [gdata.EntryLink]) - _children['{%s}indexed' % GWEBMASTERTOOLS_NAMESPACE] = ('indexed', Indexed) - _children['{%s}crawled' % GWEBMASTERTOOLS_NAMESPACE] = ( - 'crawled', Crawled) - _children['{%s}geolocation' % GWEBMASTERTOOLS_NAMESPACE] = ( - 'geolocation', GeoLocation) - _children['{%s}preferred-domain' % GWEBMASTERTOOLS_NAMESPACE] = ( - 'preferred_domain', PreferredDomain) - _children['{%s}crawl-rate' % GWEBMASTERTOOLS_NAMESPACE] = ( - 'crawl_rate', CrawlRate) - _children['{%s}enhanced-image-search' % GWEBMASTERTOOLS_NAMESPACE] = ( - 'enhanced_image_search', EnhancedImageSearch) - _children['{%s}verified' % GWEBMASTERTOOLS_NAMESPACE] = ( - 'verified', Verified) - _children['{%s}verification-method' % GWEBMASTERTOOLS_NAMESPACE] = ( - 'verification_method', [VerificationMethod]) - - def __GetId(self): - return self.__id - - # This method was created to strip the unwanted whitespace from the id's - # text node. - def __SetId(self, id): - self.__id = id - if id is not None and id.text is not None: - self.__id.text = id.text.strip() - - id = property(__GetId, __SetId) - - def __init__(self, category=None, content=None, - atom_id=None, link=None, title=None, updated=None, - entry_link=None, indexed=None, crawled=None, - geolocation=None, preferred_domain=None, crawl_rate=None, - enhanced_image_search=None, - verified=None, verification_method=None, - extension_elements=None, extension_attributes=None, text=None): - atom.Entry.__init__(self, category=category, - content=content, atom_id=atom_id, link=link, - title=title, updated=updated, text=text) - - self.entry_link = entry_link or [] - self.indexed = indexed - self.crawled = crawled - self.geolocation = geolocation - self.preferred_domain = preferred_domain - self.crawl_rate = crawl_rate - self.enhanced_image_search = enhanced_image_search - self.verified = verified - self.verification_method = verification_method or [] - - -def SitesEntryFromString(xml_string): - return atom.CreateClassFromXMLString(SitesEntry, xml_string) - - -class SitesFeed(atom.Feed, LinkFinder): - """A Google Webmaster Tools meta Sites feed flavor of an Atom Feed""" - - _tag = atom.Feed._tag - _namespace = atom.Feed._namespace - _children = atom.Feed._children.copy() - _attributes = atom.Feed._attributes.copy() - _children['{%s}startIndex' % gdata.OPENSEARCH_NAMESPACE] = ( - 'start_index', gdata.StartIndex) - _children['{%s}entry' % atom.ATOM_NAMESPACE] = ('entry', [SitesEntry]) - del _children['{%s}generator' % atom.ATOM_NAMESPACE] - del _children['{%s}author' % atom.ATOM_NAMESPACE] - del _children['{%s}contributor' % atom.ATOM_NAMESPACE] - del _children['{%s}logo' % atom.ATOM_NAMESPACE] - del _children['{%s}icon' % atom.ATOM_NAMESPACE] - del _children['{%s}rights' % atom.ATOM_NAMESPACE] - del _children['{%s}subtitle' % atom.ATOM_NAMESPACE] - - def __GetId(self): - return self.__id - - def __SetId(self, id): - self.__id = id - if id is not None and id.text is not None: - self.__id.text = id.text.strip() - - id = property(__GetId, __SetId) - - def __init__(self, start_index=None, atom_id=None, title=None, entry=None, - category=None, link=None, updated=None, - extension_elements=None, extension_attributes=None, text=None): - """Constructor for Source - - Args: - category: list (optional) A list of Category instances - id: Id (optional) The entry's Id element - link: list (optional) A list of Link instances - title: Title (optional) the entry's title element - updated: Updated (optional) the entry's updated element - entry: list (optional) A list of the Entry instances contained in the - feed. - text: String (optional) The text contents of the element. This is the - contents of the Entry's XML text node. - (Example: This is the text) - extension_elements: list (optional) A list of ExtensionElement instances - which are children of this element. - extension_attributes: dict (optional) A dictionary of strings which are - the values for additional XML attributes of this element. - """ - - self.start_index = start_index - self.category = category or [] - self.id = atom_id - self.link = link or [] - self.title = title - self.updated = updated - self.entry = entry or [] - self.text = text - self.extension_elements = extension_elements or [] - self.extension_attributes = extension_attributes or {} - - -def SitesFeedFromString(xml_string): - return atom.CreateClassFromXMLString(SitesFeed, xml_string) - - -class SitemapsEntry(atom.Entry, LinkFinder): - """A Google Webmaster Tools meta Sitemaps Entry flavor of an Atom Entry """ - - _tag = atom.Entry._tag - _namespace = atom.Entry._namespace - _children = atom.Entry._children.copy() - _attributes = atom.Entry._attributes.copy() - _children['{%s}sitemap-type' % GWEBMASTERTOOLS_NAMESPACE] = ( - 'sitemap_type', SitemapType) - _children['{%s}sitemap-status' % GWEBMASTERTOOLS_NAMESPACE] = ( - 'sitemap_status', SitemapStatus) - _children['{%s}sitemap-last-downloaded' % GWEBMASTERTOOLS_NAMESPACE] = ( - 'sitemap_last_downloaded', SitemapLastDownloaded) - _children['{%s}sitemap-url-count' % GWEBMASTERTOOLS_NAMESPACE] = ( - 'sitemap_url_count', SitemapUrlCount) - _children['{%s}sitemap-mobile-markup-language' % GWEBMASTERTOOLS_NAMESPACE] \ - = ('sitemap_mobile_markup_language', SitemapMobileMarkupLanguage) - _children['{%s}sitemap-news-publication-label' % GWEBMASTERTOOLS_NAMESPACE] \ - = ('sitemap_news_publication_label', SitemapNewsPublicationLabel) - - def __GetId(self): - return self.__id - - # This method was created to strip the unwanted whitespace from the id's - # text node. - def __SetId(self, id): - self.__id = id - if id is not None and id.text is not None: - self.__id.text = id.text.strip() - - id = property(__GetId, __SetId) - - def __init__(self, category=None, content=None, - atom_id=None, link=None, title=None, updated=None, - sitemap_type=None, sitemap_status=None, sitemap_last_downloaded=None, - sitemap_url_count=None, sitemap_mobile_markup_language=None, - sitemap_news_publication_label=None, - extension_elements=None, extension_attributes=None, text=None): - atom.Entry.__init__(self, category=category, - content=content, atom_id=atom_id, link=link, - title=title, updated=updated, text=text) - - self.sitemap_type = sitemap_type - self.sitemap_status = sitemap_status - self.sitemap_last_downloaded = sitemap_last_downloaded - self.sitemap_url_count = sitemap_url_count - self.sitemap_mobile_markup_language = sitemap_mobile_markup_language - self.sitemap_news_publication_label = sitemap_news_publication_label - - -def SitemapsEntryFromString(xml_string): - return atom.CreateClassFromXMLString(SitemapsEntry, xml_string) - - -class SitemapsFeed(atom.Feed, LinkFinder): - """A Google Webmaster Tools meta Sitemaps feed flavor of an Atom Feed""" - - _tag = atom.Feed._tag - _namespace = atom.Feed._namespace - _children = atom.Feed._children.copy() - _attributes = atom.Feed._attributes.copy() - _children['{%s}entry' % atom.ATOM_NAMESPACE] = ('entry', [SitemapsEntry]) - _children['{%s}sitemap-mobile' % GWEBMASTERTOOLS_NAMESPACE] = ( - 'sitemap_mobile', SitemapMobile) - _children['{%s}sitemap-news' % GWEBMASTERTOOLS_NAMESPACE] = ( - 'sitemap_news', SitemapNews) - del _children['{%s}generator' % atom.ATOM_NAMESPACE] - del _children['{%s}author' % atom.ATOM_NAMESPACE] - del _children['{%s}contributor' % atom.ATOM_NAMESPACE] - del _children['{%s}logo' % atom.ATOM_NAMESPACE] - del _children['{%s}icon' % atom.ATOM_NAMESPACE] - del _children['{%s}rights' % atom.ATOM_NAMESPACE] - del _children['{%s}subtitle' % atom.ATOM_NAMESPACE] - - def __GetId(self): - return self.__id - - def __SetId(self, id): - self.__id = id - if id is not None and id.text is not None: - self.__id.text = id.text.strip() - - id = property(__GetId, __SetId) - - def __init__(self, category=None, content=None, - atom_id=None, link=None, title=None, updated=None, - entry=None, sitemap_mobile=None, sitemap_news=None, - extension_elements=None, extension_attributes=None, text=None): - - self.category = category or [] - self.id = atom_id - self.link = link or [] - self.title = title - self.updated = updated - self.entry = entry or [] - self.text = text - self.sitemap_mobile = sitemap_mobile - self.sitemap_news = sitemap_news - self.extension_elements = extension_elements or [] - self.extension_attributes = extension_attributes or {} - - -def SitemapsFeedFromString(xml_string): - return atom.CreateClassFromXMLString(SitemapsFeed, xml_string) diff --git a/gdata/analytics/webmastertools/data.py b/gdata/analytics/webmastertools/data.py deleted file mode 100644 index 8b50a47a89..0000000000 --- a/gdata/analytics/webmastertools/data.py +++ /dev/null @@ -1,217 +0,0 @@ -#!/usr/bin/python -# -# Copyright (C) 2009 Google 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. - - -"""Contains the data classes of the Google Webmaster Tools Data API""" - - -__author__ = 'j.s@google.com (Jeff Scudder)' - - -import atom.core -import atom.data -import gdata.data -import gdata.opensearch.data - - -WT_TEMPLATE = '{http://schemas.google.com/webmaster/tools/2007/}%s' - - -class CrawlIssueCrawlType(atom.core.XmlElement): - """Type of crawl of the crawl issue""" - _qname = WT_TEMPLATE % 'crawl-type' - - -class CrawlIssueDateDetected(atom.core.XmlElement): - """Detection date for the issue""" - _qname = WT_TEMPLATE % 'date-detected' - - -class CrawlIssueDetail(atom.core.XmlElement): - """Detail of the crawl issue""" - _qname = WT_TEMPLATE % 'detail' - - -class CrawlIssueIssueType(atom.core.XmlElement): - """Type of crawl issue""" - _qname = WT_TEMPLATE % 'issue-type' - - -class CrawlIssueLinkedFromUrl(atom.core.XmlElement): - """Source URL that links to the issue URL""" - _qname = WT_TEMPLATE % 'linked-from' - - -class CrawlIssueUrl(atom.core.XmlElement): - """URL affected by the crawl issue""" - _qname = WT_TEMPLATE % 'url' - - -class CrawlIssueEntry(gdata.data.GDEntry): - """Describes a crawl issue entry""" - date_detected = CrawlIssueDateDetected - url = CrawlIssueUrl - detail = CrawlIssueDetail - issue_type = CrawlIssueIssueType - crawl_type = CrawlIssueCrawlType - linked_from = [CrawlIssueLinkedFromUrl] - - -class CrawlIssuesFeed(gdata.data.GDFeed): - """Feed of crawl issues for a particular site""" - entry = [CrawlIssueEntry] - - -class Indexed(atom.core.XmlElement): - """Describes the indexing status of a site""" - _qname = WT_TEMPLATE % 'indexed' - - -class Keyword(atom.core.XmlElement): - """A keyword in a site or in a link to a site""" - _qname = WT_TEMPLATE % 'keyword' - source = 'source' - - -class KeywordEntry(gdata.data.GDEntry): - """Describes a keyword entry""" - - -class KeywordsFeed(gdata.data.GDFeed): - """Feed of keywords for a particular site""" - entry = [KeywordEntry] - keyword = [Keyword] - - -class LastCrawled(atom.core.XmlElement): - """Describes the last crawled date of a site""" - _qname = WT_TEMPLATE % 'last-crawled' - - -class MessageBody(atom.core.XmlElement): - """Message body""" - _qname = WT_TEMPLATE % 'body' - - -class MessageDate(atom.core.XmlElement): - """Message date""" - _qname = WT_TEMPLATE % 'date' - - -class MessageLanguage(atom.core.XmlElement): - """Message language""" - _qname = WT_TEMPLATE % 'language' - - -class MessageRead(atom.core.XmlElement): - """Indicates if the message has already been read""" - _qname = WT_TEMPLATE % 'read' - - -class MessageSubject(atom.core.XmlElement): - """Message subject""" - _qname = WT_TEMPLATE % 'subject' - - -class SiteId(atom.core.XmlElement): - """Site URL""" - _qname = WT_TEMPLATE % 'id' - - -class MessageEntry(gdata.data.GDEntry): - """Describes a message entry""" - wt_id = SiteId - subject = MessageSubject - date = MessageDate - body = MessageBody - language = MessageLanguage - read = MessageRead - - -class MessagesFeed(gdata.data.GDFeed): - """Describes a messages feed""" - entry = [MessageEntry] - - -class SitemapEntry(gdata.data.GDEntry): - """Describes a sitemap entry""" - indexed = Indexed - wt_id = SiteId - - -class SitemapMobileMarkupLanguage(atom.core.XmlElement): - """Describes a markup language for URLs in this sitemap""" - _qname = WT_TEMPLATE % 'sitemap-mobile-markup-language' - - -class SitemapMobile(atom.core.XmlElement): - """Lists acceptable mobile markup languages for URLs in this sitemap""" - _qname = WT_TEMPLATE % 'sitemap-mobile' - sitemap_mobile_markup_language = [SitemapMobileMarkupLanguage] - - -class SitemapNewsPublicationLabel(atom.core.XmlElement): - """Specifies the publication label for this sitemap""" - _qname = WT_TEMPLATE % 'sitemap-news-publication-label' - - -class SitemapNews(atom.core.XmlElement): - """Lists publication labels for this sitemap""" - _qname = WT_TEMPLATE % 'sitemap-news' - sitemap_news_publication_label = [SitemapNewsPublicationLabel] - - -class SitemapType(atom.core.XmlElement): - """Indicates the type of sitemap. Not used for News or Mobile Sitemaps""" - _qname = WT_TEMPLATE % 'sitemap-type' - - -class SitemapUrlCount(atom.core.XmlElement): - """Indicates the number of URLs contained in the sitemap""" - _qname = WT_TEMPLATE % 'sitemap-url-count' - - -class SitemapsFeed(gdata.data.GDFeed): - """Describes a sitemaps feed""" - entry = [SitemapEntry] - - -class VerificationMethod(atom.core.XmlElement): - """Describes a verification method that may be used for a site""" - _qname = WT_TEMPLATE % 'verification-method' - in_use = 'in-use' - type = 'type' - - -class Verified(atom.core.XmlElement): - """Describes the verification status of a site""" - _qname = WT_TEMPLATE % 'verified' - - -class SiteEntry(gdata.data.GDEntry): - """Describes a site entry""" - indexed = Indexed - wt_id = SiteId - verified = Verified - last_crawled = LastCrawled - verification_method = [VerificationMethod] - - -class SitesFeed(gdata.data.GDFeed): - """Describes a sites feed""" - entry = [SiteEntry] - - diff --git a/gdata/analytics/webmastertools/service.py b/gdata/analytics/webmastertools/service.py deleted file mode 100644 index 8c3286db40..0000000000 --- a/gdata/analytics/webmastertools/service.py +++ /dev/null @@ -1,516 +0,0 @@ -#!/usr/bin/python -# -# Copyright (C) 2008 Yu-Jie Lin -# -# 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. - -"""GWebmasterToolsService extends the GDataService to streamline -Google Webmaster Tools operations. - - GWebmasterToolsService: Provides methods to query feeds and manipulate items. - Extends GDataService. -""" - -__author__ = 'livibetter (Yu-Jie Lin)' - -import urllib -import gdata -import atom.service -import gdata.service -import gdata.webmastertools as webmastertools -import atom - - -FEED_BASE = 'https://www.google.com/webmasters/tools/feeds/' -SITES_FEED = FEED_BASE + 'sites/' -SITE_TEMPLATE = SITES_FEED + '%s' -SITEMAPS_FEED_TEMPLATE = FEED_BASE + '%(site_id)s/sitemaps/' -SITEMAP_TEMPLATE = SITEMAPS_FEED_TEMPLATE + '%(sitemap_id)s' - - -class Error(Exception): - pass - - -class RequestError(Error): - pass - - -class GWebmasterToolsService(gdata.service.GDataService): - """Client for the Google Webmaster Tools service.""" - - def __init__(self, email=None, password=None, source=None, - server='www.google.com', **kwargs): - """Creates a client for the Google Webmaster Tools 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: 'www.google.com'. - **kwargs: The other parameters to pass to gdata.service.GDataService - constructor. - """ - gdata.service.GDataService.__init__( - self, email=email, password=password, service='sitemaps', source=source, - server=server, **kwargs) - - def GetSitesFeed(self, uri=SITES_FEED, - converter=webmastertools.SitesFeedFromString): - """Gets sites feed. - - Args: - uri: str (optional) URI to retrieve sites feed. - converter: func (optional) Function which is executed on the server's - response before it is returned. Usually this is a function like - SitesFeedFromString which will parse the response and turn it into - an object. - - Returns: - If converter is defined, the results of running converter on the server's - response. Otherwise, it will be a SitesFeed object. - """ - return self.Get(uri, converter=converter) - - def AddSite(self, site_uri, uri=SITES_FEED, - url_params=None, escape_params=True, converter=None): - """Adds a site to Google Webmaster Tools. - - Args: - site_uri: str URI of which site to add. - uri: str (optional) URI to add a site. - url_params: dict (optional) Additional URL parameters to be included - in the insertion request. - escape_params: boolean (optional) If true, the url_parameters will be - escaped before they are included in the request. - converter: func (optional) Function which is executed on the server's - response before it is returned. Usually this is a function like - SitesEntryFromString which will parse the response and turn it into - an object. - - Returns: - If converter is defined, the results of running converter on the server's - response. Otherwise, it will be a SitesEntry object. - """ - - site_entry = webmastertools.SitesEntry() - site_entry.content = atom.Content(src=site_uri) - response = self.Post(site_entry, uri, - url_params=url_params, - escape_params=escape_params, converter=converter) - if not converter and isinstance(response, atom.Entry): - return webmastertools.SitesEntryFromString(response.ToString()) - return response - - def DeleteSite(self, site_uri, uri=SITE_TEMPLATE, - url_params=None, escape_params=True): - """Removes a site from Google Webmaster Tools. - - Args: - site_uri: str URI of which site to remove. - uri: str (optional) A URI template to send DELETE request. - Default SITE_TEMPLATE. - url_params: dict (optional) Additional URL parameters to be included - in the insertion request. - escape_params: boolean (optional) If true, the url_parameters will be - escaped before they are included in the request. - - Returns: - True if the delete succeeded. - """ - - return self.Delete( - uri % urllib.quote_plus(site_uri), - url_params=url_params, escape_params=escape_params) - - def VerifySite(self, site_uri, verification_method, uri=SITE_TEMPLATE, - url_params=None, escape_params=True, converter=None): - """Requests a verification of a site. - - Args: - site_uri: str URI of which site to add sitemap for. - verification_method: str The method to verify a site. Valid values are - 'htmlpage', and 'metatag'. - uri: str (optional) URI template to update a site. - Default SITE_TEMPLATE. - url_params: dict (optional) Additional URL parameters to be included - in the insertion request. - escape_params: boolean (optional) If true, the url_parameters will be - escaped before they are included in the request. - converter: func (optional) Function which is executed on the server's - response before it is returned. Usually this is a function like - SitemapsEntryFromString which will parse the response and turn it into - an object. - - Returns: - If converter is defined, the results of running converter on the server's - response. Otherwise, it will be a SitesEntry object. - """ - - site_entry = webmastertools.SitesEntry( - atom_id=atom.Id(text=site_uri), - category=atom.Category( - scheme='http://schemas.google.com/g/2005#kind', - term='http://schemas.google.com/webmasters/tools/2007#sites-info'), - verification_method=webmastertools.VerificationMethod( - type=verification_method, in_use='true') - ) - response = self.Put( - site_entry, - uri % urllib.quote_plus(site_uri), - url_params=url_params, - escape_params=escape_params, converter=converter) - if not converter and isinstance(response, atom.Entry): - return webmastertools.SitesEntryFromString(response.ToString()) - return response - - - def UpdateGeoLocation(self, site_uri, geolocation, uri=SITE_TEMPLATE, - url_params=None, escape_params=True, converter=None): - """Updates geolocation setting of a site. - - Args: - site_uri: str URI of which site to add sitemap for. - geolocation: str The geographic location. Valid values are listed in - http://en.wikipedia.org/wiki/ISO_3166-1_alpha-2 - uri: str (optional) URI template to update a site. - Default SITE_TEMPLATE. - url_params: dict (optional) Additional URL parameters to be included - in the insertion request. - escape_params: boolean (optional) If true, the url_parameters will be - escaped before they are included in the request. - converter: func (optional) Function which is executed on the server's - response before it is returned. Usually this is a function like - SitemapsEntryFromString which will parse the response and turn it into - an object. - - Returns: - If converter is defined, the results of running converter on the server's - response. Otherwise, it will be a SitesEntry object. - """ - - site_entry = webmastertools.SitesEntry( - atom_id=atom.Id(text=site_uri), - category=atom.Category( - scheme='http://schemas.google.com/g/2005#kind', - term='http://schemas.google.com/webmasters/tools/2007#sites-info'), - geolocation=webmastertools.GeoLocation(text=geolocation) - ) - response = self.Put( - site_entry, - uri % urllib.quote_plus(site_uri), - url_params=url_params, - escape_params=escape_params, converter=converter) - if not converter and isinstance(response, atom.Entry): - return webmastertools.SitesEntryFromString(response.ToString()) - return response - - def UpdateCrawlRate(self, site_uri, crawl_rate, uri=SITE_TEMPLATE, - url_params=None, escape_params=True, converter=None): - """Updates crawl rate setting of a site. - - Args: - site_uri: str URI of which site to add sitemap for. - crawl_rate: str The crawl rate for a site. Valid values are 'slower', - 'normal', and 'faster'. - uri: str (optional) URI template to update a site. - Default SITE_TEMPLATE. - url_params: dict (optional) Additional URL parameters to be included - in the insertion request. - escape_params: boolean (optional) If true, the url_parameters will be - escaped before they are included in the request. - converter: func (optional) Function which is executed on the server's - response before it is returned. Usually this is a function like - SitemapsEntryFromString which will parse the response and turn it into - an object. - - Returns: - If converter is defined, the results of running converter on the server's - response. Otherwise, it will be a SitesEntry object. - """ - - site_entry = webmastertools.SitesEntry( - atom_id=atom.Id(text=site_uri), - category=atom.Category( - scheme='http://schemas.google.com/g/2005#kind', - term='http://schemas.google.com/webmasters/tools/2007#sites-info'), - crawl_rate=webmastertools.CrawlRate(text=crawl_rate) - ) - response = self.Put( - site_entry, - uri % urllib.quote_plus(site_uri), - url_params=url_params, - escape_params=escape_params, converter=converter) - if not converter and isinstance(response, atom.Entry): - return webmastertools.SitesEntryFromString(response.ToString()) - return response - - def UpdatePreferredDomain(self, site_uri, preferred_domain, uri=SITE_TEMPLATE, - url_params=None, escape_params=True, converter=None): - """Updates preferred domain setting of a site. - - Note that if using 'preferwww', will also need www.example.com in account to - take effect. - - Args: - site_uri: str URI of which site to add sitemap for. - preferred_domain: str The preferred domain for a site. Valid values are 'none', - 'preferwww', and 'prefernowww'. - uri: str (optional) URI template to update a site. - Default SITE_TEMPLATE. - url_params: dict (optional) Additional URL parameters to be included - in the insertion request. - escape_params: boolean (optional) If true, the url_parameters will be - escaped before they are included in the request. - converter: func (optional) Function which is executed on the server's - response before it is returned. Usually this is a function like - SitemapsEntryFromString which will parse the response and turn it into - an object. - - Returns: - If converter is defined, the results of running converter on the server's - response. Otherwise, it will be a SitesEntry object. - """ - - site_entry = webmastertools.SitesEntry( - atom_id=atom.Id(text=site_uri), - category=atom.Category( - scheme='http://schemas.google.com/g/2005#kind', - term='http://schemas.google.com/webmasters/tools/2007#sites-info'), - preferred_domain=webmastertools.PreferredDomain(text=preferred_domain) - ) - response = self.Put( - site_entry, - uri % urllib.quote_plus(site_uri), - url_params=url_params, - escape_params=escape_params, converter=converter) - if not converter and isinstance(response, atom.Entry): - return webmastertools.SitesEntryFromString(response.ToString()) - return response - - def UpdateEnhancedImageSearch(self, site_uri, enhanced_image_search, - uri=SITE_TEMPLATE, url_params=None, escape_params=True, converter=None): - """Updates enhanced image search setting of a site. - - Args: - site_uri: str URI of which site to add sitemap for. - enhanced_image_search: str The enhanced image search setting for a site. - Valid values are 'true', and 'false'. - uri: str (optional) URI template to update a site. - Default SITE_TEMPLATE. - url_params: dict (optional) Additional URL parameters to be included - in the insertion request. - escape_params: boolean (optional) If true, the url_parameters will be - escaped before they are included in the request. - converter: func (optional) Function which is executed on the server's - response before it is returned. Usually this is a function like - SitemapsEntryFromString which will parse the response and turn it into - an object. - - Returns: - If converter is defined, the results of running converter on the server's - response. Otherwise, it will be a SitesEntry object. - """ - - site_entry = webmastertools.SitesEntry( - atom_id=atom.Id(text=site_uri), - category=atom.Category( - scheme='http://schemas.google.com/g/2005#kind', - term='http://schemas.google.com/webmasters/tools/2007#sites-info'), - enhanced_image_search=webmastertools.EnhancedImageSearch( - text=enhanced_image_search) - ) - response = self.Put( - site_entry, - uri % urllib.quote_plus(site_uri), - url_params=url_params, - escape_params=escape_params, converter=converter) - if not converter and isinstance(response, atom.Entry): - return webmastertools.SitesEntryFromString(response.ToString()) - return response - - def GetSitemapsFeed(self, site_uri, uri=SITEMAPS_FEED_TEMPLATE, - converter=webmastertools.SitemapsFeedFromString): - """Gets sitemaps feed of a site. - - Args: - site_uri: str (optional) URI of which site to retrieve its sitemaps feed. - uri: str (optional) URI to retrieve sites feed. - converter: func (optional) Function which is executed on the server's - response before it is returned. Usually this is a function like - SitemapsFeedFromString which will parse the response and turn it into - an object. - - Returns: - If converter is defined, the results of running converter on the server's - response. Otherwise, it will be a SitemapsFeed object. - """ - return self.Get(uri % {'site_id': urllib.quote_plus(site_uri)}, - converter=converter) - - def AddSitemap(self, site_uri, sitemap_uri, sitemap_type='WEB', - uri=SITEMAPS_FEED_TEMPLATE, - url_params=None, escape_params=True, converter=None): - """Adds a regular sitemap to a site. - - Args: - site_uri: str URI of which site to add sitemap for. - sitemap_uri: str URI of sitemap to add to a site. - sitemap_type: str Type of added sitemap. Valid types: WEB, VIDEO, or CODE. - uri: str (optional) URI template to add a sitemap. - Default SITEMAP_FEED_TEMPLATE. - url_params: dict (optional) Additional URL parameters to be included - in the insertion request. - escape_params: boolean (optional) If true, the url_parameters will be - escaped before they are included in the request. - converter: func (optional) Function which is executed on the server's - response before it is returned. Usually this is a function like - SitemapsEntryFromString which will parse the response and turn it into - an object. - - Returns: - If converter is defined, the results of running converter on the server's - response. Otherwise, it will be a SitemapsEntry object. - """ - - sitemap_entry = webmastertools.SitemapsEntry( - atom_id=atom.Id(text=sitemap_uri), - category=atom.Category( - scheme='http://schemas.google.com/g/2005#kind', - term='http://schemas.google.com/webmasters/tools/2007#sitemap-regular'), - sitemap_type=webmastertools.SitemapType(text=sitemap_type)) - response = self.Post( - sitemap_entry, - uri % {'site_id': urllib.quote_plus(site_uri)}, - url_params=url_params, - escape_params=escape_params, converter=converter) - if not converter and isinstance(response, atom.Entry): - return webmastertools.SitemapsEntryFromString(response.ToString()) - return response - - def AddMobileSitemap(self, site_uri, sitemap_uri, - sitemap_mobile_markup_language='XHTML', uri=SITEMAPS_FEED_TEMPLATE, - url_params=None, escape_params=True, converter=None): - """Adds a mobile sitemap to a site. - - Args: - site_uri: str URI of which site to add sitemap for. - sitemap_uri: str URI of sitemap to add to a site. - sitemap_mobile_markup_language: str Format of added sitemap. Valid types: - XHTML, WML, or cHTML. - uri: str (optional) URI template to add a sitemap. - Default SITEMAP_FEED_TEMPLATE. - url_params: dict (optional) Additional URL parameters to be included - in the insertion request. - escape_params: boolean (optional) If true, the url_parameters will be - escaped before they are included in the request. - converter: func (optional) Function which is executed on the server's - response before it is returned. Usually this is a function like - SitemapsEntryFromString which will parse the response and turn it into - an object. - - Returns: - If converter is defined, the results of running converter on the server's - response. Otherwise, it will be a SitemapsEntry object. - """ - # FIXME - sitemap_entry = webmastertools.SitemapsEntry( - atom_id=atom.Id(text=sitemap_uri), - category=atom.Category( - scheme='http://schemas.google.com/g/2005#kind', - term='http://schemas.google.com/webmasters/tools/2007#sitemap-mobile'), - sitemap_mobile_markup_language=\ - webmastertools.SitemapMobileMarkupLanguage( - text=sitemap_mobile_markup_language)) - print sitemap_entry - response = self.Post( - sitemap_entry, - uri % {'site_id': urllib.quote_plus(site_uri)}, - url_params=url_params, - escape_params=escape_params, converter=converter) - if not converter and isinstance(response, atom.Entry): - return webmastertools.SitemapsEntryFromString(response.ToString()) - return response - - def AddNewsSitemap(self, site_uri, sitemap_uri, - sitemap_news_publication_label, uri=SITEMAPS_FEED_TEMPLATE, - url_params=None, escape_params=True, converter=None): - """Adds a news sitemap to a site. - - Args: - site_uri: str URI of which site to add sitemap for. - sitemap_uri: str URI of sitemap to add to a site. - sitemap_news_publication_label: str, list of str Publication Labels for - sitemap. - uri: str (optional) URI template to add a sitemap. - Default SITEMAP_FEED_TEMPLATE. - url_params: dict (optional) Additional URL parameters to be included - in the insertion request. - escape_params: boolean (optional) If true, the url_parameters will be - escaped before they are included in the request. - converter: func (optional) Function which is executed on the server's - response before it is returned. Usually this is a function like - SitemapsEntryFromString which will parse the response and turn it into - an object. - - Returns: - If converter is defined, the results of running converter on the server's - response. Otherwise, it will be a SitemapsEntry object. - """ - - sitemap_entry = webmastertools.SitemapsEntry( - atom_id=atom.Id(text=sitemap_uri), - category=atom.Category( - scheme='http://schemas.google.com/g/2005#kind', - term='http://schemas.google.com/webmasters/tools/2007#sitemap-news'), - sitemap_news_publication_label=[], - ) - if isinstance(sitemap_news_publication_label, str): - sitemap_news_publication_label = [sitemap_news_publication_label] - for label in sitemap_news_publication_label: - sitemap_entry.sitemap_news_publication_label.append( - webmastertools.SitemapNewsPublicationLabel(text=label)) - print sitemap_entry - response = self.Post( - sitemap_entry, - uri % {'site_id': urllib.quote_plus(site_uri)}, - url_params=url_params, - escape_params=escape_params, converter=converter) - if not converter and isinstance(response, atom.Entry): - return webmastertools.SitemapsEntryFromString(response.ToString()) - return response - - def DeleteSitemap(self, site_uri, sitemap_uri, uri=SITEMAP_TEMPLATE, - url_params=None, escape_params=True): - """Removes a sitemap from a site. - - Args: - site_uri: str URI of which site to remove a sitemap from. - sitemap_uri: str URI of sitemap to remove from a site. - uri: str (optional) A URI template to send DELETE request. - Default SITEMAP_TEMPLATE. - url_params: dict (optional) Additional URL parameters to be included - in the insertion request. - escape_params: boolean (optional) If true, the url_parameters will be - escaped before they are included in the request. - - Returns: - True if the delete succeeded. - """ - - return self.Delete( - uri % {'site_id': urllib.quote_plus(site_uri), - 'sitemap_id': urllib.quote_plus(sitemap_uri)}, - url_params=url_params, escape_params=escape_params) diff --git a/gdata/analytics/youtube/__init__.py b/gdata/analytics/youtube/__init__.py deleted file mode 100644 index c41aaea528..0000000000 --- a/gdata/analytics/youtube/__init__.py +++ /dev/null @@ -1,684 +0,0 @@ -#!/usr/bin/python -# -# Copyright (C) 2008 Google 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. - -__author__ = ('api.stephaniel@gmail.com (Stephanie Liu)' - ', api.jhartmann@gmail.com (Jochen Hartmann)') - -import atom -import gdata -import gdata.media as Media -import gdata.geo as Geo - -YOUTUBE_NAMESPACE = 'http://gdata.youtube.com/schemas/2007' -YOUTUBE_FORMAT = '{http://gdata.youtube.com/schemas/2007}format' -YOUTUBE_DEVELOPER_TAG_SCHEME = '%s/%s' % (YOUTUBE_NAMESPACE, - 'developertags.cat') -YOUTUBE_SUBSCRIPTION_TYPE_SCHEME = '%s/%s' % (YOUTUBE_NAMESPACE, - 'subscriptiontypes.cat') - -class Username(atom.AtomBase): - """The YouTube Username element""" - _tag = 'username' - _namespace = YOUTUBE_NAMESPACE - -class QueryString(atom.AtomBase): - """The YouTube QueryString element""" - _tag = 'queryString' - _namespace = YOUTUBE_NAMESPACE - - -class FirstName(atom.AtomBase): - """The YouTube FirstName element""" - _tag = 'firstName' - _namespace = YOUTUBE_NAMESPACE - - -class LastName(atom.AtomBase): - """The YouTube LastName element""" - _tag = 'lastName' - _namespace = YOUTUBE_NAMESPACE - - -class Age(atom.AtomBase): - """The YouTube Age element""" - _tag = 'age' - _namespace = YOUTUBE_NAMESPACE - - -class Books(atom.AtomBase): - """The YouTube Books element""" - _tag = 'books' - _namespace = YOUTUBE_NAMESPACE - - -class Gender(atom.AtomBase): - """The YouTube Gender element""" - _tag = 'gender' - _namespace = YOUTUBE_NAMESPACE - - -class Company(atom.AtomBase): - """The YouTube Company element""" - _tag = 'company' - _namespace = YOUTUBE_NAMESPACE - - -class Hobbies(atom.AtomBase): - """The YouTube Hobbies element""" - _tag = 'hobbies' - _namespace = YOUTUBE_NAMESPACE - - -class Hometown(atom.AtomBase): - """The YouTube Hometown element""" - _tag = 'hometown' - _namespace = YOUTUBE_NAMESPACE - - -class Location(atom.AtomBase): - """The YouTube Location element""" - _tag = 'location' - _namespace = YOUTUBE_NAMESPACE - - -class Movies(atom.AtomBase): - """The YouTube Movies element""" - _tag = 'movies' - _namespace = YOUTUBE_NAMESPACE - - -class Music(atom.AtomBase): - """The YouTube Music element""" - _tag = 'music' - _namespace = YOUTUBE_NAMESPACE - - -class Occupation(atom.AtomBase): - """The YouTube Occupation element""" - _tag = 'occupation' - _namespace = YOUTUBE_NAMESPACE - - -class School(atom.AtomBase): - """The YouTube School element""" - _tag = 'school' - _namespace = YOUTUBE_NAMESPACE - - -class Relationship(atom.AtomBase): - """The YouTube Relationship element""" - _tag = 'relationship' - _namespace = YOUTUBE_NAMESPACE - - -class Recorded(atom.AtomBase): - """The YouTube Recorded element""" - _tag = 'recorded' - _namespace = YOUTUBE_NAMESPACE - - -class Statistics(atom.AtomBase): - """The YouTube Statistics element.""" - _tag = 'statistics' - _namespace = YOUTUBE_NAMESPACE - _attributes = atom.AtomBase._attributes.copy() - _attributes['viewCount'] = 'view_count' - _attributes['videoWatchCount'] = 'video_watch_count' - _attributes['subscriberCount'] = 'subscriber_count' - _attributes['lastWebAccess'] = 'last_web_access' - _attributes['favoriteCount'] = 'favorite_count' - - def __init__(self, view_count=None, video_watch_count=None, - favorite_count=None, subscriber_count=None, last_web_access=None, - extension_elements=None, extension_attributes=None, text=None): - - self.view_count = view_count - self.video_watch_count = video_watch_count - self.subscriber_count = subscriber_count - self.last_web_access = last_web_access - self.favorite_count = favorite_count - - atom.AtomBase.__init__(self, extension_elements=extension_elements, - extension_attributes=extension_attributes, text=text) - - -class Status(atom.AtomBase): - """The YouTube Status element""" - _tag = 'status' - _namespace = YOUTUBE_NAMESPACE - - -class Position(atom.AtomBase): - """The YouTube Position element. The position in a playlist feed.""" - _tag = 'position' - _namespace = YOUTUBE_NAMESPACE - - -class Racy(atom.AtomBase): - """The YouTube Racy element.""" - _tag = 'racy' - _namespace = YOUTUBE_NAMESPACE - -class Description(atom.AtomBase): - """The YouTube Description element.""" - _tag = 'description' - _namespace = YOUTUBE_NAMESPACE - - -class Private(atom.AtomBase): - """The YouTube Private element.""" - _tag = 'private' - _namespace = YOUTUBE_NAMESPACE - - -class NoEmbed(atom.AtomBase): - """The YouTube VideoShare element. Whether a video can be embedded or not.""" - _tag = 'noembed' - _namespace = YOUTUBE_NAMESPACE - - -class Comments(atom.AtomBase): - """The GData Comments element""" - _tag = 'comments' - _namespace = gdata.GDATA_NAMESPACE - _children = atom.AtomBase._children.copy() - _attributes = atom.AtomBase._attributes.copy() - _children['{%s}feedLink' % gdata.GDATA_NAMESPACE] = ('feed_link', - [gdata.FeedLink]) - - def __init__(self, feed_link=None, extension_elements=None, - extension_attributes=None, text=None): - - self.feed_link = feed_link - atom.AtomBase.__init__(self, extension_elements=extension_elements, - extension_attributes=extension_attributes, text=text) - - -class Rating(atom.AtomBase): - """The GData Rating element""" - _tag = 'rating' - _namespace = gdata.GDATA_NAMESPACE - _attributes = atom.AtomBase._attributes.copy() - _attributes['min'] = 'min' - _attributes['max'] = 'max' - _attributes['numRaters'] = 'num_raters' - _attributes['average'] = 'average' - - def __init__(self, min=None, max=None, - num_raters=None, average=None, extension_elements=None, - extension_attributes=None, text=None): - - self.min = min - self.max = max - self.num_raters = num_raters - self.average = average - - atom.AtomBase.__init__(self, extension_elements=extension_elements, - extension_attributes=extension_attributes, text=text) - - -class YouTubePlaylistVideoEntry(gdata.GDataEntry): - """Represents a YouTubeVideoEntry on a YouTubePlaylist.""" - _tag = gdata.GDataEntry._tag - _namespace = gdata.GDataEntry._namespace - _children = gdata.GDataEntry._children.copy() - _attributes = gdata.GDataEntry._attributes.copy() - _children['{%s}feedLink' % gdata.GDATA_NAMESPACE] = ('feed_link', - [gdata.FeedLink]) - _children['{%s}description' % YOUTUBE_NAMESPACE] = ('description', - Description) - _children['{%s}rating' % gdata.GDATA_NAMESPACE] = ('rating', Rating) - _children['{%s}comments' % gdata.GDATA_NAMESPACE] = ('comments', Comments) - _children['{%s}statistics' % YOUTUBE_NAMESPACE] = ('statistics', Statistics) - _children['{%s}location' % YOUTUBE_NAMESPACE] = ('location', Location) - _children['{%s}position' % YOUTUBE_NAMESPACE] = ('position', Position) - _children['{%s}group' % gdata.media.MEDIA_NAMESPACE] = ('media', Media.Group) - - def __init__(self, author=None, category=None, content=None, - atom_id=None, link=None, published=None, title=None, - updated=None, feed_link=None, description=None, - rating=None, comments=None, statistics=None, - location=None, position=None, media=None, - extension_elements=None, extension_attributes=None): - - self.feed_link = feed_link - self.description = description - self.rating = rating - self.comments = comments - self.statistics = statistics - self.location = location - self.position = position - self.media = media - - gdata.GDataEntry.__init__(self, author=author, category=category, - content=content, atom_id=atom_id, - link=link, published=published, title=title, - updated=updated, - extension_elements=extension_elements, - extension_attributes=extension_attributes) - - -class YouTubeVideoCommentEntry(gdata.GDataEntry): - """Represents a comment on YouTube.""" - _tag = gdata.GDataEntry._tag - _namespace = gdata.GDataEntry._namespace - _children = gdata.GDataEntry._children.copy() - _attributes = gdata.GDataEntry._attributes.copy() - - -class YouTubeSubscriptionEntry(gdata.GDataEntry): - """Represents a subscription entry on YouTube.""" - _tag = gdata.GDataEntry._tag - _namespace = gdata.GDataEntry._namespace - _children = gdata.GDataEntry._children.copy() - _attributes = gdata.GDataEntry._attributes.copy() - _children['{%s}username' % YOUTUBE_NAMESPACE] = ('username', Username) - _children['{%s}queryString' % YOUTUBE_NAMESPACE] = ( - 'query_string', QueryString) - _children['{%s}feedLink' % gdata.GDATA_NAMESPACE] = ('feed_link', - [gdata.FeedLink]) - - def __init__(self, author=None, category=None, content=None, - atom_id=None, link=None, published=None, title=None, - updated=None, username=None, query_string=None, feed_link=None, - extension_elements=None, extension_attributes=None): - - gdata.GDataEntry.__init__(self, author=author, category=category, - content=content, atom_id=atom_id, link=link, - published=published, title=title, updated=updated) - - self.username = username - self.query_string = query_string - self.feed_link = feed_link - - - def GetSubscriptionType(self): - """Retrieve the type of this subscription. - - Returns: - A string that is either 'channel, 'query' or 'favorites' - """ - for category in self.category: - if category.scheme == YOUTUBE_SUBSCRIPTION_TYPE_SCHEME: - return category.term - - -class YouTubeVideoResponseEntry(gdata.GDataEntry): - """Represents a video response. """ - _tag = gdata.GDataEntry._tag - _namespace = gdata.GDataEntry._namespace - _children = gdata.GDataEntry._children.copy() - _attributes = gdata.GDataEntry._attributes.copy() - _children['{%s}rating' % gdata.GDATA_NAMESPACE] = ('rating', Rating) - _children['{%s}noembed' % YOUTUBE_NAMESPACE] = ('noembed', NoEmbed) - _children['{%s}statistics' % YOUTUBE_NAMESPACE] = ('statistics', Statistics) - _children['{%s}racy' % YOUTUBE_NAMESPACE] = ('racy', Racy) - _children['{%s}group' % gdata.media.MEDIA_NAMESPACE] = ('media', Media.Group) - - def __init__(self, author=None, category=None, content=None, atom_id=None, - link=None, published=None, title=None, updated=None, rating=None, - noembed=None, statistics=None, racy=None, media=None, - extension_elements=None, extension_attributes=None): - - gdata.GDataEntry.__init__(self, author=author, category=category, - content=content, atom_id=atom_id, link=link, - published=published, title=title, updated=updated) - - self.rating = rating - self.noembed = noembed - self.statistics = statistics - self.racy = racy - self.media = media or Media.Group() - - -class YouTubeContactEntry(gdata.GDataEntry): - """Represents a contact entry.""" - _tag = gdata.GDataEntry._tag - _namespace = gdata.GDataEntry._namespace - _children = gdata.GDataEntry._children.copy() - _attributes = gdata.GDataEntry._attributes.copy() - _children['{%s}username' % YOUTUBE_NAMESPACE] = ('username', Username) - _children['{%s}status' % YOUTUBE_NAMESPACE] = ('status', Status) - - - def __init__(self, author=None, category=None, content=None, atom_id=None, - link=None, published=None, title=None, updated=None, - username=None, status=None, extension_elements=None, - extension_attributes=None, text=None): - - gdata.GDataEntry.__init__(self, author=author, category=category, - content=content, atom_id=atom_id, link=link, - published=published, title=title, updated=updated) - - self.username = username - self.status = status - - -class YouTubeVideoEntry(gdata.GDataEntry): - """Represents a video on YouTube.""" - _tag = gdata.GDataEntry._tag - _namespace = gdata.GDataEntry._namespace - _children = gdata.GDataEntry._children.copy() - _attributes = gdata.GDataEntry._attributes.copy() - _children['{%s}rating' % gdata.GDATA_NAMESPACE] = ('rating', Rating) - _children['{%s}comments' % gdata.GDATA_NAMESPACE] = ('comments', Comments) - _children['{%s}noembed' % YOUTUBE_NAMESPACE] = ('noembed', NoEmbed) - _children['{%s}statistics' % YOUTUBE_NAMESPACE] = ('statistics', Statistics) - _children['{%s}recorded' % YOUTUBE_NAMESPACE] = ('recorded', Recorded) - _children['{%s}racy' % YOUTUBE_NAMESPACE] = ('racy', Racy) - _children['{%s}group' % gdata.media.MEDIA_NAMESPACE] = ('media', Media.Group) - _children['{%s}where' % gdata.geo.GEORSS_NAMESPACE] = ('geo', Geo.Where) - - def __init__(self, author=None, category=None, content=None, atom_id=None, - link=None, published=None, title=None, updated=None, rating=None, - noembed=None, statistics=None, racy=None, media=None, geo=None, - recorded=None, comments=None, extension_elements=None, - extension_attributes=None): - - self.rating = rating - self.noembed = noembed - self.statistics = statistics - self.racy = racy - self.comments = comments - self.media = media or Media.Group() - self.geo = geo - self.recorded = recorded - - gdata.GDataEntry.__init__(self, author=author, category=category, - content=content, atom_id=atom_id, link=link, - published=published, title=title, updated=updated, - extension_elements=extension_elements, - extension_attributes=extension_attributes) - - def GetSwfUrl(self): - """Return the URL for the embeddable Video - - Returns: - URL of the embeddable video - """ - if self.media.content: - for content in self.media.content: - if content.extension_attributes[YOUTUBE_FORMAT] == '5': - return content.url - else: - return None - - def AddDeveloperTags(self, developer_tags): - """Add a developer tag for this entry. - - Developer tags can only be set during the initial upload. - - Arguments: - developer_tags: A list of developer tags as strings. - - Returns: - A list of all developer tags for this video entry. - """ - for tag_text in developer_tags: - self.media.category.append(gdata.media.Category( - text=tag_text, label=tag_text, scheme=YOUTUBE_DEVELOPER_TAG_SCHEME)) - - return self.GetDeveloperTags() - - def GetDeveloperTags(self): - """Retrieve developer tags for this video entry.""" - developer_tags = [] - for category in self.media.category: - if category.scheme == YOUTUBE_DEVELOPER_TAG_SCHEME: - developer_tags.append(category) - if len(developer_tags) > 0: - return developer_tags - - def GetYouTubeCategoryAsString(self): - """Convenience method to return the YouTube category as string. - - YouTubeVideoEntries can contain multiple Category objects with differing - schemes. This method returns only the category with the correct - scheme, ignoring developer tags. - """ - for category in self.media.category: - if category.scheme != YOUTUBE_DEVELOPER_TAG_SCHEME: - return category.text - -class YouTubeUserEntry(gdata.GDataEntry): - """Represents a user on YouTube.""" - _tag = gdata.GDataEntry._tag - _namespace = gdata.GDataEntry._namespace - _children = gdata.GDataEntry._children.copy() - _attributes = gdata.GDataEntry._attributes.copy() - _children['{%s}username' % YOUTUBE_NAMESPACE] = ('username', Username) - _children['{%s}firstName' % YOUTUBE_NAMESPACE] = ('first_name', FirstName) - _children['{%s}lastName' % YOUTUBE_NAMESPACE] = ('last_name', LastName) - _children['{%s}age' % YOUTUBE_NAMESPACE] = ('age', Age) - _children['{%s}books' % YOUTUBE_NAMESPACE] = ('books', Books) - _children['{%s}gender' % YOUTUBE_NAMESPACE] = ('gender', Gender) - _children['{%s}company' % YOUTUBE_NAMESPACE] = ('company', Company) - _children['{%s}description' % YOUTUBE_NAMESPACE] = ('description', - Description) - _children['{%s}hobbies' % YOUTUBE_NAMESPACE] = ('hobbies', Hobbies) - _children['{%s}hometown' % YOUTUBE_NAMESPACE] = ('hometown', Hometown) - _children['{%s}location' % YOUTUBE_NAMESPACE] = ('location', Location) - _children['{%s}movies' % YOUTUBE_NAMESPACE] = ('movies', Movies) - _children['{%s}music' % YOUTUBE_NAMESPACE] = ('music', Music) - _children['{%s}occupation' % YOUTUBE_NAMESPACE] = ('occupation', Occupation) - _children['{%s}school' % YOUTUBE_NAMESPACE] = ('school', School) - _children['{%s}relationship' % YOUTUBE_NAMESPACE] = ('relationship', - Relationship) - _children['{%s}statistics' % YOUTUBE_NAMESPACE] = ('statistics', Statistics) - _children['{%s}feedLink' % gdata.GDATA_NAMESPACE] = ('feed_link', - [gdata.FeedLink]) - _children['{%s}thumbnail' % gdata.media.MEDIA_NAMESPACE] = ('thumbnail', - Media.Thumbnail) - - def __init__(self, author=None, category=None, content=None, atom_id=None, - link=None, published=None, title=None, updated=None, - username=None, first_name=None, last_name=None, age=None, - books=None, gender=None, company=None, description=None, - hobbies=None, hometown=None, location=None, movies=None, - music=None, occupation=None, school=None, relationship=None, - statistics=None, feed_link=None, extension_elements=None, - extension_attributes=None, text=None): - - self.username = username - self.first_name = first_name - self.last_name = last_name - self.age = age - self.books = books - self.gender = gender - self.company = company - self.description = description - self.hobbies = hobbies - self.hometown = hometown - self.location = location - self.movies = movies - self.music = music - self.occupation = occupation - self.school = school - self.relationship = relationship - self.statistics = statistics - self.feed_link = feed_link - - gdata.GDataEntry.__init__(self, author=author, category=category, - content=content, atom_id=atom_id, - link=link, published=published, - title=title, updated=updated, - extension_elements=extension_elements, - extension_attributes=extension_attributes, - text=text) - - -class YouTubeVideoFeed(gdata.GDataFeed, gdata.LinkFinder): - """Represents a video feed on YouTube.""" - _tag = gdata.GDataFeed._tag - _namespace = gdata.GDataFeed._namespace - _children = gdata.GDataFeed._children.copy() - _attributes = gdata.GDataFeed._attributes.copy() - _children['{%s}entry' % atom.ATOM_NAMESPACE] = ('entry', [YouTubeVideoEntry]) - -class YouTubePlaylistEntry(gdata.GDataEntry): - """Represents a playlist in YouTube.""" - _tag = gdata.GDataEntry._tag - _namespace = gdata.GDataEntry._namespace - _children = gdata.GDataEntry._children.copy() - _attributes = gdata.GDataEntry._attributes.copy() - _children['{%s}description' % YOUTUBE_NAMESPACE] = ('description', - Description) - _children['{%s}private' % YOUTUBE_NAMESPACE] = ('private', - Private) - _children['{%s}feedLink' % gdata.GDATA_NAMESPACE] = ('feed_link', - [gdata.FeedLink]) - - def __init__(self, author=None, category=None, content=None, - atom_id=None, link=None, published=None, title=None, - updated=None, private=None, feed_link=None, - description=None, extension_elements=None, - extension_attributes=None): - - self.description = description - self.private = private - self.feed_link = feed_link - - gdata.GDataEntry.__init__(self, author=author, category=category, - content=content, atom_id=atom_id, - link=link, published=published, title=title, - updated=updated, - extension_elements=extension_elements, - extension_attributes=extension_attributes) - - - -class YouTubePlaylistFeed(gdata.GDataFeed, gdata.LinkFinder): - """Represents a feed of a user's playlists """ - _tag = gdata.GDataFeed._tag - _namespace = gdata.GDataFeed._namespace - _children = gdata.GDataFeed._children.copy() - _attributes = gdata.GDataFeed._attributes.copy() - _children['{%s}entry' % atom.ATOM_NAMESPACE] = ('entry', - [YouTubePlaylistEntry]) - - -class YouTubePlaylistVideoFeed(gdata.GDataFeed, gdata.LinkFinder): - """Represents a feed of video entry on a playlist.""" - _tag = gdata.GDataFeed._tag - _namespace = gdata.GDataFeed._namespace - _children = gdata.GDataFeed._children.copy() - _attributes = gdata.GDataFeed._attributes.copy() - _children['{%s}entry' % atom.ATOM_NAMESPACE] = ('entry', - [YouTubePlaylistVideoEntry]) - - -class YouTubeContactFeed(gdata.GDataFeed, gdata.LinkFinder): - """Represents a feed of a users contacts.""" - _tag = gdata.GDataFeed._tag - _namespace = gdata.GDataFeed._namespace - _children = gdata.GDataFeed._children.copy() - _attributes = gdata.GDataFeed._attributes.copy() - _children['{%s}entry' % atom.ATOM_NAMESPACE] = ('entry', - [YouTubeContactEntry]) - - -class YouTubeSubscriptionFeed(gdata.GDataFeed, gdata.LinkFinder): - """Represents a feed of a users subscriptions.""" - _tag = gdata.GDataFeed._tag - _namespace = gdata.GDataFeed._namespace - _children = gdata.GDataFeed._children.copy() - _attributes = gdata.GDataFeed._attributes.copy() - _children['{%s}entry' % atom.ATOM_NAMESPACE] = ('entry', - [YouTubeSubscriptionEntry]) - - -class YouTubeVideoCommentFeed(gdata.GDataFeed, gdata.LinkFinder): - """Represents a feed of comments for a video.""" - _tag = gdata.GDataFeed._tag - _namespace = gdata.GDataFeed._namespace - _children = gdata.GDataFeed._children.copy() - _attributes = gdata.GDataFeed._attributes.copy() - _children['{%s}entry' % atom.ATOM_NAMESPACE] = ('entry', - [YouTubeVideoCommentEntry]) - - -class YouTubeVideoResponseFeed(gdata.GDataFeed, gdata.LinkFinder): - """Represents a feed of video responses.""" - _tag = gdata.GDataFeed._tag - _namespace = gdata.GDataFeed._namespace - _children = gdata.GDataFeed._children.copy() - _attributes = gdata.GDataFeed._attributes.copy() - _children['{%s}entry' % atom.ATOM_NAMESPACE] = ('entry', - [YouTubeVideoResponseEntry]) - - -def YouTubeVideoFeedFromString(xml_string): - return atom.CreateClassFromXMLString(YouTubeVideoFeed, xml_string) - - -def YouTubeVideoEntryFromString(xml_string): - return atom.CreateClassFromXMLString(YouTubeVideoEntry, xml_string) - - -def YouTubeContactFeedFromString(xml_string): - return atom.CreateClassFromXMLString(YouTubeContactFeed, xml_string) - - -def YouTubeContactEntryFromString(xml_string): - return atom.CreateClassFromXMLString(YouTubeContactEntry, xml_string) - - -def YouTubeVideoCommentFeedFromString(xml_string): - return atom.CreateClassFromXMLString(YouTubeVideoCommentFeed, xml_string) - - -def YouTubeVideoCommentEntryFromString(xml_string): - return atom.CreateClassFromXMLString(YouTubeVideoCommentEntry, xml_string) - - -def YouTubeUserFeedFromString(xml_string): - return atom.CreateClassFromXMLString(YouTubeVideoFeed, xml_string) - - -def YouTubeUserEntryFromString(xml_string): - return atom.CreateClassFromXMLString(YouTubeUserEntry, xml_string) - - -def YouTubePlaylistFeedFromString(xml_string): - return atom.CreateClassFromXMLString(YouTubePlaylistFeed, xml_string) - - -def YouTubePlaylistVideoFeedFromString(xml_string): - return atom.CreateClassFromXMLString(YouTubePlaylistVideoFeed, xml_string) - - -def YouTubePlaylistEntryFromString(xml_string): - return atom.CreateClassFromXMLString(YouTubePlaylistEntry, xml_string) - - -def YouTubePlaylistVideoEntryFromString(xml_string): - return atom.CreateClassFromXMLString(YouTubePlaylistVideoEntry, xml_string) - - -def YouTubeSubscriptionFeedFromString(xml_string): - return atom.CreateClassFromXMLString(YouTubeSubscriptionFeed, xml_string) - - -def YouTubeSubscriptionEntryFromString(xml_string): - return atom.CreateClassFromXMLString(YouTubeSubscriptionEntry, xml_string) - - -def YouTubeVideoResponseFeedFromString(xml_string): - return atom.CreateClassFromXMLString(YouTubeVideoResponseFeed, xml_string) - - -def YouTubeVideoResponseEntryFromString(xml_string): - return atom.CreateClassFromXMLString(YouTubeVideoResponseEntry, xml_string) diff --git a/gdata/analytics/youtube/client.py b/gdata/analytics/youtube/client.py deleted file mode 100644 index 2e34d6af1a..0000000000 --- a/gdata/analytics/youtube/client.py +++ /dev/null @@ -1,264 +0,0 @@ -#!/usr/bin/env python -# -# Copyright (C) 2009 Google 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. - -"""Contains a client to communicate with the YouTube servers. - - A quick and dirty port of the YouTube GDATA 1.0 Python client - libraries to version 2.0 of the GDATA library. - -""" - -# __author__ = 's.@google.com (John Skidgel)' - -import logging - -import gdata.client -import gdata.youtube.data -import atom.data -import atom.http_core - -# Constants -# ----------------------------------------------------------------------------- -YOUTUBE_CLIENTLOGIN_AUTHENTICATION_URL = 'https://www.google.com/youtube/accounts/ClientLogin' -YOUTUBE_SUPPORTED_UPLOAD_TYPES = ('mov', 'avi', 'wmv', 'mpg', 'quicktime', - 'flv') -YOUTUBE_QUERY_VALID_TIME_PARAMETERS = ('today', 'this_week', 'this_month', - 'all_time') -YOUTUBE_QUERY_VALID_ORDERBY_PARAMETERS = ('published', 'viewCount', 'rating', - 'relevance') -YOUTUBE_QUERY_VALID_RACY_PARAMETERS = ('include', 'exclude') -YOUTUBE_QUERY_VALID_FORMAT_PARAMETERS = ('1', '5', '6') -YOUTUBE_STANDARDFEEDS = ('most_recent', 'recently_featured', - 'top_rated', 'most_viewed','watch_on_mobile') - -YOUTUBE_UPLOAD_TOKEN_URI = 'http://gdata.youtube.com/action/GetUploadToken' -YOUTUBE_SERVER = 'gdata.youtube.com/feeds/api' -YOUTUBE_SERVICE = 'youtube' -YOUTUBE_VIDEO_FEED_URI = 'http://%s/videos' % YOUTUBE_SERVER -YOUTUBE_USER_FEED_URI = 'http://%s/users/' % YOUTUBE_SERVER - -# Takes a youtube video ID. -YOUTUBE_CAPTION_FEED_URI = 'http://gdata.youtube.com/feeds/api/videos/%s/captions' - -# Takes a youtube video ID and a caption track ID. -YOUTUBE_CAPTION_URI = 'http://gdata.youtube.com/feeds/api/videos/%s/captiondata/%s' - -YOUTUBE_CAPTION_MIME_TYPE = 'application/vnd.youtube.timedtext; charset=UTF-8' - - -# Classes -# ----------------------------------------------------------------------------- -class Error(Exception): - """Base class for errors within the YouTube service.""" - pass - - -class RequestError(Error): - """Error class that is thrown in response to an invalid HTTP Request.""" - pass - - -class YouTubeError(Error): - """YouTube service specific error class.""" - pass - - -class YouTubeClient(gdata.client.GDClient): - """Client for the YouTube service. - - Performs a partial list of Google Data YouTube API functions, such as - retrieving the videos feed for a user and the feed for a video. - YouTube Service requires authentication for any write, update or delete - actions. - """ - api_version = '2' - auth_service = YOUTUBE_SERVICE - auth_scopes = ['http://%s' % YOUTUBE_SERVER, 'https://%s' % YOUTUBE_SERVER] - - def get_videos(self, uri=YOUTUBE_VIDEO_FEED_URI, auth_token=None, - desired_class=gdata.youtube.data.VideoFeed, - **kwargs): - """Retrieves a YouTube video feed. - Args: - uri: A string representing the URI of the feed that is to be retrieved. - - Returns: - A YouTubeVideoFeed if successfully retrieved. - """ - return self.get_feed(uri, auth_token=auth_token, - desired_class=desired_class, - **kwargs) - - GetVideos = get_videos - - - def get_user_feed(self, uri=None, username=None): - """Retrieve a YouTubeVideoFeed of user uploaded videos. - - Either a uri or a username must be provided. This will retrieve list - of videos uploaded by specified user. The uri will be of format - "http://gdata.youtube.com/feeds/api/users/{username}/uploads". - - Args: - uri: An optional string representing the URI of the user feed that is - to be retrieved. - username: An optional string representing the username. - - Returns: - A YouTubeUserFeed if successfully retrieved. - - Raises: - YouTubeError: You must provide at least a uri or a username to the - GetYouTubeUserFeed() method. - """ - if uri is None and username is None: - raise YouTubeError('You must provide at least a uri or a username ' - 'to the GetYouTubeUserFeed() method') - elif username and not uri: - uri = '%s%s/%s' % (YOUTUBE_USER_FEED_URI, username, 'uploads') - return self.get_feed(uri, desired_class=gdata.youtube.data.VideoFeed) - - GetUserFeed = get_user_feed - - - def get_video_entry(self, uri=None, video_id=None, - auth_token=None, **kwargs): - """Retrieve a YouTubeVideoEntry. - - Either a uri or a video_id must be provided. - - Args: - uri: An optional string representing the URI of the entry that is to - be retrieved. - video_id: An optional string representing the ID of the video. - - Returns: - A YouTubeVideoFeed if successfully retrieved. - - Raises: - YouTubeError: You must provide at least a uri or a video_id to the - GetYouTubeVideoEntry() method. - """ - if uri is None and video_id is None: - raise YouTubeError('You must provide at least a uri or a video_id ' - 'to the get_youtube_video_entry() method') - elif video_id and uri is None: - uri = '%s/%s' % (YOUTUBE_VIDEO_FEED_URI, video_id) - return self.get_feed(uri, - desired_class=gdata.youtube.data.VideoEntry, - auth_token=auth_token, - **kwargs) - - GetVideoEntry = get_video_entry - - - def get_caption_feed(self, uri): - """Retrieve a Caption feed of tracks. - - Args: - uri: A string representing the caption feed's URI to be retrieved. - - Returns: - A YouTube CaptionFeed if successfully retrieved. - """ - return self.get_feed(uri, desired_class=gdata.youtube.data.CaptionFeed) - - GetCaptionFeed = get_caption_feed - - def get_caption_track(self, track_url, client_id, - developer_key, auth_token=None, **kwargs): - http_request = atom.http_core.HttpRequest(uri = track_url, method = 'GET') - dev_key = 'key=' + developer_key - authsub = 'AuthSub token="' + str(auth_token) + '"' - http_request.headers = { - 'Authorization': authsub, - 'X-GData-Client': client_id, - 'X-GData-Key': dev_key - } - return self.request(http_request=http_request, **kwargs) - - GetCaptionTrack = get_caption_track - - def create_track(self, video_id, title, language, body, client_id, - developer_key, auth_token=None, title_type='text', **kwargs): - """Creates a closed-caption track and adds to an existing YouTube video. - """ - new_entry = gdata.youtube.data.TrackEntry( - content = gdata.youtube.data.TrackContent(text = body, lang = language)) - uri = YOUTUBE_CAPTION_FEED_URI % video_id - http_request = atom.http_core.HttpRequest(uri = uri, method = 'POST') - dev_key = 'key=' + developer_key - authsub = 'AuthSub token="' + str(auth_token) + '"' - http_request.headers = { - 'Content-Type': YOUTUBE_CAPTION_MIME_TYPE, - 'Content-Language': language, - 'Slug': title, - 'Authorization': authsub, - 'GData-Version': self.api_version, - 'X-GData-Client': client_id, - 'X-GData-Key': dev_key - } - http_request.add_body_part(body, http_request.headers['Content-Type']) - return self.request(http_request = http_request, - desired_class = new_entry.__class__, **kwargs) - - - CreateTrack = create_track - - def delete_track(self, video_id, track, client_id, developer_key, - auth_token=None, **kwargs): - """Deletes a track.""" - if isinstance(track, gdata.youtube.data.TrackEntry): - track_id_text_node = track.get_id().split(':') - track_id = track_id_text_node[3] - else: - track_id = track - uri = YOUTUBE_CAPTION_URI % (video_id, track_id) - http_request = atom.http_core.HttpRequest(uri = uri, method = 'DELETE') - dev_key = 'key=' + developer_key - authsub = 'AuthSub token="' + str(auth_token) + '"' - http_request.headers = { - 'Authorization': authsub, - 'GData-Version': self.api_version, - 'X-GData-Client': client_id, - 'X-GData-Key': dev_key - } - return self.request(http_request=http_request, **kwargs) - - DeleteTrack = delete_track - - def update_track(self, video_id, track, body, client_id, developer_key, - auth_token=None, **kwargs): - """Updates a closed-caption track for an existing YouTube video. - """ - track_id_text_node = track.get_id().split(':') - track_id = track_id_text_node[3] - uri = YOUTUBE_CAPTION_URI % (video_id, track_id) - http_request = atom.http_core.HttpRequest(uri = uri, method = 'PUT') - dev_key = 'key=' + developer_key - authsub = 'AuthSub token="' + str(auth_token) + '"' - http_request.headers = { - 'Content-Type': YOUTUBE_CAPTION_MIME_TYPE, - 'Authorization': authsub, - 'GData-Version': self.api_version, - 'X-GData-Client': client_id, - 'X-GData-Key': dev_key - } - http_request.add_body_part(body, http_request.headers['Content-Type']) - return self.request(http_request = http_request, - desired_class = track.__class__, **kwargs) - - UpdateTrack = update_track diff --git a/gdata/analytics/youtube/data.py b/gdata/analytics/youtube/data.py deleted file mode 100644 index 4ef2d62135..0000000000 --- a/gdata/analytics/youtube/data.py +++ /dev/null @@ -1,502 +0,0 @@ -#!/usr/bin/python -# -# Copyright (C) 2009 Google 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. - - -"""Contains the data classes of the YouTube Data API""" - - -__author__ = 'j.s@google.com (Jeff Scudder)' - - -import atom.core -import atom.data -import gdata.data -import gdata.geo.data -import gdata.media.data -import gdata.opensearch.data -import gdata.youtube.data - - -YT_TEMPLATE = '{http://gdata.youtube.com/schemas/2007/}%s' - - -class ComplaintEntry(gdata.data.GDEntry): - """Describes a complaint about a video""" - - -class ComplaintFeed(gdata.data.GDFeed): - """Describes complaints about a video""" - entry = [ComplaintEntry] - - -class RatingEntry(gdata.data.GDEntry): - """A rating about a video""" - rating = gdata.data.Rating - - -class RatingFeed(gdata.data.GDFeed): - """Describes ratings for a video""" - entry = [RatingEntry] - - -class YouTubeMediaContent(gdata.media.data.MediaContent): - """Describes a you tube media content""" - _qname = gdata.media.data.MEDIA_TEMPLATE % 'content' - format = 'format' - - -class YtAge(atom.core.XmlElement): - """User's age""" - _qname = YT_TEMPLATE % 'age' - - -class YtBooks(atom.core.XmlElement): - """User's favorite books""" - _qname = YT_TEMPLATE % 'books' - - -class YtCompany(atom.core.XmlElement): - """User's company""" - _qname = YT_TEMPLATE % 'company' - - -class YtDescription(atom.core.XmlElement): - """Description""" - _qname = YT_TEMPLATE % 'description' - - -class YtDuration(atom.core.XmlElement): - """Video duration""" - _qname = YT_TEMPLATE % 'duration' - seconds = 'seconds' - - -class YtFirstName(atom.core.XmlElement): - """User's first name""" - _qname = YT_TEMPLATE % 'firstName' - - -class YtGender(atom.core.XmlElement): - """User's gender""" - _qname = YT_TEMPLATE % 'gender' - - -class YtHobbies(atom.core.XmlElement): - """User's hobbies""" - _qname = YT_TEMPLATE % 'hobbies' - - -class YtHometown(atom.core.XmlElement): - """User's hometown""" - _qname = YT_TEMPLATE % 'hometown' - - -class YtLastName(atom.core.XmlElement): - """User's last name""" - _qname = YT_TEMPLATE % 'lastName' - - -class YtLocation(atom.core.XmlElement): - """Location""" - _qname = YT_TEMPLATE % 'location' - - -class YtMovies(atom.core.XmlElement): - """User's favorite movies""" - _qname = YT_TEMPLATE % 'movies' - - -class YtMusic(atom.core.XmlElement): - """User's favorite music""" - _qname = YT_TEMPLATE % 'music' - - -class YtNoEmbed(atom.core.XmlElement): - """Disables embedding for the video""" - _qname = YT_TEMPLATE % 'noembed' - - -class YtOccupation(atom.core.XmlElement): - """User's occupation""" - _qname = YT_TEMPLATE % 'occupation' - - -class YtPlaylistId(atom.core.XmlElement): - """Playlist id""" - _qname = YT_TEMPLATE % 'playlistId' - - -class YtPosition(atom.core.XmlElement): - """Video position on the playlist""" - _qname = YT_TEMPLATE % 'position' - - -class YtPrivate(atom.core.XmlElement): - """Flags the entry as private""" - _qname = YT_TEMPLATE % 'private' - - -class YtQueryString(atom.core.XmlElement): - """Keywords or query string associated with a subscription""" - _qname = YT_TEMPLATE % 'queryString' - - -class YtRacy(atom.core.XmlElement): - """Mature content""" - _qname = YT_TEMPLATE % 'racy' - - -class YtRecorded(atom.core.XmlElement): - """Date when the video was recorded""" - _qname = YT_TEMPLATE % 'recorded' - - -class YtRelationship(atom.core.XmlElement): - """User's relationship status""" - _qname = YT_TEMPLATE % 'relationship' - - -class YtSchool(atom.core.XmlElement): - """User's school""" - _qname = YT_TEMPLATE % 'school' - - -class YtStatistics(atom.core.XmlElement): - """Video and user statistics""" - _qname = YT_TEMPLATE % 'statistics' - favorite_count = 'favoriteCount' - video_watch_count = 'videoWatchCount' - view_count = 'viewCount' - last_web_access = 'lastWebAccess' - subscriber_count = 'subscriberCount' - - -class YtStatus(atom.core.XmlElement): - """Status of a contact""" - _qname = YT_TEMPLATE % 'status' - - -class YtUserProfileStatistics(YtStatistics): - """User statistics""" - _qname = YT_TEMPLATE % 'statistics' - - -class YtUsername(atom.core.XmlElement): - """Youtube username""" - _qname = YT_TEMPLATE % 'username' - - -class FriendEntry(gdata.data.BatchEntry): - """Describes a contact in friend list""" - username = YtUsername - status = YtStatus - email = gdata.data.Email - - -class FriendFeed(gdata.data.BatchFeed): - """Describes user's friends""" - entry = [FriendEntry] - - -class YtVideoStatistics(YtStatistics): - """Video statistics""" - _qname = YT_TEMPLATE % 'statistics' - - -class ChannelEntry(gdata.data.GDEntry): - """Describes a video channel""" - - -class ChannelFeed(gdata.data.GDFeed): - """Describes channels""" - entry = [ChannelEntry] - - -class FavoriteEntry(gdata.data.BatchEntry): - """Describes a favorite video""" - - -class FavoriteFeed(gdata.data.BatchFeed): - """Describes favorite videos""" - entry = [FavoriteEntry] - - -class YouTubeMediaCredit(gdata.media.data.MediaCredit): - """Describes a you tube media credit""" - _qname = gdata.media.data.MEDIA_TEMPLATE % 'credit' - type = 'type' - - -class YouTubeMediaRating(gdata.media.data.MediaRating): - """Describes a you tube media rating""" - _qname = gdata.media.data.MEDIA_TEMPLATE % 'rating' - country = 'country' - - -class YtAboutMe(atom.core.XmlElement): - """User's self description""" - _qname = YT_TEMPLATE % 'aboutMe' - - -class UserProfileEntry(gdata.data.BatchEntry): - """Describes an user's profile""" - relationship = YtRelationship - description = YtDescription - location = YtLocation - statistics = YtUserProfileStatistics - school = YtSchool - music = YtMusic - first_name = YtFirstName - gender = YtGender - occupation = YtOccupation - hometown = YtHometown - company = YtCompany - movies = YtMovies - books = YtBooks - username = YtUsername - about_me = YtAboutMe - last_name = YtLastName - age = YtAge - thumbnail = gdata.media.data.MediaThumbnail - hobbies = YtHobbies - - -class UserProfileFeed(gdata.data.BatchFeed): - """Describes a feed of user's profile""" - entry = [UserProfileEntry] - - -class YtAspectRatio(atom.core.XmlElement): - """The aspect ratio of a media file""" - _qname = YT_TEMPLATE % 'aspectRatio' - - -class YtBasePublicationState(atom.core.XmlElement): - """Status of an unpublished entry""" - _qname = YT_TEMPLATE % 'state' - help_url = 'helpUrl' - - -class YtPublicationState(YtBasePublicationState): - """Status of an unpublished video""" - _qname = YT_TEMPLATE % 'state' - name = 'name' - reason_code = 'reasonCode' - - -class YouTubeAppControl(atom.data.Control): - """Describes a you tube app control""" - _qname = (atom.data.APP_TEMPLATE_V1 % 'control', - atom.data.APP_TEMPLATE_V2 % 'control') - state = YtPublicationState - - -class YtCaptionPublicationState(YtBasePublicationState): - """Status of an unpublished caption track""" - _qname = YT_TEMPLATE % 'state' - reason_code = 'reasonCode' - name = 'name' - - -class YouTubeCaptionAppControl(atom.data.Control): - """Describes a you tube caption app control""" - _qname = atom.data.APP_TEMPLATE_V2 % 'control' - state = YtCaptionPublicationState - - -class CaptionTrackEntry(gdata.data.GDEntry): - """Describes a caption track""" - - -class CaptionTrackFeed(gdata.data.GDFeed): - """Describes caption tracks""" - entry = [CaptionTrackEntry] - - -class YtCountHint(atom.core.XmlElement): - """Hint as to how many entries the linked feed contains""" - _qname = YT_TEMPLATE % 'countHint' - - -class PlaylistLinkEntry(gdata.data.BatchEntry): - """Describes a playlist""" - description = YtDescription - playlist_id = YtPlaylistId - count_hint = YtCountHint - private = YtPrivate - - -class PlaylistLinkFeed(gdata.data.BatchFeed): - """Describes list of playlists""" - entry = [PlaylistLinkEntry] - - -class YtModerationStatus(atom.core.XmlElement): - """Moderation status""" - _qname = YT_TEMPLATE % 'moderationStatus' - - -class YtPlaylistTitle(atom.core.XmlElement): - """Playlist title""" - _qname = YT_TEMPLATE % 'playlistTitle' - - -class SubscriptionEntry(gdata.data.BatchEntry): - """Describes user's channel subscritpions""" - count_hint = YtCountHint - playlist_title = YtPlaylistTitle - thumbnail = gdata.media.data.MediaThumbnail - username = YtUsername - query_string = YtQueryString - playlist_id = YtPlaylistId - - -class SubscriptionFeed(gdata.data.BatchFeed): - """Describes list of user's video subscriptions""" - entry = [SubscriptionEntry] - - -class YtSpam(atom.core.XmlElement): - """Indicates that the entry probably contains spam""" - _qname = YT_TEMPLATE % 'spam' - - -class CommentEntry(gdata.data.BatchEntry): - """Describes a comment for a video""" - spam = YtSpam - - -class CommentFeed(gdata.data.BatchFeed): - """Describes comments for a video""" - entry = [CommentEntry] - - -class YtUploaded(atom.core.XmlElement): - """Date/Time at which the video was uploaded""" - _qname = YT_TEMPLATE % 'uploaded' - - -class YtVideoId(atom.core.XmlElement): - """Video id""" - _qname = YT_TEMPLATE % 'videoid' - - -class YouTubeMediaGroup(gdata.media.data.MediaGroup): - """Describes a you tube media group""" - _qname = gdata.media.data.MEDIA_TEMPLATE % 'group' - videoid = YtVideoId - private = YtPrivate - duration = YtDuration - aspect_ratio = YtAspectRatio - uploaded = YtUploaded - - -class VideoEntryBase(gdata.data.GDEntry): - """Elements that describe or contain videos""" - group = YouTubeMediaGroup - statistics = YtVideoStatistics - racy = YtRacy - recorded = YtRecorded - where = gdata.geo.data.GeoRssWhere - rating = gdata.data.Rating - noembed = YtNoEmbed - location = YtLocation - comments = gdata.data.Comments - - -class PlaylistEntry(gdata.data.BatchEntry): - """Describes a video in a playlist""" - description = YtDescription - position = YtPosition - - -class PlaylistFeed(gdata.data.BatchFeed): - """Describes videos in a playlist""" - private = YtPrivate - group = YouTubeMediaGroup - playlist_id = YtPlaylistId - entry = [PlaylistEntry] - - -class VideoEntry(gdata.data.BatchEntry): - """Describes a video""" - - -class VideoFeed(gdata.data.BatchFeed): - """Describes a video feed""" - entry = [VideoEntry] - - -class VideoMessageEntry(gdata.data.BatchEntry): - """Describes a video message""" - description = YtDescription - - -class VideoMessageFeed(gdata.data.BatchFeed): - """Describes videos in a videoMessage""" - entry = [VideoMessageEntry] - - -class UserEventEntry(gdata.data.GDEntry): - """Describes a user event""" - playlist_id = YtPlaylistId - videoid = YtVideoId - username = YtUsername - query_string = YtQueryString - rating = gdata.data.Rating - - -class UserEventFeed(gdata.data.GDFeed): - """Describes list of events""" - entry = [UserEventEntry] - - -class VideoModerationEntry(gdata.data.GDEntry): - """Describes video moderation""" - moderation_status = YtModerationStatus - videoid = YtVideoId - - -class VideoModerationFeed(gdata.data.GDFeed): - """Describes a video moderation feed""" - entry = [VideoModerationEntry] - - -class TrackContent(atom.data.Content): - lang = atom.data.XML_TEMPLATE % 'lang' - - -class TrackEntry(gdata.data.GDEntry): - """Represents the URL for a caption track""" - content = TrackContent - - def get_caption_track_id(self): - """Extracts the ID of this caption track. - Returns: - The caption track's id as a string. - """ - if self.id.text: - match = CAPTION_TRACK_ID_PATTERN.match(self.id.text) - if match: - return match.group(2) - return None - - GetCaptionTrackId = get_caption_track_id - - -class CaptionFeed(gdata.data.GDFeed): - """Represents a caption feed for a video on YouTube.""" - entry = [TrackEntry] diff --git a/gdata/analytics/youtube/service.py b/gdata/analytics/youtube/service.py deleted file mode 100644 index 9e0346f624..0000000000 --- a/gdata/analytics/youtube/service.py +++ /dev/null @@ -1,1563 +0,0 @@ -#!/usr/bin/python -# -# Copyright (C) 2008 Google 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. - -"""YouTubeService extends GDataService to streamline YouTube operations. - - YouTubeService: Provides methods to perform CRUD operations on YouTube feeds. - Extends GDataService. -""" - -__author__ = ('api.stephaniel@gmail.com (Stephanie Liu), ' - 'api.jhartmann@gmail.com (Jochen Hartmann)') - -try: - from xml.etree import cElementTree as ElementTree -except ImportError: - try: - import cElementTree as ElementTree - except ImportError: - try: - from xml.etree import ElementTree - except ImportError: - from elementtree import ElementTree -import os -import atom -import gdata -import gdata.service -import gdata.youtube - -YOUTUBE_SERVER = 'gdata.youtube.com' -YOUTUBE_SERVICE = 'youtube' -YOUTUBE_CLIENTLOGIN_AUTHENTICATION_URL = 'https://www.google.com/youtube/accounts/ClientLogin' -YOUTUBE_SUPPORTED_UPLOAD_TYPES = ('mov', 'avi', 'wmv', 'mpg', 'quicktime', - 'flv', 'mp4', 'x-flv') -YOUTUBE_QUERY_VALID_TIME_PARAMETERS = ('today', 'this_week', 'this_month', - 'all_time') -YOUTUBE_QUERY_VALID_ORDERBY_PARAMETERS = ('published', 'viewCount', 'rating', - 'relevance') -YOUTUBE_QUERY_VALID_RACY_PARAMETERS = ('include', 'exclude') -YOUTUBE_QUERY_VALID_FORMAT_PARAMETERS = ('1', '5', '6') -YOUTUBE_STANDARDFEEDS = ('most_recent', 'recently_featured', - 'top_rated', 'most_viewed','watch_on_mobile') -YOUTUBE_UPLOAD_URI = 'http://uploads.gdata.youtube.com/feeds/api/users' -YOUTUBE_UPLOAD_TOKEN_URI = 'http://gdata.youtube.com/action/GetUploadToken' -YOUTUBE_VIDEO_URI = 'http://gdata.youtube.com/feeds/api/videos' -YOUTUBE_USER_FEED_URI = 'http://gdata.youtube.com/feeds/api/users' -YOUTUBE_PLAYLIST_FEED_URI = 'http://gdata.youtube.com/feeds/api/playlists' - -YOUTUBE_STANDARD_FEEDS = 'http://gdata.youtube.com/feeds/api/standardfeeds' -YOUTUBE_STANDARD_TOP_RATED_URI = '%s/%s' % (YOUTUBE_STANDARD_FEEDS, 'top_rated') -YOUTUBE_STANDARD_MOST_VIEWED_URI = '%s/%s' % (YOUTUBE_STANDARD_FEEDS, - 'most_viewed') -YOUTUBE_STANDARD_RECENTLY_FEATURED_URI = '%s/%s' % (YOUTUBE_STANDARD_FEEDS, - 'recently_featured') -YOUTUBE_STANDARD_WATCH_ON_MOBILE_URI = '%s/%s' % (YOUTUBE_STANDARD_FEEDS, - 'watch_on_mobile') -YOUTUBE_STANDARD_TOP_FAVORITES_URI = '%s/%s' % (YOUTUBE_STANDARD_FEEDS, - 'top_favorites') -YOUTUBE_STANDARD_MOST_RECENT_URI = '%s/%s' % (YOUTUBE_STANDARD_FEEDS, - 'most_recent') -YOUTUBE_STANDARD_MOST_DISCUSSED_URI = '%s/%s' % (YOUTUBE_STANDARD_FEEDS, - 'most_discussed') -YOUTUBE_STANDARD_MOST_LINKED_URI = '%s/%s' % (YOUTUBE_STANDARD_FEEDS, - 'most_linked') -YOUTUBE_STANDARD_MOST_RESPONDED_URI = '%s/%s' % (YOUTUBE_STANDARD_FEEDS, - 'most_responded') -YOUTUBE_SCHEMA = 'http://gdata.youtube.com/schemas' - -YOUTUBE_RATING_LINK_REL = '%s#video.ratings' % YOUTUBE_SCHEMA - -YOUTUBE_COMPLAINT_CATEGORY_SCHEME = '%s/%s' % (YOUTUBE_SCHEMA, - 'complaint-reasons.cat') -YOUTUBE_SUBSCRIPTION_CATEGORY_SCHEME = '%s/%s' % (YOUTUBE_SCHEMA, - 'subscriptiontypes.cat') - -YOUTUBE_COMPLAINT_CATEGORY_TERMS = ('PORN', 'VIOLENCE', 'HATE', 'DANGEROUS', - 'RIGHTS', 'SPAM') -YOUTUBE_CONTACT_STATUS = ('accepted', 'rejected') -YOUTUBE_CONTACT_CATEGORY = ('Friends', 'Family') - -UNKOWN_ERROR = 1000 -YOUTUBE_BAD_REQUEST = 400 -YOUTUBE_CONFLICT = 409 -YOUTUBE_INTERNAL_SERVER_ERROR = 500 -YOUTUBE_INVALID_ARGUMENT = 601 -YOUTUBE_INVALID_CONTENT_TYPE = 602 -YOUTUBE_NOT_A_VIDEO = 603 -YOUTUBE_INVALID_KIND = 604 - - -class Error(Exception): - """Base class for errors within the YouTube service.""" - pass - -class RequestError(Error): - """Error class that is thrown in response to an invalid HTTP Request.""" - pass - -class YouTubeError(Error): - """YouTube service specific error class.""" - pass - -class YouTubeService(gdata.service.GDataService): - - """Client for the YouTube service. - - Performs all documented Google Data YouTube API functions, such as inserting, - updating and deleting videos, comments, playlist, subscriptions etc. - YouTube Service requires authentication for any write, update or delete - actions. - - Attributes: - email: An optional string identifying the user. Required only for - authenticated actions. - password: An optional string identifying the user's password. - source: An optional string identifying the name of your application. - server: An optional address of the YouTube API server. gdata.youtube.com - is provided as the default value. - additional_headers: An optional dictionary containing additional headers - to be passed along with each request. Use to store developer key. - client_id: An optional string identifying your application, required for - authenticated requests, along with a developer key. - developer_key: An optional string value. Register your application at - http://code.google.com/apis/youtube/dashboard to obtain a (free) key. - """ - - def __init__(self, email=None, password=None, source=None, - server=YOUTUBE_SERVER, additional_headers=None, client_id=None, - developer_key=None, **kwargs): - """Creates a client for the YouTube 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: 'gdata.youtube.com'. - client_id: string (optional) Identifies your application, required for - authenticated requests, along with a developer key. - developer_key: string (optional) Register your application at - http://code.google.com/apis/youtube/dashboard to obtain a (free) key. - **kwargs: The other parameters to pass to gdata.service.GDataService - constructor. - """ - - gdata.service.GDataService.__init__( - self, email=email, password=password, service=YOUTUBE_SERVICE, - source=source, server=server, additional_headers=additional_headers, - **kwargs) - - if client_id is not None: - self.additional_headers['X-Gdata-Client'] = client_id - - if developer_key is not None: - self.additional_headers['X-GData-Key'] = 'key=%s' % developer_key - - self.auth_service_url = YOUTUBE_CLIENTLOGIN_AUTHENTICATION_URL - - def GetYouTubeVideoFeed(self, uri): - """Retrieve a YouTubeVideoFeed. - - Args: - uri: A string representing the URI of the feed that is to be retrieved. - - Returns: - A YouTubeVideoFeed if successfully retrieved. - """ - return self.Get(uri, converter=gdata.youtube.YouTubeVideoFeedFromString) - - def GetYouTubeVideoEntry(self, uri=None, video_id=None): - """Retrieve a YouTubeVideoEntry. - - Either a uri or a video_id must be provided. - - Args: - uri: An optional string representing the URI of the entry that is to - be retrieved. - video_id: An optional string representing the ID of the video. - - Returns: - A YouTubeVideoFeed if successfully retrieved. - - Raises: - YouTubeError: You must provide at least a uri or a video_id to the - GetYouTubeVideoEntry() method. - """ - if uri is None and video_id is None: - raise YouTubeError('You must provide at least a uri or a video_id ' - 'to the GetYouTubeVideoEntry() method') - elif video_id and not uri: - uri = '%s/%s' % (YOUTUBE_VIDEO_URI, video_id) - return self.Get(uri, converter=gdata.youtube.YouTubeVideoEntryFromString) - - def GetYouTubeContactFeed(self, uri=None, username='default'): - """Retrieve a YouTubeContactFeed. - - Either a uri or a username must be provided. - - Args: - uri: An optional string representing the URI of the contact feed that - is to be retrieved. - username: An optional string representing the username. Defaults to the - currently authenticated user. - - Returns: - A YouTubeContactFeed if successfully retrieved. - - Raises: - YouTubeError: You must provide at least a uri or a username to the - GetYouTubeContactFeed() method. - """ - if uri is None: - uri = '%s/%s/%s' % (YOUTUBE_USER_FEED_URI, username, 'contacts') - return self.Get(uri, converter=gdata.youtube.YouTubeContactFeedFromString) - - def GetYouTubeContactEntry(self, uri): - """Retrieve a YouTubeContactEntry. - - Args: - uri: A string representing the URI of the contact entry that is to - be retrieved. - - Returns: - A YouTubeContactEntry if successfully retrieved. - """ - return self.Get(uri, converter=gdata.youtube.YouTubeContactEntryFromString) - - def GetYouTubeVideoCommentFeed(self, uri=None, video_id=None): - """Retrieve a YouTubeVideoCommentFeed. - - Either a uri or a video_id must be provided. - - Args: - uri: An optional string representing the URI of the comment feed that - is to be retrieved. - video_id: An optional string representing the ID of the video for which - to retrieve the comment feed. - - Returns: - A YouTubeVideoCommentFeed if successfully retrieved. - - Raises: - YouTubeError: You must provide at least a uri or a video_id to the - GetYouTubeVideoCommentFeed() method. - """ - if uri is None and video_id is None: - raise YouTubeError('You must provide at least a uri or a video_id ' - 'to the GetYouTubeVideoCommentFeed() method') - elif video_id and not uri: - uri = '%s/%s/%s' % (YOUTUBE_VIDEO_URI, video_id, 'comments') - return self.Get( - uri, converter=gdata.youtube.YouTubeVideoCommentFeedFromString) - - def GetYouTubeVideoCommentEntry(self, uri): - """Retrieve a YouTubeVideoCommentEntry. - - Args: - uri: A string representing the URI of the comment entry that is to - be retrieved. - - Returns: - A YouTubeCommentEntry if successfully retrieved. - """ - return self.Get( - uri, converter=gdata.youtube.YouTubeVideoCommentEntryFromString) - - def GetYouTubeUserFeed(self, uri=None, username=None): - """Retrieve a YouTubeVideoFeed of user uploaded videos - - Either a uri or a username must be provided. This will retrieve list - of videos uploaded by specified user. The uri will be of format - "http://gdata.youtube.com/feeds/api/users/{username}/uploads". - - Args: - uri: An optional string representing the URI of the user feed that is - to be retrieved. - username: An optional string representing the username. - - Returns: - A YouTubeUserFeed if successfully retrieved. - - Raises: - YouTubeError: You must provide at least a uri or a username to the - GetYouTubeUserFeed() method. - """ - if uri is None and username is None: - raise YouTubeError('You must provide at least a uri or a username ' - 'to the GetYouTubeUserFeed() method') - elif username and not uri: - uri = '%s/%s/%s' % (YOUTUBE_USER_FEED_URI, username, 'uploads') - return self.Get(uri, converter=gdata.youtube.YouTubeUserFeedFromString) - - def GetYouTubeUserEntry(self, uri=None, username=None): - """Retrieve a YouTubeUserEntry. - - Either a uri or a username must be provided. - - Args: - uri: An optional string representing the URI of the user entry that is - to be retrieved. - username: An optional string representing the username. - - Returns: - A YouTubeUserEntry if successfully retrieved. - - Raises: - YouTubeError: You must provide at least a uri or a username to the - GetYouTubeUserEntry() method. - """ - if uri is None and username is None: - raise YouTubeError('You must provide at least a uri or a username ' - 'to the GetYouTubeUserEntry() method') - elif username and not uri: - uri = '%s/%s' % (YOUTUBE_USER_FEED_URI, username) - return self.Get(uri, converter=gdata.youtube.YouTubeUserEntryFromString) - - def GetYouTubePlaylistFeed(self, uri=None, username='default'): - """Retrieve a YouTubePlaylistFeed (a feed of playlists for a user). - - Either a uri or a username must be provided. - - Args: - uri: An optional string representing the URI of the playlist feed that - is to be retrieved. - username: An optional string representing the username. Defaults to the - currently authenticated user. - - Returns: - A YouTubePlaylistFeed if successfully retrieved. - - Raises: - YouTubeError: You must provide at least a uri or a username to the - GetYouTubePlaylistFeed() method. - """ - if uri is None: - uri = '%s/%s/%s' % (YOUTUBE_USER_FEED_URI, username, 'playlists') - return self.Get(uri, converter=gdata.youtube.YouTubePlaylistFeedFromString) - - def GetYouTubePlaylistEntry(self, uri): - """Retrieve a YouTubePlaylistEntry. - - Args: - uri: A string representing the URI of the playlist feed that is to - be retrieved. - - Returns: - A YouTubePlaylistEntry if successfully retrieved. - """ - return self.Get(uri, converter=gdata.youtube.YouTubePlaylistEntryFromString) - - def GetYouTubePlaylistVideoFeed(self, uri=None, playlist_id=None): - """Retrieve a YouTubePlaylistVideoFeed (a feed of videos on a playlist). - - Either a uri or a playlist_id must be provided. - - Args: - uri: An optional string representing the URI of the playlist video feed - that is to be retrieved. - playlist_id: An optional string representing the Id of the playlist whose - playlist video feed is to be retrieved. - - Returns: - A YouTubePlaylistVideoFeed if successfully retrieved. - - Raises: - YouTubeError: You must provide at least a uri or a playlist_id to the - GetYouTubePlaylistVideoFeed() method. - """ - if uri is None and playlist_id is None: - raise YouTubeError('You must provide at least a uri or a playlist_id ' - 'to the GetYouTubePlaylistVideoFeed() method') - elif playlist_id and not uri: - uri = '%s/%s' % (YOUTUBE_PLAYLIST_FEED_URI, playlist_id) - return self.Get( - uri, converter=gdata.youtube.YouTubePlaylistVideoFeedFromString) - - def GetYouTubeVideoResponseFeed(self, uri=None, video_id=None): - """Retrieve a YouTubeVideoResponseFeed. - - Either a uri or a playlist_id must be provided. - - Args: - uri: An optional string representing the URI of the video response feed - that is to be retrieved. - video_id: An optional string representing the ID of the video whose - response feed is to be retrieved. - - Returns: - A YouTubeVideoResponseFeed if successfully retrieved. - - Raises: - YouTubeError: You must provide at least a uri or a video_id to the - GetYouTubeVideoResponseFeed() method. - """ - if uri is None and video_id is None: - raise YouTubeError('You must provide at least a uri or a video_id ' - 'to the GetYouTubeVideoResponseFeed() method') - elif video_id and not uri: - uri = '%s/%s/%s' % (YOUTUBE_VIDEO_URI, video_id, 'responses') - return self.Get( - uri, converter=gdata.youtube.YouTubeVideoResponseFeedFromString) - - def GetYouTubeVideoResponseEntry(self, uri): - """Retrieve a YouTubeVideoResponseEntry. - - Args: - uri: A string representing the URI of the video response entry that - is to be retrieved. - - Returns: - A YouTubeVideoResponseEntry if successfully retrieved. - """ - return self.Get( - uri, converter=gdata.youtube.YouTubeVideoResponseEntryFromString) - - def GetYouTubeSubscriptionFeed(self, uri=None, username='default'): - """Retrieve a YouTubeSubscriptionFeed. - - Either the uri of the feed or a username must be provided. - - Args: - uri: An optional string representing the URI of the feed that is to - be retrieved. - username: An optional string representing the username whose subscription - feed is to be retrieved. Defaults to the currently authenticted user. - - Returns: - A YouTubeVideoSubscriptionFeed if successfully retrieved. - """ - if uri is None: - uri = '%s/%s/%s' % (YOUTUBE_USER_FEED_URI, username, 'subscriptions') - return self.Get( - uri, converter=gdata.youtube.YouTubeSubscriptionFeedFromString) - - def GetYouTubeSubscriptionEntry(self, uri): - """Retrieve a YouTubeSubscriptionEntry. - - Args: - uri: A string representing the URI of the entry that is to be retrieved. - - Returns: - A YouTubeVideoSubscriptionEntry if successfully retrieved. - """ - return self.Get( - uri, converter=gdata.youtube.YouTubeSubscriptionEntryFromString) - - def GetYouTubeRelatedVideoFeed(self, uri=None, video_id=None): - """Retrieve a YouTubeRelatedVideoFeed. - - Either a uri for the feed or a video_id is required. - - Args: - uri: An optional string representing the URI of the feed that is to - be retrieved. - video_id: An optional string representing the ID of the video for which - to retrieve the related video feed. - - Returns: - A YouTubeRelatedVideoFeed if successfully retrieved. - - Raises: - YouTubeError: You must provide at least a uri or a video_id to the - GetYouTubeRelatedVideoFeed() method. - """ - if uri is None and video_id is None: - raise YouTubeError('You must provide at least a uri or a video_id ' - 'to the GetYouTubeRelatedVideoFeed() method') - elif video_id and not uri: - uri = '%s/%s/%s' % (YOUTUBE_VIDEO_URI, video_id, 'related') - return self.Get( - uri, converter=gdata.youtube.YouTubeVideoFeedFromString) - - def GetTopRatedVideoFeed(self): - """Retrieve the 'top_rated' standard video feed. - - Returns: - A YouTubeVideoFeed if successfully retrieved. - """ - return self.GetYouTubeVideoFeed(YOUTUBE_STANDARD_TOP_RATED_URI) - - def GetMostViewedVideoFeed(self): - """Retrieve the 'most_viewed' standard video feed. - - Returns: - A YouTubeVideoFeed if successfully retrieved. - """ - return self.GetYouTubeVideoFeed(YOUTUBE_STANDARD_MOST_VIEWED_URI) - - def GetRecentlyFeaturedVideoFeed(self): - """Retrieve the 'recently_featured' standard video feed. - - Returns: - A YouTubeVideoFeed if successfully retrieved. - """ - return self.GetYouTubeVideoFeed(YOUTUBE_STANDARD_RECENTLY_FEATURED_URI) - - def GetWatchOnMobileVideoFeed(self): - """Retrieve the 'watch_on_mobile' standard video feed. - - Returns: - A YouTubeVideoFeed if successfully retrieved. - """ - return self.GetYouTubeVideoFeed(YOUTUBE_STANDARD_WATCH_ON_MOBILE_URI) - - def GetTopFavoritesVideoFeed(self): - """Retrieve the 'top_favorites' standard video feed. - - Returns: - A YouTubeVideoFeed if successfully retrieved. - """ - return self.GetYouTubeVideoFeed(YOUTUBE_STANDARD_TOP_FAVORITES_URI) - - def GetMostRecentVideoFeed(self): - """Retrieve the 'most_recent' standard video feed. - - Returns: - A YouTubeVideoFeed if successfully retrieved. - """ - return self.GetYouTubeVideoFeed(YOUTUBE_STANDARD_MOST_RECENT_URI) - - def GetMostDiscussedVideoFeed(self): - """Retrieve the 'most_discussed' standard video feed. - - Returns: - A YouTubeVideoFeed if successfully retrieved. - """ - return self.GetYouTubeVideoFeed(YOUTUBE_STANDARD_MOST_DISCUSSED_URI) - - def GetMostLinkedVideoFeed(self): - """Retrieve the 'most_linked' standard video feed. - - Returns: - A YouTubeVideoFeed if successfully retrieved. - """ - return self.GetYouTubeVideoFeed(YOUTUBE_STANDARD_MOST_LINKED_URI) - - def GetMostRespondedVideoFeed(self): - """Retrieve the 'most_responded' standard video feed. - - Returns: - A YouTubeVideoFeed if successfully retrieved. - """ - return self.GetYouTubeVideoFeed(YOUTUBE_STANDARD_MOST_RESPONDED_URI) - - def GetUserFavoritesFeed(self, username='default'): - """Retrieve the favorites feed for a given user. - - Args: - username: An optional string representing the username whose favorites - feed is to be retrieved. Defaults to the currently authenticated user. - - Returns: - A YouTubeVideoFeed if successfully retrieved. - """ - favorites_feed_uri = '%s/%s/%s' % (YOUTUBE_USER_FEED_URI, username, - 'favorites') - return self.GetYouTubeVideoFeed(favorites_feed_uri) - - def InsertVideoEntry(self, video_entry, filename_or_handle, - youtube_username='default', - content_type='video/quicktime'): - """Upload a new video to YouTube using the direct upload mechanism. - - Needs authentication. - - Args: - video_entry: The YouTubeVideoEntry to upload. - filename_or_handle: A file-like object or file name where the video - will be read from. - youtube_username: An optional string representing the username into whose - account this video is to be uploaded to. Defaults to the currently - authenticated user. - content_type: An optional string representing internet media type - (a.k.a. mime type) of the media object. Currently the YouTube API - supports these types: - o video/mpeg - o video/quicktime - o video/x-msvideo - o video/mp4 - o video/x-flv - - Returns: - The newly created YouTubeVideoEntry if successful. - - Raises: - AssertionError: video_entry must be a gdata.youtube.VideoEntry instance. - YouTubeError: An error occurred trying to read the video file provided. - gdata.service.RequestError: An error occurred trying to upload the video - to the API server. - """ - - # We need to perform a series of checks on the video_entry and on the - # file that we plan to upload, such as checking whether we have a valid - # video_entry and that the file is the correct type and readable, prior - # to performing the actual POST request. - - try: - assert(isinstance(video_entry, gdata.youtube.YouTubeVideoEntry)) - except AssertionError: - raise YouTubeError({'status':YOUTUBE_INVALID_ARGUMENT, - 'body':'`video_entry` must be a gdata.youtube.VideoEntry instance', - 'reason':'Found %s, not VideoEntry' % type(video_entry) - }) - #majtype, mintype = content_type.split('/') - # - #try: - # assert(mintype in YOUTUBE_SUPPORTED_UPLOAD_TYPES) - #except (ValueError, AssertionError): - # raise YouTubeError({'status':YOUTUBE_INVALID_CONTENT_TYPE, - # 'body':'This is not a valid content type: %s' % content_type, - # 'reason':'Accepted content types: %s' % - # ['video/%s' % (t) for t in YOUTUBE_SUPPORTED_UPLOAD_TYPES]}) - - if (isinstance(filename_or_handle, (str, unicode)) - and os.path.exists(filename_or_handle)): - mediasource = gdata.MediaSource() - mediasource.setFile(filename_or_handle, content_type) - elif hasattr(filename_or_handle, 'read'): - import StringIO - if hasattr(filename_or_handle, 'seek'): - filename_or_handle.seek(0) - file_handle = filename_or_handle - name = 'video' - if hasattr(filename_or_handle, 'name'): - name = filename_or_handle.name - mediasource = gdata.MediaSource(file_handle, content_type, - content_length=file_handle.len, file_name=name) - else: - raise YouTubeError({'status':YOUTUBE_INVALID_ARGUMENT, 'body': - '`filename_or_handle` must be a path name or a file-like object', - 'reason': ('Found %s, not path name or object ' - 'with a .read() method' % type(filename_or_handle))}) - upload_uri = '%s/%s/%s' % (YOUTUBE_UPLOAD_URI, youtube_username, - 'uploads') - self.additional_headers['Slug'] = mediasource.file_name - - # Using a nested try statement to retain Python 2.4 compatibility - try: - try: - return self.Post(video_entry, uri=upload_uri, media_source=mediasource, - converter=gdata.youtube.YouTubeVideoEntryFromString) - except gdata.service.RequestError, e: - raise YouTubeError(e.args[0]) - finally: - del(self.additional_headers['Slug']) - - def CheckUploadStatus(self, video_entry=None, video_id=None): - """Check upload status on a recently uploaded video entry. - - Needs authentication. Either video_entry or video_id must be provided. - - Args: - video_entry: An optional YouTubeVideoEntry whose upload status to check - video_id: An optional string representing the ID of the uploaded video - whose status is to be checked. - - Returns: - A tuple containing (video_upload_state, detailed_message) or None if - no status information is found. - - Raises: - YouTubeError: You must provide at least a video_entry or a video_id to the - CheckUploadStatus() method. - """ - if video_entry is None and video_id is None: - raise YouTubeError('You must provide at least a uri or a video_id ' - 'to the CheckUploadStatus() method') - elif video_id and not video_entry: - video_entry = self.GetYouTubeVideoEntry(video_id=video_id) - - control = video_entry.control - if control is not None: - draft = control.draft - if draft is not None: - if draft.text == 'yes': - yt_state = control.extension_elements[0] - if yt_state is not None: - state_value = yt_state.attributes['name'] - message = '' - if yt_state.text is not None: - message = yt_state.text - - return (state_value, message) - - def GetFormUploadToken(self, video_entry, uri=YOUTUBE_UPLOAD_TOKEN_URI): - """Receives a YouTube Token and a YouTube PostUrl from a YouTubeVideoEntry. - - Needs authentication. - - Args: - video_entry: The YouTubeVideoEntry to upload (meta-data only). - uri: An optional string representing the URI from where to fetch the - token information. Defaults to the YOUTUBE_UPLOADTOKEN_URI. - - Returns: - A tuple containing the URL to which to post your video file, along - with the youtube token that must be included with your upload in the - form of: (post_url, youtube_token). - """ - try: - response = self.Post(video_entry, uri) - except gdata.service.RequestError, e: - raise YouTubeError(e.args[0]) - - tree = ElementTree.fromstring(response) - - for child in tree: - if child.tag == 'url': - post_url = child.text - elif child.tag == 'token': - youtube_token = child.text - return (post_url, youtube_token) - - def UpdateVideoEntry(self, video_entry): - """Updates a video entry's meta-data. - - Needs authentication. - - Args: - video_entry: The YouTubeVideoEntry to update, containing updated - meta-data. - - Returns: - An updated YouTubeVideoEntry on success or None. - """ - for link in video_entry.link: - if link.rel == 'edit': - edit_uri = link.href - return self.Put(video_entry, uri=edit_uri, - converter=gdata.youtube.YouTubeVideoEntryFromString) - - def DeleteVideoEntry(self, video_entry): - """Deletes a video entry. - - Needs authentication. - - Args: - video_entry: The YouTubeVideoEntry to be deleted. - - Returns: - True if entry was deleted successfully. - """ - for link in video_entry.link: - if link.rel == 'edit': - edit_uri = link.href - return self.Delete(edit_uri) - - def AddRating(self, rating_value, video_entry): - """Add a rating to a video entry. - - Needs authentication. - - Args: - rating_value: The integer value for the rating (between 1 and 5). - video_entry: The YouTubeVideoEntry to be rated. - - Returns: - True if the rating was added successfully. - - Raises: - YouTubeError: rating_value must be between 1 and 5 in AddRating(). - """ - if rating_value < 1 or rating_value > 5: - raise YouTubeError('rating_value must be between 1 and 5 in AddRating()') - - entry = gdata.GDataEntry() - rating = gdata.youtube.Rating(min='1', max='5') - rating.extension_attributes['name'] = 'value' - rating.extension_attributes['value'] = str(rating_value) - entry.extension_elements.append(rating) - - for link in video_entry.link: - if link.rel == YOUTUBE_RATING_LINK_REL: - rating_uri = link.href - - return self.Post(entry, uri=rating_uri) - - def AddComment(self, comment_text, video_entry): - """Add a comment to a video entry. - - Needs authentication. Note that each comment that is posted must contain - the video entry that it is to be posted to. - - Args: - comment_text: A string representing the text of the comment. - video_entry: The YouTubeVideoEntry to be commented on. - - Returns: - True if the comment was added successfully. - """ - content = atom.Content(text=comment_text) - comment_entry = gdata.youtube.YouTubeVideoCommentEntry(content=content) - comment_post_uri = video_entry.comments.feed_link[0].href - - return self.Post(comment_entry, uri=comment_post_uri) - - def AddVideoResponse(self, video_id_to_respond_to, video_response): - """Add a video response. - - Needs authentication. - - Args: - video_id_to_respond_to: A string representing the ID of the video to be - responded to. - video_response: YouTubeVideoEntry to be posted as a response. - - Returns: - True if video response was posted successfully. - """ - post_uri = '%s/%s/%s' % (YOUTUBE_VIDEO_URI, video_id_to_respond_to, - 'responses') - return self.Post(video_response, uri=post_uri) - - def DeleteVideoResponse(self, video_id, response_video_id): - """Delete a video response. - - Needs authentication. - - Args: - video_id: A string representing the ID of video that contains the - response. - response_video_id: A string representing the ID of the video that was - posted as a response. - - Returns: - True if video response was deleted succcessfully. - """ - delete_uri = '%s/%s/%s/%s' % (YOUTUBE_VIDEO_URI, video_id, 'responses', - response_video_id) - return self.Delete(delete_uri) - - def AddComplaint(self, complaint_text, complaint_term, video_id): - """Add a complaint for a particular video entry. - - Needs authentication. - - Args: - complaint_text: A string representing the complaint text. - complaint_term: A string representing the complaint category term. - video_id: A string representing the ID of YouTubeVideoEntry to - complain about. - - Returns: - True if posted successfully. - - Raises: - YouTubeError: Your complaint_term is not valid. - """ - if complaint_term not in YOUTUBE_COMPLAINT_CATEGORY_TERMS: - raise YouTubeError('Your complaint_term is not valid') - - content = atom.Content(text=complaint_text) - category = atom.Category(term=complaint_term, - scheme=YOUTUBE_COMPLAINT_CATEGORY_SCHEME) - - complaint_entry = gdata.GDataEntry(content=content, category=[category]) - post_uri = '%s/%s/%s' % (YOUTUBE_VIDEO_URI, video_id, 'complaints') - - return self.Post(complaint_entry, post_uri) - - def AddVideoEntryToFavorites(self, video_entry, username='default'): - """Add a video entry to a users favorite feed. - - Needs authentication. - - Args: - video_entry: The YouTubeVideoEntry to add. - username: An optional string representing the username to whose favorite - feed you wish to add the entry. Defaults to the currently - authenticated user. - Returns: - The posted YouTubeVideoEntry if successfully posted. - """ - post_uri = '%s/%s/%s' % (YOUTUBE_USER_FEED_URI, username, 'favorites') - - return self.Post(video_entry, post_uri, - converter=gdata.youtube.YouTubeVideoEntryFromString) - - def DeleteVideoEntryFromFavorites(self, video_id, username='default'): - """Delete a video entry from the users favorite feed. - - Needs authentication. - - Args: - video_id: A string representing the ID of the video that is to be removed - username: An optional string representing the username of the user's - favorite feed. Defaults to the currently authenticated user. - - Returns: - True if entry was successfully deleted. - """ - edit_link = '%s/%s/%s/%s' % (YOUTUBE_USER_FEED_URI, username, 'favorites', - video_id) - return self.Delete(edit_link) - - def AddPlaylist(self, playlist_title, playlist_description, - playlist_private=None): - """Add a new playlist to the currently authenticated users account. - - Needs authentication. - - Args: - playlist_title: A string representing the title for the new playlist. - playlist_description: A string representing the description of the - playlist. - playlist_private: An optional boolean, set to True if the playlist is - to be private. - - Returns: - The YouTubePlaylistEntry if successfully posted. - """ - playlist_entry = gdata.youtube.YouTubePlaylistEntry( - title=atom.Title(text=playlist_title), - description=gdata.youtube.Description(text=playlist_description)) - if playlist_private: - playlist_entry.private = gdata.youtube.Private() - - playlist_post_uri = '%s/%s/%s' % (YOUTUBE_USER_FEED_URI, 'default', - 'playlists') - return self.Post(playlist_entry, playlist_post_uri, - converter=gdata.youtube.YouTubePlaylistEntryFromString) - - def UpdatePlaylist(self, playlist_id, new_playlist_title, - new_playlist_description, playlist_private=None, - username='default'): - """Update a playlist with new meta-data. - - Needs authentication. - - Args: - playlist_id: A string representing the ID of the playlist to be updated. - new_playlist_title: A string representing a new title for the playlist. - new_playlist_description: A string representing a new description for the - playlist. - playlist_private: An optional boolean, set to True if the playlist is - to be private. - username: An optional string representing the username whose playlist is - to be updated. Defaults to the currently authenticated user. - - Returns: - A YouTubePlaylistEntry if the update was successful. - """ - updated_playlist = gdata.youtube.YouTubePlaylistEntry( - title=atom.Title(text=new_playlist_title), - description=gdata.youtube.Description(text=new_playlist_description)) - if playlist_private: - updated_playlist.private = gdata.youtube.Private() - - playlist_put_uri = '%s/%s/playlists/%s' % (YOUTUBE_USER_FEED_URI, username, - playlist_id) - - return self.Put(updated_playlist, playlist_put_uri, - converter=gdata.youtube.YouTubePlaylistEntryFromString) - - def DeletePlaylist(self, playlist_uri): - """Delete a playlist from the currently authenticated users playlists. - - Needs authentication. - - Args: - playlist_uri: A string representing the URI of the playlist that is - to be deleted. - - Returns: - True if successfully deleted. - """ - return self.Delete(playlist_uri) - - def AddPlaylistVideoEntryToPlaylist( - self, playlist_uri, video_id, custom_video_title=None, - custom_video_description=None): - """Add a video entry to a playlist, optionally providing a custom title - and description. - - Needs authentication. - - Args: - playlist_uri: A string representing the URI of the playlist to which this - video entry is to be added. - video_id: A string representing the ID of the video entry to add. - custom_video_title: An optional string representing a custom title for - the video (only shown on the playlist). - custom_video_description: An optional string representing a custom - description for the video (only shown on the playlist). - - Returns: - A YouTubePlaylistVideoEntry if successfully posted. - """ - playlist_video_entry = gdata.youtube.YouTubePlaylistVideoEntry( - atom_id=atom.Id(text=video_id)) - if custom_video_title: - playlist_video_entry.title = atom.Title(text=custom_video_title) - if custom_video_description: - playlist_video_entry.description = gdata.youtube.Description( - text=custom_video_description) - - return self.Post(playlist_video_entry, playlist_uri, - converter=gdata.youtube.YouTubePlaylistVideoEntryFromString) - - def UpdatePlaylistVideoEntryMetaData( - self, playlist_uri, playlist_entry_id, new_video_title, - new_video_description, new_video_position): - """Update the meta data for a YouTubePlaylistVideoEntry. - - Needs authentication. - - Args: - playlist_uri: A string representing the URI of the playlist that contains - the entry to be updated. - playlist_entry_id: A string representing the ID of the entry to be - updated. - new_video_title: A string representing the new title for the video entry. - new_video_description: A string representing the new description for - the video entry. - new_video_position: An integer representing the new position on the - playlist for the video. - - Returns: - A YouTubePlaylistVideoEntry if the update was successful. - """ - playlist_video_entry = gdata.youtube.YouTubePlaylistVideoEntry( - title=atom.Title(text=new_video_title), - description=gdata.youtube.Description(text=new_video_description), - position=gdata.youtube.Position(text=str(new_video_position))) - - playlist_put_uri = playlist_uri + '/' + playlist_entry_id - - return self.Put(playlist_video_entry, playlist_put_uri, - converter=gdata.youtube.YouTubePlaylistVideoEntryFromString) - - def DeletePlaylistVideoEntry(self, playlist_uri, playlist_video_entry_id): - """Delete a playlist video entry from a playlist. - - Needs authentication. - - Args: - playlist_uri: A URI representing the playlist from which the playlist - video entry is to be removed from. - playlist_video_entry_id: A string representing id of the playlist video - entry that is to be removed. - - Returns: - True if entry was successfully deleted. - """ - delete_uri = '%s/%s' % (playlist_uri, playlist_video_entry_id) - return self.Delete(delete_uri) - - def AddSubscriptionToChannel(self, username_to_subscribe_to, - my_username = 'default'): - """Add a new channel subscription to the currently authenticated users - account. - - Needs authentication. - - Args: - username_to_subscribe_to: A string representing the username of the - channel to which we want to subscribe to. - my_username: An optional string representing the name of the user which - we want to subscribe. Defaults to currently authenticated user. - - Returns: - A new YouTubeSubscriptionEntry if successfully posted. - """ - subscription_category = atom.Category( - scheme=YOUTUBE_SUBSCRIPTION_CATEGORY_SCHEME, - term='channel') - subscription_username = gdata.youtube.Username( - text=username_to_subscribe_to) - - subscription_entry = gdata.youtube.YouTubeSubscriptionEntry( - category=subscription_category, - username=subscription_username) - - post_uri = '%s/%s/%s' % (YOUTUBE_USER_FEED_URI, my_username, - 'subscriptions') - - return self.Post(subscription_entry, post_uri, - converter=gdata.youtube.YouTubeSubscriptionEntryFromString) - - def AddSubscriptionToFavorites(self, username, my_username = 'default'): - """Add a new subscription to a users favorites to the currently - authenticated user's account. - - Needs authentication - - Args: - username: A string representing the username of the user's favorite feed - to subscribe to. - my_username: An optional string representing the username of the user - that is to be subscribed. Defaults to currently authenticated user. - - Returns: - A new YouTubeSubscriptionEntry if successful. - """ - subscription_category = atom.Category( - scheme=YOUTUBE_SUBSCRIPTION_CATEGORY_SCHEME, - term='favorites') - subscription_username = gdata.youtube.Username(text=username) - - subscription_entry = gdata.youtube.YouTubeSubscriptionEntry( - category=subscription_category, - username=subscription_username) - - post_uri = '%s/%s/%s' % (YOUTUBE_USER_FEED_URI, my_username, - 'subscriptions') - - return self.Post(subscription_entry, post_uri, - converter=gdata.youtube.YouTubeSubscriptionEntryFromString) - - def AddSubscriptionToQuery(self, query, my_username = 'default'): - """Add a new subscription to a specific keyword query to the currently - authenticated user's account. - - Needs authentication - - Args: - query: A string representing the keyword query to subscribe to. - my_username: An optional string representing the username of the user - that is to be subscribed. Defaults to currently authenticated user. - - Returns: - A new YouTubeSubscriptionEntry if successful. - """ - subscription_category = atom.Category( - scheme=YOUTUBE_SUBSCRIPTION_CATEGORY_SCHEME, - term='query') - subscription_query_string = gdata.youtube.QueryString(text=query) - - subscription_entry = gdata.youtube.YouTubeSubscriptionEntry( - category=subscription_category, - query_string=subscription_query_string) - - post_uri = '%s/%s/%s' % (YOUTUBE_USER_FEED_URI, my_username, - 'subscriptions') - - return self.Post(subscription_entry, post_uri, - converter=gdata.youtube.YouTubeSubscriptionEntryFromString) - - - - def DeleteSubscription(self, subscription_uri): - """Delete a subscription from the currently authenticated user's account. - - Needs authentication. - - Args: - subscription_uri: A string representing the URI of the subscription that - is to be deleted. - - Returns: - True if deleted successfully. - """ - return self.Delete(subscription_uri) - - def AddContact(self, contact_username, my_username='default'): - """Add a new contact to the currently authenticated user's contact feed. - - Needs authentication. - - Args: - contact_username: A string representing the username of the contact - that you wish to add. - my_username: An optional string representing the username to whose - contact the new contact is to be added. - - Returns: - A YouTubeContactEntry if added successfully. - """ - contact_category = atom.Category( - scheme = 'http://gdata.youtube.com/schemas/2007/contact.cat', - term = 'Friends') - contact_username = gdata.youtube.Username(text=contact_username) - contact_entry = gdata.youtube.YouTubeContactEntry( - category=contact_category, - username=contact_username) - - contact_post_uri = '%s/%s/%s' % (YOUTUBE_USER_FEED_URI, my_username, - 'contacts') - - return self.Post(contact_entry, contact_post_uri, - converter=gdata.youtube.YouTubeContactEntryFromString) - - def UpdateContact(self, contact_username, new_contact_status, - new_contact_category, my_username='default'): - """Update a contact, providing a new status and a new category. - - Needs authentication. - - Args: - contact_username: A string representing the username of the contact - that is to be updated. - new_contact_status: A string representing the new status of the contact. - This can either be set to 'accepted' or 'rejected'. - new_contact_category: A string representing the new category for the - contact, either 'Friends' or 'Family'. - my_username: An optional string representing the username of the user - whose contact feed we are modifying. Defaults to the currently - authenticated user. - - Returns: - A YouTubeContactEntry if updated succesfully. - - Raises: - YouTubeError: New contact status must be within the accepted values. Or - new contact category must be within the accepted categories. - """ - if new_contact_status not in YOUTUBE_CONTACT_STATUS: - raise YouTubeError('New contact status must be one of %s' % - (' '.join(YOUTUBE_CONTACT_STATUS))) - if new_contact_category not in YOUTUBE_CONTACT_CATEGORY: - raise YouTubeError('New contact category must be one of %s' % - (' '.join(YOUTUBE_CONTACT_CATEGORY))) - - contact_category = atom.Category( - scheme='http://gdata.youtube.com/schemas/2007/contact.cat', - term=new_contact_category) - - contact_status = gdata.youtube.Status(text=new_contact_status) - contact_entry = gdata.youtube.YouTubeContactEntry( - category=contact_category, - status=contact_status) - - contact_put_uri = '%s/%s/%s/%s' % (YOUTUBE_USER_FEED_URI, my_username, - 'contacts', contact_username) - - return self.Put(contact_entry, contact_put_uri, - converter=gdata.youtube.YouTubeContactEntryFromString) - - def DeleteContact(self, contact_username, my_username='default'): - """Delete a contact from a users contact feed. - - Needs authentication. - - Args: - contact_username: A string representing the username of the contact - that is to be deleted. - my_username: An optional string representing the username of the user's - contact feed from which to delete the contact. Defaults to the - currently authenticated user. - - Returns: - True if the contact was deleted successfully - """ - contact_edit_uri = '%s/%s/%s/%s' % (YOUTUBE_USER_FEED_URI, my_username, - 'contacts', contact_username) - return self.Delete(contact_edit_uri) - - def _GetDeveloperKey(self): - """Getter for Developer Key property. - - Returns: - If the developer key has been set, a string representing the developer key - is returned or None. - """ - if 'X-GData-Key' in self.additional_headers: - return self.additional_headers['X-GData-Key'][4:] - else: - return None - - def _SetDeveloperKey(self, developer_key): - """Setter for Developer Key property. - - Sets the developer key in the 'X-GData-Key' header. The actual value that - is set is 'key=' plus the developer_key that was passed. - """ - self.additional_headers['X-GData-Key'] = 'key=' + developer_key - - developer_key = property(_GetDeveloperKey, _SetDeveloperKey, - doc="""The Developer Key property""") - - def _GetClientId(self): - """Getter for Client Id property. - - Returns: - If the client_id has been set, a string representing it is returned - or None. - """ - if 'X-Gdata-Client' in self.additional_headers: - return self.additional_headers['X-Gdata-Client'] - else: - return None - - def _SetClientId(self, client_id): - """Setter for Client Id property. - - Sets the 'X-Gdata-Client' header. - """ - self.additional_headers['X-Gdata-Client'] = client_id - - client_id = property(_GetClientId, _SetClientId, - doc="""The ClientId property""") - - def Query(self, uri): - """Performs a query and returns a resulting feed or entry. - - Args: - uri: A string representing the URI of the feed that is to be queried. - - Returns: - On success, a tuple in the form: - (boolean succeeded=True, ElementTree._Element result) - On failure, a tuple in the form: - (boolean succeeded=False, {'status': HTTP status code from server, - 'reason': HTTP reason from the server, - 'body': HTTP body of the server's response}) - """ - result = self.Get(uri) - return result - - def YouTubeQuery(self, query): - """Performs a YouTube specific query and returns a resulting feed or entry. - - Args: - query: A Query object or one if its sub-classes (YouTubeVideoQuery, - YouTubeUserQuery or YouTubePlaylistQuery). - - Returns: - Depending on the type of Query object submitted returns either a - YouTubeVideoFeed, a YouTubeUserFeed, a YouTubePlaylistFeed. If the - Query object provided was not YouTube-related, a tuple is returned. - On success the tuple will be in this form: - (boolean succeeded=True, ElementTree._Element result) - On failure, the tuple will be in this form: - (boolean succeeded=False, {'status': HTTP status code from server, - 'reason': HTTP reason from the server, - 'body': HTTP body of the server response}) - """ - result = self.Query(query.ToUri()) - if isinstance(query, YouTubeUserQuery): - return gdata.youtube.YouTubeUserFeedFromString(result.ToString()) - elif isinstance(query, YouTubePlaylistQuery): - return gdata.youtube.YouTubePlaylistFeedFromString(result.ToString()) - elif isinstance(query, YouTubeVideoQuery): - return gdata.youtube.YouTubeVideoFeedFromString(result.ToString()) - else: - return result - -class YouTubeVideoQuery(gdata.service.Query): - - """Subclasses gdata.service.Query to represent a YouTube Data API query. - - Attributes are set dynamically via properties. Properties correspond to - the standard Google Data API query parameters with YouTube Data API - extensions. Please refer to the API documentation for details. - - Attributes: - vq: The vq parameter, which is only supported for video feeds, specifies a - search query term. Refer to API documentation for further details. - orderby: The orderby parameter, which is only supported for video feeds, - specifies the value that will be used to sort videos in the search - result set. Valid values for this parameter are relevance, published, - viewCount and rating. - time: The time parameter, which is only available for the top_rated, - top_favorites, most_viewed, most_discussed, most_linked and - most_responded standard feeds, restricts the search to videos uploaded - within the specified time. Valid values for this parameter are today - (1 day), this_week (7 days), this_month (1 month) and all_time. - The default value for this parameter is all_time. - format: The format parameter specifies that videos must be available in a - particular video format. Refer to the API documentation for details. - racy: The racy parameter allows a search result set to include restricted - content as well as standard content. Valid values for this parameter - are include and exclude. By default, restricted content is excluded. - lr: The lr parameter restricts the search to videos that have a title, - description or keywords in a specific language. Valid values for the lr - parameter are ISO 639-1 two-letter language codes. - restriction: The restriction parameter identifies the IP address that - should be used to filter videos that can only be played in specific - countries. - location: A string of geo coordinates. Note that this is not used when the - search is performed but rather to filter the returned videos for ones - that match to the location entered. - feed: str (optional) The base URL which is the beginning of the query URL. - defaults to 'http://%s/feeds/videos' % (YOUTUBE_SERVER) - """ - - def __init__(self, video_id=None, feed_type=None, text_query=None, - params=None, categories=None, feed=None): - - if feed_type in YOUTUBE_STANDARDFEEDS and feed is None: - feed = 'http://%s/feeds/standardfeeds/%s' % (YOUTUBE_SERVER, feed_type) - elif (feed_type is 'responses' or feed_type is 'comments' and video_id - and feed is None): - feed = 'http://%s/feeds/videos/%s/%s' % (YOUTUBE_SERVER, video_id, - feed_type) - elif feed is None: - feed = 'http://%s/feeds/videos' % (YOUTUBE_SERVER) - - gdata.service.Query.__init__(self, feed, text_query=text_query, - params=params, categories=categories) - - def _GetVideoQuery(self): - if 'vq' in self: - return self['vq'] - else: - return None - - def _SetVideoQuery(self, val): - self['vq'] = val - - vq = property(_GetVideoQuery, _SetVideoQuery, - doc="""The video query (vq) query parameter""") - - def _GetOrderBy(self): - if 'orderby' in self: - return self['orderby'] - else: - return None - - def _SetOrderBy(self, val): - if val not in YOUTUBE_QUERY_VALID_ORDERBY_PARAMETERS: - if val.startswith('relevance_lang_') is False: - raise YouTubeError('OrderBy must be one of: %s ' % - ' '.join(YOUTUBE_QUERY_VALID_ORDERBY_PARAMETERS)) - self['orderby'] = val - - orderby = property(_GetOrderBy, _SetOrderBy, - doc="""The orderby query parameter""") - - def _GetTime(self): - if 'time' in self: - return self['time'] - else: - return None - - def _SetTime(self, val): - if val not in YOUTUBE_QUERY_VALID_TIME_PARAMETERS: - raise YouTubeError('Time must be one of: %s ' % - ' '.join(YOUTUBE_QUERY_VALID_TIME_PARAMETERS)) - self['time'] = val - - time = property(_GetTime, _SetTime, - doc="""The time query parameter""") - - def _GetFormat(self): - if 'format' in self: - return self['format'] - else: - return None - - def _SetFormat(self, val): - if val not in YOUTUBE_QUERY_VALID_FORMAT_PARAMETERS: - raise YouTubeError('Format must be one of: %s ' % - ' '.join(YOUTUBE_QUERY_VALID_FORMAT_PARAMETERS)) - self['format'] = val - - format = property(_GetFormat, _SetFormat, - doc="""The format query parameter""") - - def _GetRacy(self): - if 'racy' in self: - return self['racy'] - else: - return None - - def _SetRacy(self, val): - if val not in YOUTUBE_QUERY_VALID_RACY_PARAMETERS: - raise YouTubeError('Racy must be one of: %s ' % - ' '.join(YOUTUBE_QUERY_VALID_RACY_PARAMETERS)) - self['racy'] = val - - racy = property(_GetRacy, _SetRacy, - doc="""The racy query parameter""") - - def _GetLanguageRestriction(self): - if 'lr' in self: - return self['lr'] - else: - return None - - def _SetLanguageRestriction(self, val): - self['lr'] = val - - lr = property(_GetLanguageRestriction, _SetLanguageRestriction, - doc="""The lr (language restriction) query parameter""") - - def _GetIPRestriction(self): - if 'restriction' in self: - return self['restriction'] - else: - return None - - def _SetIPRestriction(self, val): - self['restriction'] = val - - restriction = property(_GetIPRestriction, _SetIPRestriction, - doc="""The restriction query parameter""") - - def _GetLocation(self): - if 'location' in self: - return self['location'] - else: - return None - - def _SetLocation(self, val): - self['location'] = val - - location = property(_GetLocation, _SetLocation, - doc="""The location query parameter""") - - - -class YouTubeUserQuery(YouTubeVideoQuery): - - """Subclasses YouTubeVideoQuery to perform user-specific queries. - - Attributes are set dynamically via properties. Properties correspond to - the standard Google Data API query parameters with YouTube Data API - extensions. - """ - - def __init__(self, username=None, feed_type=None, subscription_id=None, - text_query=None, params=None, categories=None): - - uploads_favorites_playlists = ('uploads', 'favorites', 'playlists') - - if feed_type is 'subscriptions' and subscription_id and username: - feed = "http://%s/feeds/users/%s/%s/%s" % (YOUTUBE_SERVER, username, - feed_type, subscription_id) - elif feed_type is 'subscriptions' and not subscription_id and username: - feed = "http://%s/feeds/users/%s/%s" % (YOUTUBE_SERVER, username, - feed_type) - elif feed_type in uploads_favorites_playlists: - feed = "http://%s/feeds/users/%s/%s" % (YOUTUBE_SERVER, username, - feed_type) - else: - feed = "http://%s/feeds/users" % (YOUTUBE_SERVER) - - YouTubeVideoQuery.__init__(self, feed=feed, text_query=text_query, - params=params, categories=categories) - - -class YouTubePlaylistQuery(YouTubeVideoQuery): - - """Subclasses YouTubeVideoQuery to perform playlist-specific queries. - - Attributes are set dynamically via properties. Properties correspond to - the standard Google Data API query parameters with YouTube Data API - extensions. - """ - - def __init__(self, playlist_id, text_query=None, params=None, - categories=None): - if playlist_id: - feed = "http://%s/feeds/playlists/%s" % (YOUTUBE_SERVER, playlist_id) - else: - feed = "http://%s/feeds/playlists" % (YOUTUBE_SERVER) - - YouTubeVideoQuery.__init__(self, feed=feed, text_query=text_query, - params=params, categories=categories) diff --git a/gdata/apps/adminaudit/__init__.py b/gdata/apps/adminaudit/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/gdata/apps/adminaudit/service.py b/gdata/apps/adminaudit/service.py deleted file mode 100644 index 378ce9b3b1..0000000000 --- a/gdata/apps/adminaudit/service.py +++ /dev/null @@ -1,82 +0,0 @@ -#!/usr/bin/python2.4 -# -# 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. - -"""AdminAuditService simplifies Admin Audit API calls. - -AdminAuditService extends gdata.apps.service.PropertyService to ease interaction with -the Google Apps Admin Audit API. -""" - -__author__ = 'Jay Lee ' - -import gdata.apps -import gdata.apps.service -import gdata.service -import json - - -class AdminAuditService(gdata.apps.service.PropertyService): - """Service extension for the Google Admin Audit API service.""" - - def __init__(self, email=None, password=None, domain=None, source=None, - server='www.googleapis.com', additional_headers=None, - **kwargs): - """Creates a client for the Admin Audit service. - - Args: - email: string (optional) The user's email address, used for - authentication. - password: string (optional) The user's password. - domain: string (optional) The Google Apps domain name. - 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: 'apps-apis.google.com'. - **kwargs: The other parameters to pass to gdata.service.GDataService - constructor. - """ - gdata.service.GDataService.__init__( - self, email=email, password=password, service='apps', source=source, - server=server, additional_headers=additional_headers, **kwargs) - self.ssl = True - self.port = 443 - self.domain = domain - - def retrieve_audit(self, customer_id, admin=None, event=None, start_date=None, end_date=None, max_results=None): - """Retrieves an audit - - """ - uri = '/apps/reporting/audit/v1/%s/207535951991' % customer_id - use_char = '?' - if admin != None: - uri += '%sactorEmail=%s' % (use_char, admin) - use_char = '&' - if event != None: - uri += '%seventName=%s' % (use_char, event) - use_char = '&' - if start_date != None: - uri += '%sstartTime=%s' % (use_char, start_date) - use_char = '&' - if end_date != None: - uri += '%sendTime=%s' % (use_char, end_date) - use_char = '&' - if max_results != None: - uri += '%smaxResults=%s' % (use_char, max_results) - try: - return self.Get(uri, converter=str) - except gdata.service.RequestError, e: - raise gdata.apps.service.AppsForYourDomainException(e.args[0]) - - RetrieveAudit = retrieve_audit diff --git a/gdata/apps/emailsettings/client.py b/gdata/apps/emailsettings/client.py deleted file mode 100644 index 8752d2d4fe..0000000000 --- a/gdata/apps/emailsettings/client.py +++ /dev/null @@ -1,386 +0,0 @@ -#!/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. - -"""EmailSettingsClient simplifies Email Settings API calls. - -EmailSettingsClient extends gdata.client.GDClient to ease interaction with -the Google Apps Email Settings API. These interactions include the ability -to create labels, filters, aliases, and update web-clip, forwarding, POP, -IMAP, vacation-responder, signature, language, and general settings. -""" - - -__author__ = 'Claudio Cherubino ' - - -import gdata.apps.emailsettings.data -import gdata.client -import urllib - - -# Email Settings URI template -# The strings in this template are eventually replaced with the API version, -# Google Apps domain name, username, and settingID, respectively. -EMAIL_SETTINGS_URI_TEMPLATE = '/a/feeds/emailsettings/%s/%s/%s/%s' - - -# The settingID value for the label requests -SETTING_ID_LABEL = 'label' -# The settingID value for the filter requests -SETTING_ID_FILTER = 'filter' -# The settingID value for the send-as requests -SETTING_ID_SENDAS = 'sendas' -# The settingID value for the webclip requests -SETTING_ID_WEBCLIP = 'webclip' -# The settingID value for the forwarding requests -SETTING_ID_FORWARDING = 'forwarding' -# The settingID value for the POP requests -SETTING_ID_POP = 'pop' -# The settingID value for the IMAP requests -SETTING_ID_IMAP = 'imap' -# The settingID value for the vacation responder requests -SETTING_ID_VACATION_RESPONDER = 'vacation' -# The settingID value for the signature requests -SETTING_ID_SIGNATURE = 'signature' -# The settingID value for the language requests -SETTING_ID_LANGUAGE = 'language' -# The settingID value for the general requests -SETTING_ID_GENERAL = 'general' - - -class EmailSettingsClient(gdata.client.GDClient): - """Client extension for the Google Email Settings API service. - - Attributes: - host: string The hostname for the Email Settings API service. - api_version: string The version of the Email Settings API. - """ - - host = 'apps-apis.google.com' - api_version = '2.0' - auth_service = 'apps' - auth_scopes = gdata.gauth.AUTH_SCOPES['apps'] - ssl = True - - def __init__(self, domain, auth_token=None, **kwargs): - """Constructs a new client for the Email Settings API. - - Args: - domain: string The Google Apps domain with Email Settings. - auth_token: (optional) gdata.gauth.ClientLoginToken, AuthSubToken, or - OAuthToken which authorizes this client to edit the email settings. - kwargs: The other parameters to pass to the gdata.client.GDClient - constructor. - """ - gdata.client.GDClient.__init__(self, auth_token=auth_token, **kwargs) - self.domain = domain - - def make_email_settings_uri(self, username, setting_id): - """Creates the URI for the Email Settings API call. - - Using this client's Google Apps domain, create the URI to setup - email settings for the given user in that domain. If params are provided, - append them as GET params. - - Args: - username: string The name of the user affected by this setting. - setting_id: string The key of the setting to be configured. - - Returns: - A string giving the URI for Email Settings API calls for this client's - Google Apps domain. - """ - uri = EMAIL_SETTINGS_URI_TEMPLATE % (self.api_version, self.domain, - username, setting_id) - return uri - - MakeEmailSettingsUri = make_email_settings_uri - - def create_label(self, username, name, **kwargs): - """Creates a label with the given properties. - - Args: - username: string The name of the user. - name: string The name of the label. - kwargs: The other parameters to pass to gdata.client.GDClient.post(). - - Returns: - gdata.apps.emailsettings.data.EmailSettingsLabel of the new resource. - """ - uri = self.MakeEmailSettingsUri(username=username, - setting_id=SETTING_ID_LABEL) - new_label = gdata.apps.emailsettings.data.EmailSettingsLabel( - uri=uri, name=name) - return self.post(new_label, uri, **kwargs) - - CreateLabel = create_label - - def create_filter(self, username, from_address=None, - to_address=None, subject=None, has_the_word=None, - does_not_have_the_word=None, has_attachments=None, - label=None, mark_as_read=None, archive=None, **kwargs): - """Creates a filter with the given properties. - - Args: - username: string The name of the user. - from_address: string The source email address for the filter. - to_address: string (optional) The destination email address for - the filter. - subject: string (optional) The value the email must have in its - subject to be filtered. - has_the_word: string (optional) The value the email must have - in its subject or body to be filtered. - does_not_have_the_word: string (optional) The value the email - cannot have in its subject or body to be filtered. - has_attachments: string (optional) A boolean string representing - whether the email must have an attachment to be filtered. - label: string (optional) The name of the label to apply to - messages matching the filter criteria. - mark_as_read: Boolean (optional) Whether or not to mark - messages matching the filter criteria as read. - archive: Boolean (optional) Whether or not to move messages - matching to Archived state. - kwargs: The other parameters to pass to gdata.client.GDClient.post(). - - Returns: - gdata.apps.emailsettings.data.EmailSettingsFilter of the new resource. - """ - uri = self.MakeEmailSettingsUri(username=username, - setting_id=SETTING_ID_FILTER) - new_filter = gdata.apps.emailsettings.data.EmailSettingsFilter( - uri=uri, from_address=from_address, - to_address=to_address, subject=subject, - has_the_word=has_the_word, - does_not_have_the_word=does_not_have_the_word, - has_attachments=has_attachments, label=label, - mark_as_read=mark_as_read, archive=archive) - return self.post(new_filter, uri, **kwargs) - - CreateFilter = create_filter - - def create_sendas(self, username, name, address, reply_to=None, - make_default=None, **kwargs): - """Creates a send-as alias with the given properties. - - Args: - username: string The name of the user. - name: string The name that will appear in the "From" field. - address: string The email address that appears as the - origination address for emails sent by this user. - reply_to: string (optional) The address to be used as the reply-to - address in email sent using the alias. - make_default: Boolean (optional) Whether or not this alias should - become the default alias for this user. - kwargs: The other parameters to pass to gdata.client.GDClient.post(). - - Returns: - gdata.apps.emailsettings.data.EmailSettingsSendAsAlias of the - new resource. - """ - uri = self.MakeEmailSettingsUri(username=username, - setting_id=SETTING_ID_SENDAS) - new_alias = gdata.apps.emailsettings.data.EmailSettingsSendAsAlias( - uri=uri, name=name, address=address, - reply_to=reply_to, make_default=make_default) - return self.post(new_alias, uri, **kwargs) - - CreateSendAs = create_sendas - - def update_webclip(self, username, enable, **kwargs): - """Enable/Disable Google Mail web clip. - - Args: - username: string The name of the user. - enable: Boolean Whether to enable showing Web clips. - kwargs: The other parameters to pass to the update method. - - Returns: - gdata.apps.emailsettings.data.EmailSettingsWebClip of the - updated resource. - """ - uri = self.MakeEmailSettingsUri(username=username, - setting_id=SETTING_ID_WEBCLIP) - new_webclip = gdata.apps.emailsettings.data.EmailSettingsWebClip( - uri=uri, enable=enable) - return self.update(new_webclip, **kwargs) - - UpdateWebclip = update_webclip - - def update_forwarding(self, username, enable, forward_to=None, - action=None, **kwargs): - """Update Google Mail Forwarding settings. - - Args: - username: string The name of the user. - enable: Boolean Whether to enable incoming email forwarding. - forward_to: (optional) string The address email will be forwarded to. - action: string (optional) The action to perform after forwarding - an email ("KEEP", "ARCHIVE", "DELETE"). - kwargs: The other parameters to pass to the update method. - - Returns: - gdata.apps.emailsettings.data.EmailSettingsForwarding of the - updated resource - """ - uri = self.MakeEmailSettingsUri(username=username, - setting_id=SETTING_ID_FORWARDING) - new_forwarding = gdata.apps.emailsettings.data.EmailSettingsForwarding( - uri=uri, enable=enable, forward_to=forward_to, action=action) - return self.update(new_forwarding, **kwargs) - - UpdateForwarding = update_forwarding - - def update_pop(self, username, enable, enable_for=None, action=None, - **kwargs): - """Update Google Mail POP settings. - - Args: - username: string The name of the user. - enable: Boolean Whether to enable incoming POP3 access. - enable_for: string (optional) Whether to enable POP3 for all mail - ("ALL_MAIL"), or mail from now on ("MAIL_FROM_NOW_ON"). - action: string (optional) What Google Mail should do with its copy - of the email after it is retrieved using POP ("KEEP", - "ARCHIVE", or "DELETE"). - kwargs: The other parameters to pass to the update method. - - Returns: - gdata.apps.emailsettings.data.EmailSettingsPop of the updated resource. - """ - uri = self.MakeEmailSettingsUri(username=username, - setting_id=SETTING_ID_POP) - new_pop = gdata.apps.emailsettings.data.EmailSettingsPop( - uri=uri, enable=enable, - enable_for=enable_for, action=action) - return self.update(new_pop, **kwargs) - - UpdatePop = update_pop - - def update_imap(self, username, enable, **kwargs): - """Update Google Mail IMAP settings. - - Args:import gdata.apps_property - username: string The name of the user. - enable: Boolean Whether to enable IMAP access.language - kwargs: The other parameters to pass to the update method. - - Returns: - gdata.apps.emailsettings.data.EmailSettingsImap of the updated resource. - """ - uri = self.MakeEmailSettingsUri(username=username, - setting_id=SETTING_ID_IMAP) - new_imap = gdata.apps.emailsettings.data.EmailSettingsImap( - uri=uri, enable=enable) - return self.update(new_imap, **kwargs) - - UpdateImap = update_imap - - def update_vacation(self, username, enable, subject=None, message=None, - contacts_only=None, **kwargs): - """Update Google Mail vacation-responder settings. - - Args: - username: string The name of the user. - enable: Boolean Whether to enable the vacation responder. - subject: string (optional) The subject line of the vacation responder - autoresponse. - message: string (optional) The message body of the vacation responder - autoresponse. - contacts_only: Boolean (optional) Whether to only send autoresponses - to known contacts. - kwargs: The other parameters to pass to the update method. - - Returns: - gdata.apps.emailsettings.data.EmailSettingsVacationResponder of the - updated resource. - """ - uri = self.MakeEmailSettingsUri(username=username, - setting_id=SETTING_ID_VACATION_RESPONDER) - new_vacation = gdata.apps.emailsettings.data.EmailSettingsVacationResponder( - uri=uri, enable=enable, subject=subject, - message=message, contacts_only=contacts_only) - return self.update(new_vacation, **kwargs) - - UpdateVacation = update_vacation - - def update_signature(self, username, signature, **kwargs): - """Update Google Mail signature. - - Args: - username: string The name of the user. - signature: string The signature to be appended to outgoing messages. - kwargs: The other parameters to pass to the update method. - - Returns: - gdata.apps.emailsettings.data.EmailSettingsSignature of the - updated resource. - """ - uri = self.MakeEmailSettingsUri(username=username, - setting_id=SETTING_ID_SIGNATURE) - new_signature = gdata.apps.emailsettings.data.EmailSettingsSignature( - uri=uri, signature=signature) - return self.update(new_signature, **kwargs) - - UpdateSignature = update_signature - - def update_language(self, username, language, **kwargs): - """Update Google Mail language settings. - - Args: - username: string The name of the user. - signature: string The language tag for Google Mail's display language. - kwargs: The other parameters to pass to the update method. - - Returns: - gdata.apps.emailsettings.data.EmailSettingsLanguage of the - updated resource. - """ - uri = self.MakeEmailSettingsUri(username=username, - setting_id=SETTING_ID_LANGUAGE) - new_language = gdata.apps.emailsettings.data.EmailSettingsLanguage( - uri=uri, language=language) - return self.update(new_language, **kwargs) - - UpdateLanguage = update_language - - def update_general(self, username, page_size=None, shortcuts=None, arrows=None, - snippets=None, use_unicode=None, **kwargs): - """Update Google Mail general settings. - - Args: - username: string The name of the user. - page_size: int (optional) The number of conversations to be shown per page. - shortcuts: Boolean (optional) Whether to enable keyboard shortcuts. - arrows: Boolean (optional) Whether to display arrow-shaped personal - indicators next to email sent specifically to the user. - snippets: Boolean (optional) Whether to display snippets of the messages - in the inbox and when searching. - use_unicode: Boolean (optional) Whether to use UTF-8 (unicode) encoding - for all outgoing messages. - kwargs: The other parameters to pass to the update method. - - Returns: - gdata.apps.emailsettings.data.EmailSettingsGeneral of the - updated resource. - """ - uri = self.MakeEmailSettingsUri(username=username, - setting_id=SETTING_ID_GENERAL) - new_general = gdata.apps.emailsettings.data.EmailSettingsGeneral( - uri=uri, page_size=page_size, shortcuts=shortcuts, - arrows=arrows, snippets=snippets, use_unicode=use_unicode) - return self.update(new_general, **kwargs) - - UpdateGeneral = update_general diff --git a/gdata/apps/emailsettings/data.py b/gdata/apps/emailsettings/data.py deleted file mode 100644 index f753c0699c..0000000000 --- a/gdata/apps/emailsettings/data.py +++ /dev/null @@ -1,1130 +0,0 @@ -#!/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 the Email Settings API.""" - - -__author__ = 'Claudio Cherubino ' - - -import atom.data -import gdata.apps -import gdata.apps_property -import gdata.data - - -# This is required to work around a naming conflict between the Google -# Spreadsheets API and Python's built-in property function -pyproperty = property - - -# The apps:property label of the label property -LABEL_NAME = 'label' - -# The apps:property from of the filter property -FILTER_FROM_NAME = 'from' -# The apps:property to of the filter property -FILTER_TO_NAME = 'to' -# The apps:property subject of the filter property -FILTER_SUBJECT_NAME = 'subject' -# The apps:property hasTheWord of the filter property -FILTER_HAS_THE_WORD_NAME = 'hasTheWord' -# The apps:property doesNotHaveTheWord of the filter property -FILTER_DOES_NOT_HAVE_THE_WORD_NAME = 'doesNotHaveTheWord' -# The apps:property hasAttachment of the filter property -FILTER_HAS_ATTACHMENTS_NAME = 'hasAttachment' -# The apps:property label of the filter action property -FILTER_LABEL = 'label' -# The apps:property shouldMarkAsRead of the filter action property -FILTER_MARK_AS_READ = 'shouldMarkAsRead' -# The apps:property shouldArchive of the filter action propertylabel -FILTER_ARCHIVE = 'shouldArchive' - -# The apps:property name of the send-as alias property -SENDAS_ALIAS_NAME = 'name' -# The apps:property address of theAPPS_TEMPLATE send-as alias property -SENDAS_ALIAS_ADDRESS = 'address' -# The apps:property replyTo of the send-as alias property -SENDAS_ALIAS_REPLY_TO = 'replyTo' -# The apps:property makeDefault of the send-as alias property -SENDAS_ALIAS_MAKE_DEFAULT = 'makeDefault' - -# The apps:property enable of the webclip property -WEBCLIP_ENABLE = 'enable' - -# The apps:property enable of the forwarding property -FORWARDING_ENABLE = 'enable' -# The apps:property forwardTo of the forwarding property -FORWARDING_TO = 'forwardTo' -# The apps:property action of the forwarding property -FORWARDING_ACTION = 'action' - -# The apps:property enable of the POP property -POP_ENABLE = 'enable' -# The apps:property enableFor of the POP propertyACTION -POP_ENABLE_FOR = 'enableFor' -# The apps:property action of the POP property -POP_ACTION = 'action' - -# The apps:property enable of the IMAP property -IMAP_ENABLE = 'enable' - -# The apps:property enable of the vacation responder property -VACATION_RESPONDER_ENABLE = 'enable' -# The apps:property subject of the vacation responder property -VACATION_RESPONDER_SUBJECT = 'subject' -# The apps:property message of the vacation responder property -VACATION_RESPONDER_MESSAGE = 'message' -# The apps:property contactsOnly of the vacation responder property -VACATION_RESPONDER_CONTACTS_ONLY = 'contactsOnly' - -# The apps:property signature of the signature property -SIGNATURE_VALUE = 'signature' - -# The apps:property language of the language property -LANGUAGE_TAG = 'language' - -# The apps:property pageSize of the general settings property -GENERAL_PAGE_SIZE = 'pageSize' -# The apps:property shortcuts of the general settings property -GENERAL_SHORTCUTS = 'shortcuts' -# The apps:property arrows of the general settings property -GENERAL_ARROWS = 'arrows' -# The apps:prgdata.appsoperty snippets of the general settings property -GENERAL_SNIPPETS = 'snippets' -# The apps:property uniAppsProcode of the general settings property -GENERAL_UNICODE = 'unicode' - - -class EmailSettingsEntry(gdata.data.GDEntry): - """Represents an Email Settings entry in object form.""" - - property = [gdata.apps_property.AppsProperty] - - def _GetProperty(self, name): - """Get the apps:property value with the given name. - - Args: - name: string Name of the apps:property value to get. - - Returns: - The apps:property value with the given name, or None if the name was - invalid. - """ - - value = None - for p in self.property: - if p.name == name: - value = p.value - break - return value - - def _SetProperty(self, name, value): - """Set the apps:property value with the given name to the given value. - - Args: - name: string Name of the apps:property value to set. - value: string Value to give the apps:property value with the given name. - """ - found = False - for i in range(len(self.property)): - if self.property[i].name == name: - self.property[i].value = value - found = True - break - if not found: - self.property.append(gdata.apps_property.AppsProperty(name=name, value=value)) - - def find_edit_link(self): - return self.uri - - -class EmailSettingsLabel(EmailSettingsEntry): - """Represents a Label in object form.""" - - def GetName(self): - """Get the name of the Label object. - - Returns: - The name of this Label object as a string or None. - """ - - return self._GetProperty(LABEL_NAME) - - def SetName(self, value): - """Set the name of this Label object. - - Args: - value: string The new label name to give this object. - """ - - self._SetProperty(LABEL_NAME, value) - - name = pyproperty(GetName, SetName) - - def __init__(self, uri=None, name=None, *args, **kwargs): - """Constructs a new EmailSettingsLabel object with the given arguments. - - Args: - uri: string (optional) The uri of of this object for HTTP requests. - name: string (optional) The name to give this new object. - args: The other parameters to pass to gdata.entry.GDEntry constructor. - kwargs: The other parameters to pass to gdata.entry.GDEntry constructor. - """ - super(EmailSettingsLabel, self).__init__(*args, **kwargs) - if uri: - self.uri = uri - if name: - self.name = name - - -class EmailSettingsFilter(EmailSettingsEntry): - """Represents an Email Settings Filter in object form.""" - - def GetFrom(self): - """Get the From value of the Filter object. - - Returns: - The From value of this Filter object as a string or None. - """ - - return self._GetProperty(FILTER_FROM_NAME) - - def SetFrom(self, value): - """Set the From value of this Filter object. - - Args: - value: string The new From value to give this object. - """ - - self._SetProperty(FILTER_FROM_NAME, value) - - from_address = pyproperty(GetFrom, SetFrom) - - def GetTo(self): - """Get the To value of the Filter object. - - Returns: - The To value of this Filter object as a string or None. - """ - - return self._GetProperty(FILTER_TO_NAME) - - def SetTo(self, value): - """Set the To value of this Filter object. - - Args: - value: string The new To value to give this object. - """ - - self._SetProperty(FILTER_TO_NAME, value) - - to_address = pyproperty(GetTo, SetTo) - - def GetSubject(self): - """Get the Subject value of the Filter object. - - Returns: - The Subject value of this Filter object as a string or None. - """ - - return self._GetProperty(FILTER_SUBJECT_NAME) - - def SetSubject(self, value): - """Set the Subject value of this Filter object. - - Args: - value: string The new Subject value to give this object. - """ - - self._SetProperty(FILTER_SUBJECT_NAME, value) - - subject = pyproperty(GetSubject, SetSubject) - - def GetHasTheWord(self): - """Get the HasTheWord value of the Filter object. - - Returns: - The HasTheWord value of this Filter object as a string or None. - """ - - return self._GetProperty(FILTER_HAS_THE_WORD_NAME) - - def SetHasTheWord(self, value): - """Set the HasTheWord value of this Filter object. - - Args: - value: string The new HasTheWord value to give this object. - """ - - self._SetProperty(FILTER_HAS_THE_WORD_NAME, value) - - has_the_word = pyproperty(GetHasTheWord, SetHasTheWord) - - def GetDoesNotHaveTheWord(self): - """Get the DoesNotHaveTheWord value of the Filter object. - - Returns: - The DoesNotHaveTheWord value of this Filter object as a string or None. - """ - - return self._GetProperty(FILTER_DOES_NOT_HAVE_THE_WORD_NAME) - - def SetDoesNotHaveTheWord(self, value): - """Set the DoesNotHaveTheWord value of this Filter object. - - Args: - value: string The new DoesNotHaveTheWord value to give this object. - """ - - self._SetProperty(FILTER_DOES_NOT_HAVE_THE_WORD_NAME, value) - - does_not_have_the_word = pyproperty(GetDoesNotHaveTheWord, - SetDoesNotHaveTheWord) - - def GetHasAttachments(self): - """Get the HasAttachments value of the Filter object. - - Returns: - The HasAttachments value of this Filter object as a string or None. - """ - - return self._GetProperty(FILTER_HAS_ATTACHMENTS_NAME) - - def SetHasAttachments(self, value): - """Set the HasAttachments value of this Filter object. - - Args: - value: string The new HasAttachments value to give this object. - """ - - self._SetProperty(FILTER_HAS_ATTACHMENTS_NAME, value) - - has_attachments = pyproperty(GetHasAttachments, - SetHasAttachments) - - def GetLabel(self): - """Get the Label value of the Filter object. - - Returns: - The Label value of this Filter object as a string or None. - """ - - return self._GetProperty(FILTER_LABEL) - - def SetLabel(self, value): - """Set the Label value of this Filter object. - - Args: - value: string The new Label value to give this object. - """ - - self._SetProperty(FILTER_LABEL, value) - - label = pyproperty(GetLabel, SetLabel) - - def GetMarkAsRead(self): - """Get the MarkAsRead value of the Filter object. - - Returns: - The MarkAsRead value of this Filter object as a string or None. - """ - - return self._GetProperty(FILTER_MARK_AS_READ) - - def SetMarkAsRead(self, value): - """Set the MarkAsRead value of this Filter object. - - Args: - value: string The new MarkAsRead value to give this object. - """ - - self._SetProperty(FILTER_MARK_AS_READ, value) - - mark_as_read = pyproperty(GetMarkAsRead, SetMarkAsRead) - - def GetArchive(self): - """Get the Archive value of the Filter object. - - Returns: - The Archive value of this Filter object as a string or None. - """ - - return self._GetProperty(FILTER_ARCHIVE) - - def SetArchive(self, value): - """Set the Archive value of this Filter object. - - Args: - value: string The new Archive value to give this object. - """ - - self._SetProperty(FILTER_ARCHIVE, value) - - archive = pyproperty(GetArchive, SetArchive) - - def __init__(self, uri=None, from_address=None, to_address=None, - subject=None, has_the_word=None, does_not_have_the_word=None, - has_attachments=None, label=None, mark_as_read=None, - archive=None, *args, **kwargs): - """Constructs a new EmailSettingsFilter object with the given arguments. - - Args: - uri: string (optional) The uri of of this object for HTTP requests. - from_address: string (optional) The source email address for the filter. - to_address: string (optional) The destination email address for - the filter. - subject: string (optional) The value the email must have in its - subject to be filtered. - has_the_word: string (optional) The value the email must have in its - subject or body to be filtered. - does_not_have_the_word: string (optional) The value the email cannot - have in its subject or body to be filtered. - has_attachments: Boolean (optional) Whether or not the email must - have an attachment to be filtered. - label: string (optional) The name of the label to apply to - messages matching the filter criteria. - mark_as_read: Boolean (optional) Whether or not to mark messages - matching the filter criteria as read. - archive: Boolean (optional) Whether or not to move messages - matching to Archived state. - args: The other parameters to pass to gdata.entry.GDEntry constructor. - kwargs: The other parameters to pass to gdata.entry.GDEntry constructor. - """ - super(EmailSettingsFilter, self).__init__(*args, **kwargs) - if uri: - self.uri = uri - if from_address: - self.from_address = from_address - if to_address: - self.to_address = to_address - if subject: - self.subject = subject - if has_the_word: - self.has_the_word = has_the_word - if does_not_have_the_word: - self.does_not_have_the_word = does_not_have_the_word - if has_attachments is not None: - self.has_attachments = str(has_attachments) - if label: - self.label = label - if mark_as_read is not None: - self.mark_as_read = str(mark_as_read) - if archive is not None: - self.archive = str(archive) - - -class EmailSettingsSendAsAlias(EmailSettingsEntry): - """Represents an Email Settings send-as Alias in object form.""" - - def GetName(self): - """Get the Name of the send-as Alias object. - - Returns: - The Name of this send-as Alias object as a string or None. - """ - - return self._GetProperty(SENDAS_ALIAS_NAME) - - def SetName(self, value): - """Set the Name of this send-as Alias object. - - Args: - value: string The new Name to give this object. - """ - - self._SetProperty(SENDAS_ALIAS_NAME, value) - - name = pyproperty(GetName, SetName) - - def GetAddress(self): - """Get the Address of the send-as Alias object. - - Returns: - The Address of this send-as Alias object as a string or None. - """ - - return self._GetProperty(SENDAS_ALIAS_ADDRESS) - - def SetAddress(self, value): - """Set the Address of this send-as Alias object. - - Args: - value: string The new Address to give this object. - """ - - self._SetProperty(SENDAS_ALIAS_ADDRESS, value) - - address = pyproperty(GetAddress, SetAddress) - - def GetReplyTo(self): - """Get the ReplyTo address of the send-as Alias object. - - Returns: - The ReplyTo address of this send-as Alias object as a string or None. - """ - - return self._GetProperty(SENDAS_ALIAS_REPLY_TO) - - def SetReplyTo(self, value): - """Set the ReplyTo address of this send-as Alias object. - - Args: - value: string The new ReplyTo address to give this object. - """ - - self._SetProperty(SENDAS_ALIAS_REPLY_TO, value) - - reply_to = pyproperty(GetReplyTo, SetReplyTo) - - def GetMakeDefault(self): - """Get the MakeDefault value of the send-as Alias object. - - Returns: - The MakeDefault value of this send-as Alias object as a string or None. - """ - - return self._GetProperty(SENDAS_ALIAS_MAKE_DEFAULT) - - def SetMakeDefault(self, value): - """Set the MakeDefault value of this send-as Alias object. - - Args: - value: string The new MakeDefault valueto give this object.WebClip - """ - - self._SetProperty(SENDAS_ALIAS_MAKE_DEFAULT, value) - - make_default = pyproperty(GetMakeDefault, SetMakeDefault) - - def __init__(self, uri=None, name=None, address=None, reply_to=None, - make_default=None, *args, **kwargs): - """Constructs a new EmailSettingsSendAsAlias object with the given - arguments. - - Args: - uri: string (optional) The uri of of this object for HTTP requests. - name: string (optional) The name that will appear in the "From" field - for this user. - address: string (optional) The email address that appears as the - origination address for emails sent by this user. - reply_to: string (optional) The address to be used as the reply-to - address in email sent using the alias. - make_default: Boolean (optional) Whether or not this alias should - become the default alias for this user. - args: The other parameters to pass to gdata.entry.GDEntry constructor. - kwargs: The other parameters to pass to gdata.entry.GDEntry constructor. - """ - super(EmailSettingsSendAsAlias, self).__init__(*args, **kwargs) - if uri: - self.uri = uri - if name: - self.name = name - if address: - self.address = address - if reply_to: - self.reply_to = reply_to - if make_default is not None: - self.make_default = str(make_default) - - -class EmailSettingsWebClip(EmailSettingsEntry): - """Represents a WebClip in object form.""" - - def GetEnable(self): - """Get the Enable value of the WebClip object. - - Returns: - The Enable value of this WebClip object as a string or None. - """ - - return self._GetProperty(WEBCLIP_ENABLE) - - def SetEnable(self, value): - """Set the Enable value of this WebClip object. - - Args: - value: string The new Enable value to give this object. - """ - - self._SetProperty(WEBCLIP_ENABLE, value) - - enable = pyproperty(GetEnable, SetEnable) - - def __init__(self, uri=None, enable=None, *args, **kwargs): - """Constructs a new EmailSettingsWebClip object with the given arguments. - - Args: - uri: string (optional) The uri of of this object for HTTP requests. - enable: Boolean (optional) Whether to enable showing Web clips. - args: The other parameters to pass to gdata.entry.GDEntry constructor. - kwargs: The other parameters to pass to gdata.entry.GDEntry constructor. - """ - super(EmailSettingsWebClip, self).__init__(*args, **kwargs) - if uri: - self.uri = uri - if enable is not None: - self.enable = str(enable) - - -class EmailSettingsForwarding(EmailSettingsEntry): - """Represents Forwarding settings in object form.""" - - def GetEnable(self): - """Get the Enable value of the Forwarding object. - - Returns: - The Enable value of this Forwarding object as a string or None. - """ - - return self._GetProperty(FORWARDING_ENABLE) - - def SetEnable(self, value): - """Set the Enable value of this Forwarding object. - - Args: - value: string The new Enable value to give this object. - """ - - self._SetProperty(FORWARDING_ENABLE, value) - - enable = pyproperty(GetEnable, SetEnable) - - def GetForwardTo(self): - """Get the ForwardTo value of the Forwarding object. - - Returns: - The ForwardTo value of this Forwarding object as a string or None. - """ - - return self._GetProperty(FORWARDING_TO) - - def SetForwardTo(self, value): - """Set the ForwardTo value of this Forwarding object. - - Args: - value: string The new ForwardTo value to give this object. - """ - - self._SetProperty(FORWARDING_TO, value) - - forward_to = pyproperty(GetForwardTo, SetForwardTo) - - def GetAction(self): - """Get the Action value of the Forwarding object. - - Returns: - The Action value of this Forwarding object as a string or None. - """ - - return self._GetProperty(FORWARDING_ACTION) - - def SetAction(self, value): - """Set the Action value of this Forwarding object. - - Args: - value: string The new Action value to give this object. - """ - - self._SetProperty(FORWARDING_ACTION, value) - - action = pyproperty(GetAction, SetAction) - - def __init__(self, uri=None, enable=None, forward_to=None, action=None, - *args, **kwargs): - """Constructs a new EmailSettingsForwarding object with the given arguments. - - Args: - uri: string (optional) The uri of of this object for HTTP requests. - enable: Boolean (optional) Whether to enable incoming email forwarding. - forward_to: string (optional) The address email will be forwarded to. - action: string (optional) The action to perform after forwarding an - email ("KEEP", "ARCHIVE", "DELETE"). - args: The other parameters to pass to gdata.entry.GDEntry constructor. - kwargs: The other parameters to pass to gdata.entry.GDEntry constructor. - """ - super(EmailSettingsForwarding, self).__init__(*args, **kwargs) - if uri: - self.uri = uri - if enable is not None: - self.enable = str(enable) - if forward_to: - self.forward_to = forward_to - if action: - self.action = action - - -class EmailSettingsPop(EmailSettingsEntry): - """Represents POP settings in object form.""" - - def GetEnable(self): - """Get the Enable value of the POP object. - - Returns: - The Enable value of this POP object as a string or None. - """ - - return self._GetProperty(POP_ENABLE) - - def SetEnable(self, value): - """Set the Enable value of this POP object. - - Args: - value: string The new Enable value to give this object. - """ - - self._SetProperty(POP_ENABLE, value) - - enable = pyproperty(GetEnable, SetEnable) - - def GetEnableFor(self): - """Get the EnableFor value of the POP object. - - Returns: - The EnableFor value of this POP object as a string or None. - """ - - return self._GetProperty(POP_ENABLE_FOR) - - def SetEnableFor(self, value): - """Set the EnableFor value of this POP object. - - Args: - value: string The new EnableFor value to give this object. - """ - - self._SetProperty(POP_ENABLE_FOR, value) - - enable_for = pyproperty(GetEnableFor, SetEnableFor) - - def GetPopAction(self): - """Get the Action value of the POP object. - - Returns: - The Action value of this POP object as a string or None. - """ - - return self._GetProperty(POP_ACTION) - - def SetPopAction(self, value): - """Set the Action value of this POP object. - - Args: - value: string The new Action value to give this object. - """ - - self._SetProperty(POP_ACTION, value) - - action = pyproperty(GetPopAction, SetPopAction) - - def __init__(self, uri=None, enable=None, enable_for=None, - action=None, *args, **kwargs): - """Constructs a new EmailSettingsPOP object with the given arguments. - - Args: - uri: string (optional) The uri of of this object for HTTP requests. - enable: Boolean (optional) Whether to enable incoming POP3 access. - enable_for: string (optional) Whether to enable POP3 for all mail - ("ALL_MAIL"), or mail from now on ("MAIL_FROM_NOW_ON"). - action: string (optional) What Google Mail should do with its copy - of the email after it is retrieved using POP - ("KEEP", "ARCHIVE", or "DELETE"). - args: The other parameters to pass to gdata.entry.GDEntry constructor. - kwargs: The other parameters to pass to gdata.entry.GDEntry constructor. - """ - super(EmailSettingsPop, self).__init__(*args, **kwargs) - if uri: - self.uri = uri - if enable is not None: - self.enable = str(enable) - if enable_for: - self.enable_for = enable_for - if action: - self.action = action - - -class EmailSettingsImap(EmailSettingsEntry): - """Represents IMAP settings in object form.""" - - def GetEnable(self): - """Get the Enable value of the IMAP object. - - Returns: - The Enable value of this IMAP object as a string or None. - """ - - return self._GetProperty(IMAP_ENABLE) - - def SetEnable(self, value): - """Set the Enable value of this IMAP object. - - Args: - value: string The new Enable value to give this object. - """ - - self._SetProperty(IMAP_ENABLE, value) - - enable = pyproperty(GetEnable, SetEnable) - - def __init__(self, uri=None, enable=None, *args, **kwargs): - """Constructs a new EmailSettingsImap object with the given arguments. - - Args: - uri: string (optional) The uri of of this object for HTTP requests. - enable: Boolean (optional) Whether to enable IMAP access. - args: The other parameters to pass to gdata.entry.GDEntry constructor. - kwargs: The other parameters to pass to gdata.entry.GDEntry constructor. - """ - super(EmailSettingsImap, self).__init__(*args, **kwargs) - if uri: - self.uri = uri - if enable is not None: - self.enable = str(enable) - - -class EmailSettingsVacationResponder(EmailSettingsEntry): - """Represents Vacation Responder settings in object form.""" - - def GetEnable(self): - """Get the Enable value of the Vacation Responder object. - - Returns: - The Enable value of this Vacation Responder object as a string or None. - """ - - return self._GetProperty(VACATION_RESPONDER_ENABLE) - - def SetEnable(self, value): - """Set the Enable value of this Vacation Responder object. - - Args: - value: string The new Enable value to give this object. - """ - - self._SetProperty(VACATION_RESPONDER_ENABLE, value) - - enable = pyproperty(GetEnable, SetEnable) - - def GetSubject(self): - """Get the Subject value of the Vacation Responder object. - - Returns: - The Subject value of this Vacation Responder object as a string or None. - """ - - return self._GetProperty(VACATION_RESPONDER_SUBJECT) - - def SetSubject(self, value): - """Set the Subject value of this Vacation Responder object. - - Args: - value: string The new Subject value to give this object. - """ - - self._SetProperty(VACATION_RESPONDER_SUBJECT, value) - - subject = pyproperty(GetSubject, SetSubject) - - def GetMessage(self): - """Get the Message value of the Vacation Responder object. - - Returns: - The Message value of this Vacation Responder object as a string or None. - """ - - return self._GetProperty(VACATION_RESPONDER_MESSAGE) - - def SetMessage(self, value): - """Set the Message value of this Vacation Responder object. - - Args: - value: string The new Message value to give this object. - """ - - self._SetProperty(VACATION_RESPONDER_MESSAGE, value) - - message = pyproperty(GetMessage, SetMessage) - - def GetContactsOnly(self): - """Get the ContactsOnly value of the Vacation Responder object. - - Returns: - The ContactsOnly value of this Vacation Responder object as a - string or None. - """ - - return self._GetProperty(VACATION_RESPONDER_ENABLE) - - def SetContactsOnly(self, value): - """Set the ContactsOnly value of this Vacation Responder object. - - Args: - value: string The new ContactsOnly value to give this object. - """ - - self._SetProperty(VACATION_RESPONDER_CONTACTS_ONLY, value) - - contacts_only = pyproperty(GetContactsOnly, SetContactsOnly) - - def __init__(self, uri=None, enable=None, subject=None, - message=None, contacts_only=None, *args, **kwargs): - """Constructs a new EmailSettingsVacationResponder object with the - given arguments. - - Args: - uri: string (optional) The uri of of this object for HTTP requests. - enable: Boolean (optional) Whether to enable the vacation responder. - subject: string (optional) The subject line of the vacation responder - autoresponse. - message: string (optional) The message body of the vacation responder - autoresponse. - contacts_only: Boolean (optional) Whether to only send autoresponses - to known contacts. - args: The other parameters to pass to gdata.entry.GDEntry constructor. - kwargs: The other parameters to pass to gdata.entry.GDEntry constructor. - """ - super(EmailSettingsVacationResponder, self).__init__(*args, **kwargs) - if uri: - self.uri = uri - if enable is not None: - self.enable = str(enable) - if subject: - self.subject = subject - if message: - self.message = message - if contacts_only is not None: - self.contacts_only = str(contacts_only) - - -class EmailSettingsSignature(EmailSettingsEntry): - """Represents a Signature in object form.""" - - def GetValue(self): - """Get the value of the Signature object. - - Returns: - The value of this Signature object as a string or None. - """ - - value = self._GetProperty(SIGNATURE_VALUE) - if value == ' ': # hack to support empty signature - return '' - else: - return value - - def SetValue(self, value): - """Set the name of this Signature object. - - Args: - value: string The new signature value to give this object. - """ - - if value == '': # hack to support empty signature - value = ' ' - self._SetProperty(SIGNATURE_VALUE, value) - - signature_value = pyproperty(GetValue, SetValue) - - def __init__(self, uri=None, signature=None, *args, **kwargs): - """Constructs a new EmailSettingsSignature object with the given arguments. - - Args: - uri: string (optional) The uri of of this object for HTTP requests. - signature: string (optional) The signature to be appended to outgoing - messages. - args: The other parameters to pass to gdata.entry.GDEntry constructor. - kwargs: The other parameters to pass to gdata.entry.GDEntry constructor. - """ - super(EmailSettingsSignature, self).__init__(*args, **kwargs) - if uri: - self.uri = uri - if signature is not None: - self.signature_value = signature - - -class EmailSettingsLanguage(EmailSettingsEntry): - """Represents Language Settings in object form.""" - - def GetLanguage(self): - """Get the tag of the Language object. - - Returns: - The tag of this Language object as a string or None. - """ - - return self._GetProperty(LANGUAGE_TAG) - - def SetLanguage(self, value): - """Set the tag of this Language object. - - Args: - value: string The new tag value to give this object. - """ - - self._SetProperty(LANGUAGE_TAG, value) - - language_tag = pyproperty(GetLanguage, SetLanguage) - - def __init__(self, uri=None, language=None, *args, **kwargs): - """Constructs a new EmailSettingsLanguage object with the given arguments. - - Args: - uri: string (optional) The uri of of this object for HTTP requests. - language: string (optional) The language tag for Google Mail's display - language. - args: The other parameters to pass to gdata.entry.GDEntry constructor. - kwargs: The other parameters to pass to gdata.entry.GDEntry constructor. - """ - super(EmailSettingsLanguage, self).__init__(*args, **kwargs) - if uri: - self.uri = uri - if language: - self.language_tag = language - - -class EmailSettingsGeneral(EmailSettingsEntry): - """Represents General Settings in object form.""" - - def GetPageSize(self): - """Get the Page Size value of the General Settings object. - - Returns: - The Page Size value of this General Settings object as a string or None. - """ - - return self._GetProperty(GENERAL_PAGE_SIZE) - - def SetPageSize(self, value): - """Set the Page Size value of this General Settings object. - - Args: - value: string The new Page Size value to give this object. - """ - - self._SetProperty(GENERAL_PAGE_SIZE, value) - - page_size = pyproperty(GetPageSize, SetPageSize) - - def GetShortcuts(self): - """Get the Shortcuts value of the General Settings object. - - Returns: - The Shortcuts value of this General Settings object as a string or None. - """ - - return self._GetProperty(GENERAL_SHORTCUTS) - - def SetShortcuts(self, value): - """Set the Shortcuts value of this General Settings object. - - Args: - value: string The new Shortcuts value to give this object. - """ - - self._SetProperty(GENERAL_SHORTCUTS, value) - - shortcuts = pyproperty(GetShortcuts, SetShortcuts) - - def GetArrows(self): - """Get the Arrows value of the General Settings object. - - Returns: - The Arrows value of this General Settings object as a string or None. - """ - - return self._GetProperty(GENERAL_ARROWS) - - def SetArrows(self, value): - """Set the Arrows value of this General Settings object. - - Args: - value: string The new Arrows value to give this object. - """ - - self._SetProperty(GENERAL_ARROWS, value) - - arrows = pyproperty(GetArrows, SetArrows) - - def GetSnippets(self): - """Get the Snippets value of the General Settings object. - - Returns: - The Snippets value of this General Settings object as a string or None. - """ - - return self._GetProperty(GENERAL_SNIPPETS) - - def SetSnippets(self, value): - """Set the Snippets value of this General Settings object. - - Args: - value: string The new Snippets value to give this object. - """ - - self._SetProperty(GENERAL_SNIPPETS, value) - - snippets = pyproperty(GetSnippets, SetSnippets) - - def GetUnicode(self): - """Get the Unicode value of the General Settings object. - - Returns: - The Unicode value of this General Settings object as a string or None. - """ - - return self._GetProperty(GENERAL_UNICODE) - - def SetUnicode(self, value): - """Set the Unicode value of this General Settings object. - - Args: - value: string The new Unicode value to give this object. - """ - - self._SetProperty(GENERAL_UNICODE, value) - - use_unicode = pyproperty(GetUnicode, SetUnicode) - - def __init__(self, uri=None, page_size=None, shortcuts=None, - arrows=None, snippets=None, use_unicode=None, *args, **kwargs): - """Constructs a new EmailSettingsGeneral object with the given arguments. - - Args: - uri: string (optional) The uri of of this object for HTTP requests. - page_size: int (optional) The number of conversations to be shown per page. - shortcuts: Boolean (optional) Whether to enable keyboard shortcuts. - arrows: Boolean (optional) Whether to display arrow-shaped personal - indicators next to email sent specifically to the user. - snippets: Boolean (optional) Whether to display snippets of the messages - in the inbox and when searching. - use_unicode: Boolean (optional) Whether to use UTF-8 (unicode) encoding - for all outgoing messages. - args: The other parameters to pass to gdata.entry.GDEntry constructor. - kwargs: The other parameters to pass to gdata.entry.GDEntry constructor. - """ - super(EmailSettingsGeneral, self).__init__(*args, **kwargs) - if uri: - self.uri = uri - if page_size is not None: - self.page_size = str(page_size) - if shortcuts is not None: - self.shortcuts = str(shortcuts) - if arrows is not None: - self.arrows = str(arrows) - if snippets is not None: - self.snippets = str(snippets) - if use_unicode is not None: - self.use_unicode = str(use_unicode) diff --git a/gdata/apps/groups/__init__.py b/gdata/apps/groups/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/gdata/apps/groups/service.py b/gdata/apps/groups/service.py deleted file mode 100644 index 987a045e60..0000000000 --- a/gdata/apps/groups/service.py +++ /dev/null @@ -1,392 +0,0 @@ -#!/usr/bin/python -# -# Copyright (C) 2008 Google, 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. - -"""Allow Google Apps domain administrators to manage groups, group members and group owners. - - GroupsService: Provides methods to manage groups, members and owners. -""" - -__author__ = 'google-apps-apis@googlegroups.com' - - -import urllib -import gdata.apps -import gdata.apps.service -import gdata.service - - -API_VER = '2.0' -BASE_URL = '/a/feeds/group/' + API_VER + '/%s' -GROUP_MEMBER_URL = BASE_URL + '?member=%s' -GROUP_MEMBER_DIRECT_URL = GROUP_MEMBER_URL + '&directOnly=%s' -GROUP_ID_URL = BASE_URL + '/%s' -MEMBER_URL = BASE_URL + '/%s/member' -MEMBER_WITH_SUSPENDED_URL = MEMBER_URL + '?includeSuspendedUsers=%s' -MEMBER_ID_URL = MEMBER_URL + '/%s' -OWNER_URL = BASE_URL + '/%s/owner' -OWNER_WITH_SUSPENDED_URL = OWNER_URL + '?includeSuspendedUsers=%s' -OWNER_ID_URL = OWNER_URL + '/%s' - -PERMISSION_OWNER = 'Owner' -PERMISSION_MEMBER = 'Member' -PERMISSION_DOMAIN = 'Domain' -PERMISSION_ANYONE = 'Anyone' - - -class GroupsService(gdata.apps.service.PropertyService): - """Client for the Google Apps Groups service.""" - - def _ServiceUrl(self, service_type, is_existed, group_id, member_id, owner_email, - direct_only=False, domain=None, suspended_users=False): - if domain is None: - domain = self.domain - - if service_type == 'group': - if group_id != '' and is_existed: - return GROUP_ID_URL % (domain, group_id) - elif member_id != '': - #if direct_only: - return GROUP_MEMBER_DIRECT_URL % (domain, urllib.quote_plus(member_id), - self._Bool2Str(direct_only)) - #else: - # return GROUP_MEMBER_URL % (domain, urllib.quote_plus(member_id)) - else: - return BASE_URL % (domain) - - if service_type == 'member': - if member_id != '' and is_existed: - return MEMBER_ID_URL % (domain, group_id, urllib.quote_plus(member_id)) - elif suspended_users: - return MEMBER_WITH_SUSPENDED_URL % (domain, group_id, - self._Bool2Str(suspended_users)) - else: - return MEMBER_URL % (domain, group_id) - - if service_type == 'owner': - if owner_email != '' and is_existed: - return OWNER_ID_URL % (domain, group_id, urllib.quote_plus(owner_email)) - elif suspended_users: - return OWNER_WITH_SUSPENDED_URL % (domain, group_id, - self._Bool2Str(suspended_users)) - else: - return OWNER_URL % (domain, group_id) - - def _Bool2Str(self, b): - if b is None: - return None - return str(b is True).lower() - - def _IsExisted(self, uri): - try: - self._GetProperties(uri) - return True - except gdata.apps.service.AppsForYourDomainException, e: - if e.error_code == gdata.apps.service.ENTITY_DOES_NOT_EXIST: - return False - else: - raise e - - def CreateGroup(self, group_id, group_name, description, email_permission): - """Create a group. - - Args: - group_id: The ID of the group (e.g. us-sales). - group_name: The name of the group. - description: A description of the group - email_permission: The subscription permission of the group. - - Returns: - A dict containing the result of the create operation. - """ - uri = self._ServiceUrl('group', False, group_id, '', '') - properties = {} - properties['groupId'] = group_id - properties['groupName'] = group_name - properties['description'] = description - properties['emailPermission'] = email_permission - return self._PostProperties(uri, properties) - - def UpdateGroup(self, group_id, group_name, description, email_permission): - """Update a group's name, description and/or permission. - - Args: - group_id: The ID of the group (e.g. us-sales). - group_name: The name of the group. - description: A description of the group - email_permission: The subscription permission of the group. - - Returns: - A dict containing the result of the update operation. - """ - uri = self._ServiceUrl('group', True, group_id, '', '') - properties = {} - properties['groupId'] = group_id - properties['groupName'] = group_name - properties['description'] = description - properties['emailPermission'] = email_permission - return self._PutProperties(uri, properties) - - def RetrieveGroup(self, group_id): - """Retrieve a group based on its ID. - - Args: - group_id: The ID of the group (e.g. us-sales). - - Returns: - A dict containing the result of the retrieve operation. - """ - uri = self._ServiceUrl('group', True, group_id, '', '') - return self._GetProperties(uri) - - def RetrieveAllGroups(self, noUserManagedGroups=False): - """Retrieve all groups in the domain. - - Args: - None - - Returns: - A list containing the result of the retrieve operation. - """ - uri = self._ServiceUrl('group', True, '', '', '') - if noUserManagedGroups: - uri = uri + '?skipUserCreatedGroups=True' - return self._GetPropertiesList(uri) - - def RetrievePageOfGroups(self, start_group=None): - """Retrieve one page of groups in the domain. - - Args: - start_group: The key to continue for pagination through all groups. - - Returns: - A feed object containing the result of the retrieve operation. - """ - uri = self._ServiceUrl('group', True, '', '', '') - if start_group is not None: - uri += "?start="+start_group - property_feed = self._GetPropertyFeed(uri) - return property_feed - - def RetrieveGroups(self, member_id, direct_only=False): - """Retrieve all groups that belong to the given member_id. - - Args: - member_id: The member's email address (e.g. member@example.com). - direct_only: Boolean whether only return groups that this member directly belongs to. - - Returns: - A list containing the result of the retrieve operation. - """ - uri = self._ServiceUrl('group', True, '', member_id, '', direct_only=direct_only) - return self._GetPropertiesList(uri) - - def DeleteGroup(self, group_id): - """Delete a group based on its ID. - - Args: - group_id: The ID of the group (e.g. us-sales). - - Returns: - A dict containing the result of the delete operation. - """ - uri = self._ServiceUrl('group', True, group_id, '', '') - return self._DeleteProperties(uri) - - def AddMemberToGroup(self, member_id, group_id): - """Add a member to a group. - - Args: - member_id: The member's email address (e.g. member@example.com). - group_id: The ID of the group (e.g. us-sales). - - Returns: - A dict containing the result of the add operation. - """ - uri = self._ServiceUrl('member', False, group_id, member_id, '') - properties = {} - properties['memberId'] = member_id - properties['membershipType'] = 'manager' - properties['membership_type'] = 'manager' - properties['delivery'] = '0' - return self._PostProperties(uri, properties) - - def IsMember(self, member_id, group_id): - """Check whether the given member already exists in the given group. - - Args: - member_id: The member's email address (e.g. member@example.com). - group_id: The ID of the group (e.g. us-sales). - - Returns: - True if the member exists in the group. False otherwise. - """ - uri = self._ServiceUrl('member', True, group_id, member_id, '') - return self._IsExisted(uri) - - def RetrieveMember(self, member_id, group_id): - """Retrieve the given member in the given group. - - Args: - member_id: The member's email address (e.g. member@example.com). - group_id: The ID of the group (e.g. us-sales). - - Returns: - A dict containing the result of the retrieve operation. - """ - uri = self._ServiceUrl('member', True, group_id, member_id, '') - return self._GetProperties(uri) - - def RetrieveAllMembers(self, group_id, suspended_users=False): - """Retrieve all members in the given group. - - Args: - group_id: The ID of the group (e.g. us-sales). - suspended_users: A boolean; should we include any suspended users in - the membership list returned? - - Returns: - A list containing the result of the retrieve operation. - """ - uri = self._ServiceUrl('member', True, group_id, '', '', - suspended_users=suspended_users) - return self._GetPropertiesList(uri) - - def RetrievePageOfMembers(self, group_id, suspended_users=False, start=None): - """Retrieve one page of members of a given group. - - Args: - group_id: The ID of the group (e.g. us-sales). - suspended_users: A boolean; should we include any suspended users in - the membership list returned? - start: The key to continue for pagination through all members. - - Returns: - A feed object containing the result of the retrieve operation. - """ - - uri = self._ServiceUrl('member', True, group_id, '', '', - suspended_users=suspended_users) - if start is not None: - if suspended_users: - uri += "&start="+start - else: - uri += "?start="+start - property_feed = self._GetPropertyFeed(uri) - return property_feed - - def RemoveMemberFromGroup(self, member_id, group_id): - """Remove the given member from the given group. - - Args: - member_id: The member's email address (e.g. member@example.com). - group_id: The ID of the group (e.g. us-sales). - - Returns: - A dict containing the result of the remove operation. - """ - uri = self._ServiceUrl('member', True, group_id, member_id, '') - return self._DeleteProperties(uri) - - def AddOwnerToGroup(self, owner_email, group_id): - """Add an owner to a group. - - Args: - owner_email: The email address of a group owner. - group_id: The ID of the group (e.g. us-sales). - - Returns: - A dict containing the result of the add operation. - """ - uri = self._ServiceUrl('owner', False, group_id, '', owner_email) - properties = {} - properties['email'] = owner_email - return self._PostProperties(uri, properties) - - def IsOwner(self, owner_email, group_id): - """Check whether the given member an owner of the given group. - - Args: - owner_email: The email address of a group owner. - group_id: The ID of the group (e.g. us-sales). - - Returns: - True if the member is an owner of the given group. False otherwise. - """ - uri = self._ServiceUrl('owner', True, group_id, '', owner_email) - return self._IsExisted(uri) - - def RetrieveOwner(self, owner_email, group_id): - """Retrieve the given owner in the given group. - - Args: - owner_email: The email address of a group owner. - group_id: The ID of the group (e.g. us-sales). - - Returns: - A dict containing the result of the retrieve operation. - """ - uri = self._ServiceUrl('owner', True, group_id, '', owner_email) - return self._GetProperties(uri) - - def RetrieveAllOwners(self, group_id, suspended_users=False): - """Retrieve all owners of the given group. - - Args: - group_id: The ID of the group (e.g. us-sales). - suspended_users: A boolean; should we include any suspended users in - the ownership list returned? - - Returns: - A list containing the result of the retrieve operation. - """ - uri = self._ServiceUrl('owner', True, group_id, '', '', - suspended_users=suspended_users) - return self._GetPropertiesList(uri) - - def RetrievePageOfOwners(self, group_id, suspended_users=False, start=None): - """Retrieve one page of owners of the given group. - - Args: - group_id: The ID of the group (e.g. us-sales). - suspended_users: A boolean; should we include any suspended users in - the ownership list returned? - start: The key to continue for pagination through all owners. - - Returns: - A feed object containing the result of the retrieve operation. - """ - uri = self._ServiceUrl('owner', True, group_id, '', '', - suspended_users=suspended_users) - if start is not None: - if suspended_users: - uri += "&start="+start - else: - uri += "?start="+start - property_feed = self._GetPropertyFeed(uri) - return property_feed - - def RemoveOwnerFromGroup(self, owner_email, group_id): - """Remove the given owner from the given group. - - Args: - owner_email: The email address of a group owner. - group_id: The ID of the group (e.g. us-sales). - - Returns: - A dict containing the result of the remove operation. - """ - uri = self._ServiceUrl('owner', True, group_id, '', owner_email) - return self._DeleteProperties(uri) diff --git a/gdata/apps/groupsettings/__init__.py b/gdata/apps/groupsettings/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/gdata/apps/groupsettings/service.py b/gdata/apps/groupsettings/service.py deleted file mode 100644 index 6890d944ab..0000000000 --- a/gdata/apps/groupsettings/service.py +++ /dev/null @@ -1,172 +0,0 @@ -#!/usr/bin/python2.4 -# -# 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. - -"""GroupSettingsService simplifies Group Settings API calls. - -GroupSettingsService extends gdata.apps.service.PropertyService to ease interaction with -the Google Apps Group Settings API. -""" - -__author__ = 'Jay Lee ' - -import gdata.apps -import gdata.apps.service -import gdata.service - -# Group Settings URI template -GROUP_SETTINGS_URI_TEMPLATE = '/groups/v1/groups/%s?alt=atom' - -class GroupSettingsService(gdata.apps.service.PropertyService): - """Service extension for the Google Group Settings API service.""" - - def __init__(self, email=None, password=None, domain=None, source=None, - server='www.googleapis.com', additional_headers=None, - **kwargs): - """Creates a client for the Group Settings service. - - Args: - email: string (optional) The user's email address, used for - authentication. - password: string (optional) The user's password. - domain: string (optional) The Google Apps domain name. - 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: 'apps-apis.google.com'. - **kwargs: The other parameters to pass to gdata.service.GDataService - constructor. - """ - gdata.service.GDataService.__init__( - self, email=email, password=password, service='apps', source=source, - server=server, additional_headers=additional_headers, **kwargs) - self.ssl = True - self.port = 443 - self.domain = domain - - def make_group_settings_uri(self, group_email): - """Creates the URI for the Group Settings API call. - - Create the URI to access the group settings API. If params are provided, - append them as GET params. - - Args: - group: email address of the group - - Returns: - A string giving the URI for Group Settings API calls - - """ - uri = GROUP_SETTINGS_URI_TEMPLATE % (group_email) - return uri - - MakeGroupSettingsUri = make_group_settings_uri - - def retrieve_group_settings(self, group_email): - """Retrieves group settings - - Args: - group_email: string, the group email address - - Returns: - A dict. The group settings - """ - uri = self.MakeGroupSettingsUri(group_email) - group_settings_entry = self.Get(uri) - group_settings_values = [] - for group_settings_value in group_settings_entry.extension_elements: - group_settings_values.append({group_settings_value.tag: group_settings_value.text}) - return group_settings_values - - RetrieveGroupSettings = retrieve_group_settings - - def update_group_settings(self, group_email, allow_external_members=None, - allow_google_communication=None, allow_web_posting=None, archive_only=None, custom_reply_to=None, - default_message_deny_notification_text=None, description=None, is_archived=None, max_message_bytes=None, - members_can_post_as_the_group=None, message_display_font=None, message_moderation_level=None, name=None, - primary_language=None, reply_to=None, send_message_deny_notification=None, show_in_group_directory=None, - who_can_invite=None, who_can_join=None, who_can_post_message=None, who_can_view_group=None, - who_can_view_membership=None, include_in_global_address_list=None, spam_moderation_level=None): - - uri = self.MakeGroupSettingsUri(group_email) - - xml = ''' - - tag:googleapis.com,2010:apps:groupssettings:GROUP:NNN - Groups Resource Entry - - Google - - %s - %s - -''' % (group_email, group_email) - - template = "%s\n" - if name != None: - xml += template % ('name', name, 'name') - if allow_external_members != None: - xml += template % ('allowExternalMembers', allow_external_members, 'allowExternalMembers') - if allow_google_communication != None: - xml += template % ('allowGoogleCommunication', allow_google_communication, 'allowGoogleCommunication') - if allow_web_posting != None: - xml += template % ('allowWebPosting', allow_web_posting, 'allowWebPosting') - if archive_only != None: - xml += template % ('archiveOnly', archive_only, 'archiveOnly') - if custom_reply_to != None: - xml += template % ('customReplyTo', custom_reply_to, 'customReplyTo') - if default_message_deny_notification_text != None: - xml += template % ('defaultMessageDenyNotificationText', default_message_deny_notification_text, 'defaultMessageDenyNotificationText') - if description != None: - xml += template % ('description', description, 'description') - if is_archived != None: - xml += template % ('isArchived', is_archived, 'isArchived') - if max_message_bytes != None: - xml += template % ('maxMessageBytes', max_message_bytes, 'maxMessageBytes') - if members_can_post_as_the_group != None: - xml += template % ('membersCanPostAsTheGroup', members_can_post_as_the_group, 'membersCanPostAsTheGroup') - if message_display_font != None: - xml += template % ('messageDisplayFont', message_display_font, 'messageDisplayFont') - if message_moderation_level != None: - xml += template % ('messageModerationLevel', message_moderation_level, 'messageModerationLevel') - if primary_language != None: - xml += template % ('primaryLanguage', primary_language, 'primaryLanguage') - if reply_to != None: - xml += template % ('replyTo', reply_to, 'replyTo') - if send_message_deny_notification != None: - xml += template % ('sendMessageDenyNotification', send_message_deny_notification, 'sendMessageDenyNotification') - if show_in_group_directory != None: - xml += template % ('showInGroupDirectory', show_in_group_directory, 'showInGroupDirectory') - if who_can_invite != None: - xml += template % ('whoCanInvite', who_can_invite, 'whoCanInvite') - if who_can_join != None: - xml += template % ('whoCanJoin', who_can_join, 'whoCanJoin') - if who_can_post_message != None: - xml += template % ('whoCanPostMessage', who_can_post_message, 'whoCanPostMessage') - if who_can_view_group != None: - xml += template % ('whoCanViewGroup', who_can_view_group, 'whoCanViewGroup') - if who_can_view_membership != None: - xml += template % ('whoCanViewMembership', who_can_view_membership, 'whoCanViewMembership') - if include_in_global_address_list != None: - xml += template % ('includeInGlobalAddressList', include_in_global_address_list, 'includeInGlobalAddressList') - if spam_moderation_level != None: - xml += template % ('spamModerationLevel', spam_moderation_level, 'spamModerationLevel') - xml += '' - group_settings_entry = self.Put(uri=uri, data=xml) - group_settings_values = [] - for group_settings_value in group_settings_entry.extension_elements: - group_settings_values.append({group_settings_value.tag: group_settings_value.text}) - return group_settings_values - - UpdateGroupSettings = update_group_settings diff --git a/gdata/apps/migration/__init__.py b/gdata/apps/migration/__init__.py deleted file mode 100644 index 9892671336..0000000000 --- a/gdata/apps/migration/__init__.py +++ /dev/null @@ -1,212 +0,0 @@ -#!/usr/bin/python -# -# Copyright (C) 2008 Google -# -# 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. - -"""Contains objects used with Google Apps.""" - -__author__ = 'google-apps-apis@googlegroups.com' - - -import atom -import gdata - - -# XML namespaces which are often used in Google Apps entity. -APPS_NAMESPACE = 'http://schemas.google.com/apps/2006' -APPS_TEMPLATE = '{http://schemas.google.com/apps/2006}%s' - - -class Rfc822Msg(atom.AtomBase): - """The Migration rfc822Msg element.""" - - _tag = 'rfc822Msg' - _namespace = APPS_NAMESPACE - _children = atom.AtomBase._children.copy() - _attributes = atom.AtomBase._attributes.copy() - _attributes['encoding'] = 'encoding' - - def __init__(self, extension_elements=None, - extension_attributes=None, text=None): - self.text = text - self.encoding = 'base64' - self.extension_elements = extension_elements or [] - self.extension_attributes = extension_attributes or {} - - -def Rfc822MsgFromString(xml_string): - """Parse in the Rrc822 message from the XML definition.""" - - return atom.CreateClassFromXMLString(Rfc822Msg, xml_string) - - -class MailItemProperty(atom.AtomBase): - """The Migration mailItemProperty element.""" - - _tag = 'mailItemProperty' - _namespace = APPS_NAMESPACE - _children = atom.AtomBase._children.copy() - _attributes = atom.AtomBase._attributes.copy() - _attributes['value'] = 'value' - - def __init__(self, value=None, extension_elements=None, - extension_attributes=None, text=None): - self.value = value - self.text = text - self.extension_elements = extension_elements or [] - self.extension_attributes = extension_attributes or {} - - -def MailItemPropertyFromString(xml_string): - """Parse in the MailItemProperiy from the XML definition.""" - - return atom.CreateClassFromXMLString(MailItemProperty, xml_string) - - -class Label(atom.AtomBase): - """The Migration label element.""" - - _tag = 'label' - _namespace = APPS_NAMESPACE - _children = atom.AtomBase._children.copy() - _attributes = atom.AtomBase._attributes.copy() - _attributes['labelName'] = 'label_name' - - def __init__(self, label_name=None, - extension_elements=None, extension_attributes=None, - text=None): - self.label_name = label_name - self.text = text - self.extension_elements = extension_elements or [] - self.extension_attributes = extension_attributes or {} - - -def LabelFromString(xml_string): - """Parse in the mailItemProperty from the XML definition.""" - - return atom.CreateClassFromXMLString(Label, xml_string) - - -class MailEntry(gdata.GDataEntry): - """A Google Migration flavor of an Atom Entry.""" - - _tag = 'entry' - _namespace = atom.ATOM_NAMESPACE - _children = gdata.GDataEntry._children.copy() - _attributes = gdata.GDataEntry._attributes.copy() - _children['{%s}rfc822Msg' % APPS_NAMESPACE] = ('rfc822_msg', Rfc822Msg) - _children['{%s}mailItemProperty' % APPS_NAMESPACE] = ('mail_item_property', - [MailItemProperty]) - _children['{%s}label' % APPS_NAMESPACE] = ('label', [Label]) - - def __init__(self, author=None, category=None, content=None, - atom_id=None, link=None, published=None, - title=None, updated=None, - rfc822_msg=None, mail_item_property=None, label=None, - extended_property=None, - extension_elements=None, extension_attributes=None, text=None): - - gdata.GDataEntry.__init__(self, author=author, category=category, - content=content, - atom_id=atom_id, link=link, published=published, - title=title, updated=updated) - self.rfc822_msg = rfc822_msg - self.mail_item_property = mail_item_property - self.label = label - self.extended_property = extended_property or [] - self.text = text - self.extension_elements = extension_elements or [] - self.extension_attributes = extension_attributes or {} - - -def MailEntryFromString(xml_string): - """Parse in the MailEntry from the XML definition.""" - - return atom.CreateClassFromXMLString(MailEntry, xml_string) - - -class BatchMailEntry(gdata.BatchEntry): - """A Google Migration flavor of an Atom Entry.""" - - _tag = gdata.BatchEntry._tag - _namespace = gdata.BatchEntry._namespace - _children = gdata.BatchEntry._children.copy() - _attributes = gdata.BatchEntry._attributes.copy() - _children['{%s}rfc822Msg' % APPS_NAMESPACE] = ('rfc822_msg', Rfc822Msg) - _children['{%s}mailItemProperty' % APPS_NAMESPACE] = ('mail_item_property', - [MailItemProperty]) - _children['{%s}label' % APPS_NAMESPACE] = ('label', [Label]) - - def __init__(self, author=None, category=None, content=None, - atom_id=None, link=None, published=None, - title=None, updated=None, - rfc822_msg=None, mail_item_property=None, label=None, - batch_operation=None, batch_id=None, batch_status=None, - extended_property=None, - extension_elements=None, extension_attributes=None, text=None): - - gdata.BatchEntry.__init__(self, author=author, category=category, - content=content, - atom_id=atom_id, link=link, published=published, - batch_operation=batch_operation, - batch_id=batch_id, batch_status=batch_status, - title=title, updated=updated) - self.rfc822_msg = rfc822_msg or None - self.mail_item_property = mail_item_property or [] - self.label = label or [] - self.extended_property = extended_property or [] - self.text = text - self.extension_elements = extension_elements or [] - self.extension_attributes = extension_attributes or {} - - -def BatchMailEntryFromString(xml_string): - """Parse in the BatchMailEntry from the XML definition.""" - - return atom.CreateClassFromXMLString(BatchMailEntry, xml_string) - - -class BatchMailEventFeed(gdata.BatchFeed): - """A Migration event feed flavor of an Atom Feed.""" - - _tag = gdata.BatchFeed._tag - _namespace = gdata.BatchFeed._namespace - _children = gdata.BatchFeed._children.copy() - _attributes = gdata.BatchFeed._attributes.copy() - _children['{%s}entry' % atom.ATOM_NAMESPACE] = ('entry', [BatchMailEntry]) - - def __init__(self, author=None, category=None, contributor=None, - generator=None, icon=None, atom_id=None, link=None, logo=None, - rights=None, subtitle=None, title=None, updated=None, - entry=None, total_results=None, start_index=None, - items_per_page=None, interrupted=None, extension_elements=None, - extension_attributes=None, text=None): - gdata.BatchFeed.__init__(self, author=author, category=category, - contributor=contributor, generator=generator, - icon=icon, atom_id=atom_id, link=link, - logo=logo, rights=rights, subtitle=subtitle, - title=title, updated=updated, entry=entry, - total_results=total_results, - start_index=start_index, - items_per_page=items_per_page, - interrupted=interrupted, - extension_elements=extension_elements, - extension_attributes=extension_attributes, - text=text) - - -def BatchMailEventFeedFromString(xml_string): - """Parse in the BatchMailEventFeed from the XML definition.""" - - return atom.CreateClassFromXMLString(BatchMailEventFeed, xml_string) diff --git a/gdata/apps/migration/service.py b/gdata/apps/migration/service.py deleted file mode 100644 index 631999577d..0000000000 --- a/gdata/apps/migration/service.py +++ /dev/null @@ -1,129 +0,0 @@ -#!/usr/bin/python -# -# Copyright (C) 2008 Google. -# -# 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. - -"""Contains the methods to import mail via Google Apps Email Migration API. - - MigrationService: Provides methids to import mail. -""" - -__author__ = 'google-apps-apis@googlegroups.com' - - -import base64 -import gdata -import gdata.apps.service -import gdata.service -from gdata.apps import migration - - -API_VER = '2.0' - - -class MigrationService(gdata.apps.service.AppsService): - """Client for the EMAPI migration service. Use either ImportMail to import - one message at a time, or AddBatchEntry and SubmitBatch to import a batch of - messages at a time. - """ - def __init__(self, email=None, password=None, domain=None, source=None, - server='apps-apis.google.com', additional_headers=None): - gdata.apps.service.AppsService.__init__( - self, email=email, password=password, domain=domain, source=source, - server=server, additional_headers=additional_headers) - self.mail_batch = migration.BatchMailEventFeed() - - def _BaseURL(self): - return '/a/feeds/migration/%s/%s' % (API_VER, self.domain) - - def ImportMail(self, user_name, mail_message, mail_item_properties, - mail_labels): - """Import a single mail message. - - Args: - user_name: The username to import messages to. - mail_message: An RFC822 format email message. - mail_item_properties: A list of Gmail properties to apply to the message. - mail_labels: A list of labels to apply to the message. - - Returns: - A MailEntry representing the successfully imported message. - - Raises: - AppsForYourDomainException: An error occurred importing the message. - """ - uri = '%s/%s/mail' % (self._BaseURL(), user_name) - - mail_entry = migration.MailEntry() - mail_entry.rfc822_msg = migration.Rfc822Msg(text=(base64.b64encode( - mail_message))) - mail_entry.rfc822_msg.encoding = 'base64' - mail_entry.mail_item_property = map( - lambda x: migration.MailItemProperty(value=x), mail_item_properties) - mail_entry.label = map(lambda x: migration.Label(label_name=x), - mail_labels) - - try: - return migration.MailEntryFromString(str(self.Post(mail_entry, uri))) - except gdata.service.RequestError, e: - raise gdata.apps.service.AppsForYourDomainException(e.args[0]) - - def AddBatchEntry(self, mail_message, mail_item_properties, - mail_labels): - """Add a message to the current batch that you later will submit. - - Args: - mail_message: An RFC822 format email message. - mail_item_properties: A list of Gmail properties to apply to the message. - mail_labels: A list of labels to apply to the message. - - Returns: - The length of the MailEntry representing the message. - """ - mail_entry = migration.BatchMailEntry() - mail_entry.rfc822_msg = migration.Rfc822Msg(text=(base64.b64encode( - mail_message))) - mail_entry.rfc822_msg.encoding = 'base64' - mail_entry.mail_item_property = map( - lambda x: migration.MailItemProperty(value=x), mail_item_properties) - mail_entry.label = map(lambda x: migration.Label(label_name=x), - mail_labels) - - self.mail_batch.AddBatchEntry(mail_entry) - - return len(str(mail_entry)) - - def SubmitBatch(self, user_name): - """Send a all the mail items you have added to the batch to the server. - - Args: - user_name: The username to import messages to. - - Returns: - A HTTPResponse from the web service call. - - Raises: - AppsForYourDomainException: An error occurred importing the batch. - """ - uri = '%s/%s/mail/batch' % (self._BaseURL(), user_name) - - try: - self.result = self.Post(self.mail_batch, uri, - converter=migration.BatchMailEventFeedFromString) - except gdata.service.RequestError, e: - raise gdata.apps.service.AppsForYourDomainException(e.args[0]) - - self.mail_batch = migration.BatchMailEventFeed() - - return self.result diff --git a/gdata/apps/multidomain/__init__.py b/gdata/apps/multidomain/__init__.py deleted file mode 100644 index d284c7cee3..0000000000 --- a/gdata/apps/multidomain/__init__.py +++ /dev/null @@ -1,16 +0,0 @@ -#!/usr/bin/python -# -# Copyright (C) 2008 Google -# -# 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. - diff --git a/gdata/apps/multidomain/service.py b/gdata/apps/multidomain/service.py deleted file mode 100644 index 158aff49a1..0000000000 --- a/gdata/apps/multidomain/service.py +++ /dev/null @@ -1,155 +0,0 @@ -#!/usr/bin/python -# -# Copyright (C) 2008 Google, 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. - -"""Extended Multi Domain Support. - - MultiDomainService: Multi Domain Support.""" - -__author__ = 'jay@ditoweb.com' - - -import gdata.apps -import gdata.apps.service -import gdata.service - -import urllib - -API_VER='2.0' - -class MultiDomainService(gdata.apps.service.PropertyService): - """Extended functions for Google Apps Multi-Domain Support.""" - - def _serviceUrl(self, setting_id, email=None, params=None): - uri = '/a/feeds/%s/%s/%s' % (setting_id, API_VER, self.domain) - if email: - uri += '/' + email - if params: - uri += '?' + urllib.urlencode(params) - return uri - - - def CreateUser(self, user_email, password, first_name, last_name, is_admin=None, hash_function=None, change_password=None, agreed_to_terms=None, - suspended=None, ip_whitelisted=None, quota_in_gb=None): - if user_email.find('@') == -1: - user_email = '%s@%s' % (user_email, self.domain) - uri = self._serviceUrl(setting_id='user') - properties = {} - properties['userEmail'] = user_email - properties['password'] = password - properties['firstName'] = first_name - properties['lastName'] = last_name - if is_admin != None: - properties['isAdmin'] = gdata.apps.service._bool2str(is_admin) - if hash_function != None: - properties['hashFunction'] = hash_function - if change_password != None: - properties['isChangePasswordAtNextLogin'] = gdata.apps.service._bool2str(change_password) - if agreed_to_terms != None: - properties['agreedToTerms'] = gdata.apps.service._bool2str(agreed_to_terms) - if suspended != None: - properties['isSuspended'] = gdata.apps.service._bool2str(suspended) - if ip_whitelisted != None: - properties['ipWhitelisted'] = gdata.apps.service._bool2str(ip_whitelisted) - if quota_in_gb != None: - properties['quotaInGb'] = quota_in_gb - return self._PostProperties(uri, properties) - - def UpdateUser(self, user_email, password=None, first_name=None, last_name=None, is_admin=None, hash_function=None, change_password=None, agreed_to_terms=None, - suspended=None, ip_whitelisted=None, quota_in_gb=None): - if user_email.find('@') == -1: - user_email = '%s@%s' % (user_email, self.domain) - uri = self._serviceUrl(setting_id='user', email=user_email) - properties = {} - if password != None: - properties['password'] = password - if first_name != None: - properties['firstName'] = first_name - if last_name != None: - properties['lastName'] = last_name - if is_admin != None: - properties['isAdmin'] = gdata.apps.service._bool2str(is_admin) - if hash_function != None: - properties['hashFunction'] = hash_function - if change_password != None: - properties['isChangePasswordAtNextLogin'] = gdata.apps.service._bool2str(change_password) - if agreed_to_terms != None: - properties['agreedToTerms'] = gdata.apps.service._bool2str(agreed_to_terms) - if suspended != None: - properties['isSuspended'] = gdata.apps.service._bool2str(suspended) - if ip_whitelisted != None: - properties['ipWhitelisted'] = gdata.apps.service._bool2str(ip_whitelisted) - if quota_in_gb != None: - properties['quotaInGb'] = str(quota_in_gb) - return self._PutProperties(uri, properties) - - def RetrieveUser(self, user_email): - if user_email.find('@') == -1: - user_email = '%s@%s' % (user_email, self.domain) - uri = self._serviceUrl(setting_id='user', email=user_email) - return self._GetProperties(uri) - - def RetrieveAllUsers(self): - uri = self._serviceUrl(setting_id='user') - return self._GetPropertiesList(uri) - - def DeleteUser(self, user_email): - if user_email.find('@') == -1: - user_email = '%s@%s' % (user_email, self.domain) - uri = self._serviceUrl(setting_id='user', email=user_email) - return self._DeleteProperties(uri) - - def RenameUser(self, old_email, new_email): - if old_email.find('@') == -1: - old_email = '%s@%s' % (old_email, self.domain) - if new_email.find('@') == -1: - new_email = '%s@%s' % (new_email, self.domain) - uri = self._serviceUrl(setting_id='user/userEmail', email=old_email) - properties = {} - properties['newEmail'] = new_email - return self._PutProperties(uri, properties) - - def CreateAlias(self, user_email, alias_email): - if alias_email.find('@') == -1: - alias_email = '%s@%s' % (alias_email, self.domain) - if user_email.find('@') == -1: - user_email = '%s@%s' % (user_email, self.domain) - uri = self._serviceUrl(setting_id='alias') - properties = {} - properties['userEmail'] = user_email - properties['aliasEmail'] = alias_email - return self._PostProperties(uri, properties) - - def RetrieveAlias(self, alias_email): - if alias_email.find('@') == -1: - alias_email = '%s@%s' % (alias_email, self.domain) - uri = self._serviceUrl(setting_id='alias', email=alias_email) - return self._GetProperties(uri) - - def RetrieveAllAliases(self): - uri = self._serviceUrl(setting_id='alias') - return self._GetPropertiesList(uri) - - def DeleteAlias(self, alias_email): - if alias_email.find('@') == -1: - alias_email = '%s@%s' % (alias_email, self.domain) - uri = self._serviceUrl(setting_id='alias', email=alias_email) - return self._DeleteProperties(uri) - - def GetUserAliases(self, user_email): - if user_email.find('@') == -1: - user_email = '%s@%s' % (user_email, self.domain) - uri = self._serviceUrl(setting_id='alias', params={'userEmail' : user_email}) - return self._GetPropertiesList(uri) diff --git a/gdata/apps/orgs/__init__.py b/gdata/apps/orgs/__init__.py deleted file mode 100644 index d284c7cee3..0000000000 --- a/gdata/apps/orgs/__init__.py +++ /dev/null @@ -1,16 +0,0 @@ -#!/usr/bin/python -# -# Copyright (C) 2008 Google -# -# 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. - diff --git a/gdata/apps/orgs/service.py b/gdata/apps/orgs/service.py deleted file mode 100644 index b9002a0834..0000000000 --- a/gdata/apps/orgs/service.py +++ /dev/null @@ -1,170 +0,0 @@ -#!/usr/bin/python -# -# Copyright (C) 2008 Google, 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. - -"""Organization Support. - - OrganizationService: Organization Support.""" - -__author__ = 'jlee@pbu.edu' - - -import urllib -import gdata.apps -import gdata.apps.service -import gdata.service - - -API_VER='2.0' - -class OrganizationService(gdata.apps.service.PropertyService): - """Extended functions for Google Apps Organization Support.""" - - def _serviceUrl(self, setting_id, domain=None): - if domain is None: - domain = self.domain - return '/a/feeds/%s/%s/%s' % (setting_id, API_VER, domain) - - def RetrieveCustomerId(self): - - uri = '/a/feeds/customer/2.0/customerId' - return self._GetProperties(uri) - - def CreateOrganizationUnit(self, name, description, parent_org_unit_path='/', block_inheritance=False): - - customer_id = self.RetrieveCustomerId()['customerId'] - uri = '/a/feeds/orgunit/2.0/%s' % customer_id - properties = {} - properties['name'] = name - properties['description'] = description - properties['parentOrgUnitPath'] = urllib.quote_plus(parent_org_unit_path, safe='/') - properties['blockInheritance'] = gdata.apps.service._bool2str(block_inheritance) - return self._PostProperties(uri, properties) - - def UpdateOrganizationUnit(self, old_name, new_name=None, description=None, parent_org_unit_path=None, block_inheritance=None, users_to_move=None): - - customer_id = self.RetrieveCustomerId()['customerId'] - old_name = urllib.quote_plus(old_name, safe='/') - uri = '/a/feeds/orgunit/2.0/%s/%s' % (customer_id, old_name) - properties = {} - if new_name != None: - properties['name'] = new_name - if description != None: - properties['description'] = description - if parent_org_unit_path != None: - properties['parentOrgUnitPath'] = urllib.quote_plus(parent_org_unit_path, safe='/') - if block_inheritance != None: - properties['blockInheritance'] = gdata.apps.service._bool2str(block_inheritance) - if users_to_move != None: - properties['usersToMove'] = '' - for user in users_to_move: - if user.find('@') < 0: - user = user+'@'+self.domain - properties['usersToMove'] += user+', ' - return self._PutProperties(uri, properties) - - def UpdateUserOrganization(self, user, new_name, old_name=None, customer_id=None): - - if customer_id == None: - customer_id = self.RetrieveCustomerId()['customerId'] - uri = '/a/feeds/orguser/2.0/%s/%s' % (customer_id, urllib.quote_plus(user)) - properties = {} - properties['orgUnitPath'] = new_name - if old_name != None: - properties['oldOrgUnitPath'] = old_name - return self._PutProperties(uri, properties) - - def RetrieveOrganizationUnit(self, name): - - customer_id = self.RetrieveCustomerId()['customerId'] - name = urllib.quote_plus(name, safe='/') - uri = '/a/feeds/orgunit/2.0/%s/%s' % (customer_id, name) - org = self._GetProperties(uri) - try: - org['orgUnitPath'] = urllib.unquote_plus(org['orgUnitPath']) - org['parentOrgUnitPath'] = urllib.unquote_plus(org['parentOrgUnitPath']) - except AttributeError: - pass - return org - - def RetrieveAllOrganizationUnits(self): - - customer_id = self.RetrieveCustomerId()['customerId'] - uri = '/a/feeds/orgunit/2.0/%s?get=all' % customer_id - all_orgs = self._GetPropertiesList(uri) - for org in all_orgs: - try: - org['orgUnitPath'] = urllib.unquote_plus(org['orgUnitPath']) - org['parentOrgUnitPath'] = urllib.unquote_plus(org['parentOrgUnitPath']) - except AttributeError: - pass - return all_orgs - - def RetrieveSubOrganizationUnits(self, name): - - customer_id = self.RetrieveCustomerId()['customerId'] - uri = '/a/feeds/orgunit/2.0/%s?get=children&orgUnitPath=%s' % (customer_id, urllib.quote_plus(name, safe='/')) - sub_orgs = self._GetPropertiesList(uri) - for org in sub_orgs: - try: - org['orgUnitPath'] = urllib.unquote_plus(org['orgUnitPath']) - org['parentOrgUnitPath'] = urllib.unquote_plus(org['parentOrgUnitPath']) - except AttributeError: - pass - return sub_orgs - - def DeleteOrganizationUnit(self, name): - - customer_id = self.RetrieveCustomerId()['customerId'] - name = urllib.quote_plus(name, safe='/') - uri = '/a/feeds/orgunit/2.0/%s/%s' % (customer_id, name) - return self._DeleteProperties(uri) - - def RetrieveUserOrganization(self, user): - - customer_id = self.RetrieveCustomerId()['customerId'] - if user.find('@') < 0: - user = user+'@'+self.domain - uri = '/a/feeds/orguser/2.0/%s/%s' % (customer_id, urllib.quote_plus(user)) - org = self._GetProperties(uri) - try: - org['orgUnitPath'] = urllib.unquote_plus(org['orgUnitPath']) - except AttributeError: - pass - return org - - def RetrieveAllOrganizationUsers(self): - - customer_id = self.RetrieveCustomerId()['customerId'] - uri = '/a/feeds/orguser/2.0/%s?get=all' % customer_id - all_users = self._GetPropertiesList(uri) - for user in all_users: - try: - user['orgUnitPath'] = urllib.unquote_plus(user['orgUnitPath']) - except AttributeError: - pass - return all_users - - def RetrieveAllOrganizationUnitUsers(self, name): - - customer_id = self.RetrieveCustomerId()['customerId'] - uri = '/a/feeds/orguser/2.0/%s?get=children&orgUnitPath=%s' % (customer_id, urllib.quote_plus(name)) - all_users = self._GetPropertiesList(uri) - for user in all_users: - try: - user['orgUnitPath'] = urllib.unquote_plus(user['orgUnitPath']) - except AttributeError: - pass - return all_users diff --git a/gdata/apps/reporting/__init__.py b/gdata/apps/reporting/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/gdata/apps/reporting/service.py b/gdata/apps/reporting/service.py deleted file mode 100644 index f8b3b8c46f..0000000000 --- a/gdata/apps/reporting/service.py +++ /dev/null @@ -1,99 +0,0 @@ -#!/usr/bin/python2.4 -# -# 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. - -"""ReportService simplifies Reporting API calls. - -ReportService extends gdata.apps.service.PropertyService to ease interaction with -the Google Apps Reporting API. -""" - -__author__ = 'Jay Lee ' - -import gdata.apps -import gdata.apps.service -import gdata.service -import time - -class ReportService(gdata.apps.service.PropertyService): - """Service extension for the Google Reporting API service.""" - - def __init__(self, email=None, password=None, domain=None, source=None, - server='www.google.com', additional_headers=None, - **kwargs): - """Creates a client for the Reporting service. - - Args: - email: string (optional) The user's email address, used for - authentication. - password: string (optional) The user's password. - domain: string (optional) The Google Apps domain name. - 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: 'apps-apis.google.com'. - **kwargs: The other parameters to pass to gdata.service.GDataService - constructor. - """ - gdata.service.GDataService.__init__( - self, email=email, password=password, service='apps', source=source, - server=server, additional_headers=additional_headers, **kwargs) - self.ssl = True - self.port = 443 - self.domain = domain - - def retrieve_report(self, report, date=None): - """Retrieves a report - - Args: - report: string, accounts, activity, disk_space, email_clients or summary - date: string: YYYY-MM-DD. If not specified, most recent day that has past 12pm PST will be used (in other words, today if it's after 12pm PST or yesterday if not) - - Returns: - String, the report data - """ - uri = '/hosted/services/v1.0/reports/ReportingData' - if date == None: - now = time.time() - report_time = time.gmtime(now) - if report_time.tm_hour < 20: - report_time = time.gmtime(now - 60*60*24) - date = '%s-%s-0%s' % (report_time.tm_year, report_time.tm_mon, report_time.tm_mday) - page = 1 - report_data = '' - while True: - xml = ''' - - Report - %s - %s - %s - daily - %s -''' % (self.domain, date, page, report) - try: - report_page = self.Post(xml, uri, converter=str) - except gdata.service.RequestError, e: - raise gdata.apps.service.AppsForYourDomainException(e.args[0]) - if report_page == 'End-Of-Report': - return report_data - else: - if page == 1: - report_data += report_page # 1st page has headers - else: - report_data += report_page[report_page.find('\n')+1:] # remove header on additional pages - page = page + 1 - - RetrieveReport = retrieve_report diff --git a/gdata/apps_property.py b/gdata/apps_property.py deleted file mode 100644 index 5afa1f38b9..0000000000 --- a/gdata/apps_property.py +++ /dev/null @@ -1,39 +0,0 @@ -#!/usr/bin/env python -# -# Copyright (C) 2010 Google 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. - - -# This module is used for version 2 of the Google Data APIs. - - -"""Provides a base class to represent property elements in feeds. - -This module is used for version 2 of the Google Data APIs. The primary class -in this module is AppsProperty. -""" - - -__author__ = 'Vic Fryzel ' - - -import atom.core -import gdata.apps - - -class AppsProperty(atom.core.XmlElement): - """Represents an element in a feed.""" - _qname = gdata.apps.APPS_TEMPLATE % 'property' - name = 'name' - value = 'value' diff --git a/gdata/base/__init__.py b/gdata/base/__init__.py deleted file mode 100644 index 32401a92ca..0000000000 --- a/gdata/base/__init__.py +++ /dev/null @@ -1,697 +0,0 @@ -#!/usr/bin/python -# -# Copyright (C) 2006 Google 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. - -"""Contains extensions to Atom objects used with Google Base.""" - - -__author__ = 'api.jscudder (Jeffrey Scudder)' - - -try: - from xml.etree import cElementTree as ElementTree -except ImportError: - try: - import cElementTree as ElementTree - except ImportError: - try: - from xml.etree import ElementTree - except ImportError: - from elementtree import ElementTree -import atom -import gdata - - -# XML namespaces which are often used in Google Base entities. -GBASE_NAMESPACE = 'http://base.google.com/ns/1.0' -GBASE_TEMPLATE = '{http://base.google.com/ns/1.0}%s' -GMETA_NAMESPACE = 'http://base.google.com/ns-metadata/1.0' -GMETA_TEMPLATE = '{http://base.google.com/ns-metadata/1.0}%s' - - -class ItemAttributeContainer(atom.AtomBase): - """Provides methods for finding Google Base Item attributes. - - Google Base item attributes are child nodes in the gbase namespace. Google - Base allows you to define your own item attributes and this class provides - methods to interact with the custom attributes. - """ - - def GetItemAttributes(self, name): - """Returns a list of all item attributes which have the desired name. - - Args: - name: str The tag of the desired base attributes. For example, calling - this method with 'rating' would return a list of ItemAttributes - represented by a 'g:rating' tag. - - Returns: - A list of matching ItemAttribute objects. - """ - result = [] - for attrib in self.item_attributes: - if attrib.name == name: - result.append(attrib) - return result - - def FindItemAttribute(self, name): - """Get the contents of the first Base item attribute which matches name. - - This method is deprecated, please use GetItemAttributes instead. - - Args: - name: str The tag of the desired base attribute. For example, calling - this method with name = 'rating' would search for a tag rating - in the GBase namespace in the item attributes. - - Returns: - The text contents of the item attribute, or none if the attribute was - not found. - """ - - for attrib in self.item_attributes: - if attrib.name == name: - return attrib.text - return None - - def AddItemAttribute(self, name, value, value_type=None, access=None): - """Adds a new item attribute tag containing the value. - - Creates a new extension element in the GBase namespace to represent a - Google Base item attribute. - - Args: - name: str The tag name for the new attribute. This must be a valid xml - tag name. The tag will be placed in the GBase namespace. - value: str Contents for the item attribute - value_type: str (optional) The type of data in the vlaue, Examples: text - float - access: str (optional) Used to hide attributes. The attribute is not - exposed in the snippets feed if access is set to 'private'. - """ - - new_attribute = ItemAttribute(name, text=value, - text_type=value_type, access=access) - self.item_attributes.append(new_attribute) - return new_attribute - - def SetItemAttribute(self, name, value): - """Changes an existing item attribute's value.""" - - for attrib in self.item_attributes: - if attrib.name == name: - attrib.text = value - return - - def RemoveItemAttribute(self, name): - """Deletes the first extension element which matches name. - - Deletes the first extension element which matches name. - """ - - for i in xrange(len(self.item_attributes)): - if self.item_attributes[i].name == name: - del self.item_attributes[i] - return - - # We need to overwrite _ConvertElementTreeToMember to add special logic to - # convert custom attributes to members - def _ConvertElementTreeToMember(self, child_tree): - # Find the element's tag in this class's list of child members - if self.__class__._children.has_key(child_tree.tag): - member_name = self.__class__._children[child_tree.tag][0] - member_class = self.__class__._children[child_tree.tag][1] - # If the class member is supposed to contain a list, make sure the - # matching member is set to a list, then append the new member - # instance to the list. - if isinstance(member_class, list): - if getattr(self, member_name) is None: - setattr(self, member_name, []) - getattr(self, member_name).append(atom._CreateClassFromElementTree( - member_class[0], child_tree)) - else: - setattr(self, member_name, - atom._CreateClassFromElementTree(member_class, child_tree)) - elif child_tree.tag.find('{%s}' % GBASE_NAMESPACE) == 0: - # If this is in the gbase namespace, make it into an extension element. - name = child_tree.tag[child_tree.tag.index('}')+1:] - value = child_tree.text - if child_tree.attrib.has_key('type'): - value_type = child_tree.attrib['type'] - else: - value_type = None - attrib=self.AddItemAttribute(name, value, value_type) - for sub in child_tree.getchildren(): - sub_name = sub.tag[sub.tag.index('}')+1:] - sub_value=sub.text - if sub.attrib.has_key('type'): - sub_type = sub.attrib['type'] - else: - sub_type=None - attrib.AddItemAttribute(sub_name, sub_value, sub_type) - else: - atom.ExtensionContainer._ConvertElementTreeToMember(self, child_tree) - - # We need to overwtite _AddMembersToElementTree to add special logic to - # convert custom members to XML nodes. - def _AddMembersToElementTree(self, tree): - # Convert the members of this class which are XML child nodes. - # This uses the class's _children dictionary to find the members which - # should become XML child nodes. - member_node_names = [values[0] for tag, values in - self.__class__._children.iteritems()] - for member_name in member_node_names: - member = getattr(self, member_name) - if member is None: - pass - elif isinstance(member, list): - for instance in member: - instance._BecomeChildElement(tree) - else: - member._BecomeChildElement(tree) - # Convert the members of this class which are XML attributes. - for xml_attribute, member_name in self.__class__._attributes.iteritems(): - member = getattr(self, member_name) - if member is not None: - tree.attrib[xml_attribute] = member - # Convert all special custom item attributes to nodes - for attribute in self.item_attributes: - attribute._BecomeChildElement(tree) - # Lastly, call the ExtensionContainers's _AddMembersToElementTree to - # convert any extension attributes. - atom.ExtensionContainer._AddMembersToElementTree(self, tree) - - -class ItemAttribute(ItemAttributeContainer): - """An optional or user defined attribute for a GBase item. - - Google Base allows items to have custom attribute child nodes. These nodes - have contents and a type attribute which tells Google Base whether the - contents are text, a float value with units, etc. The Atom text class has - the same structure, so this class inherits from Text. - """ - - _namespace = GBASE_NAMESPACE - _children = atom.Text._children.copy() - _attributes = atom.Text._attributes.copy() - _attributes['access'] = 'access' - - def __init__(self, name, text_type=None, access=None, text=None, - extension_elements=None, extension_attributes=None, item_attributes=None): - """Constructor for a GBase item attribute - - Args: - name: str The name of the attribute. Examples include - price, color, make, model, pages, salary, etc. - text_type: str (optional) The type associated with the text contents - access: str (optional) If the access attribute is set to 'private', the - attribute will not be included in the item's description in the - snippets feed - text: str (optional) The text data in the this element - extension_elements: list (optional) A list of ExtensionElement - instances - extension_attributes: dict (optional) A dictionary of attribute - value string pairs - """ - - self.name = name - self.type = text_type - self.access = access - self.text = text - self.extension_elements = extension_elements or [] - self.extension_attributes = extension_attributes or {} - self.item_attributes = item_attributes or [] - - def _BecomeChildElement(self, tree): - new_child = ElementTree.Element('') - tree.append(new_child) - new_child.tag = '{%s}%s' % (self.__class__._namespace, - self.name) - self._AddMembersToElementTree(new_child) - - def _ToElementTree(self): - new_tree = ElementTree.Element('{%s}%s' % (self.__class__._namespace, - self.name)) - self._AddMembersToElementTree(new_tree) - return new_tree - - -def ItemAttributeFromString(xml_string): - element_tree = ElementTree.fromstring(xml_string) - return _ItemAttributeFromElementTree(element_tree) - - -def _ItemAttributeFromElementTree(element_tree): - if element_tree.tag.find(GBASE_TEMPLATE % '') == 0: - to_return = ItemAttribute('') - to_return._HarvestElementTree(element_tree) - to_return.name = element_tree.tag[element_tree.tag.index('}')+1:] - if to_return.name and to_return.name != '': - return to_return - return None - - -class Label(atom.AtomBase): - """The Google Base label element""" - - _tag = 'label' - _namespace = GBASE_NAMESPACE - _children = atom.AtomBase._children.copy() - _attributes = atom.AtomBase._attributes.copy() - - def __init__(self, text=None, extension_elements=None, - extension_attributes=None): - self.text = text - self.extension_elements = extension_elements or [] - self.extension_attributes = extension_attributes or {} - - -def LabelFromString(xml_string): - return atom.CreateClassFromXMLString(Label, xml_string) - - -class Thumbnail(atom.AtomBase): - """The Google Base thumbnail element""" - - _tag = 'thumbnail' - _namespace = GMETA_NAMESPACE - _children = atom.AtomBase._children.copy() - _attributes = atom.AtomBase._attributes.copy() - _attributes['width'] = 'width' - _attributes['height'] = 'height' - - def __init__(self, width=None, height=None, text=None, extension_elements=None, - extension_attributes=None): - self.text = text - self.extension_elements = extension_elements or [] - self.extension_attributes = extension_attributes or {} - self.width = width - self.height = height - - -def ThumbnailFromString(xml_string): - return atom.CreateClassFromXMLString(Thumbnail, xml_string) - - -class ImageLink(atom.Text): - """The Google Base image_link element""" - - _tag = 'image_link' - _namespace = GBASE_NAMESPACE - _children = atom.Text._children.copy() - _attributes = atom.Text._attributes.copy() - _children['{%s}thumbnail' % GMETA_NAMESPACE] = ('thumbnail', [Thumbnail]) - - def __init__(self, thumbnail=None, text=None, extension_elements=None, - text_type=None, extension_attributes=None): - self.thumbnail = thumbnail or [] - self.text = text - self.type = text_type - self.extension_elements = extension_elements or [] - self.extension_attributes = extension_attributes or {} - - -def ImageLinkFromString(xml_string): - return atom.CreateClassFromXMLString(ImageLink, xml_string) - - -class ItemType(atom.Text): - """The Google Base item_type element""" - - _tag = 'item_type' - _namespace = GBASE_NAMESPACE - _children = atom.Text._children.copy() - _attributes = atom.Text._attributes.copy() - - def __init__(self, text=None, extension_elements=None, - text_type=None, extension_attributes=None): - self.text = text - self.type = text_type - self.extension_elements = extension_elements or [] - self.extension_attributes = extension_attributes or {} - - -def ItemTypeFromString(xml_string): - return atom.CreateClassFromXMLString(ItemType, xml_string) - - -class MetaItemType(ItemType): - """The Google Base item_type element""" - - _tag = 'item_type' - _namespace = GMETA_NAMESPACE - _children = ItemType._children.copy() - _attributes = ItemType._attributes.copy() - - -def MetaItemTypeFromString(xml_string): - return atom.CreateClassFromXMLString(MetaItemType, xml_string) - - -class Value(atom.AtomBase): - """Metadata about common values for a given attribute - - A value is a child of an attribute which comes from the attributes feed. - The value's text is a commonly used value paired with an attribute name - and the value's count tells how often this value appears for the given - attribute in the search results. - """ - - _tag = 'value' - _namespace = GMETA_NAMESPACE - _children = atom.AtomBase._children.copy() - _attributes = atom.AtomBase._attributes.copy() - _attributes['count'] = 'count' - - def __init__(self, count=None, text=None, extension_elements=None, - extension_attributes=None): - """Constructor for Attribute metadata element - - Args: - count: str (optional) The number of times the value in text is given - for the parent attribute. - text: str (optional) The value which appears in the search results. - extension_elements: list (optional) A list of ExtensionElement - instances - extension_attributes: dict (optional) A dictionary of attribute value - string pairs - """ - - self.count = count - self.text = text - self.extension_elements = extension_elements or [] - self.extension_attributes = extension_attributes or {} - - -def ValueFromString(xml_string): - return atom.CreateClassFromXMLString(Value, xml_string) - - -class Attribute(atom.Text): - """Metadata about an attribute from the attributes feed - - An entry from the attributes feed contains a list of attributes. Each - attribute describes the attribute's type and count of the items which - use the attribute. - """ - - _tag = 'attribute' - _namespace = GMETA_NAMESPACE - _children = atom.Text._children.copy() - _attributes = atom.Text._attributes.copy() - _children['{%s}value' % GMETA_NAMESPACE] = ('value', [Value]) - _attributes['count'] = 'count' - _attributes['name'] = 'name' - - def __init__(self, name=None, attribute_type=None, count=None, value=None, - text=None, extension_elements=None, extension_attributes=None): - """Constructor for Attribute metadata element - - Args: - name: str (optional) The name of the attribute - attribute_type: str (optional) The type for the attribute. Examples: - test, float, etc. - count: str (optional) The number of times this attribute appears in - the query results. - value: list (optional) The values which are often used for this - attirbute. - text: str (optional) The text contents of the XML for this attribute. - extension_elements: list (optional) A list of ExtensionElement - instances - extension_attributes: dict (optional) A dictionary of attribute value - string pairs - """ - - self.name = name - self.type = attribute_type - self.count = count - self.value = value or [] - self.text = text - self.extension_elements = extension_elements or [] - self.extension_attributes = extension_attributes or {} - - -def AttributeFromString(xml_string): - return atom.CreateClassFromXMLString(Attribute, xml_string) - - -class Attributes(atom.AtomBase): - """A collection of Google Base metadata attributes""" - - _tag = 'attributes' - _namespace = GMETA_NAMESPACE - _children = atom.AtomBase._children.copy() - _attributes = atom.AtomBase._attributes.copy() - _children['{%s}attribute' % GMETA_NAMESPACE] = ('attribute', [Attribute]) - - def __init__(self, attribute=None, extension_elements=None, - extension_attributes=None, text=None): - self.attribute = attribute or [] - self.extension_elements = extension_elements or [] - self.extension_attributes = extension_attributes or {} - self.text = text - - -class GBaseItem(ItemAttributeContainer, gdata.BatchEntry): - """An Google Base flavor of an Atom Entry. - - Google Base items have required attributes, recommended attributes, and user - defined attributes. The required attributes are stored in this class as - members, and other attributes are stored as extension elements. You can - access the recommended and user defined attributes by using - AddItemAttribute, SetItemAttribute, FindItemAttribute, and - RemoveItemAttribute. - - The Base Item - """ - - _tag = 'entry' - _namespace = atom.ATOM_NAMESPACE - _children = gdata.BatchEntry._children.copy() - _attributes = gdata.BatchEntry._attributes.copy() - _children['{%s}label' % GBASE_NAMESPACE] = ('label', [Label]) - _children['{%s}item_type' % GBASE_NAMESPACE] = ('item_type', ItemType) - - def __init__(self, author=None, category=None, content=None, - contributor=None, atom_id=None, link=None, published=None, rights=None, - source=None, summary=None, title=None, updated=None, control=None, - label=None, item_type=None, item_attributes=None, - batch_operation=None, batch_id=None, batch_status=None, - text=None, extension_elements=None, extension_attributes=None): - self.author = author or [] - self.category = category or [] - self.content = content - self.contributor = contributor or [] - self.id = atom_id - self.link = link or [] - self.published = published - self.rights = rights - self.source = source - self.summary = summary - self.title = title - self.updated = updated - self.control = control - self.label = label or [] - self.item_type = item_type - self.item_attributes = item_attributes or [] - self.batch_operation = batch_operation - self.batch_id = batch_id - self.batch_status = batch_status - self.text = text - self.extension_elements = extension_elements or [] - self.extension_attributes = extension_attributes or {} - - -def GBaseItemFromString(xml_string): - return atom.CreateClassFromXMLString(GBaseItem, xml_string) - - -class GBaseSnippet(GBaseItem): - _tag = 'entry' - _namespace = atom.ATOM_NAMESPACE - _children = GBaseItem._children.copy() - _attributes = GBaseItem._attributes.copy() - - -def GBaseSnippetFromString(xml_string): - return atom.CreateClassFromXMLString(GBaseSnippet, xml_string) - - -class GBaseAttributeEntry(gdata.GDataEntry): - """An Atom Entry from the attributes feed""" - - _tag = 'entry' - _namespace = atom.ATOM_NAMESPACE - _children = gdata.GDataEntry._children.copy() - _attributes = gdata.GDataEntry._attributes.copy() - _children['{%s}attribute' % GMETA_NAMESPACE] = ('attribute', [Attribute]) - - def __init__(self, author=None, category=None, content=None, - contributor=None, atom_id=None, link=None, published=None, rights=None, - source=None, summary=None, title=None, updated=None, label=None, - attribute=None, control=None, - text=None, extension_elements=None, extension_attributes=None): - self.author = author or [] - self.category = category or [] - self.content = content - self.contributor = contributor or [] - self.id = atom_id - self.link = link or [] - self.published = published - self.rights = rights - self.source = source - self.summary = summary - self.control = control - self.title = title - self.updated = updated - self.label = label or [] - self.attribute = attribute or [] - self.text = text - self.extension_elements = extension_elements or [] - self.extension_attributes = extension_attributes or {} - - -def GBaseAttributeEntryFromString(xml_string): - return atom.CreateClassFromXMLString(GBaseAttributeEntry, xml_string) - - -class GBaseItemTypeEntry(gdata.GDataEntry): - """An Atom entry from the item types feed - - These entries contain a list of attributes which are stored in one - XML node called attributes. This class simplifies the data structure - by treating attributes as a list of attribute instances. - - Note that the item_type for an item type entry is in the Google Base meta - namespace as opposed to item_types encountered in other feeds. - """ - - _tag = 'entry' - _namespace = atom.ATOM_NAMESPACE - _children = gdata.GDataEntry._children.copy() - _attributes = gdata.GDataEntry._attributes.copy() - _children['{%s}attributes' % GMETA_NAMESPACE] = ('attributes', Attributes) - _children['{%s}attribute' % GMETA_NAMESPACE] = ('attribute', [Attribute]) - _children['{%s}item_type' % GMETA_NAMESPACE] = ('item_type', MetaItemType) - - def __init__(self, author=None, category=None, content=None, - contributor=None, atom_id=None, link=None, published=None, rights=None, - source=None, summary=None, title=None, updated=None, label=None, - item_type=None, control=None, attribute=None, attributes=None, - text=None, extension_elements=None, extension_attributes=None): - self.author = author or [] - self.category = category or [] - self.content = content - self.contributor = contributor or [] - self.id = atom_id - self.link = link or [] - self.published = published - self.rights = rights - self.source = source - self.summary = summary - self.title = title - self.updated = updated - self.control = control - self.label = label or [] - self.item_type = item_type - self.attributes = attributes - self.attribute = attribute or [] - self.text = text - self.extension_elements = extension_elements or [] - self.extension_attributes = extension_attributes or {} - - -def GBaseItemTypeEntryFromString(xml_string): - return atom.CreateClassFromXMLString(GBaseItemTypeEntry, xml_string) - - -class GBaseItemFeed(gdata.BatchFeed): - """A feed containing Google Base Items""" - - _tag = 'feed' - _namespace = atom.ATOM_NAMESPACE - _children = gdata.BatchFeed._children.copy() - _attributes = gdata.BatchFeed._attributes.copy() - _children['{%s}entry' % atom.ATOM_NAMESPACE] = ('entry', [GBaseItem]) - - -def GBaseItemFeedFromString(xml_string): - return atom.CreateClassFromXMLString(GBaseItemFeed, xml_string) - - -class GBaseSnippetFeed(gdata.GDataFeed): - """A feed containing Google Base Snippets""" - - _tag = 'feed' - _namespace = atom.ATOM_NAMESPACE - _children = gdata.GDataFeed._children.copy() - _attributes = gdata.GDataFeed._attributes.copy() - _children['{%s}entry' % atom.ATOM_NAMESPACE] = ('entry', [GBaseSnippet]) - - -def GBaseSnippetFeedFromString(xml_string): - return atom.CreateClassFromXMLString(GBaseSnippetFeed, xml_string) - - -class GBaseAttributesFeed(gdata.GDataFeed): - """A feed containing Google Base Attributes - - A query sent to the attributes feed will return a feed of - attributes which are present in the items that match the - query. - """ - - _tag = 'feed' - _namespace = atom.ATOM_NAMESPACE - _children = gdata.GDataFeed._children.copy() - _attributes = gdata.GDataFeed._attributes.copy() - _children['{%s}entry' % atom.ATOM_NAMESPACE] = ('entry', - [GBaseAttributeEntry]) - - -def GBaseAttributesFeedFromString(xml_string): - return atom.CreateClassFromXMLString(GBaseAttributesFeed, xml_string) - - -class GBaseLocalesFeed(gdata.GDataFeed): - """The locales feed from Google Base. - - This read-only feed defines the permitted locales for Google Base. The - locale value identifies the language, currency, and date formats used in a - feed. - """ - - _tag = 'feed' - _namespace = atom.ATOM_NAMESPACE - _children = gdata.GDataFeed._children.copy() - _attributes = gdata.GDataFeed._attributes.copy() - - -def GBaseLocalesFeedFromString(xml_string): - return atom.CreateClassFromXMLString(GBaseLocalesFeed, xml_string) - - -class GBaseItemTypesFeed(gdata.GDataFeed): - """A feed from the Google Base item types feed""" - - _tag = 'feed' - _namespace = atom.ATOM_NAMESPACE - _children = gdata.GDataFeed._children.copy() - _attributes = gdata.GDataFeed._attributes.copy() - _children['{%s}entry' % atom.ATOM_NAMESPACE] = ('entry', [GBaseItemTypeEntry]) - - -def GBaseItemTypesFeedFromString(xml_string): - return atom.CreateClassFromXMLString(GBaseItemTypesFeed, xml_string) diff --git a/gdata/base/service.py b/gdata/base/service.py deleted file mode 100644 index f6fbfa5bfb..0000000000 --- a/gdata/base/service.py +++ /dev/null @@ -1,256 +0,0 @@ -#!/usr/bin/python -# -# Copyright (C) 2006 Google 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. - -"""GBaseService extends the GDataService to streamline Google Base operations. - - GBaseService: Provides methods to query feeds and manipulate items. Extends - GDataService. - - DictionaryToParamList: Function which converts a dictionary into a list of - URL arguments (represented as strings). This is a - utility function used in CRUD operations. -""" - -__author__ = 'api.jscudder (Jeffrey Scudder)' - -import urllib -import gdata -import atom.service -import gdata.service -import gdata.base -import atom - - -# URL to which all batch requests are sent. -BASE_BATCH_URL = 'http://www.google.com/base/feeds/items/batch' - - -class Error(Exception): - pass - - -class RequestError(Error): - pass - - -class GBaseService(gdata.service.GDataService): - """Client for the Google Base service.""" - - def __init__(self, email=None, password=None, source=None, - server='base.google.com', api_key=None, additional_headers=None, - handler=None, **kwargs): - """Creates a client for the Google Base 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: 'base.google.com'. - api_key: string (optional) The Google Base API key to use. - **kwargs: The other parameters to pass to gdata.service.GDataService - constructor. - """ - gdata.service.GDataService.__init__( - self, email=email, password=password, service='gbase', source=source, - server=server, additional_headers=additional_headers, handler=handler, - **kwargs) - self.api_key = api_key - - def _SetAPIKey(self, api_key): - if not isinstance(self.additional_headers, dict): - self.additional_headers = {} - self.additional_headers['X-Google-Key'] = api_key - - def __SetAPIKey(self, api_key): - self._SetAPIKey(api_key) - - def _GetAPIKey(self): - if 'X-Google-Key' not in self.additional_headers: - return None - else: - return self.additional_headers['X-Google-Key'] - - def __GetAPIKey(self): - return self._GetAPIKey() - - api_key = property(__GetAPIKey, __SetAPIKey, - doc="""Get or set the API key to be included in all requests.""") - - def Query(self, uri, converter=None): - """Performs a style query and returns a resulting feed or entry. - - Args: - uri: string The full URI which be queried. Examples include - '/base/feeds/snippets?bq=digital+camera', - 'http://www.google.com/base/feeds/snippets?bq=digital+camera' - '/base/feeds/items' - I recommend creating a URI using a query class. - converter: func (optional) A function which will be executed on the - server's response. Examples include GBaseItemFromString, etc. - - Returns: - If converter was specified, returns the results of calling converter on - the server's response. If converter was not specified, and the result - was an Atom Entry, returns a GBaseItem, by default, the method returns - the result of calling gdata.service's Get method. - """ - - result = self.Get(uri, converter=converter) - if converter: - return result - elif isinstance(result, atom.Entry): - return gdata.base.GBaseItemFromString(result.ToString()) - return result - - def QuerySnippetsFeed(self, uri): - return self.Get(uri, converter=gdata.base.GBaseSnippetFeedFromString) - - def QueryItemsFeed(self, uri): - return self.Get(uri, converter=gdata.base.GBaseItemFeedFromString) - - def QueryAttributesFeed(self, uri): - return self.Get(uri, converter=gdata.base.GBaseAttributesFeedFromString) - - def QueryItemTypesFeed(self, uri): - return self.Get(uri, converter=gdata.base.GBaseItemTypesFeedFromString) - - def QueryLocalesFeed(self, uri): - return self.Get(uri, converter=gdata.base.GBaseLocalesFeedFromString) - - def GetItem(self, uri): - return self.Get(uri, converter=gdata.base.GBaseItemFromString) - - def GetSnippet(self, uri): - return self.Get(uri, converter=gdata.base.GBaseSnippetFromString) - - def GetAttribute(self, uri): - return self.Get(uri, converter=gdata.base.GBaseAttributeEntryFromString) - - def GetItemType(self, uri): - return self.Get(uri, converter=gdata.base.GBaseItemTypeEntryFromString) - - def GetLocale(self, uri): - return self.Get(uri, converter=gdata.base.GDataEntryFromString) - - def InsertItem(self, new_item, url_params=None, escape_params=True, - converter=None): - """Adds an item to Google Base. - - Args: - new_item: atom.Entry or subclass A new item which is to be added to - Google Base. - url_params: dict (optional) Additional URL parameters to be included - in the insertion request. - escape_params: boolean (optional) If true, the url_parameters will be - escaped before they are included in the request. - converter: func (optional) Function which is executed on the server's - response before it is returned. Usually this is a function like - GBaseItemFromString which will parse the response and turn it into - an object. - - Returns: - If converter is defined, the results of running converter on the server's - response. Otherwise, it will be a GBaseItem. - """ - - response = self.Post(new_item, '/base/feeds/items', url_params=url_params, - escape_params=escape_params, converter=converter) - - if not converter and isinstance(response, atom.Entry): - return gdata.base.GBaseItemFromString(response.ToString()) - return response - - def DeleteItem(self, item_id, url_params=None, escape_params=True): - """Removes an item with the specified ID from Google Base. - - Args: - item_id: string The ID of the item to be deleted. Example: - 'http://www.google.com/base/feeds/items/13185446517496042648' - url_params: dict (optional) Additional URL parameters to be included - in the deletion request. - escape_params: boolean (optional) If true, the url_parameters will be - escaped before they are included in the request. - - Returns: - True if the delete succeeded. - """ - - return self.Delete('%s' % (item_id[len('http://www.google.com'):],), - url_params=url_params, escape_params=escape_params) - - def UpdateItem(self, item_id, updated_item, url_params=None, - escape_params=True, - converter=gdata.base.GBaseItemFromString): - """Updates an existing item. - - Args: - item_id: string The ID of the item to be updated. Example: - 'http://www.google.com/base/feeds/items/13185446517496042648' - updated_item: atom.Entry, subclass, or string, containing - the Atom Entry which will replace the base item which is - stored at the item_id. - url_params: dict (optional) Additional URL parameters to be included - in the update request. - escape_params: boolean (optional) If true, the url_parameters will be - escaped before they are included in the request. - converter: func (optional) Function which is executed on the server's - response before it is returned. Usually this is a function like - GBaseItemFromString which will parse the response and turn it into - an object. - - Returns: - If converter is defined, the results of running converter on the server's - response. Otherwise, it will be a GBaseItem. - """ - - response = self.Put(updated_item, - item_id, url_params=url_params, escape_params=escape_params, - converter=converter) - if not converter and isinstance(response, atom.Entry): - return gdata.base.GBaseItemFromString(response.ToString()) - return response - - def ExecuteBatch(self, batch_feed, - converter=gdata.base.GBaseItemFeedFromString): - """Sends a batch request feed to the server. - - Args: - batch_feed: gdata.BatchFeed A feed containing BatchEntry elements which - contain the desired CRUD operation and any necessary entry data. - converter: Function (optional) Function to be executed on the server's - response. This function should take one string as a parameter. The - default value is GBaseItemFeedFromString which will turn the result - into a gdata.base.GBaseItem object. - - Returns: - A gdata.BatchFeed containing the results. - """ - - return self.Post(batch_feed, BASE_BATCH_URL, converter=converter) - - -class BaseQuery(gdata.service.Query): - - def _GetBaseQuery(self): - return self['bq'] - - def _SetBaseQuery(self, base_query): - self['bq'] = base_query - - bq = property(_GetBaseQuery, _SetBaseQuery, - doc="""The bq query parameter""") diff --git a/gdata/blogger/__init__.py b/gdata/blogger/__init__.py deleted file mode 100644 index 156f25c563..0000000000 --- a/gdata/blogger/__init__.py +++ /dev/null @@ -1,202 +0,0 @@ -#!/usr/bin/python -# -# Copyright (C) 2007, 2008 Google 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. - - -"""Contains extensions to Atom objects used with Blogger.""" - - -__author__ = 'api.jscudder (Jeffrey Scudder)' - - -import atom -import gdata -import re - - -LABEL_SCHEME = 'http://www.blogger.com/atom/ns#' -THR_NAMESPACE = 'http://purl.org/syndication/thread/1.0' - - -class BloggerEntry(gdata.GDataEntry): - """Adds convenience methods inherited by all Blogger entries.""" - - blog_name_pattern = re.compile('(http://)(\w*)') - blog_id_pattern = re.compile('(tag:blogger.com,1999:blog-)(\w*)') - blog_id2_pattern = re.compile('tag:blogger.com,1999:user-(\d+)\.blog-(\d+)') - - def GetBlogId(self): - """Extracts the Blogger id of this blog. - This method is useful when contructing URLs by hand. The blog id is - often used in blogger operation URLs. This should not be confused with - the id member of a BloggerBlog. The id element is the Atom id XML element. - The blog id which this method returns is a part of the Atom id. - - Returns: - The blog's unique id as a string. - """ - if self.id.text: - match = self.blog_id_pattern.match(self.id.text) - if match: - return match.group(2) - else: - return self.blog_id2_pattern.match(self.id.text).group(2) - return None - - def GetBlogName(self): - """Finds the name of this blog as used in the 'alternate' URL. - An alternate URL is in the form 'http://blogName.blogspot.com/'. For an - entry representing the above example, this method would return 'blogName'. - - Returns: - The blog's URL name component as a string. - """ - for link in self.link: - if link.rel == 'alternate': - return self.blog_name_pattern.match(link.href).group(2) - return None - - -class BlogEntry(BloggerEntry): - """Describes a blog entry in the feed listing a user's blogs.""" - - -def BlogEntryFromString(xml_string): - return atom.CreateClassFromXMLString(BlogEntry, xml_string) - - -class BlogFeed(gdata.GDataFeed): - """Describes a feed of a user's blogs.""" - - _children = gdata.GDataFeed._children.copy() - _children['{%s}entry' % atom.ATOM_NAMESPACE] = ('entry', [BlogEntry]) - - -def BlogFeedFromString(xml_string): - return atom.CreateClassFromXMLString(BlogFeed, xml_string) - - -class BlogPostEntry(BloggerEntry): - """Describes a blog post entry in the feed of a blog's posts.""" - - post_id_pattern = re.compile('(tag:blogger.com,1999:blog-)(\w*)(.post-)(\w*)') - - def AddLabel(self, label): - """Adds a label to the blog post. - - The label is represented by an Atom category element, so this method - is shorthand for appending a new atom.Category object. - - Args: - label: str - """ - self.category.append(atom.Category(scheme=LABEL_SCHEME, term=label)) - - def GetPostId(self): - """Extracts the postID string from the entry's Atom id. - - Returns: A string of digits which identify this post within the blog. - """ - if self.id.text: - return self.post_id_pattern.match(self.id.text).group(4) - return None - - -def BlogPostEntryFromString(xml_string): - return atom.CreateClassFromXMLString(BlogPostEntry, xml_string) - - -class BlogPostFeed(gdata.GDataFeed): - """Describes a feed of a blog's posts.""" - - _children = gdata.GDataFeed._children.copy() - _children['{%s}entry' % atom.ATOM_NAMESPACE] = ('entry', [BlogPostEntry]) - - -def BlogPostFeedFromString(xml_string): - return atom.CreateClassFromXMLString(BlogPostFeed, xml_string) - - -class InReplyTo(atom.AtomBase): - _tag = 'in-reply-to' - _namespace = THR_NAMESPACE - _attributes = atom.AtomBase._attributes.copy() - _attributes['href'] = 'href' - _attributes['ref'] = 'ref' - _attributes['source'] = 'source' - _attributes['type'] = 'type' - - def __init__(self, href=None, ref=None, source=None, type=None, - extension_elements=None, extension_attributes=None, text=None): - self.href = href - self.ref = ref - self.source = source - self.type = type - self.extension_elements = extension_elements or [] - self.extension_attributes = extension_attributes or {} - self.text = text - - -def InReplyToFromString(xml_string): - return atom.CreateClassFromXMLString(InReplyTo, xml_string) - - -class CommentEntry(BloggerEntry): - """Describes a blog post comment entry in the feed of a blog post's - comments.""" - - _children = BloggerEntry._children.copy() - _children['{%s}in-reply-to' % THR_NAMESPACE] = ('in_reply_to', InReplyTo) - - comment_id_pattern = re.compile('.*-(\w*)$') - - def __init__(self, author=None, category=None, content=None, - contributor=None, atom_id=None, link=None, published=None, rights=None, - source=None, summary=None, control=None, title=None, updated=None, - in_reply_to=None, extension_elements=None, extension_attributes=None, - text=None): - BloggerEntry.__init__(self, author=author, category=category, - content=content, contributor=contributor, atom_id=atom_id, link=link, - published=published, rights=rights, source=source, summary=summary, - control=control, title=title, updated=updated, - extension_elements=extension_elements, - extension_attributes=extension_attributes, text=text) - self.in_reply_to = in_reply_to - - def GetCommentId(self): - """Extracts the commentID string from the entry's Atom id. - - Returns: A string of digits which identify this post within the blog. - """ - if self.id.text: - return self.comment_id_pattern.match(self.id.text).group(1) - return None - - -def CommentEntryFromString(xml_string): - return atom.CreateClassFromXMLString(CommentEntry, xml_string) - - -class CommentFeed(gdata.GDataFeed): - """Describes a feed of a blog post's comments.""" - - _children = gdata.GDataFeed._children.copy() - _children['{%s}entry' % atom.ATOM_NAMESPACE] = ('entry', [CommentEntry]) - - -def CommentFeedFromString(xml_string): - return atom.CreateClassFromXMLString(CommentFeed, xml_string) - - diff --git a/gdata/blogger/client.py b/gdata/blogger/client.py deleted file mode 100644 index a0bad63687..0000000000 --- a/gdata/blogger/client.py +++ /dev/null @@ -1,175 +0,0 @@ -#!/usr/bin/env python -# -# Copyright (C) 2009 Google 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. - - -"""Contains a client to communicate with the Blogger servers. - -For documentation on the Blogger API, see: -http://code.google.com/apis/blogger/ -""" - - -__author__ = 'j.s@google.com (Jeff Scudder)' - - -import gdata.client -import gdata.gauth -import gdata.blogger.data -import atom.data -import atom.http_core - - -# List user's blogs, takes a user ID, or 'default'. -BLOGS_URL = 'http://www.blogger.com/feeds/%s/blogs' -# Takes a blog ID. -BLOG_POST_URL = 'http://www.blogger.com/feeds/%s/posts/default' -# Takes a blog ID. -BLOG_PAGE_URL = 'http://www.blogger.com/feeds/%s/pages/default' -# Takes a blog ID and post ID. -BLOG_POST_COMMENTS_URL = 'http://www.blogger.com/feeds/%s/%s/comments/default' -# Takes a blog ID. -BLOG_COMMENTS_URL = 'http://www.blogger.com/feeds/%s/comments/default' -# Takes a blog ID. -BLOG_ARCHIVE_URL = 'http://www.blogger.com/feeds/%s/archive/full' - - -class BloggerClient(gdata.client.GDClient): - api_version = '2' - auth_service = 'blogger' - auth_scopes = gdata.gauth.AUTH_SCOPES['blogger'] - - def get_blogs(self, user_id='default', auth_token=None, - desired_class=gdata.blogger.data.BlogFeed, **kwargs): - return self.get_feed(BLOGS_URL % user_id, auth_token=auth_token, - desired_class=desired_class, **kwargs) - - GetBlogs = get_blogs - - def get_posts(self, blog_id, auth_token=None, - desired_class=gdata.blogger.data.BlogPostFeed, query=None, - **kwargs): - return self.get_feed(BLOG_POST_URL % blog_id, auth_token=auth_token, - desired_class=desired_class, query=query, **kwargs) - - GetPosts = get_posts - - def get_pages(self, blog_id, auth_token=None, - desired_class=gdata.blogger.data.BlogPageFeed, query=None, - **kwargs): - return self.get_feed(BLOG_PAGE_URL % blog_id, auth_token=auth_token, - desired_class=desired_class, query=query, **kwargs) - - GetPages = get_pages - - def get_post_comments(self, blog_id, post_id, auth_token=None, - desired_class=gdata.blogger.data.CommentFeed, - query=None, **kwargs): - return self.get_feed(BLOG_POST_COMMENTS_URL % (blog_id, post_id), - auth_token=auth_token, desired_class=desired_class, - query=query, **kwargs) - - GetPostComments = get_post_comments - - def get_blog_comments(self, blog_id, auth_token=None, - desired_class=gdata.blogger.data.CommentFeed, - query=None, **kwargs): - return self.get_feed(BLOG_COMMENTS_URL % blog_id, auth_token=auth_token, - desired_class=desired_class, query=query, **kwargs) - - GetBlogComments = get_blog_comments - - def get_blog_archive(self, blog_id, auth_token=None, **kwargs): - return self.get_feed(BLOG_ARCHIVE_URL % blog_id, auth_token=auth_token, - **kwargs) - - GetBlogArchive = get_blog_archive - - def add_post(self, blog_id, title, body, labels=None, draft=False, - auth_token=None, title_type='text', body_type='html', **kwargs): - # Construct an atom Entry for the blog post to be sent to the server. - new_entry = gdata.blogger.data.BlogPost( - title=atom.data.Title(text=title, type=title_type), - content=atom.data.Content(text=body, type=body_type)) - if labels: - for label in labels: - new_entry.add_label(label) - if draft: - new_entry.control = atom.data.Control(draft=atom.data.Draft(text='yes')) - return self.post(new_entry, BLOG_POST_URL % blog_id, auth_token=auth_token, **kwargs) - - AddPost = add_post - - def add_page(self, blog_id, title, body, draft=False, auth_token=None, - title_type='text', body_type='html', **kwargs): - new_entry = gdata.blogger.data.BlogPage( - title=atom.data.Title(text=title, type=title_type), - content=atom.data.Content(text=body, type=body_type)) - if draft: - new_entry.control = atom.data.Control(draft=atom.data.Draft(text='yes')) - return self.post(new_entry, BLOG_PAGE_URL % blog_id, auth_token=auth_token, **kwargs) - - AddPage = add_page - - def add_comment(self, blog_id, post_id, body, auth_token=None, - title_type='text', body_type='html', **kwargs): - new_entry = gdata.blogger.data.Comment( - content=atom.data.Content(text=body, type=body_type)) - return self.post(new_entry, BLOG_POST_COMMENTS_URL % (blog_id, post_id), - auth_token=auth_token, **kwargs) - - AddComment = add_comment - - def update(self, entry, auth_token=None, **kwargs): - # The Blogger API does not currently support ETags, so for now remove - # the ETag before performing an update. - old_etag = entry.etag - entry.etag = None - response = gdata.client.GDClient.update(self, entry, - auth_token=auth_token, **kwargs) - entry.etag = old_etag - return response - - Update = update - - def delete(self, entry_or_uri, auth_token=None, **kwargs): - if isinstance(entry_or_uri, (str, unicode, atom.http_core.Uri)): - return gdata.client.GDClient.delete(self, entry_or_uri, - auth_token=auth_token, **kwargs) - # The Blogger API does not currently support ETags, so for now remove - # the ETag before performing a delete. - old_etag = entry_or_uri.etag - entry_or_uri.etag = None - response = gdata.client.GDClient.delete(self, entry_or_uri, - auth_token=auth_token, **kwargs) - # TODO: if GDClient.delete raises and exception, the entry's etag may be - # left as None. Should revisit this logic. - entry_or_uri.etag = old_etag - return response - - Delete = delete - - -class Query(gdata.client.Query): - - def __init__(self, order_by=None, **kwargs): - gdata.client.Query.__init__(self, **kwargs) - self.order_by = order_by - - def modify_request(self, http_request): - gdata.client._add_query_param('orderby', self.order_by, http_request) - gdata.client.Query.modify_request(self, http_request) - - ModifyRequest = modify_request diff --git a/gdata/blogger/data.py b/gdata/blogger/data.py deleted file mode 100644 index c9a05a7b5d..0000000000 --- a/gdata/blogger/data.py +++ /dev/null @@ -1,167 +0,0 @@ -#!/usr/bin/env python -# -# Copyright (C) 2009 Google 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. - - -"""Data model classes for parsing and generating XML for the Blogger API.""" - - -__author__ = 'j.s@google.com (Jeff Scudder)' - - -import re -import atom.core -import gdata.data - - -LABEL_SCHEME = 'http://www.blogger.com/atom/ns#' -THR_TEMPLATE = '{http://purl.org/syndication/thread/1.0}%s' - -BLOG_NAME_PATTERN = re.compile('(http://)(\w*)') -BLOG_ID_PATTERN = re.compile('(tag:blogger.com,1999:blog-)(\w*)') -BLOG_ID2_PATTERN = re.compile('tag:blogger.com,1999:user-(\d+)\.blog-(\d+)') -POST_ID_PATTERN = re.compile( - '(tag:blogger.com,1999:blog-)(\w*)(.post-)(\w*)') -PAGE_ID_PATTERN = re.compile( - '(tag:blogger.com,1999:blog-)(\w*)(.page-)(\w*)') -COMMENT_ID_PATTERN = re.compile('.*-(\w*)$') - - -class BloggerEntry(gdata.data.GDEntry): - """Adds convenience methods inherited by all Blogger entries.""" - - def get_blog_id(self): - """Extracts the Blogger id of this blog. - - This method is useful when contructing URLs by hand. The blog id is - often used in blogger operation URLs. This should not be confused with - the id member of a BloggerBlog. The id element is the Atom id XML element. - The blog id which this method returns is a part of the Atom id. - - Returns: - The blog's unique id as a string. - """ - if self.id.text: - match = BLOG_ID_PATTERN.match(self.id.text) - if match: - return match.group(2) - else: - return BLOG_ID2_PATTERN.match(self.id.text).group(2) - return None - - GetBlogId = get_blog_id - - def get_blog_name(self): - """Finds the name of this blog as used in the 'alternate' URL. - - An alternate URL is in the form 'http://blogName.blogspot.com/'. For an - entry representing the above example, this method would return 'blogName'. - - Returns: - The blog's URL name component as a string. - """ - for link in self.link: - if link.rel == 'alternate': - return BLOG_NAME_PATTERN.match(link.href).group(2) - return None - - GetBlogName = get_blog_name - - -class Blog(BloggerEntry): - """Represents a blog which belongs to the user.""" - - -class BlogFeed(gdata.data.GDFeed): - entry = [Blog] - - -class BlogPost(BloggerEntry): - """Represents a single post on a blog.""" - - def add_label(self, label): - """Adds a label to the blog post. - - The label is represented by an Atom category element, so this method - is shorthand for appending a new atom.Category object. - - Args: - label: str - """ - self.category.append(atom.data.Category(scheme=LABEL_SCHEME, term=label)) - - AddLabel = add_label - - def get_post_id(self): - """Extracts the postID string from the entry's Atom id. - - Returns: A string of digits which identify this post within the blog. - """ - if self.id.text: - return POST_ID_PATTERN.match(self.id.text).group(4) - return None - - GetPostId = get_post_id - - -class BlogPostFeed(gdata.data.GDFeed): - entry = [BlogPost] - - -class BlogPage(BloggerEntry): - """Represents a single page on a blog.""" - - def get_page_id(self): - """Extracts the pageID string from entry's Atom id. - - Returns: A string of digits which identify this post within the blog. - """ - if self.id.text: - return PAGE_ID_PATTERN.match(self.id.text).group(4) - return None - - GetPageId = get_page_id - - -class BlogPageFeed(gdata.data.GDFeed): - entry = [BlogPage] - - -class InReplyTo(atom.core.XmlElement): - _qname = THR_TEMPLATE % 'in-reply-to' - href = 'href' - ref = 'ref' - source = 'source' - type = 'type' - - -class Comment(BloggerEntry): - """Blog post comment entry in a feed listing comments on a post or blog.""" - in_reply_to = InReplyTo - - def get_comment_id(self): - """Extracts the commentID string from the entry's Atom id. - - Returns: A string of digits which identify this post within the blog. - """ - if self.id.text: - return COMMENT_ID_PATTERN.match(self.id.text).group(1) - return None - - GetCommentId = get_comment_id - - -class CommentFeed(gdata.data.GDFeed): - entry = [Comment] diff --git a/gdata/blogger/service.py b/gdata/blogger/service.py deleted file mode 100644 index ad74d6329c..0000000000 --- a/gdata/blogger/service.py +++ /dev/null @@ -1,142 +0,0 @@ -#!/usr/bin/python -# -# Copyright (C) 2007 Google 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. - -"""Classes to interact with the Blogger server.""" - -__author__ = 'api.jscudder (Jeffrey Scudder)' - -import gdata.service -import gdata.blogger - - -class BloggerService(gdata.service.GDataService): - - def __init__(self, email=None, password=None, source=None, - server='www.blogger.com', **kwargs): - """Creates a client for the Blogger 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: 'www.blogger.com'. - **kwargs: The other parameters to pass to gdata.service.GDataService - constructor. - """ - gdata.service.GDataService.__init__( - self, email=email, password=password, service='blogger', source=source, - server=server, **kwargs) - - def GetBlogFeed(self, uri=None): - """Retrieve a list of the blogs to which the current user may manage.""" - if not uri: - uri = '/feeds/default/blogs' - return self.Get(uri, converter=gdata.blogger.BlogFeedFromString) - - def GetBlogCommentFeed(self, blog_id=None, uri=None): - """Retrieve a list of the comments for this blog.""" - if blog_id: - uri = '/feeds/%s/comments/default' % blog_id - return self.Get(uri, converter=gdata.blogger.CommentFeedFromString) - - def GetBlogPostFeed(self, blog_id=None, uri=None): - if blog_id: - uri = '/feeds/%s/posts/default' % blog_id - return self.Get(uri, converter=gdata.blogger.BlogPostFeedFromString) - - def GetPostCommentFeed(self, blog_id=None, post_id=None, uri=None): - """Retrieve a list of the comments for this particular blog post.""" - if blog_id and post_id: - uri = '/feeds/%s/%s/comments/default' % (blog_id, post_id) - return self.Get(uri, converter=gdata.blogger.CommentFeedFromString) - - def AddPost(self, entry, blog_id=None, uri=None): - if blog_id: - uri = '/feeds/%s/posts/default' % blog_id - return self.Post(entry, uri, - converter=gdata.blogger.BlogPostEntryFromString) - - def UpdatePost(self, entry, uri=None): - if not uri: - uri = entry.GetEditLink().href - return self.Put(entry, uri, - converter=gdata.blogger.BlogPostEntryFromString) - - def DeletePost(self, entry=None, uri=None): - if not uri: - uri = entry.GetEditLink().href - return self.Delete(uri) - - def AddComment(self, comment_entry, blog_id=None, post_id=None, uri=None): - """Adds a new comment to the specified blog post.""" - if blog_id and post_id: - uri = '/feeds/%s/%s/comments/default' % (blog_id, post_id) - return self.Post(comment_entry, uri, - converter=gdata.blogger.CommentEntryFromString) - - def DeleteComment(self, entry=None, uri=None): - if not uri: - uri = entry.GetEditLink().href - return self.Delete(uri) - - -class BlogQuery(gdata.service.Query): - - def __init__(self, feed=None, params=None, categories=None, blog_id=None): - """Constructs a query object for the list of a user's Blogger blogs. - - Args: - feed: str (optional) The beginning of the URL to be queried. If the - feed is not set, and there is no blog_id passed in, the default - value is used ('/feeds/default/blogs'). - params: dict (optional) - categories: list (optional) - blog_id: str (optional) - """ - if not feed and blog_id: - feed = '/feeds/default/blogs/%s' % blog_id - elif not feed: - feed = '/feeds/default/blogs' - gdata.service.Query.__init__(self, feed=feed, params=params, - categories=categories) - - -class BlogPostQuery(gdata.service.Query): - - def __init__(self, feed=None, params=None, categories=None, blog_id=None, - post_id=None): - if not feed and blog_id and post_id: - feed = '/feeds/%s/posts/default/%s' % (blog_id, post_id) - elif not feed and blog_id: - feed = '/feeds/%s/posts/default' % blog_id - gdata.service.Query.__init__(self, feed=feed, params=params, - categories=categories) - - -class BlogCommentQuery(gdata.service.Query): - - def __init__(self, feed=None, params=None, categories=None, blog_id=None, - post_id=None, comment_id=None): - if not feed and blog_id and comment_id: - feed = '/feeds/%s/comments/default/%s' % (blog_id, comment_id) - elif not feed and blog_id and post_id: - feed = '/feeds/%s/%s/comments/default' % (blog_id, post_id) - elif not feed and blog_id: - feed = '/feeds/%s/comments/default' % blog_id - gdata.service.Query.__init__(self, feed=feed, params=params, - categories=categories) diff --git a/gdata/books/__init__.py b/gdata/books/__init__.py deleted file mode 100644 index 1a961ab7d2..0000000000 --- a/gdata/books/__init__.py +++ /dev/null @@ -1,473 +0,0 @@ -#!/usr/bin/python - -""" - Data Models for books.service - - All classes can be instantiated from an xml string using their FromString - class method. - - Notes: - * Book.title displays the first dc:title because the returned XML - repeats that datum as atom:title. - There is an undocumented gbs:openAccess element that is not parsed. -""" - -__author__ = "James Sams " -__copyright__ = "Apache License v2.0" - -import atom -import gdata - - -BOOK_SEARCH_NAMESPACE = 'http://schemas.google.com/books/2008' -DC_NAMESPACE = 'http://purl.org/dc/terms' -ANNOTATION_REL = "http://schemas.google.com/books/2008/annotation" -INFO_REL = "http://schemas.google.com/books/2008/info" -LABEL_SCHEME = "http://schemas.google.com/books/2008/labels" -PREVIEW_REL = "http://schemas.google.com/books/2008/preview" -THUMBNAIL_REL = "http://schemas.google.com/books/2008/thumbnail" -FULL_VIEW = "http://schemas.google.com/books/2008#view_all_pages" -PARTIAL_VIEW = "http://schemas.google.com/books/2008#view_partial" -NO_VIEW = "http://schemas.google.com/books/2008#view_no_pages" -UNKNOWN_VIEW = "http://schemas.google.com/books/2008#view_unknown" -EMBEDDABLE = "http://schemas.google.com/books/2008#embeddable" -NOT_EMBEDDABLE = "http://schemas.google.com/books/2008#not_embeddable" - - - -class _AtomFromString(atom.AtomBase): - - #@classmethod - def FromString(cls, s): - return atom.CreateClassFromXMLString(cls, s) - - FromString = classmethod(FromString) - - -class Creator(_AtomFromString): - """ - The element identifies an author-or more generally, an entity - responsible for creating the volume in question. Examples of a creator - include a person, an organization, or a service. In the case of - anthologies, proceedings, or other edited works, this field may be used to - indicate editors or other entities responsible for collecting the volume's - contents. - - This element appears as a child of . If there are multiple authors or - contributors to the book, there may be multiple elements in the - volume entry (one for each creator or contributor). - """ - - _tag = 'creator' - _namespace = DC_NAMESPACE - - -class Date(_AtomFromString): #iso 8601 / W3CDTF profile - """ - The element indicates the publication date of the specific volume - in question. If the book is a reprint, this is the reprint date, not the - original publication date. The date is encoded according to the ISO-8601 - standard (and more specifically, the W3CDTF profile). - - The element can appear only as a child of . - - Usually only the year or the year and the month are given. - - YYYY-MM-DDThh:mm:ssTZD TZD = -hh:mm or +hh:mm - """ - - _tag = 'date' - _namespace = DC_NAMESPACE - - -class Description(_AtomFromString): - """ - The element includes text that describes a book or book - result. In a search result feed, this may be a search result "snippet" that - contains the words around the user's search term. For a single volume feed, - this element may contain a synopsis of the book. - - The element can appear only as a child of - """ - - _tag = 'description' - _namespace = DC_NAMESPACE - - -class Format(_AtomFromString): - """ - The element describes the physical properties of the volume. - Currently, it indicates the number of pages in the book, but more - information may be added to this field in the future. - - This element can appear only as a child of . - """ - - _tag = 'format' - _namespace = DC_NAMESPACE - - -class Identifier(_AtomFromString): - """ - The element provides an unambiguous reference to a - particular book. - * Every contains at least one child. - * The first identifier is always the unique string Book Search has assigned - to the volume (such as s1gVAAAAYAAJ). This is the ID that appears in the - book's URL in the Book Search GUI, as well as in the URL of that book's - single item feed. - * Many books contain additional elements. These provide - alternate, external identifiers to the volume. Such identifiers may - include the ISBNs, ISSNs, Library of Congress Control Numbers (LCCNs), - and OCLC numbers; they are prepended with a corresponding namespace - prefix (such as "ISBN:"). - * Any can be passed to the Dynamic Links, used to - instantiate an Embedded Viewer, or even used to construct static links to - Book Search. - The element can appear only as a child of . - """ - - _tag = 'identifier' - _namespace = DC_NAMESPACE - - -class Publisher(_AtomFromString): - """ - The element contains the name of the entity responsible for - producing and distributing the volume (usually the specific edition of this - book). Examples of a publisher include a person, an organization, or a - service. - - This element can appear only as a child of . If there is more than - one publisher, multiple elements may appear. - """ - - _tag = 'publisher' - _namespace = DC_NAMESPACE - - -class Subject(_AtomFromString): - """ - The element identifies the topic of the book. Usually this is - a Library of Congress Subject Heading (LCSH) or Book Industry Standards - and Communications Subject Heading (BISAC). - - The element can appear only as a child of . There may - be multiple elements per entry. - """ - - _tag = 'subject' - _namespace = DC_NAMESPACE - - -class Title(_AtomFromString): - """ - The element contains the title of a book as it was published. If - a book has a subtitle, it appears as a second element in the book - result's . - """ - - _tag = 'title' - _namespace = DC_NAMESPACE - - -class Viewability(_AtomFromString): - """ - Google Book Search respects the user's local copyright restrictions. As a - result, previews or full views of some books are not available in all - locations. The element indicates whether a book is fully - viewable, can be previewed, or only has "about the book" information. These - three "viewability modes" are the same ones returned by the Dynamic Links - API. - - The element can appear only as a child of . - - The value attribute will take the form of the following URIs to represent - the relevant viewing capability: - - Full View: http://schemas.google.com/books/2008#view_all_pages - Limited Preview: http://schemas.google.com/books/2008#view_partial - Snippet View/No Preview: http://schemas.google.com/books/2008#view_no_pages - Unknown view: http://schemas.google.com/books/2008#view_unknown - """ - - _tag = 'viewability' - _namespace = BOOK_SEARCH_NAMESPACE - _attributes = atom.AtomBase._attributes.copy() - _attributes['value'] = 'value' - - def __init__(self, value=None, text=None, - extension_elements=None, extension_attributes=None): - self.value = value - _AtomFromString.__init__(self, extension_elements=extension_elements, - extension_attributes=extension_attributes, text=text) - - -class Embeddability(_AtomFromString): - """ - Many of the books found on Google Book Search can be embedded on third-party - sites using the Embedded Viewer. The element indicates - whether a particular book result is available for embedding. By definition, - a book that cannot be previewed on Book Search cannot be embedded on third- - party sites. - - The element can appear only as a child of . - - The value attribute will take on one of the following URIs: - embeddable: http://schemas.google.com/books/2008#embeddable - not embeddable: http://schemas.google.com/books/2008#not_embeddable - """ - - _tag = 'embeddability' - _namespace = BOOK_SEARCH_NAMESPACE - _attributes = atom.AtomBase._attributes.copy() - _attributes['value'] = 'value' - - def __init__(self, value=None, text=None, extension_elements=None, - extension_attributes=None): - self.value = value - _AtomFromString.__init__(self, extension_elements=extension_elements, - extension_attributes=extension_attributes, text=text) - - -class Review(_AtomFromString): - """ - When present, the element contains a user-generated review for - a given book. This element currently appears only in the user library and - user annotation feeds, as a child of . - - type: text, html, xhtml - xml:lang: id of the language, a guess, (always two letters?) - """ - - _tag = 'review' - _namespace = BOOK_SEARCH_NAMESPACE - _attributes = atom.AtomBase._attributes.copy() - _attributes['type'] = 'type' - _attributes['{http://www.w3.org/XML/1998/namespace}lang'] = 'lang' - - def __init__(self, type=None, lang=None, text=None, - extension_elements=None, extension_attributes=None): - self.type = type - self.lang = lang - _AtomFromString.__init__(self, extension_elements=extension_elements, - extension_attributes=extension_attributes, text=text) - - -class Rating(_AtomFromString): - """All attributes must take an integral string between 1 and 5. - The min, max, and average attributes represent 'community' ratings. The - value attribute is the user's (of the feed from which the item is fetched, - not necessarily the authenticated user) rating of the book. - """ - - _tag = 'rating' - _namespace = gdata.GDATA_NAMESPACE - _attributes = atom.AtomBase._attributes.copy() - _attributes['min'] = 'min' - _attributes['max'] = 'max' - _attributes['average'] = 'average' - _attributes['value'] = 'value' - - def __init__(self, min=None, max=None, average=None, value=None, text=None, - extension_elements=None, extension_attributes=None): - self.min = min - self.max = max - self.average = average - self.value = value - _AtomFromString.__init__(self, extension_elements=extension_elements, - extension_attributes=extension_attributes, text=text) - - -class Book(_AtomFromString, gdata.GDataEntry): - """ - Represents an from either a search, annotation, library, or single - item feed. Note that dc_title attribute is the proper title of the volume, - title is an atom element and may not represent the full title. - """ - - _tag = 'entry' - _namespace = atom.ATOM_NAMESPACE - _children = gdata.GDataEntry._children.copy() - for i in (Creator, Identifier, Publisher, Subject,): - _children['{%s}%s' % (i._namespace, i._tag)] = (i._tag, [i]) - for i in (Date, Description, Format, Viewability, Embeddability, - Review, Rating): # Review, Rating maybe only in anno/lib entrys - _children['{%s}%s' % (i._namespace, i._tag)] = (i._tag, i) - # there is an atom title as well, should we clobber that? - del(i) - _children['{%s}%s' % (Title._namespace, Title._tag)] = ('dc_title', [Title]) - - def to_dict(self): - """Returns a dictionary of the book's available metadata. If the data - cannot be discovered, it is not included as a key in the returned dict. - The possible keys are: authors, embeddability, date, description, - format, identifiers, publishers, rating, review, subjects, title, and - viewability. - - Notes: - * Plural keys will be lists - * Singular keys will be strings - * Title, despite usually being a list, joins the title and subtitle - with a space as a single string. - * embeddability and viewability only return the portion of the URI - after # - * identifiers is a list of tuples, where the first item of each tuple - is the type of identifier and the second item is the identifying - string. Note that while doing dict() on this tuple may be possible, - some items may have multiple of the same identifier and converting - to a dict may resulted in collisions/dropped data. - * Rating returns only the user's rating. See Rating class for precise - definition. - """ - d = {} - if self.GetAnnotationLink(): - d['annotation'] = self.GetAnnotationLink().href - if self.creator: - d['authors'] = [x.text for x in self.creator] - if self.embeddability: - d['embeddability'] = self.embeddability.value.split('#')[-1] - if self.date: - d['date'] = self.date.text - if self.description: - d['description'] = self.description.text - if self.format: - d['format'] = self.format.text - if self.identifier: - d['identifiers'] = [('google_id', self.identifier[0].text)] - for x in self.identifier[1:]: - l = x.text.split(':') # should we lower the case of the ids? - d['identifiers'].append((l[0], ':'.join(l[1:]))) - if self.GetInfoLink(): - d['info'] = self.GetInfoLink().href - if self.GetPreviewLink(): - d['preview'] = self.GetPreviewLink().href - if self.publisher: - d['publishers'] = [x.text for x in self.publisher] - if self.rating: - d['rating'] = self.rating.value - if self.review: - d['review'] = self.review.text - if self.subject: - d['subjects'] = [x.text for x in self.subject] - if self.GetThumbnailLink(): - d['thumbnail'] = self.GetThumbnailLink().href - if self.dc_title: - d['title'] = ' '.join([x.text for x in self.dc_title]) - if self.viewability: - d['viewability'] = self.viewability.value.split('#')[-1] - return d - - def __init__(self, creator=None, date=None, - description=None, format=None, author=None, identifier=None, - publisher=None, subject=None, dc_title=None, viewability=None, - embeddability=None, review=None, rating=None, category=None, - content=None, contributor=None, atom_id=None, link=None, - published=None, rights=None, source=None, summary=None, - title=None, control=None, updated=None, text=None, - extension_elements=None, extension_attributes=None): - self.creator = creator - self.date = date - self.description = description - self.format = format - self.identifier = identifier - self.publisher = publisher - self.subject = subject - self.dc_title = dc_title or [] - self.viewability = viewability - self.embeddability = embeddability - self.review = review - self.rating = rating - gdata.GDataEntry.__init__(self, author=author, category=category, - content=content, contributor=contributor, atom_id=atom_id, - link=link, published=published, rights=rights, source=source, - summary=summary, title=title, control=control, updated=updated, - text=text, extension_elements=extension_elements, - extension_attributes=extension_attributes) - - def GetThumbnailLink(self): - """Returns the atom.Link object representing the thumbnail URI.""" - for i in self.link: - if i.rel == THUMBNAIL_REL: - return i - - def GetInfoLink(self): - """ - Returns the atom.Link object representing the human-readable info URI. - """ - for i in self.link: - if i.rel == INFO_REL: - return i - - def GetPreviewLink(self): - """Returns the atom.Link object representing the preview URI.""" - for i in self.link: - if i.rel == PREVIEW_REL: - return i - - def GetAnnotationLink(self): - """ - Returns the atom.Link object representing the Annotation URI. - Note that the use of www.books in the href of this link seems to make - this information useless. Using books.service.ANNOTATION_FEED and - BOOK_SERVER to construct your URI seems to work better. - """ - for i in self.link: - if i.rel == ANNOTATION_REL: - return i - - def set_rating(self, value): - """Set user's rating. Must be an integral string between 1 nad 5""" - assert (value in ('1','2','3','4','5')) - if not isinstance(self.rating, Rating): - self.rating = Rating() - self.rating.value = value - - def set_review(self, text, type='text', lang='en'): - """Set user's review text""" - self.review = Review(text=text, type=type, lang=lang) - - def get_label(self): - """Get users label for the item as a string""" - for i in self.category: - if i.scheme == LABEL_SCHEME: - return i.term - - def set_label(self, term): - """Clear pre-existing label for the item and set term as the label.""" - self.remove_label() - self.category.append(atom.Category(term=term, scheme=LABEL_SCHEME)) - - def remove_label(self): - """Clear the user's label for the item""" - ln = len(self.category) - for i, j in enumerate(self.category[::-1]): - if j.scheme == LABEL_SCHEME: - del(self.category[ln-1-i]) - - def clean_annotations(self): - """Clear all annotations from an item. Useful for taking an item from - another user's library/annotation feed and adding it to the - authenticated user's library without adopting annotations.""" - self.remove_label() - self.review = None - self.rating = None - - - def get_google_id(self): - """Get Google's ID of the item.""" - return self.id.text.split('/')[-1] - - -class BookFeed(_AtomFromString, gdata.GDataFeed): - """Represents a feed of entries from a search.""" - - _tag = 'feed' - _namespace = atom.ATOM_NAMESPACE - _children = gdata.GDataFeed._children.copy() - _children['{%s}%s' % (Book._namespace, Book._tag)] = (Book._tag, [Book]) - - -if __name__ == '__main__': - import doctest - doctest.testfile('datamodels.txt') diff --git a/gdata/books/data.py b/gdata/books/data.py deleted file mode 100644 index 3f7f978b34..0000000000 --- a/gdata/books/data.py +++ /dev/null @@ -1,90 +0,0 @@ -#!/usr/bin/python -# -# Copyright (C) 2009 Google 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. - -"""Contains the data classes of the Google Book Search Data API""" - - -__author__ = 'j.s@google.com (Jeff Scudder)' - - -import atom.core -import atom.data -import gdata.data -import gdata.dublincore.data -import gdata.opensearch.data - - -GBS_TEMPLATE = '{http://schemas.google.com/books/2008/}%s' - - -class CollectionEntry(gdata.data.GDEntry): - """Describes an entry in a feed of collections.""" - - -class CollectionFeed(gdata.data.BatchFeed): - """Describes a Book Search collection feed.""" - entry = [CollectionEntry] - - -class Embeddability(atom.core.XmlElement): - """Describes an embeddability.""" - _qname = GBS_TEMPLATE % 'embeddability' - value = 'value' - - -class OpenAccess(atom.core.XmlElement): - """Describes an open access.""" - _qname = GBS_TEMPLATE % 'openAccess' - value = 'value' - - -class Review(atom.core.XmlElement): - """User-provided review.""" - _qname = GBS_TEMPLATE % 'review' - lang = 'lang' - type = 'type' - - -class Viewability(atom.core.XmlElement): - """Describes a viewability.""" - _qname = GBS_TEMPLATE % 'viewability' - value = 'value' - - -class VolumeEntry(gdata.data.GDEntry): - """Describes an entry in a feed of Book Search volumes.""" - comments = gdata.data.Comments - language = [gdata.dublincore.data.Language] - open_access = OpenAccess - format = [gdata.dublincore.data.Format] - dc_title = [gdata.dublincore.data.Title] - viewability = Viewability - embeddability = Embeddability - creator = [gdata.dublincore.data.Creator] - rating = gdata.data.Rating - description = [gdata.dublincore.data.Description] - publisher = [gdata.dublincore.data.Publisher] - date = [gdata.dublincore.data.Date] - subject = [gdata.dublincore.data.Subject] - identifier = [gdata.dublincore.data.Identifier] - review = Review - - -class VolumeFeed(gdata.data.BatchFeed): - """Describes a Book Search volume feed.""" - entry = [VolumeEntry] - - diff --git a/gdata/books/service.py b/gdata/books/service.py deleted file mode 100644 index cbb846fcd4..0000000000 --- a/gdata/books/service.py +++ /dev/null @@ -1,266 +0,0 @@ -#!/usr/bin/python - -""" - Extend gdata.service.GDataService to support authenticated CRUD ops on - Books API - - http://code.google.com/apis/books/docs/getting-started.html - http://code.google.com/apis/books/docs/gdata/developers_guide_protocol.html - - TODO: (here and __init__) - * search based on label, review, or other annotations (possible?) - * edit (specifically, Put requests) seem to fail effect a change - - Problems With API: - * Adding a book with a review to the library adds a note, not a review. - This does not get included in the returned item. You see this by - looking at My Library through the website. - * Editing a review never edits a review (unless it is freshly added, but - see above). More generally, - * a Put request with changed annotations (label/rating/review) does NOT - change the data. Note: Put requests only work on the href from - GetEditLink (as per the spec). Do not try to PUT to the annotate or - library feeds, this will cause a 400 Invalid URI Bad Request response. - Attempting to Post to one of the feeds with the updated annotations - does not update them. See the following for (hopefully) a follow up: - google.com/support/forum/p/booksearch-apis/thread?tid=27fd7f68de438fc8 - * Attempts to workaround the edit problem continue to fail. For example, - removing the item, editing the data, readding the item, gives us only - our originally added data (annotations). This occurs even if we - completely shut python down, refetch the book from the public feed, - and re-add it. There is some kind of persistence going on that I - cannot change. This is likely due to the annotations being cached in - the annotation feed and the inability to edit (see Put, above) - * GetAnnotationLink has www.books.... as the server, but hitting www... - results in a bad URI error. - * Spec indicates there may be multiple labels, but there does not seem - to be a way to get the server to accept multiple labels, nor does the - web interface have an obvious way to have multiple labels. Multiple - labels are never returned. -""" - -__author__ = "James Sams " -__copyright__ = "Apache License v2.0" - -from shlex import split - -import gdata.service -try: - import books -except ImportError: - import gdata.books as books - - -BOOK_SERVER = "books.google.com" -GENERAL_FEED = "/books/feeds/volumes" -ITEM_FEED = "/books/feeds/volumes/" -LIBRARY_FEED = "/books/feeds/users/%s/collections/library/volumes" -ANNOTATION_FEED = "/books/feeds/users/%s/volumes" -PARTNER_FEED = "/books/feeds/p/%s/volumes" -BOOK_SERVICE = "print" -ACCOUNT_TYPE = "HOSTED_OR_GOOGLE" - - -class BookService(gdata.service.GDataService): - - def __init__(self, email=None, password=None, source=None, - server=BOOK_SERVER, account_type=ACCOUNT_TYPE, - exception_handlers=tuple(), **kwargs): - """source should be of form 'ProgramCompany - ProgramName - Version'""" - - gdata.service.GDataService.__init__(self, email=email, - password=password, service=BOOK_SERVICE, source=source, - server=server, **kwargs) - self.exception_handlers = exception_handlers - - def search(self, q, start_index="1", max_results="10", - min_viewability="none", feed=GENERAL_FEED, - converter=books.BookFeed.FromString): - """ - Query the Public search feed. q is either a search string or a - gdata.service.Query instance with a query set. - - min_viewability must be "none", "partial", or "full". - - If you change the feed to a single item feed, note that you will - probably need to change the converter to be Book.FromString - """ - - if not isinstance(q, gdata.service.Query): - q = gdata.service.Query(text_query=q) - if feed: - q.feed = feed - q['start-index'] = start_index - q['max-results'] = max_results - q['min-viewability'] = min_viewability - return self.Get(uri=q.ToUri(),converter=converter) - - def search_by_keyword(self, q='', feed=GENERAL_FEED, start_index="1", - max_results="10", min_viewability="none", **kwargs): - """ - Query the Public Search Feed by keyword. Non-keyword strings can be - set in q. This is quite fragile. Is there a function somewhere in - the Google library that will parse a query the same way that Google - does? - - Legal Identifiers are listed below and correspond to their meaning - at http://books.google.com/advanced_book_search: - all_words - exact_phrase - at_least_one - without_words - title - author - publisher - subject - isbn - lccn - oclc - seemingly unsupported: - publication_date: a sequence of two, two tuples: - ((min_month,min_year),(max_month,max_year)) - where month is one/two digit month, year is 4 digit, eg: - (('1','2000'),('10','2003')). Lower bound is inclusive, - upper bound is exclusive - """ - - for k, v in kwargs.items(): - if not v: - continue - k = k.lower() - if k == 'all_words': - q = "%s %s" % (q, v) - elif k == 'exact_phrase': - q = '%s "%s"' % (q, v.strip('"')) - elif k == 'at_least_one': - q = '%s %s' % (q, ' '.join(['OR "%s"' % x for x in split(v)])) - elif k == 'without_words': - q = '%s %s' % (q, ' '.join(['-"%s"' % x for x in split(v)])) - elif k in ('author','title', 'publisher'): - q = '%s %s' % (q, ' '.join(['in%s:"%s"'%(k,x) for x in split(v)])) - elif k == 'subject': - q = '%s %s' % (q, ' '.join(['%s:"%s"' % (k,x) for x in split(v)])) - elif k == 'isbn': - q = '%s ISBN%s' % (q, v) - elif k == 'issn': - q = '%s ISSN%s' % (q,v) - elif k == 'oclc': - q = '%s OCLC%s' % (q,v) - else: - raise ValueError("Unsupported search keyword") - return self.search(q.strip(),start_index=start_index, feed=feed, - max_results=max_results, - min_viewability=min_viewability) - - def search_library(self, q, id='me', **kwargs): - """Like search, but in a library feed. Default is the authenticated - user's feed. Change by setting id.""" - - if 'feed' in kwargs: - raise ValueError("kwarg 'feed' conflicts with library_id") - feed = LIBRARY_FEED % id - return self.search(q, feed=feed, **kwargs) - - def search_library_by_keyword(self, id='me', **kwargs): - """Hybrid of search_by_keyword and search_library - """ - - if 'feed' in kwargs: - raise ValueError("kwarg 'feed' conflicts with library_id") - feed = LIBRARY_FEED % id - return self.search_by_keyword(feed=feed,**kwargs) - - def search_annotations(self, q, id='me', **kwargs): - """Like search, but in an annotation feed. Default is the authenticated - user's feed. Change by setting id.""" - - if 'feed' in kwargs: - raise ValueError("kwarg 'feed' conflicts with library_id") - feed = ANNOTATION_FEED % id - return self.search(q, feed=feed, **kwargs) - - def search_annotations_by_keyword(self, id='me', **kwargs): - """Hybrid of search_by_keyword and search_annotations - """ - - if 'feed' in kwargs: - raise ValueError("kwarg 'feed' conflicts with library_id") - feed = ANNOTATION_FEED % id - return self.search_by_keyword(feed=feed,**kwargs) - - def add_item_to_library(self, item): - """Add the item, either an XML string or books.Book instance, to the - user's library feed""" - - feed = LIBRARY_FEED % 'me' - return self.Post(data=item, uri=feed, converter=books.Book.FromString) - - def remove_item_from_library(self, item): - """ - Remove the item, a books.Book instance, from the authenticated user's - library feed. Using an item retrieved from a public search will fail. - """ - - return self.Delete(item.GetEditLink().href) - - def add_annotation(self, item): - """ - Add the item, either an XML string or books.Book instance, to the - user's annotation feed. - """ - # do not use GetAnnotationLink, results in 400 Bad URI due to www - return self.Post(data=item, uri=ANNOTATION_FEED % 'me', - converter=books.Book.FromString) - - def edit_annotation(self, item): - """ - Send an edited item, a books.Book instance, to the user's annotation - feed. Note that whereas extra annotations in add_annotations, minus - ratings which are immutable once set, are simply added to the item in - the annotation feed, if an annotation has been removed from the item, - sending an edit request will remove that annotation. This should not - happen with add_annotation. - """ - - return self.Put(data=item, uri=item.GetEditLink().href, - converter=books.Book.FromString) - - def get_by_google_id(self, id): - return self.Get(ITEM_FEED + id, converter=books.Book.FromString) - - def get_library(self, id='me',feed=LIBRARY_FEED, start_index="1", - max_results="100", min_viewability="none", - converter=books.BookFeed.FromString): - """ - Return a generator object that will return gbook.Book instances until - the search feed no longer returns an item from the GetNextLink method. - Thus max_results is not the maximum number of items that will be - returned, but rather the number of items per page of searches. This has - been set high to reduce the required number of network requests. - """ - - q = gdata.service.Query() - q.feed = feed % id - q['start-index'] = start_index - q['max-results'] = max_results - q['min-viewability'] = min_viewability - x = self.Get(uri=q.ToUri(), converter=converter) - while 1: - for entry in x.entry: - yield entry - else: - l = x.GetNextLink() - if l: # hope the server preserves our preferences - x = self.Get(uri=l.href, converter=converter) - else: - break - - def get_annotations(self, id='me', start_index="1", max_results="100", - min_viewability="none", converter=books.BookFeed.FromString): - """ - Like get_library, but for the annotation feed - """ - - return self.get_library(id=id, feed=ANNOTATION_FEED, - max_results=max_results, min_viewability = min_viewability, - converter=converter) diff --git a/gdata/calendar/__init__.py b/gdata/calendar/__init__.py deleted file mode 100644 index 06c041075a..0000000000 --- a/gdata/calendar/__init__.py +++ /dev/null @@ -1,1044 +0,0 @@ -#!/usr/bin/python -# -# Copyright (C) 2006 Google 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. - - -"""Contains extensions to ElementWrapper objects used with Google Calendar.""" - - -__author__ = 'api.vli (Vivian Li), api.rboyd (Ryan Boyd)' - - -try: - from xml.etree import cElementTree as ElementTree -except ImportError: - try: - import cElementTree as ElementTree - except ImportError: - try: - from xml.etree import ElementTree - except ImportError: - from elementtree import ElementTree -import atom -import gdata - - -# XML namespaces which are often used in Google Calendar entities. -GCAL_NAMESPACE = 'http://schemas.google.com/gCal/2005' -GCAL_TEMPLATE = '{http://schemas.google.com/gCal/2005}%s' -WEB_CONTENT_LINK_REL = '%s/%s' % (GCAL_NAMESPACE, 'webContent') -GACL_NAMESPACE = gdata.GACL_NAMESPACE -GACL_TEMPLATE = gdata.GACL_TEMPLATE - - - -class ValueAttributeContainer(atom.AtomBase): - """A parent class for all Calendar classes which have a value attribute. - - Children include Color, AccessLevel, Hidden - """ - - _children = atom.AtomBase._children.copy() - _attributes = atom.AtomBase._attributes.copy() - _attributes['value'] = 'value' - - def __init__(self, value=None, extension_elements=None, - extension_attributes=None, text=None): - self.value = value - self.text = text - self.extension_elements = extension_elements or [] - self.extension_attributes = extension_attributes or {} - -class Color(ValueAttributeContainer): - """The Google Calendar color element""" - - _tag = 'color' - _namespace = GCAL_NAMESPACE - _children = ValueAttributeContainer._children.copy() - _attributes = ValueAttributeContainer._attributes.copy() - - - -class AccessLevel(ValueAttributeContainer): - """The Google Calendar accesslevel element""" - - _tag = 'accesslevel' - _namespace = GCAL_NAMESPACE - _children = ValueAttributeContainer._children.copy() - _attributes = ValueAttributeContainer._attributes.copy() - - -class Hidden(ValueAttributeContainer): - """The Google Calendar hidden element""" - - _tag = 'hidden' - _namespace = GCAL_NAMESPACE - _children = ValueAttributeContainer._children.copy() - _attributes = ValueAttributeContainer._attributes.copy() - - -class Selected(ValueAttributeContainer): - """The Google Calendar selected element""" - - _tag = 'selected' - _namespace = GCAL_NAMESPACE - _children = ValueAttributeContainer._children.copy() - _attributes = ValueAttributeContainer._attributes.copy() - - -class Timezone(ValueAttributeContainer): - """The Google Calendar timezone element""" - - _tag = 'timezone' - _namespace = GCAL_NAMESPACE - _children = ValueAttributeContainer._children.copy() - _attributes = ValueAttributeContainer._attributes.copy() - - -class Where(atom.AtomBase): - """The Google Calendar Where element""" - - _tag = 'where' - _namespace = gdata.GDATA_NAMESPACE - _children = atom.AtomBase._children.copy() - _attributes = atom.AtomBase._attributes.copy() - _attributes['valueString'] = 'value_string' - - def __init__(self, value_string=None, extension_elements=None, - extension_attributes=None, text=None): - self.value_string = value_string - self.text = text - self.extension_elements = extension_elements or [] - self.extension_attributes = extension_attributes or {} - - -class CalendarListEntry(gdata.GDataEntry, gdata.LinkFinder): - """A Google Calendar meta Entry flavor of an Atom Entry """ - - _tag = gdata.GDataEntry._tag - _namespace = gdata.GDataEntry._namespace - _children = gdata.GDataEntry._children.copy() - _attributes = gdata.GDataEntry._attributes.copy() - _children['{%s}color' % GCAL_NAMESPACE] = ('color', Color) - _children['{%s}accesslevel' % GCAL_NAMESPACE] = ('access_level', - AccessLevel) - _children['{%s}hidden' % GCAL_NAMESPACE] = ('hidden', Hidden) - _children['{%s}selected' % GCAL_NAMESPACE] = ('selected', Selected) - _children['{%s}timezone' % GCAL_NAMESPACE] = ('timezone', Timezone) - _children['{%s}where' % gdata.GDATA_NAMESPACE] = ('where', Where) - - def __init__(self, author=None, category=None, content=None, - atom_id=None, link=None, published=None, - title=None, updated=None, - color=None, access_level=None, hidden=None, timezone=None, - selected=None, - where=None, - extension_elements=None, extension_attributes=None, text=None): - gdata.GDataEntry.__init__(self, author=author, category=category, - content=content, atom_id=atom_id, link=link, - published=published, title=title, - updated=updated, text=None) - - self.color = color - self.access_level = access_level - self.hidden = hidden - self.selected = selected - self.timezone = timezone - self.where = where - - -class CalendarListFeed(gdata.GDataFeed, gdata.LinkFinder): - """A Google Calendar meta feed flavor of an Atom Feed""" - - _tag = gdata.GDataFeed._tag - _namespace = gdata.GDataFeed._namespace - _children = gdata.GDataFeed._children.copy() - _attributes = gdata.GDataFeed._attributes.copy() - _children['{%s}entry' % atom.ATOM_NAMESPACE] = ('entry', [CalendarListEntry]) - - -class Scope(atom.AtomBase): - """The Google ACL scope element""" - - _tag = 'scope' - _namespace = GACL_NAMESPACE - _children = atom.AtomBase._children.copy() - _attributes = atom.AtomBase._attributes.copy() - _attributes['value'] = 'value' - _attributes['type'] = 'type' - - def __init__(self, extension_elements=None, value=None, scope_type=None, - extension_attributes=None, text=None): - self.value = value - self.type = scope_type - self.text = text - self.extension_elements = extension_elements or [] - self.extension_attributes = extension_attributes or {} - - -class Role(ValueAttributeContainer): - """The Google Calendar timezone element""" - - _tag = 'role' - _namespace = GACL_NAMESPACE - _children = ValueAttributeContainer._children.copy() - _attributes = ValueAttributeContainer._attributes.copy() - - -class CalendarAclEntry(gdata.GDataEntry, gdata.LinkFinder): - """A Google Calendar ACL Entry flavor of an Atom Entry """ - - _tag = gdata.GDataEntry._tag - _namespace = gdata.GDataEntry._namespace - _children = gdata.GDataEntry._children.copy() - _attributes = gdata.GDataEntry._attributes.copy() - _children['{%s}scope' % GACL_NAMESPACE] = ('scope', Scope) - _children['{%s}role' % GACL_NAMESPACE] = ('role', Role) - - def __init__(self, author=None, category=None, content=None, - atom_id=None, link=None, published=None, - title=None, updated=None, - scope=None, role=None, - extension_elements=None, extension_attributes=None, text=None): - gdata.GDataEntry.__init__(self, author=author, category=category, - content=content, atom_id=atom_id, link=link, - published=published, title=title, - updated=updated, text=None) - self.scope = scope - self.role = role - - -class CalendarAclFeed(gdata.GDataFeed, gdata.LinkFinder): - """A Google Calendar ACL feed flavor of an Atom Feed""" - - _tag = gdata.GDataFeed._tag - _namespace = gdata.GDataFeed._namespace - _children = gdata.GDataFeed._children.copy() - _attributes = gdata.GDataFeed._attributes.copy() - _children['{%s}entry' % atom.ATOM_NAMESPACE] = ('entry', [CalendarAclEntry]) - - -class CalendarEventCommentEntry(gdata.GDataEntry, gdata.LinkFinder): - """A Google Calendar event comments entry flavor of an Atom Entry""" - - _tag = gdata.GDataEntry._tag - _namespace = gdata.GDataEntry._namespace - _children = gdata.GDataEntry._children.copy() - _attributes = gdata.GDataEntry._attributes.copy() - - -class CalendarEventCommentFeed(gdata.GDataFeed, gdata.LinkFinder): - """A Google Calendar event comments feed flavor of an Atom Feed""" - - _tag = gdata.GDataFeed._tag - _namespace = gdata.GDataFeed._namespace - _children = gdata.GDataFeed._children.copy() - _attributes = gdata.GDataFeed._attributes.copy() - _children['{%s}entry' % atom.ATOM_NAMESPACE] = ('entry', - [CalendarEventCommentEntry]) - - -class ExtendedProperty(gdata.ExtendedProperty): - """A transparent subclass of gdata.ExtendedProperty added to this module - for backwards compatibility.""" - - -class Reminder(atom.AtomBase): - """The Google Calendar reminder element""" - - _tag = 'reminder' - _namespace = gdata.GDATA_NAMESPACE - _children = atom.AtomBase._children.copy() - _attributes = atom.AtomBase._attributes.copy() - _attributes['absoluteTime'] = 'absolute_time' - _attributes['days'] = 'days' - _attributes['hours'] = 'hours' - _attributes['minutes'] = 'minutes' - _attributes['method'] = 'method' - - def __init__(self, absolute_time=None, - days=None, hours=None, minutes=None, method=None, - extension_elements=None, - extension_attributes=None, text=None): - self.absolute_time = absolute_time - if days is not None: - self.days = str(days) - else: - self.days = None - if hours is not None: - self.hours = str(hours) - else: - self.hours = None - if minutes is not None: - self.minutes = str(minutes) - else: - self.minutes = None - self.method = method - self.text = text - self.extension_elements = extension_elements or [] - self.extension_attributes = extension_attributes or {} - - -class When(atom.AtomBase): - """The Google Calendar When element""" - - _tag = 'when' - _namespace = gdata.GDATA_NAMESPACE - _children = atom.AtomBase._children.copy() - _attributes = atom.AtomBase._attributes.copy() - _children['{%s}reminder' % gdata.GDATA_NAMESPACE] = ('reminder', [Reminder]) - _attributes['startTime'] = 'start_time' - _attributes['endTime'] = 'end_time' - - def __init__(self, start_time=None, end_time=None, reminder=None, - extension_elements=None, extension_attributes=None, text=None): - self.start_time = start_time - self.end_time = end_time - self.reminder = reminder or [] - self.text = text - self.extension_elements = extension_elements or [] - self.extension_attributes = extension_attributes or {} - - -class Recurrence(atom.AtomBase): - """The Google Calendar Recurrence element""" - - _tag = 'recurrence' - _namespace = gdata.GDATA_NAMESPACE - _children = atom.AtomBase._children.copy() - _attributes = atom.AtomBase._attributes.copy() - - -class UriEnumElement(atom.AtomBase): - - _children = atom.AtomBase._children.copy() - _attributes = atom.AtomBase._attributes.copy() - - def __init__(self, tag, enum_map, attrib_name='value', - extension_elements=None, extension_attributes=None, text=None): - self.tag=tag - self.enum_map=enum_map - self.attrib_name=attrib_name - self.value=None - self.text=text - self.extension_elements = extension_elements or [] - self.extension_attributes = extension_attributes or {} - - def findKey(self, value): - res=[item[0] for item in self.enum_map.items() if item[1] == value] - if res is None or len(res) == 0: - return None - return res[0] - - def _ConvertElementAttributeToMember(self, attribute, value): - # Special logic to use the enum_map to set the value of the object's value member. - if attribute == self.attrib_name and value != '': - self.value = self.enum_map[value] - return - # Find the attribute in this class's list of attributes. - if self.__class__._attributes.has_key(attribute): - # Find the member of this class which corresponds to the XML attribute - # (lookup in current_class._attributes) and set this member to the - # desired value (using self.__dict__). - setattr(self, self.__class__._attributes[attribute], value) - else: - # The current class doesn't map this attribute, so try to parent class. - atom.ExtensionContainer._ConvertElementAttributeToMember(self, - attribute, - value) - - def _AddMembersToElementTree(self, tree): - # Convert the members of this class which are XML child nodes. - # This uses the class's _children dictionary to find the members which - # should become XML child nodes. - member_node_names = [values[0] for tag, values in - self.__class__._children.iteritems()] - for member_name in member_node_names: - member = getattr(self, member_name) - if member is None: - pass - elif isinstance(member, list): - for instance in member: - instance._BecomeChildElement(tree) - else: - member._BecomeChildElement(tree) - # Special logic to set the desired XML attribute. - key = self.findKey(self.value) - if key is not None: - tree.attrib[self.attrib_name]=key - # Convert the members of this class which are XML attributes. - for xml_attribute, member_name in self.__class__._attributes.iteritems(): - member = getattr(self, member_name) - if member is not None: - tree.attrib[xml_attribute] = member - # Lastly, call the parent's _AddMembersToElementTree to get any - # extension elements. - atom.ExtensionContainer._AddMembersToElementTree(self, tree) - - - -class AttendeeStatus(UriEnumElement): - """The Google Calendar attendeeStatus element""" - - _tag = 'attendeeStatus' - _namespace = gdata.GDATA_NAMESPACE - _children = UriEnumElement._children.copy() - _attributes = UriEnumElement._attributes.copy() - - attendee_enum = { - 'http://schemas.google.com/g/2005#event.accepted' : 'ACCEPTED', - 'http://schemas.google.com/g/2005#event.declined' : 'DECLINED', - 'http://schemas.google.com/g/2005#event.invited' : 'INVITED', - 'http://schemas.google.com/g/2005#event.tentative' : 'TENTATIVE'} - - def __init__(self, extension_elements=None, - extension_attributes=None, text=None): - UriEnumElement.__init__(self, 'attendeeStatus', AttendeeStatus.attendee_enum, - extension_elements=extension_elements, - extension_attributes=extension_attributes, - text=text) - - -class AttendeeType(UriEnumElement): - """The Google Calendar attendeeType element""" - - _tag = 'attendeeType' - _namespace = gdata.GDATA_NAMESPACE - _children = UriEnumElement._children.copy() - _attributes = UriEnumElement._attributes.copy() - - attendee_type_enum = { - 'http://schemas.google.com/g/2005#event.optional' : 'OPTIONAL', - 'http://schemas.google.com/g/2005#event.required' : 'REQUIRED' } - - def __init__(self, extension_elements=None, - extension_attributes=None, text=None): - UriEnumElement.__init__(self, 'attendeeType', - AttendeeType.attendee_type_enum, - extension_elements=extension_elements, - extension_attributes=extension_attributes,text=text) - - -class Visibility(UriEnumElement): - """The Google Calendar Visibility element""" - - _tag = 'visibility' - _namespace = gdata.GDATA_NAMESPACE - _children = UriEnumElement._children.copy() - _attributes = UriEnumElement._attributes.copy() - - visibility_enum = { - 'http://schemas.google.com/g/2005#event.confidential' : 'CONFIDENTIAL', - 'http://schemas.google.com/g/2005#event.default' : 'DEFAULT', - 'http://schemas.google.com/g/2005#event.private' : 'PRIVATE', - 'http://schemas.google.com/g/2005#event.public' : 'PUBLIC' } - - def __init__(self, extension_elements=None, - extension_attributes=None, text=None): - UriEnumElement.__init__(self, 'visibility', Visibility.visibility_enum, - extension_elements=extension_elements, - extension_attributes=extension_attributes, - text=text) - - -class Transparency(UriEnumElement): - """The Google Calendar Transparency element""" - - _tag = 'transparency' - _namespace = gdata.GDATA_NAMESPACE - _children = UriEnumElement._children.copy() - _attributes = UriEnumElement._attributes.copy() - - transparency_enum = { - 'http://schemas.google.com/g/2005#event.opaque' : 'OPAQUE', - 'http://schemas.google.com/g/2005#event.transparent' : 'TRANSPARENT' } - - def __init__(self, extension_elements=None, - extension_attributes=None, text=None): - UriEnumElement.__init__(self, tag='transparency', - enum_map=Transparency.transparency_enum, - extension_elements=extension_elements, - extension_attributes=extension_attributes, - text=text) - - -class Comments(atom.AtomBase): - """The Google Calendar comments element""" - - _tag = 'comments' - _namespace = gdata.GDATA_NAMESPACE - _children = atom.AtomBase._children.copy() - _attributes = atom.AtomBase._attributes.copy() - _children['{%s}feedLink' % gdata.GDATA_NAMESPACE] = ('feed_link', - gdata.FeedLink) - _attributes['rel'] = 'rel' - - def __init__(self, rel=None, feed_link=None, extension_elements=None, - extension_attributes=None, text=None): - self.rel = rel - self.feed_link = feed_link - self.text = text - self.extension_elements = extension_elements or [] - self.extension_attributes = extension_attributes or {} - - -class EventStatus(UriEnumElement): - """The Google Calendar eventStatus element""" - - _tag = 'eventStatus' - _namespace = gdata.GDATA_NAMESPACE - _children = UriEnumElement._children.copy() - _attributes = UriEnumElement._attributes.copy() - - status_enum = { 'http://schemas.google.com/g/2005#event.canceled' : 'CANCELED', - 'http://schemas.google.com/g/2005#event.confirmed' : 'CONFIRMED', - 'http://schemas.google.com/g/2005#event.tentative' : 'TENTATIVE'} - - def __init__(self, extension_elements=None, - extension_attributes=None, text=None): - UriEnumElement.__init__(self, tag='eventStatus', - enum_map=EventStatus.status_enum, - extension_elements=extension_elements, - extension_attributes=extension_attributes, - text=text) - - -class Who(UriEnumElement): - """The Google Calendar Who element""" - - _tag = 'who' - _namespace = gdata.GDATA_NAMESPACE - _children = UriEnumElement._children.copy() - _attributes = UriEnumElement._attributes.copy() - _children['{%s}attendeeStatus' % gdata.GDATA_NAMESPACE] = ( - 'attendee_status', AttendeeStatus) - _children['{%s}attendeeType' % gdata.GDATA_NAMESPACE] = ('attendee_type', - AttendeeType) - _attributes['valueString'] = 'name' - _attributes['email'] = 'email' - - relEnum = { 'http://schemas.google.com/g/2005#event.attendee' : 'ATTENDEE', - 'http://schemas.google.com/g/2005#event.organizer' : 'ORGANIZER', - 'http://schemas.google.com/g/2005#event.performer' : 'PERFORMER', - 'http://schemas.google.com/g/2005#event.speaker' : 'SPEAKER', - 'http://schemas.google.com/g/2005#message.bcc' : 'BCC', - 'http://schemas.google.com/g/2005#message.cc' : 'CC', - 'http://schemas.google.com/g/2005#message.from' : 'FROM', - 'http://schemas.google.com/g/2005#message.reply-to' : 'REPLY_TO', - 'http://schemas.google.com/g/2005#message.to' : 'TO' } - - def __init__(self, name=None, email=None, attendee_status=None, - attendee_type=None, rel=None, extension_elements=None, - extension_attributes=None, text=None): - UriEnumElement.__init__(self, 'who', Who.relEnum, attrib_name='rel', - extension_elements=extension_elements, - extension_attributes=extension_attributes, - text=text) - self.name = name - self.email = email - self.attendee_status = attendee_status - self.attendee_type = attendee_type - self.rel = rel - - -class OriginalEvent(atom.AtomBase): - """The Google Calendar OriginalEvent element""" - - _tag = 'originalEvent' - _namespace = gdata.GDATA_NAMESPACE - _children = atom.AtomBase._children.copy() - _attributes = atom.AtomBase._attributes.copy() - # TODO: The when tag used to map to a EntryLink, make sure it should really be a When. - _children['{%s}when' % gdata.GDATA_NAMESPACE] = ('when', When) - _attributes['id'] = 'id' - _attributes['href'] = 'href' - - def __init__(self, id=None, href=None, when=None, - extension_elements=None, extension_attributes=None, text=None): - self.id = id - self.href = href - self.when = when - self.text = text - self.extension_elements = extension_elements or [] - self.extension_attributes = extension_attributes or {} - - -def GetCalendarEventEntryClass(): - return CalendarEventEntry - - -# This class is not completely defined here, because of a circular reference -# in which CalendarEventEntryLink and CalendarEventEntry refer to one another. -class CalendarEventEntryLink(gdata.EntryLink): - """An entryLink which contains a calendar event entry - - Within an event's recurranceExceptions, an entry link - points to a calendar event entry. This class exists - to capture the calendar specific extensions in the entry. - """ - - _tag = 'entryLink' - _namespace = gdata.GDATA_NAMESPACE - _children = gdata.EntryLink._children.copy() - _attributes = gdata.EntryLink._attributes.copy() - # The CalendarEventEntryLink should like CalendarEventEntry as a child but - # that class hasn't been defined yet, so we will wait until after defining - # CalendarEventEntry to list it in _children. - - -class RecurrenceException(atom.AtomBase): - """The Google Calendar RecurrenceException element""" - - _tag = 'recurrenceException' - _namespace = gdata.GDATA_NAMESPACE - _children = atom.AtomBase._children.copy() - _attributes = atom.AtomBase._attributes.copy() - _children['{%s}entryLink' % gdata.GDATA_NAMESPACE] = ('entry_link', - CalendarEventEntryLink) - _children['{%s}originalEvent' % gdata.GDATA_NAMESPACE] = ('original_event', - OriginalEvent) - _attributes['specialized'] = 'specialized' - - def __init__(self, specialized=None, entry_link=None, - original_event=None, extension_elements=None, - extension_attributes=None, text=None): - self.specialized = specialized - self.entry_link = entry_link - self.original_event = original_event - self.text = text - self.extension_elements = extension_elements or [] - self.extension_attributes = extension_attributes or {} - - -class SendEventNotifications(atom.AtomBase): - """The Google Calendar sendEventNotifications element""" - - _tag = 'sendEventNotifications' - _namespace = GCAL_NAMESPACE - _children = atom.AtomBase._children.copy() - _attributes = atom.AtomBase._attributes.copy() - _attributes['value'] = 'value' - - def __init__(self, extension_elements=None, - value=None, extension_attributes=None, text=None): - self.value = value - self.text = text - self.extension_elements = extension_elements or [] - self.extension_attributes = extension_attributes or {} - - -class QuickAdd(atom.AtomBase): - """The Google Calendar quickadd element""" - - _tag = 'quickadd' - _namespace = GCAL_NAMESPACE - _children = atom.AtomBase._children.copy() - _attributes = atom.AtomBase._attributes.copy() - _attributes['value'] = 'value' - - def __init__(self, extension_elements=None, - value=None, extension_attributes=None, text=None): - self.value = value - self.text = text - self.extension_elements = extension_elements or [] - self.extension_attributes = extension_attributes or {} - - def _TransferToElementTree(self, element_tree): - if self.value: - element_tree.attrib['value'] = self.value - element_tree.tag = GCAL_TEMPLATE % 'quickadd' - atom.AtomBase._TransferToElementTree(self, element_tree) - return element_tree - - def _TakeAttributeFromElementTree(self, attribute, element_tree): - if attribute == 'value': - self.value = element_tree.attrib[attribute] - del element_tree.attrib[attribute] - else: - atom.AtomBase._TakeAttributeFromElementTree(self, attribute, - element_tree) - - -class SyncEvent(atom.AtomBase): - _tag = 'syncEvent' - _namespace = GCAL_NAMESPACE - _children = atom.AtomBase._children.copy() - _attributes = atom.AtomBase._attributes.copy() - _attributes['value'] = 'value' - - def __init__(self, value='false', extension_elements=None, - extension_attributes=None, text=None): - self.value = value - self.text = text - self.extension_elements = extension_elements or [] - self.extension_attributes = extension_attributes or {} - - -class UID(atom.AtomBase): - _tag = 'uid' - _namespace = GCAL_NAMESPACE - _children = atom.AtomBase._children.copy() - _attributes = atom.AtomBase._attributes.copy() - _attributes['value'] = 'value' - - def __init__(self, value=None, extension_elements=None, - extension_attributes=None, text=None): - self.value = value - self.text = text - self.extension_elements = extension_elements or [] - self.extension_attributes = extension_attributes or {} - - -class Sequence(atom.AtomBase): - _tag = 'sequence' - _namespace = GCAL_NAMESPACE - _children = atom.AtomBase._children.copy() - _attributes = atom.AtomBase._attributes.copy() - _attributes['value'] = 'value' - - def __init__(self, value=None, extension_elements=None, - extension_attributes=None, text=None): - self.value = value - self.text = text - self.extension_elements = extension_elements or [] - self.extension_attributes = extension_attributes or {} - - -class WebContentGadgetPref(atom.AtomBase): - - _tag = 'webContentGadgetPref' - _namespace = GCAL_NAMESPACE - _children = atom.AtomBase._children.copy() - _attributes = atom.AtomBase._attributes.copy() - _attributes['name'] = 'name' - _attributes['value'] = 'value' - - """The Google Calendar Web Content Gadget Preferences element""" - - def __init__(self, name=None, value=None, extension_elements=None, - extension_attributes=None, text=None): - self.name = name - self.value = value - self.text = text - self.extension_elements = extension_elements or [] - self.extension_attributes = extension_attributes or {} - - -class WebContent(atom.AtomBase): - - _tag = 'webContent' - _namespace = GCAL_NAMESPACE - _children = atom.AtomBase._children.copy() - _attributes = atom.AtomBase._attributes.copy() - _children['{%s}webContentGadgetPref' % GCAL_NAMESPACE] = ('gadget_pref', - [WebContentGadgetPref]) - _attributes['url'] = 'url' - _attributes['width'] = 'width' - _attributes['height'] = 'height' - - def __init__(self, url=None, width=None, height=None, text=None, - gadget_pref=None, extension_elements=None, extension_attributes=None): - self.url = url - self.width = width - self.height = height - self.text = text - self.gadget_pref = gadget_pref or [] - self.extension_elements = extension_elements or [] - self.extension_attributes = extension_attributes or {} - - -class WebContentLink(atom.Link): - - _tag = 'link' - _namespace = atom.ATOM_NAMESPACE - _children = atom.Link._children.copy() - _attributes = atom.Link._attributes.copy() - _children['{%s}webContent' % GCAL_NAMESPACE] = ('web_content', WebContent) - - def __init__(self, title=None, href=None, link_type=None, - web_content=None): - atom.Link.__init__(self, rel=WEB_CONTENT_LINK_REL, title=title, href=href, - link_type=link_type) - self.web_content = web_content - - -class GuestsCanInviteOthers(atom.AtomBase): - """Indicates whether event attendees may invite others to the event. - - This element may only be changed by the organizer of the event. If not - included as part of the event entry, this element will default to true - during a POST request, and will inherit its previous value during a PUT - request. - """ - _tag = 'guestsCanInviteOthers' - _namespace = GCAL_NAMESPACE - _attributes = atom.AtomBase._attributes.copy() - _attributes['value'] = 'value' - - def __init__(self, value='true', *args, **kwargs): - atom.AtomBase.__init__(self, *args, **kwargs) - self.value = value - - -class GuestsCanSeeGuests(atom.AtomBase): - """Indicates whether attendees can see other people invited to the event. - - The organizer always sees all attendees. Guests always see themselves. This - property affects what attendees see in the event's guest list via both the - Calendar UI and API feeds. - - This element may only be changed by the organizer of the event. - - If not included as part of the event entry, this element will default to - true during a POST request, and will inherit its previous value during a - PUT request. - """ - _tag = 'guestsCanSeeGuests' - _namespace = GCAL_NAMESPACE - _attributes = atom.AtomBase._attributes.copy() - _attributes['value'] = 'value' - - def __init__(self, value='true', *args, **kwargs): - atom.AtomBase.__init__(self, *args, **kwargs) - self.value = value - - -class GuestsCanModify(atom.AtomBase): - """Indicates whether event attendees may modify the original event. - - If yes, changes are visible to organizer and other attendees. Otherwise, - any changes made by attendees will be restricted to that attendee's - calendar. - - This element may only be changed by the organizer of the event, and may - be set to 'true' only if both gCal:guestsCanInviteOthers and - gCal:guestsCanSeeGuests are set to true in the same PUT/POST request. - Otherwise, request fails with HTTP error code 400 (Bad Request). - - If not included as part of the event entry, this element will default to - false during a POST request, and will inherit its previous value during a - PUT request.""" - _tag = 'guestsCanModify' - _namespace = GCAL_NAMESPACE - _attributes = atom.AtomBase._attributes.copy() - _attributes['value'] = 'value' - - def __init__(self, value='false', *args, **kwargs): - atom.AtomBase.__init__(self, *args, **kwargs) - self.value = value - - - -class CalendarEventEntry(gdata.BatchEntry): - """A Google Calendar flavor of an Atom Entry """ - - _tag = gdata.BatchEntry._tag - _namespace = gdata.BatchEntry._namespace - _children = gdata.BatchEntry._children.copy() - _attributes = gdata.BatchEntry._attributes.copy() - # This class also contains WebContentLinks but converting those members - # is handled in a special version of _ConvertElementTreeToMember. - _children['{%s}where' % gdata.GDATA_NAMESPACE] = ('where', [Where]) - _children['{%s}when' % gdata.GDATA_NAMESPACE] = ('when', [When]) - _children['{%s}who' % gdata.GDATA_NAMESPACE] = ('who', [Who]) - _children['{%s}extendedProperty' % gdata.GDATA_NAMESPACE] = ( - 'extended_property', [ExtendedProperty]) - _children['{%s}visibility' % gdata.GDATA_NAMESPACE] = ('visibility', - Visibility) - _children['{%s}transparency' % gdata.GDATA_NAMESPACE] = ('transparency', - Transparency) - _children['{%s}eventStatus' % gdata.GDATA_NAMESPACE] = ('event_status', - EventStatus) - _children['{%s}recurrence' % gdata.GDATA_NAMESPACE] = ('recurrence', - Recurrence) - _children['{%s}recurrenceException' % gdata.GDATA_NAMESPACE] = ( - 'recurrence_exception', [RecurrenceException]) - _children['{%s}sendEventNotifications' % GCAL_NAMESPACE] = ( - 'send_event_notifications', SendEventNotifications) - _children['{%s}quickadd' % GCAL_NAMESPACE] = ('quick_add', QuickAdd) - _children['{%s}comments' % gdata.GDATA_NAMESPACE] = ('comments', Comments) - _children['{%s}originalEvent' % gdata.GDATA_NAMESPACE] = ('original_event', - OriginalEvent) - _children['{%s}sequence' % GCAL_NAMESPACE] = ('sequence', Sequence) - _children['{%s}reminder' % gdata.GDATA_NAMESPACE] = ('reminder', [Reminder]) - _children['{%s}syncEvent' % GCAL_NAMESPACE] = ('sync_event', SyncEvent) - _children['{%s}uid' % GCAL_NAMESPACE] = ('uid', UID) - _children['{%s}guestsCanInviteOthers' % GCAL_NAMESPACE] = ( - 'guests_can_invite_others', GuestsCanInviteOthers) - _children['{%s}guestsCanModify' % GCAL_NAMESPACE] = ( - 'guests_can_modify', GuestsCanModify) - _children['{%s}guestsCanSeeGuests' % GCAL_NAMESPACE] = ( - 'guests_can_see_guests', GuestsCanSeeGuests) - - def __init__(self, author=None, category=None, content=None, - atom_id=None, link=None, published=None, - title=None, updated=None, - transparency=None, comments=None, event_status=None, - send_event_notifications=None, visibility=None, - recurrence=None, recurrence_exception=None, - where=None, when=None, who=None, quick_add=None, - extended_property=None, original_event=None, - batch_operation=None, batch_id=None, batch_status=None, - sequence=None, reminder=None, sync_event=None, uid=None, - guests_can_invite_others=None, guests_can_modify=None, - guests_can_see_guests=None, - extension_elements=None, extension_attributes=None, text=None): - - gdata.BatchEntry.__init__(self, author=author, category=category, - content=content, - atom_id=atom_id, link=link, published=published, - batch_operation=batch_operation, batch_id=batch_id, - batch_status=batch_status, - title=title, updated=updated) - - self.transparency = transparency - self.comments = comments - self.event_status = event_status - self.send_event_notifications = send_event_notifications - self.visibility = visibility - self.recurrence = recurrence - self.recurrence_exception = recurrence_exception or [] - self.where = where or [] - self.when = when or [] - self.who = who or [] - self.quick_add = quick_add - self.extended_property = extended_property or [] - self.original_event = original_event - self.sequence = sequence - self.reminder = reminder or [] - self.sync_event = sync_event - self.uid = uid - self.text = text - self.guests_can_invite_others = guests_can_invite_others - self.guests_can_modify = guests_can_modify - self.guests_can_see_guests = guests_can_see_guests - self.extension_elements = extension_elements or [] - self.extension_attributes = extension_attributes or {} - - # We needed to add special logic to _ConvertElementTreeToMember because we - # want to make links with a rel of WEB_CONTENT_LINK_REL into a - # WebContentLink - def _ConvertElementTreeToMember(self, child_tree): - # Special logic to handle Web Content links - if (child_tree.tag == '{%s}link' % atom.ATOM_NAMESPACE and - child_tree.attrib['rel'] == WEB_CONTENT_LINK_REL): - if self.link is None: - self.link = [] - self.link.append(atom._CreateClassFromElementTree(WebContentLink, - child_tree)) - return - # Find the element's tag in this class's list of child members - if self.__class__._children.has_key(child_tree.tag): - member_name = self.__class__._children[child_tree.tag][0] - member_class = self.__class__._children[child_tree.tag][1] - # If the class member is supposed to contain a list, make sure the - # matching member is set to a list, then append the new member - # instance to the list. - if isinstance(member_class, list): - if getattr(self, member_name) is None: - setattr(self, member_name, []) - getattr(self, member_name).append(atom._CreateClassFromElementTree( - member_class[0], child_tree)) - else: - setattr(self, member_name, - atom._CreateClassFromElementTree(member_class, child_tree)) - else: - atom.ExtensionContainer._ConvertElementTreeToMember(self, child_tree) - - - def GetWebContentLink(self): - """Finds the first link with rel set to WEB_CONTENT_REL - - Returns: - A gdata.calendar.WebContentLink or none if none of the links had rel - equal to WEB_CONTENT_REL - """ - - for a_link in self.link: - if a_link.rel == WEB_CONTENT_LINK_REL: - return a_link - return None - - -def CalendarEventEntryFromString(xml_string): - return atom.CreateClassFromXMLString(CalendarEventEntry, xml_string) - - -def CalendarEventCommentEntryFromString(xml_string): - return atom.CreateClassFromXMLString(CalendarEventCommentEntry, xml_string) - - -CalendarEventEntryLink._children = {'{%s}entry' % atom.ATOM_NAMESPACE: - ('entry', CalendarEventEntry)} - - -def CalendarEventEntryLinkFromString(xml_string): - return atom.CreateClassFromXMLString(CalendarEventEntryLink, xml_string) - - -class CalendarEventFeed(gdata.BatchFeed, gdata.LinkFinder): - """A Google Calendar event feed flavor of an Atom Feed""" - - _tag = gdata.BatchFeed._tag - _namespace = gdata.BatchFeed._namespace - _children = gdata.BatchFeed._children.copy() - _attributes = gdata.BatchFeed._attributes.copy() - _children['{%s}entry' % atom.ATOM_NAMESPACE] = ('entry', - [CalendarEventEntry]) - _children['{%s}timezone' % GCAL_NAMESPACE] = ('timezone', Timezone) - - def __init__(self, author=None, category=None, contributor=None, - generator=None, icon=None, atom_id=None, link=None, logo=None, - rights=None, subtitle=None, title=None, updated=None, entry=None, - total_results=None, start_index=None, items_per_page=None, - interrupted=None, timezone=None, - extension_elements=None, extension_attributes=None, text=None): - gdata.BatchFeed.__init__(self, author=author, category=category, - contributor=contributor, generator=generator, - icon=icon, atom_id=atom_id, link=link, - logo=logo, rights=rights, subtitle=subtitle, - title=title, updated=updated, entry=entry, - total_results=total_results, - start_index=start_index, - items_per_page=items_per_page, - interrupted=interrupted, - extension_elements=extension_elements, - extension_attributes=extension_attributes, - text=text) - self.timezone = timezone - - -def CalendarListEntryFromString(xml_string): - return atom.CreateClassFromXMLString(CalendarListEntry, xml_string) - - -def CalendarAclEntryFromString(xml_string): - return atom.CreateClassFromXMLString(CalendarAclEntry, xml_string) - - -def CalendarListFeedFromString(xml_string): - return atom.CreateClassFromXMLString(CalendarListFeed, xml_string) - - -def CalendarAclFeedFromString(xml_string): - return atom.CreateClassFromXMLString(CalendarAclFeed, xml_string) - - -def CalendarEventFeedFromString(xml_string): - return atom.CreateClassFromXMLString(CalendarEventFeed, xml_string) - - -def CalendarEventCommentFeedFromString(xml_string): - return atom.CreateClassFromXMLString(CalendarEventCommentFeed, xml_string) diff --git a/gdata/calendar/data.py b/gdata/calendar/data.py deleted file mode 100644 index c24f04d9d4..0000000000 --- a/gdata/calendar/data.py +++ /dev/null @@ -1,300 +0,0 @@ -#!/usr/bin/python -# -# Copyright (C) 2009 Google 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. - - -"""Contains the data classes of the Google Calendar Data API""" - - -__author__ = 'j.s@google.com (Jeff Scudder)' - - -import atom.core -import atom.data -import gdata.acl.data -import gdata.data -import gdata.geo.data -import gdata.opensearch.data - - -GCAL_TEMPLATE = '{http://schemas.google.com/gCal/2005/}%s' - - -class AccessLevelProperty(atom.core.XmlElement): - """Describes how much a given user may do with an event or calendar""" - _qname = GCAL_TEMPLATE % 'accesslevel' - value = 'value' - - -class AllowGSync2Property(atom.core.XmlElement): - """Whether the user is permitted to run Google Apps Sync""" - _qname = GCAL_TEMPLATE % 'allowGSync2' - value = 'value' - - -class AllowGSyncProperty(atom.core.XmlElement): - """Whether the user is permitted to run Google Apps Sync""" - _qname = GCAL_TEMPLATE % 'allowGSync' - value = 'value' - - -class AnyoneCanAddSelfProperty(atom.core.XmlElement): - """Whether anyone can add self as attendee""" - _qname = GCAL_TEMPLATE % 'anyoneCanAddSelf' - value = 'value' - - -class CalendarAclRole(gdata.acl.data.AclRole): - """Describes the Calendar roles of an entry in the Calendar access control list""" - _qname = gdata.acl.data.GACL_TEMPLATE % 'role' - - -class CalendarCommentEntry(gdata.data.GDEntry): - """Describes an entry in a feed of a Calendar event's comments""" - - -class CalendarCommentFeed(gdata.data.GDFeed): - """Describes feed of a Calendar event's comments""" - entry = [CalendarCommentEntry] - - -class CalendarComments(gdata.data.Comments): - """Describes a container of a feed link for Calendar comment entries""" - _qname = gdata.data.GD_TEMPLATE % 'comments' - - -class CalendarExtendedProperty(gdata.data.ExtendedProperty): - """Defines a value for the realm attribute that is used only in the calendar API""" - _qname = gdata.data.GD_TEMPLATE % 'extendedProperty' - - -class CalendarWhere(gdata.data.Where): - """Extends the base Where class with Calendar extensions""" - _qname = gdata.data.GD_TEMPLATE % 'where' - - -class ColorProperty(atom.core.XmlElement): - """Describes the color of a calendar""" - _qname = GCAL_TEMPLATE % 'color' - value = 'value' - - -class GuestsCanInviteOthersProperty(atom.core.XmlElement): - """Whether guests can invite others to the event""" - _qname = GCAL_TEMPLATE % 'guestsCanInviteOthers' - value = 'value' - - -class GuestsCanModifyProperty(atom.core.XmlElement): - """Whether guests can modify event""" - _qname = GCAL_TEMPLATE % 'guestsCanModify' - value = 'value' - - -class GuestsCanSeeGuestsProperty(atom.core.XmlElement): - """Whether guests can see other attendees""" - _qname = GCAL_TEMPLATE % 'guestsCanSeeGuests' - value = 'value' - - -class HiddenProperty(atom.core.XmlElement): - """Describes whether a calendar is hidden""" - _qname = GCAL_TEMPLATE % 'hidden' - value = 'value' - - -class IcalUIDProperty(atom.core.XmlElement): - """Describes the UID in the ical export of the event""" - _qname = GCAL_TEMPLATE % 'uid' - value = 'value' - - -class OverrideNameProperty(atom.core.XmlElement): - """Describes the override name property of a calendar""" - _qname = GCAL_TEMPLATE % 'overridename' - value = 'value' - - -class PrivateCopyProperty(atom.core.XmlElement): - """Indicates whether this is a private copy of the event, changes to which should not be sent to other calendars""" - _qname = GCAL_TEMPLATE % 'privateCopy' - value = 'value' - - -class QuickAddProperty(atom.core.XmlElement): - """Describes whether gd:content is for quick-add processing""" - _qname = GCAL_TEMPLATE % 'quickadd' - value = 'value' - - -class ResourceProperty(atom.core.XmlElement): - """Describes whether gd:who is a resource such as a conference room""" - _qname = GCAL_TEMPLATE % 'resource' - value = 'value' - id = 'id' - - -class EventWho(gdata.data.Who): - """Extends the base Who class with Calendar extensions""" - _qname = gdata.data.GD_TEMPLATE % 'who' - resource = ResourceProperty - - -class SelectedProperty(atom.core.XmlElement): - """Describes whether a calendar is selected""" - _qname = GCAL_TEMPLATE % 'selected' - value = 'value' - - -class SendAclNotificationsProperty(atom.core.XmlElement): - """Describes whether to send ACL notifications to grantees""" - _qname = GCAL_TEMPLATE % 'sendAclNotifications' - value = 'value' - - -class CalendarAclEntry(gdata.data.GDEntry): - """Describes an entry in a feed of a Calendar access control list (ACL)""" - send_acl_notifications = SendAclNotificationsProperty - - -class CalendarAclFeed(gdata.data.GDFeed): - """Describes a Calendar access contorl list (ACL) feed""" - entry = [CalendarAclEntry] - - -class SendEventNotificationsProperty(atom.core.XmlElement): - """Describes whether to send event notifications to other participants of the event""" - _qname = GCAL_TEMPLATE % 'sendEventNotifications' - value = 'value' - - -class SequenceNumberProperty(atom.core.XmlElement): - """Describes sequence number of an event""" - _qname = GCAL_TEMPLATE % 'sequence' - value = 'value' - - -class CalendarRecurrenceExceptionEntry(gdata.data.GDEntry): - """Describes an entry used by a Calendar recurrence exception entry link""" - uid = IcalUIDProperty - sequence = SequenceNumberProperty - - -class CalendarRecurrenceException(gdata.data.RecurrenceException): - """Describes an exception to a recurring Calendar event""" - _qname = gdata.data.GD_TEMPLATE % 'recurrenceException' - - -class SettingsProperty(atom.core.XmlElement): - """User preference name-value pair""" - _qname = GCAL_TEMPLATE % 'settingsProperty' - name = 'name' - value = 'value' - - -class SettingsEntry(gdata.data.GDEntry): - """Describes a Calendar Settings property entry""" - settings_property = SettingsProperty - - -class CalendarSettingsFeed(gdata.data.GDFeed): - """Personal settings for Calendar application""" - entry = [SettingsEntry] - - -class SuppressReplyNotificationsProperty(atom.core.XmlElement): - """Lists notification methods to be suppressed for this reply""" - _qname = GCAL_TEMPLATE % 'suppressReplyNotifications' - methods = 'methods' - - -class SyncEventProperty(atom.core.XmlElement): - """Describes whether this is a sync scenario where the Ical UID and Sequence number are honored during inserts and updates""" - _qname = GCAL_TEMPLATE % 'syncEvent' - value = 'value' - - -class CalendarEventEntry(gdata.data.BatchEntry): - """Describes a Calendar event entry""" - quickadd = QuickAddProperty - send_event_notifications = SendEventNotificationsProperty - sync_event = SyncEventProperty - anyone_can_add_self = AnyoneCanAddSelfProperty - extended_property = [CalendarExtendedProperty] - sequence = SequenceNumberProperty - guests_can_invite_others = GuestsCanInviteOthersProperty - guests_can_modify = GuestsCanModifyProperty - guests_can_see_guests = GuestsCanSeeGuestsProperty - georss_where = gdata.geo.data.GeoRssWhere - private_copy = PrivateCopyProperty - suppress_reply_notifications = SuppressReplyNotificationsProperty - uid = IcalUIDProperty - - -class TimeZoneProperty(atom.core.XmlElement): - """Describes the time zone of a calendar""" - _qname = GCAL_TEMPLATE % 'timezone' - value = 'value' - - -class TimesCleanedProperty(atom.core.XmlElement): - """Describes how many times calendar was cleaned via Manage Calendars""" - _qname = GCAL_TEMPLATE % 'timesCleaned' - value = 'value' - - -class CalendarEntry(gdata.data.GDEntry): - """Describes a Calendar entry in the feed of a user's calendars""" - timezone = TimeZoneProperty - overridename = OverrideNameProperty - hidden = HiddenProperty - selected = SelectedProperty - times_cleaned = TimesCleanedProperty - color = ColorProperty - where = [CalendarWhere] - accesslevel = AccessLevelProperty - - -class CalendarEventFeed(gdata.data.BatchFeed): - """Describes a Calendar event feed""" - allow_g_sync2 = AllowGSync2Property - timezone = TimeZoneProperty - entry = [CalendarEventEntry] - times_cleaned = TimesCleanedProperty - allow_g_sync = AllowGSyncProperty - - -class CalendarFeed(gdata.data.GDFeed): - """Describes a feed of Calendars""" - entry = [CalendarEntry] - - -class WebContentGadgetPref(atom.core.XmlElement): - """Describes a single web content gadget preference""" - _qname = GCAL_TEMPLATE % 'webContentGadgetPref' - name = 'name' - value = 'value' - - -class WebContent(atom.core.XmlElement): - """Describes a "web content" extension""" - _qname = GCAL_TEMPLATE % 'webContent' - height = 'height' - width = 'width' - web_content_gadget_pref = [WebContentGadgetPref] - url = 'url' - display = 'display' - - diff --git a/gdata/calendar/service.py b/gdata/calendar/service.py deleted file mode 100644 index 115d1b839b..0000000000 --- a/gdata/calendar/service.py +++ /dev/null @@ -1,598 +0,0 @@ -#!/usr/bin/python -# -# Copyright (C) 2006 Google 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. - -"""CalendarService extends the GDataService to streamline Google Calendar operations. - - CalendarService: Provides methods to query feeds and manipulate items. Extends - GDataService. - - DictionaryToParamList: Function which converts a dictionary into a list of - URL arguments (represented as strings). This is a - utility function used in CRUD operations. -""" - - -__author__ = 'api.vli (Vivian Li)' - - -import urllib -import gdata -import atom.service -import gdata.service -import gdata.calendar -import atom - - -DEFAULT_BATCH_URL = ('http://www.google.com/calendar/feeds/default/private' - '/full/batch') - - -class Error(Exception): - pass - - -class RequestError(Error): - pass - - -class CalendarService(gdata.service.GDataService): - """Client for the Google Calendar service.""" - - def __init__(self, email=None, password=None, source=None, - server='www.google.com', additional_headers=None, **kwargs): - """Creates a client for the Google Calendar 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: 'www.google.com'. - **kwargs: The other parameters to pass to gdata.service.GDataService - constructor. - """ - gdata.service.GDataService.__init__( - self, email=email, password=password, service='cl', source=source, - server=server, additional_headers=additional_headers, **kwargs) - - def GetCalendarEventFeed(self, uri='/calendar/feeds/default/private/full'): - return self.Get(uri, converter=gdata.calendar.CalendarEventFeedFromString) - - def GetCalendarEventEntry(self, uri): - return self.Get(uri, converter=gdata.calendar.CalendarEventEntryFromString) - - def GetCalendarListFeed(self, uri='/calendar/feeds/default/allcalendars/full'): - return self.Get(uri, converter=gdata.calendar.CalendarListFeedFromString) - - def GetCalendarSettingsFeed(self, uri='/calendar/feeds/default/settings'): - return self.Get(uri) - - def GetAllCalendarsFeed(self, uri='/calendar/feeds/default/allcalendars/full'): - return self.Get(uri, converter=gdata.calendar.CalendarListFeedFromString) - - def GetOwnCalendarsFeed(self, uri='/calendar/feeds/default/owncalendars/full'): - return self.Get(uri, converter=gdata.calendar.CalendarListFeedFromString) - - def GetCalendarListEntry(self, uri): - return self.Get(uri, converter=gdata.calendar.CalendarListEntryFromString) - - def GetCalendarAclFeed(self, uri='/calendar/feeds/default/acl/full'): - return self.Get(uri, converter=gdata.calendar.CalendarAclFeedFromString) - - def GetCalendarAclEntry(self, uri): - return self.Get(uri, converter=gdata.calendar.CalendarAclEntryFromString) - - def GetCalendarEventCommentFeed(self, uri): - return self.Get(uri, converter=gdata.calendar.CalendarEventCommentFeedFromString) - - def GetCalendarEventCommentEntry(self, uri): - return self.Get(uri, converter=gdata.calendar.CalendarEventCommentEntryFromString) - - def Query(self, uri, converter=None): - """Performs a query and returns a resulting feed or entry. - - Args: - feed: string The feed which is to be queried - - Returns: - On success, a GDataFeed or Entry depending on which is sent from the - server. - On failure, a RequestError is raised of the form: - {'status': HTTP status code from server, - 'reason': HTTP reason from the server, - 'body': HTTP body of the server's response} - """ - - if converter: - result = self.Get(uri, converter=converter) - else: - result = self.Get(uri) - return result - - def CalendarQuery(self, query): - if isinstance(query, CalendarEventQuery): - return self.Query(query.ToUri(), - converter=gdata.calendar.CalendarEventFeedFromString) - elif isinstance(query, CalendarListQuery): - return self.Query(query.ToUri(), - converter=gdata.calendar.CalendarListFeedFromString) - elif isinstance(query, CalendarEventCommentQuery): - return self.Query(query.ToUri(), - converter=gdata.calendar.CalendarEventCommentFeedFromString) - else: - return self.Query(query.ToUri()) - - def InsertEvent(self, new_event, insert_uri, url_params=None, - escape_params=True): - """Adds an event to Google Calendar. - - Args: - new_event: atom.Entry or subclass A new event which is to be added to - Google Calendar. - insert_uri: the URL to post new events to the feed - url_params: dict (optional) Additional URL parameters to be included - in the insertion request. - escape_params: boolean (optional) If true, the url_parameters will be - escaped before they are included in the request. - - Returns: - On successful insert, an entry containing the event created - On failure, a RequestError is raised of the form: - {'status': HTTP status code from server, - 'reason': HTTP reason from the server, - 'body': HTTP body of the server's response} - """ - - return self.Post(new_event, insert_uri, url_params=url_params, - escape_params=escape_params, - converter=gdata.calendar.CalendarEventEntryFromString) - - def InsertCalendarSubscription(self, calendar, insert_uri='/calendar/feeds/default/allcalendars/full', - url_params=None, escape_params=True): - """Subscribes user to the provided calendar. - - Args: - calendar: The calendar to which the user should be subscribed. - insert_uri: string The insert URL of the entry to be added. - url_params: dict (optional) Additional URL parameters to be included - in the insertion request. - escape_params: boolean (optional) If true, the url_parameters will be - escaped before they are included in the request. - - Returns: - On successful insert, an entry containing the subscription created - On failure, a RequestError is raised of the form: - {'status': HTTP status code from server, - 'reason': HTTP reason from the server, - 'body': HTTP body of the server's response} - """ - - return self.Post(calendar, insert_uri, url_params=url_params, - escape_params=escape_params, - converter=gdata.calendar.CalendarListEntryFromString) - - def InsertCalendar(self, new_calendar, url_params=None, - escape_params=True): - """Creates a new calendar. - - Args: - new_calendar: The calendar to be created - url_params: dict (optional) Additional URL parameters to be included - in the insertion request. - escape_params: boolean (optional) If true, the url_parameters will be - escaped before they are included in the request. - - Returns: - On successful insert, an entry containing the calendar created - On failure, a RequestError is raised of the form: - {'status': HTTP status code from server, - 'reason': HTTP reason from the server, - 'body': HTTP body of the server's response} - """ - - insert_uri = '/calendar/feeds/default/owncalendars/full' - response = self.Post(new_calendar, insert_uri, url_params=url_params, - escape_params=escape_params, - converter=gdata.calendar.CalendarListEntryFromString) - return response - - def UpdateCalendar(self, calendar, url_params=None, - escape_params=True): - """Updates a calendar. - - Args: - calendar: The calendar which should be updated - url_params: dict (optional) Additional URL parameters to be included - in the insertion request. - escape_params: boolean (optional) If true, the url_parameters will be - escaped before they are included in the request. - - Returns: - On successful insert, an entry containing the calendar created - On failure, a RequestError is raised of the form: - {'status': HTTP status code from server, - 'reason': HTTP reason from the server, - 'body': HTTP body of the server's response} - """ - - update_uri = calendar.GetEditLink().href - response = self.Put(data=calendar, uri=update_uri, url_params=url_params, - escape_params=escape_params, - converter=gdata.calendar.CalendarListEntryFromString) - return response - - def InsertAclEntry(self, new_entry, insert_uri, url_params=None, - escape_params=True): - """Adds an ACL entry (rule) to Google Calendar. - - Args: - new_entry: atom.Entry or subclass A new ACL entry which is to be added to - Google Calendar. - insert_uri: the URL to post new entries to the ACL feed - url_params: dict (optional) Additional URL parameters to be included - in the insertion request. - escape_params: boolean (optional) If true, the url_parameters will be - escaped before they are included in the request. - - Returns: - On successful insert, an entry containing the ACL entry created - On failure, a RequestError is raised of the form: - {'status': HTTP status code from server, - 'reason': HTTP reason from the server, - 'body': HTTP body of the server's response} - """ - - return self.Post(new_entry, insert_uri, url_params=url_params, - escape_params=escape_params, - converter=gdata.calendar.CalendarAclEntryFromString) - - def InsertEventComment(self, new_entry, insert_uri, url_params=None, - escape_params=True): - """Adds an entry to Google Calendar. - - Args: - new_entry: atom.Entry or subclass A new entry which is to be added to - Google Calendar. - insert_uri: the URL to post new entrys to the feed - url_params: dict (optional) Additional URL parameters to be included - in the insertion request. - escape_params: boolean (optional) If true, the url_parameters will be - escaped before they are included in the request. - - Returns: - On successful insert, an entry containing the comment created - On failure, a RequestError is raised of the form: - {'status': HTTP status code from server, - 'reason': HTTP reason from the server, - 'body': HTTP body of the server's response} - """ - - return self.Post(new_entry, insert_uri, url_params=url_params, - escape_params=escape_params, - converter=gdata.calendar.CalendarEventCommentEntryFromString) - - def _RemoveStandardUrlPrefix(self, url): - url_prefix = 'http://%s/' % self.server - if url.startswith(url_prefix): - return url[len(url_prefix) - 1:] - return url - - def DeleteEvent(self, edit_uri, extra_headers=None, - url_params=None, escape_params=True): - """Removes an event with the specified ID from Google Calendar. - - Args: - edit_uri: string The edit URL of the entry to be deleted. Example: - 'http://www.google.com/calendar/feeds/default/private/full/abx' - url_params: dict (optional) Additional URL parameters to be included - in the deletion request. - escape_params: boolean (optional) If true, the url_parameters will be - escaped before they are included in the request. - - Returns: - On successful delete, a httplib.HTTPResponse containing the server's - response to the DELETE request. - On failure, a RequestError is raised of the form: - {'status': HTTP status code from server, - 'reason': HTTP reason from the server, - 'body': HTTP body of the server's response} - """ - - edit_uri = self._RemoveStandardUrlPrefix(edit_uri) - return self.Delete('%s' % edit_uri, - url_params=url_params, escape_params=escape_params) - - def DeleteAclEntry(self, edit_uri, extra_headers=None, - url_params=None, escape_params=True): - """Removes an ACL entry at the given edit_uri from Google Calendar. - - Args: - edit_uri: string The edit URL of the entry to be deleted. Example: - 'http://www.google.com/calendar/feeds/liz%40gmail.com/acl/full/default' - url_params: dict (optional) Additional URL parameters to be included - in the deletion request. - escape_params: boolean (optional) If true, the url_parameters will be - escaped before they are included in the request. - - Returns: - On successful delete, a httplib.HTTPResponse containing the server's - response to the DELETE request. - On failure, a RequestError is raised of the form: - {'status': HTTP status code from server, - 'reason': HTTP reason from the server, - 'body': HTTP body of the server's response} - """ - - edit_uri = self._RemoveStandardUrlPrefix(edit_uri) - return self.Delete('%s' % edit_uri, - url_params=url_params, escape_params=escape_params) - - def DeleteCalendarEntry(self, edit_uri, extra_headers=None, - url_params=None, escape_params=True): - """Removes a calendar entry at the given edit_uri from Google Calendar. - - Args: - edit_uri: string The edit URL of the entry to be deleted. Example: - 'http://www.google.com/calendar/feeds/default/allcalendars/abcdef@group.calendar.google.com' - url_params: dict (optional) Additional URL parameters to be included - in the deletion request. - escape_params: boolean (optional) If true, the url_parameters will be - escaped before they are included in the request. - - Returns: - On successful delete, True is returned - On failure, a RequestError is raised of the form: - {'status': HTTP status code from server, - 'reason': HTTP reason from the server, - 'body': HTTP body of the server's response} - """ - - return self.Delete(edit_uri, url_params=url_params, - escape_params=escape_params) - - def UpdateEvent(self, edit_uri, updated_event, url_params=None, - escape_params=True): - """Updates an existing event. - - Args: - edit_uri: string The edit link URI for the element being updated - updated_event: string, atom.Entry, or subclass containing - the Atom Entry which will replace the event which is - stored at the edit_url - url_params: dict (optional) Additional URL parameters to be included - in the update request. - escape_params: boolean (optional) If true, the url_parameters will be - escaped before they are included in the request. - - Returns: - On successful update, a httplib.HTTPResponse containing the server's - response to the PUT request. - On failure, a RequestError is raised of the form: - {'status': HTTP status code from server, - 'reason': HTTP reason from the server, - 'body': HTTP body of the server's response} - """ - - edit_uri = self._RemoveStandardUrlPrefix(edit_uri) - return self.Put(updated_event, '%s' % edit_uri, - url_params=url_params, - escape_params=escape_params, - converter=gdata.calendar.CalendarEventEntryFromString) - - def UpdateAclEntry(self, edit_uri, updated_rule, url_params=None, - escape_params=True): - """Updates an existing ACL rule. - - Args: - edit_uri: string The edit link URI for the element being updated - updated_rule: string, atom.Entry, or subclass containing - the Atom Entry which will replace the event which is - stored at the edit_url - url_params: dict (optional) Additional URL parameters to be included - in the update request. - escape_params: boolean (optional) If true, the url_parameters will be - escaped before they are included in the request. - - Returns: - On successful update, a httplib.HTTPResponse containing the server's - response to the PUT request. - On failure, a RequestError is raised of the form: - {'status': HTTP status code from server, - 'reason': HTTP reason from the server, - 'body': HTTP body of the server's response} - """ - - edit_uri = self._RemoveStandardUrlPrefix(edit_uri) - return self.Put(updated_rule, '%s' % edit_uri, - url_params=url_params, - escape_params=escape_params, - converter=gdata.calendar.CalendarAclEntryFromString) - - def ExecuteBatch(self, batch_feed, url, - converter=gdata.calendar.CalendarEventFeedFromString): - """Sends a batch request feed to the server. - - The batch request needs to be sent to the batch URL for a particular - calendar. You can find the URL by calling GetBatchLink().href on the - CalendarEventFeed. - - Args: - batch_feed: gdata.calendar.CalendarEventFeed A feed containing batch - request entries. Each entry contains the operation to be performed - on the data contained in the entry. For example an entry with an - operation type of insert will be used as if the individual entry - had been inserted. - url: str The batch URL for the Calendar to which these operations should - be applied. - converter: Function (optional) The function used to convert the server's - response to an object. The default value is - CalendarEventFeedFromString. - - Returns: - The results of the batch request's execution on the server. If the - default converter is used, this is stored in a CalendarEventFeed. - """ - return self.Post(batch_feed, url, converter=converter) - - -class CalendarEventQuery(gdata.service.Query): - - def __init__(self, user='default', visibility='private', projection='full', - text_query=None, params=None, categories=None): - gdata.service.Query.__init__(self, - feed='http://www.google.com/calendar/feeds/%s/%s/%s' % ( - urllib.quote(user), - urllib.quote(visibility), - urllib.quote(projection)), - text_query=text_query, params=params, categories=categories) - - def _GetStartMin(self): - if 'start-min' in self.keys(): - return self['start-min'] - else: - return None - - def _SetStartMin(self, val): - self['start-min'] = val - - start_min = property(_GetStartMin, _SetStartMin, - doc="""The start-min query parameter""") - - def _GetStartMax(self): - if 'start-max' in self.keys(): - return self['start-max'] - else: - return None - - def _SetStartMax(self, val): - self['start-max'] = val - - start_max = property(_GetStartMax, _SetStartMax, - doc="""The start-max query parameter""") - - def _GetOrderBy(self): - if 'orderby' in self.keys(): - return self['orderby'] - else: - return None - - def _SetOrderBy(self, val): - if val is not 'lastmodified' and val is not 'starttime': - raise Error, "Order By must be either 'lastmodified' or 'starttime'" - self['orderby'] = val - - orderby = property(_GetOrderBy, _SetOrderBy, - doc="""The orderby query parameter""") - - def _GetSortOrder(self): - if 'sortorder' in self.keys(): - return self['sortorder'] - else: - return None - - def _SetSortOrder(self, val): - if (val is not 'ascending' and val is not 'descending' - and val is not 'a' and val is not 'd' and val is not 'ascend' - and val is not 'descend'): - raise Error, "Sort order must be either ascending, ascend, " + ( - "a or descending, descend, or d") - self['sortorder'] = val - - sortorder = property(_GetSortOrder, _SetSortOrder, - doc="""The sortorder query parameter""") - - def _GetSingleEvents(self): - if 'singleevents' in self.keys(): - return self['singleevents'] - else: - return None - - def _SetSingleEvents(self, val): - self['singleevents'] = val - - singleevents = property(_GetSingleEvents, _SetSingleEvents, - doc="""The singleevents query parameter""") - - def _GetFutureEvents(self): - if 'futureevents' in self.keys(): - return self['futureevents'] - else: - return None - - def _SetFutureEvents(self, val): - self['futureevents'] = val - - futureevents = property(_GetFutureEvents, _SetFutureEvents, - doc="""The futureevents query parameter""") - - def _GetRecurrenceExpansionStart(self): - if 'recurrence-expansion-start' in self.keys(): - return self['recurrence-expansion-start'] - else: - return None - - def _SetRecurrenceExpansionStart(self, val): - self['recurrence-expansion-start'] = val - - recurrence_expansion_start = property(_GetRecurrenceExpansionStart, - _SetRecurrenceExpansionStart, - doc="""The recurrence-expansion-start query parameter""") - - def _GetRecurrenceExpansionEnd(self): - if 'recurrence-expansion-end' in self.keys(): - return self['recurrence-expansion-end'] - else: - return None - - def _SetRecurrenceExpansionEnd(self, val): - self['recurrence-expansion-end'] = val - - recurrence_expansion_end = property(_GetRecurrenceExpansionEnd, - _SetRecurrenceExpansionEnd, - doc="""The recurrence-expansion-end query parameter""") - - def _SetTimezone(self, val): - self['ctz'] = val - - def _GetTimezone(self): - if 'ctz' in self.keys(): - return self['ctz'] - else: - return None - - ctz = property(_GetTimezone, _SetTimezone, - doc="""The ctz query parameter which sets report time on the server.""") - - -class CalendarListQuery(gdata.service.Query): - """Queries the Google Calendar meta feed""" - - def __init__(self, userId=None, text_query=None, - params=None, categories=None): - if userId is None: - userId = 'default' - - gdata.service.Query.__init__(self, feed='http://www.google.com/calendar/feeds/' - +userId, - text_query=text_query, params=params, - categories=categories) - -class CalendarEventCommentQuery(gdata.service.Query): - """Queries the Google Calendar event comments feed""" - - def __init__(self, feed=None): - gdata.service.Query.__init__(self, feed=feed) diff --git a/gdata/calendar_resource/__init__.py b/gdata/calendar_resource/__init__.py deleted file mode 100644 index 8b13789179..0000000000 --- a/gdata/calendar_resource/__init__.py +++ /dev/null @@ -1 +0,0 @@ - diff --git a/gdata/calendar_resource/client.py b/gdata/calendar_resource/client.py deleted file mode 100644 index 5f3f7bcb2f..0000000000 --- a/gdata/calendar_resource/client.py +++ /dev/null @@ -1,199 +0,0 @@ -#!/usr/bin/python -# -# Copyright 2009 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. - -"""CalendarResourceClient simplifies Calendar Resources API calls. - -CalendarResourceClient extends gdata.client.GDClient to ease interaction with -the Google Apps Calendar Resources API. These interactions include the ability -to create, retrieve, update, and delete calendar resources in a Google Apps -domain. -""" - - -__author__ = 'Vic Fryzel ' - - -import gdata.calendar_resource.data -import gdata.client -import urllib - - -# Feed URI template. This must end with a / -# The strings in this template are eventually replaced with the API version -# and Google Apps domain name, respectively. -RESOURCE_FEED_TEMPLATE = '/a/feeds/calendar/resource/%s/%s/' - - -class CalendarResourceClient(gdata.client.GDClient): - """Client extension for the Google Calendar Resource API service. - - Attributes: - host: string The hostname for the Calendar Resouce API service. - api_version: string The version of the Calendar Resource API. - """ - - host = 'apps-apis.google.com' - api_version = '2.0' - auth_service = 'apps' - auth_scopes = gdata.gauth.AUTH_SCOPES['apps'] - - def __init__(self, domain, auth_token=None, **kwargs): - """Constructs a new client for the Calendar Resource API. - - Args: - domain: string The Google Apps domain with Calendar Resources. - auth_token: (optional) gdata.gauth.ClientLoginToken, AuthSubToken, or - OAuthToken which authorizes this client to edit the calendar resource - data. - kwargs: The other parameters to pass to the gdata.client.GDClient - constructor. - """ - gdata.client.GDClient.__init__(self, auth_token=auth_token, **kwargs) - self.domain = domain - - def make_resource_feed_uri(self, resource_id=None, params=None): - """Creates a resource feed URI for the Calendar Resource API. - - Using this client's Google Apps domain, create a feed URI for calendar - resources in that domain. If a resource_id is provided, return a URI - for that specific resource. If params are provided, append them as GET - params. - - Args: - resource_id: string (optional) The ID of the calendar resource for which - to make a feed URI. - params: dict (optional) key -> value params to append as GET vars to the - URI. Example: params={'start': 'my-resource-id'} - Returns: - A string giving the URI for calendar resources for this client's Google - Apps domain. - """ - uri = RESOURCE_FEED_TEMPLATE % (self.api_version, self.domain) - if resource_id: - uri += resource_id - if params: - uri += '?' + urllib.urlencode(params) - return uri - - MakeResourceFeedUri = make_resource_feed_uri - - def get_resource_feed(self, uri=None, **kwargs): - """Fetches a ResourceFeed of calendar resources at the given URI. - - Args: - uri: string The URI of the feed to pull. - kwargs: The other parameters to pass to gdata.client.GDClient.get_feed(). - - Returns: - A ResourceFeed object representing the feed at the given URI. - """ - - if uri is None: - uri = self.MakeResourceFeedUri() - return self.get_feed( - uri, - desired_class=gdata.calendar_resource.data.CalendarResourceFeed, - **kwargs) - - GetResourceFeed = get_resource_feed - - def get_resource(self, uri=None, resource_id=None, **kwargs): - """Fetches a single calendar resource by resource ID. - - Args: - uri: string The base URI of the feed from which to fetch the resource. - resource_id: string The string ID of the Resource to fetch. - kwargs: The other parameters to pass to gdata.client.GDClient.get_entry(). - - Returns: - A Resource object representing the calendar resource with the given - base URI and resource ID. - """ - - if uri is None: - uri = self.MakeResourceFeedUri(resource_id) - return self.get_entry( - uri, - desired_class=gdata.calendar_resource.data.CalendarResourceEntry, - **kwargs) - - GetResource = get_resource - - def create_resource(self, resource_id, resource_common_name=None, - resource_description=None, resource_type=None, **kwargs): - """Creates a calendar resource with the given properties. - - Args: - resource_id: string The resource ID of the calendar resource. - resource_common_name: string (optional) The common name of the resource. - resource_description: string (optional) The description of the resource. - resource_type: string (optional) The type of the resource. - kwargs: The other parameters to pass to gdata.client.GDClient.post(). - - Returns: - gdata.calendar_resource.data.CalendarResourceEntry of the new resource. - """ - new_resource = gdata.calendar_resource.data.CalendarResourceEntry( - resource_id=resource_id, - resource_common_name=resource_common_name, - resource_description=resource_description, - resource_type=resource_type) - return self.post(new_resource, self.MakeResourceFeedUri(), **kwargs) - - CreateResource = create_resource - - def update_resource(self, resource_id, resource_common_name=None, - resource_description=None, resource_type=None, **kwargs): - """Updates the calendar resource with the given resource ID. - - Args: - resource_id: string The resource ID of the calendar resource to update. - resource_common_name: string (optional) The common name to give the - resource. - resource_description: string (optional) The description to give the - resource. - resource_type: string (optional) The type to give the resource. - kwargs: The other parameters to pass to gdata.client.GDClient.update(). - - Returns: - gdata.calendar_resource.data.CalendarResourceEntry of the updated - resource. - """ - new_resource = gdata.calendar_resource.data.CalendarResourceEntry( - resource_id=resource_id, - resource_common_name=resource_common_name, - resource_description=resource_description, - resource_type=resource_type) - return self.update( - new_resource, - **kwargs) - - UpdateResource = update_resource - - def delete_resource(self, resource_id, **kwargs): - """Deletes the calendar resource with the given resource ID. - - Args: - resource_id: string The resource ID of the calendar resource to delete. - kwargs: The other parameters to pass to gdata.client.GDClient.delete() - - Returns: - An HTTP response object. See gdata.client.request(). - """ - - return self.delete(self.MakeResourceFeedUri(resource_id), **kwargs) - - DeleteResource = delete_resource diff --git a/gdata/calendar_resource/data.py b/gdata/calendar_resource/data.py deleted file mode 100644 index 527fd484b8..0000000000 --- a/gdata/calendar_resource/data.py +++ /dev/null @@ -1,193 +0,0 @@ -#!/usr/bin/python -# -# Copyright 2009 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 for parsing and generating XML for the Calendar Resource API.""" - - -__author__ = 'Vic Fryzel ' - - -import atom.core -import atom.data -import gdata.apps -import gdata.apps_property -import gdata.data - - -# This is required to work around a naming conflict between the Google -# Spreadsheets API and Python's built-in property function -pyproperty = property - - -# The apps:property name of the resourceId property -RESOURCE_ID_NAME = 'resourceId' -# The apps:property name of the resourceCommonName property -RESOURCE_COMMON_NAME_NAME = 'resourceCommonName' -# The apps:property name of the resourceDescription property -RESOURCE_DESCRIPTION_NAME = 'resourceDescription' -# The apps:property name of the resourceType property -RESOURCE_TYPE_NAME = 'resourceType' - - -class CalendarResourceEntry(gdata.data.GDEntry): - """Represents a Calendar Resource entry in object form.""" - - property = [gdata.apps_property.AppsProperty] - - def _GetProperty(self, name): - """Get the apps:property value with the given name. - - Args: - name: string Name of the apps:property value to get. - - Returns: - The apps:property value with the given name, or None if the name was - invalid. - """ - - for p in self.property: - if p.name == name: - return p.value - return None - - def _SetProperty(self, name, value): - """Set the apps:property value with the given name to the given value. - - Args: - name: string Name of the apps:property value to set. - value: string Value to give the apps:property value with the given name. - """ - - for i in range(len(self.property)): - if self.property[i].name == name: - self.property[i].value = value - return - self.property.append(gdata.apps_property.AppsProperty(name=name, value=value)) - - def GetResourceId(self): - """Get the resource ID of this Calendar Resource object. - - Returns: - The resource ID of this Calendar Resource object as a string or None. - """ - - return self._GetProperty(RESOURCE_ID_NAME) - - def SetResourceId(self, value): - """Set the resource ID of this Calendar Resource object. - - Args: - value: string The new resource ID value to give this object. - """ - - self._SetProperty(RESOURCE_ID_NAME, value) - - resource_id = pyproperty(GetResourceId, SetResourceId) - - def GetResourceCommonName(self): - """Get the common name of this Calendar Resource object. - - Returns: - The common name of this Calendar Resource object as a string or None. - """ - - return self._GetProperty(RESOURCE_COMMON_NAME_NAME) - - def SetResourceCommonName(self, value): - """Set the common name of this Calendar Resource object. - - Args: - value: string The new common name value to give this object. - """ - - self._SetProperty(RESOURCE_COMMON_NAME_NAME, value) - - resource_common_name = pyproperty( - GetResourceCommonName, - SetResourceCommonName) - - def GetResourceDescription(self): - """Get the description of this Calendar Resource object. - - Returns: - The description of this Calendar Resource object as a string or None. - """ - - return self._GetProperty(RESOURCE_DESCRIPTION_NAME) - - def SetResourceDescription(self, value): - """Set the description of this Calendar Resource object. - - Args: - value: string The new description value to give this object. - """ - - self._SetProperty(RESOURCE_DESCRIPTION_NAME, value) - - resource_description = pyproperty( - GetResourceDescription, - SetResourceDescription) - - def GetResourceType(self): - """Get the type of this Calendar Resource object. - - Returns: - The type of this Calendar Resource object as a string or None. - """ - - return self._GetProperty(RESOURCE_TYPE_NAME) - - def SetResourceType(self, value): - """Set the type value of this Calendar Resource object. - - Args: - value: string The new type value to give this object. - """ - - self._SetProperty(RESOURCE_TYPE_NAME, value) - - resource_type = pyproperty(GetResourceType, SetResourceType) - - def __init__(self, resource_id=None, resource_common_name=None, - resource_description=None, resource_type=None, *args, **kwargs): - """Constructs a new CalendarResourceEntry object with the given arguments. - - Args: - resource_id: string (optional) The resource ID to give this new object. - resource_common_name: string (optional) The common name to give this new - object. - resource_description: string (optional) The description to give this new - object. - resource_type: string (optional) The type to give this new object. - args: The other parameters to pass to gdata.entry.GDEntry constructor. - kwargs: The other parameters to pass to gdata.entry.GDEntry constructor. - """ - super(CalendarResourceEntry, self).__init__(*args, **kwargs) - if resource_id: - self.resource_id = resource_id - if resource_common_name: - self.resource_common_name = resource_common_name - if resource_description: - self.resource_description = resource_description - if resource_type: - self.resource_type = resource_type - - -class CalendarResourceFeed(gdata.data.GDFeed): - """Represents a feed of CalendarResourceEntry objects.""" - - # Override entry so that this feed knows how to type its list of entries. - entry = [CalendarResourceEntry] diff --git a/gdata/client.py b/gdata/client.py deleted file mode 100644 index 7e2314c3c3..0000000000 --- a/gdata/client.py +++ /dev/null @@ -1,1126 +0,0 @@ -#!/usr/bin/env python -# -# Copyright (C) 2008, 2009 Google 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. - - -# This module is used for version 2 of the Google Data APIs. - - -"""Provides a client to interact with Google Data API servers. - -This module is used for version 2 of the Google Data APIs. The primary class -in this module is GDClient. - - GDClient: handles auth and CRUD operations when communicating with servers. - GDataClient: deprecated client for version one services. Will be removed. -""" - - -__author__ = 'j.s@google.com (Jeff Scudder)' - - -import re -import atom.client -import atom.core -import atom.http_core -import gdata.gauth -import gdata.data - - -class Error(Exception): - pass - - -class RequestError(Error): - status = None - reason = None - body = None - headers = None - - -class RedirectError(RequestError): - pass - - -class CaptchaChallenge(RequestError): - captcha_url = None - captcha_token = None - - -class ClientLoginTokenMissing(Error): - pass - - -class MissingOAuthParameters(Error): - pass - - -class ClientLoginFailed(RequestError): - pass - - -class UnableToUpgradeToken(RequestError): - pass - - -class Unauthorized(Error): - pass - - -class BadAuthenticationServiceURL(RedirectError): - pass - - -class BadAuthentication(RequestError): - pass - - -class NotModified(RequestError): - pass - -class NotImplemented(RequestError): - pass - - -def error_from_response(message, http_response, error_class, - response_body=None): - - """Creates a new exception and sets the HTTP information in the error. - - Args: - message: str human readable message to be displayed if the exception is - not caught. - http_response: The response from the server, contains error information. - error_class: The exception to be instantiated and populated with - information from the http_response - response_body: str (optional) specify if the response has already been read - from the http_response object. - """ - if response_body is None: - body = http_response.read() - else: - body = response_body - error = error_class('%s: %i, %s' % (message, http_response.status, body)) - error.status = http_response.status - error.reason = http_response.reason - error.body = body - error.headers = atom.http_core.get_headers(http_response) - return error - - -def get_xml_version(version): - """Determines which XML schema to use based on the client API version. - - Args: - version: string which is converted to an int. The version string is in - the form 'Major.Minor.x.y.z' and only the major version number - is considered. If None is provided assume version 1. - """ - if version is None: - return 1 - return int(version.split('.')[0]) - - -class GDClient(atom.client.AtomPubClient): - """Communicates with Google Data servers to perform CRUD operations. - - This class is currently experimental and may change in backwards - incompatible ways. - - This class exists to simplify the following three areas involved in using - the Google Data APIs. - - CRUD Operations: - - The client provides a generic 'request' method for making HTTP requests. - There are a number of convenience methods which are built on top of - request, which include get_feed, get_entry, get_next, post, update, and - delete. These methods contact the Google Data servers. - - Auth: - - Reading user-specific private data requires authorization from the user as - do any changes to user data. An auth_token object can be passed into any - of the HTTP requests to set the Authorization header in the request. - - You may also want to set the auth_token member to a an object which can - use modify_request to set the Authorization header in the HTTP request. - - If you are authenticating using the email address and password, you can - use the client_login method to obtain an auth token and set the - auth_token member. - - If you are using browser redirects, specifically AuthSub, you will want - to use gdata.gauth.AuthSubToken.from_url to obtain the token after the - redirect, and you will probably want to updgrade this since use token - to a multiple use (session) token using the upgrade_token method. - - API Versions: - - This client is multi-version capable and can be used with Google Data API - version 1 and version 2. The version should be specified by setting the - api_version member to a string, either '1' or '2'. - """ - - # The gsessionid is used by Google Calendar to prevent redirects. - __gsessionid = None - api_version = None - # Name of the Google Data service when making a ClientLogin request. - auth_service = None - # URL prefixes which should be requested for AuthSub and OAuth. - auth_scopes = None - - def request(self, method=None, uri=None, auth_token=None, - http_request=None, converter=None, desired_class=None, - redirects_remaining=4, **kwargs): - """Make an HTTP request to the server. - - See also documentation for atom.client.AtomPubClient.request. - - If a 302 redirect is sent from the server to the client, this client - assumes that the redirect is in the form used by the Google Calendar API. - The same request URI and method will be used as in the original request, - but a gsessionid URL parameter will be added to the request URI with - the value provided in the server's 302 redirect response. If the 302 - redirect is not in the format specified by the Google Calendar API, a - RedirectError will be raised containing the body of the server's - response. - - The method calls the client's modify_request method to make any changes - required by the client before the request is made. For example, a - version 2 client could add a GData-Version: 2 header to the request in - its modify_request method. - - Args: - method: str The HTTP verb for this request, usually 'GET', 'POST', - 'PUT', or 'DELETE' - uri: atom.http_core.Uri, str, or unicode The URL being requested. - auth_token: An object which sets the Authorization HTTP header in its - modify_request method. Recommended classes include - gdata.gauth.ClientLoginToken and gdata.gauth.AuthSubToken - among others. - http_request: (optional) atom.http_core.HttpRequest - converter: function which takes the body of the response as it's only - argument and returns the desired object. - desired_class: class descended from atom.core.XmlElement to which a - successful response should be converted. If there is no - converter function specified (converter=None) then the - desired_class will be used in calling the - atom.core.parse function. If neither - the desired_class nor the converter is specified, an - HTTP reponse object will be returned. - redirects_remaining: (optional) int, if this number is 0 and the - server sends a 302 redirect, the request method - will raise an exception. This parameter is used in - recursive request calls to avoid an infinite loop. - - Any additional arguments are passed through to - atom.client.AtomPubClient.request. - - Returns: - An HTTP response object (see atom.http_core.HttpResponse for a - description of the object's interface) if no converter was - specified and no desired_class was specified. If a converter function - was provided, the results of calling the converter are returned. If no - converter was specified but a desired_class was provided, the response - body will be converted to the class using - atom.core.parse. - """ - if isinstance(uri, (str, unicode)): - uri = atom.http_core.Uri.parse_uri(uri) - - # Add the gsession ID to the URL to prevent further redirects. - # TODO: If different sessions are using the same client, there will be a - # multitude of redirects and session ID shuffling. - # If the gsession ID is in the URL, adopt it as the standard location. - if uri is not None and uri.query is not None and 'gsessionid' in uri.query: - self.__gsessionid = uri.query['gsessionid'] - # The gsession ID could also be in the HTTP request. - elif (http_request is not None and http_request.uri is not None - and http_request.uri.query is not None - and 'gsessionid' in http_request.uri.query): - self.__gsessionid = http_request.uri.query['gsessionid'] - # If the gsession ID is stored in the client, and was not present in the - # URI then add it to the URI. - elif self.__gsessionid is not None: - uri.query['gsessionid'] = self.__gsessionid - - # The AtomPubClient should call this class' modify_request before - # performing the HTTP request. - #http_request = self.modify_request(http_request) - - response = atom.client.AtomPubClient.request(self, method=method, - uri=uri, auth_token=auth_token, http_request=http_request, **kwargs) - # On success, convert the response body using the desired converter - # function if present. - if response is None: - return None - if response.status == 200 or response.status == 201: - if converter is not None: - return converter(response) - elif desired_class is not None: - if self.api_version is not None: - return atom.core.parse(response.read(), desired_class, - version=get_xml_version(self.api_version)) - else: - # No API version was specified, so allow parse to - # use the default version. - return atom.core.parse(response.read(), desired_class) - else: - return response - # TODO: move the redirect logic into the Google Calendar client once it - # exists since the redirects are only used in the calendar API. - elif response.status == 302: - if redirects_remaining > 0: - location = (response.getheader('Location') - or response.getheader('location')) - if location is not None: - m = re.compile('[\?\&]gsessionid=(\w*)').search(location) - if m is not None: - self.__gsessionid = m.group(1) - # Make a recursive call with the gsession ID in the URI to follow - # the redirect. - return self.request(method=method, uri=uri, auth_token=auth_token, - http_request=http_request, converter=converter, - desired_class=desired_class, - redirects_remaining=redirects_remaining-1, - **kwargs) - else: - raise error_from_response('302 received without Location header', - response, RedirectError) - else: - raise error_from_response('Too many redirects from server', - response, RedirectError) - elif response.status == 401: - raise error_from_response('Unauthorized - Server responded with', - response, Unauthorized) - elif response.status == 304: - raise error_from_response('Entry Not Modified - Server responded with', - response, NotModified) - elif response.status == 501: - raise error_from_response( - 'This API operation is not implemented. - Server responded with', - response, NotImplemented) - # If the server's response was not a 200, 201, 302, 304, 401, or 501, raise - # an exception. - else: - raise error_from_response('Server responded with', response, - RequestError) - - Request = request - - def request_client_login_token( - self, email, password, source, service=None, - account_type='HOSTED_OR_GOOGLE', - auth_url=atom.http_core.Uri.parse_uri( - 'https://www.google.com/accounts/ClientLogin'), - captcha_token=None, captcha_response=None): - service = service or self.auth_service - # Set the target URL. - http_request = atom.http_core.HttpRequest(uri=auth_url, method='POST') - http_request.add_body_part( - gdata.gauth.generate_client_login_request_body(email=email, - password=password, service=service, source=source, - account_type=account_type, captcha_token=captcha_token, - captcha_response=captcha_response), - 'application/x-www-form-urlencoded') - - # Use the underlying http_client to make the request. - response = self.http_client.request(http_request) - - response_body = response.read() - if response.status == 200: - token_string = gdata.gauth.get_client_login_token_string(response_body) - if token_string is not None: - return gdata.gauth.ClientLoginToken(token_string) - else: - raise ClientLoginTokenMissing( - 'Recieved a 200 response to client login request,' - ' but no token was present. %s' % (response_body,)) - elif response.status == 403: - captcha_challenge = gdata.gauth.get_captcha_challenge(response_body) - if captcha_challenge: - challenge = CaptchaChallenge('CAPTCHA required') - challenge.captcha_url = captcha_challenge['url'] - challenge.captcha_token = captcha_challenge['token'] - raise challenge - elif response_body.splitlines()[0] == 'Error=BadAuthentication': - raise BadAuthentication('Incorrect username or password') - else: - raise error_from_response('Server responded with a 403 code', - response, RequestError, response_body) - elif response.status == 302: - # Google tries to redirect all bad URLs back to - # http://www.google.. If a redirect - # attempt is made, assume the user has supplied an incorrect - # authentication URL - raise error_from_response('Server responded with a redirect', - response, BadAuthenticationServiceURL, - response_body) - else: - raise error_from_response('Server responded to ClientLogin request', - response, ClientLoginFailed, response_body) - - RequestClientLoginToken = request_client_login_token - - def client_login(self, email, password, source, service=None, - account_type='HOSTED_OR_GOOGLE', - auth_url=atom.http_core.Uri.parse_uri( - 'https://www.google.com/accounts/ClientLogin'), - captcha_token=None, captcha_response=None): - """Performs an auth request using the user's email address and password. - - In order to modify user specific data and read user private data, your - application must be authorized by the user. One way to demonstrage - authorization is by including a Client Login token in the Authorization - HTTP header of all requests. This method requests the Client Login token - by sending the user's email address, password, the name of the - application, and the service code for the service which will be accessed - by the application. If the username and password are correct, the server - will respond with the client login code and a new ClientLoginToken - object will be set in the client's auth_token member. With the auth_token - set, future requests from this client will include the Client Login - token. - - For a list of service names, see - http://code.google.com/apis/gdata/faq.html#clientlogin - For more information on Client Login, see: - http://code.google.com/apis/accounts/docs/AuthForInstalledApps.html - - Args: - email: str The user's email address or username. - password: str The password for the user's account. - source: str The name of your application. This can be anything you - like but should should give some indication of which app is - making the request. - service: str The service code for the service you would like to access. - For example, 'cp' for contacts, 'cl' for calendar. For a full - list see - http://code.google.com/apis/gdata/faq.html#clientlogin - If you are using a subclass of the gdata.client.GDClient, the - service will usually be filled in for you so you do not need - to specify it. For example see BloggerClient, - SpreadsheetsClient, etc. - account_type: str (optional) The type of account which is being - authenticated. This can be either 'GOOGLE' for a Google - Account, 'HOSTED' for a Google Apps Account, or the - default 'HOSTED_OR_GOOGLE' which will select the Google - Apps Account if the same email address is used for both - a Google Account and a Google Apps Account. - auth_url: str (optional) The URL to which the login request should be - sent. - captcha_token: str (optional) If a previous login attempt was reponded - to with a CAPTCHA challenge, this is the token which - identifies the challenge (from the CAPTCHA's URL). - captcha_response: str (optional) If a previous login attempt was - reponded to with a CAPTCHA challenge, this is the - response text which was contained in the challenge. - - Returns: - None - - Raises: - A RequestError or one of its suclasses: BadAuthentication, - BadAuthenticationServiceURL, ClientLoginFailed, - ClientLoginTokenMissing, or CaptchaChallenge - """ - service = service or self.auth_service - self.auth_token = self.request_client_login_token(email, password, - source, service=service, account_type=account_type, auth_url=auth_url, - captcha_token=captcha_token, captcha_response=captcha_response) - - ClientLogin = client_login - - def upgrade_token(self, token=None, url=atom.http_core.Uri.parse_uri( - 'https://www.google.com/accounts/AuthSubSessionToken')): - """Asks the Google auth server for a multi-use AuthSub token. - - For details on AuthSub, see: - http://code.google.com/apis/accounts/docs/AuthSub.html - - Args: - token: gdata.gauth.AuthSubToken or gdata.gauth.SecureAuthSubToken - (optional) If no token is passed in, the client's auth_token member - is used to request the new token. The token object will be modified - to contain the new session token string. - url: str or atom.http_core.Uri (optional) The URL to which the token - upgrade request should be sent. Defaults to: - https://www.google.com/accounts/AuthSubSessionToken - - Returns: - The upgraded gdata.gauth.AuthSubToken object. - """ - # Default to using the auth_token member if no token is provided. - if token is None: - token = self.auth_token - # We cannot upgrade a None token. - if token is None: - raise UnableToUpgradeToken('No token was provided.') - if not isinstance(token, gdata.gauth.AuthSubToken): - raise UnableToUpgradeToken( - 'Cannot upgrade the token because it is not an AuthSubToken object.') - http_request = atom.http_core.HttpRequest(uri=url, method='GET') - token.modify_request(http_request) - # Use the lower level HttpClient to make the request. - response = self.http_client.request(http_request) - if response.status == 200: - token._upgrade_token(response.read()) - return token - else: - raise UnableToUpgradeToken( - 'Server responded to token upgrade request with %s: %s' % ( - response.status, response.read())) - - UpgradeToken = upgrade_token - - def revoke_token(self, token=None, url=atom.http_core.Uri.parse_uri( - 'https://www.google.com/accounts/AuthSubRevokeToken')): - """Requests that the token be invalidated. - - This method can be used for both AuthSub and OAuth tokens (to invalidate - a ClientLogin token, the user must change their password). - - Returns: - True if the server responded with a 200. - - Raises: - A RequestError if the server responds with a non-200 status. - """ - # Default to using the auth_token member if no token is provided. - if token is None: - token = self.auth_token - - http_request = atom.http_core.HttpRequest(uri=url, method='GET') - token.modify_request(http_request) - response = self.http_client.request(http_request) - if response.status != 200: - raise error_from_response('Server sent non-200 to revoke token', - response, RequestError, response.read()) - - return True - - RevokeToken = revoke_token - - def get_oauth_token(self, scopes, next, consumer_key, consumer_secret=None, - rsa_private_key=None, - url=gdata.gauth.REQUEST_TOKEN_URL): - """Obtains an OAuth request token to allow the user to authorize this app. - - Once this client has a request token, the user can authorize the request - token by visiting the authorization URL in their browser. After being - redirected back to this app at the 'next' URL, this app can then exchange - the authorized request token for an access token. - - For more information see the documentation on Google Accounts with OAuth: - http://code.google.com/apis/accounts/docs/OAuth.html#AuthProcess - - Args: - scopes: list of strings or atom.http_core.Uri objects which specify the - URL prefixes which this app will be accessing. For example, to access - the Google Calendar API, you would want to use scopes: - ['https://www.google.com/calendar/feeds/', - 'http://www.google.com/calendar/feeds/'] - next: str or atom.http_core.Uri object, The URL which the user's browser - should be sent to after they authorize access to their data. This - should be a URL in your application which will read the token - information from the URL and upgrade the request token to an access - token. - consumer_key: str This is the identifier for this application which you - should have received when you registered your application with Google - to use OAuth. - consumer_secret: str (optional) The shared secret between your app and - Google which provides evidence that this request is coming from you - application and not another app. If present, this libraries assumes - you want to use an HMAC signature to verify requests. Keep this data - a secret. - rsa_private_key: str (optional) The RSA private key which is used to - generate a digital signature which is checked by Google's server. If - present, this library assumes that you want to use an RSA signature - to verify requests. Keep this data a secret. - url: The URL to which a request for a token should be made. The default - is Google's OAuth request token provider. - """ - http_request = None - if rsa_private_key is not None: - http_request = gdata.gauth.generate_request_for_request_token( - consumer_key, gdata.gauth.RSA_SHA1, scopes, - rsa_key=rsa_private_key, auth_server_url=url, next=next) - elif consumer_secret is not None: - http_request = gdata.gauth.generate_request_for_request_token( - consumer_key, gdata.gauth.HMAC_SHA1, scopes, - consumer_secret=consumer_secret, auth_server_url=url, next=next) - else: - raise MissingOAuthParameters( - 'To request an OAuth token, you must provide your consumer secret' - ' or your private RSA key.') - - response = self.http_client.request(http_request) - response_body = response.read() - - if response.status != 200: - raise error_from_response('Unable to obtain OAuth request token', - response, RequestError, response_body) - - if rsa_private_key is not None: - return gdata.gauth.rsa_token_from_body(response_body, consumer_key, - rsa_private_key, - gdata.gauth.REQUEST_TOKEN) - elif consumer_secret is not None: - return gdata.gauth.hmac_token_from_body(response_body, consumer_key, - consumer_secret, - gdata.gauth.REQUEST_TOKEN) - - GetOAuthToken = get_oauth_token - - def get_access_token(self, request_token, - url=gdata.gauth.ACCESS_TOKEN_URL): - """Exchanges an authorized OAuth request token for an access token. - - Contacts the Google OAuth server to upgrade a previously authorized - request token. Once the request token is upgraded to an access token, - the access token may be used to access the user's data. - - For more details, see the Google Accounts OAuth documentation: - http://code.google.com/apis/accounts/docs/OAuth.html#AccessToken - - Args: - request_token: An OAuth token which has been authorized by the user. - url: (optional) The URL to which the upgrade request should be sent. - Defaults to: https://www.google.com/accounts/OAuthAuthorizeToken - """ - http_request = gdata.gauth.generate_request_for_access_token( - request_token, auth_server_url=url) - response = self.http_client.request(http_request) - response_body = response.read() - if response.status != 200: - raise error_from_response( - 'Unable to upgrade OAuth request token to access token', - response, RequestError, response_body) - - return gdata.gauth.upgrade_to_access_token(request_token, response_body) - - GetAccessToken = get_access_token - - def modify_request(self, http_request): - """Adds or changes request before making the HTTP request. - - This client will add the API version if it is specified. - Subclasses may override this method to add their own request - modifications before the request is made. - """ - http_request = atom.client.AtomPubClient.modify_request(self, - http_request) - if self.api_version is not None: - http_request.headers['GData-Version'] = self.api_version - return http_request - - ModifyRequest = modify_request - - def get_feed(self, uri, auth_token=None, converter=None, - desired_class=gdata.data.GDFeed, **kwargs): - return self.request(method='GET', uri=uri, auth_token=auth_token, - converter=converter, desired_class=desired_class, - **kwargs) - - GetFeed = get_feed - - def get_entry(self, uri, auth_token=None, converter=None, - desired_class=gdata.data.GDEntry, etag=None, **kwargs): - http_request = atom.http_core.HttpRequest() - # Conditional retrieval - if etag is not None: - http_request.headers['If-None-Match'] = etag - return self.request(method='GET', uri=uri, auth_token=auth_token, - http_request=http_request, converter=converter, - desired_class=desired_class, **kwargs) - - GetEntry = get_entry - - def get_next(self, feed, auth_token=None, converter=None, - desired_class=None, **kwargs): - """Fetches the next set of results from the feed. - - When requesting a feed, the number of entries returned is capped at a - service specific default limit (often 25 entries). You can specify your - own entry-count cap using the max-results URL query parameter. If there - are more results than could fit under max-results, the feed will contain - a next link. This method performs a GET against this next results URL. - - Returns: - A new feed object containing the next set of entries in this feed. - """ - if converter is None and desired_class is None: - desired_class = feed.__class__ - return self.get_feed(feed.find_next_link(), auth_token=auth_token, - converter=converter, desired_class=desired_class, - **kwargs) - - GetNext = get_next - - # TODO: add a refresh method to re-fetch the entry/feed from the server - # if it has been updated. - - def post(self, entry, uri, auth_token=None, converter=None, - desired_class=None, **kwargs): - if converter is None and desired_class is None: - desired_class = entry.__class__ - http_request = atom.http_core.HttpRequest() - http_request.add_body_part( - entry.to_string(get_xml_version(self.api_version)), - 'application/atom+xml') - return self.request(method='POST', uri=uri, auth_token=auth_token, - http_request=http_request, converter=converter, - desired_class=desired_class, **kwargs) - - Post = post - - def update(self, entry, auth_token=None, force=False, **kwargs): - """Edits the entry on the server by sending the XML for this entry. - - Performs a PUT and converts the response to a new entry object with a - matching class to the entry passed in. - - Args: - entry: - auth_token: - force: boolean stating whether an update should be forced. Defaults to - False. Normally, if a change has been made since the passed in - entry was obtained, the server will not overwrite the entry since - the changes were based on an obsolete version of the entry. - Setting force to True will cause the update to silently - overwrite whatever version is present. - - Returns: - A new Entry object of a matching type to the entry which was passed in. - """ - http_request = atom.http_core.HttpRequest() - http_request.add_body_part( - entry.to_string(get_xml_version(self.api_version)), - 'application/atom+xml') - # Include the ETag in the request if present. - if force: - http_request.headers['If-Match'] = '*' - elif hasattr(entry, 'etag') and entry.etag: - http_request.headers['If-Match'] = entry.etag - - return self.request(method='PUT', uri=entry.find_edit_link(), - auth_token=auth_token, http_request=http_request, - desired_class=entry.__class__, **kwargs) - - Update = update - - def delete(self, entry_or_uri, auth_token=None, force=False, **kwargs): - http_request = atom.http_core.HttpRequest() - - # Include the ETag in the request if present. - if force: - http_request.headers['If-Match'] = '*' - elif hasattr(entry_or_uri, 'etag') and entry_or_uri.etag: - http_request.headers['If-Match'] = entry_or_uri.etag - - # If the user passes in a URL, just delete directly, may not work as - # the service might require an ETag. - if isinstance(entry_or_uri, (str, unicode, atom.http_core.Uri)): - return self.request(method='DELETE', uri=entry_or_uri, - http_request=http_request, auth_token=auth_token, - **kwargs) - - return self.request(method='DELETE', uri=entry_or_uri.find_edit_link(), - http_request=http_request, auth_token=auth_token, - **kwargs) - - Delete = delete - - #TODO: implement batch requests. - #def batch(feed, uri, auth_token=None, converter=None, **kwargs): - # pass - - # TODO: add a refresh method to request a conditional update to an entry - # or feed. - - -def _add_query_param(param_string, value, http_request): - if value: - http_request.uri.query[param_string] = value - - -class Query(object): - - def __init__(self, text_query=None, categories=None, author=None, alt=None, - updated_min=None, updated_max=None, pretty_print=False, - published_min=None, published_max=None, start_index=None, - max_results=None, strict=False): - """Constructs a Google Data Query to filter feed contents serverside. - - Args: - text_query: Full text search str (optional) - categories: list of strings (optional). Each string is a required - category. To include an 'or' query, put a | in the string between - terms. For example, to find everything in the Fitz category and - the Laurie or Jane category (Fitz and (Laurie or Jane)) you would - set categories to ['Fitz', 'Laurie|Jane']. - author: str (optional) The service returns entries where the author - name and/or email address match your query string. - alt: str (optional) for the Alternative representation type you'd like - the feed in. If you don't specify an alt parameter, the service - returns an Atom feed. This is equivalent to alt='atom'. - alt='rss' returns an RSS 2.0 result feed. - alt='json' returns a JSON representation of the feed. - alt='json-in-script' Requests a response that wraps JSON in a script - tag. - alt='atom-in-script' Requests an Atom response that wraps an XML - string in a script tag. - alt='rss-in-script' Requests an RSS response that wraps an XML - string in a script tag. - updated_min: str (optional), RFC 3339 timestamp format, lower bounds. - For example: 2005-08-09T10:57:00-08:00 - updated_max: str (optional) updated time must be earlier than timestamp. - pretty_print: boolean (optional) If True the server's XML response will - be indented to make it more human readable. Defaults to False. - published_min: str (optional), Similar to updated_min but for published - time. - published_max: str (optional), Similar to updated_max but for published - time. - start_index: int or str (optional) 1-based index of the first result to - be retrieved. Note that this isn't a general cursoring mechanism. - If you first send a query with ?start-index=1&max-results=10 and - then send another query with ?start-index=11&max-results=10, the - service cannot guarantee that the results are equivalent to - ?start-index=1&max-results=20, because insertions and deletions - could have taken place in between the two queries. - max_results: int or str (optional) Maximum number of results to be - retrieved. Each service has a default max (usually 25) which can - vary from service to service. There is also a service-specific - limit to the max_results you can fetch in a request. - strict: boolean (optional) If True, the server will return an error if - the server does not recognize any of the parameters in the request - URL. Defaults to False. - """ - self.text_query = text_query - self.categories = categories or [] - self.author = author - self.alt = alt - self.updated_min = updated_min - self.updated_max = updated_max - self.pretty_print = pretty_print - self.published_min = published_min - self.published_max = published_max - self.start_index = start_index - self.max_results = max_results - self.strict = strict - - def modify_request(self, http_request): - _add_query_param('q', self.text_query, http_request) - if self.categories: - http_request.uri.query['categories'] = ','.join(self.categories) - _add_query_param('author', self.author, http_request) - _add_query_param('alt', self.alt, http_request) - _add_query_param('updated-min', self.updated_min, http_request) - _add_query_param('updated-max', self.updated_max, http_request) - if self.pretty_print: - http_request.uri.query['prettyprint'] = 'true' - _add_query_param('published-min', self.published_min, http_request) - _add_query_param('published-max', self.published_max, http_request) - if self.start_index is not None: - http_request.uri.query['start-index'] = str(self.start_index) - if self.max_results is not None: - http_request.uri.query['max-results'] = str(self.max_results) - if self.strict: - http_request.uri.query['strict'] = 'true' - - - ModifyRequest = modify_request - - -class GDQuery(atom.http_core.Uri): - - def _get_text_query(self): - return self.query['q'] - - def _set_text_query(self, value): - self.query['q'] = value - - text_query = property(_get_text_query, _set_text_query, - doc='The q parameter for searching for an exact text match on content') - - -class ResumableUploader(object): - """Resumable upload helper for the Google Data protocol.""" - - DEFAULT_CHUNK_SIZE = 5242880 # 5MB - - def __init__(self, client, file_handle, content_type, total_file_size, - chunk_size=None, desired_class=None): - """Starts a resumable upload to a service that supports the protocol. - - Args: - client: gdata.client.GDClient A Google Data API service. - file_handle: object A file-like object containing the file to upload. - content_type: str The mimetype of the file to upload. - total_file_size: int The file's total size in bytes. - chunk_size: int The size of each upload chunk. If None, the - DEFAULT_CHUNK_SIZE will be used. - desired_class: object (optional) The type of gdata.data.GDEntry to parse - the completed entry as. This should be specific to the API. - """ - self.client = client - self.file_handle = file_handle - self.content_type = content_type - self.total_file_size = total_file_size - self.chunk_size = chunk_size or self.DEFAULT_CHUNK_SIZE - self.desired_class = desired_class or gdata.data.GDEntry - self.upload_uri = None - - # Send the entire file if the chunk size is less than fize's total size. - if self.total_file_size <= self.chunk_size: - self.chunk_size = total_file_size - - def _init_session(self, resumable_media_link, entry=None, headers=None, - auth_token=None): - """Starts a new resumable upload to a service that supports the protocol. - - The method makes a request to initiate a new upload session. The unique - upload uri returned by the server (and set in this method) should be used - to send upload chunks to the server. - - Args: - resumable_media_link: str The full URL for the #resumable-create-media or - #resumable-edit-media link for starting a resumable upload request or - updating media using a resumable PUT. - entry: A (optional) gdata.data.GDEntry containging metadata to create the - upload from. - headers: dict (optional) Additional headers to send in the initial request - to create the resumable upload request. These headers will override - any default headers sent in the request. For example: - headers={'Slug': 'MyTitle'}. - auth_token: (optional) An object which sets the Authorization HTTP header - in its modify_request method. Recommended classes include - gdata.gauth.ClientLoginToken and gdata.gauth.AuthSubToken - among others. - - Returns: - The final Atom entry as created on the server. The entry will be - parsed accoring to the class specified in self.desired_class. - - Raises: - RequestError if the unique upload uri is not set or the - server returns something other than an HTTP 308 when the upload is - incomplete. - """ - http_request = atom.http_core.HttpRequest() - - # Send empty POST if Atom XML wasn't specified. - if entry is None: - http_request.add_body_part('', self.content_type, size=0) - else: - http_request.add_body_part(str(entry), 'application/atom+xml', - size=len(str(entry))) - http_request.headers['X-Upload-Content-Type'] = self.content_type - http_request.headers['X-Upload-Content-Length'] = self.total_file_size - - if headers is not None: - http_request.headers.update(headers) - - response = self.client.request(method='POST', - uri=resumable_media_link, - auth_token=auth_token, - http_request=http_request) - - self.upload_uri = (response.getheader('location') or - response.getheader('Location')) - - _InitSession = _init_session - - def upload_chunk(self, start_byte, content_bytes): - """Uploads a byte range (chunk) to the resumable upload server. - - Args: - start_byte: int The byte offset of the total file where the byte range - passed in lives. - content_bytes: str The file contents of this chunk. - - Returns: - The final Atom entry created on the server. The entry object's type will - be the class specified in self.desired_class. - - Raises: - RequestError if the unique upload uri is not set or the - server returns something other than an HTTP 308 when the upload is - incomplete. - """ - if self.upload_uri is None: - raise RequestError('Resumable upload request not initialized.') - - # Adjustment if last byte range is less than defined chunk size. - chunk_size = self.chunk_size - if len(content_bytes) <= chunk_size: - chunk_size = len(content_bytes) - - http_request = atom.http_core.HttpRequest() - http_request.add_body_part(content_bytes, self.content_type, - size=len(content_bytes)) - http_request.headers['Content-Range'] = ('bytes %s-%s/%s' - % (start_byte, - start_byte + chunk_size - 1, - self.total_file_size)) - - try: - response = self.client.request(method='POST', uri=self.upload_uri, - http_request=http_request, - desired_class=self.desired_class) - return response - except RequestError, error: - if error.status == 308: - return None - else: - raise error - - UploadChunk = upload_chunk - - def upload_file(self, resumable_media_link, entry=None, headers=None, - auth_token=None): - """Uploads an entire file in chunks using the resumable upload protocol. - - If you are interested in pausing an upload or controlling the chunking - yourself, use the upload_chunk() method instead. - - Args: - resumable_media_link: str The full URL for the #resumable-create-media for - starting a resumable upload request. - entry: A (optional) gdata.data.GDEntry containging metadata to create the - upload from. - headers: dict Additional headers to send in the initial request to create - the resumable upload request. These headers will override any default - headers sent in the request. For example: headers={'Slug': 'MyTitle'}. - auth_token: (optional) An object which sets the Authorization HTTP header - in its modify_request method. Recommended classes include - gdata.gauth.ClientLoginToken and gdata.gauth.AuthSubToken - among others. - - Returns: - The final Atom entry created on the server. The entry object's type will - be the class specified in self.desired_class. - - Raises: - RequestError if anything other than a HTTP 308 is returned - when the request raises an exception. - """ - self._init_session(resumable_media_link, headers=headers, - auth_token=auth_token, entry=entry) - - start_byte = 0 - entry = None - - while not entry: - entry = self.upload_chunk( - start_byte, self.file_handle.read(self.chunk_size)) - start_byte += self.chunk_size - - return entry - - UploadFile = upload_file - - def update_file(self, entry_or_resumable_edit_link, headers=None, force=False, - auth_token=None): - """Updates the contents of an existing file using the resumable protocol. - - If you are interested in pausing an upload or controlling the chunking - yourself, use the upload_chunk() method instead. - - Args: - entry_or_resumable_edit_link: object or string A gdata.data.GDEntry for - the entry/file to update or the full uri of the link with rel - #resumable-edit-media. - headers: dict Additional headers to send in the initial request to create - the resumable upload request. These headers will override any default - headers sent in the request. For example: headers={'Slug': 'MyTitle'}. - force boolean (optional) True to force an update and set the If-Match - header to '*'. If False and entry_or_resumable_edit_link is a - gdata.data.GDEntry object, its etag value is used. Otherwise this - parameter should be set to True to force the update. - auth_token: (optional) An object which sets the Authorization HTTP header - in its modify_request method. Recommended classes include - gdata.gauth.ClientLoginToken and gdata.gauth.AuthSubToken - among others. - - Returns: - The final Atom entry created on the server. The entry object's type will - be the class specified in self.desired_class. - - Raises: - RequestError if anything other than a HTTP 308 is returned - when the request raises an exception. - """ - # Need to override the POST request for a resumable update (required). - customer_headers = {'X-HTTP-Method-Override': 'PUT'} - - if headers is not None: - customer_headers.update(headers) - - if isinstance(entry_or_resumable_edit_link, gdata.data.GDEntry): - resumable_edit_link = entry_or_resumable_edit_link.find_url( - 'http://schemas.google.com/g/2005#resumable-edit-media') - customer_headers['If-Match'] = entry_or_resumable_edit_link.etag - else: - resumable_edit_link = entry_or_resumable_edit_link - - if force: - customer_headers['If-Match'] = '*' - - return self.upload_file(resumable_edit_link, headers=customer_headers, - auth_token=auth_token) - - UpdateFile = update_file - - def query_upload_status(self, uri=None): - """Queries the current status of a resumable upload request. - - Args: - uri: str (optional) A resumable upload uri to query and override the one - that is set in this object. - - Returns: - An integer representing the file position (byte) to resume the upload from - or True if the upload is complete. - - Raises: - RequestError if anything other than a HTTP 308 is returned - when the request raises an exception. - """ - # Override object's unique upload uri. - if uri is None: - uri = self.upload_uri - - http_request = atom.http_core.HttpRequest() - http_request.headers['Content-Length'] = '0' - http_request.headers['Content-Range'] = 'bytes */%s' % self.total_file_size - - try: - response = self.client.request( - method='POST', uri=uri, http_request=http_request) - if response.status == 201: - return True - else: - raise error_from_response( - '%s returned by server' % response.status, response, RequestError) - except RequestError, error: - if error.status == 308: - for pair in error.headers: - if pair[0].capitalize() == 'Range': - return int(pair[1].split('-')[1]) + 1 - else: - raise error - - QueryUploadStatus = query_upload_status diff --git a/gdata/codesearch/__init__.py b/gdata/codesearch/__init__.py deleted file mode 100644 index fa23ef021d..0000000000 --- a/gdata/codesearch/__init__.py +++ /dev/null @@ -1,136 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright (c) 2007 Benoit Chesneau -# -# Permission to use, copy, modify, and distribute this software for any -# purpose with or without fee is hereby granted, provided that the above -# copyright notice and this permission notice appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF -# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - - -"""Contains extensions to Atom objects used by Google Codesearch""" - -__author__ = 'Benoit Chesneau' - - -import atom -import gdata - - -CODESEARCH_NAMESPACE='http://schemas.google.com/codesearch/2006' -CODESEARCH_TEMPLATE='{http://shema.google.com/codesearch/2006}%s' - - -class Match(atom.AtomBase): - """ The Google Codesearch match element """ - _tag = 'match' - _namespace = CODESEARCH_NAMESPACE - _children = atom.AtomBase._children.copy() - _attributes = atom.AtomBase._attributes.copy() - _attributes['lineNumber'] = 'line_number' - _attributes['type'] = 'type' - - def __init__(self, line_number=None, type=None, extension_elements=None, - extension_attributes=None, text=None): - self.text = text - self.type = type - self.line_number = line_number - self.extension_elements = extension_elements or [] - self.extension_attributes = extension_attributes or {} - - -class File(atom.AtomBase): - """ The Google Codesearch file element""" - _tag = 'file' - _namespace = CODESEARCH_NAMESPACE - _children = atom.AtomBase._children.copy() - _attributes = atom.AtomBase._attributes.copy() - _attributes['name'] = 'name' - - def __init__(self, name=None, extension_elements=None, - extension_attributes=None, text=None): - self.text = text - self.name = name - self.extension_elements = extension_elements or [] - self.extension_attributes = extension_attributes or {} - - -class Package(atom.AtomBase): - """ The Google Codesearch package element""" - _tag = 'package' - _namespace = CODESEARCH_NAMESPACE - _children = atom.AtomBase._children.copy() - _attributes = atom.AtomBase._attributes.copy() - _attributes['name'] = 'name' - _attributes['uri'] = 'uri' - - def __init__(self, name=None, uri=None, extension_elements=None, - extension_attributes=None, text=None): - self.text = text - self.name = name - self.uri = uri - self.extension_elements = extension_elements or [] - self.extension_attributes = extension_attributes or {} - - -class CodesearchEntry(gdata.GDataEntry): - """ Google codesearch atom entry""" - _tag = gdata.GDataEntry._tag - _namespace = gdata.GDataEntry._namespace - _children = gdata.GDataEntry._children.copy() - _attributes = gdata.GDataEntry._attributes.copy() - - _children['{%s}file' % CODESEARCH_NAMESPACE] = ('file', File) - _children['{%s}package' % CODESEARCH_NAMESPACE] = ('package', Package) - _children['{%s}match' % CODESEARCH_NAMESPACE] = ('match', [Match]) - - def __init__(self, author=None, category=None, content=None, - atom_id=None, link=None, published=None, - title=None, updated=None, - match=None, - extension_elements=None, extension_attributes=None, text=None): - - gdata.GDataEntry.__init__(self, author=author, category=category, - content=content, atom_id=atom_id, link=link, - published=published, title=title, - updated=updated, text=None) - - self.match = match or [] - - -def CodesearchEntryFromString(xml_string): - """Converts an XML string into a CodesearchEntry object. - - Args: - xml_string: string The XML describing a Codesearch feed entry. - - Returns: - A CodesearchEntry object corresponding to the given XML. - """ - return atom.CreateClassFromXMLString(CodesearchEntry, xml_string) - - -class CodesearchFeed(gdata.GDataFeed): - """feed containing list of Google codesearch Items""" - _tag = gdata.GDataFeed._tag - _namespace = gdata.GDataFeed._namespace - _children = gdata.GDataFeed._children.copy() - _attributes = gdata.GDataFeed._attributes.copy() - _children['{%s}entry' % atom.ATOM_NAMESPACE] = ('entry', [CodesearchEntry]) - - -def CodesearchFeedFromString(xml_string): - """Converts an XML string into a CodesearchFeed object. - Args: - xml_string: string The XML describing a Codesearch feed. - Returns: - A CodeseartchFeed object corresponding to the given XML. - """ - return atom.CreateClassFromXMLString(CodesearchFeed, xml_string) diff --git a/gdata/codesearch/service.py b/gdata/codesearch/service.py deleted file mode 100644 index d6e23359a4..0000000000 --- a/gdata/codesearch/service.py +++ /dev/null @@ -1,109 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright (c) 2007 Benoit Chesneau -# -# Permission to use, copy, modify, and distribute this software for any -# purpose with or without fee is hereby granted, provided that the above -# copyright notice and this permission notice appear in all copies. -# -# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF -# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - - -"""CodesearchService extends GDataService to streamline Google Codesearch -operations""" - - -__author__ = 'Benoit Chesneau' - - -import atom -import gdata.service -import gdata.codesearch - - -class CodesearchService(gdata.service.GDataService): - """Client extension for Google codesearch service""" - - def __init__(self, email=None, password=None, source=None, - server='www.google.com', additional_headers=None, **kwargs): - """Creates a client for the Google codesearch 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: 'www.google.com'. - **kwargs: The other parameters to pass to gdata.service.GDataService - constructor. - """ - gdata.service.GDataService.__init__( - self, email=email, password=password, service='codesearch', - source=source, server=server, additional_headers=additional_headers, - **kwargs) - - def Query(self, uri, converter=gdata.codesearch.CodesearchFeedFromString): - """Queries the Codesearch feed and returns the resulting feed of - entries. - - Args: - uri: string The full URI to be queried. This can contain query - parameters, a hostname, or simply the relative path to a Document - List feed. The DocumentQuery object is useful when constructing - query parameters. - converter: func (optional) A function which will be executed on the - retrieved item, generally to render it into a Python object. - By default the CodesearchFeedFromString function is used to - return a CodesearchFeed object. This is because most feed - queries will result in a feed and not a single entry. - - Returns : - A CodesearchFeed objects representing the feed returned by the server - """ - return self.Get(uri, converter=converter) - - def GetSnippetsFeed(self, text_query=None): - """Retrieve Codesearch feed for a keyword - - Args: - text_query : string (optional) The contents of the q query parameter. This - string is URL escaped upon conversion to a URI. - Returns: - A CodesearchFeed objects representing the feed returned by the server - """ - - query=gdata.codesearch.service.CodesearchQuery(text_query=text_query) - feed = self.Query(query.ToUri()) - return feed - - -class CodesearchQuery(gdata.service.Query): - """Object used to construct the query to the Google Codesearch feed. here only as a shorcut""" - - def __init__(self, feed='/codesearch/feeds/search', text_query=None, - params=None, categories=None): - """Constructor for Codesearch Query. - - Args: - feed: string (optional) The path for the feed. (e.g. '/codesearch/feeds/search') - 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. - - Yelds: - A CodesearchQuery object to construct a URI based on Codesearch feed - """ - - gdata.service.Query.__init__(self, feed, text_query, params, categories) diff --git a/gdata/contacts/__init__.py b/gdata/contacts/__init__.py deleted file mode 100644 index 363a4a5240..0000000000 --- a/gdata/contacts/__init__.py +++ /dev/null @@ -1,741 +0,0 @@ -#!/usr/bin/env python -# -# Copyright 2009 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. - -"""Contains extensions to ElementWrapper objects used with Google Contacts.""" - -__author__ = 'dbrattli (Dag Brattli)' - - -import atom -import gdata - - -## Constants from http://code.google.com/apis/gdata/elements.html ## -REL_HOME = 'http://schemas.google.com/g/2005#home' -REL_WORK = 'http://schemas.google.com/g/2005#work' -REL_OTHER = 'http://schemas.google.com/g/2005#other' - -# AOL Instant Messenger protocol -IM_AIM = 'http://schemas.google.com/g/2005#AIM' -IM_MSN = 'http://schemas.google.com/g/2005#MSN' # MSN Messenger protocol -IM_YAHOO = 'http://schemas.google.com/g/2005#YAHOO' # Yahoo Messenger protocol -IM_SKYPE = 'http://schemas.google.com/g/2005#SKYPE' # Skype protocol -IM_QQ = 'http://schemas.google.com/g/2005#QQ' # QQ protocol -# Google Talk protocol -IM_GOOGLE_TALK = 'http://schemas.google.com/g/2005#GOOGLE_TALK' -IM_ICQ = 'http://schemas.google.com/g/2005#ICQ' # ICQ protocol -IM_JABBER = 'http://schemas.google.com/g/2005#JABBER' # Jabber protocol -IM_NETMEETING = 'http://schemas.google.com/g/2005#netmeeting' # NetMeeting - -PHOTO_LINK_REL = 'http://schemas.google.com/contacts/2008/rel#photo' -PHOTO_EDIT_LINK_REL = 'http://schemas.google.com/contacts/2008/rel#edit-photo' - -# Different phone types, for moro info see: -# http://code.google.com/apis/gdata/docs/2.0/elements.html#gdPhoneNumber -PHONE_CAR = 'http://schemas.google.com/g/2005#car' -PHONE_FAX = 'http://schemas.google.com/g/2005#fax' -PHONE_GENERAL = 'http://schemas.google.com/g/2005#general' -PHONE_HOME = REL_HOME -PHONE_HOME_FAX = 'http://schemas.google.com/g/2005#home_fax' -PHONE_INTERNAL = 'http://schemas.google.com/g/2005#internal-extension' -PHONE_MOBILE = 'http://schemas.google.com/g/2005#mobile' -PHONE_OTHER = REL_OTHER -PHONE_PAGER = 'http://schemas.google.com/g/2005#pager' -PHONE_SATELLITE = 'http://schemas.google.com/g/2005#satellite' -PHONE_VOIP = 'http://schemas.google.com/g/2005#voip' -PHONE_WORK = REL_WORK -PHONE_WORK_FAX = 'http://schemas.google.com/g/2005#work_fax' -PHONE_WORK_MOBILE = 'http://schemas.google.com/g/2005#work_mobile' -PHONE_WORK_PAGER = 'http://schemas.google.com/g/2005#work_pager' -PHONE_MAIN = 'http://schemas.google.com/g/2005#main' -PHONE_ASSISTANT = 'http://schemas.google.com/g/2005#assistant' -PHONE_CALLBACK = 'http://schemas.google.com/g/2005#callback' -PHONE_COMPANY_MAIN = 'http://schemas.google.com/g/2005#company_main' -PHONE_ISDN = 'http://schemas.google.com/g/2005#isdn' -PHONE_OTHER_FAX = 'http://schemas.google.com/g/2005#other_fax' -PHONE_RADIO = 'http://schemas.google.com/g/2005#radio' -PHONE_TELEX = 'http://schemas.google.com/g/2005#telex' -PHONE_TTY_TDD = 'http://schemas.google.com/g/2005#tty_tdd' - -EXTERNAL_ID_ORGANIZATION = 'organization' - -RELATION_MANAGER = 'manager' - -CONTACTS_NAMESPACE = 'http://schemas.google.com/contact/2008' - - -class GDataBase(atom.AtomBase): - """The Google Contacts intermediate class from atom.AtomBase.""" - - _namespace = gdata.GDATA_NAMESPACE - _children = atom.AtomBase._children.copy() - _attributes = atom.AtomBase._attributes.copy() - - def __init__(self, text=None, - extension_elements=None, extension_attributes=None): - self.text = text - self.extension_elements = extension_elements or [] - self.extension_attributes = extension_attributes or {} - - -class ContactsBase(GDataBase): - """The Google Contacts intermediate class for Contacts namespace.""" - - _namespace = CONTACTS_NAMESPACE - - -class OrgName(GDataBase): - """The Google Contacts OrgName element.""" - - _tag = 'orgName' - - -class OrgTitle(GDataBase): - """The Google Contacts OrgTitle element.""" - - _tag = 'orgTitle' - - -class OrgDepartment(GDataBase): - """The Google Contacts OrgDepartment element.""" - - _tag = 'orgDepartment' - - -class OrgJobDescription(GDataBase): - """The Google Contacts OrgJobDescription element.""" - - _tag = 'orgJobDescription' - - -class Where(GDataBase): - """The Google Contacts Where element.""" - - _tag = 'where' - _children = GDataBase._children.copy() - _attributes = GDataBase._attributes.copy() - _attributes['rel'] = 'rel' - _attributes['label'] = 'label' - _attributes['valueString'] = 'value_string' - - def __init__(self, value_string=None, rel=None, label=None, - text=None, extension_elements=None, extension_attributes=None): - GDataBase.__init__(self, text=text, extension_elements=extension_elements, - extension_attributes=extension_attributes) - self.rel = rel - self.label = label - self.value_string = value_string - - -class When(GDataBase): - """The Google Contacts When element.""" - - _tag = 'when' - _children = GDataBase._children.copy() - _attributes = GDataBase._attributes.copy() - _attributes['startTime'] = 'start_time' - _attributes['endTime'] = 'end_time' - _attributes['label'] = 'label' - - def __init__(self, start_time=None, end_time=None, label=None, - text=None, extension_elements=None, extension_attributes=None): - GDataBase.__init__(self, text=text, extension_elements=extension_elements, - extension_attributes=extension_attributes) - self.start_time = start_time - self.end_time = end_time - self.label = label - - -class Organization(GDataBase): - """The Google Contacts Organization element.""" - - _tag = 'organization' - _children = GDataBase._children.copy() - _attributes = GDataBase._attributes.copy() - _attributes['label'] = 'label' - _attributes['rel'] = 'rel' - _attributes['primary'] = 'primary' - _children['{%s}orgName' % GDataBase._namespace] = ( - 'org_name', OrgName) - _children['{%s}orgTitle' % GDataBase._namespace] = ( - 'org_title', OrgTitle) - _children['{%s}orgDepartment' % GDataBase._namespace] = ( - 'org_department', OrgDepartment) - _children['{%s}orgJobDescription' % GDataBase._namespace] = ( - 'org_job_description', OrgJobDescription) - #_children['{%s}where' % GDataBase._namespace] = ('where', Where) - - def __init__(self, label=None, rel=None, primary='false', org_name=None, - org_title=None, org_department=None, org_job_description=None, - where=None, text=None, - extension_elements=None, extension_attributes=None): - GDataBase.__init__(self, text=text, extension_elements=extension_elements, - extension_attributes=extension_attributes) - self.label = label - self.rel = rel or REL_OTHER - self.primary = primary - self.org_name = org_name - self.org_title = org_title - self.org_department = org_department - self.org_job_description = org_job_description - self.where = where - - -class PostalAddress(GDataBase): - """The Google Contacts PostalAddress element.""" - - _tag = 'postalAddress' - _children = GDataBase._children.copy() - _attributes = GDataBase._attributes.copy() - _attributes['rel'] = 'rel' - _attributes['primary'] = 'primary' - - def __init__(self, primary=None, rel=None, text=None, - extension_elements=None, extension_attributes=None): - GDataBase.__init__(self, text=text, extension_elements=extension_elements, - extension_attributes=extension_attributes) - self.rel = rel or REL_OTHER - self.primary = primary - - -class FormattedAddress(GDataBase): - """The Google Contacts FormattedAddress element.""" - - _tag = 'formattedAddress' - - -class StructuredPostalAddress(GDataBase): - """The Google Contacts StructuredPostalAddress element.""" - - _tag = 'structuredPostalAddress' - _children = GDataBase._children.copy() - _attributes = GDataBase._attributes.copy() - _attributes['rel'] = 'rel' - _attributes['primary'] = 'primary' - _children['{%s}formattedAddress' % GDataBase._namespace] = ( - 'formatted_address', FormattedAddress) - - def __init__(self, rel=None, primary=None, - formatted_address=None, text=None, - extension_elements=None, extension_attributes=None): - GDataBase.__init__(self, text=text, extension_elements=extension_elements, - extension_attributes=extension_attributes) - self.rel = rel or REL_OTHER - self.primary = primary - self.formatted_address = formatted_address - - -class IM(GDataBase): - """The Google Contacts IM element.""" - - _tag = 'im' - _children = GDataBase._children.copy() - _attributes = GDataBase._attributes.copy() - _attributes['address'] = 'address' - _attributes['primary'] = 'primary' - _attributes['protocol'] = 'protocol' - _attributes['label'] = 'label' - _attributes['rel'] = 'rel' - - def __init__(self, primary='false', rel=None, address=None, protocol=None, - label=None, text=None, - extension_elements=None, extension_attributes=None): - GDataBase.__init__(self, text=text, extension_elements=extension_elements, - extension_attributes=extension_attributes) - self.protocol = protocol - self.address = address - self.primary = primary - self.rel = rel or REL_OTHER - self.label = label - - -class Email(GDataBase): - """The Google Contacts Email element.""" - - _tag = 'email' - _children = GDataBase._children.copy() - _attributes = GDataBase._attributes.copy() - _attributes['address'] = 'address' - _attributes['primary'] = 'primary' - _attributes['rel'] = 'rel' - _attributes['label'] = 'label' - - def __init__(self, label=None, rel=None, address=None, primary='false', - text=None, extension_elements=None, extension_attributes=None): - GDataBase.__init__(self, text=text, extension_elements=extension_elements, - extension_attributes=extension_attributes) - self.label = label - self.rel = rel or REL_OTHER - self.address = address - self.primary = primary - - -class PhoneNumber(GDataBase): - """The Google Contacts PhoneNumber element.""" - - _tag = 'phoneNumber' - _children = GDataBase._children.copy() - _attributes = GDataBase._attributes.copy() - _attributes['label'] = 'label' - _attributes['rel'] = 'rel' - _attributes['uri'] = 'uri' - _attributes['primary'] = 'primary' - - def __init__(self, label=None, rel=None, uri=None, primary='false', - text=None, extension_elements=None, extension_attributes=None): - GDataBase.__init__(self, text=text, extension_elements=extension_elements, - extension_attributes=extension_attributes) - self.label = label - self.rel = rel or REL_OTHER - self.uri = uri - self.primary = primary - - -class Nickname(ContactsBase): - """The Google Contacts Nickname element.""" - - _tag = 'nickname' - - -class Occupation(ContactsBase): - """The Google Contacts Occupation element.""" - - _tag = 'occupation' - - -class Gender(ContactsBase): - """The Google Contacts Gender element.""" - - _tag = 'gender' - _children = ContactsBase._children.copy() - _attributes = ContactsBase._attributes.copy() - _attributes['value'] = 'value' - - def __init__(self, value=None, - text=None, extension_elements=None, extension_attributes=None): - ContactsBase.__init__(self, text=text, - extension_elements=extension_elements, - extension_attributes=extension_attributes) - self.value = value - - -class Birthday(ContactsBase): - """The Google Contacts Birthday element.""" - - _tag = 'birthday' - _children = ContactsBase._children.copy() - _attributes = ContactsBase._attributes.copy() - _attributes['when'] = 'when' - - def __init__(self, when=None, - text=None, extension_elements=None, extension_attributes=None): - ContactsBase.__init__(self, text=text, - extension_elements=extension_elements, - extension_attributes=extension_attributes) - self.when = when - - -class Relation(ContactsBase): - """The Google Contacts Relation element.""" - - _tag = 'relation' - _children = ContactsBase._children.copy() - _attributes = ContactsBase._attributes.copy() - _attributes['label'] = 'label' - _attributes['rel'] = 'rel' - - def __init__(self, label=None, rel=None, - text=None, extension_elements=None, extension_attributes=None): - ContactsBase.__init__(self, text=text, - extension_elements=extension_elements, - extension_attributes=extension_attributes) - self.label = label - self.rel = rel - - -def RelationFromString(xml_string): - return atom.CreateClassFromXMLString(Relation, xml_string) - - -class UserDefinedField(ContactsBase): - """The Google Contacts UserDefinedField element.""" - - _tag = 'userDefinedField' - _children = ContactsBase._children.copy() - _attributes = ContactsBase._attributes.copy() - _attributes['key'] = 'key' - _attributes['value'] = 'value' - - def __init__(self, key=None, value=None, - text=None, extension_elements=None, extension_attributes=None): - ContactsBase.__init__(self, text=text, - extension_elements=extension_elements, - extension_attributes=extension_attributes) - self.key = key - self.value = value - - -def UserDefinedFieldFromString(xml_string): - return atom.CreateClassFromXMLString(UserDefinedField, xml_string) - - -class Website(ContactsBase): - """The Google Contacts Website element.""" - - _tag = 'website' - _children = ContactsBase._children.copy() - _attributes = ContactsBase._attributes.copy() - _attributes['href'] = 'href' - _attributes['label'] = 'label' - _attributes['primary'] = 'primary' - _attributes['rel'] = 'rel' - - def __init__(self, href=None, label=None, primary='false', rel=None, - text=None, extension_elements=None, extension_attributes=None): - ContactsBase.__init__(self, text=text, - extension_elements=extension_elements, - extension_attributes=extension_attributes) - self.href = href - self.label = label - self.primary = primary - self.rel = rel - - -def WebsiteFromString(xml_string): - return atom.CreateClassFromXMLString(Website, xml_string) - - -class ExternalId(ContactsBase): - """The Google Contacts ExternalId element.""" - - _tag = 'externalId' - _children = ContactsBase._children.copy() - _attributes = ContactsBase._attributes.copy() - _attributes['label'] = 'label' - _attributes['rel'] = 'rel' - _attributes['value'] = 'value' - - def __init__(self, label=None, rel=None, value=None, - text=None, extension_elements=None, extension_attributes=None): - ContactsBase.__init__(self, text=text, - extension_elements=extension_elements, - extension_attributes=extension_attributes) - self.label = label - self.rel = rel - self.value = value - - -def ExternalIdFromString(xml_string): - return atom.CreateClassFromXMLString(ExternalId, xml_string) - - -class Event(ContactsBase): - """The Google Contacts Event element.""" - - _tag = 'event' - _children = ContactsBase._children.copy() - _attributes = ContactsBase._attributes.copy() - _attributes['label'] = 'label' - _attributes['rel'] = 'rel' - _children['{%s}when' % ContactsBase._namespace] = ('when', When) - - def __init__(self, label=None, rel=None, when=None, - text=None, extension_elements=None, extension_attributes=None): - ContactsBase.__init__(self, text=text, - extension_elements=extension_elements, - extension_attributes=extension_attributes) - self.label = label - self.rel = rel - self.when = when - - -def EventFromString(xml_string): - return atom.CreateClassFromXMLString(Event, xml_string) - - -class Deleted(GDataBase): - """The Google Contacts Deleted element.""" - - _tag = 'deleted' - - -class GroupMembershipInfo(ContactsBase): - """The Google Contacts GroupMembershipInfo element.""" - - _tag = 'groupMembershipInfo' - - _children = ContactsBase._children.copy() - _attributes = ContactsBase._attributes.copy() - _attributes['deleted'] = 'deleted' - _attributes['href'] = 'href' - - def __init__(self, deleted=None, href=None, text=None, - extension_elements=None, extension_attributes=None): - ContactsBase.__init__(self, text=text, - extension_elements=extension_elements, - extension_attributes=extension_attributes) - self.deleted = deleted - self.href = href - - -class PersonEntry(gdata.BatchEntry): - """Base class for ContactEntry and ProfileEntry.""" - - _children = gdata.BatchEntry._children.copy() - _children['{%s}organization' % gdata.GDATA_NAMESPACE] = ( - 'organization', [Organization]) - _children['{%s}phoneNumber' % gdata.GDATA_NAMESPACE] = ( - 'phone_number', [PhoneNumber]) - _children['{%s}nickname' % CONTACTS_NAMESPACE] = ('nickname', Nickname) - _children['{%s}occupation' % CONTACTS_NAMESPACE] = ('occupation', Occupation) - _children['{%s}gender' % CONTACTS_NAMESPACE] = ('gender', Gender) - _children['{%s}birthday' % CONTACTS_NAMESPACE] = ('birthday', Birthday) - _children['{%s}postalAddress' % gdata.GDATA_NAMESPACE] = ('postal_address', - [PostalAddress]) - _children['{%s}structuredPostalAddress' % gdata.GDATA_NAMESPACE] = ( - 'structured_pstal_address', [StructuredPostalAddress]) - _children['{%s}email' % gdata.GDATA_NAMESPACE] = ('email', [Email]) - _children['{%s}im' % gdata.GDATA_NAMESPACE] = ('im', [IM]) - _children['{%s}relation' % CONTACTS_NAMESPACE] = ('relation', [Relation]) - _children['{%s}userDefinedField' % CONTACTS_NAMESPACE] = ( - 'user_defined_field', [UserDefinedField]) - _children['{%s}website' % CONTACTS_NAMESPACE] = ('website', [Website]) - _children['{%s}externalId' % CONTACTS_NAMESPACE] = ( - 'external_id', [ExternalId]) - _children['{%s}event' % CONTACTS_NAMESPACE] = ('event', [Event]) - # The following line should be removed once the Python support - # for GData 2.0 is mature. - _attributes = gdata.BatchEntry._attributes.copy() - _attributes['{%s}etag' % gdata.GDATA_NAMESPACE] = 'etag' - - def __init__(self, author=None, category=None, content=None, - atom_id=None, link=None, published=None, - title=None, updated=None, organization=None, phone_number=None, - nickname=None, occupation=None, gender=None, birthday=None, - postal_address=None, structured_pstal_address=None, email=None, - im=None, relation=None, user_defined_field=None, website=None, - external_id=None, event=None, batch_operation=None, - batch_id=None, batch_status=None, text=None, - extension_elements=None, extension_attributes=None, etag=None): - gdata.BatchEntry.__init__(self, author=author, category=category, - content=content, atom_id=atom_id, link=link, - published=published, - batch_operation=batch_operation, - batch_id=batch_id, batch_status=batch_status, - title=title, updated=updated) - self.organization = organization or [] - self.phone_number = phone_number or [] - self.nickname = nickname - self.occupation = occupation - self.gender = gender - self.birthday = birthday - self.postal_address = postal_address or [] - self.structured_pstal_address = structured_pstal_address or [] - self.email = email or [] - self.im = im or [] - self.relation = relation or [] - self.user_defined_field = user_defined_field or [] - self.website = website or [] - self.external_id = external_id or [] - self.event = event or [] - self.text = text - self.extension_elements = extension_elements or [] - self.extension_attributes = extension_attributes or {} - # The following line should be removed once the Python support - # for GData 2.0 is mature. - self.etag = etag - - -class ContactEntry(PersonEntry): - """A Google Contact flavor of an Atom Entry.""" - - _children = PersonEntry._children.copy() - - _children['{%s}deleted' % gdata.GDATA_NAMESPACE] = ('deleted', Deleted) - _children['{%s}groupMembershipInfo' % CONTACTS_NAMESPACE] = ( - 'group_membership_info', [GroupMembershipInfo]) - _children['{%s}extendedProperty' % gdata.GDATA_NAMESPACE] = ( - 'extended_property', [gdata.ExtendedProperty]) - # Overwrite the organization rule in PersonEntry so that a ContactEntry - # may only contain one element. - _children['{%s}organization' % gdata.GDATA_NAMESPACE] = ( - 'organization', Organization) - - def __init__(self, author=None, category=None, content=None, - atom_id=None, link=None, published=None, - title=None, updated=None, organization=None, phone_number=None, - nickname=None, occupation=None, gender=None, birthday=None, - postal_address=None, structured_pstal_address=None, email=None, - im=None, relation=None, user_defined_field=None, website=None, - external_id=None, event=None, batch_operation=None, - batch_id=None, batch_status=None, text=None, - extension_elements=None, extension_attributes=None, etag=None, - deleted=None, extended_property=None, - group_membership_info=None): - PersonEntry.__init__(self, author=author, category=category, - content=content, atom_id=atom_id, link=link, - published=published, title=title, updated=updated, - organization=organization, phone_number=phone_number, - nickname=nickname, occupation=occupation, - gender=gender, birthday=birthday, - postal_address=postal_address, - structured_pstal_address=structured_pstal_address, - email=email, im=im, relation=relation, - user_defined_field=user_defined_field, - website=website, external_id=external_id, event=event, - batch_operation=batch_operation, batch_id=batch_id, - batch_status=batch_status, text=text, - extension_elements=extension_elements, - extension_attributes=extension_attributes, etag=etag) - self.deleted = deleted - self.extended_property = extended_property or [] - self.group_membership_info = group_membership_info or [] - - def GetPhotoLink(self): - for a_link in self.link: - if a_link.rel == PHOTO_LINK_REL: - return a_link - return None - - def GetPhotoEditLink(self): - for a_link in self.link: - if a_link.rel == PHOTO_EDIT_LINK_REL: - return a_link - return None - - -def ContactEntryFromString(xml_string): - return atom.CreateClassFromXMLString(ContactEntry, xml_string) - - -class ContactsFeed(gdata.BatchFeed, gdata.LinkFinder): - """A Google Contacts feed flavor of an Atom Feed.""" - - _children = gdata.BatchFeed._children.copy() - - _children['{%s}entry' % atom.ATOM_NAMESPACE] = ('entry', [ContactEntry]) - - def __init__(self, author=None, category=None, contributor=None, - generator=None, icon=None, atom_id=None, link=None, logo=None, - rights=None, subtitle=None, title=None, updated=None, - entry=None, total_results=None, start_index=None, - items_per_page=None, extension_elements=None, - extension_attributes=None, text=None): - gdata.BatchFeed.__init__(self, author=author, category=category, - contributor=contributor, generator=generator, - icon=icon, atom_id=atom_id, link=link, - logo=logo, rights=rights, subtitle=subtitle, - title=title, updated=updated, entry=entry, - total_results=total_results, - start_index=start_index, - items_per_page=items_per_page, - extension_elements=extension_elements, - extension_attributes=extension_attributes, - text=text) - - -def ContactsFeedFromString(xml_string): - return atom.CreateClassFromXMLString(ContactsFeed, xml_string) - - -class GroupEntry(gdata.BatchEntry): - """Represents a contact group.""" - _children = gdata.BatchEntry._children.copy() - _children['{%s}extendedProperty' % gdata.GDATA_NAMESPACE] = ( - 'extended_property', [gdata.ExtendedProperty]) - - def __init__(self, author=None, category=None, content=None, - contributor=None, atom_id=None, link=None, published=None, - rights=None, source=None, summary=None, control=None, - title=None, updated=None, - extended_property=None, batch_operation=None, batch_id=None, - batch_status=None, - extension_elements=None, extension_attributes=None, text=None): - gdata.BatchEntry.__init__(self, author=author, category=category, - content=content, - atom_id=atom_id, link=link, published=published, - batch_operation=batch_operation, - batch_id=batch_id, batch_status=batch_status, - title=title, updated=updated) - self.extended_property = extended_property or [] - - -def GroupEntryFromString(xml_string): - return atom.CreateClassFromXMLString(GroupEntry, xml_string) - - -class GroupsFeed(gdata.BatchFeed): - """A Google contact groups feed flavor of an Atom Feed.""" - _children = gdata.BatchFeed._children.copy() - _children['{%s}entry' % atom.ATOM_NAMESPACE] = ('entry', [GroupEntry]) - - -def GroupsFeedFromString(xml_string): - return atom.CreateClassFromXMLString(GroupsFeed, xml_string) - - -class ProfileEntry(PersonEntry): - """A Google Profiles flavor of an Atom Entry.""" - - -def ProfileEntryFromString(xml_string): - """Converts an XML string into a ProfileEntry object. - - Args: - xml_string: string The XML describing a Profile entry. - - Returns: - A ProfileEntry object corresponding to the given XML. - """ - return atom.CreateClassFromXMLString(ProfileEntry, xml_string) - - -class ProfilesFeed(gdata.BatchFeed, gdata.LinkFinder): - """A Google Profiles feed flavor of an Atom Feed.""" - - _children = gdata.BatchFeed._children.copy() - _children['{%s}entry' % atom.ATOM_NAMESPACE] = ('entry', [ProfileEntry]) - - def __init__(self, author=None, category=None, contributor=None, - generator=None, icon=None, atom_id=None, link=None, logo=None, - rights=None, subtitle=None, title=None, updated=None, - entry=None, total_results=None, start_index=None, - items_per_page=None, extension_elements=None, - extension_attributes=None, text=None): - gdata.BatchFeed.__init__(self, author=author, category=category, - contributor=contributor, generator=generator, - icon=icon, atom_id=atom_id, link=link, - logo=logo, rights=rights, subtitle=subtitle, - title=title, updated=updated, entry=entry, - total_results=total_results, - start_index=start_index, - items_per_page=items_per_page, - extension_elements=extension_elements, - extension_attributes=extension_attributes, - text=text) - - -def ProfilesFeedFromString(xml_string): - """Converts an XML string into a ProfilesFeed object. - - Args: - xml_string: string The XML describing a Profiles feed. - - Returns: - A ProfilesFeed object corresponding to the given XML. - """ - return atom.CreateClassFromXMLString(ProfilesFeed, xml_string) - diff --git a/gdata/contacts/client.py b/gdata/contacts/client.py deleted file mode 100644 index ef712ce709..0000000000 --- a/gdata/contacts/client.py +++ /dev/null @@ -1,474 +0,0 @@ -#!/usr/bin/env python -# -# Copyright (C) 2009 Google 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. -from types import ListType, DictionaryType - - -"""Contains a client to communicate with the Contacts servers. - -For documentation on the Contacts API, see: -http://code.google.com/apis/contatcs/ -""" - -__author__ = 'vinces1979@gmail.com (Vince Spicer)' - - -import gdata.client -import gdata.contacts.data -import atom.data -import atom.http_core -import gdata.gauth - - -class ContactsClient(gdata.client.GDClient): - api_version = '3' - auth_service = 'cp' - server = "www.google.com" - contact_list = "default" - auth_scopes = gdata.gauth.AUTH_SCOPES['cp'] - - def get_feed_uri(self, kind='contacts', contact_list=None, projection='full', - scheme="http"): - """Builds a feed URI. - - Args: - kind: The type of feed to return, typically 'groups' or 'contacts'. - Default value: 'contacts'. - contact_list: The contact list to return a feed for. - Default value: self.contact_list. - projection: The projection to apply to the feed contents, for example - 'full', 'base', 'base/12345', 'full/batch'. Default value: 'full'. - scheme: The URL scheme such as 'http' or 'https', None to return a - relative URI without hostname. - - Returns: - A feed URI using the given kind, contact list, and projection. - Example: '/m8/feeds/contacts/default/full'. - """ - contact_list = contact_list or self.contact_list - if kind == 'profiles': - contact_list = 'domain/%s' % contact_list - prefix = scheme and '%s://%s' % (scheme, self.server) or '' - return '%s/m8/feeds/%s/%s/%s' % (prefix, kind, contact_list, projection) - - GetFeedUri = get_feed_uri - - def get_contact(self, uri, desired_class=gdata.contacts.data.ContactEntry, - auth_token=None, **kwargs): - return self.get_feed(uri, auth_token=auth_token, - desired_class=desired_class, **kwargs) - - - GetContact = get_contact - - - def create_contact(self, new_contact, insert_uri=None, auth_token=None, **kwargs): - """Adds an new contact to Google Contacts. - - Args: - new_contact: atom.Entry or subclass A new contact which is to be added to - Google Contacts. - insert_uri: the URL to post new contacts to the feed - url_params: dict (optional) Additional URL parameters to be included - in the insertion request. - escape_params: boolean (optional) If true, the url_parameters will be - escaped before they are included in the request. - - Returns: - On successful insert, an entry containing the contact created - On failure, a RequestError is raised of the form: - {'status': HTTP status code from server, - 'reason': HTTP reason from the server, - 'body': HTTP body of the server's response} - """ - insert_uri = insert_uri or self.GetFeedUri() - return self.Post(new_contact, insert_uri, - auth_token=auth_token, **kwargs) - - CreateContact = create_contact - - def add_contact(self, new_contact, insert_uri=None, auth_token=None, - billing_information=None, birthday=None, calendar_link=None, **kwargs): - """Adds an new contact to Google Contacts. - - Args: - new_contact: atom.Entry or subclass A new contact which is to be added to - Google Contacts. - insert_uri: the URL to post new contacts to the feed - url_params: dict (optional) Additional URL parameters to be included - in the insertion request. - escape_params: boolean (optional) If true, the url_parameters will be - escaped before they are included in the request. - - Returns: - On successful insert, an entry containing the contact created - On failure, a RequestError is raised of the form: - {'status': HTTP status code from server, - 'reason': HTTP reason from the server, - 'body': HTTP body of the server's response} - """ - - contact = gdata.contacts.data.ContactEntry() - - if billing_information is not None: - if not isinstance(billing_information, gdata.contacts.data.BillingInformation): - billing_information = gdata.contacts.data.BillingInformation(text=billing_information) - - contact.billing_information = billing_information - - if birthday is not None: - if not isinstance(birthday, gdata.contacts.data.Birthday): - birthday = gdata.contacts.data.Birthday(when=birthday) - - contact.birthday = birthday - - if calendar_link is not None: - if type(calendar_link) is not ListType: - calendar_link = [calendar_link] - - for link in calendar_link: - if not isinstance(link, gdata.contacts.data.CalendarLink): - if type(link) is not DictionaryType: - raise TypeError, "calendar_link Requires dictionary not %s" % type(link) - - link = gdata.contacts.data.CalendarLink( - rel=link.get("rel", None), - label=link.get("label", None), - primary=link.get("primary", None), - href=link.get("href", None), - ) - - contact.calendar_link.append(link) - - insert_uri = insert_uri or self.GetFeedUri() - return self.Post(contact, insert_uri, - auth_token=auth_token, **kwargs) - - AddContact = add_contact - - def get_contacts(self, desired_class=gdata.contacts.data.ContactsFeed, - auth_token=None, **kwargs): - """Obtains a feed with the contacts belonging to the current user. - - Args: - auth_token: An object which sets the Authorization HTTP header in its - modify_request method. Recommended classes include - gdata.gauth.ClientLoginToken and gdata.gauth.AuthSubToken - among others. Represents the current user. Defaults to None - and if None, this method will look for a value in the - auth_token member of SpreadsheetsClient. - desired_class: class descended from atom.core.XmlElement to which a - successful response should be converted. If there is no - converter function specified (desired_class=None) then the - desired_class will be used in calling the - atom.core.parse function. If neither - the desired_class nor the converter is specified, an - HTTP reponse object will be returned. Defaults to - gdata.spreadsheets.data.SpreadsheetsFeed. - """ - return self.get_feed(self.GetFeedUri(), auth_token=auth_token, - desired_class=desired_class, **kwargs) - - GetContacts = get_contacts - - def get_group(self, uri=None, desired_class=gdata.contacts.data.GroupEntry, - auth_token=None, **kwargs): - """ Get a single groups details - Args: - uri: the group uri or id - """ - return self.get(uri, desired_class=desired_class, auth_token=auth_token, **kwargs) - - GetGroup = get_group - - def get_groups(self, uri=None, desired_class=gdata.contacts.data.GroupsFeed, - auth_token=None, **kwargs): - uri = uri or self.GetFeedUri('groups') - return self.get_feed(uri, desired_class=desired_class, auth_token=auth_token, **kwargs) - - GetGroups = get_groups - - def create_group(self, new_group, insert_uri=None, url_params=None, - desired_class=None): - insert_uri = insert_uri or self.GetFeedUri('groups') - return self.Post(new_group, insert_uri, url_params=url_params, - desired_class=desired_class) - - CreateGroup = create_group - - def update_group(self, edit_uri, updated_group, url_params=None, - escape_params=True, desired_class=None): - return self.Put(updated_group, self._CleanUri(edit_uri), - url_params=url_params, - escape_params=escape_params, - desired_class=desired_class) - - UpdateGroup = update_group - - def delete_group(self, edit_uri, extra_headers=None, - url_params=None, escape_params=True): - return self.Delete(self._CleanUri(edit_uri), - url_params=url_params, escape_params=escape_params) - - DeleteGroup = delete_group - - def change_photo(self, media, contact_entry_or_url, content_type=None, - content_length=None): - """Change the photo for the contact by uploading a new photo. - - Performs a PUT against the photo edit URL to send the binary data for the - photo. - - Args: - media: filename, file-like-object, or a gdata.MediaSource object to send. - contact_entry_or_url: ContactEntry or str If it is a ContactEntry, this - method will search for an edit photo link URL and - perform a PUT to the URL. - content_type: str (optional) the mime type for the photo data. This is - necessary if media is a file or file name, but if media - is a MediaSource object then the media object can contain - the mime type. If media_type is set, it will override the - mime type in the media object. - content_length: int or str (optional) Specifying the content length is - only required if media is a file-like object. If media - is a filename, the length is determined using - os.path.getsize. If media is a MediaSource object, it is - assumed that it already contains the content length. - """ - if isinstance(contact_entry_or_url, gdata.contacts.data.ContactEntry): - url = contact_entry_or_url.GetPhotoEditLink().href - else: - url = contact_entry_or_url - if isinstance(media, gdata.MediaSource): - payload = media - # If the media object is a file-like object, then use it as the file - # handle in the in the MediaSource. - elif hasattr(media, 'read'): - payload = gdata.MediaSource(file_handle=media, - content_type=content_type, content_length=content_length) - # Assume that the media object is a file name. - else: - payload = gdata.MediaSource(content_type=content_type, - content_length=content_length, file_path=media) - return self.Put(payload, url) - - ChangePhoto = change_photo - - def get_photo(self, contact_entry_or_url): - """Retrives the binary data for the contact's profile photo as a string. - - Args: - contact_entry_or_url: a gdata.contacts.ContactEntry objecr or a string - containing the photo link's URL. If the contact entry does not - contain a photo link, the image will not be fetched and this method - will return None. - """ - # TODO: add the ability to write out the binary image data to a file, - # reading and writing a chunk at a time to avoid potentially using up - # large amounts of memory. - url = None - if isinstance(contact_entry_or_url, gdata.contacts.data.ContactEntry): - photo_link = contact_entry_or_url.GetPhotoLink() - if photo_link: - url = photo_link.href - else: - url = contact_entry_or_url - if url: - return self.Get(url, desired_class=str) - else: - return None - - GetPhoto = get_photo - - def delete_photo(self, contact_entry_or_url): - url = None - if isinstance(contact_entry_or_url, gdata.contacts.data.ContactEntry): - url = contact_entry_or_url.GetPhotoEditLink().href - else: - url = contact_entry_or_url - if url: - self.Delete(url) - - DeletePhoto = delete_photo - - def get_profiles_feed(self, uri=None): - """Retrieves a feed containing all domain's profiles. - - Args: - uri: string (optional) the URL to retrieve the profiles feed, - for example /m8/feeds/profiles/default/full - - Returns: - On success, a ProfilesFeed containing the profiles. - On failure, raises a RequestError. - """ - - uri = uri or self.GetFeedUri('profiles') - return self.Get(uri, - desired_class=gdata.contacts.data.ProfilesFeedFromString) - - GetProfilesFeed = get_profiles_feed - - def get_profile(self, uri): - """Retrieves a domain's profile for the user. - - Args: - uri: string the URL to retrieve the profiles feed, - for example /m8/feeds/profiles/default/full/username - - Returns: - On success, a ProfileEntry containing the profile for the user. - On failure, raises a RequestError - """ - return self.Get(uri, - desired_class=gdata.contacts.data.ProfileEntryFromString) - - GetProfile = get_profile - - def update_profile(self, edit_uri, updated_profile, auth_token=None, **kwargs): - """Updates an existing profile. - - Args: - edit_uri: string The edit link URI for the element being updated - updated_profile: string atom.Entry or subclass containing - the Atom Entry which will replace the profile which is - stored at the edit_url. - url_params: dict (optional) Additional URL parameters to be included - in the update request. - escape_params: boolean (optional) If true, the url_params will be - escaped before they are included in the request. - - Returns: - On successful update, a httplib.HTTPResponse containing the server's - response to the PUT request. - On failure, raises a RequestError. - """ - return self.Put(updated_profile, self._CleanUri(edit_uri), - desired_class=gdata.contacts.data.ProfileEntryFromString) - - UpdateProfile = update_profile - - def execute_batch(self, batch_feed, url, desired_class=None): - """Sends a batch request feed to the server. - - Args: - batch_feed: gdata.contacts.ContactFeed A feed containing batch - request entries. Each entry contains the operation to be performed - on the data contained in the entry. For example an entry with an - operation type of insert will be used as if the individual entry - had been inserted. - url: str The batch URL to which these operations should be applied. - converter: Function (optional) The function used to convert the server's - response to an object. - - Returns: - The results of the batch request's execution on the server. If the - default converter is used, this is stored in a ContactsFeed. - """ - return self.Post(batch_feed, url, desired_class=desired_class) - - ExecuteBatch = execute_batch - - def execute_batch_profiles(self, batch_feed, url, - desired_class=gdata.contacts.data.ProfilesFeedFromString): - """Sends a batch request feed to the server. - - Args: - batch_feed: gdata.profiles.ProfilesFeed A feed containing batch - request entries. Each entry contains the operation to be performed - on the data contained in the entry. For example an entry with an - operation type of insert will be used as if the individual entry - had been inserted. - url: string The batch URL to which these operations should be applied. - converter: Function (optional) The function used to convert the server's - response to an object. The default value is - gdata.profiles.ProfilesFeedFromString. - - Returns: - The results of the batch request's execution on the server. If the - default converter is used, this is stored in a ProfilesFeed. - """ - return self.Post(batch_feed, url, desired_class=desired_class) - - ExecuteBatchProfiles = execute_batch_profiles - - -class ContactsQuery(gdata.client.Query): - """ - Create a custom Contacts Query - - Full specs can be found at: U{Contacts query parameters reference - } - """ - - def __init__(self, feed=None, group=None, orderby=None, showdeleted=None, - sortorder=None, requirealldeleted=None, **kwargs): - """ - @param max_results: The maximum number of entries to return. If you want - to receive all of the contacts, rather than only the default maximum, you - can specify a very large number for max-results. - @param start-index: The 1-based index of the first result to be retrieved. - @param updated-min: The lower bound on entry update dates. - @param group: Constrains the results to only the contacts belonging to the - group specified. Value of this parameter specifies group ID - @param orderby: Sorting criterion. The only supported value is - lastmodified. - @param showdeleted: Include deleted contacts in the returned contacts feed - @pram sortorder: Sorting order direction. Can be either ascending or - descending. - @param requirealldeleted: Only relevant if showdeleted and updated-min - are also provided. It dictates the behavior of the server in case it - detects that placeholders of some entries deleted since the point in - time specified as updated-min may have been lost. - """ - gdata.client.Query.__init__(self, **kwargs) - self.group = group - self.orderby = orderby - self.sortorder = sortorder - self.showdeleted = showdeleted - - def modify_request(self, http_request): - if self.group: - gdata.client._add_query_param('group', self.group, http_request) - if self.orderby: - gdata.client._add_query_param('orderby', self.orderby, http_request) - if self.sortorder: - gdata.client._add_query_param('sortorder', self.sortorder, http_request) - if self.showdeleted: - gdata.client._add_query_param('showdeleted', self.showdeleted, http_request) - gdata.client.Query.modify_request(self, http_request) - - ModifyRequest = modify_request - - -class ProfilesQuery(gdata.client.Query): - def __init__(self, feed=None): - self.feed = feed or 'http://www.google.com/m8/feeds/profiles/default/full' - - - def _CleanUri(self, uri): - """Sanitizes a feed URI. - - Args: - uri: The URI to sanitize, can be relative or absolute. - - Returns: - The given URI without its http://server prefix, if any. - Keeps the leading slash of the URI. - """ - url_prefix = 'http://%s' % self.server - if uri.startswith(url_prefix): - uri = uri[len(url_prefix):] - return uri diff --git a/gdata/contacts/data.py b/gdata/contacts/data.py deleted file mode 100644 index 782a05dae3..0000000000 --- a/gdata/contacts/data.py +++ /dev/null @@ -1,474 +0,0 @@ -#!/usr/bin/env python -# -# Copyright (C) 2009 Google 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. - -"""Data model classes for parsing and generating XML for the Contacts API.""" - - -__author__ = 'vinces1979@gmail.com (Vince Spicer)' - - -import atom.core -import gdata -import gdata.data - - -PHOTO_LINK_REL = 'http://schemas.google.com/contacts/2008/rel#photo' -PHOTO_EDIT_LINK_REL = 'http://schemas.google.com/contacts/2008/rel#edit-photo' - -EXTERNAL_ID_ORGANIZATION = 'organization' - -RELATION_MANAGER = 'manager' - -CONTACTS_NAMESPACE = 'http://schemas.google.com/contact/2008' -CONTACTS_TEMPLATE = '{%s}%%s' % CONTACTS_NAMESPACE - - -class BillingInformation(atom.core.XmlElement): - """ - gContact:billingInformation - Specifies billing information of the entity represented by the contact. The element cannot be repeated. - """ - - _qname = CONTACTS_TEMPLATE % 'billingInformation' - - -class Birthday(atom.core.XmlElement): - """ - Stores birthday date of the person represented by the contact. The element cannot be repeated. - """ - - _qname = CONTACTS_TEMPLATE % 'birthday' - when = 'when' - - -class CalendarLink(atom.core.XmlElement): - """ - Storage for URL of the contact's calendar. The element can be repeated. - """ - - _qname = CONTACTS_TEMPLATE % 'calendarLink' - rel = 'rel' - label = 'label' - primary = 'primary' - href = 'href' - - -class DirectoryServer(atom.core.XmlElement): - """ - A directory server associated with this contact. - May not be repeated. - """ - - _qname = CONTACTS_TEMPLATE % 'directoryServer' - - -class Event(atom.core.XmlElement): - """ - These elements describe events associated with a contact. - They may be repeated - """ - - _qname = CONTACTS_TEMPLATE % 'event' - label = 'label' - rel = 'rel' - when = gdata.data.When - - -class ExternalId(atom.core.XmlElement): - """ - Describes an ID of the contact in an external system of some kind. - This element may be repeated. - """ - - _qname = CONTACTS_TEMPLATE % 'externalId' - - -def ExternalIdFromString(xml_string): - return atom.core.parse(ExternalId, xml_string) - - -class Gender(atom.core.XmlElement): - """ - Specifies the gender of the person represented by the contact. - The element cannot be repeated. - """ - - _qname = CONTACTS_TEMPLATE % 'directoryServer' - value = 'value' - - -class Hobby(atom.core.XmlElement): - """ - Describes an ID of the contact in an external system of some kind. - This element may be repeated. - """ - - _qname = CONTACTS_TEMPLATE % 'hobby' - - -class Initials(atom.core.XmlElement): - """ Specifies the initials of the person represented by the contact. The - element cannot be repeated. """ - - _qname = CONTACTS_TEMPLATE % 'initials' - - -class Jot(atom.core.XmlElement): - """ - Storage for arbitrary pieces of information about the contact. Each jot - has a type specified by the rel attribute and a text value. - The element can be repeated. - """ - - _qname = CONTACTS_TEMPLATE % 'jot' - rel = 'rel' - - -class Language(atom.core.XmlElement): - """ - Specifies the preferred languages of the contact. - The element can be repeated. - - The language must be specified using one of two mutually exclusive methods: - using the freeform @label attribute, or using the @code attribute, whose value - must conform to the IETF BCP 47 specification. - """ - - _qname = CONTACTS_TEMPLATE % 'language' - code = 'code' - label = 'label' - - -class MaidenName(atom.core.XmlElement): - """ - Specifies maiden name of the person represented by the contact. - The element cannot be repeated. - """ - - _qname = CONTACTS_TEMPLATE % 'maidenName' - - -class Mileage(atom.core.XmlElement): - """ - Specifies the mileage for the entity represented by the contact. - Can be used for example to document distance needed for reimbursement - purposes. The value is not interpreted. The element cannot be repeated. - """ - - _qname = CONTACTS_TEMPLATE % 'mileage' - - -class NickName(atom.core.XmlElement): - """ - Specifies the nickname of the person represented by the contact. - The element cannot be repeated. - """ - - _qname = CONTACTS_TEMPLATE % 'nickname' - - -class Occupation(atom.core.XmlElement): - """ - Specifies the occupation/profession of the person specified by the contact. - The element cannot be repeated. - """ - - _qname = CONTACTS_TEMPLATE % 'occupation' - - -class Priority(atom.core.XmlElement): - """ - Classifies importance of the contact into 3 categories: - * Low - * Normal - * High - - The priority element cannot be repeated. - """ - - _qname = CONTACTS_TEMPLATE % 'priority' - - -class Relation(atom.core.XmlElement): - """ - This element describe another entity (usually a person) that is in a - relation of some kind with the contact. - """ - - _qname = CONTACTS_TEMPLATE % 'relation' - rel = 'rel' - label = 'label' - - -class Sensitivity(atom.core.XmlElement): - """ - Classifies sensitivity of the contact into the following categories: - * Confidential - * Normal - * Personal - * Private - - The sensitivity element cannot be repeated. - """ - - _qname = CONTACTS_TEMPLATE % 'sensitivity' - rel = 'rel' - - -class UserDefinedField(atom.core.XmlElement): - """ - Represents an arbitrary key-value pair attached to the contact. - """ - - _qname = CONTACTS_TEMPLATE % 'userDefinedField' - key = 'key' - value = 'value' - - -def UserDefinedFieldFromString(xml_string): - return atom.core.parse(UserDefinedField, xml_string) - - -class Website(atom.core.XmlElement): - """ - Describes websites associated with the contact, including links. - May be repeated. - """ - - _qname = CONTACTS_TEMPLATE % 'website' - - href = 'href' - label = 'label' - primary = 'primary' - rel = 'rel' - - -def WebsiteFromString(xml_string): - return atom.core.parse(Website, xml_string) - - -class HouseName(atom.core.XmlElement): - """ - Used in places where houses or buildings have names (and - not necessarily numbers), eg. "The Pillars". - """ - - _qname = CONTACTS_TEMPLATE % 'housename' - - -class Street(atom.core.XmlElement): - """ - Can be street, avenue, road, etc. This element also includes the house - number and room/apartment/flat/floor number. - """ - - _qname = CONTACTS_TEMPLATE % 'street' - - -class POBox(atom.core.XmlElement): - """ - Covers actual P.O. boxes, drawers, locked bags, etc. This is usually but not - always mutually exclusive with street - """ - - _qname = CONTACTS_TEMPLATE % 'pobox' - - -class Neighborhood(atom.core.XmlElement): - """ - This is used to disambiguate a street address when a city contains more than - one street with the same name, or to specify a small place whose mail is - routed through a larger postal town. In China it could be a county or a - minor city. - """ - - _qname = CONTACTS_TEMPLATE % 'neighborhood' - - -class City(atom.core.XmlElement): - """ - Can be city, village, town, borough, etc. This is the postal town and not - necessarily the place of residence or place of business. - """ - - _qname = CONTACTS_TEMPLATE % 'city' - - -class SubRegion(atom.core.XmlElement): - """ - Handles administrative districts such as U.S. or U.K. counties that are not - used for mail addressing purposes. Subregion is not intended for - delivery addresses. - """ - - _qname = CONTACTS_TEMPLATE % 'subregion' - - -class Region(atom.core.XmlElement): - """ - A state, province, county (in Ireland), Land (in Germany), - departement (in France), etc. - """ - - _qname = CONTACTS_TEMPLATE % 'region' - - -class PostalCode(atom.core.XmlElement): - """ - Postal code. Usually country-wide, but sometimes specific to the - city (e.g. "2" in "Dublin 2, Ireland" addresses). - """ - - _qname = CONTACTS_TEMPLATE % 'postcode' - - -class Country(atom.core.XmlElement): - """ The name or code of the country. """ - - _qname = CONTACTS_TEMPLATE % 'country' - - -class PersonEntry(gdata.data.BatchEntry): - """Represents a google contact""" - - billing_information = BillingInformation - birthday = Birthday - calendar_link = [CalendarLink] - directory_server = DirectoryServer - event = [Event] - external_id = [ExternalId] - gender = Gender - hobby = [Hobby] - initals = Initials - jot = [Jot] - language= [Language] - maiden_name = MaidenName - mileage = Mileage - nickname = NickName - occupation = Occupation - priority = Priority - relation = [Relation] - sensitivity = Sensitivity - user_defined_field = [UserDefinedField] - website = [Website] - - name = gdata.data.Name - phone_number = [gdata.data.PhoneNumber] - organization = gdata.data.Organization - postal_address = [gdata.data.PostalAddress] - email = [gdata.data.Email] - im = [gdata.data.Im] - structured_postal_address = [gdata.data.StructuredPostalAddress] - extended_property = [gdata.data.ExtendedProperty] - - -class Deleted(atom.core.XmlElement): - """If present, indicates that this contact has been deleted.""" - _qname = gdata.GDATA_TEMPLATE % 'deleted' - - -class GroupMembershipInfo(atom.core.XmlElement): - """ - Identifies the group to which the contact belongs or belonged. - The group is referenced by its id. - """ - - _qname = CONTACTS_TEMPLATE % 'groupMembershipInfo' - - href = 'href' - deleted = 'deleted' - - -class ContactEntry(PersonEntry): - """A Google Contacts flavor of an Atom Entry.""" - - deleted = Deleted - group_membership_info = [GroupMembershipInfo] - organization = gdata.data.Organization - - def GetPhotoLink(self): - for a_link in self.link: - if a_link.rel == PHOTO_LINK_REL: - return a_link - return None - - def GetPhotoEditLink(self): - for a_link in self.link: - if a_link.rel == PHOTO_EDIT_LINK_REL: - return a_link - return None - - -class ContactsFeed(gdata.data.BatchFeed): - """A collection of Contacts.""" - entry = [ContactEntry] - - -class SystemGroup(atom.core.XmlElement): - """The contacts systemGroup element. - - When used within a contact group entry, indicates that the group in - question is one of the predefined system groups.""" - - _qname = CONTACTS_TEMPLATE % 'systemGroup' - id = 'id' - - -class GroupEntry(gdata.data.BatchEntry): - """Represents a contact group.""" - extended_property = [gdata.data.ExtendedProperty] - system_group = SystemGroup - - -class GroupsFeed(gdata.data.BatchFeed): - """A Google contact groups feed flavor of an Atom Feed.""" - entry = [GroupEntry] - - -class ProfileEntry(PersonEntry): - """A Google Profiles flavor of an Atom Entry.""" - - -def ProfileEntryFromString(xml_string): - """Converts an XML string into a ProfileEntry object. - - Args: - xml_string: string The XML describing a Profile entry. - - Returns: - A ProfileEntry object corresponding to the given XML. - """ - return atom.core.parse(ProfileEntry, xml_string) - - -class ProfilesFeed(gdata.data.BatchFeed): - """A Google Profiles feed flavor of an Atom Feed.""" - _qname = atom.data.ATOM_TEMPLATE % 'feed' - entry = [ProfileEntry] - - -def ProfilesFeedFromString(xml_string): - """Converts an XML string into a ProfilesFeed object. - - Args: - xml_string: string The XML describing a Profiles feed. - - Returns: - A ProfilesFeed object corresponding to the given XML. - """ - return atom.core.parse(ProfilesFeed, xml_string) - - diff --git a/gdata/contacts/service.py b/gdata/contacts/service.py deleted file mode 100644 index a9270177e4..0000000000 --- a/gdata/contacts/service.py +++ /dev/null @@ -1,427 +0,0 @@ -#!/usr/bin/env python -# -# Copyright 2009 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. - -"""ContactsService extends the GDataService for Google Contacts operations. - - ContactsService: Provides methods to query feeds and manipulate items. - Extends GDataService. - - DictionaryToParamList: Function which converts a dictionary into a list of - URL arguments (represented as strings). This is a - utility function used in CRUD operations. -""" - -__author__ = 'dbrattli (Dag Brattli)' - - -import gdata -import gdata.calendar -import gdata.service - - -DEFAULT_BATCH_URL = ('http://www.google.com/m8/feeds/contacts/default/full' - '/batch') -DEFAULT_PROFILES_BATCH_URL = ('http://www.google.com' - '/m8/feeds/profiles/default/full/batch') - -GDATA_VER_HEADER = 'GData-Version' - - -class Error(Exception): - pass - - -class RequestError(Error): - pass - - -class ContactsService(gdata.service.GDataService): - """Client for the Google Contacts service.""" - - def __init__(self, email=None, password=None, source=None, - server='www.google.com', additional_headers=None, - contact_list='default', **kwargs): - """Creates a client for the Contacts 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: 'www.google.com'. - contact_list: string (optional) The name of the default contact list to - use when no URI is specified to the methods of the service. - Default value: 'default' (the logged in user's contact list). - **kwargs: The other parameters to pass to gdata.service.GDataService - constructor. - """ - - self.contact_list = contact_list - gdata.service.GDataService.__init__( - self, email=email, password=password, service='cp', source=source, - server=server, additional_headers=additional_headers, **kwargs) - - def GetFeedUri(self, kind='contacts', contact_list=None, projection='full', - scheme=None): - """Builds a feed URI. - - Args: - kind: The type of feed to return, typically 'groups' or 'contacts'. - Default value: 'contacts'. - contact_list: The contact list to return a feed for. - Default value: self.contact_list. - projection: The projection to apply to the feed contents, for example - 'full', 'base', 'base/12345', 'full/batch'. Default value: 'full'. - scheme: The URL scheme such as 'http' or 'https', None to return a - relative URI without hostname. - - Returns: - A feed URI using the given kind, contact list, and projection. - Example: '/m8/feeds/contacts/default/full'. - """ - contact_list = contact_list or self.contact_list - if kind == 'profiles': - contact_list = 'domain/%s' % contact_list - prefix = scheme and '%s://%s' % (scheme, self.server) or '' - return '%s/m8/feeds/%s/%s/%s' % (prefix, kind, contact_list, projection) - - def GetContactsFeed(self, uri=None): - uri = uri or self.GetFeedUri() - return self.Get(uri, converter=gdata.contacts.ContactsFeedFromString) - - def GetContact(self, uri): - return self.Get(uri, converter=gdata.contacts.ContactEntryFromString) - - def CreateContact(self, new_contact, insert_uri=None, url_params=None, - escape_params=True): - """Adds an new contact to Google Contacts. - - Args: - new_contact: atom.Entry or subclass A new contact which is to be added to - Google Contacts. - insert_uri: the URL to post new contacts to the feed - url_params: dict (optional) Additional URL parameters to be included - in the insertion request. - escape_params: boolean (optional) If true, the url_parameters will be - escaped before they are included in the request. - - Returns: - On successful insert, an entry containing the contact created - On failure, a RequestError is raised of the form: - {'status': HTTP status code from server, - 'reason': HTTP reason from the server, - 'body': HTTP body of the server's response} - """ - insert_uri = insert_uri or self.GetFeedUri() - return self.Post(new_contact, insert_uri, url_params=url_params, - escape_params=escape_params, - converter=gdata.contacts.ContactEntryFromString) - - def UpdateContact(self, edit_uri, updated_contact, url_params=None, - escape_params=True): - """Updates an existing contact. - - Args: - edit_uri: string The edit link URI for the element being updated - updated_contact: string, atom.Entry or subclass containing - the Atom Entry which will replace the contact which is - stored at the edit_url - url_params: dict (optional) Additional URL parameters to be included - in the update request. - escape_params: boolean (optional) If true, the url_parameters will be - escaped before they are included in the request. - - Returns: - On successful update, a httplib.HTTPResponse containing the server's - response to the PUT request. - On failure, a RequestError is raised of the form: - {'status': HTTP status code from server, - 'reason': HTTP reason from the server, - 'body': HTTP body of the server's response} - """ - return self.Put(updated_contact, self._CleanUri(edit_uri), - url_params=url_params, - escape_params=escape_params, - converter=gdata.contacts.ContactEntryFromString) - - def DeleteContact(self, edit_uri, extra_headers=None, - url_params=None, escape_params=True): - """Removes an contact with the specified ID from Google Contacts. - - Args: - edit_uri: string The edit URL of the entry to be deleted. Example: - '/m8/feeds/contacts/default/full/xxx/yyy' - url_params: dict (optional) Additional URL parameters to be included - in the deletion request. - escape_params: boolean (optional) If true, the url_parameters will be - escaped before they are included in the request. - - Returns: - On successful delete, a httplib.HTTPResponse containing the server's - response to the DELETE request. - On failure, a RequestError is raised of the form: - {'status': HTTP status code from server, - 'reason': HTTP reason from the server, - 'body': HTTP body of the server's response} - """ - return self.Delete(self._CleanUri(edit_uri), - url_params=url_params, escape_params=escape_params) - - def GetGroupsFeed(self, uri=None): - uri = uri or self.GetFeedUri('groups') - return self.Get(uri, converter=gdata.contacts.GroupsFeedFromString) - - def CreateGroup(self, new_group, insert_uri=None, url_params=None, - escape_params=True): - insert_uri = insert_uri or self.GetFeedUri('groups') - return self.Post(new_group, insert_uri, url_params=url_params, - escape_params=escape_params, - converter=gdata.contacts.GroupEntryFromString) - - def UpdateGroup(self, edit_uri, updated_group, url_params=None, - escape_params=True): - return self.Put(updated_group, self._CleanUri(edit_uri), - url_params=url_params, - escape_params=escape_params, - converter=gdata.contacts.GroupEntryFromString) - - def DeleteGroup(self, edit_uri, extra_headers=None, - url_params=None, escape_params=True): - return self.Delete(self._CleanUri(edit_uri), - url_params=url_params, escape_params=escape_params) - - def ChangePhoto(self, media, contact_entry_or_url, content_type=None, - content_length=None): - """Change the photo for the contact by uploading a new photo. - - Performs a PUT against the photo edit URL to send the binary data for the - photo. - - Args: - media: filename, file-like-object, or a gdata.MediaSource object to send. - contact_entry_or_url: ContactEntry or str If it is a ContactEntry, this - method will search for an edit photo link URL and - perform a PUT to the URL. - content_type: str (optional) the mime type for the photo data. This is - necessary if media is a file or file name, but if media - is a MediaSource object then the media object can contain - the mime type. If media_type is set, it will override the - mime type in the media object. - content_length: int or str (optional) Specifying the content length is - only required if media is a file-like object. If media - is a filename, the length is determined using - os.path.getsize. If media is a MediaSource object, it is - assumed that it already contains the content length. - """ - if isinstance(contact_entry_or_url, gdata.contacts.ContactEntry): - url = contact_entry_or_url.GetPhotoEditLink().href - else: - url = contact_entry_or_url - if isinstance(media, gdata.MediaSource): - payload = media - # If the media object is a file-like object, then use it as the file - # handle in the in the MediaSource. - elif hasattr(media, 'read'): - payload = gdata.MediaSource(file_handle=media, - content_type=content_type, content_length=content_length) - # Assume that the media object is a file name. - else: - payload = gdata.MediaSource(content_type=content_type, - content_length=content_length, file_path=media) - return self.Put(payload, url, extra_headers={'If-Match': '*'}) - - def GetPhoto(self, contact_entry_or_url): - """Retrives the binary data for the contact's profile photo as a string. - - Args: - contact_entry_or_url: a gdata.contacts.ContactEntry objecr or a string - containing the photo link's URL. If the contact entry does not - contain a photo link, the image will not be fetched and this method - will return None. - """ - # TODO: add the ability to write out the binary image data to a file, - # reading and writing a chunk at a time to avoid potentially using up - # large amounts of memory. - url = None - if isinstance(contact_entry_or_url, gdata.contacts.ContactEntry): - photo_link = contact_entry_or_url.GetPhotoLink() - if photo_link: - url = photo_link.href - else: - url = contact_entry_or_url - if url: - return self.Get(url, converter=str) - else: - return None - - def DeletePhoto(self, contact_entry_or_url): - url = None - if isinstance(contact_entry_or_url, gdata.contacts.ContactEntry): - url = contact_entry_or_url.GetPhotoEditLink().href - else: - url = contact_entry_or_url - if url: - self.Delete(url, extra_headers={'If-Match': '*'}) - - def GetProfilesFeed(self, uri=None): - """Retrieves a feed containing all domain's profiles. - - Args: - uri: string (optional) the URL to retrieve the profiles feed, - for example /m8/feeds/profiles/default/full - - Returns: - On success, a ProfilesFeed containing the profiles. - On failure, raises a RequestError. - """ - - uri = uri or self.GetFeedUri('profiles') - return self.Get(uri, - converter=gdata.contacts.ProfilesFeedFromString) - - def GetProfile(self, uri): - """Retrieves a domain's profile for the user. - - Args: - uri: string the URL to retrieve the profiles feed, - for example /m8/feeds/profiles/default/full/username - - Returns: - On success, a ProfileEntry containing the profile for the user. - On failure, raises a RequestError - """ - return self.Get(uri, - converter=gdata.contacts.ProfileEntryFromString) - - def UpdateProfile(self, edit_uri, updated_profile, url_params=None, - escape_params=True): - """Updates an existing profile. - - Args: - edit_uri: string The edit link URI for the element being updated - updated_profile: string atom.Entry or subclass containing - the Atom Entry which will replace the profile which is - stored at the edit_url. - url_params: dict (optional) Additional URL parameters to be included - in the update request. - escape_params: boolean (optional) If true, the url_params will be - escaped before they are included in the request. - - Returns: - On successful update, a httplib.HTTPResponse containing the server's - response to the PUT request. - On failure, raises a RequestError. - """ - return self.Put(updated_profile, self._CleanUri(edit_uri), - url_params=url_params, escape_params=escape_params, - converter=gdata.contacts.ProfileEntryFromString) - - def ExecuteBatch(self, batch_feed, url, - converter=gdata.contacts.ContactsFeedFromString): - """Sends a batch request feed to the server. - - Args: - batch_feed: gdata.contacts.ContactFeed A feed containing batch - request entries. Each entry contains the operation to be performed - on the data contained in the entry. For example an entry with an - operation type of insert will be used as if the individual entry - had been inserted. - url: str The batch URL to which these operations should be applied. - converter: Function (optional) The function used to convert the server's - response to an object. The default value is ContactsFeedFromString. - - Returns: - The results of the batch request's execution on the server. If the - default converter is used, this is stored in a ContactsFeed. - """ - return self.Post(batch_feed, url, converter=converter) - - def ExecuteBatchProfiles(self, batch_feed, url, - converter=gdata.contacts.ProfilesFeedFromString): - """Sends a batch request feed to the server. - - Args: - batch_feed: gdata.profiles.ProfilesFeed A feed containing batch - request entries. Each entry contains the operation to be performed - on the data contained in the entry. For example an entry with an - operation type of insert will be used as if the individual entry - had been inserted. - url: string The batch URL to which these operations should be applied. - converter: Function (optional) The function used to convert the server's - response to an object. The default value is - gdata.profiles.ProfilesFeedFromString. - - Returns: - The results of the batch request's execution on the server. If the - default converter is used, this is stored in a ProfilesFeed. - """ - return self.Post(batch_feed, url, converter=converter) - - def _CleanUri(self, uri): - """Sanitizes a feed URI. - - Args: - uri: The URI to sanitize, can be relative or absolute. - - Returns: - The given URI without its http://server prefix, if any. - Keeps the leading slash of the URI. - """ - url_prefix = 'http://%s' % self.server - if uri.startswith(url_prefix): - uri = uri[len(url_prefix):] - return uri - -class ContactsQuery(gdata.service.Query): - - def __init__(self, feed=None, text_query=None, params=None, - categories=None, group=None): - self.feed = feed or '/m8/feeds/contacts/default/full' - if group: - self._SetGroup(group) - gdata.service.Query.__init__(self, feed=self.feed, text_query=text_query, - params=params, categories=categories) - - def _GetGroup(self): - if 'group' in self: - return self['group'] - else: - return None - - def _SetGroup(self, group_id): - self['group'] = group_id - - group = property(_GetGroup, _SetGroup, - doc='The group query parameter to find only contacts in this group') - -class GroupsQuery(gdata.service.Query): - - def __init__(self, feed=None, text_query=None, params=None, - categories=None): - self.feed = feed or '/m8/feeds/groups/default/full' - gdata.service.Query.__init__(self, feed=self.feed, text_query=text_query, - params=params, categories=categories) - - -class ProfilesQuery(gdata.service.Query): - """Constructs a query object for the profiles feed.""" - - def __init__(self, feed=None, text_query=None, params=None, - categories=None): - self.feed = feed or '/m8/feeds/profiles/default/full' - gdata.service.Query.__init__(self, feed=self.feed, text_query=text_query, - params=params, categories=categories) diff --git a/gdata/core.py b/gdata/core.py deleted file mode 100644 index 0661ec6a0a..0000000000 --- a/gdata/core.py +++ /dev/null @@ -1,279 +0,0 @@ -#!/usr/bin/env python -# -# Copyright (C) 2010 Google 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. - - -# This module is used for version 2 of the Google Data APIs. - - -__author__ = 'j.s@google.com (Jeff Scudder)' - - -"""Provides classes and methods for working with JSON-C. - -This module is experimental and subject to backwards incompatible changes. - - Jsonc: Class which represents JSON-C data and provides pythonic member - access which is a bit cleaner than working with plain old dicts. - parse_json: Converts a JSON-C string into a Jsonc object. - jsonc_to_string: Converts a Jsonc object into a string of JSON-C. -""" - - -try: - import simplejson -except ImportError: - try: - # Try to import from django, should work on App Engine - from django.utils import simplejson - except ImportError: - # Should work for Python2.6 and higher. - import json as simplejson - - -def _convert_to_jsonc(x): - """Builds a Jsonc objects which wraps the argument's members.""" - - if isinstance(x, dict): - jsonc_obj = Jsonc() - # Recursively transform all members of the dict. - # When converting a dict, we do not convert _name items into private - # Jsonc members. - for key, value in x.iteritems(): - jsonc_obj._dict[key] = _convert_to_jsonc(value) - return jsonc_obj - elif isinstance(x, list): - # Recursively transform all members of the list. - members = [] - for item in x: - members.append(_convert_to_jsonc(item)) - return members - else: - # Return the base object. - return x - - -def parse_json(json_string): - """Converts a JSON-C string into a Jsonc object. - - Args: - json_string: str or unicode The JSON to be parsed. - - Returns: - A new Jsonc object. - """ - - return _convert_to_jsonc(simplejson.loads(json_string)) - - -def parse_json_file(json_file): - return _convert_to_jsonc(simplejson.load(json_file)) - - -def jsonc_to_string(jsonc_obj): - """Converts a Jsonc object into a string of JSON-C.""" - - return simplejson.dumps(_convert_to_object(jsonc_obj)) - - -def prettify_jsonc(jsonc_obj, indentation=2): - """Converts a Jsonc object to a pretified (intented) JSON string.""" - - return simplejson.dumps(_convert_to_object(jsonc_obj), indent=indentation) - - - -def _convert_to_object(jsonc_obj): - """Creates a new dict or list which has the data in the Jsonc object. - - Used to convert the Jsonc object to a plain old Python object to simplify - conversion to a JSON-C string. - - Args: - jsonc_obj: A Jsonc object to be converted into simple Python objects - (dicts, lists, etc.) - - Returns: - Either a dict, list, or other object with members converted from Jsonc - objects to the corresponding simple Python object. - """ - - if isinstance(jsonc_obj, Jsonc): - plain = {} - for key, value in jsonc_obj._dict.iteritems(): - plain[key] = _convert_to_object(value) - return plain - elif isinstance(jsonc_obj, list): - plain = [] - for item in jsonc_obj: - plain.append(_convert_to_object(item)) - return plain - else: - return jsonc_obj - - -def _to_jsonc_name(member_name): - """Converts a Python style member name to a JSON-C style name. - - JSON-C uses camelCaseWithLower while Python tends to use - lower_with_underscores so this method converts as follows: - - spam becomes spam - spam_and_eggs becomes spamAndEggs - - Args: - member_name: str or unicode The Python syle name which should be - converted to JSON-C style. - - Returns: - The JSON-C style name as a str or unicode. - """ - - characters = [] - uppercase_next = False - for character in member_name: - if character == '_': - uppercase_next = True - elif uppercase_next: - characters.append(character.upper()) - uppercase_next = False - else: - characters.append(character) - return ''.join(characters) - - -class Jsonc(object): - """Represents JSON-C data in an easy to access object format. - - To access the members of a JSON structure which looks like this: - { - "data": { - "totalItems": 800, - "items": [ - { - "content": { - "1": "rtsp://v5.cache3.c.youtube.com/CiILENy.../0/0/0/video.3gp" - }, - "viewCount": 220101, - "commentCount": 22, - "favoriteCount": 201 - } - ] - }, - "apiVersion": "2.0" - } - - You would do the following: - x = gdata.core.parse_json(the_above_string) - # Gives you 800 - x.data.total_items - # Should be 22 - x.data.items[0].comment_count - # The apiVersion is '2.0' - x.api_version - - To create a Jsonc object which would produce the above JSON, you would do: - gdata.core.Jsonc( - api_version='2.0', - data=gdata.core.Jsonc( - total_items=800, - items=[ - gdata.core.Jsonc( - view_count=220101, - comment_count=22, - favorite_count=201, - content={ - '1': ('rtsp://v5.cache3.c.youtube.com' - '/CiILENy.../0/0/0/video.3gp')})])) - or - x = gdata.core.Jsonc() - x.api_version = '2.0' - x.data = gdata.core.Jsonc() - x.data.total_items = 800 - x.data.items = [] - # etc. - - How it works: - The JSON-C data is stored in an internal dictionary (._dict) and the - getattr, setattr, and delattr methods rewrite the name which you provide - to mirror the expected format in JSON-C. (For more details on name - conversion see _to_jsonc_name.) You may also access members using - getitem, setitem, delitem as you would for a dictionary. For example - x.data.total_items is equivalent to x['data']['totalItems'] - (Not all dict methods are supported so if you need something other than - the item operations, then you will want to use the ._dict member). - - You may need to use getitem or the _dict member to access certain - properties in cases where the JSON-C syntax does not map neatly to Python - objects. For example the YouTube Video feed has some JSON like this: - "content": {"1": "rtsp://v5.cache3.c.youtube.com..."...} - You cannot do x.content.1 in Python, so you would use the getitem as - follows: - x.content['1'] - or you could use the _dict member as follows: - x.content._dict['1'] - - If you need to create a new object with such a mapping you could use. - - x.content = gdata.core.Jsonc(_dict={'1': 'rtsp://cache3.c.youtube.com...'}) - """ - - def __init__(self, _dict=None, **kwargs): - json = _dict or {} - for key, value in kwargs.iteritems(): - if key.startswith('_'): - object.__setattr__(self, key, value) - else: - json[_to_jsonc_name(key)] = _convert_to_jsonc(value) - - object.__setattr__(self, '_dict', json) - - def __setattr__(self, name, value): - if name.startswith('_'): - object.__setattr__(self, name, value) - else: - object.__getattribute__( - self, '_dict')[_to_jsonc_name(name)] = _convert_to_jsonc(value) - - def __getattr__(self, name): - if name.startswith('_'): - object.__getattribute__(self, name) - else: - try: - return object.__getattribute__(self, '_dict')[_to_jsonc_name(name)] - except KeyError: - raise AttributeError( - 'No member for %s or [\'%s\']' % (name, _to_jsonc_name(name))) - - - def __delattr__(self, name): - if name.startswith('_'): - object.__delattr__(self, name) - else: - try: - del object.__getattribute__(self, '_dict')[_to_jsonc_name(name)] - except KeyError: - raise AttributeError( - 'No member for %s (or [\'%s\'])' % (name, _to_jsonc_name(name))) - - # For container methods pass-through to the underlying dict. - def __getitem__(self, key): - return self._dict[key] - - def __setitem__(self, key, value): - self._dict[key] = value - - def __delitem__(self, key): - del self._dict[key] diff --git a/gdata/data.py b/gdata/data.py deleted file mode 100644 index 3f03d49581..0000000000 --- a/gdata/data.py +++ /dev/null @@ -1,1186 +0,0 @@ -#!/usr/bin/env python -# -# Copyright (C) 2009 Google 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. - - -# This module is used for version 2 of the Google Data APIs. - - -"""Provides classes and constants for the XML in the Google Data namespace. - -Documentation for the raw XML which these classes represent can be found here: -http://code.google.com/apis/gdata/docs/2.0/elements.html -""" - - -__author__ = 'j.s@google.com (Jeff Scudder)' - - -import os -import atom.core -import atom.data - - -GDATA_TEMPLATE = '{http://schemas.google.com/g/2005}%s' -GD_TEMPLATE = GDATA_TEMPLATE -OPENSEARCH_TEMPLATE_V1 = '{http://a9.com/-/spec/opensearchrss/1.0/}%s' -OPENSEARCH_TEMPLATE_V2 = '{http://a9.com/-/spec/opensearch/1.1/}%s' -BATCH_TEMPLATE = '{http://schemas.google.com/gdata/batch}%s' - - -# Labels used in batch request entries to specify the desired CRUD operation. -BATCH_INSERT = 'insert' -BATCH_UPDATE = 'update' -BATCH_DELETE = 'delete' -BATCH_QUERY = 'query' - -EVENT_LOCATION = 'http://schemas.google.com/g/2005#event' -ALTERNATE_LOCATION = 'http://schemas.google.com/g/2005#event.alternate' -PARKING_LOCATION = 'http://schemas.google.com/g/2005#event.parking' - -CANCELED_EVENT = 'http://schemas.google.com/g/2005#event.canceled' -CONFIRMED_EVENT = 'http://schemas.google.com/g/2005#event.confirmed' -TENTATIVE_EVENT = 'http://schemas.google.com/g/2005#event.tentative' - -CONFIDENTIAL_EVENT = 'http://schemas.google.com/g/2005#event.confidential' -DEFAULT_EVENT = 'http://schemas.google.com/g/2005#event.default' -PRIVATE_EVENT = 'http://schemas.google.com/g/2005#event.private' -PUBLIC_EVENT = 'http://schemas.google.com/g/2005#event.public' - -OPAQUE_EVENT = 'http://schemas.google.com/g/2005#event.opaque' -TRANSPARENT_EVENT = 'http://schemas.google.com/g/2005#event.transparent' - -CHAT_MESSAGE = 'http://schemas.google.com/g/2005#message.chat' -INBOX_MESSAGE = 'http://schemas.google.com/g/2005#message.inbox' -SENT_MESSAGE = 'http://schemas.google.com/g/2005#message.sent' -SPAM_MESSAGE = 'http://schemas.google.com/g/2005#message.spam' -STARRED_MESSAGE = 'http://schemas.google.com/g/2005#message.starred' -UNREAD_MESSAGE = 'http://schemas.google.com/g/2005#message.unread' - -BCC_RECIPIENT = 'http://schemas.google.com/g/2005#message.bcc' -CC_RECIPIENT = 'http://schemas.google.com/g/2005#message.cc' -SENDER = 'http://schemas.google.com/g/2005#message.from' -REPLY_TO = 'http://schemas.google.com/g/2005#message.reply-to' -TO_RECIPIENT = 'http://schemas.google.com/g/2005#message.to' - -ASSISTANT_REL = 'http://schemas.google.com/g/2005#assistant' -CALLBACK_REL = 'http://schemas.google.com/g/2005#callback' -CAR_REL = 'http://schemas.google.com/g/2005#car' -COMPANY_MAIN_REL = 'http://schemas.google.com/g/2005#company_main' -FAX_REL = 'http://schemas.google.com/g/2005#fax' -HOME_REL = 'http://schemas.google.com/g/2005#home' -HOME_FAX_REL = 'http://schemas.google.com/g/2005#home_fax' -ISDN_REL = 'http://schemas.google.com/g/2005#isdn' -MAIN_REL = 'http://schemas.google.com/g/2005#main' -MOBILE_REL = 'http://schemas.google.com/g/2005#mobile' -OTHER_REL = 'http://schemas.google.com/g/2005#other' -OTHER_FAX_REL = 'http://schemas.google.com/g/2005#other_fax' -PAGER_REL = 'http://schemas.google.com/g/2005#pager' -RADIO_REL = 'http://schemas.google.com/g/2005#radio' -TELEX_REL = 'http://schemas.google.com/g/2005#telex' -TTL_TDD_REL = 'http://schemas.google.com/g/2005#tty_tdd' -WORK_REL = 'http://schemas.google.com/g/2005#work' -WORK_FAX_REL = 'http://schemas.google.com/g/2005#work_fax' -WORK_MOBILE_REL = 'http://schemas.google.com/g/2005#work_mobile' -WORK_PAGER_REL = 'http://schemas.google.com/g/2005#work_pager' -NETMEETING_REL = 'http://schemas.google.com/g/2005#netmeeting' -OVERALL_REL = 'http://schemas.google.com/g/2005#overall' -PRICE_REL = 'http://schemas.google.com/g/2005#price' -QUALITY_REL = 'http://schemas.google.com/g/2005#quality' -EVENT_REL = 'http://schemas.google.com/g/2005#event' -EVENT_ALTERNATE_REL = 'http://schemas.google.com/g/2005#event.alternate' -EVENT_PARKING_REL = 'http://schemas.google.com/g/2005#event.parking' - -AIM_PROTOCOL = 'http://schemas.google.com/g/2005#AIM' -MSN_PROTOCOL = 'http://schemas.google.com/g/2005#MSN' -YAHOO_MESSENGER_PROTOCOL = 'http://schemas.google.com/g/2005#YAHOO' -SKYPE_PROTOCOL = 'http://schemas.google.com/g/2005#SKYPE' -QQ_PROTOCOL = 'http://schemas.google.com/g/2005#QQ' -GOOGLE_TALK_PROTOCOL = 'http://schemas.google.com/g/2005#GOOGLE_TALK' -ICQ_PROTOCOL = 'http://schemas.google.com/g/2005#ICQ' -JABBER_PROTOCOL = 'http://schemas.google.com/g/2005#JABBER' - -REGULAR_COMMENTS = 'http://schemas.google.com/g/2005#regular' -REVIEW_COMMENTS = 'http://schemas.google.com/g/2005#reviews' - -MAIL_BOTH = 'http://schemas.google.com/g/2005#both' -MAIL_LETTERS = 'http://schemas.google.com/g/2005#letters' -MAIL_PARCELS = 'http://schemas.google.com/g/2005#parcels' -MAIL_NEITHER = 'http://schemas.google.com/g/2005#neither' - -GENERAL_ADDRESS = 'http://schemas.google.com/g/2005#general' -LOCAL_ADDRESS = 'http://schemas.google.com/g/2005#local' - -OPTIONAL_ATENDEE = 'http://schemas.google.com/g/2005#event.optional' -REQUIRED_ATENDEE = 'http://schemas.google.com/g/2005#event.required' - -ATTENDEE_ACCEPTED = 'http://schemas.google.com/g/2005#event.accepted' -ATTENDEE_DECLINED = 'http://schemas.google.com/g/2005#event.declined' -ATTENDEE_INVITED = 'http://schemas.google.com/g/2005#event.invited' -ATTENDEE_TENTATIVE = 'http://schemas.google.com/g/2005#event.tentative' - -FULL_PROJECTION = 'full' -VALUES_PROJECTION = 'values' -BASIC_PROJECTION = 'basic' - -PRIVATE_VISIBILITY = 'private' -PUBLIC_VISIBILITY = 'public' - -ACL_REL = 'http://schemas.google.com/acl/2007#accessControlList' - - -class Error(Exception): - pass - - -class MissingRequiredParameters(Error): - pass - - -class LinkFinder(atom.data.LinkFinder): - """Mixin used in Feed and Entry classes to simplify link lookups by type. - - Provides lookup methods for edit, edit-media, post, ACL and other special - links which are common across Google Data APIs. - """ - - def find_html_link(self): - """Finds the first link with rel of alternate and type of text/html.""" - for link in self.link: - if link.rel == 'alternate' and link.type == 'text/html': - return link.href - return None - - FindHtmlLink = find_html_link - - def get_html_link(self): - for a_link in self.link: - if a_link.rel == 'alternate' and a_link.type == 'text/html': - return a_link - return None - - GetHtmlLink = get_html_link - - def find_post_link(self): - """Get the URL to which new entries should be POSTed. - - The POST target URL is used to insert new entries. - - Returns: - A str for the URL in the link with a rel matching the POST type. - """ - return self.find_url('http://schemas.google.com/g/2005#post') - - FindPostLink = find_post_link - - def get_post_link(self): - return self.get_link('http://schemas.google.com/g/2005#post') - - GetPostLink = get_post_link - - def find_acl_link(self): - acl_link = self.get_acl_link() - if acl_link: - return acl_link.href - - return None - - FindAclLink = find_acl_link - - def get_acl_link(self): - """Searches for a link or feed_link (if present) with the rel for ACL.""" - - acl_link = self.get_link(ACL_REL) - if acl_link: - return acl_link - elif hasattr(self, 'feed_link'): - for a_feed_link in self.feed_link: - if a_feed_link.rel == ACL_REL: - return a_feed_link - - return None - - GetAclLink = get_acl_link - - def find_feed_link(self): - return self.find_url('http://schemas.google.com/g/2005#feed') - - FindFeedLink = find_feed_link - - def get_feed_link(self): - return self.get_link('http://schemas.google.com/g/2005#feed') - - GetFeedLink = get_feed_link - - def find_previous_link(self): - return self.find_url('previous') - - FindPreviousLink = find_previous_link - - def get_previous_link(self): - return self.get_link('previous') - - GetPreviousLink = get_previous_link - - -class TotalResults(atom.core.XmlElement): - """opensearch:TotalResults for a GData feed.""" - _qname = (OPENSEARCH_TEMPLATE_V1 % 'totalResults', - OPENSEARCH_TEMPLATE_V2 % 'totalResults') - - -class StartIndex(atom.core.XmlElement): - """The opensearch:startIndex element in GData feed.""" - _qname = (OPENSEARCH_TEMPLATE_V1 % 'startIndex', - OPENSEARCH_TEMPLATE_V2 % 'startIndex') - - -class ItemsPerPage(atom.core.XmlElement): - """The opensearch:itemsPerPage element in GData feed.""" - _qname = (OPENSEARCH_TEMPLATE_V1 % 'itemsPerPage', - OPENSEARCH_TEMPLATE_V2 % 'itemsPerPage') - - -class ExtendedProperty(atom.core.XmlElement): - """The Google Data extendedProperty element. - - Used to store arbitrary key-value information specific to your - application. The value can either be a text string stored as an XML - attribute (.value), or an XML node (XmlBlob) as a child element. - - This element is used in the Google Calendar data API and the Google - Contacts data API. - """ - _qname = GDATA_TEMPLATE % 'extendedProperty' - name = 'name' - value = 'value' - - def get_xml_blob(self): - """Returns the XML blob as an atom.core.XmlElement. - - Returns: - An XmlElement representing the blob's XML, or None if no - blob was set. - """ - if self._other_elements: - return self._other_elements[0] - else: - return None - - GetXmlBlob = get_xml_blob - - def set_xml_blob(self, blob): - """Sets the contents of the extendedProperty to XML as a child node. - - Since the extendedProperty is only allowed one child element as an XML - blob, setting the XML blob will erase any preexisting member elements - in this object. - - Args: - blob: str or atom.core.XmlElement representing the XML blob stored in - the extendedProperty. - """ - # Erase any existing extension_elements, clears the child nodes from the - # extendedProperty. - if isinstance(blob, atom.core.XmlElement): - self._other_elements = [blob] - else: - self._other_elements = [atom.core.parse(str(blob))] - - SetXmlBlob = set_xml_blob - - -class GDEntry(atom.data.Entry, LinkFinder): - """Extends Atom Entry to provide data processing""" - etag = '{http://schemas.google.com/g/2005}etag' - - def get_id(self): - if self.id is not None and self.id.text is not None: - return self.id.text.strip() - return None - - GetId = get_id - - def is_media(self): - if self.find_edit_media_link(): - return True - return False - - IsMedia = is_media - - def find_media_link(self): - """Returns the URL to the media content, if the entry is a media entry. - Otherwise returns None. - """ - if self.is_media(): - return self.content.src - return None - - FindMediaLink = find_media_link - - -class GDFeed(atom.data.Feed, LinkFinder): - """A Feed from a GData service.""" - etag = '{http://schemas.google.com/g/2005}etag' - total_results = TotalResults - start_index = StartIndex - items_per_page = ItemsPerPage - entry = [GDEntry] - - def get_id(self): - if self.id is not None and self.id.text is not None: - return self.id.text.strip() - return None - - GetId = get_id - - def get_generator(self): - if self.generator and self.generator.text: - return self.generator.text.strip() - return None - - -class BatchId(atom.core.XmlElement): - """Identifies a single operation in a batch request.""" - _qname = BATCH_TEMPLATE % 'id' - - -class BatchOperation(atom.core.XmlElement): - """The CRUD operation which this batch entry represents.""" - _qname = BATCH_TEMPLATE % 'operation' - type = 'type' - - -class BatchStatus(atom.core.XmlElement): - """The batch:status element present in a batch response entry. - - A status element contains the code (HTTP response code) and - reason as elements. In a single request these fields would - be part of the HTTP response, but in a batch request each - Entry operation has a corresponding Entry in the response - feed which includes status information. - - See http://code.google.com/apis/gdata/batch.html#Handling_Errors - """ - _qname = BATCH_TEMPLATE % 'status' - code = 'code' - reason = 'reason' - content_type = 'content-type' - - -class BatchEntry(GDEntry): - """An atom:entry for use in batch requests. - - The BatchEntry contains additional members to specify the operation to be - performed on this entry and a batch ID so that the server can reference - individual operations in the response feed. For more information, see: - http://code.google.com/apis/gdata/batch.html - """ - batch_operation = BatchOperation - batch_id = BatchId - batch_status = BatchStatus - - -class BatchInterrupted(atom.core.XmlElement): - """The batch:interrupted element sent if batch request was interrupted. - - Only appears in a feed if some of the batch entries could not be processed. - See: http://code.google.com/apis/gdata/batch.html#Handling_Errors - """ - _qname = BATCH_TEMPLATE % 'interrupted' - reason = 'reason' - success = 'success' - failures = 'failures' - parsed = 'parsed' - - -class BatchFeed(GDFeed): - """A feed containing a list of batch request entries.""" - interrupted = BatchInterrupted - entry = [BatchEntry] - - def add_batch_entry(self, entry=None, id_url_string=None, - batch_id_string=None, operation_string=None): - """Logic for populating members of a BatchEntry and adding to the feed. - - If the entry is not a BatchEntry, it is converted to a BatchEntry so - that the batch specific members will be present. - - The id_url_string can be used in place of an entry if the batch operation - applies to a URL. For example query and delete operations require just - the URL of an entry, no body is sent in the HTTP request. If an - id_url_string is sent instead of an entry, a BatchEntry is created and - added to the feed. - - This method also assigns the desired batch id to the entry so that it - can be referenced in the server's response. If the batch_id_string is - None, this method will assign a batch_id to be the index at which this - entry will be in the feed's entry list. - - Args: - entry: BatchEntry, atom.data.Entry, or another Entry flavor (optional) - The entry which will be sent to the server as part of the batch - request. The item must have a valid atom id so that the server - knows which entry this request references. - id_url_string: str (optional) The URL of the entry to be acted on. You - can find this URL in the text member of the atom id for an entry. - If an entry is not sent, this id will be used to construct a new - BatchEntry which will be added to the request feed. - batch_id_string: str (optional) The batch ID to be used to reference - this batch operation in the results feed. If this parameter is None, - the current length of the feed's entry array will be used as a - count. Note that batch_ids should either always be specified or - never, mixing could potentially result in duplicate batch ids. - operation_string: str (optional) The desired batch operation which will - set the batch_operation.type member of the entry. Options are - 'insert', 'update', 'delete', and 'query' - - Raises: - MissingRequiredParameters: Raised if neither an id_ url_string nor an - entry are provided in the request. - - Returns: - The added entry. - """ - if entry is None and id_url_string is None: - raise MissingRequiredParameters('supply either an entry or URL string') - if entry is None and id_url_string is not None: - entry = BatchEntry(id=atom.data.Id(text=id_url_string)) - if batch_id_string is not None: - entry.batch_id = BatchId(text=batch_id_string) - elif entry.batch_id is None or entry.batch_id.text is None: - entry.batch_id = BatchId(text=str(len(self.entry))) - if operation_string is not None: - entry.batch_operation = BatchOperation(type=operation_string) - self.entry.append(entry) - return entry - - AddBatchEntry = add_batch_entry - - def add_insert(self, entry, batch_id_string=None): - """Add an insert request to the operations in this batch request feed. - - If the entry doesn't yet have an operation or a batch id, these will - be set to the insert operation and a batch_id specified as a parameter. - - Args: - entry: BatchEntry The entry which will be sent in the batch feed as an - insert request. - batch_id_string: str (optional) The batch ID to be used to reference - this batch operation in the results feed. If this parameter is None, - the current length of the feed's entry array will be used as a - count. Note that batch_ids should either always be specified or - never, mixing could potentially result in duplicate batch ids. - """ - self.add_batch_entry(entry=entry, batch_id_string=batch_id_string, - operation_string=BATCH_INSERT) - - AddInsert = add_insert - - def add_update(self, entry, batch_id_string=None): - """Add an update request to the list of batch operations in this feed. - - Sets the operation type of the entry to insert if it is not already set - and assigns the desired batch id to the entry so that it can be - referenced in the server's response. - - Args: - entry: BatchEntry The entry which will be sent to the server as an - update (HTTP PUT) request. The item must have a valid atom id - so that the server knows which entry to replace. - batch_id_string: str (optional) The batch ID to be used to reference - this batch operation in the results feed. If this parameter is None, - the current length of the feed's entry array will be used as a - count. See also comments for AddInsert. - """ - self.add_batch_entry(entry=entry, batch_id_string=batch_id_string, - operation_string=BATCH_UPDATE) - - AddUpdate = add_update - - def add_delete(self, url_string=None, entry=None, batch_id_string=None): - """Adds a delete request to the batch request feed. - - This method takes either the url_string which is the atom id of the item - to be deleted, or the entry itself. The atom id of the entry must be - present so that the server knows which entry should be deleted. - - Args: - url_string: str (optional) The URL of the entry to be deleted. You can - find this URL in the text member of the atom id for an entry. - entry: BatchEntry (optional) The entry to be deleted. - batch_id_string: str (optional) - - Raises: - MissingRequiredParameters: Raised if neither a url_string nor an entry - are provided in the request. - """ - self.add_batch_entry(entry=entry, id_url_string=url_string, - batch_id_string=batch_id_string, operation_string=BATCH_DELETE) - - AddDelete = add_delete - - def add_query(self, url_string=None, entry=None, batch_id_string=None): - """Adds a query request to the batch request feed. - - This method takes either the url_string which is the query URL - whose results will be added to the result feed. The query URL will - be encapsulated in a BatchEntry, and you may pass in the BatchEntry - with a query URL instead of sending a url_string. - - Args: - url_string: str (optional) - entry: BatchEntry (optional) - batch_id_string: str (optional) - - Raises: - MissingRequiredParameters - """ - self.add_batch_entry(entry=entry, id_url_string=url_string, - batch_id_string=batch_id_string, operation_string=BATCH_QUERY) - - AddQuery = add_query - - def find_batch_link(self): - return self.find_url('http://schemas.google.com/g/2005#batch') - - FindBatchLink = find_batch_link - - -class EntryLink(atom.core.XmlElement): - """The gd:entryLink element. - - Represents a logically nested entry. For example, a - representing a contact might have a nested entry from a contact feed. - """ - _qname = GDATA_TEMPLATE % 'entryLink' - entry = GDEntry - rel = 'rel' - read_only = 'readOnly' - href = 'href' - - -class FeedLink(atom.core.XmlElement): - """The gd:feedLink element. - - Represents a logically nested feed. For example, a calendar feed might - have a nested feed representing all comments on entries. - """ - _qname = GDATA_TEMPLATE % 'feedLink' - feed = GDFeed - rel = 'rel' - read_only = 'readOnly' - count_hint = 'countHint' - href = 'href' - - -class AdditionalName(atom.core.XmlElement): - """The gd:additionalName element. - - Specifies additional (eg. middle) name of the person. - Contains an attribute for the phonetic representaton of the name. - """ - _qname = GDATA_TEMPLATE % 'additionalName' - yomi = 'yomi' - - -class Comments(atom.core.XmlElement): - """The gd:comments element. - - Contains a comments feed for the enclosing entry (such as a calendar event). - """ - _qname = GDATA_TEMPLATE % 'comments' - rel = 'rel' - feed_link = FeedLink - - -class Country(atom.core.XmlElement): - """The gd:country element. - - Country name along with optional country code. The country code is - given in accordance with ISO 3166-1 alpha-2: - http://www.iso.org/iso/iso-3166-1_decoding_table - """ - _qname = GDATA_TEMPLATE % 'country' - code = 'code' - - -class EmailImParent(atom.core.XmlElement): - address = 'address' - label = 'label' - rel = 'rel' - primary = 'primary' - - -class Email(EmailImParent): - """The gd:email element. - - An email address associated with the containing entity (which is - usually an entity representing a person or a location). - """ - _qname = GDATA_TEMPLATE % 'email' - display_name = 'displayName' - - -class FamilyName(atom.core.XmlElement): - """The gd:familyName element. - - Specifies family name of the person, eg. "Smith". - """ - _qname = GDATA_TEMPLATE % 'familyName' - yomi = 'yomi' - - -class Im(EmailImParent): - """The gd:im element. - - An instant messaging address associated with the containing entity. - """ - _qname = GDATA_TEMPLATE % 'im' - protocol = 'protocol' - - -class GivenName(atom.core.XmlElement): - """The gd:givenName element. - - Specifies given name of the person, eg. "John". - """ - _qname = GDATA_TEMPLATE % 'givenName' - yomi = 'yomi' - - -class NamePrefix(atom.core.XmlElement): - """The gd:namePrefix element. - - Honorific prefix, eg. 'Mr' or 'Mrs'. - """ - _qname = GDATA_TEMPLATE % 'namePrefix' - - -class NameSuffix(atom.core.XmlElement): - """The gd:nameSuffix element. - - Honorific suffix, eg. 'san' or 'III'. - """ - _qname = GDATA_TEMPLATE % 'nameSuffix' - - -class FullName(atom.core.XmlElement): - """The gd:fullName element. - - Unstructured representation of the name. - """ - _qname = GDATA_TEMPLATE % 'fullName' - - -class Name(atom.core.XmlElement): - """The gd:name element. - - Allows storing person's name in a structured way. Consists of - given name, additional name, family name, prefix, suffix and full name. - """ - _qname = GDATA_TEMPLATE % 'name' - given_name = GivenName - additional_name = AdditionalName - family_name = FamilyName - name_prefix = NamePrefix - name_suffix = NameSuffix - full_name = FullName - - -class OrgDepartment(atom.core.XmlElement): - """The gd:orgDepartment element. - - Describes a department within an organization. Must appear within a - gd:organization element. - """ - _qname = GDATA_TEMPLATE % 'orgDepartment' - - -class OrgJobDescription(atom.core.XmlElement): - """The gd:orgJobDescription element. - - Describes a job within an organization. Must appear within a - gd:organization element. - """ - _qname = GDATA_TEMPLATE % 'orgJobDescription' - - -class OrgName(atom.core.XmlElement): - """The gd:orgName element. - - The name of the organization. Must appear within a gd:organization - element. - - Contains a Yomigana attribute (Japanese reading aid) for the - organization name. - """ - _qname = GDATA_TEMPLATE % 'orgName' - yomi = 'yomi' - - -class OrgSymbol(atom.core.XmlElement): - """The gd:orgSymbol element. - - Provides a symbol of an organization. Must appear within a - gd:organization element. - """ - _qname = GDATA_TEMPLATE % 'orgSymbol' - - -class OrgTitle(atom.core.XmlElement): - """The gd:orgTitle element. - - The title of a person within an organization. Must appear within a - gd:organization element. - """ - _qname = GDATA_TEMPLATE % 'orgTitle' - - -class Organization(atom.core.XmlElement): - """The gd:organization element. - - An organization, typically associated with a contact. - """ - _qname = GDATA_TEMPLATE % 'organization' - label = 'label' - primary = 'primary' - rel = 'rel' - department = OrgDepartment - job_description = OrgJobDescription - name = OrgName - symbol = OrgSymbol - title = OrgTitle - - -class When(atom.core.XmlElement): - """The gd:when element. - - Represents a period of time or an instant. - """ - _qname = GDATA_TEMPLATE % 'when' - end = 'endTime' - start = 'startTime' - value = 'valueString' - - -class OriginalEvent(atom.core.XmlElement): - """The gd:originalEvent element. - - Equivalent to the Recurrence ID property specified in section 4.8.4.4 - of RFC 2445. Appears in every instance of a recurring event, to identify - the original event. - - Contains a element specifying the original start time of the - instance that has become an exception. - """ - _qname = GDATA_TEMPLATE % 'originalEvent' - id = 'id' - href = 'href' - when = When - - -class PhoneNumber(atom.core.XmlElement): - """The gd:phoneNumber element. - - A phone number associated with the containing entity (which is usually - an entity representing a person or a location). - """ - _qname = GDATA_TEMPLATE % 'phoneNumber' - label = 'label' - rel = 'rel' - uri = 'uri' - primary = 'primary' - - -class PostalAddress(atom.core.XmlElement): - """The gd:postalAddress element.""" - _qname = GDATA_TEMPLATE % 'postalAddress' - label = 'label' - rel = 'rel' - uri = 'uri' - primary = 'primary' - - -class Rating(atom.core.XmlElement): - """The gd:rating element. - - Represents a numeric rating of the enclosing entity, such as a - comment. Each rating supplies its own scale, although it may be - normalized by a service; for example, some services might convert all - ratings to a scale from 1 to 5. - """ - _qname = GDATA_TEMPLATE % 'rating' - average = 'average' - max = 'max' - min = 'min' - num_raters = 'numRaters' - rel = 'rel' - value = 'value' - - -class Recurrence(atom.core.XmlElement): - """The gd:recurrence element. - - Represents the dates and times when a recurring event takes place. - - The string that defines the recurrence consists of a set of properties, - each of which is defined in the iCalendar standard (RFC 2445). - - Specifically, the string usually begins with a DTSTART property that - indicates the starting time of the first instance of the event, and - often a DTEND property or a DURATION property to indicate when the - first instance ends. Next come RRULE, RDATE, EXRULE, and/or EXDATE - properties, which collectively define a recurring event and its - exceptions (but see below). (See section 4.8.5 of RFC 2445 for more - information about these recurrence component properties.) Last comes a - VTIMEZONE component, providing detailed timezone rules for any timezone - ID mentioned in the preceding properties. - - Google services like Google Calendar don't generally generate EXRULE - and EXDATE properties to represent exceptions to recurring events; - instead, they generate elements. However, - Google services may include EXRULE and/or EXDATE properties anyway; - for example, users can import events and exceptions into Calendar, and - if those imported events contain EXRULE or EXDATE properties, then - Calendar will provide those properties when it sends a - element. - - Note the the use of means that you can't be - sure just from examining a element whether there are - any exceptions to the recurrence description. To ensure that you find - all exceptions, look for elements in the feed, - and use their elements to match them up with - elements. - """ - _qname = GDATA_TEMPLATE % 'recurrence' - - -class RecurrenceException(atom.core.XmlElement): - """The gd:recurrenceException element. - - Represents an event that's an exception to a recurring event-that is, - an instance of a recurring event in which one or more aspects of the - recurring event (such as attendance list, time, or location) have been - changed. - - Contains a element that specifies the original - recurring event that this event is an exception to. - - When you change an instance of a recurring event, that instance becomes - an exception. Depending on what change you made to it, the exception - behaves in either of two different ways when the original recurring - event is changed: - - - If you add, change, or remove comments, attendees, or attendee - responses, then the exception remains tied to the original event, and - changes to the original event also change the exception. - - If you make any other changes to the exception (such as changing the - time or location) then the instance becomes "specialized," which means - that it's no longer as tightly tied to the original event. If you - change the original event, specialized exceptions don't change. But - see below. - - For example, say you have a meeting every Tuesday and Thursday at - 2:00 p.m. If you change the attendance list for this Thursday's meeting - (but not for the regularly scheduled meeting), then it becomes an - exception. If you change the time for this Thursday's meeting (but not - for the regularly scheduled meeting), then it becomes specialized. - - Regardless of whether an exception is specialized or not, if you do - something that deletes the instance that the exception was derived from, - then the exception is deleted. Note that changing the day or time of a - recurring event deletes all instances, and creates new ones. - - For example, after you've specialized this Thursday's meeting, say you - change the recurring meeting to happen on Monday, Wednesday, and Friday. - That change deletes all of the recurring instances of the - Tuesday/Thursday meeting, including the specialized one. - - If a particular instance of a recurring event is deleted, then that - instance appears as a containing a - that has its set to - "http://schemas.google.com/g/2005#event.canceled". (For more - information about canceled events, see RFC 2445.) - """ - _qname = GDATA_TEMPLATE % 'recurrenceException' - specialized = 'specialized' - entry_link = EntryLink - original_event = OriginalEvent - - -class Reminder(atom.core.XmlElement): - """The gd:reminder element. - - A time interval, indicating how long before the containing entity's start - time or due time attribute a reminder should be issued. Alternatively, - may specify an absolute time at which a reminder should be issued. Also - specifies a notification method, indicating what medium the system - should use to remind the user. - """ - _qname = GDATA_TEMPLATE % 'reminder' - absolute_time = 'absoluteTime' - method = 'method' - days = 'days' - hours = 'hours' - minutes = 'minutes' - - -class Agent(atom.core.XmlElement): - """The gd:agent element. - - The agent who actually receives the mail. Used in work addresses. - Also for 'in care of' or 'c/o'. - """ - _qname = GDATA_TEMPLATE % 'agent' - - -class HouseName(atom.core.XmlElement): - """The gd:housename element. - - Used in places where houses or buildings have names (and not - necessarily numbers), eg. "The Pillars". - """ - _qname = GDATA_TEMPLATE % 'housename' - - -class Street(atom.core.XmlElement): - """The gd:street element. - - Can be street, avenue, road, etc. This element also includes the - house number and room/apartment/flat/floor number. - """ - _qname = GDATA_TEMPLATE % 'street' - - -class PoBox(atom.core.XmlElement): - """The gd:pobox element. - - Covers actual P.O. boxes, drawers, locked bags, etc. This is usually - but not always mutually exclusive with street. - """ - _qname = GDATA_TEMPLATE % 'pobox' - - -class Neighborhood(atom.core.XmlElement): - """The gd:neighborhood element. - - This is used to disambiguate a street address when a city contains more - than one street with the same name, or to specify a small place whose - mail is routed through a larger postal town. In China it could be a - county or a minor city. - """ - _qname = GDATA_TEMPLATE % 'neighborhood' - - -class City(atom.core.XmlElement): - """The gd:city element. - - Can be city, village, town, borough, etc. This is the postal town and - not necessarily the place of residence or place of business. - """ - _qname = GDATA_TEMPLATE % 'city' - - -class Subregion(atom.core.XmlElement): - """The gd:subregion element. - - Handles administrative districts such as U.S. or U.K. counties that are - not used for mail addressing purposes. Subregion is not intended for - delivery addresses. - """ - _qname = GDATA_TEMPLATE % 'subregion' - - -class Region(atom.core.XmlElement): - """The gd:region element. - - A state, province, county (in Ireland), Land (in Germany), - departement (in France), etc. - """ - _qname = GDATA_TEMPLATE % 'region' - - -class Postcode(atom.core.XmlElement): - """The gd:postcode element. - - Postal code. Usually country-wide, but sometimes specific to the - city (e.g. "2" in "Dublin 2, Ireland" addresses). - """ - _qname = GDATA_TEMPLATE % 'postcode' - - -class Country(atom.core.XmlElement): - """The gd:country element. - - The name or code of the country. - """ - _qname = GDATA_TEMPLATE % 'country' - - -class FormattedAddress(atom.core.XmlElement): - """The gd:formattedAddress element. - - The full, unstructured postal address. - """ - _qname = GDATA_TEMPLATE % 'formattedAddress' - - -class StructuredPostalAddress(atom.core.XmlElement): - """The gd:structuredPostalAddress element. - - Postal address split into components. It allows to store the address - in locale independent format. The fields can be interpreted and used - to generate formatted, locale dependent address. The following elements - reperesent parts of the address: agent, house name, street, P.O. box, - neighborhood, city, subregion, region, postal code, country. The - subregion element is not used for postal addresses, it is provided for - extended uses of addresses only. In order to store postal address in an - unstructured form formatted address field is provided. - """ - _qname = GDATA_TEMPLATE % 'structuredPostalAddress' - rel = 'rel' - mail_class = 'mailClass' - usage = 'usage' - label = 'label' - primary = 'primary' - agent = Agent - house_name = HouseName - street = Street - po_box = PoBox - neighborhood = Neighborhood - city = City - subregion = Subregion - region = Region - postcode = Postcode - country = Country - formatted_address = FormattedAddress - - -class Where(atom.core.XmlElement): - """The gd:where element. - - A place (such as an event location) associated with the containing - entity. The type of the association is determined by the rel attribute; - the details of the location are contained in an embedded or linked-to - Contact entry. - - A element is more general than a element. The - former identifies a place using a text description and/or a Contact - entry, while the latter identifies a place using a specific geographic - location. - """ - _qname = GDATA_TEMPLATE % 'where' - label = 'label' - rel = 'rel' - value = 'valueString' - entry_link = EntryLink - - -class AttendeeType(atom.core.XmlElement): - """The gd:attendeeType element.""" - _qname = GDATA_TEMPLATE % 'attendeeType' - value = 'value' - - -class AttendeeStatus(atom.core.XmlElement): - """The gd:attendeeStatus element.""" - _qname = GDATA_TEMPLATE % 'attendeeStatus' - value = 'value' - - -class Who(atom.core.XmlElement): - """The gd:who element. - - A person associated with the containing entity. The type of the - association is determined by the rel attribute; the details about the - person are contained in an embedded or linked-to Contact entry. - - The element can be used to specify email senders and - recipients, calendar event organizers, and so on. - """ - _qname = GDATA_TEMPLATE % 'who' - email = 'email' - rel = 'rel' - value = 'valueString' - attendee_status = AttendeeStatus - attendee_type = AttendeeType - entry_link = EntryLink - - -class Deleted(atom.core.XmlElement): - """gd:deleted when present, indicates the containing entry is deleted.""" - _qname = GD_TEMPLATE % 'deleted' - - -class Money(atom.core.XmlElement): - """Describes money""" - _qname = GD_TEMPLATE % 'money' - amount = 'amount' - currency_code = 'currencyCode' - - -class MediaSource(object): - """GData Entries can refer to media sources, so this class provides a - place to store references to these objects along with some metadata. - """ - - def __init__(self, file_handle=None, content_type=None, content_length=None, - file_path=None, file_name=None): - """Creates an object of type MediaSource. - - Args: - file_handle: A file handle pointing to the file to be encapsulated in the - MediaSource. - content_type: string The MIME type of the file. Required if a file_handle - is given. - content_length: int The size of the file. Required if a file_handle is - given. - file_path: string (optional) A full path name to the file. Used in - place of a file_handle. - file_name: string The name of the file without any path information. - Required if a file_handle is given. - """ - self.file_handle = file_handle - self.content_type = content_type - self.content_length = content_length - self.file_name = file_name - - if (file_handle is None and content_type is not None and - file_path is not None): - self.set_file_handle(file_path, content_type) - - def set_file_handle(self, file_name, content_type): - """A helper function which can create a file handle from a given filename - and set the content type and length all at once. - - Args: - file_name: string The path and file name to the file containing the media - content_type: string A MIME type representing the type of the media - """ - - self.file_handle = open(file_name, 'rb') - self.content_type = content_type - self.content_length = os.path.getsize(file_name) - self.file_name = os.path.basename(file_name) - - SetFileHandle = set_file_handle - - def modify_request(self, http_request): - http_request.add_body_part(self.file_handle, self.content_type, - self.content_length) - return http_request - - ModifyRequest = modify_request diff --git a/gdata/docs/__init__.py b/gdata/docs/__init__.py deleted file mode 100644 index 8031bc9b7a..0000000000 --- a/gdata/docs/__init__.py +++ /dev/null @@ -1,269 +0,0 @@ -#!/usr/bin/python -# -# Copyright 2009 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. - -"""Contains extensions to Atom objects used with Google Documents.""" - -__author__ = ('api.jfisher (Jeff Fisher), ' - 'api.eric@google.com (Eric Bidelman)') - -import atom -import gdata - - -DOCUMENTS_NAMESPACE = 'http://schemas.google.com/docs/2007' - - -class Scope(atom.AtomBase): - """The DocList ACL scope element""" - - _tag = 'scope' - _namespace = gdata.GACL_NAMESPACE - _children = atom.AtomBase._children.copy() - _attributes = atom.AtomBase._attributes.copy() - _attributes['value'] = 'value' - _attributes['type'] = 'type' - - def __init__(self, value=None, type=None, extension_elements=None, - extension_attributes=None, text=None): - self.value = value - self.type = type - self.text = text - self.extension_elements = extension_elements or [] - self.extension_attributes = extension_attributes or {} - - -class Role(atom.AtomBase): - """The DocList ACL role element""" - - _tag = 'role' - _namespace = gdata.GACL_NAMESPACE - _children = atom.AtomBase._children.copy() - _attributes = atom.AtomBase._attributes.copy() - _attributes['value'] = 'value' - - def __init__(self, value=None, extension_elements=None, - extension_attributes=None, text=None): - self.value = value - self.text = text - self.extension_elements = extension_elements or [] - self.extension_attributes = extension_attributes or {} - - -class FeedLink(atom.AtomBase): - """The DocList gd:feedLink element""" - - _tag = 'feedLink' - _namespace = gdata.GDATA_NAMESPACE - _attributes = atom.AtomBase._attributes.copy() - _attributes['rel'] = 'rel' - _attributes['href'] = 'href' - - def __init__(self, href=None, rel=None, text=None, extension_elements=None, - extension_attributes=None): - self.href = href - self.rel = rel - atom.AtomBase.__init__(self, extension_elements=extension_elements, - extension_attributes=extension_attributes, text=text) - - -class ResourceId(atom.AtomBase): - """The DocList gd:resourceId element""" - - _tag = 'resourceId' - _namespace = gdata.GDATA_NAMESPACE - _children = atom.AtomBase._children.copy() - _attributes = atom.AtomBase._attributes.copy() - _attributes['value'] = 'value' - - def __init__(self, value=None, extension_elements=None, - extension_attributes=None, text=None): - self.value = value - self.text = text - self.extension_elements = extension_elements or [] - self.extension_attributes = extension_attributes or {} - - -class LastModifiedBy(atom.Person): - """The DocList gd:lastModifiedBy element""" - - _tag = 'lastModifiedBy' - _namespace = gdata.GDATA_NAMESPACE - - -class LastViewed(atom.Person): - """The DocList gd:lastViewed element""" - - _tag = 'lastViewed' - _namespace = gdata.GDATA_NAMESPACE - - -class WritersCanInvite(atom.AtomBase): - """The DocList docs:writersCanInvite element""" - - _tag = 'writersCanInvite' - _namespace = DOCUMENTS_NAMESPACE - _attributes = atom.AtomBase._attributes.copy() - _attributes['value'] = 'value' - - -class DocumentListEntry(gdata.GDataEntry): - """The Google Documents version of an Atom Entry""" - - _tag = gdata.GDataEntry._tag - _namespace = atom.ATOM_NAMESPACE - _children = gdata.GDataEntry._children.copy() - _attributes = gdata.GDataEntry._attributes.copy() - _children['{%s}feedLink' % gdata.GDATA_NAMESPACE] = ('feedLink', FeedLink) - _children['{%s}resourceId' % gdata.GDATA_NAMESPACE] = ('resourceId', - ResourceId) - _children['{%s}lastModifiedBy' % gdata.GDATA_NAMESPACE] = ('lastModifiedBy', - LastModifiedBy) - _children['{%s}lastViewed' % gdata.GDATA_NAMESPACE] = ('lastViewed', - LastViewed) - _children['{%s}writersCanInvite' % DOCUMENTS_NAMESPACE] = ( - 'writersCanInvite', WritersCanInvite) - - def __init__(self, resourceId=None, feedLink=None, lastViewed=None, - lastModifiedBy=None, writersCanInvite=None, author=None, - category=None, content=None, atom_id=None, link=None, - published=None, title=None, updated=None, text=None, - extension_elements=None, extension_attributes=None): - self.feedLink = feedLink - self.lastViewed = lastViewed - self.lastModifiedBy = lastModifiedBy - self.resourceId = resourceId - self.writersCanInvite = writersCanInvite - gdata.GDataEntry.__init__( - self, author=author, category=category, content=content, - atom_id=atom_id, link=link, published=published, title=title, - updated=updated, extension_elements=extension_elements, - extension_attributes=extension_attributes, text=text) - - def GetAclLink(self): - """Extracts the DocListEntry's . - - Returns: - A FeedLink object. - """ - return self.feedLink - - def GetDocumentType(self): - """Extracts the type of document from the DocListEntry. - - This method returns the type of document the DocListEntry - represents. Possible values are document, presentation, - spreadsheet, folder, or pdf. - - Returns: - A string representing the type of document. - """ - if self.category: - for category in self.category: - if category.scheme == gdata.GDATA_NAMESPACE + '#kind': - return category.label - else: - return None - - -def DocumentListEntryFromString(xml_string): - """Converts an XML string into a DocumentListEntry object. - - Args: - xml_string: string The XML describing a Document List feed entry. - - Returns: - A DocumentListEntry object corresponding to the given XML. - """ - return atom.CreateClassFromXMLString(DocumentListEntry, xml_string) - - -class DocumentListAclEntry(gdata.GDataEntry): - """A DocList ACL Entry flavor of an Atom Entry""" - - _tag = gdata.GDataEntry._tag - _namespace = gdata.GDataEntry._namespace - _children = gdata.GDataEntry._children.copy() - _attributes = gdata.GDataEntry._attributes.copy() - _children['{%s}scope' % gdata.GACL_NAMESPACE] = ('scope', Scope) - _children['{%s}role' % gdata.GACL_NAMESPACE] = ('role', Role) - - def __init__(self, category=None, atom_id=None, link=None, - title=None, updated=None, scope=None, role=None, - extension_elements=None, extension_attributes=None, text=None): - gdata.GDataEntry.__init__(self, author=None, category=category, - content=None, atom_id=atom_id, link=link, - published=None, title=title, - updated=updated, text=None) - self.scope = scope - self.role = role - - -def DocumentListAclEntryFromString(xml_string): - """Converts an XML string into a DocumentListAclEntry object. - - Args: - xml_string: string The XML describing a Document List ACL feed entry. - - Returns: - A DocumentListAclEntry object corresponding to the given XML. - """ - return atom.CreateClassFromXMLString(DocumentListAclEntry, xml_string) - - -class DocumentListFeed(gdata.GDataFeed): - """A feed containing a list of Google Documents Items""" - - _tag = gdata.GDataFeed._tag - _namespace = atom.ATOM_NAMESPACE - _children = gdata.GDataFeed._children.copy() - _attributes = gdata.GDataFeed._attributes.copy() - _children['{%s}entry' % atom.ATOM_NAMESPACE] = ('entry', - [DocumentListEntry]) - - -def DocumentListFeedFromString(xml_string): - """Converts an XML string into a DocumentListFeed object. - - Args: - xml_string: string The XML describing a DocumentList feed. - - Returns: - A DocumentListFeed object corresponding to the given XML. - """ - return atom.CreateClassFromXMLString(DocumentListFeed, xml_string) - - -class DocumentListAclFeed(gdata.GDataFeed): - """A DocList ACL feed flavor of a Atom feed""" - - _tag = gdata.GDataFeed._tag - _namespace = atom.ATOM_NAMESPACE - _children = gdata.GDataFeed._children.copy() - _attributes = gdata.GDataFeed._attributes.copy() - _children['{%s}entry' % atom.ATOM_NAMESPACE] = ('entry', - [DocumentListAclEntry]) - - -def DocumentListAclFeedFromString(xml_string): - """Converts an XML string into a DocumentListAclFeed object. - - Args: - xml_string: string The XML describing a DocumentList feed. - - Returns: - A DocumentListFeed object corresponding to the given XML. - """ - return atom.CreateClassFromXMLString(DocumentListAclFeed, xml_string) diff --git a/gdata/docs/client.py b/gdata/docs/client.py deleted file mode 100644 index c42244b794..0000000000 --- a/gdata/docs/client.py +++ /dev/null @@ -1,625 +0,0 @@ -#!/usr/bin/python -# -# Copyright 2009 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. - -"""DocsClient extends gdata.client.GDClient to streamline DocList API calls.""" - - -__author__ = 'e.bidelman (Eric Bidelman)' - -import mimetypes -import urllib -import atom.data -import atom.http_core -import gdata.client -import gdata.docs.data -import gdata.gauth - - -# Feed URI templates -DOCLIST_FEED_URI = '/feeds/default/private/full/' -FOLDERS_FEED_TEMPLATE = DOCLIST_FEED_URI + '%s/contents' -ACL_FEED_TEMPLATE = DOCLIST_FEED_URI + '%s/acl' -REVISIONS_FEED_TEMPLATE = DOCLIST_FEED_URI + '%s/revisions' - - -class DocsClient(gdata.client.GDClient): - """Client extension for the Google Documents List API.""" - - host = 'docs.google.com' # default server for the API - api_version = '3.0' # default major version for the service. - auth_service = 'writely' - auth_scopes = gdata.gauth.AUTH_SCOPES['writely'] - - def __init__(self, auth_token=None, **kwargs): - """Constructs a new client for the DocList API. - - Args: - auth_token: (optional) gdata.gauth.ClientLoginToken, AuthSubToken, or - OAuthToken which 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_file_content(self, uri, auth_token=None, **kwargs): - """Fetches the file content from the specified uri. - - This method is useful for downloading/exporting a file within enviornments - like Google App Engine, where the user does not have the ability to write - the file to a local disk. - - Args: - uri: str The full URL to fetch the file contents from. - auth_token: (optional) gdata.gauth.ClientLoginToken, AuthSubToken, or - OAuthToken which authorizes this client to edit the user's data. - kwargs: Other parameters to pass to self.request(). - - Returns: - The binary file content. - - Raises: - gdata.client.RequestError: on error response from server. - """ - server_response = self.request('GET', uri, auth_token=auth_token, **kwargs) - if server_response.status != 200: - raise gdata.client.RequestError, {'status': server_response.status, - 'reason': server_response.reason, - 'body': server_response.read()} - return server_response.read() - - GetFileContent = get_file_content - - def _download_file(self, uri, file_path, auth_token=None, **kwargs): - """Downloads a file to disk from the specified URI. - - Note: to download a file in memory, use the GetFileContent() method. - - Args: - uri: str The full URL to download the file from. - file_path: str The full path to save the file to. - auth_token: (optional) gdata.gauth.ClientLoginToken, AuthSubToken, or - OAuthToken which authorizes this client to edit the user's data. - kwargs: Other parameters to pass to self.get_file_content(). - - Raises: - gdata.client.RequestError: on error response from server. - """ - f = open(file_path, 'wb') - try: - f.write(self.get_file_content(uri, auth_token=auth_token, **kwargs)) - except gdata.client.RequestError, e: - f.close() - raise e - f.flush() - f.close() - - _DownloadFile = _download_file - - def get_doclist(self, uri=None, limit=None, auth_token=None, **kwargs): - """Retrieves the main doclist feed containing the user's items. - - Args: - uri: str (optional) A URI to query the doclist feed. - limit: int (optional) A maximum cap for the number of results to - return in the feed. By default, the API returns a maximum of 100 - per page. Thus, if you set limit=5000, you will get <= 5000 - documents (guarenteed no more than 5000), and will need to follow the - feed's next links (feed.GetNextLink()) to the rest. See - get_everything(). Similarly, if you set limit=50, only <= 50 - documents are returned. Note: if the max-results parameter is set in - the uri parameter, it is chosen over a value set for limit. - auth_token: (optional) gdata.gauth.ClientLoginToken, AuthSubToken, or - OAuthToken which authorizes this client to edit the user's data. - kwargs: Other parameters to pass to self.get_feed(). - - Returns: - gdata.docs.data.DocList feed. - """ - if uri is None: - uri = DOCLIST_FEED_URI - - if isinstance(uri, (str, unicode)): - uri = atom.http_core.Uri.parse_uri(uri) - - # Add max-results param if it wasn't included in the uri. - if limit is not None and not 'max-results' in uri.query: - uri.query['max-results'] = limit - - return self.get_feed(uri, desired_class=gdata.docs.data.DocList, - auth_token=auth_token, **kwargs) - - GetDocList = get_doclist - - def get_metadata(self, auth_token=None, **kwargs): - """Retrieves user metadata. - - Args: - auth_token: (optional) gdata.gauth.ClientLoginToken, AuthSubToken, or - OAuthToken which authorizes this client to edit the user's data. - kwargs: Other parameters to pass to self.get_entry(). - - Returns: - A metadata feed. - - """ - return self.get_entry('https://docs.google.com/feeds/metadata/default', - auth_token=auth_token, **kwargs) - - GetMetadata = get_metadata - - def get_doc(self, resource_id, etag=None, auth_token=None, **kwargs): - """Retrieves a particular document given by its resource id. - - Args: - resource_id: str The document/item's resource id. Example spreadsheet: - 'spreadsheet%3A0A1234567890'. - etag: str (optional) The document/item's etag value to be used in a - conditional GET. See http://code.google.com/apis/documents/docs/3.0/ - developers_guide_protocol.html#RetrievingCached. - auth_token: (optional) gdata.gauth.ClientLoginToken, AuthSubToken, or - OAuthToken which authorizes this client to edit the user's data. - kwargs: Other parameters to pass to self.get_entry(). - - Returns: - A gdata.docs.data.DocsEntry object representing the retrieved entry. - - Raises: - ValueError if the resource_id is not a valid format. - """ - match = gdata.docs.data.RESOURCE_ID_PATTERN.match(resource_id) - if match is None: - raise ValueError, 'Invalid resource id: %s' % resource_id - return self.get_entry( - DOCLIST_FEED_URI + resource_id, etag=etag, - desired_class=gdata.docs.data.DocsEntry, - auth_token=auth_token, **kwargs) - - GetDoc = get_doc - - def get_everything(self, uri=None, auth_token=None, **kwargs): - """Retrieves the user's entire doc list. - - The method makes multiple HTTP requests (by following the feed's next links) - in order to fetch the user's entire document list. - - Args: - uri: str (optional) A URI to query the doclist feed with. - auth_token: (optional) gdata.gauth.ClientLoginToken, AuthSubToken, or - OAuthToken which authorizes this client to edit the user's data. - kwargs: Other parameters to pass to self.GetDocList(). - - Returns: - A list of gdata.docs.data.DocsEntry objects representing the retrieved - entries. - """ - if uri is None: - uri = DOCLIST_FEED_URI - - feed = self.GetDocList(uri=uri, auth_token=auth_token, **kwargs) - entries = feed.entry - - while feed.GetNextLink() is not None: - feed = self.GetDocList( - feed.GetNextLink().href, auth_token=auth_token, **kwargs) - entries.extend(feed.entry) - - return entries - - GetEverything = get_everything - - def get_acl_permissions(self, resource_id, auth_token=None, **kwargs): - """Retrieves a the ACL sharing permissions for a document. - - Args: - resource_id: str The document/item's resource id. Example for pdf: - 'pdf%3A0A1234567890'. - auth_token: (optional) gdata.gauth.ClientLoginToken, AuthSubToken, or - OAuthToken which authorizes this client to edit the user's data. - kwargs: Other parameters to pass to self.get_feed(). - - Returns: - A gdata.docs.data.AclFeed object representing the document's ACL entries. - - Raises: - ValueError if the resource_id is not a valid format. - """ - match = gdata.docs.data.RESOURCE_ID_PATTERN.match(resource_id) - if match is None: - raise ValueError, 'Invalid resource id: %s' % resource_id - - return self.get_feed( - ACL_FEED_TEMPLATE % resource_id, desired_class=gdata.docs.data.AclFeed, - auth_token=auth_token, **kwargs) - - GetAclPermissions = get_acl_permissions - - def get_revisions(self, resource_id, auth_token=None, **kwargs): - """Retrieves the revision history for a document. - - Args: - resource_id: str The document/item's resource id. Example for pdf: - 'pdf%3A0A1234567890'. - auth_token: (optional) gdata.gauth.ClientLoginToken, AuthSubToken, or - OAuthToken which authorizes this client to edit the user's data. - kwargs: Other parameters to pass to self.get_feed(). - - Returns: - A gdata.docs.data.RevisionFeed representing the document's revisions. - - Raises: - ValueError if the resource_id is not a valid format. - """ - match = gdata.docs.data.RESOURCE_ID_PATTERN.match(resource_id) - if match is None: - raise ValueError, 'Invalid resource id: %s' % resource_id - - return self.get_feed( - REVISIONS_FEED_TEMPLATE % resource_id, - desired_class=gdata.docs.data.RevisionFeed, auth_token=auth_token, - **kwargs) - - GetRevisions = get_revisions - - def create(self, doc_type, title, folder_or_id=None, writers_can_invite=None, - auth_token=None, **kwargs): - """Creates a new item in the user's doclist. - - Args: - doc_type: str The type of object to create. For example: 'document', - 'spreadsheet', 'folder', 'presentation'. - title: str A title for the document. - folder_or_id: gdata.docs.data.DocsEntry or str (optional) Folder entry or - the resouce id of a folder to create the object under. Note: A valid - resource id for a folder is of the form: folder%3Afolder_id. - writers_can_invite: bool (optional) False prevents collaborators from - being able to invite others to edit or view the document. - auth_token: (optional) gdata.gauth.ClientLoginToken, AuthSubToken, or - OAuthToken which authorizes this client to edit the user's data. - kwargs: Other parameters to pass to self.post(). - - Returns: - gdata.docs.data.DocsEntry containing information newly created item. - """ - entry = gdata.docs.data.DocsEntry(title=atom.data.Title(text=title)) - entry.category.append(gdata.docs.data.make_kind_category(doc_type)) - - if isinstance(writers_can_invite, gdata.docs.data.WritersCanInvite): - entry.writers_can_invite = writers_can_invite - elif isinstance(writers_can_invite, bool): - entry.writers_can_invite = gdata.docs.data.WritersCanInvite( - value=str(writers_can_invite).lower()) - - uri = DOCLIST_FEED_URI - - if folder_or_id is not None: - if isinstance(folder_or_id, gdata.docs.data.DocsEntry): - # Verify that we're uploading the resource into to a folder. - if folder_or_id.get_document_type() == gdata.docs.data.FOLDER_LABEL: - uri = folder_or_id.content.src - else: - raise gdata.client.Error, 'Trying to upload item to a non-folder.' - else: - uri = FOLDERS_FEED_TEMPLATE % folder_or_id - - return self.post(entry, uri, auth_token=auth_token, **kwargs) - - Create = create - - def copy(self, source_entry, title, auth_token=None, **kwargs): - """Copies a native Google document, spreadsheet, or presentation. - - Note: arbitrary file types and PDFs do not support this feature. - - Args: - source_entry: gdata.docs.data.DocsEntry An object representing the source - document/folder. - title: str A title for the new document. - auth_token: (optional) gdata.gauth.ClientLoginToken, AuthSubToken, or - OAuthToken which authorizes this client to edit the user's data. - kwargs: Other parameters to pass to self.post(). - - Returns: - A gdata.docs.data.DocsEntry of the duplicated document. - """ - entry = gdata.docs.data.DocsEntry( - title=atom.data.Title(text=title), - id=atom.data.Id(text=source_entry.GetSelfLink().href)) - return self.post(entry, DOCLIST_FEED_URI, auth_token=auth_token, **kwargs) - - Copy = copy - - def move(self, source_entry, folder_entry=None, - keep_in_folders=False, auth_token=None, **kwargs): - """Moves an item into a different folder (or to the root document list). - - Args: - source_entry: gdata.docs.data.DocsEntry An object representing the source - document/folder. - folder_entry: gdata.docs.data.DocsEntry (optional) An object representing - the destination folder. If None, set keep_in_folders to - True to remove the item from all parent folders. - keep_in_folders: boolean (optional) If True, the source entry - is not removed from any existing parent folders it is in. - auth_token: (optional) gdata.gauth.ClientLoginToken, AuthSubToken, or - OAuthToken which authorizes this client to edit the user's data. - kwargs: Other parameters to pass to self.post(). - - Returns: - A gdata.docs.data.DocsEntry of the moved entry or True if just moving the - item out of all folders (e.g. Move(source_entry)). - """ - entry = gdata.docs.data.DocsEntry(id=source_entry.id) - - # Remove the item from any folders it is already in. - if not keep_in_folders: - for folder in source_entry.InFolders(): - self.delete( - '%s/contents/%s' % (folder.href, source_entry.resource_id.text), - force=True) - - # If we're moving the resource into a folder, verify it is a folder entry. - if folder_entry is not None: - if folder_entry.get_document_type() == gdata.docs.data.FOLDER_LABEL: - return self.post(entry, folder_entry.content.src, - auth_token=auth_token, **kwargs) - else: - raise gdata.client.Error, 'Trying to move item into a non-folder.' - - return True - - Move = move - - def upload(self, media, title, folder_or_uri=None, content_type=None, - auth_token=None, **kwargs): - """Uploads a file to Google Docs. - - Args: - media: A gdata.data.MediaSource object containing the file to be - uploaded or a string of the filepath. - title: str The title of the document on the server after being - uploaded. - folder_or_uri: gdata.docs.data.DocsEntry or str (optional) An object with - a link to the folder or the uri to upload the file to. - Note: A valid uri for a folder is of the form: - /feeds/default/private/full/folder%3Afolder_id/contents - content_type: str (optional) The file's mimetype. If not provided, the - one in the media source object is used or the mimetype is inferred - from the filename (if media is a string). When media is a filename, - it is always recommended to pass in a content type. - auth_token: (optional) gdata.gauth.ClientLoginToken, AuthSubToken, or - OAuthToken which authorizes this client to edit the user's data. - kwargs: Other parameters to pass to self.post(). - - Returns: - A gdata.docs.data.DocsEntry containing information about uploaded doc. - """ - uri = None - if folder_or_uri is not None: - if isinstance(folder_or_uri, gdata.docs.data.DocsEntry): - # Verify that we're uploading the resource into to a folder. - if folder_or_uri.get_document_type() == gdata.docs.data.FOLDER_LABEL: - uri = folder_or_uri.content.src - else: - raise gdata.client.Error, 'Trying to upload item to a non-folder.' - else: - uri = folder_or_uri - else: - uri = DOCLIST_FEED_URI - - # Create media source if media is a filepath. - if isinstance(media, (str, unicode)): - mimetype = mimetypes.guess_type(media)[0] - if mimetype is None and content_type is None: - raise ValueError, ("Unknown mimetype. Please pass in the file's " - "content_type") - else: - media = gdata.data.MediaSource(file_path=media, - content_type=content_type) - - entry = gdata.docs.data.DocsEntry(title=atom.data.Title(text=title)) - - return self.post(entry, uri, media_source=media, - desired_class=gdata.docs.data.DocsEntry, - auth_token=auth_token, **kwargs) - - Upload = upload - - def download(self, entry_or_id_or_url, file_path, extra_params=None, - auth_token=None, **kwargs): - """Downloads a file from the Document List to local disk. - - Note: to download a file in memory, use the GetFileContent() method. - - Args: - entry_or_id_or_url: gdata.docs.data.DocsEntry or string representing a - resource id or URL to download the document from (such as the content - src link). - file_path: str The full path to save the file to. - extra_params: dict (optional) A map of any further parameters to control - how the document is downloaded/exported. For example, exporting a - spreadsheet as a .csv: extra_params={'gid': 0, 'exportFormat': 'csv'} - auth_token: (optional) gdata.gauth.ClientLoginToken, AuthSubToken, or - OAuthToken which authorizes this client to edit the user's data. - kwargs: Other parameters to pass to self._download_file(). - - Raises: - gdata.client.RequestError if the download URL is malformed or the server's - response was not successful. - ValueError if entry_or_id_or_url was a resource id for a filetype - in which the download link cannot be manually constructed (e.g. pdf). - """ - if isinstance(entry_or_id_or_url, gdata.docs.data.DocsEntry): - url = entry_or_id_or_url.content.src - else: - if gdata.docs.data.RESOURCE_ID_PATTERN.match(entry_or_id_or_url): - url = gdata.docs.data.make_content_link_from_resource_id( - entry_or_id_or_url) - else: - url = entry_or_id_or_url - - if extra_params is not None: - if 'exportFormat' in extra_params and url.find('/Export?') == -1: - raise gdata.client.Error, ('This entry type cannot be exported ' - 'as a different format.') - - if 'gid' in extra_params and url.find('spreadsheets') == -1: - raise gdata.client.Error, 'gid param is not valid for this doc type.' - - url += '&' + urllib.urlencode(extra_params) - - self._download_file(url, file_path, auth_token=auth_token, **kwargs) - - Download = download - - def export(self, entry_or_id_or_url, file_path, gid=None, auth_token=None, - **kwargs): - """Exports a document from the Document List in a different format. - - Args: - entry_or_id_or_url: gdata.docs.data.DocsEntry or string representing a - resource id or URL to download the document from (such as the content - src link). - file_path: str The full path to save the file to. The export - format is inferred from the the file extension. - gid: str (optional) grid id for downloading a single grid of a - spreadsheet. The param should only be used for .csv and .tsv - spreadsheet exports. - auth_token: (optional) gdata.gauth.ClientLoginToken, AuthSubToken, or - OAuthToken which authorizes this client to edit the user's data. - kwargs: Other parameters to pass to self.download(). - - Raises: - gdata.client.RequestError if the download URL is malformed or the server's - response was not successful. - """ - extra_params = {} - - match = gdata.docs.data.FILE_EXT_PATTERN.match(file_path) - if match: - extra_params['exportFormat'] = match.group(1) - - if gid is not None: - extra_params['gid'] = gid - - self.download(entry_or_id_or_url, file_path, extra_params, - auth_token=auth_token, **kwargs) - - Export = export - - -class DocsQuery(gdata.client.Query): - - def __init__(self, title=None, title_exact=None, opened_min=None, - opened_max=None, edited_min=None, edited_max=None, owner=None, - writer=None, reader=None, show_folders=None, - show_deleted=None, ocr=None, target_language=None, - source_language=None, convert=None, **kwargs): - """Constructs a query URL for the Google Documents List API. - - Args: - title: str (optional) Specifies the search terms for the title of a - document. This parameter used without title_exact will only - submit partial queries, not exact queries. - title_exact: str (optional) Meaningless without title. Possible values - are 'true' and 'false'. Note: Matches are case-insensitive. - opened_min: str (optional) Lower bound on the last time a document was - opened by the current user. Use the RFC 3339 timestamp - format. For example: opened_min='2005-08-09T09:57:00-08:00'. - opened_max: str (optional) Upper bound on the last time a document was - opened by the current user. (See also opened_min.) - edited_min: str (optional) Lower bound on the last time a document was - edited by the current user. This value corresponds to the - edited.text value in the doc's entry object, which - represents changes to the document's content or metadata. - Use the RFC 3339 timestamp format. For example: - edited_min='2005-08-09T09:57:00-08:00' - edited_max: str (optional) Upper bound on the last time a document was - edited by the user. (See also edited_min.) - owner: str (optional) Searches for documents with a specific owner. Use - the email address of the owner. For example: - owner='user@gmail.com' - writer: str (optional) Searches for documents which can be written to - by specific users. Use a single email address or a comma - separated list of email addresses. For example: - writer='user1@gmail.com,user@example.com' - reader: str (optional) Searches for documents which can be read by - specific users. (See also writer.) - show_folders: str (optional) Specifies whether the query should return - folders as well as documents. Possible values are 'true' - and 'false'. Default is false. - show_deleted: str (optional) Specifies whether the query should return - documents which are in the trash as well as other - documents. Possible values are 'true' and 'false'. - Default is false. - ocr: str (optional) Specifies whether to attempt OCR on a .jpg, .png, or - .gif upload. Possible values are 'true' and 'false'. Default is - false. See OCR in the Protocol Guide: - http://code.google.com/apis/documents/docs/3.0/developers_guide_protocol.html#OCR - target_language: str (optional) Specifies the language to translate a - document into. See Document Translation in the Protocol - Guide for a table of possible values: - http://code.google.com/apis/documents/docs/3.0/developers_guide_protocol.html#DocumentTranslation - source_language: str (optional) Specifies the source language of the - original document. Optional when using the translation - service. If not provided, Google will attempt to - auto-detect the source language. See Document - Translation in the Protocol Guide for a table of - possible values (link in target_language). - convert: str (optional) Used when uploading arbitrary file types to - specity if document-type uploads should convert to a native - Google Docs format. Possible values are 'true' and 'false'. - The default is 'true'. - """ - gdata.client.Query.__init__(self, **kwargs) - self.convert = convert - self.title = title - self.title_exact = title_exact - self.opened_min = opened_min - self.opened_max = opened_max - self.edited_min = edited_min - self.edited_max = edited_max - self.owner = owner - self.writer = writer - self.reader = reader - self.show_folders = show_folders - self.show_deleted = show_deleted - self.ocr = ocr - self.target_language = target_language - self.source_language = source_language - - def modify_request(self, http_request): - gdata.client._add_query_param('convert', self.convert, http_request) - gdata.client._add_query_param('title', self.title, http_request) - gdata.client._add_query_param('title-exact', self.title_exact, - http_request) - gdata.client._add_query_param('opened-min', self.opened_min, http_request) - gdata.client._add_query_param('opened-max', self.opened_max, http_request) - gdata.client._add_query_param('edited-min', self.edited_min, http_request) - gdata.client._add_query_param('edited-max', self.edited_max, http_request) - gdata.client._add_query_param('owner', self.owner, http_request) - gdata.client._add_query_param('writer', self.writer, http_request) - gdata.client._add_query_param('reader', self.reader, http_request) - gdata.client._add_query_param('showfolders', self.show_folders, - http_request) - gdata.client._add_query_param('showdeleted', self.show_deleted, - http_request) - gdata.client._add_query_param('ocr', self.ocr, http_request) - gdata.client._add_query_param('targetLanguage', self.target_language, - http_request) - gdata.client._add_query_param('sourceLanguage', self.source_language, - http_request) - gdata.client.Query.modify_request(self, http_request) - - ModifyRequest = modify_request diff --git a/gdata/docs/data.py b/gdata/docs/data.py deleted file mode 100644 index 3bee41079f..0000000000 --- a/gdata/docs/data.py +++ /dev/null @@ -1,280 +0,0 @@ -#!/usr/bin/python -# -# Copyright 2009 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 the DocList Data API""" - -__author__ = 'e.bidelman (Eric Bidelman)' - - -import re -import atom.core -import atom.data -import gdata.acl.data -import gdata.data - -DOCUMENTS_NS = 'http://schemas.google.com/docs/2007' -DOCUMENTS_TEMPLATE = '{http://schemas.google.com/docs/2007}%s' -ACL_FEEDLINK_REL = 'http://schemas.google.com/acl/2007#accessControlList' -REVISION_FEEDLINK_REL = DOCUMENTS_NS + '/revisions' - -# XML Namespaces used in Google Documents entities. -DATA_KIND_SCHEME = 'http://schemas.google.com/g/2005#kind' -DOCUMENT_LABEL = 'document' -SPREADSHEET_LABEL = 'spreadsheet' -PRESENTATION_LABEL = 'presentation' -FOLDER_LABEL = 'folder' -PDF_LABEL = 'pdf' - -LABEL_SCHEME = 'http://schemas.google.com/g/2005/labels' -STARRED_LABEL_TERM = LABEL_SCHEME + '#starred' -TRASHED_LABEL_TERM = LABEL_SCHEME + '#trashed' -HIDDEN_LABEL_TERM = LABEL_SCHEME + '#hidden' -MINE_LABEL_TERM = LABEL_SCHEME + '#mine' -PRIVATE_LABEL_TERM = LABEL_SCHEME + '#private' -SHARED_WITH_DOMAIN_LABEL_TERM = LABEL_SCHEME + '#shared-with-domain' -VIEWED_LABEL_TERM = LABEL_SCHEME + '#viewed' - -DOCS_PARENT_LINK_REL = DOCUMENTS_NS + '#parent' -DOCS_PUBLISH_LINK_REL = DOCUMENTS_NS + '#publish' - -FILE_EXT_PATTERN = re.compile('.*\.([a-zA-Z]{3,}$)') -RESOURCE_ID_PATTERN = re.compile('^([a-z]*)(:|%3A)([\w-]*)$') - -# File extension/mimetype pairs of common format. -MIMETYPES = { - 'CSV': 'text/csv', - 'TSV': 'text/tab-separated-values', - 'TAB': 'text/tab-separated-values', - 'DOC': 'application/msword', - 'DOCX': ('application/vnd.openxmlformats-officedocument.' - 'wordprocessingml.document'), - 'ODS': 'application/x-vnd.oasis.opendocument.spreadsheet', - 'ODT': 'application/vnd.oasis.opendocument.text', - 'RTF': 'application/rtf', - 'SXW': 'application/vnd.sun.xml.writer', - 'TXT': 'text/plain', - 'XLS': 'application/vnd.ms-excel', - 'XLSX': 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', - 'PDF': 'application/pdf', - 'PNG': 'image/png', - 'PPT': 'application/vnd.ms-powerpoint', - 'PPS': 'application/vnd.ms-powerpoint', - 'HTM': 'text/html', - 'HTML': 'text/html', - 'ZIP': 'application/zip', - 'SWF': 'application/x-shockwave-flash' - } - - -def make_kind_category(label): - """Builds the appropriate atom.data.Category for the label passed in. - - Args: - label: str The value for the category entry. - - Returns: - An atom.data.Category or None if label is None. - """ - if label is None: - return None - - return atom.data.Category( - scheme=DATA_KIND_SCHEME, term='%s#%s' % (DOCUMENTS_NS, label), label=label) - -MakeKindCategory = make_kind_category - -def make_content_link_from_resource_id(resource_id): - """Constructs export URL for a given resource. - - Args: - resource_id: str The document/item's resource id. Example presentation: - 'presentation%3A0A1234567890'. - - Raises: - gdata.client.ValueError if the resource_id is not a valid format. - """ - match = RESOURCE_ID_PATTERN.match(resource_id) - - if match: - label = match.group(1) - doc_id = match.group(3) - if label == DOCUMENT_LABEL: - return '/feeds/download/documents/Export?docId=%s' % doc_id - if label == PRESENTATION_LABEL: - return '/feeds/download/presentations/Export?docId=%s' % doc_id - if label == SPREADSHEET_LABEL: - return ('http://spreadsheets.google.com/feeds/download/spreadsheets/' - 'Export?key=%s' % doc_id) - raise ValueError, ('Invalid resource id: %s, or manually creating the ' - 'download url for this type of doc is not possible' - % resource_id) - -MakeContentLinkFromResourceId = make_content_link_from_resource_id - - -class ResourceId(atom.core.XmlElement): - """The DocList gd:resourceId element.""" - _qname = gdata.data.GDATA_TEMPLATE % 'resourceId' - - -class LastModifiedBy(atom.data.Person): - """The DocList gd:lastModifiedBy element.""" - _qname = gdata.data.GDATA_TEMPLATE % 'lastModifiedBy' - - -class LastViewed(atom.data.Person): - """The DocList gd:lastViewed element.""" - _qname = gdata.data.GDATA_TEMPLATE % 'lastViewed' - - -class WritersCanInvite(atom.core.XmlElement): - """The DocList docs:writersCanInvite element.""" - _qname = DOCUMENTS_TEMPLATE % 'writersCanInvite' - value = 'value' - - -class QuotaBytesUsed(atom.core.XmlElement): - """The DocList gd:quotaBytesUsed element.""" - _qname = gdata.data.GDATA_TEMPLATE % 'quotaBytesUsed' - - -class Publish(atom.core.XmlElement): - """The DocList docs:publish element.""" - _qname = DOCUMENTS_TEMPLATE % 'publish' - value = 'value' - - -class PublishAuto(atom.core.XmlElement): - """The DocList docs:publishAuto element.""" - _qname = DOCUMENTS_TEMPLATE % 'publishAuto' - value = 'value' - - -class PublishOutsideDomain(atom.core.XmlElement): - """The DocList docs:publishOutsideDomain element.""" - _qname = DOCUMENTS_TEMPLATE % 'publishOutsideDomain' - value = 'value' - - -class DocsEntry(gdata.data.GDEntry): - """A DocList version of an Atom Entry.""" - - last_viewed = LastViewed - last_modified_by = LastModifiedBy - resource_id = ResourceId - writers_can_invite = WritersCanInvite - quota_bytes_used = QuotaBytesUsed - feed_link = [gdata.data.FeedLink] - - def get_document_type(self): - """Extracts the type of document this DocsEntry is. - - This method returns the type of document the DocsEntry represents. Possible - values are document, presentation, spreadsheet, folder, or pdf. - - Returns: - A string representing the type of document. - """ - if self.category: - for category in self.category: - if category.scheme == DATA_KIND_SCHEME: - return category.label - else: - return None - - GetDocumentType = get_document_type - - def get_acl_feed_link(self): - """Extracts the DocsEntry's ACL feed . - - Returns: - A gdata.data.FeedLink object. - """ - for feed_link in self.feed_link: - if feed_link.rel == ACL_FEEDLINK_REL: - return feed_link - return None - - GetAclFeedLink = get_acl_feed_link - - def get_revisions_feed_link(self): - """Extracts the DocsEntry's revisions feed . - - Returns: - A gdata.data.FeedLink object. - """ - for feed_link in self.feed_link: - if feed_link.rel == REVISION_FEEDLINK_REL: - return feed_link - return None - - GetRevisionsFeedLink = get_revisions_feed_link - - def in_folders(self): - """Returns the parents link(s) (folders) of this entry.""" - links = [] - for link in self.link: - if link.rel == DOCS_PARENT_LINK_REL and link.href: - links.append(link) - return links - - InFolders = in_folders - - -class Acl(gdata.acl.data.AclEntry): - """A document ACL entry.""" - - -class DocList(gdata.data.GDFeed): - """The main DocList feed containing a list of Google Documents.""" - entry = [DocsEntry] - - -class AclFeed(gdata.acl.data.AclFeed): - """A DocList ACL feed.""" - entry = [Acl] - - -class Revision(gdata.data.GDEntry): - """A document Revision entry.""" - publish = Publish - publish_auto = PublishAuto - publish_outside_domain = PublishOutsideDomain - - def find_publish_link(self): - """Get the link that points to the published document on the web. - - Returns: - A str for the URL in the link with a rel ending in #publish. - """ - return self.find_url(DOCS_PUBLISH_LINK_REL) - - FindPublishLink = find_publish_link - - def get_publish_link(self): - """Get the link that points to the published document on the web. - - Returns: - A gdata.data.Link for the link with a rel ending in #publish. - """ - return self.get_link(DOCS_PUBLISH_LINK_REL) - - GetPublishLink = get_publish_link - - -class RevisionFeed(gdata.data.GDFeed): - """A DocList Revision feed.""" - entry = [Revision] diff --git a/gdata/docs/service.py b/gdata/docs/service.py deleted file mode 100644 index 019528a820..0000000000 --- a/gdata/docs/service.py +++ /dev/null @@ -1,611 +0,0 @@ -#!/usr/bin/python -# -# Copyright 2009 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. - -"""DocsService extends the GDataService to streamline Google Documents - operations. - - DocsService: Provides methods to query feeds and manipulate items. - Extends GDataService. - - DocumentQuery: Queries a Google Document list feed. - - DocumentAclQuery: Queries a Google Document Acl feed. -""" - - -__author__ = ('api.jfisher (Jeff Fisher), ' - 'e.bidelman (Eric Bidelman)') - -import re -import atom -import gdata.service -import gdata.docs -import urllib - -# XML Namespaces used in Google Documents entities. -DATA_KIND_SCHEME = gdata.GDATA_NAMESPACE + '#kind' -DOCUMENT_LABEL = 'document' -SPREADSHEET_LABEL = 'spreadsheet' -PRESENTATION_LABEL = 'presentation' -FOLDER_LABEL = 'folder' -PDF_LABEL = 'pdf' - -LABEL_SCHEME = gdata.GDATA_NAMESPACE + '/labels' -STARRED_LABEL_TERM = LABEL_SCHEME + '#starred' -TRASHED_LABEL_TERM = LABEL_SCHEME + '#trashed' -HIDDEN_LABEL_TERM = LABEL_SCHEME + '#hidden' -MINE_LABEL_TERM = LABEL_SCHEME + '#mine' -PRIVATE_LABEL_TERM = LABEL_SCHEME + '#private' -SHARED_WITH_DOMAIN_LABEL_TERM = LABEL_SCHEME + '#shared-with-domain' -VIEWED_LABEL_TERM = LABEL_SCHEME + '#viewed' - -FOLDERS_SCHEME_PREFIX = gdata.docs.DOCUMENTS_NAMESPACE + '/folders/' - -# File extensions of documents that are permitted to be uploaded or downloaded. -SUPPORTED_FILETYPES = { - 'CSV': 'text/csv', - 'TSV': 'text/tab-separated-values', - 'TAB': 'text/tab-separated-values', - 'DOC': 'application/msword', - 'DOCX': ('application/vnd.openxmlformats-officedocument.' - 'wordprocessingml.document'), - 'ODS': 'application/x-vnd.oasis.opendocument.spreadsheet', - 'ODT': 'application/vnd.oasis.opendocument.text', - 'RTF': 'application/rtf', - 'SXW': 'application/vnd.sun.xml.writer', - 'TXT': 'text/plain', - 'XLS': 'application/vnd.ms-excel', - 'XLSX': 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', - 'PDF': 'application/pdf', - 'PNG': 'image/png', - 'PPT': 'application/vnd.ms-powerpoint', - 'PPS': 'application/vnd.ms-powerpoint', - 'HTM': 'text/html', - 'HTML': 'text/html', - 'ZIP': 'application/zip', - 'SWF': 'application/x-shockwave-flash' - } - - -class DocsService(gdata.service.GDataService): - - """Client extension for the Google Documents service Document List feed.""" - - __FILE_EXT_PATTERN = re.compile('.*\.([a-zA-Z]{3,}$)') - __RESOURCE_ID_PATTERN = re.compile('^([a-z]*)(:|%3A)([\w-]*)$') - - def __init__(self, email=None, password=None, source=None, - server='docs.google.com', additional_headers=None, **kwargs): - """Creates a client for the Google Documents 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='writely', source=source, - server=server, additional_headers=additional_headers, **kwargs) - - def _MakeKindCategory(self, label): - if label is None: - return None - return atom.Category(scheme=DATA_KIND_SCHEME, - term=gdata.docs.DOCUMENTS_NAMESPACE + '#' + label, label=label) - - def _MakeContentLinkFromId(self, resource_id): - match = self.__RESOURCE_ID_PATTERN.match(resource_id) - label = match.group(1) - doc_id = match.group(3) - if label == DOCUMENT_LABEL: - return '/feeds/download/documents/Export?docId=%s' % doc_id - if label == PRESENTATION_LABEL: - return '/feeds/download/presentations/Export?docId=%s' % doc_id - if label == SPREADSHEET_LABEL: - return ('http://spreadsheets.google.com/feeds/download/spreadsheets/' - 'Export?key=%s' % doc_id) - raise ValueError, 'Invalid resource id: %s' % resource_id - - def _UploadFile(self, media_source, title, category, folder_or_uri=None): - """Uploads a file to the Document List feed. - - Args: - media_source: A gdata.MediaSource object containing the file to be - uploaded. - title: string The title of the document on the server after being - uploaded. - category: An atom.Category object specifying the appropriate document - type. - folder_or_uri: DocumentListEntry or string (optional) An object with a - link to a folder or a uri to a folder to upload to. - Note: A valid uri for a folder is of the form: - /feeds/folders/private/full/folder%3Afolder_id - - Returns: - A DocumentListEntry containing information about the document created on - the Google Documents service. - """ - if folder_or_uri: - try: - uri = folder_or_uri.content.src - except AttributeError: - uri = folder_or_uri - else: - uri = '/feeds/documents/private/full' - - entry = gdata.docs.DocumentListEntry() - entry.title = atom.Title(text=title) - if category is not None: - entry.category.append(category) - entry = self.Post(entry, uri, media_source=media_source, - extra_headers={'Slug': media_source.file_name}, - converter=gdata.docs.DocumentListEntryFromString) - return entry - - def _DownloadFile(self, uri, file_path): - """Downloads a file. - - Args: - uri: string The full Export URL to download the file from. - file_path: string The full path to save the file to. - - Raises: - RequestError: on error response from server. - """ - server_response = self.request('GET', uri) - response_body = server_response.read() - if server_response.status != 200: - raise gdata.service.RequestError, {'status': server_response.status, - 'reason': server_response.reason, - 'body': response_body} - f = open(file_path, 'wb') - f.write(response_body) - f.flush() - f.close() - - def MoveIntoFolder(self, source_entry, folder_entry): - """Moves a document into a folder in the Document List Feed. - - Args: - source_entry: DocumentListEntry An object representing the source - document/folder. - folder_entry: DocumentListEntry An object with a link to the destination - folder. - - Returns: - A DocumentListEntry containing information about the document created on - the Google Documents service. - """ - entry = gdata.docs.DocumentListEntry() - entry.id = source_entry.id - entry = self.Post(entry, folder_entry.content.src, - converter=gdata.docs.DocumentListEntryFromString) - return entry - - def Query(self, uri, converter=gdata.docs.DocumentListFeedFromString): - """Queries the Document List feed and returns the resulting feed of - entries. - - Args: - uri: string The full URI to be queried. This can contain query - parameters, a hostname, or simply the relative path to a Document - List feed. The DocumentQuery object is useful when constructing - query parameters. - converter: func (optional) A function which will be executed on the - retrieved item, generally to render it into a Python object. - By default the DocumentListFeedFromString function is used to - return a DocumentListFeed object. This is because most feed - queries will result in a feed and not a single entry. - """ - return self.Get(uri, converter=converter) - - def QueryDocumentListFeed(self, uri): - """Retrieves a DocumentListFeed by retrieving a URI based off the Document - List feed, including any query parameters. A DocumentQuery object can - be used to construct these parameters. - - Args: - uri: string The URI of the feed being retrieved possibly with query - parameters. - - Returns: - A DocumentListFeed object representing the feed returned by the server. - """ - return self.Get(uri, converter=gdata.docs.DocumentListFeedFromString) - - def GetDocumentListEntry(self, uri): - """Retrieves a particular DocumentListEntry by its unique URI. - - Args: - uri: string The unique URI of an entry in a Document List feed. - - Returns: - A DocumentListEntry object representing the retrieved entry. - """ - return self.Get(uri, converter=gdata.docs.DocumentListEntryFromString) - - def GetDocumentListFeed(self, uri=None): - """Retrieves a feed containing all of a user's documents. - - Args: - uri: string A full URI to query the Document List feed. - """ - if not uri: - uri = gdata.docs.service.DocumentQuery().ToUri() - return self.QueryDocumentListFeed(uri) - - def GetDocumentListAclEntry(self, uri): - """Retrieves a particular DocumentListAclEntry by its unique URI. - - Args: - uri: string The unique URI of an entry in a Document List feed. - - Returns: - A DocumentListAclEntry object representing the retrieved entry. - """ - return self.Get(uri, converter=gdata.docs.DocumentListAclEntryFromString) - - def GetDocumentListAclFeed(self, uri): - """Retrieves a feed containing all of a user's documents. - - Args: - uri: string The URI of a document's Acl feed to retrieve. - - Returns: - A DocumentListAclFeed object representing the ACL feed - returned by the server. - """ - return self.Get(uri, converter=gdata.docs.DocumentListAclFeedFromString) - - def Upload(self, media_source, title, folder_or_uri=None, label=None): - """Uploads a document inside of a MediaSource object to the Document List - feed with the given title. - - Args: - media_source: MediaSource The gdata.MediaSource object containing a - document file to be uploaded. - title: string The title of the document on the server after being - uploaded. - folder_or_uri: DocumentListEntry or string (optional) An object with a - link to a folder or a uri to a folder to upload to. - Note: A valid uri for a folder is of the form: - /feeds/folders/private/full/folder%3Afolder_id - label: optional label describing the type of the document to be created. - - Returns: - A DocumentListEntry containing information about the document created - on the Google Documents service. - """ - - return self._UploadFile(media_source, title, self._MakeKindCategory(label), - folder_or_uri) - - def Download(self, entry_or_id_or_url, file_path, export_format=None, - gid=None, extra_params=None): - """Downloads a document from the Document List. - - Args: - entry_or_id_or_url: a DocumentListEntry, or the resource id of an entry, - or a url to download from (such as the content src). - file_path: string The full path to save the file to. - export_format: the format to convert to, if conversion is required. - gid: grid id, for downloading a single grid of a spreadsheet - extra_params: a map of any further parameters to control how the document - is downloaded - - Raises: - RequestError if the service does not respond with success - """ - - if isinstance(entry_or_id_or_url, gdata.docs.DocumentListEntry): - url = entry_or_id_or_url.content.src - else: - if self.__RESOURCE_ID_PATTERN.match(entry_or_id_or_url): - url = self._MakeContentLinkFromId(entry_or_id_or_url) - else: - url = entry_or_id_or_url - - if export_format is not None: - if url.find('/Export?') == -1: - raise gdata.service.Error, ('This entry cannot be exported ' - 'as a different format') - url += '&exportFormat=%s' % export_format - - if gid is not None: - if url.find('spreadsheets') == -1: - raise gdata.service.Error, 'grid id param is not valid for this entry' - url += '&gid=%s' % gid - - if extra_params: - url += '&' + urllib.urlencode(extra_params) - - self._DownloadFile(url, file_path) - - def Export(self, entry_or_id_or_url, file_path, gid=None, extra_params=None): - """Downloads a document from the Document List in a different format. - - Args: - entry_or_id_or_url: a DocumentListEntry, or the resource id of an entry, - or a url to download from (such as the content src). - file_path: string The full path to save the file to. The export - format is inferred from the the file extension. - gid: grid id, for downloading a single grid of a spreadsheet - extra_params: a map of any further parameters to control how the document - is downloaded - - Raises: - RequestError if the service does not respond with success - """ - ext = None - match = self.__FILE_EXT_PATTERN.match(file_path) - if match: - ext = match.group(1) - self.Download(entry_or_id_or_url, file_path, ext, gid, extra_params) - - def CreateFolder(self, title, folder_or_uri=None): - """Creates a folder in the Document List feed. - - Args: - title: string The title of the folder on the server after being created. - folder_or_uri: DocumentListEntry or string (optional) An object with a - link to a folder or a uri to a folder to upload to. - Note: A valid uri for a folder is of the form: - /feeds/folders/private/full/folder%3Afolder_id - - Returns: - A DocumentListEntry containing information about the folder created on - the Google Documents service. - """ - if folder_or_uri: - try: - uri = folder_or_uri.content.src - except AttributeError: - uri = folder_or_uri - else: - uri = '/feeds/documents/private/full' - - folder_entry = gdata.docs.DocumentListEntry() - folder_entry.title = atom.Title(text=title) - folder_entry.category.append(self._MakeKindCategory(FOLDER_LABEL)) - folder_entry = self.Post(folder_entry, uri, - converter=gdata.docs.DocumentListEntryFromString) - - return folder_entry - - - def MoveOutOfFolder(self, source_entry): - """Moves a document into a folder in the Document List Feed. - - Args: - source_entry: DocumentListEntry An object representing the source - document/folder. - - Returns: - True if the entry was moved out. - """ - return self.Delete(source_entry.GetEditLink().href) - - # Deprecated methods - - #@atom.deprecated('Please use Upload instead') - def UploadPresentation(self, media_source, title, folder_or_uri=None): - """Uploads a presentation inside of a MediaSource object to the Document - List feed with the given title. - - This method is deprecated, use Upload instead. - - Args: - media_source: MediaSource The MediaSource object containing a - presentation file to be uploaded. - title: string The title of the presentation on the server after being - uploaded. - folder_or_uri: DocumentListEntry or string (optional) An object with a - link to a folder or a uri to a folder to upload to. - Note: A valid uri for a folder is of the form: - /feeds/folders/private/full/folder%3Afolder_id - - Returns: - A DocumentListEntry containing information about the presentation created - on the Google Documents service. - """ - return self._UploadFile( - media_source, title, self._MakeKindCategory(PRESENTATION_LABEL), - folder_or_uri=folder_or_uri) - - UploadPresentation = atom.deprecated('Please use Upload instead')( - UploadPresentation) - - #@atom.deprecated('Please use Upload instead') - def UploadSpreadsheet(self, media_source, title, folder_or_uri=None): - """Uploads a spreadsheet inside of a MediaSource object to the Document - List feed with the given title. - - This method is deprecated, use Upload instead. - - Args: - media_source: MediaSource The MediaSource object containing a spreadsheet - file to be uploaded. - title: string The title of the spreadsheet on the server after being - uploaded. - folder_or_uri: DocumentListEntry or string (optional) An object with a - link to a folder or a uri to a folder to upload to. - Note: A valid uri for a folder is of the form: - /feeds/folders/private/full/folder%3Afolder_id - - Returns: - A DocumentListEntry containing information about the spreadsheet created - on the Google Documents service. - """ - return self._UploadFile( - media_source, title, self._MakeKindCategory(SPREADSHEET_LABEL), - folder_or_uri=folder_or_uri) - - UploadSpreadsheet = atom.deprecated('Please use Upload instead')( - UploadSpreadsheet) - - #@atom.deprecated('Please use Upload instead') - def UploadDocument(self, media_source, title, folder_or_uri=None): - """Uploads a document inside of a MediaSource object to the Document List - feed with the given title. - - This method is deprecated, use Upload instead. - - Args: - media_source: MediaSource The gdata.MediaSource object containing a - document file to be uploaded. - title: string The title of the document on the server after being - uploaded. - folder_or_uri: DocumentListEntry or string (optional) An object with a - link to a folder or a uri to a folder to upload to. - Note: A valid uri for a folder is of the form: - /feeds/folders/private/full/folder%3Afolder_id - - Returns: - A DocumentListEntry containing information about the document created - on the Google Documents service. - """ - return self._UploadFile( - media_source, title, self._MakeKindCategory(DOCUMENT_LABEL), - folder_or_uri=folder_or_uri) - - UploadDocument = atom.deprecated('Please use Upload instead')( - UploadDocument) - - """Calling any of these functions is the same as calling Export""" - DownloadDocument = atom.deprecated('Please use Export instead')(Export) - DownloadPresentation = atom.deprecated('Please use Export instead')(Export) - DownloadSpreadsheet = atom.deprecated('Please use Export instead')(Export) - - """Calling any of these functions is the same as calling MoveIntoFolder""" - MoveDocumentIntoFolder = atom.deprecated( - 'Please use MoveIntoFolder instead')(MoveIntoFolder) - MovePresentationIntoFolder = atom.deprecated( - 'Please use MoveIntoFolder instead')(MoveIntoFolder) - MoveSpreadsheetIntoFolder = atom.deprecated( - 'Please use MoveIntoFolder instead')(MoveIntoFolder) - MoveFolderIntoFolder = atom.deprecated( - 'Please use MoveIntoFolder instead')(MoveIntoFolder) - - -class DocumentQuery(gdata.service.Query): - - """Object used to construct a URI to query the Google Document List feed""" - - def __init__(self, feed='/feeds/documents', visibility='private', - projection='full', text_query=None, params=None, - categories=None): - """Constructor for Document 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. - - Yields: - A DocumentQuery object used to construct a URI based on the Document - List feed. - """ - self.visibility = visibility - self.projection = projection - 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 Document - List feed. - """ - old_feed = self.feed - self.feed = '/'.join([old_feed, self.visibility, self.projection]) - new_feed = gdata.service.Query.ToUri(self) - self.feed = old_feed - return new_feed - - def AddNamedFolder(self, email, folder_name): - """Adds a named folder category, qualified by a schema. - - This function lets you query for documents that are contained inside a - named folder without fear of collision with other categories. - - Args: - email: string The email of the user who owns the folder. - folder_name: string The name of the folder. - - Returns: - The string of the category that was added to the object. - """ - - category = '{%s%s}%s' % (FOLDERS_SCHEME_PREFIX, email, folder_name) - self.categories.append(category) - return category - - def RemoveNamedFolder(self, email, folder_name): - """Removes a named folder category, qualified by a schema. - - Args: - email: string The email of the user who owns the folder. - folder_name: string The name of the folder. - - Returns: - The string of the category that was removed to the object. - """ - category = '{%s%s}%s' % (FOLDERS_SCHEME_PREFIX, email, folder_name) - self.categories.remove(category) - return category - - -class DocumentAclQuery(gdata.service.Query): - - """Object used to construct a URI to query a Document's ACL feed""" - - def __init__(self, resource_id, feed='/feeds/acl/private/full'): - """Constructor for Document ACL Query - - Args: - resource_id: string The resource id. (e.g. 'document%3Adocument_id', - 'spreadsheet%3Aspreadsheet_id', etc.) - feed: string (optional) The path for the feed. - (e.g. '/feeds/acl/private/full') - - Yields: - A DocumentAclQuery object used to construct a URI based on the Document - ACL feed. - """ - self.resource_id = resource_id - gdata.service.Query.__init__(self, feed) - - 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 Document - ACL feed. - """ - return '%s/%s' % (gdata.service.Query.ToUri(self), self.resource_id) diff --git a/gdata/dublincore/__init__.py b/gdata/dublincore/__init__.py deleted file mode 100644 index 22071f7a11..0000000000 --- a/gdata/dublincore/__init__.py +++ /dev/null @@ -1,15 +0,0 @@ -#!/usr/bin/python -# -# Copyright (C) 2009 Google 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. diff --git a/gdata/dublincore/data.py b/gdata/dublincore/data.py deleted file mode 100644 index c6345c16fb..0000000000 --- a/gdata/dublincore/data.py +++ /dev/null @@ -1,78 +0,0 @@ -#!/usr/bin/python -# -# Copyright (C) 2009 Google 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. - -"""Contains the data classes of the Dublin Core Metadata Initiative (DCMI) Extension""" - - -__author__ = 'j.s@google.com (Jeff Scudder)' - - -import atom.core - - -DC_TEMPLATE = '{http://purl.org/dc/terms/}%s' - - -class Creator(atom.core.XmlElement): - """Entity primarily responsible for making the resource.""" - _qname = DC_TEMPLATE % 'creator' - - -class Date(atom.core.XmlElement): - """Point or period of time associated with an event in the lifecycle of the resource.""" - _qname = DC_TEMPLATE % 'date' - - -class Description(atom.core.XmlElement): - """Account of the resource.""" - _qname = DC_TEMPLATE % 'description' - - -class Format(atom.core.XmlElement): - """File format, physical medium, or dimensions of the resource.""" - _qname = DC_TEMPLATE % 'format' - - -class Identifier(atom.core.XmlElement): - """An unambiguous reference to the resource within a given context.""" - _qname = DC_TEMPLATE % 'identifier' - - -class Language(atom.core.XmlElement): - """Language of the resource.""" - _qname = DC_TEMPLATE % 'language' - - -class Publisher(atom.core.XmlElement): - """Entity responsible for making the resource available.""" - _qname = DC_TEMPLATE % 'publisher' - - -class Rights(atom.core.XmlElement): - """Information about rights held in and over the resource.""" - _qname = DC_TEMPLATE % 'rights' - - -class Subject(atom.core.XmlElement): - """Topic of the resource.""" - _qname = DC_TEMPLATE % 'subject' - - -class Title(atom.core.XmlElement): - """Name given to the resource.""" - _qname = DC_TEMPLATE % 'title' - - diff --git a/gdata/exif/__init__.py b/gdata/exif/__init__.py deleted file mode 100644 index 7f1f9c2abd..0000000000 --- a/gdata/exif/__init__.py +++ /dev/null @@ -1,217 +0,0 @@ -# -*-*- encoding: utf-8 -*-*- -# -# This is gdata.photos.exif, implementing the exif namespace in gdata -# -# $Id: __init__.py 81 2007-10-03 14:41:42Z havard.gulldahl $ -# -# Copyright 2007 HÃ¥vard Gulldahl -# Portions copyright 2007 Google 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. - -"""This module maps elements from the {EXIF} namespace[1] to GData objects. -These elements describe image data, using exif attributes[2]. - -Picasa Web Albums uses the exif namespace to represent Exif data encoded -in a photo [3]. - -Picasa Web Albums uses the following exif elements: -exif:distance -exif:exposure -exif:flash -exif:focallength -exif:fstop -exif:imageUniqueID -exif:iso -exif:make -exif:model -exif:tags -exif:time - -[1]: http://schemas.google.com/photos/exif/2007. -[2]: http://en.wikipedia.org/wiki/Exif -[3]: http://code.google.com/apis/picasaweb/reference.html#exif_reference -""" - - -__author__ = u'havard@gulldahl.no'# (HÃ¥vard Gulldahl)' #BUG: pydoc chokes on non-ascii chars in __author__ -__license__ = 'Apache License v2' - - -import atom -import gdata - -EXIF_NAMESPACE = 'http://schemas.google.com/photos/exif/2007' - -class ExifBaseElement(atom.AtomBase): - """Base class for elements in the EXIF_NAMESPACE (%s). To add new elements, you only need to add the element tag name to self._tag - """ % EXIF_NAMESPACE - - _tag = '' - _namespace = EXIF_NAMESPACE - _children = atom.AtomBase._children.copy() - _attributes = atom.AtomBase._attributes.copy() - - def __init__(self, name=None, extension_elements=None, - extension_attributes=None, text=None): - self.name = name - self.text = text - self.extension_elements = extension_elements or [] - self.extension_attributes = extension_attributes or {} - -class Distance(ExifBaseElement): - "(float) The distance to the subject, e.g. 0.0" - - _tag = 'distance' -def DistanceFromString(xml_string): - return atom.CreateClassFromXMLString(Distance, xml_string) - -class Exposure(ExifBaseElement): - "(float) The exposure time used, e.g. 0.025 or 8.0E4" - - _tag = 'exposure' -def ExposureFromString(xml_string): - return atom.CreateClassFromXMLString(Exposure, xml_string) - -class Flash(ExifBaseElement): - """(string) Boolean value indicating whether the flash was used. - The .text attribute will either be `true' or `false' - - As a convenience, this object's .bool method will return what you want, - so you can say: - - flash_used = bool(Flash) - - """ - - _tag = 'flash' - def __bool__(self): - if self.text.lower() in ('true','false'): - return self.text.lower() == 'true' -def FlashFromString(xml_string): - return atom.CreateClassFromXMLString(Flash, xml_string) - -class Focallength(ExifBaseElement): - "(float) The focal length used, e.g. 23.7" - - _tag = 'focallength' -def FocallengthFromString(xml_string): - return atom.CreateClassFromXMLString(Focallength, xml_string) - -class Fstop(ExifBaseElement): - "(float) The fstop value used, e.g. 5.0" - - _tag = 'fstop' -def FstopFromString(xml_string): - return atom.CreateClassFromXMLString(Fstop, xml_string) - -class ImageUniqueID(ExifBaseElement): - "(string) The unique image ID for the photo. Generated by Google Photo servers" - - _tag = 'imageUniqueID' -def ImageUniqueIDFromString(xml_string): - return atom.CreateClassFromXMLString(ImageUniqueID, xml_string) - -class Iso(ExifBaseElement): - "(int) The iso equivalent value used, e.g. 200" - - _tag = 'iso' -def IsoFromString(xml_string): - return atom.CreateClassFromXMLString(Iso, xml_string) - -class Make(ExifBaseElement): - "(string) The make of the camera used, e.g. Fictitious Camera Company" - - _tag = 'make' -def MakeFromString(xml_string): - return atom.CreateClassFromXMLString(Make, xml_string) - -class Model(ExifBaseElement): - "(string) The model of the camera used,e.g AMAZING-100D" - - _tag = 'model' -def ModelFromString(xml_string): - return atom.CreateClassFromXMLString(Model, xml_string) - -class Time(ExifBaseElement): - """(int) The date/time the photo was taken, e.g. 1180294337000. - Represented as the number of milliseconds since January 1st, 1970. - - The value of this element will always be identical to the value - of the . - - Look at this object's .isoformat() for a human friendly datetime string: - - photo_epoch = Time.text # 1180294337000 - photo_isostring = Time.isoformat() # '2007-05-27T19:32:17.000Z' - - Alternatively: - photo_datetime = Time.datetime() # (requires python >= 2.3) - """ - - _tag = 'time' - def isoformat(self): - """(string) Return the timestamp as a ISO 8601 formatted string, - e.g. '2007-05-27T19:32:17.000Z' - """ - import time - epoch = float(self.text)/1000 - return time.strftime('%Y-%m-%dT%H:%M:%S.000Z', time.gmtime(epoch)) - - def datetime(self): - """(datetime.datetime) Return the timestamp as a datetime.datetime object - - Requires python 2.3 - """ - import datetime - epoch = float(self.text)/1000 - return datetime.datetime.fromtimestamp(epoch) - -def TimeFromString(xml_string): - return atom.CreateClassFromXMLString(Time, xml_string) - -class Tags(ExifBaseElement): - """The container for all exif elements. - The element can appear as a child of a photo entry. - """ - - _tag = 'tags' - _children = atom.AtomBase._children.copy() - _children['{%s}fstop' % EXIF_NAMESPACE] = ('fstop', Fstop) - _children['{%s}make' % EXIF_NAMESPACE] = ('make', Make) - _children['{%s}model' % EXIF_NAMESPACE] = ('model', Model) - _children['{%s}distance' % EXIF_NAMESPACE] = ('distance', Distance) - _children['{%s}exposure' % EXIF_NAMESPACE] = ('exposure', Exposure) - _children['{%s}flash' % EXIF_NAMESPACE] = ('flash', Flash) - _children['{%s}focallength' % EXIF_NAMESPACE] = ('focallength', Focallength) - _children['{%s}iso' % EXIF_NAMESPACE] = ('iso', Iso) - _children['{%s}time' % EXIF_NAMESPACE] = ('time', Time) - _children['{%s}imageUniqueID' % EXIF_NAMESPACE] = ('imageUniqueID', ImageUniqueID) - - def __init__(self, extension_elements=None, extension_attributes=None, text=None): - ExifBaseElement.__init__(self, extension_elements=extension_elements, - extension_attributes=extension_attributes, - text=text) - self.fstop=None - self.make=None - self.model=None - self.distance=None - self.exposure=None - self.flash=None - self.focallength=None - self.iso=None - self.time=None - self.imageUniqueID=None -def TagsFromString(xml_string): - return atom.CreateClassFromXMLString(Tags, xml_string) - diff --git a/gdata/finance/__init__.py b/gdata/finance/__init__.py deleted file mode 100644 index 28ab898d06..0000000000 --- a/gdata/finance/__init__.py +++ /dev/null @@ -1,486 +0,0 @@ -#!/usr/bin/python -# -# Copyright (C) 2009 Tan Swee Heng -# -# 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. - - -"""Contains extensions to Atom objects used with Google Finance.""" - - -__author__ = 'thesweeheng@gmail.com' - - -import atom -import gdata - - -GD_NAMESPACE = 'http://schemas.google.com/g/2005' -GF_NAMESPACE = 'http://schemas.google.com/finance/2007' - - -class Money(atom.AtomBase): - """The element.""" - _tag = 'money' - _namespace = GD_NAMESPACE - _attributes = atom.AtomBase._attributes.copy() - _attributes['amount'] = 'amount' - _attributes['currencyCode'] = 'currency_code' - - def __init__(self, amount=None, currency_code=None, **kwargs): - self.amount = amount - self.currency_code = currency_code - atom.AtomBase.__init__(self, **kwargs) - - def __str__(self): - return "%s %s" % (self.amount, self.currency_code) - - -def MoneyFromString(xml_string): - return atom.CreateClassFromXMLString(Money, xml_string) - - -class _Monies(atom.AtomBase): - """An element containing multiple in multiple currencies.""" - _namespace = GF_NAMESPACE - _children = atom.AtomBase._children.copy() - _children['{%s}money' % GD_NAMESPACE] = ('money', [Money]) - - def __init__(self, money=None, **kwargs): - self.money = money or [] - atom.AtomBase.__init__(self, **kwargs) - - def __str__(self): - return " / ".join(["%s" % i for i in self.money]) - - -class CostBasis(_Monies): - """The element.""" - _tag = 'costBasis' - - -def CostBasisFromString(xml_string): - return atom.CreateClassFromXMLString(CostBasis, xml_string) - - -class DaysGain(_Monies): - """The element.""" - _tag = 'daysGain' - - -def DaysGainFromString(xml_string): - return atom.CreateClassFromXMLString(DaysGain, xml_string) - - -class Gain(_Monies): - """The element.""" - _tag = 'gain' - - -def GainFromString(xml_string): - return atom.CreateClassFromXMLString(Gain, xml_string) - - -class MarketValue(_Monies): - """The element.""" - _tag = 'gain' - _tag = 'marketValue' - - -def MarketValueFromString(xml_string): - return atom.CreateClassFromXMLString(MarketValue, xml_string) - - -class Commission(_Monies): - """The element.""" - _tag = 'commission' - - -def CommissionFromString(xml_string): - return atom.CreateClassFromXMLString(Commission, xml_string) - - -class Price(_Monies): - """The element.""" - _tag = 'price' - - -def PriceFromString(xml_string): - return atom.CreateClassFromXMLString(Price, xml_string) - - -class Symbol(atom.AtomBase): - """The element.""" - _tag = 'symbol' - _namespace = GF_NAMESPACE - _attributes = atom.AtomBase._attributes.copy() - _attributes['fullName'] = 'full_name' - _attributes['exchange'] = 'exchange' - _attributes['symbol'] = 'symbol' - - def __init__(self, full_name=None, exchange=None, symbol=None, **kwargs): - self.full_name = full_name - self.exchange = exchange - self.symbol = symbol - atom.AtomBase.__init__(self, **kwargs) - - def __str__(self): - return "%s:%s (%s)" % (self.exchange, self.symbol, self.full_name) - - -def SymbolFromString(xml_string): - return atom.CreateClassFromXMLString(Symbol, xml_string) - - -class TransactionData(atom.AtomBase): - """The element.""" - _tag = 'transactionData' - _namespace = GF_NAMESPACE - _attributes = atom.AtomBase._attributes.copy() - _attributes['type'] = 'type' - _attributes['date'] = 'date' - _attributes['shares'] = 'shares' - _attributes['notes'] = 'notes' - _children = atom.AtomBase._children.copy() - _children['{%s}commission' % GF_NAMESPACE] = ('commission', Commission) - _children['{%s}price' % GF_NAMESPACE] = ('price', Price) - - def __init__(self, type=None, date=None, shares=None, - notes=None, commission=None, price=None, **kwargs): - self.type = type - self.date = date - self.shares = shares - self.notes = notes - self.commission = commission - self.price = price - atom.AtomBase.__init__(self, **kwargs) - - -def TransactionDataFromString(xml_string): - return atom.CreateClassFromXMLString(TransactionData, xml_string) - - -class TransactionEntry(gdata.GDataEntry): - """An entry of the transaction feed. - - A TransactionEntry contains TransactionData such as the transaction - type (Buy, Sell, Sell Short, or Buy to Cover), the number of units, - the date, the price, any commission, and any notes. - """ - _tag = 'entry' - _namespace = atom.ATOM_NAMESPACE - _children = gdata.GDataEntry._children.copy() - _children['{%s}transactionData' % GF_NAMESPACE] = ( - 'transaction_data', TransactionData) - - def __init__(self, transaction_data=None, **kwargs): - self.transaction_data = transaction_data - gdata.GDataEntry.__init__(self, **kwargs) - - def transaction_id(self): - return self.id.text.split("/")[-1] - - transaction_id = property(transaction_id, doc='The transaction ID.') - - -def TransactionEntryFromString(xml_string): - return atom.CreateClassFromXMLString(TransactionEntry, xml_string) - - -class TransactionFeed(gdata.GDataFeed): - """A feed that lists all of the transactions that have been recorded for - a particular position. - - A transaction is a collection of information about an instance of - buying or selling a particular security. The TransactionFeed lists all - of the transactions that have been recorded for a particular position - as a list of TransactionEntries. - """ - _tag = 'feed' - _namespace = atom.ATOM_NAMESPACE - _children = gdata.GDataFeed._children.copy() - _children['{%s}entry' % atom.ATOM_NAMESPACE] = ('entry', [TransactionEntry]) - - -def TransactionFeedFromString(xml_string): - return atom.CreateClassFromXMLString(TransactionFeed, xml_string) - - -class TransactionFeedLink(atom.AtomBase): - """Link to TransactionFeed embedded in PositionEntry. - - If a PositionFeed is queried with transactions='true', TransactionFeeds - are inlined in the returned PositionEntries. These TransactionFeeds are - accessible via TransactionFeedLink's feed attribute. - """ - _tag = 'feedLink' - _namespace = GD_NAMESPACE - _attributes = atom.AtomBase._attributes.copy() - _attributes['href'] = 'href' - _children = atom.AtomBase._children.copy() - _children['{%s}feed' % atom.ATOM_NAMESPACE] = ( - 'feed', TransactionFeed) - - def __init__(self, href=None, feed=None, **kwargs): - self.href = href - self.feed = feed - atom.AtomBase.__init__(self, **kwargs) - - -class PositionData(atom.AtomBase): - """The element.""" - _tag = 'positionData' - _namespace = GF_NAMESPACE - _attributes = atom.AtomBase._attributes.copy() - _attributes['gainPercentage'] = 'gain_percentage' - _attributes['return1w'] = 'return1w' - _attributes['return4w'] = 'return4w' - _attributes['return3m'] = 'return3m' - _attributes['returnYTD'] = 'returnYTD' - _attributes['return1y'] = 'return1y' - _attributes['return3y'] = 'return3y' - _attributes['return5y'] = 'return5y' - _attributes['returnOverall'] = 'return_overall' - _attributes['shares'] = 'shares' - _children = atom.AtomBase._children.copy() - _children['{%s}costBasis' % GF_NAMESPACE] = ('cost_basis', CostBasis) - _children['{%s}daysGain' % GF_NAMESPACE] = ('days_gain', DaysGain) - _children['{%s}gain' % GF_NAMESPACE] = ('gain', Gain) - _children['{%s}marketValue' % GF_NAMESPACE] = ('market_value', MarketValue) - - def __init__(self, gain_percentage=None, - return1w=None, return4w=None, return3m=None, returnYTD=None, - return1y=None, return3y=None, return5y=None, return_overall=None, - shares=None, cost_basis=None, days_gain=None, - gain=None, market_value=None, **kwargs): - self.gain_percentage = gain_percentage - self.return1w = return1w - self.return4w = return4w - self.return3m = return3m - self.returnYTD = returnYTD - self.return1y = return1y - self.return3y = return3y - self.return5y = return5y - self.return_overall = return_overall - self.shares = shares - self.cost_basis = cost_basis - self.days_gain = days_gain - self.gain = gain - self.market_value = market_value - atom.AtomBase.__init__(self, **kwargs) - - -def PositionDataFromString(xml_string): - return atom.CreateClassFromXMLString(PositionData, xml_string) - - -class PositionEntry(gdata.GDataEntry): - """An entry of the position feed. - - A PositionEntry contains the ticker exchange and Symbol for a stock, - mutual fund, or other security, along with PositionData such as the - number of units of that security that the user holds, and performance - statistics. - """ - _tag = 'entry' - _namespace = atom.ATOM_NAMESPACE - _children = gdata.GDataEntry._children.copy() - _children['{%s}positionData' % GF_NAMESPACE] = ( - 'position_data', PositionData) - _children['{%s}symbol' % GF_NAMESPACE] = ('symbol', Symbol) - _children['{%s}feedLink' % GD_NAMESPACE] = ( - 'feed_link', TransactionFeedLink) - - def __init__(self, position_data=None, symbol=None, feed_link=None, - **kwargs): - self.position_data = position_data - self.symbol = symbol - self.feed_link = feed_link - gdata.GDataEntry.__init__(self, **kwargs) - - def position_title(self): - return self.title.text - - position_title = property(position_title, - doc='The position title as a string (i.e. position.title.text).') - - def ticker_id(self): - return self.id.text.split("/")[-1] - - ticker_id = property(ticker_id, doc='The position TICKER ID.') - - def transactions(self): - if self.feed_link.feed: - return self.feed_link.feed.entry - else: - return None - - transactions = property(transactions, doc=""" - Inlined TransactionEntries are returned if PositionFeed is queried - with transactions='true'.""") - - -def PositionEntryFromString(xml_string): - return atom.CreateClassFromXMLString(PositionEntry, xml_string) - - -class PositionFeed(gdata.GDataFeed): - """A feed that lists all of the positions in a particular portfolio. - - A position is a collection of information about a security that the - user holds. The PositionFeed lists all of the positions in a particular - portfolio as a list of PositionEntries. - """ - _tag = 'feed' - _namespace = atom.ATOM_NAMESPACE - _children = gdata.GDataFeed._children.copy() - _children['{%s}entry' % atom.ATOM_NAMESPACE] = ('entry', [PositionEntry]) - - -def PositionFeedFromString(xml_string): - return atom.CreateClassFromXMLString(PositionFeed, xml_string) - - -class PositionFeedLink(atom.AtomBase): - """Link to PositionFeed embedded in PortfolioEntry. - - If a PortfolioFeed is queried with positions='true', the PositionFeeds - are inlined in the returned PortfolioEntries. These PositionFeeds are - accessible via PositionFeedLink's feed attribute. - """ - _tag = 'feedLink' - _namespace = GD_NAMESPACE - _attributes = atom.AtomBase._attributes.copy() - _attributes['href'] = 'href' - _children = atom.AtomBase._children.copy() - _children['{%s}feed' % atom.ATOM_NAMESPACE] = ( - 'feed', PositionFeed) - - def __init__(self, href=None, feed=None, **kwargs): - self.href = href - self.feed = feed - atom.AtomBase.__init__(self, **kwargs) - - -class PortfolioData(atom.AtomBase): - """The element.""" - _tag = 'portfolioData' - _namespace = GF_NAMESPACE - _attributes = atom.AtomBase._attributes.copy() - _attributes['currencyCode'] = 'currency_code' - _attributes['gainPercentage'] = 'gain_percentage' - _attributes['return1w'] = 'return1w' - _attributes['return4w'] = 'return4w' - _attributes['return3m'] = 'return3m' - _attributes['returnYTD'] = 'returnYTD' - _attributes['return1y'] = 'return1y' - _attributes['return3y'] = 'return3y' - _attributes['return5y'] = 'return5y' - _attributes['returnOverall'] = 'return_overall' - _children = atom.AtomBase._children.copy() - _children['{%s}costBasis' % GF_NAMESPACE] = ('cost_basis', CostBasis) - _children['{%s}daysGain' % GF_NAMESPACE] = ('days_gain', DaysGain) - _children['{%s}gain' % GF_NAMESPACE] = ('gain', Gain) - _children['{%s}marketValue' % GF_NAMESPACE] = ('market_value', MarketValue) - - def __init__(self, currency_code=None, gain_percentage=None, - return1w=None, return4w=None, return3m=None, returnYTD=None, - return1y=None, return3y=None, return5y=None, return_overall=None, - cost_basis=None, days_gain=None, gain=None, market_value=None, **kwargs): - self.currency_code = currency_code - self.gain_percentage = gain_percentage - self.return1w = return1w - self.return4w = return4w - self.return3m = return3m - self.returnYTD = returnYTD - self.return1y = return1y - self.return3y = return3y - self.return5y = return5y - self.return_overall = return_overall - self.cost_basis = cost_basis - self.days_gain = days_gain - self.gain = gain - self.market_value = market_value - atom.AtomBase.__init__(self, **kwargs) - - -def PortfolioDataFromString(xml_string): - return atom.CreateClassFromXMLString(PortfolioData, xml_string) - - -class PortfolioEntry(gdata.GDataEntry): - """An entry of the PortfolioFeed. - - A PortfolioEntry contains the portfolio's title along with PortfolioData - such as currency, total market value, and overall performance statistics. - """ - _tag = 'entry' - _namespace = atom.ATOM_NAMESPACE - _children = gdata.GDataEntry._children.copy() - _children['{%s}portfolioData' % GF_NAMESPACE] = ( - 'portfolio_data', PortfolioData) - _children['{%s}feedLink' % GD_NAMESPACE] = ( - 'feed_link', PositionFeedLink) - - def __init__(self, portfolio_data=None, feed_link=None, **kwargs): - self.portfolio_data = portfolio_data - self.feed_link = feed_link - gdata.GDataEntry.__init__(self, **kwargs) - - def portfolio_title(self): - return self.title.text - - def set_portfolio_title(self, portfolio_title): - self.title = atom.Title(text=portfolio_title, title_type='text') - - portfolio_title = property(portfolio_title, set_portfolio_title, - doc='The portfolio title as a string (i.e. portfolio.title.text).') - - def portfolio_id(self): - return self.id.text.split("/")[-1] - - portfolio_id = property(portfolio_id, - doc='The portfolio ID. Do not confuse with portfolio.id.') - - def positions(self): - if self.feed_link.feed: - return self.feed_link.feed.entry - else: - return None - - positions = property(positions, doc=""" - Inlined PositionEntries are returned if PortfolioFeed was queried - with positions='true'.""") - - -def PortfolioEntryFromString(xml_string): - return atom.CreateClassFromXMLString(PortfolioEntry, xml_string) - - -class PortfolioFeed(gdata.GDataFeed): - """A feed that lists all of the user's portfolios. - - A portfolio is a collection of positions that the user holds in various - securities, plus metadata. The PortfolioFeed lists all of the user's - portfolios as a list of PortfolioEntries. - """ - _tag = 'feed' - _namespace = atom.ATOM_NAMESPACE - _children = gdata.GDataFeed._children.copy() - _children['{%s}entry' % atom.ATOM_NAMESPACE] = ('entry', [PortfolioEntry]) - - -def PortfolioFeedFromString(xml_string): - return atom.CreateClassFromXMLString(PortfolioFeed, xml_string) - - diff --git a/gdata/finance/data.py b/gdata/finance/data.py deleted file mode 100644 index 5e0caa8920..0000000000 --- a/gdata/finance/data.py +++ /dev/null @@ -1,156 +0,0 @@ -#!/usr/bin/python -# -# Copyright (C) 2009 Google 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. - - -"""Contains the data classes of the Google Finance Portfolio Data API""" - - -__author__ = 'j.s@google.com (Jeff Scudder)' - - -import atom.core -import atom.data -import gdata.data -import gdata.opensearch.data - - -GF_TEMPLATE = '{http://schemas.google.com/finance/2007/}%s' - - -class Commission(atom.core.XmlElement): - """Commission for the transaction""" - _qname = GF_TEMPLATE % 'commission' - money = [gdata.data.Money] - - -class CostBasis(atom.core.XmlElement): - """Cost basis for the portfolio or position""" - _qname = GF_TEMPLATE % 'costBasis' - money = [gdata.data.Money] - - -class DaysGain(atom.core.XmlElement): - """Today's gain for the portfolio or position""" - _qname = GF_TEMPLATE % 'daysGain' - money = [gdata.data.Money] - - -class Gain(atom.core.XmlElement): - """Total gain for the portfolio or position""" - _qname = GF_TEMPLATE % 'gain' - money = [gdata.data.Money] - - -class MarketValue(atom.core.XmlElement): - """Market value for the portfolio or position""" - _qname = GF_TEMPLATE % 'marketValue' - money = [gdata.data.Money] - - -class PortfolioData(atom.core.XmlElement): - """Data for the portfolio""" - _qname = GF_TEMPLATE % 'portfolioData' - return_overall = 'returnOverall' - currency_code = 'currencyCode' - return3y = 'return3y' - return4w = 'return4w' - market_value = MarketValue - return_y_t_d = 'returnYTD' - cost_basis = CostBasis - gain_percentage = 'gainPercentage' - days_gain = DaysGain - return3m = 'return3m' - return5y = 'return5y' - return1w = 'return1w' - gain = Gain - return1y = 'return1y' - - -class PortfolioEntry(gdata.data.GDEntry): - """Describes an entry in a feed of Finance portfolios""" - portfolio_data = PortfolioData - - -class PortfolioFeed(gdata.data.GDFeed): - """Describes a Finance portfolio feed""" - entry = [PortfolioEntry] - - -class PositionData(atom.core.XmlElement): - """Data for the position""" - _qname = GF_TEMPLATE % 'positionData' - return_y_t_d = 'returnYTD' - return5y = 'return5y' - return_overall = 'returnOverall' - cost_basis = CostBasis - return3y = 'return3y' - return1y = 'return1y' - return4w = 'return4w' - shares = 'shares' - days_gain = DaysGain - gain_percentage = 'gainPercentage' - market_value = MarketValue - gain = Gain - return3m = 'return3m' - return1w = 'return1w' - - -class Price(atom.core.XmlElement): - """Price of the transaction""" - _qname = GF_TEMPLATE % 'price' - money = [gdata.data.Money] - - -class Symbol(atom.core.XmlElement): - """Stock symbol for the company""" - _qname = GF_TEMPLATE % 'symbol' - symbol = 'symbol' - exchange = 'exchange' - full_name = 'fullName' - - -class PositionEntry(gdata.data.GDEntry): - """Describes an entry in a feed of Finance positions""" - symbol = Symbol - position_data = PositionData - - -class PositionFeed(gdata.data.GDFeed): - """Describes a Finance position feed""" - entry = [PositionEntry] - - -class TransactionData(atom.core.XmlElement): - """Data for the transction""" - _qname = GF_TEMPLATE % 'transactionData' - shares = 'shares' - notes = 'notes' - date = 'date' - type = 'type' - commission = Commission - price = Price - - -class TransactionEntry(gdata.data.GDEntry): - """Describes an entry in a feed of Finance transactions""" - transaction_data = TransactionData - - -class TransactionFeed(gdata.data.GDFeed): - """Describes a Finance transaction feed""" - entry = [TransactionEntry] - - diff --git a/gdata/finance/service.py b/gdata/finance/service.py deleted file mode 100644 index 6e3eb86d47..0000000000 --- a/gdata/finance/service.py +++ /dev/null @@ -1,243 +0,0 @@ -#!/usr/bin/python -# -# Copyright (C) 2009 Tan Swee Heng -# -# 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. - - -"""Classes to interact with the Google Finance server.""" - - -__author__ = 'thesweeheng@gmail.com' - - -import gdata.service -import gdata.finance -import atom - - -class PortfolioQuery(gdata.service.Query): - """A query object for the list of a user's portfolios.""" - - def returns(self): - return self.get('returns', False) - - def set_returns(self, value): - if value is 'true' or value is True: - self['returns'] = 'true' - - returns = property(returns, set_returns, doc="The returns query parameter") - - def positions(self): - return self.get('positions', False) - - def set_positions(self, value): - if value is 'true' or value is True: - self['positions'] = 'true' - - positions = property(positions, set_positions, - doc="The positions query parameter") - - -class PositionQuery(gdata.service.Query): - """A query object for the list of a user's positions in a portfolio.""" - - def returns(self): - return self.get('returns', False) - - def set_returns(self, value): - if value is 'true' or value is True: - self['returns'] = 'true' - - returns = property(returns, set_returns, - doc="The returns query parameter") - - def transactions(self): - return self.get('transactions', False) - - def set_transactions(self, value): - if value is 'true' or value is True: - self['transactions'] = 'true' - - transactions = property(transactions, set_transactions, - doc="The transactions query parameter") - - -class FinanceService(gdata.service.GDataService): - - def __init__(self, email=None, password=None, source=None, - server='finance.google.com', **kwargs): - """Creates a client for the Finance 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: 'finance.google.com'. - **kwargs: The other parameters to pass to gdata.service.GDataService - constructor. - """ - gdata.service.GDataService.__init__(self, - email=email, password=password, service='finance', server=server, - **kwargs) - - def GetPortfolioFeed(self, query=None): - uri = '/finance/feeds/default/portfolios' - if query: - uri = PortfolioQuery(feed=uri, params=query).ToUri() - return self.Get(uri, converter=gdata.finance.PortfolioFeedFromString) - - def GetPositionFeed(self, portfolio_entry=None, portfolio_id=None, - query=None): - """ - Args: - portfolio_entry: PortfolioEntry (optional; see Notes) - portfolio_id: string (optional; see Notes) This may be obtained - from a PortfolioEntry's portfolio_id attribute. - query: PortfolioQuery (optional) - - Notes: - Either a PortfolioEntry OR a portfolio ID must be provided. - """ - if portfolio_entry: - uri = portfolio_entry.GetSelfLink().href + '/positions' - elif portfolio_id: - uri = '/finance/feeds/default/portfolios/%s/positions' % portfolio_id - if query: - uri = PositionQuery(feed=uri, params=query).ToUri() - return self.Get(uri, converter=gdata.finance.PositionFeedFromString) - - def GetTransactionFeed(self, position_entry=None, - portfolio_id=None, ticker_id=None): - """ - Args: - position_entry: PositionEntry (optional; see Notes) - portfolio_id: string (optional; see Notes) This may be obtained - from a PortfolioEntry's portfolio_id attribute. - ticker_id: string (optional; see Notes) This may be obtained from - a PositionEntry's ticker_id attribute. Alternatively it can - be constructed using the security's exchange and symbol, - e.g. 'NASDAQ:GOOG' - - Notes: - Either a PositionEntry OR (a portfolio ID AND ticker ID) must - be provided. - """ - if position_entry: - uri = position_entry.GetSelfLink().href + '/transactions' - elif portfolio_id and ticker_id: - uri = '/finance/feeds/default/portfolios/%s/positions/%s/transactions' \ - % (portfolio_id, ticker_id) - return self.Get(uri, converter=gdata.finance.TransactionFeedFromString) - - def GetPortfolio(self, portfolio_id=None, query=None): - uri = '/finance/feeds/default/portfolios/%s' % portfolio_id - if query: - uri = PortfolioQuery(feed=uri, params=query).ToUri() - return self.Get(uri, converter=gdata.finance.PortfolioEntryFromString) - - def AddPortfolio(self, portfolio_entry=None): - uri = '/finance/feeds/default/portfolios' - return self.Post(portfolio_entry, uri, - converter=gdata.finance.PortfolioEntryFromString) - - def UpdatePortfolio(self, portfolio_entry=None): - uri = portfolio_entry.GetEditLink().href - return self.Put(portfolio_entry, uri, - converter=gdata.finance.PortfolioEntryFromString) - - def DeletePortfolio(self, portfolio_entry=None): - uri = portfolio_entry.GetEditLink().href - return self.Delete(uri) - - def GetPosition(self, portfolio_id=None, ticker_id=None, query=None): - uri = '/finance/feeds/default/portfolios/%s/positions/%s' \ - % (portfolio_id, ticker_id) - if query: - uri = PositionQuery(feed=uri, params=query).ToUri() - return self.Get(uri, converter=gdata.finance.PositionEntryFromString) - - def DeletePosition(self, position_entry=None, - portfolio_id=None, ticker_id=None, transaction_feed=None): - """A position is deleted by deleting all its transactions. - - Args: - position_entry: PositionEntry (optional; see Notes) - portfolio_id: string (optional; see Notes) This may be obtained - from a PortfolioEntry's portfolio_id attribute. - ticker_id: string (optional; see Notes) This may be obtained from - a PositionEntry's ticker_id attribute. Alternatively it can - be constructed using the security's exchange and symbol, - e.g. 'NASDAQ:GOOG' - transaction_feed: TransactionFeed (optional; see Notes) - - Notes: - Either a PositionEntry OR (a portfolio ID AND ticker ID) OR - a TransactionFeed must be provided. - """ - if transaction_feed: - feed = transaction_feed - else: - if position_entry: - feed = self.GetTransactionFeed(position_entry=position_entry) - elif portfolio_id and ticker_id: - feed = self.GetTransactionFeed( - portfolio_id=portfolio_id, ticker_id=ticker_id) - for txn in feed.entry: - self.DeleteTransaction(txn) - return True - - def GetTransaction(self, portfolio_id=None, ticker_id=None, - transaction_id=None): - uri = '/finance/feeds/default/portfolios/%s/positions/%s/transactions/%s' \ - % (portfolio_id, ticker_id, transaction_id) - return self.Get(uri, converter=gdata.finance.TransactionEntryFromString) - - def AddTransaction(self, transaction_entry=None, transaction_feed = None, - position_entry=None, portfolio_id=None, ticker_id=None): - """ - Args: - transaction_entry: TransactionEntry (required) - transaction_feed: TransactionFeed (optional; see Notes) - position_entry: PositionEntry (optional; see Notes) - portfolio_id: string (optional; see Notes) This may be obtained - from a PortfolioEntry's portfolio_id attribute. - ticker_id: string (optional; see Notes) This may be obtained from - a PositionEntry's ticker_id attribute. Alternatively it can - be constructed using the security's exchange and symbol, - e.g. 'NASDAQ:GOOG' - - Notes: - Either a TransactionFeed OR a PositionEntry OR (a portfolio ID AND - ticker ID) must be provided. - """ - if transaction_feed: - uri = transaction_feed.GetPostLink().href - elif position_entry: - uri = position_entry.GetSelfLink().href + '/transactions' - elif portfolio_id and ticker_id: - uri = '/finance/feeds/default/portfolios/%s/positions/%s/transactions' \ - % (portfolio_id, ticker_id) - return self.Post(transaction_entry, uri, - converter=gdata.finance.TransactionEntryFromString) - - def UpdateTransaction(self, transaction_entry=None): - uri = transaction_entry.GetEditLink().href - return self.Put(transaction_entry, uri, - converter=gdata.finance.TransactionEntryFromString) - - def DeleteTransaction(self, transaction_entry=None): - uri = transaction_entry.GetEditLink().href - return self.Delete(uri) diff --git a/gdata/geo/__init__.py b/gdata/geo/__init__.py deleted file mode 100644 index 1fcf604c2c..0000000000 --- a/gdata/geo/__init__.py +++ /dev/null @@ -1,185 +0,0 @@ -# -*-*- encoding: utf-8 -*-*- -# -# This is gdata.photos.geo, implementing geological positioning in gdata structures -# -# $Id: __init__.py 81 2007-10-03 14:41:42Z havard.gulldahl $ -# -# Copyright 2007 HÃ¥vard Gulldahl -# Portions copyright 2007 Google 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. - -"""Picasa Web Albums uses the georss and gml namespaces for -elements defined in the GeoRSS and Geography Markup Language specifications. - -Specifically, Picasa Web Albums uses the following elements: - -georss:where -gml:Point -gml:pos - -http://code.google.com/apis/picasaweb/reference.html#georss_reference - - -Picasa Web Albums also accepts geographic-location data in two other formats: -W3C format and plain-GeoRSS (without GML) format. -""" -# -#Over the wire, the Picasa Web Albums only accepts and sends the -#elements mentioned above, but this module will let you seamlessly convert -#between the different formats (TODO 2007-10-18 hg) - -__author__ = u'havard@gulldahl.no'# (HÃ¥vard Gulldahl)' #BUG: api chokes on non-ascii chars in __author__ -__license__ = 'Apache License v2' - - -import atom -import gdata - -GEO_NAMESPACE = 'http://www.w3.org/2003/01/geo/wgs84_pos#' -GML_NAMESPACE = 'http://www.opengis.net/gml' -GEORSS_NAMESPACE = 'http://www.georss.org/georss' - -class GeoBaseElement(atom.AtomBase): - """Base class for elements. - - To add new elements, you only need to add the element tag name to self._tag - and the namespace to self._namespace - """ - - _tag = '' - _namespace = GML_NAMESPACE - _children = atom.AtomBase._children.copy() - _attributes = atom.AtomBase._attributes.copy() - - def __init__(self, name=None, extension_elements=None, - extension_attributes=None, text=None): - self.name = name - self.text = text - self.extension_elements = extension_elements or [] - self.extension_attributes = extension_attributes or {} - -class Pos(GeoBaseElement): - """(string) Specifies a latitude and longitude, separated by a space, - e.g. `35.669998 139.770004'""" - - _tag = 'pos' -def PosFromString(xml_string): - return atom.CreateClassFromXMLString(Pos, xml_string) - -class Point(GeoBaseElement): - """(container) Specifies a particular geographical point, by means of - a element.""" - - _tag = 'Point' - _children = atom.AtomBase._children.copy() - _children['{%s}pos' % GML_NAMESPACE] = ('pos', Pos) - def __init__(self, pos=None, extension_elements=None, extension_attributes=None, text=None): - GeoBaseElement.__init__(self, extension_elements=extension_elements, - extension_attributes=extension_attributes, - text=text) - if pos is None: - pos = Pos() - self.pos=pos -def PointFromString(xml_string): - return atom.CreateClassFromXMLString(Point, xml_string) - -class Where(GeoBaseElement): - """(container) Specifies a geographical location or region. - A container element, containing a single element. - (Not to be confused with .) - - Note that the (only) child attribute, .Point, is title-cased. - This reflects the names of elements in the xml stream - (principle of least surprise). - - As a convenience, you can get a tuple of (lat, lon) with Where.location(), - and set the same data with Where.setLocation( (lat, lon) ). - - Similarly, there are methods to set and get only latitude and longitude. - """ - - _tag = 'where' - _namespace = GEORSS_NAMESPACE - _children = atom.AtomBase._children.copy() - _children['{%s}Point' % GML_NAMESPACE] = ('Point', Point) - def __init__(self, point=None, extension_elements=None, extension_attributes=None, text=None): - GeoBaseElement.__init__(self, extension_elements=extension_elements, - extension_attributes=extension_attributes, - text=text) - if point is None: - point = Point() - self.Point=point - def location(self): - "(float, float) Return Where.Point.pos.text as a (lat,lon) tuple" - try: - return tuple([float(z) for z in self.Point.pos.text.split(' ')]) - except AttributeError: - return tuple() - def set_location(self, latlon): - """(bool) Set Where.Point.pos.text from a (lat,lon) tuple. - - Arguments: - lat (float): The latitude in degrees, from -90.0 to 90.0 - lon (float): The longitude in degrees, from -180.0 to 180.0 - - Returns True on success. - - """ - - assert(isinstance(latlon[0], float)) - assert(isinstance(latlon[1], float)) - try: - self.Point.pos.text = "%s %s" % (latlon[0], latlon[1]) - return True - except AttributeError: - return False - def latitude(self): - "(float) Get the latitude value of the geo-tag. See also .location()" - lat, lon = self.location() - return lat - - def longitude(self): - "(float) Get the longtitude value of the geo-tag. See also .location()" - lat, lon = self.location() - return lon - - longtitude = longitude - - def set_latitude(self, lat): - """(bool) Set the latitude value of the geo-tag. - - Args: - lat (float): The new latitude value - - See also .set_location() - """ - _lat, lon = self.location() - return self.set_location(lat, lon) - - def set_longitude(self, lon): - """(bool) Set the longtitude value of the geo-tag. - - Args: - lat (float): The new latitude value - - See also .set_location() - """ - lat, _lon = self.location() - return self.set_location(lat, lon) - - set_longtitude = set_longitude - -def WhereFromString(xml_string): - return atom.CreateClassFromXMLString(Where, xml_string) - diff --git a/gdata/geo/data.py b/gdata/geo/data.py deleted file mode 100644 index 2aec9112bb..0000000000 --- a/gdata/geo/data.py +++ /dev/null @@ -1,92 +0,0 @@ -#!/usr/bin/python -# -# Copyright (C) 2009 Google 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. - -"""Contains the data classes of the Geography Extension""" - - -__author__ = 'j.s@google.com (Jeff Scudder)' - - -import atom.core - - -GEORSS_TEMPLATE = '{http://www.georss.org/georss/}%s' -GML_TEMPLATE = '{http://www.opengis.net/gml/}%s' -GEO_TEMPLATE = '{http://www.w3.org/2003/01/geo/wgs84_pos#/}%s' - - -class GeoLat(atom.core.XmlElement): - """Describes a W3C latitude.""" - _qname = GEO_TEMPLATE % 'lat' - - -class GeoLong(atom.core.XmlElement): - """Describes a W3C longitude.""" - _qname = GEO_TEMPLATE % 'long' - - -class GeoRssBox(atom.core.XmlElement): - """Describes a geographical region.""" - _qname = GEORSS_TEMPLATE % 'box' - - -class GeoRssPoint(atom.core.XmlElement): - """Describes a geographical location.""" - _qname = GEORSS_TEMPLATE % 'point' - - -class GmlLowerCorner(atom.core.XmlElement): - """Describes a lower corner of a region.""" - _qname = GML_TEMPLATE % 'lowerCorner' - - -class GmlPos(atom.core.XmlElement): - """Describes a latitude and longitude.""" - _qname = GML_TEMPLATE % 'pos' - - -class GmlPoint(atom.core.XmlElement): - """Describes a particular geographical point.""" - _qname = GML_TEMPLATE % 'Point' - pos = GmlPos - - -class GmlUpperCorner(atom.core.XmlElement): - """Describes an upper corner of a region.""" - _qname = GML_TEMPLATE % 'upperCorner' - - -class GmlEnvelope(atom.core.XmlElement): - """Describes a Gml geographical region.""" - _qname = GML_TEMPLATE % 'Envelope' - lower_corner = GmlLowerCorner - upper_corner = GmlUpperCorner - - -class GeoRssWhere(atom.core.XmlElement): - """Describes a geographical location or region.""" - _qname = GEORSS_TEMPLATE % 'where' - Point = GmlPoint - Envelope = GmlEnvelope - - -class W3CPoint(atom.core.XmlElement): - """Describes a W3C geographical location.""" - _qname = GEO_TEMPLATE % 'Point' - long = GeoLong - lat = GeoLat - - diff --git a/gdata/health/__init__.py b/gdata/health/__init__.py deleted file mode 100644 index 1904ecdea6..0000000000 --- a/gdata/health/__init__.py +++ /dev/null @@ -1,229 +0,0 @@ -#!/usr/bin/python -# -# Copyright 2009 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. - -"""Contains extensions to Atom objects used with Google Health.""" - -__author__ = 'api.eric@google.com (Eric Bidelman)' - -import atom -import gdata - - -CCR_NAMESPACE = 'urn:astm-org:CCR' -METADATA_NAMESPACE = 'http://schemas.google.com/health/metadata' - - -class Ccr(atom.AtomBase): - """Represents a Google Health .""" - - _tag = 'ContinuityOfCareRecord' - _namespace = CCR_NAMESPACE - _children = atom.AtomBase._children.copy() - - def __init__(self, extension_elements=None, - extension_attributes=None, text=None): - atom.AtomBase.__init__(self, extension_elements=extension_elements, - extension_attributes=extension_attributes, text=text) - - def GetAlerts(self): - """Helper for extracting Alert/Allergy data from the CCR. - - Returns: - A list of ExtensionElements (one for each allergy found) or None if - no allergies where found in this CCR. - """ - try: - body = self.FindExtensions('Body')[0] - return body.FindChildren('Alerts')[0].FindChildren('Alert') - except: - return None - - def GetAllergies(self): - """Alias for GetAlerts().""" - return self.GetAlerts() - - def GetProblems(self): - """Helper for extracting Problem/Condition data from the CCR. - - Returns: - A list of ExtensionElements (one for each problem found) or None if - no problems where found in this CCR. - """ - try: - body = self.FindExtensions('Body')[0] - return body.FindChildren('Problems')[0].FindChildren('Problem') - except: - return None - - def GetConditions(self): - """Alias for GetProblems().""" - return self.GetProblems() - - def GetProcedures(self): - """Helper for extracting Procedure data from the CCR. - - Returns: - A list of ExtensionElements (one for each procedure found) or None if - no procedures where found in this CCR. - """ - try: - body = self.FindExtensions('Body')[0] - return body.FindChildren('Procedures')[0].FindChildren('Procedure') - except: - return None - - def GetImmunizations(self): - """Helper for extracting Immunization data from the CCR. - - Returns: - A list of ExtensionElements (one for each immunization found) or None if - no immunizations where found in this CCR. - """ - try: - body = self.FindExtensions('Body')[0] - return body.FindChildren('Immunizations')[0].FindChildren('Immunization') - except: - return None - - def GetMedications(self): - """Helper for extracting Medication data from the CCR. - - Returns: - A list of ExtensionElements (one for each medication found) or None if - no medications where found in this CCR. - """ - try: - body = self.FindExtensions('Body')[0] - return body.FindChildren('Medications')[0].FindChildren('Medication') - except: - return None - - def GetResults(self): - """Helper for extracting Results/Labresults data from the CCR. - - Returns: - A list of ExtensionElements (one for each result found) or None if - no results where found in this CCR. - """ - try: - body = self.FindExtensions('Body')[0] - return body.FindChildren('Results')[0].FindChildren('Result') - except: - return None - - -class ProfileEntry(gdata.GDataEntry): - """The Google Health version of an Atom Entry.""" - - _tag = gdata.GDataEntry._tag - _namespace = atom.ATOM_NAMESPACE - _children = gdata.GDataEntry._children.copy() - _attributes = gdata.GDataEntry._attributes.copy() - _children['{%s}ContinuityOfCareRecord' % CCR_NAMESPACE] = ('ccr', Ccr) - - def __init__(self, ccr=None, author=None, category=None, content=None, - atom_id=None, link=None, published=None, title=None, - updated=None, text=None, extension_elements=None, - extension_attributes=None): - self.ccr = ccr - gdata.GDataEntry.__init__( - self, author=author, category=category, content=content, - atom_id=atom_id, link=link, published=published, title=title, - updated=updated, extension_elements=extension_elements, - extension_attributes=extension_attributes, text=text) - - -class ProfileFeed(gdata.GDataFeed): - """A feed containing a list of Google Health profile entries.""" - - _tag = gdata.GDataFeed._tag - _namespace = atom.ATOM_NAMESPACE - _children = gdata.GDataFeed._children.copy() - _attributes = gdata.GDataFeed._attributes.copy() - _children['{%s}entry' % atom.ATOM_NAMESPACE] = ('entry', [ProfileEntry]) - - -class ProfileListEntry(gdata.GDataEntry): - """The Atom Entry in the Google Health profile list feed.""" - - _tag = gdata.GDataEntry._tag - _namespace = atom.ATOM_NAMESPACE - _children = gdata.GDataEntry._children.copy() - _attributes = gdata.GDataEntry._attributes.copy() - - def GetProfileId(self): - return self.content.text - - def GetProfileName(self): - return self.title.text - - -class ProfileListFeed(gdata.GDataFeed): - """A feed containing a list of Google Health profile list entries.""" - - _tag = gdata.GDataFeed._tag - _namespace = atom.ATOM_NAMESPACE - _children = gdata.GDataFeed._children.copy() - _attributes = gdata.GDataFeed._attributes.copy() - _children['{%s}entry' % atom.ATOM_NAMESPACE] = ('entry', [ProfileListEntry]) - - -def ProfileEntryFromString(xml_string): - """Converts an XML string into a ProfileEntry object. - - Args: - xml_string: string The XML describing a Health profile feed entry. - - Returns: - A ProfileEntry object corresponding to the given XML. - """ - return atom.CreateClassFromXMLString(ProfileEntry, xml_string) - - -def ProfileListEntryFromString(xml_string): - """Converts an XML string into a ProfileListEntry object. - - Args: - xml_string: string The XML describing a Health profile list feed entry. - - Returns: - A ProfileListEntry object corresponding to the given XML. - """ - return atom.CreateClassFromXMLString(ProfileListEntry, xml_string) - - -def ProfileFeedFromString(xml_string): - """Converts an XML string into a ProfileFeed object. - - Args: - xml_string: string The XML describing a ProfileFeed feed. - - Returns: - A ProfileFeed object corresponding to the given XML. - """ - return atom.CreateClassFromXMLString(ProfileFeed, xml_string) - - -def ProfileListFeedFromString(xml_string): - """Converts an XML string into a ProfileListFeed object. - - Args: - xml_string: string The XML describing a ProfileListFeed feed. - - Returns: - A ProfileListFeed object corresponding to the given XML. - """ - return atom.CreateClassFromXMLString(ProfileListFeed, xml_string) diff --git a/gdata/health/service.py b/gdata/health/service.py deleted file mode 100644 index 3d38411ebe..0000000000 --- a/gdata/health/service.py +++ /dev/null @@ -1,263 +0,0 @@ -#!/usr/bin/python -# -# Copyright 2009 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. - -"""HealthService extends GDataService to streamline Google Health API access. - - HealthService: Provides methods to interact with the profile, profile list, - and register/notices feeds. Extends GDataService. - - HealthProfileQuery: Queries the Google Health Profile feed. - - HealthProfileListQuery: Queries the Google Health Profile list feed. -""" - -__author__ = 'api.eric@google.com (Eric Bidelman)' - - -import atom -import gdata.health -import gdata.service - - -class HealthService(gdata.service.GDataService): - - """Client extension for the Google Health service Document List feed.""" - - def __init__(self, email=None, password=None, source=None, - use_h9_sandbox=False, server='www.google.com', - additional_headers=None, **kwargs): - """Creates a client for the Google Health 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. - use_h9_sandbox: boolean (optional) True to issue requests against the - /h9 developer's sandbox. - server: string (optional) The name of the server to which a connection - will be opened. - additional_headers: dictionary (optional) Any additional headers which - should be included with CRUD operations. - kwargs: The other parameters to pass to gdata.service.GDataService - constructor. - """ - service = use_h9_sandbox and 'weaver' or 'health' - gdata.service.GDataService.__init__( - self, email=email, password=password, service=service, source=source, - server=server, additional_headers=additional_headers, **kwargs) - self.ssl = True - self.use_h9_sandbox = use_h9_sandbox - - def __get_service(self): - return self.use_h9_sandbox and 'h9' or 'health' - - def GetProfileFeed(self, query=None, profile_id=None): - """Fetches the users Google Health profile feed. - - Args: - query: HealthProfileQuery or string (optional) A query to use on the - profile feed. If None, a HealthProfileQuery is constructed. - profile_id: string (optional) The profile id to query the profile feed - with when using ClientLogin. Note: this parameter is ignored if - query is set. - - Returns: - A gdata.health.ProfileFeed object containing the user's Health profile. - """ - if query is None: - projection = profile_id and 'ui' or 'default' - uri = HealthProfileQuery( - service=self.__get_service(), projection=projection, - profile_id=profile_id).ToUri() - elif isinstance(query, HealthProfileQuery): - uri = query.ToUri() - else: - uri = query - - return self.GetFeed(uri, converter=gdata.health.ProfileFeedFromString) - - def GetProfileListFeed(self, query=None): - """Fetches the users Google Health profile feed. - - Args: - query: HealthProfileListQuery or string (optional) A query to use - on the profile list feed. If None, a HealthProfileListQuery is - constructed to /health/feeds/profile/list or /h9/feeds/profile/list. - - Returns: - A gdata.health.ProfileListFeed object containing the user's list - of profiles. - """ - if not query: - uri = HealthProfileListQuery(service=self.__get_service()).ToUri() - elif isinstance(query, HealthProfileListQuery): - uri = query.ToUri() - else: - uri = query - - return self.GetFeed(uri, converter=gdata.health.ProfileListFeedFromString) - - def SendNotice(self, subject, body=None, content_type='html', - ccr=None, profile_id=None): - """Sends (posts) a notice to the user's Google Health profile. - - Args: - subject: A string representing the message's subject line. - body: string (optional) The message body. - content_type: string (optional) The content type of the notice message - body. This parameter is only honored when a message body is - specified. - ccr: string (optional) The CCR XML document to reconcile into the - user's profile. - profile_id: string (optional) The profile id to work with when using - ClientLogin. Note: this parameter is ignored if query is set. - - Returns: - A gdata.health.ProfileEntry object of the posted entry. - """ - if body: - content = atom.Content(content_type=content_type, text=body) - else: - content = body - - entry = gdata.GDataEntry( - title=atom.Title(text=subject), content=content, - extension_elements=[atom.ExtensionElementFromString(ccr)]) - - projection = profile_id and 'ui' or 'default' - query = HealthRegisterQuery(service=self.__get_service(), - projection=projection, profile_id=profile_id) - return self.Post(entry, query.ToUri(), - converter=gdata.health.ProfileEntryFromString) - - -class HealthProfileQuery(gdata.service.Query): - - """Object used to construct a URI to query the Google Health profile feed.""" - - def __init__(self, service='health', feed='feeds/profile', - projection='default', profile_id=None, text_query=None, - params=None, categories=None): - """Constructor for Health profile feed query. - - Args: - service: string (optional) The service to query. Either 'health' or 'h9'. - feed: string (optional) The path for the feed. The default value is - 'feeds/profile'. - projection: string (optional) The visibility of the data. Possible values - are 'default' for AuthSub and 'ui' for ClientLogin. If this value - is set to 'ui', the profile_id parameter should also be set. - profile_id: string (optional) The profile id to query. This should only - be used when using ClientLogin. - text_query: str (optional) The contents of the q query parameter. The - contents of the text_query are URL escaped upon conversion to a URI. - Note: this parameter can only be used on the register feed using - ClientLogin. - 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. - """ - self.service = service - self.profile_id = profile_id - self.projection = projection - gdata.service.Query.__init__(self, feed=feed, text_query=text_query, - params=params, categories=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 Health - profile feed. - """ - old_feed = self.feed - self.feed = '/'.join([self.service, old_feed, self.projection]) - - if self.profile_id: - self.feed += '/' + self.profile_id - self.feed = '/%s' % (self.feed,) - - new_feed = gdata.service.Query.ToUri(self) - self.feed = old_feed - return new_feed - - -class HealthProfileListQuery(gdata.service.Query): - - """Object used to construct a URI to query a Health profile list feed.""" - - def __init__(self, service='health', feed='feeds/profile/list'): - """Constructor for Health profile list feed query. - - Args: - service: string (optional) The service to query. Either 'health' or 'h9'. - feed: string (optional) The path for the feed. The default value is - 'feeds/profile/list'. - """ - gdata.service.Query.__init__(self, feed) - self.service = service - - 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 - profile list feed. - """ - return '/%s' % ('/'.join([self.service, self.feed]),) - - -class HealthRegisterQuery(gdata.service.Query): - - """Object used to construct a URI to query a Health register/notice feed.""" - - def __init__(self, service='health', feed='feeds/register', - projection='default', profile_id=None): - """Constructor for Health profile list feed query. - - Args: - service: string (optional) The service to query. Either 'health' or 'h9'. - feed: string (optional) The path for the feed. The default value is - 'feeds/register'. - projection: string (optional) The visibility of the data. Possible values - are 'default' for AuthSub and 'ui' for ClientLogin. If this value - is set to 'ui', the profile_id parameter should also be set. - profile_id: string (optional) The profile id to query. This should only - be used when using ClientLogin. - """ - gdata.service.Query.__init__(self, feed) - self.service = service - self.projection = projection - self.profile_id = profile_id - - def ToUri(self): - """Generates a URI from the query parameters set in the object. - - Returns: - A string containing the URI needed to interact with the register feed. - """ - old_feed = self.feed - self.feed = '/'.join([self.service, old_feed, self.projection]) - new_feed = gdata.service.Query.ToUri(self) - self.feed = old_feed - - if self.profile_id: - new_feed += '/' + self.profile_id - return '/%s' % (new_feed,) diff --git a/gdata/maps/__init__.py b/gdata/maps/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/gdata/maps/client.py b/gdata/maps/client.py deleted file mode 100644 index 7c7d7e91ae..0000000000 --- a/gdata/maps/client.py +++ /dev/null @@ -1,179 +0,0 @@ -#!/usr/bin/env python -# -# Copyright (C) 2009 Google 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. - - -"""Contains a client to communicate with the Maps Data servers. - -For documentation on the Maps Data API, see: -http://code.google.com/apis/maps/documentation/mapsdata/ -""" - - -__author__ = 'api.roman.public@google.com (Roman Nurik)' - - -import gdata.client -import gdata.maps.data -import atom.data -import atom.http_core -import gdata.gauth - - -# List user's maps, takes a user ID, or 'default'. -MAP_URL_TEMPLATE = 'http://maps.google.com/maps/feeds/maps/%s/full' - -# List map's features, takes a user ID (or 'default') and map ID. -MAP_FEATURE_URL_TEMPLATE = ('http://maps.google.com/maps' - '/feeds/features/%s/%s/full') - -# The KML mime type -KML_CONTENT_TYPE = 'application/vnd.google-earth.kml+xml' - - -class MapsClient(gdata.client.GDClient): - """Maps Data API GData client.""" - - api_version = '2' - auth_service = 'local' - auth_scopes = gdata.gauth.AUTH_SCOPES['local'] - - def get_maps(self, user_id='default', auth_token=None, - desired_class=gdata.maps.data.MapFeed, **kwargs): - """Retrieves a Map feed for the given user ID. - - Args: - user_id: An optional string representing the user ID; should be 'default'. - - Returns: - A gdata.maps.data.MapFeed. - """ - return self.get_feed(MAP_URL_TEMPLATE % user_id, auth_token=auth_token, - desired_class=desired_class, **kwargs) - - GetMaps = get_maps - - def get_features(self, map_id, user_id='default', auth_token=None, - desired_class=gdata.maps.data.FeatureFeed, query=None, - **kwargs): - """Retrieves a Feature feed for the given map ID/user ID combination. - - Args: - map_id: A string representing the ID of the map whose features should be - retrieved. - user_id: An optional string representing the user ID; should be 'default'. - - Returns: - A gdata.maps.data.FeatureFeed. - """ - return self.get_feed(MAP_FEATURE_URL_TEMPLATE % (user_id, map_id), - auth_token=auth_token, desired_class=desired_class, - query=query, **kwargs) - - GetFeatures = get_features - - def create_map(self, title, summary=None, unlisted=False, - auth_token=None, title_type='text', summary_type='text', - **kwargs): - """Creates a new map and posts it to the Maps Data servers. - - Args: - title: A string representing the title of the new map. - summary: An optional string representing the new map's description. - unlisted: An optional boolean identifying whether the map should be - unlisted (True) or public (False). Default False. - - Returns: - A gdata.maps.data.Map. - """ - new_entry = gdata.maps.data.Map( - title=atom.data.Title(text=title, type=title_type)) - if summary: - new_entry.summary = atom.data.Summary(text=summary, type=summary_type) - if unlisted: - new_entry.control = atom.data.Control(draft=atom.data.Draft(text='yes')) - return self.post(new_entry, MAP_URL_TEMPLATE % 'default', - auth_token=auth_token, **kwargs) - - CreateMap = create_map - - def add_feature(self, map_id, title, content, - auth_token=None, title_type='text', - content_type=KML_CONTENT_TYPE, **kwargs): - """Adds a new feature to the given map. - - Args: - map_id: A string representing the ID of the map to which the new feature - should be added. - title: A string representing the name/title of the new feature. - content: A KML string or gdata.maps.data.KmlContent object representing - the new feature's KML contents, including its description. - - Returns: - A gdata.maps.data.Feature. - """ - if content_type == KML_CONTENT_TYPE: - if type(content) != gdata.maps.data.KmlContent: - content = gdata.maps.data.KmlContent(kml=content) - else: - content = atom.data.Content(content=content, type=content_type) - new_entry = gdata.maps.data.Feature( - title=atom.data.Title(text=title, type=title_type), - content=content) - return self.post(new_entry, MAP_FEATURE_URL_TEMPLATE % ('default', map_id), - auth_token=auth_token, **kwargs) - - AddFeature = add_feature - - def update(self, entry, auth_token=None, **kwargs): - """Sends changes to a given map or feature entry to the Maps Data servers. - - Args: - entry: A gdata.maps.data.Map or gdata.maps.data.Feature to be updated - server-side. - """ - # The Maps Data API does not currently support ETags, so for now remove - # the ETag before performing an update. - old_etag = entry.etag - entry.etag = None - response = gdata.client.GDClient.update(self, entry, - auth_token=auth_token, **kwargs) - entry.etag = old_etag - return response - - Update = update - - def delete(self, entry_or_uri, auth_token=None, **kwargs): - """Deletes the given entry or entry URI server-side. - - Args: - entry_or_uri: A gdata.maps.data.Map, gdata.maps.data.Feature, or URI - string representing the entry to delete. - """ - if isinstance(entry_or_uri, (str, unicode, atom.http_core.Uri)): - return gdata.client.GDClient.delete(self, entry_or_uri, - auth_token=auth_token, **kwargs) - # The Maps Data API does not currently support ETags, so for now remove - # the ETag before performing a delete. - old_etag = entry_or_uri.etag - entry_or_uri.etag = None - response = gdata.client.GDClient.delete(self, entry_or_uri, - auth_token=auth_token, **kwargs) - # TODO: if GDClient.delete raises and exception, the entry's etag may be - # left as None. Should revisit this logic. - entry_or_uri.etag = old_etag - return response - - Delete = delete diff --git a/gdata/maps/data.py b/gdata/maps/data.py deleted file mode 100644 index 544611e227..0000000000 --- a/gdata/maps/data.py +++ /dev/null @@ -1,125 +0,0 @@ -#!/usr/bin/env python -# -# Copyright (C) 2009 Google 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. - - -"""Data model classes for parsing and generating XML for the Maps Data API.""" - - -__author__ = 'api.roman.public@google.com (Roman Nurik)' - - -import re -import atom.core -import gdata.data - - -MAP_ATOM_ID_PATTERN = re.compile('/maps/feeds/maps/' - '(?P\w+)/' - '(?P\w+)$') - -FEATURE_ATOM_ID_PATTERN = re.compile('/maps/feeds/features/' - '(?P\w+)/' - '(?P\w+)/' - '(?P\w+)$') - -# The KML mime type -KML_CONTENT_TYPE = 'application/vnd.google-earth.kml+xml' - -# The OGC KML 2.2 namespace -KML_NAMESPACE = 'http://www.opengis.net/kml/2.2' - -class MapsDataEntry(gdata.data.GDEntry): - """Adds convenience methods inherited by all Maps Data entries.""" - - def get_user_id(self): - """Extracts the user ID of this entry.""" - if self.id.text: - match = self.__class__.atom_id_pattern.search(self.id.text) - if match: - return match.group('user_id') - return None - - GetUserId = get_user_id - - def get_map_id(self): - """Extracts the map ID of this entry.""" - if self.id.text: - match = self.__class__.atom_id_pattern.search(self.id.text) - if match: - return match.group('map_id') - return None - - GetMapId = get_map_id - - -class Map(MapsDataEntry): - """Represents a map which belongs to the user.""" - atom_id_pattern = MAP_ATOM_ID_PATTERN - - -class MapFeed(gdata.data.GDFeed): - """Represents an atom feed of maps.""" - entry = [Map] - - -class KmlContent(atom.data.Content): - """Represents an atom content element that encapsulates KML content.""" - - def __init__(self, **kwargs): - super(KmlContent, self).__init__(type=KML_CONTENT_TYPE, **kwargs) - if 'kml' in kwargs: - self.kml = kwargs['kml'] - - def _get_kml(self): - if self.children: - return self.children[0] - else: - return '' - - def _set_kml(self, kml): - if not kml: - self.children = [] - return - - if type(kml) == str: - kml = atom.core.parse(kml) - if not kml.namespace: - kml.namespace = KML_NAMESPACE - - self.children = [kml] - - kml = property(_get_kml, _set_kml) - - -class Feature(MapsDataEntry): - """Represents a single feature in a map.""" - atom_id_pattern = FEATURE_ATOM_ID_PATTERN - content = KmlContent - - def get_feature_id(self): - """Extracts the feature ID of this feature.""" - if self.id.text: - match = self.__class__.atom_id_pattern.search(self.id.text) - if match: - return match.group('feature_id') - return None - - GetFeatureId = get_feature_id - - -class FeatureFeed(gdata.data.GDFeed): - """Represents an atom feed of features.""" - entry = [Feature] diff --git a/gdata/media/__init__.py b/gdata/media/__init__.py deleted file mode 100644 index e6af1ae52d..0000000000 --- a/gdata/media/__init__.py +++ /dev/null @@ -1,355 +0,0 @@ -# -*-*- encoding: utf-8 -*-*- -# -# This is gdata.photos.media, implementing parts of the MediaRSS spec in gdata structures -# -# $Id: __init__.py 81 2007-10-03 14:41:42Z havard.gulldahl $ -# -# Copyright 2007 HÃ¥vard Gulldahl -# Portions copyright 2007 Google 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. - - -"""Essential attributes of photos in Google Photos/Picasa Web Albums are -expressed using elements from the `media' namespace, defined in the -MediaRSS specification[1]. - -Due to copyright issues, the elements herein are documented sparingly, please -consult with the Google Photos API Reference Guide[2], alternatively the -official MediaRSS specification[1] for details. -(If there is a version conflict between the two sources, stick to the -Google Photos API). - -[1]: http://search.yahoo.com/mrss (version 1.1.1) -[2]: http://code.google.com/apis/picasaweb/reference.html#media_reference - -Keep in mind that Google Photos only uses a subset of the MediaRSS elements -(and some of the attributes are trimmed down, too): - -media:content -media:credit -media:description -media:group -media:keywords -media:thumbnail -media:title -""" - -__author__ = u'havard@gulldahl.no'# (HÃ¥vard Gulldahl)' #BUG: api chokes on non-ascii chars in __author__ -__license__ = 'Apache License v2' - - -import atom -import gdata - -MEDIA_NAMESPACE = 'http://search.yahoo.com/mrss/' -YOUTUBE_NAMESPACE = 'http://gdata.youtube.com/schemas/2007' - - -class MediaBaseElement(atom.AtomBase): - """Base class for elements in the MEDIA_NAMESPACE. - To add new elements, you only need to add the element tag name to self._tag - """ - - _tag = '' - _namespace = MEDIA_NAMESPACE - _children = atom.AtomBase._children.copy() - _attributes = atom.AtomBase._attributes.copy() - - def __init__(self, name=None, extension_elements=None, - extension_attributes=None, text=None): - self.name = name - self.text = text - self.extension_elements = extension_elements or [] - self.extension_attributes = extension_attributes or {} - - -class Content(MediaBaseElement): - """(attribute container) This element describes the original content, - e.g. an image or a video. There may be multiple Content elements - in a media:Group. - - For example, a video may have a - element that specifies a JPEG - representation of the video, and a - element that specifies the URL of the video itself. - - Attributes: - url: non-ambigous reference to online object - width: width of the object frame, in pixels - height: width of the object frame, in pixels - medium: one of `image' or `video', allowing the api user to quickly - determine the object's type - type: Internet media Type[1] (a.k.a. mime type) of the object -- a more - verbose way of determining the media type. To set the type member - in the contructor, use the content_type parameter. - (optional) fileSize: the size of the object, in bytes - - [1]: http://en.wikipedia.org/wiki/Internet_media_type - """ - - _tag = 'content' - _attributes = atom.AtomBase._attributes.copy() - _attributes['url'] = 'url' - _attributes['width'] = 'width' - _attributes['height'] = 'height' - _attributes['medium'] = 'medium' - _attributes['type'] = 'type' - _attributes['fileSize'] = 'fileSize' - - def __init__(self, url=None, width=None, height=None, - medium=None, content_type=None, fileSize=None, format=None, - extension_elements=None, extension_attributes=None, text=None): - MediaBaseElement.__init__(self, extension_elements=extension_elements, - extension_attributes=extension_attributes, - text=text) - self.url = url - self.width = width - self.height = height - self.medium = medium - self.type = content_type - self.fileSize = fileSize - - -def ContentFromString(xml_string): - return atom.CreateClassFromXMLString(Content, xml_string) - - -class Credit(MediaBaseElement): - """(string) Contains the nickname of the user who created the content, - e.g. `Liz Bennet'. - - This is a user-specified value that should be used when referring to - the user by name. - - Note that none of the attributes from the MediaRSS spec are supported. - """ - - _tag = 'credit' - - -def CreditFromString(xml_string): - return atom.CreateClassFromXMLString(Credit, xml_string) - - -class Description(MediaBaseElement): - """(string) A description of the media object. - Either plain unicode text, or entity-encoded html (look at the `type' - attribute). - - E.g `A set of photographs I took while vacationing in Italy.' - - For `api' projections, the description is in plain text; - for `base' projections, the description is in HTML. - - Attributes: - type: either `text' or `html'. To set the type member in the contructor, - use the description_type parameter. - """ - - _tag = 'description' - _attributes = atom.AtomBase._attributes.copy() - _attributes['type'] = 'type' - def __init__(self, description_type=None, - extension_elements=None, extension_attributes=None, text=None): - MediaBaseElement.__init__(self, extension_elements=extension_elements, - extension_attributes=extension_attributes, - text=text) - - self.type = description_type - - -def DescriptionFromString(xml_string): - return atom.CreateClassFromXMLString(Description, xml_string) - - -class Keywords(MediaBaseElement): - """(string) Lists the tags associated with the entry, - e.g `italy, vacation, sunset'. - - Contains a comma-separated list of tags that have been added to the photo, or - all tags that have been added to photos in the album. - """ - - _tag = 'keywords' - - -def KeywordsFromString(xml_string): - return atom.CreateClassFromXMLString(Keywords, xml_string) - - -class Thumbnail(MediaBaseElement): - """(attributes) Contains the URL of a thumbnail of a photo or album cover. - - There can be multiple elements for a given ; - for example, a given item may have multiple thumbnails at different sizes. - Photos generally have two thumbnails at different sizes; - albums generally have one cropped thumbnail. - - If the thumbsize parameter is set to the initial query, this element points - to thumbnails of the requested sizes; otherwise the thumbnails are the - default thumbnail size. - - This element must not be confused with the element. - - Attributes: - url: The URL of the thumbnail image. - height: The height of the thumbnail image, in pixels. - width: The width of the thumbnail image, in pixels. - """ - - _tag = 'thumbnail' - _attributes = atom.AtomBase._attributes.copy() - _attributes['url'] = 'url' - _attributes['width'] = 'width' - _attributes['height'] = 'height' - def __init__(self, url=None, width=None, height=None, - extension_attributes=None, text=None, extension_elements=None): - MediaBaseElement.__init__(self, extension_elements=extension_elements, - extension_attributes=extension_attributes, - text=text) - self.url = url - self.width = width - self.height = height - - -def ThumbnailFromString(xml_string): - return atom.CreateClassFromXMLString(Thumbnail, xml_string) - - -class Title(MediaBaseElement): - """(string) Contains the title of the entry's media content, in plain text. - - Attributes: - type: Always set to plain. To set the type member in the constructor, use - the title_type parameter. - """ - - _tag = 'title' - _attributes = atom.AtomBase._attributes.copy() - _attributes['type'] = 'type' - def __init__(self, title_type=None, - extension_attributes=None, text=None, extension_elements=None): - MediaBaseElement.__init__(self, extension_elements=extension_elements, - extension_attributes=extension_attributes, - text=text) - self.type = title_type - - -def TitleFromString(xml_string): - return atom.CreateClassFromXMLString(Title, xml_string) - - -class Player(MediaBaseElement): - """(string) Contains the embeddable player URL for the entry's media content - if the media is a video. - - Attributes: - url: Always set to plain - """ - - _tag = 'player' - _attributes = atom.AtomBase._attributes.copy() - _attributes['url'] = 'url' - - def __init__(self, player_url=None, - extension_attributes=None, extension_elements=None): - MediaBaseElement.__init__(self, extension_elements=extension_elements, - extension_attributes=extension_attributes) - self.url= player_url - - -class Private(atom.AtomBase): - """The YouTube Private element""" - _tag = 'private' - _namespace = YOUTUBE_NAMESPACE - - -class Duration(atom.AtomBase): - """The YouTube Duration element""" - _tag = 'duration' - _namespace = YOUTUBE_NAMESPACE - _attributes = atom.AtomBase._attributes.copy() - _attributes['seconds'] = 'seconds' - - -class Category(MediaBaseElement): - """The mediagroup:category element""" - - _tag = 'category' - _attributes = atom.AtomBase._attributes.copy() - _attributes['term'] = 'term' - _attributes['scheme'] = 'scheme' - _attributes['label'] = 'label' - - def __init__(self, term=None, scheme=None, label=None, text=None, - extension_elements=None, extension_attributes=None): - """Constructor for Category - - Args: - term: str - scheme: str - label: str - text: str The text data in the this element - extension_elements: list A list of ExtensionElement instances - extension_attributes: dict A dictionary of attribute value string pairs - """ - - self.term = term - self.scheme = scheme - self.label = label - self.text = text - self.extension_elements = extension_elements or [] - self.extension_attributes = extension_attributes or {} - - -class Group(MediaBaseElement): - """Container element for all media elements. - The element can appear as a child of an album, photo or - video entry.""" - - _tag = 'group' - _children = atom.AtomBase._children.copy() - _children['{%s}content' % MEDIA_NAMESPACE] = ('content', [Content,]) - _children['{%s}credit' % MEDIA_NAMESPACE] = ('credit', Credit) - _children['{%s}description' % MEDIA_NAMESPACE] = ('description', Description) - _children['{%s}keywords' % MEDIA_NAMESPACE] = ('keywords', Keywords) - _children['{%s}thumbnail' % MEDIA_NAMESPACE] = ('thumbnail', [Thumbnail,]) - _children['{%s}title' % MEDIA_NAMESPACE] = ('title', Title) - _children['{%s}category' % MEDIA_NAMESPACE] = ('category', [Category,]) - _children['{%s}duration' % YOUTUBE_NAMESPACE] = ('duration', Duration) - _children['{%s}private' % YOUTUBE_NAMESPACE] = ('private', Private) - _children['{%s}player' % MEDIA_NAMESPACE] = ('player', Player) - - def __init__(self, content=None, credit=None, description=None, keywords=None, - thumbnail=None, title=None, duration=None, private=None, - category=None, player=None, extension_elements=None, - extension_attributes=None, text=None): - - MediaBaseElement.__init__(self, extension_elements=extension_elements, - extension_attributes=extension_attributes, - text=text) - self.content=content - self.credit=credit - self.description=description - self.keywords=keywords - self.thumbnail=thumbnail or [] - self.title=title - self.duration=duration - self.private=private - self.category=category or [] - self.player=player - - -def GroupFromString(xml_string): - return atom.CreateClassFromXMLString(Group, xml_string) diff --git a/gdata/media/data.py b/gdata/media/data.py deleted file mode 100644 index bb5d2c80f8..0000000000 --- a/gdata/media/data.py +++ /dev/null @@ -1,159 +0,0 @@ -#!/usr/bin/python -# -# Copyright (C) 2009 Google 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. - -"""Contains the data classes of the Yahoo! Media RSS Extension""" - - -__author__ = 'j.s@google.com (Jeff Scudder)' - - -import atom.core - - -MEDIA_TEMPLATE = '{http://search.yahoo.com/mrss//}%s' - - -class MediaCategory(atom.core.XmlElement): - """Describes a media category.""" - _qname = MEDIA_TEMPLATE % 'category' - scheme = 'scheme' - label = 'label' - - -class MediaCopyright(atom.core.XmlElement): - """Describes a media copyright.""" - _qname = MEDIA_TEMPLATE % 'copyright' - url = 'url' - - -class MediaCredit(atom.core.XmlElement): - """Describes a media credit.""" - _qname = MEDIA_TEMPLATE % 'credit' - role = 'role' - scheme = 'scheme' - - -class MediaDescription(atom.core.XmlElement): - """Describes a media description.""" - _qname = MEDIA_TEMPLATE % 'description' - type = 'type' - - -class MediaHash(atom.core.XmlElement): - """Describes a media hash.""" - _qname = MEDIA_TEMPLATE % 'hash' - algo = 'algo' - - -class MediaKeywords(atom.core.XmlElement): - """Describes a media keywords.""" - _qname = MEDIA_TEMPLATE % 'keywords' - - -class MediaPlayer(atom.core.XmlElement): - """Describes a media player.""" - _qname = MEDIA_TEMPLATE % 'player' - height = 'height' - width = 'width' - url = 'url' - - -class MediaRating(atom.core.XmlElement): - """Describes a media rating.""" - _qname = MEDIA_TEMPLATE % 'rating' - scheme = 'scheme' - - -class MediaRestriction(atom.core.XmlElement): - """Describes a media restriction.""" - _qname = MEDIA_TEMPLATE % 'restriction' - relationship = 'relationship' - type = 'type' - - -class MediaText(atom.core.XmlElement): - """Describes a media text.""" - _qname = MEDIA_TEMPLATE % 'text' - end = 'end' - lang = 'lang' - type = 'type' - start = 'start' - - -class MediaThumbnail(atom.core.XmlElement): - """Describes a media thumbnail.""" - _qname = MEDIA_TEMPLATE % 'thumbnail' - time = 'time' - url = 'url' - width = 'width' - height = 'height' - - -class MediaTitle(atom.core.XmlElement): - """Describes a media title.""" - _qname = MEDIA_TEMPLATE % 'title' - type = 'type' - - -class MediaContent(atom.core.XmlElement): - """Describes a media content.""" - _qname = MEDIA_TEMPLATE % 'content' - bitrate = 'bitrate' - is_default = 'isDefault' - medium = 'medium' - height = 'height' - credit = [MediaCredit] - language = 'language' - hash = MediaHash - width = 'width' - player = MediaPlayer - url = 'url' - file_size = 'fileSize' - channels = 'channels' - expression = 'expression' - text = [MediaText] - samplingrate = 'samplingrate' - title = MediaTitle - category = [MediaCategory] - rating = [MediaRating] - type = 'type' - description = MediaDescription - framerate = 'framerate' - thumbnail = [MediaThumbnail] - duration = 'duration' - copyright = MediaCopyright - keywords = MediaKeywords - restriction = [MediaRestriction] - - -class MediaGroup(atom.core.XmlElement): - """Describes a media group.""" - _qname = MEDIA_TEMPLATE % 'group' - credit = [MediaCredit] - content = [MediaContent] - copyright = MediaCopyright - description = MediaDescription - category = [MediaCategory] - player = MediaPlayer - rating = [MediaRating] - hash = MediaHash - title = MediaTitle - keywords = MediaKeywords - restriction = [MediaRestriction] - thumbnail = [MediaThumbnail] - text = [MediaText] - - diff --git a/gdata/notebook/__init__.py b/gdata/notebook/__init__.py deleted file mode 100644 index 22071f7a11..0000000000 --- a/gdata/notebook/__init__.py +++ /dev/null @@ -1,15 +0,0 @@ -#!/usr/bin/python -# -# Copyright (C) 2009 Google 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. diff --git a/gdata/notebook/data.py b/gdata/notebook/data.py deleted file mode 100644 index 53405e0181..0000000000 --- a/gdata/notebook/data.py +++ /dev/null @@ -1,55 +0,0 @@ -#!/usr/bin/python -# -# Copyright (C) 2009 Google 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. - -"""Contains the data classes of the Google Notebook Data API""" - - -__author__ = 'j.s@google.com (Jeff Scudder)' - - -import atom.core -import atom.data -import gdata.data -import gdata.opensearch.data - - -NB_TEMPLATE = '{http://schemas.google.com/notes/2008/}%s' - - -class ComesAfter(atom.core.XmlElement): - """Preceding element.""" - _qname = NB_TEMPLATE % 'comesAfter' - id = 'id' - - -class NoteEntry(gdata.data.GDEntry): - """Describes a note entry in the feed of a user's notebook.""" - - -class NotebookFeed(gdata.data.GDFeed): - """Describes a notebook feed.""" - entry = [NoteEntry] - - -class NotebookListEntry(gdata.data.GDEntry): - """Describes a note list entry in the feed of a user's list of public notebooks.""" - - -class NotebookListFeed(gdata.data.GDFeed): - """Describes a notebook list feed.""" - entry = [NotebookListEntry] - - diff --git a/gdata/opensearch/__init__.py b/gdata/opensearch/__init__.py deleted file mode 100644 index 22071f7a11..0000000000 --- a/gdata/opensearch/__init__.py +++ /dev/null @@ -1,15 +0,0 @@ -#!/usr/bin/python -# -# Copyright (C) 2009 Google 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. diff --git a/gdata/opensearch/data.py b/gdata/opensearch/data.py deleted file mode 100644 index 89d7a280f3..0000000000 --- a/gdata/opensearch/data.py +++ /dev/null @@ -1,48 +0,0 @@ -#!/usr/bin/python -# -# Copyright (C) 2009 Google 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. - - -"""Contains the data classes of the OpenSearch Extension""" - - -__author__ = 'j.s@google.com (Jeff Scudder)' - - -import atom.core - - -OPENSEARCH_TEMPLATE_V1 = '{http://a9.com/-/spec/opensearchrss/1.0//}%s' -OPENSEARCH_TEMPLATE_V2 = '{http://a9.com/-/spec/opensearch/1.1//}%s' - - -class ItemsPerPage(atom.core.XmlElement): - """Describes the number of items that will be returned per page for paged feeds""" - _qname = (OPENSEARCH_TEMPLATE_V1 % 'itemsPerPage', - OPENSEARCH_TEMPLATE_V2 % 'itemsPerPage') - - -class StartIndex(atom.core.XmlElement): - """Describes the starting index of the contained entries for paged feeds""" - _qname = (OPENSEARCH_TEMPLATE_V1 % 'startIndex', - OPENSEARCH_TEMPLATE_V2 % 'startIndex') - - -class TotalResults(atom.core.XmlElement): - """Describes the total number of results associated with this feed""" - _qname = (OPENSEARCH_TEMPLATE_V1 % 'totalResults', - OPENSEARCH_TEMPLATE_V2 % 'totalResults') - - diff --git a/gdata/photos/__init__.py b/gdata/photos/__init__.py deleted file mode 100644 index 1952135c46..0000000000 --- a/gdata/photos/__init__.py +++ /dev/null @@ -1,1112 +0,0 @@ -# -*-*- encoding: utf-8 -*-*- -# -# This is the base file for the PicasaWeb python client. -# It is used for lower level operations. -# -# $Id: __init__.py 148 2007-10-28 15:09:19Z havard.gulldahl $ -# -# Copyright 2007 HÃ¥vard Gulldahl -# Portions (C) 2006 Google 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. - -"""This module provides a pythonic, gdata-centric interface to Google Photos -(a.k.a. Picasa Web Services. - -It is modelled after the gdata/* interfaces from the gdata-python-client -project[1] by Google. - -You'll find the user-friendly api in photos.service. Please see the -documentation or live help() system for available methods. - -[1]: http://gdata-python-client.googlecode.com/ - - """ - -__author__ = u'havard@gulldahl.no'# (HÃ¥vard Gulldahl)' #BUG: pydoc chokes on non-ascii chars in __author__ -__license__ = 'Apache License v2' -__version__ = '$Revision: 164 $'[11:-2] - -import re -try: - from xml.etree import cElementTree as ElementTree -except ImportError: - try: - import cElementTree as ElementTree - except ImportError: - try: - from xml.etree import ElementTree - except ImportError: - from elementtree import ElementTree -import atom -import gdata - -# importing google photo submodules -import gdata.media as Media, gdata.exif as Exif, gdata.geo as Geo - -# XML namespaces which are often used in Google Photo elements -PHOTOS_NAMESPACE = 'http://schemas.google.com/photos/2007' -MEDIA_NAMESPACE = 'http://search.yahoo.com/mrss/' -EXIF_NAMESPACE = 'http://schemas.google.com/photos/exif/2007' -OPENSEARCH_NAMESPACE = 'http://a9.com/-/spec/opensearchrss/1.0/' -GEO_NAMESPACE = 'http://www.w3.org/2003/01/geo/wgs84_pos#' -GML_NAMESPACE = 'http://www.opengis.net/gml' -GEORSS_NAMESPACE = 'http://www.georss.org/georss' -PHEED_NAMESPACE = 'http://www.pheed.com/pheed/' -BATCH_NAMESPACE = 'http://schemas.google.com/gdata/batch' - - -class PhotosBaseElement(atom.AtomBase): - """Base class for elements in the PHOTO_NAMESPACE. To add new elements, - you only need to add the element tag name to self._tag - """ - - _tag = '' - _namespace = PHOTOS_NAMESPACE - _children = atom.AtomBase._children.copy() - _attributes = atom.AtomBase._attributes.copy() - - def __init__(self, name=None, extension_elements=None, - extension_attributes=None, text=None): - self.name = name - self.text = text - self.extension_elements = extension_elements or [] - self.extension_attributes = extension_attributes or {} - #def __str__(self): - #return str(self.text) - #def __unicode__(self): - #return unicode(self.text) - def __int__(self): - return int(self.text) - def bool(self): - return self.text == 'true' - -class GPhotosBaseFeed(gdata.GDataFeed, gdata.LinkFinder): - "Base class for all Feeds in gdata.photos" - _tag = 'feed' - _namespace = atom.ATOM_NAMESPACE - _attributes = gdata.GDataFeed._attributes.copy() - _children = gdata.GDataFeed._children.copy() - # We deal with Entry elements ourselves - del _children['{%s}entry' % atom.ATOM_NAMESPACE] - - def __init__(self, author=None, category=None, contributor=None, - generator=None, icon=None, atom_id=None, link=None, logo=None, - rights=None, subtitle=None, title=None, updated=None, - entry=None, total_results=None, start_index=None, - items_per_page=None, extension_elements=None, - extension_attributes=None, text=None): - gdata.GDataFeed.__init__(self, author=author, category=category, - contributor=contributor, generator=generator, - icon=icon, atom_id=atom_id, link=link, - logo=logo, rights=rights, subtitle=subtitle, - title=title, updated=updated, entry=entry, - total_results=total_results, - start_index=start_index, - items_per_page=items_per_page, - extension_elements=extension_elements, - extension_attributes=extension_attributes, - text=text) - - def kind(self): - "(string) Returns the kind" - try: - return self.category[0].term.split('#')[1] - except IndexError: - return None - - def _feedUri(self, kind): - "Convenience method to return a uri to a feed of a special kind" - assert(kind in ('album', 'tag', 'photo', 'comment', 'user')) - here_href = self.GetSelfLink().href - if 'kind=%s' % kind in here_href: - return here_href - if not 'kind=' in here_href: - sep = '?' - if '?' in here_href: sep = '&' - return here_href + "%skind=%s" % (sep, kind) - rx = re.match('.*(kind=)(album|tag|photo|comment)', here_href) - return here_href[:rx.end(1)] + kind + here_href[rx.end(2):] - - def _ConvertElementTreeToMember(self, child_tree): - """Re-implementing the method from AtomBase, since we deal with - Entry elements specially""" - category = child_tree.find('{%s}category' % atom.ATOM_NAMESPACE) - if category is None: - return atom.AtomBase._ConvertElementTreeToMember(self, child_tree) - namespace, kind = category.get('term').split('#') - if namespace != PHOTOS_NAMESPACE: - return atom.AtomBase._ConvertElementTreeToMember(self, child_tree) - ## TODO: is it safe to use getattr on gdata.photos? - entry_class = getattr(gdata.photos, '%sEntry' % kind.title()) - if not hasattr(self, 'entry') or self.entry is None: - self.entry = [] - self.entry.append(atom._CreateClassFromElementTree( - entry_class, child_tree)) - -class GPhotosBaseEntry(gdata.GDataEntry, gdata.LinkFinder): - "Base class for all Entry elements in gdata.photos" - _tag = 'entry' - _kind = '' - _namespace = atom.ATOM_NAMESPACE - _children = gdata.GDataEntry._children.copy() - _attributes = gdata.GDataEntry._attributes.copy() - - def __init__(self, author=None, category=None, content=None, - atom_id=None, link=None, published=None, - title=None, updated=None, - extended_property=None, - extension_elements=None, extension_attributes=None, text=None): - gdata.GDataEntry.__init__(self, author=author, category=category, - content=content, atom_id=atom_id, link=link, - published=published, title=title, - updated=updated, text=text, - extension_elements=extension_elements, - extension_attributes=extension_attributes) - self.category.append( - atom.Category(scheme='http://schemas.google.com/g/2005#kind', - term = 'http://schemas.google.com/photos/2007#%s' % self._kind)) - - def kind(self): - "(string) Returns the kind" - try: - return self.category[0].term.split('#')[1] - except IndexError: - return None - - def _feedUri(self, kind): - "Convenience method to get the uri to this entry's feed of the some kind" - try: - href = self.GetFeedLink().href - except AttributeError: - return None - sep = '?' - if '?' in href: sep = '&' - return '%s%skind=%s' % (href, sep, kind) - - -class PhotosBaseEntry(GPhotosBaseEntry): - pass - -class PhotosBaseFeed(GPhotosBaseFeed): - pass - -class GPhotosBaseData(object): - pass - -class Access(PhotosBaseElement): - """The Google Photo `Access' element. - - The album's access level. Valid values are `public' or `private'. - In documentation, access level is also referred to as `visibility.'""" - - _tag = 'access' -def AccessFromString(xml_string): - return atom.CreateClassFromXMLString(Access, xml_string) - -class Albumid(PhotosBaseElement): - "The Google Photo `Albumid' element" - - _tag = 'albumid' -def AlbumidFromString(xml_string): - return atom.CreateClassFromXMLString(Albumid, xml_string) - -class BytesUsed(PhotosBaseElement): - "The Google Photo `BytesUsed' element" - - _tag = 'bytesUsed' -def BytesUsedFromString(xml_string): - return atom.CreateClassFromXMLString(BytesUsed, xml_string) - -class Client(PhotosBaseElement): - "The Google Photo `Client' element" - - _tag = 'client' -def ClientFromString(xml_string): - return atom.CreateClassFromXMLString(Client, xml_string) - -class Checksum(PhotosBaseElement): - "The Google Photo `Checksum' element" - - _tag = 'checksum' -def ChecksumFromString(xml_string): - return atom.CreateClassFromXMLString(Checksum, xml_string) - -class CommentCount(PhotosBaseElement): - "The Google Photo `CommentCount' element" - - _tag = 'commentCount' -def CommentCountFromString(xml_string): - return atom.CreateClassFromXMLString(CommentCount, xml_string) - -class CommentingEnabled(PhotosBaseElement): - "The Google Photo `CommentingEnabled' element" - - _tag = 'commentingEnabled' -def CommentingEnabledFromString(xml_string): - return atom.CreateClassFromXMLString(CommentingEnabled, xml_string) - -class Height(PhotosBaseElement): - "The Google Photo `Height' element" - - _tag = 'height' -def HeightFromString(xml_string): - return atom.CreateClassFromXMLString(Height, xml_string) - -class Id(PhotosBaseElement): - "The Google Photo `Id' element" - - _tag = 'id' -def IdFromString(xml_string): - return atom.CreateClassFromXMLString(Id, xml_string) - -class Location(PhotosBaseElement): - "The Google Photo `Location' element" - - _tag = 'location' -def LocationFromString(xml_string): - return atom.CreateClassFromXMLString(Location, xml_string) - -class MaxPhotosPerAlbum(PhotosBaseElement): - "The Google Photo `MaxPhotosPerAlbum' element" - - _tag = 'maxPhotosPerAlbum' -def MaxPhotosPerAlbumFromString(xml_string): - return atom.CreateClassFromXMLString(MaxPhotosPerAlbum, xml_string) - -class Name(PhotosBaseElement): - "The Google Photo `Name' element" - - _tag = 'name' -def NameFromString(xml_string): - return atom.CreateClassFromXMLString(Name, xml_string) - -class Nickname(PhotosBaseElement): - "The Google Photo `Nickname' element" - - _tag = 'nickname' -def NicknameFromString(xml_string): - return atom.CreateClassFromXMLString(Nickname, xml_string) - -class Numphotos(PhotosBaseElement): - "The Google Photo `Numphotos' element" - - _tag = 'numphotos' -def NumphotosFromString(xml_string): - return atom.CreateClassFromXMLString(Numphotos, xml_string) - -class Numphotosremaining(PhotosBaseElement): - "The Google Photo `Numphotosremaining' element" - - _tag = 'numphotosremaining' -def NumphotosremainingFromString(xml_string): - return atom.CreateClassFromXMLString(Numphotosremaining, xml_string) - -class Position(PhotosBaseElement): - "The Google Photo `Position' element" - - _tag = 'position' -def PositionFromString(xml_string): - return atom.CreateClassFromXMLString(Position, xml_string) - -class Photoid(PhotosBaseElement): - "The Google Photo `Photoid' element" - - _tag = 'photoid' -def PhotoidFromString(xml_string): - return atom.CreateClassFromXMLString(Photoid, xml_string) - -class Quotacurrent(PhotosBaseElement): - "The Google Photo `Quotacurrent' element" - - _tag = 'quotacurrent' -def QuotacurrentFromString(xml_string): - return atom.CreateClassFromXMLString(Quotacurrent, xml_string) - -class Quotalimit(PhotosBaseElement): - "The Google Photo `Quotalimit' element" - - _tag = 'quotalimit' -def QuotalimitFromString(xml_string): - return atom.CreateClassFromXMLString(Quotalimit, xml_string) - -class Rotation(PhotosBaseElement): - "The Google Photo `Rotation' element" - - _tag = 'rotation' -def RotationFromString(xml_string): - return atom.CreateClassFromXMLString(Rotation, xml_string) - -class Size(PhotosBaseElement): - "The Google Photo `Size' element" - - _tag = 'size' -def SizeFromString(xml_string): - return atom.CreateClassFromXMLString(Size, xml_string) - -class Snippet(PhotosBaseElement): - """The Google Photo `snippet' element. - - When searching, the snippet element will contain a - string with the word you're looking for, highlighted in html markup - E.g. when your query is `hafjell', this element may contain: - `... here at Hafjell.' - - You'll find this element in searches -- that is, feeds that combine the - `kind=photo' and `q=yoursearch' parameters in the request. - - See also gphoto:truncated and gphoto:snippettype. - - """ - - _tag = 'snippet' -def SnippetFromString(xml_string): - return atom.CreateClassFromXMLString(Snippet, xml_string) - -class Snippettype(PhotosBaseElement): - """The Google Photo `Snippettype' element - - When searching, this element will tell you the type of element that matches. - - You'll find this element in searches -- that is, feeds that combine the - `kind=photo' and `q=yoursearch' parameters in the request. - - See also gphoto:snippet and gphoto:truncated. - - Possible values and their interpretation: - o ALBUM_TITLE - The album title matches - o PHOTO_TAGS - The match is a tag/keyword - o PHOTO_DESCRIPTION - The match is in the photo's description - - If you discover a value not listed here, please submit a patch to update this docstring. - - """ - - _tag = 'snippettype' -def SnippettypeFromString(xml_string): - return atom.CreateClassFromXMLString(Snippettype, xml_string) - -class Thumbnail(PhotosBaseElement): - """The Google Photo `Thumbnail' element - - Used to display user's photo thumbnail (hackergotchi). - - (Not to be confused with the element, which gives you - small versions of the photo object.)""" - - _tag = 'thumbnail' -def ThumbnailFromString(xml_string): - return atom.CreateClassFromXMLString(Thumbnail, xml_string) - -class Timestamp(PhotosBaseElement): - """The Google Photo `Timestamp' element - Represented as the number of milliseconds since January 1st, 1970. - - - Take a look at the convenience methods .isoformat() and .datetime(): - - photo_epoch = Time.text # 1180294337000 - photo_isostring = Time.isoformat() # '2007-05-27T19:32:17.000Z' - - Alternatively: - photo_datetime = Time.datetime() # (requires python >= 2.3) - """ - - _tag = 'timestamp' - def isoformat(self): - """(string) Return the timestamp as a ISO 8601 formatted string, - e.g. '2007-05-27T19:32:17.000Z' - """ - import time - epoch = float(self.text)/1000 - return time.strftime('%Y-%m-%dT%H:%M:%S.000Z', time.gmtime(epoch)) - - def datetime(self): - """(datetime.datetime) Return the timestamp as a datetime.datetime object - - Requires python 2.3 - """ - import datetime - epoch = float(self.text)/1000 - return datetime.datetime.fromtimestamp(epoch) -def TimestampFromString(xml_string): - return atom.CreateClassFromXMLString(Timestamp, xml_string) - -class Truncated(PhotosBaseElement): - """The Google Photo `Truncated' element - - You'll find this element in searches -- that is, feeds that combine the - `kind=photo' and `q=yoursearch' parameters in the request. - - See also gphoto:snippet and gphoto:snippettype. - - Possible values and their interpretation: - 0 -- unknown - """ - - _tag = 'Truncated' -def TruncatedFromString(xml_string): - return atom.CreateClassFromXMLString(Truncated, xml_string) - -class User(PhotosBaseElement): - "The Google Photo `User' element" - - _tag = 'user' -def UserFromString(xml_string): - return atom.CreateClassFromXMLString(User, xml_string) - -class Version(PhotosBaseElement): - "The Google Photo `Version' element" - - _tag = 'version' -def VersionFromString(xml_string): - return atom.CreateClassFromXMLString(Version, xml_string) - -class Width(PhotosBaseElement): - "The Google Photo `Width' element" - - _tag = 'width' -def WidthFromString(xml_string): - return atom.CreateClassFromXMLString(Width, xml_string) - -class Weight(PhotosBaseElement): - """The Google Photo `Weight' element. - - The weight of the tag is the number of times the tag - appears in the collection of tags currently being viewed. - The default weight is 1, in which case this tags is omitted.""" - _tag = 'weight' -def WeightFromString(xml_string): - return atom.CreateClassFromXMLString(Weight, xml_string) - -class CommentAuthor(atom.Author): - """The Atom `Author' element in CommentEntry entries is augmented to - contain elements from the PHOTOS_NAMESPACE - - http://groups.google.com/group/Google-Picasa-Data-API/msg/819b0025b5ff5e38 - """ - _children = atom.Author._children.copy() - _children['{%s}user' % PHOTOS_NAMESPACE] = ('user', User) - _children['{%s}nickname' % PHOTOS_NAMESPACE] = ('nickname', Nickname) - _children['{%s}thumbnail' % PHOTOS_NAMESPACE] = ('thumbnail', Thumbnail) -def CommentAuthorFromString(xml_string): - return atom.CreateClassFromXMLString(CommentAuthor, xml_string) - -########################## ################################ - -class AlbumData(object): - _children = {} - _children['{%s}id' % PHOTOS_NAMESPACE] = ('gphoto_id', Id) - _children['{%s}name' % PHOTOS_NAMESPACE] = ('name', Name) - _children['{%s}location' % PHOTOS_NAMESPACE] = ('location', Location) - _children['{%s}access' % PHOTOS_NAMESPACE] = ('access', Access) - _children['{%s}bytesUsed' % PHOTOS_NAMESPACE] = ('bytesUsed', BytesUsed) - _children['{%s}timestamp' % PHOTOS_NAMESPACE] = ('timestamp', Timestamp) - _children['{%s}numphotos' % PHOTOS_NAMESPACE] = ('numphotos', Numphotos) - _children['{%s}numphotosremaining' % PHOTOS_NAMESPACE] = \ - ('numphotosremaining', Numphotosremaining) - _children['{%s}user' % PHOTOS_NAMESPACE] = ('user', User) - _children['{%s}nickname' % PHOTOS_NAMESPACE] = ('nickname', Nickname) - _children['{%s}commentingEnabled' % PHOTOS_NAMESPACE] = \ - ('commentingEnabled', CommentingEnabled) - _children['{%s}commentCount' % PHOTOS_NAMESPACE] = \ - ('commentCount', CommentCount) - ## NOTE: storing media:group as self.media, to create a self-explaining api - gphoto_id = None - name = None - location = None - access = None - bytesUsed = None - timestamp = None - numphotos = None - numphotosremaining = None - user = None - nickname = None - commentingEnabled = None - commentCount = None - -class AlbumEntry(GPhotosBaseEntry, AlbumData): - """All metadata for a Google Photos Album - - Take a look at AlbumData for metadata accessible as attributes to this object. - - Notes: - To avoid name clashes, and to create a more sensible api, some - objects have names that differ from the original elements: - - o media:group -> self.media, - o geo:where -> self.geo, - o photo:id -> self.gphoto_id - """ - - _kind = 'album' - _children = GPhotosBaseEntry._children.copy() - _children.update(AlbumData._children.copy()) - # child tags only for Album entries, not feeds - _children['{%s}where' % GEORSS_NAMESPACE] = ('geo', Geo.Where) - _children['{%s}group' % MEDIA_NAMESPACE] = ('media', Media.Group) - media = Media.Group() - geo = Geo.Where() - - def __init__(self, author=None, category=None, content=None, - atom_id=None, link=None, published=None, - title=None, updated=None, - #GPHOTO NAMESPACE: - gphoto_id=None, name=None, location=None, access=None, - timestamp=None, numphotos=None, user=None, nickname=None, - commentingEnabled=None, commentCount=None, thumbnail=None, - # MEDIA NAMESPACE: - media=None, - # GEORSS NAMESPACE: - geo=None, - extended_property=None, - extension_elements=None, extension_attributes=None, text=None): - GPhotosBaseEntry.__init__(self, author=author, category=category, - content=content, atom_id=atom_id, link=link, - published=published, title=title, - updated=updated, text=text, - extension_elements=extension_elements, - extension_attributes=extension_attributes) - - ## NOTE: storing photo:id as self.gphoto_id, to avoid name clash with atom:id - self.gphoto_id = gphoto_id - self.name = name - self.location = location - self.access = access - self.timestamp = timestamp - self.numphotos = numphotos - self.user = user - self.nickname = nickname - self.commentingEnabled = commentingEnabled - self.commentCount = commentCount - self.thumbnail = thumbnail - self.extended_property = extended_property or [] - self.text = text - ## NOTE: storing media:group as self.media, and geo:where as geo, - ## to create a self-explaining api - self.media = media or Media.Group() - self.geo = geo or Geo.Where() - - def GetAlbumId(self): - "Return the id of this album" - - return self.GetFeedLink().href.split('/')[-1] - - def GetPhotosUri(self): - "(string) Return the uri to this albums feed of the PhotoEntry kind" - return self._feedUri('photo') - - def GetCommentsUri(self): - "(string) Return the uri to this albums feed of the CommentEntry kind" - return self._feedUri('comment') - - def GetTagsUri(self): - "(string) Return the uri to this albums feed of the TagEntry kind" - return self._feedUri('tag') - -def AlbumEntryFromString(xml_string): - return atom.CreateClassFromXMLString(AlbumEntry, xml_string) - -class AlbumFeed(GPhotosBaseFeed, AlbumData): - """All metadata for a Google Photos Album, including its sub-elements - - This feed represents an album as the container for other objects. - - A Album feed contains entries of - PhotoEntry, CommentEntry or TagEntry, - depending on the `kind' parameter in the original query. - - Take a look at AlbumData for accessible attributes. - - """ - - _children = GPhotosBaseFeed._children.copy() - _children.update(AlbumData._children.copy()) - - def GetPhotosUri(self): - "(string) Return the uri to the same feed, but of the PhotoEntry kind" - - return self._feedUri('photo') - - def GetTagsUri(self): - "(string) Return the uri to the same feed, but of the TagEntry kind" - - return self._feedUri('tag') - - def GetCommentsUri(self): - "(string) Return the uri to the same feed, but of the CommentEntry kind" - - return self._feedUri('comment') - -def AlbumFeedFromString(xml_string): - return atom.CreateClassFromXMLString(AlbumFeed, xml_string) - - -class PhotoData(object): - _children = {} - ## NOTE: storing photo:id as self.gphoto_id, to avoid name clash with atom:id - _children['{%s}id' % PHOTOS_NAMESPACE] = ('gphoto_id', Id) - _children['{%s}albumid' % PHOTOS_NAMESPACE] = ('albumid', Albumid) - _children['{%s}checksum' % PHOTOS_NAMESPACE] = ('checksum', Checksum) - _children['{%s}client' % PHOTOS_NAMESPACE] = ('client', Client) - _children['{%s}height' % PHOTOS_NAMESPACE] = ('height', Height) - _children['{%s}position' % PHOTOS_NAMESPACE] = ('position', Position) - _children['{%s}rotation' % PHOTOS_NAMESPACE] = ('rotation', Rotation) - _children['{%s}size' % PHOTOS_NAMESPACE] = ('size', Size) - _children['{%s}timestamp' % PHOTOS_NAMESPACE] = ('timestamp', Timestamp) - _children['{%s}version' % PHOTOS_NAMESPACE] = ('version', Version) - _children['{%s}width' % PHOTOS_NAMESPACE] = ('width', Width) - _children['{%s}commentingEnabled' % PHOTOS_NAMESPACE] = \ - ('commentingEnabled', CommentingEnabled) - _children['{%s}commentCount' % PHOTOS_NAMESPACE] = \ - ('commentCount', CommentCount) - ## NOTE: storing media:group as self.media, exif:tags as self.exif, and - ## geo:where as self.geo, to create a self-explaining api - _children['{%s}tags' % EXIF_NAMESPACE] = ('exif', Exif.Tags) - _children['{%s}where' % GEORSS_NAMESPACE] = ('geo', Geo.Where) - _children['{%s}group' % MEDIA_NAMESPACE] = ('media', Media.Group) - # These elements show up in search feeds - _children['{%s}snippet' % PHOTOS_NAMESPACE] = ('snippet', Snippet) - _children['{%s}snippettype' % PHOTOS_NAMESPACE] = ('snippettype', Snippettype) - _children['{%s}truncated' % PHOTOS_NAMESPACE] = ('truncated', Truncated) - gphoto_id = None - albumid = None - checksum = None - client = None - height = None - position = None - rotation = None - size = None - timestamp = None - version = None - width = None - commentingEnabled = None - commentCount = None - snippet=None - snippettype=None - truncated=None - media = Media.Group() - geo = Geo.Where() - tags = Exif.Tags() - -class PhotoEntry(GPhotosBaseEntry, PhotoData): - """All metadata for a Google Photos Photo - - Take a look at PhotoData for metadata accessible as attributes to this object. - - Notes: - To avoid name clashes, and to create a more sensible api, some - objects have names that differ from the original elements: - - o media:group -> self.media, - o exif:tags -> self.exif, - o geo:where -> self.geo, - o photo:id -> self.gphoto_id - """ - - _kind = 'photo' - _children = GPhotosBaseEntry._children.copy() - _children.update(PhotoData._children.copy()) - - def __init__(self, author=None, category=None, content=None, - atom_id=None, link=None, published=None, - title=None, updated=None, text=None, - # GPHOTO NAMESPACE: - gphoto_id=None, albumid=None, checksum=None, client=None, height=None, - position=None, rotation=None, size=None, timestamp=None, version=None, - width=None, commentCount=None, commentingEnabled=None, - # MEDIARSS NAMESPACE: - media=None, - # EXIF_NAMESPACE: - exif=None, - # GEORSS NAMESPACE: - geo=None, - extension_elements=None, extension_attributes=None): - GPhotosBaseEntry.__init__(self, author=author, category=category, - content=content, - atom_id=atom_id, link=link, published=published, - title=title, updated=updated, text=text, - extension_elements=extension_elements, - extension_attributes=extension_attributes) - - - ## NOTE: storing photo:id as self.gphoto_id, to avoid name clash with atom:id - self.gphoto_id = gphoto_id - self.albumid = albumid - self.checksum = checksum - self.client = client - self.height = height - self.position = position - self.rotation = rotation - self.size = size - self.timestamp = timestamp - self.version = version - self.width = width - self.commentingEnabled = commentingEnabled - self.commentCount = commentCount - ## NOTE: storing media:group as self.media, to create a self-explaining api - self.media = media or Media.Group() - self.exif = exif or Exif.Tags() - self.geo = geo or Geo.Where() - - def GetPostLink(self): - "Return the uri to this photo's `POST' link (use it for updates of the object)" - - return self.GetFeedLink() - - def GetCommentsUri(self): - "Return the uri to this photo's feed of CommentEntry comments" - return self._feedUri('comment') - - def GetTagsUri(self): - "Return the uri to this photo's feed of TagEntry tags" - return self._feedUri('tag') - - def GetAlbumUri(self): - """Return the uri to the AlbumEntry containing this photo""" - - href = self.GetSelfLink().href - return href[:href.find('/photoid')] - -def PhotoEntryFromString(xml_string): - return atom.CreateClassFromXMLString(PhotoEntry, xml_string) - -class PhotoFeed(GPhotosBaseFeed, PhotoData): - """All metadata for a Google Photos Photo, including its sub-elements - - This feed represents a photo as the container for other objects. - - A Photo feed contains entries of - CommentEntry or TagEntry, - depending on the `kind' parameter in the original query. - - Take a look at PhotoData for metadata accessible as attributes to this object. - - """ - _children = GPhotosBaseFeed._children.copy() - _children.update(PhotoData._children.copy()) - - def GetTagsUri(self): - "(string) Return the uri to the same feed, but of the TagEntry kind" - - return self._feedUri('tag') - - def GetCommentsUri(self): - "(string) Return the uri to the same feed, but of the CommentEntry kind" - - return self._feedUri('comment') - -def PhotoFeedFromString(xml_string): - return atom.CreateClassFromXMLString(PhotoFeed, xml_string) - -class TagData(GPhotosBaseData): - _children = {} - _children['{%s}weight' % PHOTOS_NAMESPACE] = ('weight', Weight) - weight=None - -class TagEntry(GPhotosBaseEntry, TagData): - """All metadata for a Google Photos Tag - - The actual tag is stored in the .title.text attribute - - """ - - _kind = 'tag' - _children = GPhotosBaseEntry._children.copy() - _children.update(TagData._children.copy()) - - def __init__(self, author=None, category=None, content=None, - atom_id=None, link=None, published=None, - title=None, updated=None, - # GPHOTO NAMESPACE: - weight=None, - extended_property=None, - extension_elements=None, extension_attributes=None, text=None): - GPhotosBaseEntry.__init__(self, author=author, category=category, - content=content, - atom_id=atom_id, link=link, published=published, - title=title, updated=updated, text=text, - extension_elements=extension_elements, - extension_attributes=extension_attributes) - - self.weight = weight - - def GetAlbumUri(self): - """Return the uri to the AlbumEntry containing this tag""" - - href = self.GetSelfLink().href - pos = href.find('/photoid') - if pos == -1: - return None - return href[:pos] - - def GetPhotoUri(self): - """Return the uri to the PhotoEntry containing this tag""" - - href = self.GetSelfLink().href - pos = href.find('/tag') - if pos == -1: - return None - return href[:pos] - -def TagEntryFromString(xml_string): - return atom.CreateClassFromXMLString(TagEntry, xml_string) - - -class TagFeed(GPhotosBaseFeed, TagData): - """All metadata for a Google Photos Tag, including its sub-elements""" - - _children = GPhotosBaseFeed._children.copy() - _children.update(TagData._children.copy()) - -def TagFeedFromString(xml_string): - return atom.CreateClassFromXMLString(TagFeed, xml_string) - -class CommentData(GPhotosBaseData): - _children = {} - ## NOTE: storing photo:id as self.gphoto_id, to avoid name clash with atom:id - _children['{%s}id' % PHOTOS_NAMESPACE] = ('gphoto_id', Id) - _children['{%s}albumid' % PHOTOS_NAMESPACE] = ('albumid', Albumid) - _children['{%s}photoid' % PHOTOS_NAMESPACE] = ('photoid', Photoid) - _children['{%s}author' % atom.ATOM_NAMESPACE] = ('author', [CommentAuthor,]) - gphoto_id=None - albumid=None - photoid=None - author=None - -class CommentEntry(GPhotosBaseEntry, CommentData): - """All metadata for a Google Photos Comment - - The comment is stored in the .content.text attribute, - with a content type in .content.type. - - - """ - - _kind = 'comment' - _children = GPhotosBaseEntry._children.copy() - _children.update(CommentData._children.copy()) - def __init__(self, author=None, category=None, content=None, - atom_id=None, link=None, published=None, - title=None, updated=None, - # GPHOTO NAMESPACE: - gphoto_id=None, albumid=None, photoid=None, - extended_property=None, - extension_elements=None, extension_attributes=None, text=None): - - GPhotosBaseEntry.__init__(self, author=author, category=category, - content=content, - atom_id=atom_id, link=link, published=published, - title=title, updated=updated, - extension_elements=extension_elements, - extension_attributes=extension_attributes, - text=text) - - self.gphoto_id = gphoto_id - self.albumid = albumid - self.photoid = photoid - - def GetCommentId(self): - """Return the globally unique id of this comment""" - return self.GetSelfLink().href.split('/')[-1] - - def GetAlbumUri(self): - """Return the uri to the AlbumEntry containing this comment""" - - href = self.GetSelfLink().href - return href[:href.find('/photoid')] - - def GetPhotoUri(self): - """Return the uri to the PhotoEntry containing this comment""" - - href = self.GetSelfLink().href - return href[:href.find('/commentid')] - -def CommentEntryFromString(xml_string): - return atom.CreateClassFromXMLString(CommentEntry, xml_string) - -class CommentFeed(GPhotosBaseFeed, CommentData): - """All metadata for a Google Photos Comment, including its sub-elements""" - - _children = GPhotosBaseFeed._children.copy() - _children.update(CommentData._children.copy()) - -def CommentFeedFromString(xml_string): - return atom.CreateClassFromXMLString(CommentFeed, xml_string) - -class UserData(GPhotosBaseData): - _children = {} - _children['{%s}maxPhotosPerAlbum' % PHOTOS_NAMESPACE] = ('maxPhotosPerAlbum', MaxPhotosPerAlbum) - _children['{%s}nickname' % PHOTOS_NAMESPACE] = ('nickname', Nickname) - _children['{%s}quotalimit' % PHOTOS_NAMESPACE] = ('quotalimit', Quotalimit) - _children['{%s}quotacurrent' % PHOTOS_NAMESPACE] = ('quotacurrent', Quotacurrent) - _children['{%s}thumbnail' % PHOTOS_NAMESPACE] = ('thumbnail', Thumbnail) - _children['{%s}user' % PHOTOS_NAMESPACE] = ('user', User) - _children['{%s}id' % PHOTOS_NAMESPACE] = ('gphoto_id', Id) - - maxPhotosPerAlbum=None - nickname=None - quotalimit=None - quotacurrent=None - thumbnail=None - user=None - gphoto_id=None - - -class UserEntry(GPhotosBaseEntry, UserData): - """All metadata for a Google Photos User - - This entry represents an album owner and all appropriate metadata. - - Take a look at at the attributes of the UserData for metadata available. - """ - _children = GPhotosBaseEntry._children.copy() - _children.update(UserData._children.copy()) - _kind = 'user' - - def __init__(self, author=None, category=None, content=None, - atom_id=None, link=None, published=None, - title=None, updated=None, - # GPHOTO NAMESPACE: - gphoto_id=None, maxPhotosPerAlbum=None, nickname=None, quotalimit=None, - quotacurrent=None, thumbnail=None, user=None, - extended_property=None, - extension_elements=None, extension_attributes=None, text=None): - - GPhotosBaseEntry.__init__(self, author=author, category=category, - content=content, - atom_id=atom_id, link=link, published=published, - title=title, updated=updated, - extension_elements=extension_elements, - extension_attributes=extension_attributes, - text=text) - - - self.gphoto_id=gphoto_id - self.maxPhotosPerAlbum=maxPhotosPerAlbum - self.nickname=nickname - self.quotalimit=quotalimit - self.quotacurrent=quotacurrent - self.thumbnail=thumbnail - self.user=user - - def GetAlbumsUri(self): - "(string) Return the uri to this user's feed of the AlbumEntry kind" - return self._feedUri('album') - - def GetPhotosUri(self): - "(string) Return the uri to this user's feed of the PhotoEntry kind" - return self._feedUri('photo') - - def GetCommentsUri(self): - "(string) Return the uri to this user's feed of the CommentEntry kind" - return self._feedUri('comment') - - def GetTagsUri(self): - "(string) Return the uri to this user's feed of the TagEntry kind" - return self._feedUri('tag') - -def UserEntryFromString(xml_string): - return atom.CreateClassFromXMLString(UserEntry, xml_string) - -class UserFeed(GPhotosBaseFeed, UserData): - """Feed for a User in the google photos api. - - This feed represents a user as the container for other objects. - - A User feed contains entries of - AlbumEntry, PhotoEntry, CommentEntry, UserEntry or TagEntry, - depending on the `kind' parameter in the original query. - - The user feed itself also contains all of the metadata available - as part of a UserData object.""" - _children = GPhotosBaseFeed._children.copy() - _children.update(UserData._children.copy()) - - def GetAlbumsUri(self): - """Get the uri to this feed, but with entries of the AlbumEntry kind.""" - return self._feedUri('album') - - def GetTagsUri(self): - """Get the uri to this feed, but with entries of the TagEntry kind.""" - return self._feedUri('tag') - - def GetPhotosUri(self): - """Get the uri to this feed, but with entries of the PhotosEntry kind.""" - return self._feedUri('photo') - - def GetCommentsUri(self): - """Get the uri to this feed, but with entries of the CommentsEntry kind.""" - return self._feedUri('comment') - -def UserFeedFromString(xml_string): - return atom.CreateClassFromXMLString(UserFeed, xml_string) - - - -def AnyFeedFromString(xml_string): - """Creates an instance of the appropriate feed class from the - xml string contents. - - Args: - xml_string: str A string which contains valid XML. The root element - of the XML string should match the tag and namespace of the desired - class. - - Returns: - An instance of the target class with members assigned according to the - contents of the XML - or a basic gdata.GDataFeed instance if it is - impossible to determine the appropriate class (look for extra elements - in GDataFeed's .FindExtensions() and extension_elements[] ). - """ - tree = ElementTree.fromstring(xml_string) - category = tree.find('{%s}category' % atom.ATOM_NAMESPACE) - if category is None: - # TODO: is this the best way to handle this? - return atom._CreateClassFromElementTree(GPhotosBaseFeed, tree) - namespace, kind = category.get('term').split('#') - if namespace != PHOTOS_NAMESPACE: - # TODO: is this the best way to handle this? - return atom._CreateClassFromElementTree(GPhotosBaseFeed, tree) - ## TODO: is getattr safe this way? - feed_class = getattr(gdata.photos, '%sFeed' % kind.title()) - return atom._CreateClassFromElementTree(feed_class, tree) - -def AnyEntryFromString(xml_string): - """Creates an instance of the appropriate entry class from the - xml string contents. - - Args: - xml_string: str A string which contains valid XML. The root element - of the XML string should match the tag and namespace of the desired - class. - - Returns: - An instance of the target class with members assigned according to the - contents of the XML - or a basic gdata.GDataEndry instance if it is - impossible to determine the appropriate class (look for extra elements - in GDataEntry's .FindExtensions() and extension_elements[] ). - """ - tree = ElementTree.fromstring(xml_string) - category = tree.find('{%s}category' % atom.ATOM_NAMESPACE) - if category is None: - # TODO: is this the best way to handle this? - return atom._CreateClassFromElementTree(GPhotosBaseEntry, tree) - namespace, kind = category.get('term').split('#') - if namespace != PHOTOS_NAMESPACE: - # TODO: is this the best way to handle this? - return atom._CreateClassFromElementTree(GPhotosBaseEntry, tree) - ## TODO: is getattr safe this way? - feed_class = getattr(gdata.photos, '%sEntry' % kind.title()) - return atom._CreateClassFromElementTree(feed_class, tree) - diff --git a/gdata/photos/service.py b/gdata/photos/service.py deleted file mode 100644 index 7170379400..0000000000 --- a/gdata/photos/service.py +++ /dev/null @@ -1,680 +0,0 @@ -#!/usr/bin/env python -# -*-*- encoding: utf-8 -*-*- -# -# This is the service file for the Google Photo python client. -# It is used for higher level operations. -# -# $Id: service.py 144 2007-10-25 21:03:34Z havard.gulldahl $ -# -# Copyright 2007 HÃ¥vard Gulldahl -# -# 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. - -"""Google PhotoService provides a human-friendly interface to -Google Photo (a.k.a Picasa Web) services[1]. - -It extends gdata.service.GDataService and as such hides all the -nasty details about authenticating, parsing and communicating with -Google Photos. - -[1]: http://code.google.com/apis/picasaweb/gdata.html - -Example: - import gdata.photos, gdata.photos.service - pws = gdata.photos.service.PhotosService() - pws.ClientLogin(username, password) - #Get all albums - albums = pws.GetUserFeed().entry - # Get all photos in second album - photos = pws.GetFeed(albums[1].GetPhotosUri()).entry - # Get all tags for photos in second album and print them - tags = pws.GetFeed(albums[1].GetTagsUri()).entry - print [ tag.summary.text for tag in tags ] - # Get all comments for the first photos in list and print them - comments = pws.GetCommentFeed(photos[0].GetCommentsUri()).entry - print [ c.summary.text for c in comments ] - - # Get a photo to work with - photo = photos[0] - # Update metadata - - # Attributes from the namespace - photo.summary.text = u'A nice view from my veranda' - photo.title.text = u'Verandaview.jpg' - - # Attributes from the namespace - photo.media.keywords.text = u'Home, Long-exposure, Sunset' # Comma-separated - - # Adding attributes to media object - - # Rotate 90 degrees clockwise - photo.rotation = gdata.photos.Rotation(text='90') - - # Submit modified photo object - photo = pws.UpdatePhotoMetadata(photo) - - # Make sure you only modify the newly returned object, else you'll get - # versioning errors. See Optimistic-concurrency - - # Add comment to a picture - comment = pws.InsertComment(photo, u'I wish the water always was this warm') - - # Remove comment because it was silly - print "*blush*" - pws.Delete(comment.GetEditLink().href) - -""" - -__author__ = u'havard@gulldahl.no'# (HÃ¥vard Gulldahl)' #BUG: pydoc chokes on non-ascii chars in __author__ -__license__ = 'Apache License v2' -__version__ = '$Revision: 176 $'[11:-2] - - -import sys, os.path, StringIO -import time -import gdata.service -import gdata -import atom.service -import atom -import gdata.photos - -SUPPORTED_UPLOAD_TYPES = ('bmp', 'jpeg', 'jpg', 'gif', 'png') - -UNKOWN_ERROR=1000 -GPHOTOS_BAD_REQUEST=400 -GPHOTOS_CONFLICT=409 -GPHOTOS_INTERNAL_SERVER_ERROR=500 -GPHOTOS_INVALID_ARGUMENT=601 -GPHOTOS_INVALID_CONTENT_TYPE=602 -GPHOTOS_NOT_AN_IMAGE=603 -GPHOTOS_INVALID_KIND=604 - -class GooglePhotosException(Exception): - def __init__(self, response): - - self.error_code = response['status'] - self.reason = response['reason'].strip() - if '' in str(response['body']): #general html message, discard it - response['body'] = "" - self.body = response['body'].strip() - self.message = "(%(status)s) %(body)s -- %(reason)s" % response - - #return explicit error codes - error_map = { '(12) Not an image':GPHOTOS_NOT_AN_IMAGE, - 'kind: That is not one of the acceptable values': - GPHOTOS_INVALID_KIND, - - } - for msg, code in error_map.iteritems(): - if self.body == msg: - self.error_code = code - break - self.args = [self.error_code, self.reason, self.body] - -class PhotosService(gdata.service.GDataService): - userUri = '/data/feed/api/user/%s' - - def __init__(self, email=None, password=None, source=None, - server='picasaweb.google.com', additional_headers=None, - **kwargs): - """Creates a client for the Google Photos 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: 'picasaweb.google.com'. - **kwargs: The other parameters to pass to gdata.service.GDataService - constructor. - """ - self.email = email - self.client = source - gdata.service.GDataService.__init__( - self, email=email, password=password, service='lh2', source=source, - server=server, additional_headers=additional_headers, **kwargs) - - def GetFeed(self, uri, limit=None, start_index=None): - """Get a feed. - - The results are ordered by the values of their `updated' elements, - with the most recently updated entry appearing first in the feed. - - Arguments: - uri: the uri to fetch - limit (optional): the maximum number of entries to return. Defaults to what - the server returns. - - Returns: - one of gdata.photos.AlbumFeed, - gdata.photos.UserFeed, - gdata.photos.PhotoFeed, - gdata.photos.CommentFeed, - gdata.photos.TagFeed, - depending on the results of the query. - Raises: - GooglePhotosException - - See: - http://code.google.com/apis/picasaweb/gdata.html#Get_Album_Feed_Manual - """ - if limit is not None: - uri += '&max-results=%s' % limit - if start_index is not None: - uri += '&start-index=%s' % start_index - try: - return self.Get(uri, converter=gdata.photos.AnyFeedFromString) - except gdata.service.RequestError, e: - raise GooglePhotosException(e.args[0]) - - def GetEntry(self, uri, limit=None, start_index=None): - """Get an Entry. - - Arguments: - uri: the uri to the entry - limit (optional): the maximum number of entries to return. Defaults to what - the server returns. - - Returns: - one of gdata.photos.AlbumEntry, - gdata.photos.UserEntry, - gdata.photos.PhotoEntry, - gdata.photos.CommentEntry, - gdata.photos.TagEntry, - depending on the results of the query. - Raises: - GooglePhotosException - """ - if limit is not None: - uri += '&max-results=%s' % limit - if start_index is not None: - uri += '&start-index=%s' % start_index - try: - return self.Get(uri, converter=gdata.photos.AnyEntryFromString) - except gdata.service.RequestError, e: - raise GooglePhotosException(e.args[0]) - - def GetUserFeed(self, kind='album', user='default', limit=None): - """Get user-based feed, containing albums, photos, comments or tags; - defaults to albums. - - The entries are ordered by the values of their `updated' elements, - with the most recently updated entry appearing first in the feed. - - Arguments: - kind: the kind of entries to get, either `album', `photo', - `comment' or `tag', or a python list of these. Defaults to `album'. - user (optional): whose albums we're querying. Defaults to current user. - limit (optional): the maximum number of entries to return. - Defaults to everything the server returns. - - - Returns: - gdata.photos.UserFeed, containing appropriate Entry elements - - See: - http://code.google.com/apis/picasaweb/gdata.html#Get_Album_Feed_Manual - http://googledataapis.blogspot.com/2007/07/picasa-web-albums-adds-new-api-features.html - """ - if isinstance(kind, (list, tuple) ): - kind = ",".join(kind) - - uri = '/data/feed/api/user/%s?kind=%s' % (user, kind) - return self.GetFeed(uri, limit=limit) - - def GetTaggedPhotos(self, tag, user='default', limit=None): - """Get all photos belonging to a specific user, tagged by the given keyword - - Arguments: - tag: The tag you're looking for, e.g. `dog' - user (optional): Whose images/videos you want to search, defaults - to current user - limit (optional): the maximum number of entries to return. - Defaults to everything the server returns. - - Returns: - gdata.photos.UserFeed containing PhotoEntry elements - """ - # Lower-casing because of - # http://code.google.com/p/gdata-issues/issues/detail?id=194 - uri = '/data/feed/api/user/%s?kind=photo&tag=%s' % (user, tag.lower()) - return self.GetFeed(uri, limit) - - def SearchUserPhotos(self, query, user='default', limit=100): - """Search through all photos for a specific user and return a feed. - This will look for matches in file names and image tags (a.k.a. keywords) - - Arguments: - query: The string you're looking for, e.g. `vacation' - user (optional): The username of whose photos you want to search, defaults - to current user. - limit (optional): Don't return more than `limit' hits, defaults to 100 - - Only public photos are searched, unless you are authenticated and - searching through your own photos. - - Returns: - gdata.photos.UserFeed with PhotoEntry elements - """ - uri = '/data/feed/api/user/%s?kind=photo&q=%s' % (user, query) - return self.GetFeed(uri, limit=limit) - - def SearchCommunityPhotos(self, query, limit=100): - """Search through all public photos and return a feed. - This will look for matches in file names and image tags (a.k.a. keywords) - - Arguments: - query: The string you're looking for, e.g. `vacation' - limit (optional): Don't return more than `limit' hits, defaults to 100 - - Returns: - gdata.GDataFeed with PhotoEntry elements - """ - uri='/data/feed/api/all?q=%s' % query - return self.GetFeed(uri, limit=limit) - - def GetContacts(self, user='default', limit=None): - """Retrieve a feed that contains a list of your contacts - - Arguments: - user: Username of the user whose contacts you want - - Returns - gdata.photos.UserFeed, with UserEntry entries - - See: - http://groups.google.com/group/Google-Picasa-Data-API/msg/819b0025b5ff5e38 - """ - uri = '/data/feed/api/user/%s/contacts?kind=user' % user - return self.GetFeed(uri, limit=limit) - - def SearchContactsPhotos(self, user='default', search=None, limit=None): - """Search over your contacts' photos and return a feed - - Arguments: - user: Username of the user whose contacts you want - search (optional): What to search for (photo title, description and keywords) - - Returns - gdata.photos.UserFeed, with PhotoEntry elements - - See: - http://groups.google.com/group/Google-Picasa-Data-API/msg/819b0025b5ff5e38 - """ - - uri = '/data/feed/api/user/%s/contacts?kind=photo&q=%s' % (user, search) - return self.GetFeed(uri, limit=limit) - - def InsertAlbum(self, title, summary, location=None, access='public', - commenting_enabled='true', timestamp=None): - """Add an album. - - Needs authentication, see self.ClientLogin() - - Arguments: - title: Album title - summary: Album summary / description - access (optional): `private' or `public'. Public albums are searchable - by everyone on the internet. Defaults to `public' - commenting_enabled (optional): `true' or `false'. Defaults to `true'. - timestamp (optional): A date and time for the album, in milliseconds since - Unix epoch[1] UTC. Defaults to now. - - Returns: - The newly created gdata.photos.AlbumEntry - - See: - http://code.google.com/apis/picasaweb/gdata.html#Add_Album_Manual_Installed - - [1]: http://en.wikipedia.org/wiki/Unix_epoch - """ - album = gdata.photos.AlbumEntry() - album.title = atom.Title(text=title, title_type='text') - album.summary = atom.Summary(text=summary, summary_type='text') - if location is not None: - album.location = gdata.photos.Location(text=location) - album.access = gdata.photos.Access(text=access) - if commenting_enabled in ('true', 'false'): - album.commentingEnabled = gdata.photos.CommentingEnabled(text=commenting_enabled) - if timestamp is None: - timestamp = '%i' % int(time.time() * 1000) - album.timestamp = gdata.photos.Timestamp(text=timestamp) - try: - return self.Post(album, uri=self.userUri % self.email, - converter=gdata.photos.AlbumEntryFromString) - except gdata.service.RequestError, e: - raise GooglePhotosException(e.args[0]) - - def InsertPhoto(self, album_or_uri, photo, filename_or_handle, - content_type='image/jpeg'): - """Add a PhotoEntry - - Needs authentication, see self.ClientLogin() - - Arguments: - album_or_uri: AlbumFeed or uri of the album where the photo should go - photo: PhotoEntry to add - filename_or_handle: A file-like object or file name where the image/video - will be read from - content_type (optional): Internet media type (a.k.a. mime type) of - media object. Currently Google Photos supports these types: - o image/bmp - o image/gif - o image/jpeg - o image/png - - Images will be converted to jpeg on upload. Defaults to `image/jpeg' - - """ - - try: - assert(isinstance(photo, gdata.photos.PhotoEntry)) - except AssertionError: - raise GooglePhotosException({'status':GPHOTOS_INVALID_ARGUMENT, - 'body':'`photo` must be a gdata.photos.PhotoEntry instance', - 'reason':'Found %s, not PhotoEntry' % type(photo) - }) - try: - majtype, mintype = content_type.split('/') - assert(mintype in SUPPORTED_UPLOAD_TYPES) - except (ValueError, AssertionError): - raise GooglePhotosException({'status':GPHOTOS_INVALID_CONTENT_TYPE, - 'body':'This is not a valid content type: %s' % content_type, - 'reason':'Accepted content types: %s' % \ - ['image/'+t for t in SUPPORTED_UPLOAD_TYPES] - }) - if isinstance(filename_or_handle, (str, unicode)) and \ - os.path.exists(filename_or_handle): # it's a file name - mediasource = gdata.MediaSource() - mediasource.setFile(filename_or_handle, content_type) - elif hasattr(filename_or_handle, 'read'):# it's a file-like resource - if hasattr(filename_or_handle, 'seek'): - filename_or_handle.seek(0) # rewind pointer to the start of the file - # gdata.MediaSource needs the content length, so read the whole image - file_handle = StringIO.StringIO(filename_or_handle.read()) - name = 'image' - if hasattr(filename_or_handle, 'name'): - name = filename_or_handle.name - mediasource = gdata.MediaSource(file_handle, content_type, - content_length=file_handle.len, file_name=name) - else: #filename_or_handle is not valid - raise GooglePhotosException({'status':GPHOTOS_INVALID_ARGUMENT, - 'body':'`filename_or_handle` must be a path name or a file-like object', - 'reason':'Found %s, not path name or object with a .read() method' % \ - type(filename_or_handle) - }) - - if isinstance(album_or_uri, (str, unicode)): # it's a uri - feed_uri = album_or_uri - elif hasattr(album_or_uri, 'GetFeedLink'): # it's a AlbumFeed object - feed_uri = album_or_uri.GetFeedLink().href - - try: - return self.Post(photo, uri=feed_uri, media_source=mediasource, - converter=gdata.photos.PhotoEntryFromString) - except gdata.service.RequestError, e: - raise GooglePhotosException(e.args[0]) - - def InsertPhotoSimple(self, album_or_uri, title, summary, filename_or_handle, - content_type='image/jpeg', keywords=None): - """Add a photo without constructing a PhotoEntry. - - Needs authentication, see self.ClientLogin() - - Arguments: - album_or_uri: AlbumFeed or uri of the album where the photo should go - title: Photo title - summary: Photo summary / description - filename_or_handle: A file-like object or file name where the image/video - will be read from - content_type (optional): Internet media type (a.k.a. mime type) of - media object. Currently Google Photos supports these types: - o image/bmp - o image/gif - o image/jpeg - o image/png - - Images will be converted to jpeg on upload. Defaults to `image/jpeg' - keywords (optional): a 1) comma separated string or 2) a python list() of - keywords (a.k.a. tags) to add to the image. - E.g. 1) `dog, vacation, happy' 2) ['dog', 'happy', 'vacation'] - - Returns: - The newly created gdata.photos.PhotoEntry or GooglePhotosException on errors - - See: - http://code.google.com/apis/picasaweb/gdata.html#Add_Album_Manual_Installed - [1]: http://en.wikipedia.org/wiki/Unix_epoch - """ - - metadata = gdata.photos.PhotoEntry() - metadata.title=atom.Title(text=title) - metadata.summary = atom.Summary(text=summary, summary_type='text') - if keywords is not None: - if isinstance(keywords, list): - keywords = ','.join(keywords) - metadata.media.keywords = gdata.media.Keywords(text=keywords) - return self.InsertPhoto(album_or_uri, metadata, filename_or_handle, - content_type) - - def UpdatePhotoMetadata(self, photo): - """Update a photo's metadata. - - Needs authentication, see self.ClientLogin() - - You can update any or all of the following metadata properties: - * - * <media:description> - * <gphoto:checksum> - * <gphoto:client> - * <gphoto:rotation> - * <gphoto:timestamp> - * <gphoto:commentingEnabled> - - Arguments: - photo: a gdata.photos.PhotoEntry object with updated elements - - Returns: - The modified gdata.photos.PhotoEntry - - Example: - p = GetFeed(uri).entry[0] - p.title.text = u'My new text' - p.commentingEnabled.text = 'false' - p = UpdatePhotoMetadata(p) - - It is important that you don't keep the old object around, once - it has been updated. See - http://code.google.com/apis/gdata/reference.html#Optimistic-concurrency - """ - try: - return self.Put(data=photo, uri=photo.GetEditLink().href, - converter=gdata.photos.PhotoEntryFromString) - except gdata.service.RequestError, e: - raise GooglePhotosException(e.args[0]) - - - def UpdatePhotoBlob(self, photo_or_uri, filename_or_handle, - content_type = 'image/jpeg'): - """Update a photo's binary data. - - Needs authentication, see self.ClientLogin() - - Arguments: - photo_or_uri: a gdata.photos.PhotoEntry that will be updated, or a - `edit-media' uri pointing to it - filename_or_handle: A file-like object or file name where the image/video - will be read from - content_type (optional): Internet media type (a.k.a. mime type) of - media object. Currently Google Photos supports these types: - o image/bmp - o image/gif - o image/jpeg - o image/png - Images will be converted to jpeg on upload. Defaults to `image/jpeg' - - Returns: - The modified gdata.photos.PhotoEntry - - Example: - p = GetFeed(PhotoUri) - p = UpdatePhotoBlob(p, '/tmp/newPic.jpg') - - It is important that you don't keep the old object around, once - it has been updated. See - http://code.google.com/apis/gdata/reference.html#Optimistic-concurrency - """ - - try: - majtype, mintype = content_type.split('/') - assert(mintype in SUPPORTED_UPLOAD_TYPES) - except (ValueError, AssertionError): - raise GooglePhotosException({'status':GPHOTOS_INVALID_CONTENT_TYPE, - 'body':'This is not a valid content type: %s' % content_type, - 'reason':'Accepted content types: %s' % \ - ['image/'+t for t in SUPPORTED_UPLOAD_TYPES] - }) - - if isinstance(filename_or_handle, (str, unicode)) and \ - os.path.exists(filename_or_handle): # it's a file name - photoblob = gdata.MediaSource() - photoblob.setFile(filename_or_handle, content_type) - elif hasattr(filename_or_handle, 'read'):# it's a file-like resource - if hasattr(filename_or_handle, 'seek'): - filename_or_handle.seek(0) # rewind pointer to the start of the file - # gdata.MediaSource needs the content length, so read the whole image - file_handle = StringIO.StringIO(filename_or_handle.read()) - name = 'image' - if hasattr(filename_or_handle, 'name'): - name = filename_or_handle.name - mediasource = gdata.MediaSource(file_handle, content_type, - content_length=file_handle.len, file_name=name) - else: #filename_or_handle is not valid - raise GooglePhotosException({'status':GPHOTOS_INVALID_ARGUMENT, - 'body':'`filename_or_handle` must be a path name or a file-like object', - 'reason':'Found %s, not path name or an object with .read() method' % \ - type(filename_or_handle) - }) - - if isinstance(photo_or_uri, (str, unicode)): - entry_uri = photo_or_uri # it's a uri - elif hasattr(photo_or_uri, 'GetEditMediaLink'): - entry_uri = photo_or_uri.GetEditMediaLink().href - try: - return self.Put(photoblob, entry_uri, - converter=gdata.photos.PhotoEntryFromString) - except gdata.service.RequestError, e: - raise GooglePhotosException(e.args[0]) - - def InsertTag(self, photo_or_uri, tag): - """Add a tag (a.k.a. keyword) to a photo. - - Needs authentication, see self.ClientLogin() - - Arguments: - photo_or_uri: a gdata.photos.PhotoEntry that will be tagged, or a - `post' uri pointing to it - (string) tag: The tag/keyword - - Returns: - The new gdata.photos.TagEntry - - Example: - p = GetFeed(PhotoUri) - tag = InsertTag(p, 'Beautiful sunsets') - - """ - tag = gdata.photos.TagEntry(title=atom.Title(text=tag)) - if isinstance(photo_or_uri, (str, unicode)): - post_uri = photo_or_uri # it's a uri - elif hasattr(photo_or_uri, 'GetEditMediaLink'): - post_uri = photo_or_uri.GetPostLink().href - try: - return self.Post(data=tag, uri=post_uri, - converter=gdata.photos.TagEntryFromString) - except gdata.service.RequestError, e: - raise GooglePhotosException(e.args[0]) - - - def InsertComment(self, photo_or_uri, comment): - """Add a comment to a photo. - - Needs authentication, see self.ClientLogin() - - Arguments: - photo_or_uri: a gdata.photos.PhotoEntry that is about to be commented - , or a `post' uri pointing to it - (string) comment: The actual comment - - Returns: - The new gdata.photos.CommentEntry - - Example: - p = GetFeed(PhotoUri) - tag = InsertComment(p, 'OOOH! I would have loved to be there. - Who's that in the back?') - - """ - comment = gdata.photos.CommentEntry(content=atom.Content(text=comment)) - if isinstance(photo_or_uri, (str, unicode)): - post_uri = photo_or_uri # it's a uri - elif hasattr(photo_or_uri, 'GetEditMediaLink'): - post_uri = photo_or_uri.GetPostLink().href - try: - return self.Post(data=comment, uri=post_uri, - converter=gdata.photos.CommentEntryFromString) - except gdata.service.RequestError, e: - raise GooglePhotosException(e.args[0]) - - def Delete(self, object_or_uri, *args, **kwargs): - """Delete an object. - - Re-implementing the GDataService.Delete method, to add some - convenience. - - Arguments: - object_or_uri: Any object that has a GetEditLink() method that - returns a link, or a uri to that object. - - Returns: - ? or GooglePhotosException on errors - """ - try: - uri = object_or_uri.GetEditLink().href - except AttributeError: - uri = object_or_uri - try: - return gdata.service.GDataService.Delete(self, uri, *args, **kwargs) - except gdata.service.RequestError, e: - raise GooglePhotosException(e.args[0]) - -def GetSmallestThumbnail(media_thumbnail_list): - """Helper function to get the smallest thumbnail of a list of - gdata.media.Thumbnail. - Returns gdata.media.Thumbnail """ - r = {} - for thumb in media_thumbnail_list: - r[int(thumb.width)*int(thumb.height)] = thumb - keys = r.keys() - keys.sort() - return r[keys[0]] - -def ConvertAtomTimestampToEpoch(timestamp): - """Helper function to convert a timestamp string, for instance - from atom:updated or atom:published, to milliseconds since Unix epoch - (a.k.a. POSIX time). - - `2007-07-22T00:45:10.000Z' -> """ - return time.mktime(time.strptime(timestamp, '%Y-%m-%dT%H:%M:%S.000Z')) - ## TODO: Timezone aware diff --git a/gdata/projecthosting/__init__.py b/gdata/projecthosting/__init__.py deleted file mode 100644 index 8b13789179..0000000000 --- a/gdata/projecthosting/__init__.py +++ /dev/null @@ -1 +0,0 @@ - diff --git a/gdata/projecthosting/client.py b/gdata/projecthosting/client.py deleted file mode 100644 index 8e36a7c3c1..0000000000 --- a/gdata/projecthosting/client.py +++ /dev/null @@ -1,200 +0,0 @@ -#!/usr/bin/env python -# -# Copyright 2009 Google 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. - -import atom.data -import gdata.client -import gdata.gauth -import gdata.projecthosting.data - - -class ProjectHostingClient(gdata.client.GDClient): - """Client to interact with the Project Hosting GData API.""" - api_version = '1.0' - auth_service = 'code' - auth_scopes = gdata.gauth.AUTH_SCOPES['code'] - host = 'code.google.com' - - def get_issues(self, project_name, - desired_class=gdata.projecthosting.data.IssuesFeed, **kwargs): - """Get a feed of issues for a particular project. - - Args: - project_name str The name of the project. - query Query Set returned issues parameters. - - Returns: - data.IssuesFeed - """ - return self.get_feed(gdata.projecthosting.data.ISSUES_FULL_FEED % - project_name, desired_class=desired_class, **kwargs) - - def add_issue(self, project_name, title, content, author, - status=None, owner=None, labels=None, ccs=None, **kwargs): - """Create a new issue for the project. - - Args: - project_name str The name of the project. - title str The title of the new issue. - content str The summary of the new issue. - author str The authenticated user's username. - status str The status of the new issue, Accepted, etc. - owner str The username of new issue's owner. - labels [str] Labels to associate with the new issue. - ccs [str] usernames to Cc on the new issue. - Returns: - data.IssueEntry - """ - new_entry = gdata.projecthosting.data.IssueEntry( - title=atom.data.Title(text=title), - content=atom.data.Content(text=content), - author=[atom.data.Author(name=atom.data.Name(text=author))]) - - if status: - new_entry.status = gdata.projecthosting.data.Status(text=status) - - if owner: - owner = [gdata.projecthosting.data.Owner( - username=gdata.projecthosting.data.Username(text=owner))] - - if labels: - new_entry.label = [gdata.projecthosting.data.Label(text=label) - for label in labels] - if ccs: - new_entry.cc = [ - gdata.projecthosting.data.Cc( - username=gdata.projecthosting.data.Username(text=cc)) - for cc in ccs] - - return self.post( - new_entry, - gdata.projecthosting.data.ISSUES_FULL_FEED % project_name, - **kwargs) - - def update_issue(self, project_name, issue_id, author, comment=None, - summary=None, status=None, owner=None, labels=None, ccs=None, - **kwargs): - """Update or comment on one issue for the project. - - Args: - project_name str The name of the issue's project. - issue_id str The issue number needing updated. - author str The authenticated user's username. - comment str A comment to append to the issue - summary str Rewrite the summary of the issue. - status str A new status for the issue. - owner str The username of the new owner. - labels [str] Labels to set on the issue (prepend issue with - to remove a - label). - ccs [str] Ccs to set on th enew issue (prepend cc with - to remove a cc). - - Returns: - data.CommentEntry - """ - updates = gdata.projecthosting.data.Updates() - - if summary: - updates.summary = gdata.projecthosting.data.Summary(text=summary) - - if status: - updates.status = gdata.projecthosting.data.Status(text=status) - - if owner: - updates.ownerUpdate = gdata.projecthosting.data.OwnerUpdate(text=owner) - - if labels: - updates.label = [gdata.projecthosting.data.Label(text=label) - for label in labels] - if ccs: - updates.ccUpdate = [gdata.projecthosting.data.CcUpdate(text=cc) - for cc in ccs] - - update_entry = gdata.projecthosting.data.CommentEntry( - content=atom.data.Content(text=comment), - author=[atom.data.Author(name=atom.data.Name(text=author))], - updates=updates) - - return self.post( - update_entry, - gdata.projecthosting.data.COMMENTS_FULL_FEED % (project_name, issue_id), - **kwargs) - - def get_comments(self, project_name, issue_id, - desired_class=gdata.projecthosting.data.CommentsFeed, - **kwargs): - """Get a feed of all updates to an issue. - - Args: - project_name str The name of the issue's project. - issue_id str The issue number needing updated. - - Returns: - data.CommentsFeed - """ - return self.get_feed( - gdata.projecthosting.data.COMMENTS_FULL_FEED % (project_name, issue_id), - desired_class=desired_class, **kwargs) - - def update(self, entry, auth_token=None, force=False, **kwargs): - """Unsupported GData update method. - - Use update_*() instead. - """ - raise NotImplementedError( - 'GData Update operation unsupported, try update_*') - - def delete(self, entry_or_uri, auth_token=None, force=False, **kwargs): - """Unsupported GData delete method. - - Use update_issue(status='Closed') instead. - """ - raise NotImplementedError( - 'GData Delete API unsupported, try closing the issue instead.') - - -class Query(gdata.client.Query): - - def __init__(self, issue_id=None, label=None, canned_query=None, owner=None, - status=None, **kwargs): - """Constructs a Google Data Query to filter feed contents serverside. - Args: - issue_id: int or str The issue to return based on the issue id. - label: str A label returned issues must have. - canned_query: str Return issues based on a canned query identifier - owner: str Return issues based on the owner of the issue. For Gmail users, - this will be the part of the email preceding the '@' sign. - status: str Return issues based on the status of the issue. - """ - super(Query, self).__init__(**kwargs) - self.label = label - self.issue_id = issue_id - self.canned_query = canned_query - self.owner = owner - self.status = status - - def modify_request(self, http_request): - if self.issue_id: - gdata.client._add_query_param('id', self.issue_id, http_request) - if self.label: - gdata.client._add_query_param('label', self.label, http_request) - if self.canned_query: - gdata.client._add_query_param('can', self.canned_query, http_request) - if self.owner: - gdata.client._add_query_param('owner', self.owner, http_request) - if self.status: - gdata.client._add_query_param('status', self.status, http_request) - super(Query, self).modify_request(http_request) - - ModifyRequest = modify_request diff --git a/gdata/projecthosting/data.py b/gdata/projecthosting/data.py deleted file mode 100644 index b0af2f5f71..0000000000 --- a/gdata/projecthosting/data.py +++ /dev/null @@ -1,134 +0,0 @@ -#!/usr/bin/env python -# -# Copyright 2009 Google 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. - - -# This module is used for version 2 of the Google Data APIs. - - -"""Provides classes and constants for XML in the Google Project Hosting API. - -Canonical documentation for the raw XML which these classes represent can be -found here: http://code.google.com/p/support/wiki/IssueTrackerAPI -""" - - -__author__ = 'jlapenna@google.com (Joe LaPenna)' - -import atom.core -import gdata.data - - -ISSUES_TEMPLATE = '{http://schemas.google.com/projecthosting/issues/2009}%s' - - -ISSUES_FULL_FEED = '/feeds/issues/p/%s/issues/full' -COMMENTS_FULL_FEED = '/feeds/issues/p/%s/issues/%s/comments/full' - - -class Uri(atom.core.XmlElement): - """The issues:uri element.""" - _qname = ISSUES_TEMPLATE % 'uri' - - -class Username(atom.core.XmlElement): - """The issues:username element.""" - _qname = ISSUES_TEMPLATE % 'username' - - -class Cc(atom.core.XmlElement): - """The issues:cc element.""" - _qname = ISSUES_TEMPLATE % 'cc' - uri = Uri - username = Username - - -class Label(atom.core.XmlElement): - """The issues:label element.""" - _qname = ISSUES_TEMPLATE % 'label' - - -class Owner(atom.core.XmlElement): - """The issues:owner element.""" - _qname = ISSUES_TEMPLATE % 'owner' - uri = Uri - username = Username - - -class Stars(atom.core.XmlElement): - """The issues:stars element.""" - _qname = ISSUES_TEMPLATE % 'stars' - - -class State(atom.core.XmlElement): - """The issues:state element.""" - _qname = ISSUES_TEMPLATE % 'state' - - -class Status(atom.core.XmlElement): - """The issues:status element.""" - _qname = ISSUES_TEMPLATE % 'status' - - -class Summary(atom.core.XmlElement): - """The issues:summary element.""" - _qname = ISSUES_TEMPLATE % 'summary' - - -class OwnerUpdate(atom.core.XmlElement): - """The issues:ownerUpdate element.""" - _qname = ISSUES_TEMPLATE % 'ownerUpdate' - - -class CcUpdate(atom.core.XmlElement): - """The issues:ccUpdate element.""" - _qname = ISSUES_TEMPLATE % 'ccUpdate' - - -class Updates(atom.core.XmlElement): - """The issues:updates element.""" - _qname = ISSUES_TEMPLATE % 'updates' - summary = Summary - status = Status - ownerUpdate = OwnerUpdate - label = [Label] - ccUpdate = [CcUpdate] - - -class IssueEntry(gdata.data.GDEntry): - """Represents the information of one issue.""" - _qname = atom.data.ATOM_TEMPLATE % 'entry' - owner = Owner - cc = [Cc] - label = [Label] - stars = Stars - state = State - status = Status - - -class IssuesFeed(gdata.data.GDFeed): - """An Atom feed listing a project's issues.""" - entry = [IssueEntry] - - -class CommentEntry(gdata.data.GDEntry): - """An entry detailing one comment on an issue.""" - _qname = atom.data.ATOM_TEMPLATE % 'entry' - updates = Updates - - -class CommentsFeed(gdata.data.GDFeed): - """An Atom feed listing a project's issue's comments.""" - entry = [CommentEntry] diff --git a/gdata/sample_util.py b/gdata/sample_util.py deleted file mode 100644 index aae866e2af..0000000000 --- a/gdata/sample_util.py +++ /dev/null @@ -1,269 +0,0 @@ -#!/usr/bin/env python -# -# Copyright (C) 2009 Google 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. - - -"""Provides utility functions used with command line samples.""" - -# This module is used for version 2 of the Google Data APIs. - -import sys -import getpass -import urllib -import gdata.gauth - -__author__ = 'j.s@google.com (Jeff Scudder)' - - -CLIENT_LOGIN = 1 -AUTHSUB = 2 -OAUTH = 3 - -HMAC = 1 -RSA = 2 - - -class SettingsUtil(object): - """Gather's user preferences from flags or command prompts. - - An instance of this object stores the choices made by the user. At some - point it might be useful to save the user's preferences so that they do - not need to always set flags or answer preference prompts. - """ - - def __init__(self, prefs=None): - self.prefs = prefs or {} - - def get_param(self, name, prompt='', secret=False, ask=True, reuse=False): - # First, check in this objects stored preferences. - if name in self.prefs: - return self.prefs[name] - # Second, check for a command line parameter. - value = None - for i in xrange(len(sys.argv)): - if sys.argv[i].startswith('--%s=' % name): - value = sys.argv[i].split('=')[1] - elif sys.argv[i] == '--%s' % name: - value = sys.argv[i + 1] - # Third, if it was not on the command line, ask the user to input the - # value. - if value is None and ask: - prompt = '%s: ' % prompt - if secret: - value = getpass.getpass(prompt) - else: - value = raw_input(prompt) - # If we want to save the preference for reuse in future requests, add it - # to this object's prefs. - if value is not None and reuse: - self.prefs[name] = value - return value - - def authorize_client(self, client, auth_type=None, service=None, - source=None, scopes=None, oauth_type=None, - consumer_key=None, consumer_secret=None): - """Uses command line arguments, or prompts user for token values.""" - if 'client_auth_token' in self.prefs: - return - if auth_type is None: - auth_type = int(self.get_param( - 'auth_type', 'Please choose the authorization mechanism you want' - ' to use.\n' - '1. to use your email address and password (ClientLogin)\n' - '2. to use a web browser to visit an auth web page (AuthSub)\n' - '3. if you have registed to use OAuth\n', reuse=True)) - - # Get the scopes for the services we want to access. - if auth_type == AUTHSUB or auth_type == OAUTH: - if scopes is None: - scopes = self.get_param( - 'scopes', 'Enter the URL prefixes (scopes) for the resources you ' - 'would like to access.\nFor multiple scope URLs, place a comma ' - 'between each URL.\n' - 'Example: http://www.google.com/calendar/feeds/,' - 'http://www.google.com/m8/feeds/\n', reuse=True).split(',') - elif isinstance(scopes, (str, unicode)): - scopes = scopes.split(',') - - if auth_type == CLIENT_LOGIN: - email = self.get_param('email', 'Please enter your username', - reuse=False) - password = self.get_param('password', 'Password', True, reuse=False) - if service is None: - service = self.get_param( - 'service', 'What is the name of the service you wish to access?' - '\n(See list:' - ' http://code.google.com/apis/gdata/faq.html#clientlogin)', - reuse=True) - if source is None: - source = self.get_param('source', ask=False, reuse=True) - client.client_login(email, password, source=source, service=service) - elif auth_type == AUTHSUB: - auth_sub_token = self.get_param('auth_sub_token', ask=False, reuse=True) - session_token = self.get_param('session_token', ask=False, reuse=True) - private_key = None - auth_url = None - single_use_token = None - rsa_private_key = self.get_param( - 'rsa_private_key', - 'If you want to use secure mode AuthSub, please provide the\n' - ' location of your RSA private key which corresponds to the\n' - ' certificate you have uploaded for your domain. If you do not\n' - ' have an RSA key, simply press enter', reuse=True) - - if rsa_private_key: - try: - private_key_file = open(rsa_private_key, 'rb') - private_key = private_key_file.read() - private_key_file.close() - except IOError: - print 'Unable to read private key from file' - - if private_key is not None: - if client.auth_token is None: - if session_token: - client.auth_token = gdata.gauth.SecureAuthSubToken( - session_token, private_key, scopes) - self.prefs['client_auth_token'] = gdata.gauth.token_to_blob( - client.auth_token) - return - elif auth_sub_token: - client.auth_token = gdata.gauth.SecureAuthSubToken( - auth_sub_token, private_key, scopes) - client.upgrade_token() - self.prefs['client_auth_token'] = gdata.gauth.token_to_blob( - client.auth_token) - return - - auth_url = gdata.gauth.generate_auth_sub_url( - 'http://gauthmachine.appspot.com/authsub', scopes, True) - print 'with a private key, get ready for this URL', auth_url - - else: - if client.auth_token is None: - if session_token: - client.auth_token = gdata.gauth.AuthSubToken(session_token, - scopes) - self.prefs['client_auth_token'] = gdata.gauth.token_to_blob( - client.auth_token) - return - elif auth_sub_token: - client.auth_token = gdata.gauth.AuthSubToken(auth_sub_token, - scopes) - client.upgrade_token() - self.prefs['client_auth_token'] = gdata.gauth.token_to_blob( - client.auth_token) - return - - auth_url = gdata.gauth.generate_auth_sub_url( - 'http://gauthmachine.appspot.com/authsub', scopes) - - print 'Visit the following URL in your browser to authorize this app:' - print str(auth_url) - print 'After agreeing to authorize the app, copy the token value from' - print ' the URL. Example: "www.google.com/?token=ab12" token value is' - print ' ab12' - token_value = raw_input('Please enter the token value: ') - if private_key is not None: - single_use_token = gdata.gauth.SecureAuthSubToken( - token_value, private_key, scopes) - else: - single_use_token = gdata.gauth.AuthSubToken(token_value, scopes) - client.auth_token = single_use_token - client.upgrade_token() - - elif auth_type == OAUTH: - if oauth_type is None: - oauth_type = int(self.get_param( - 'oauth_type', 'Please choose the authorization mechanism you want' - ' to use.\n' - '1. use an HMAC signature using your consumer key and secret\n' - '2. use RSA with your private key to sign requests\n', - reuse=True)) - - consumer_key = self.get_param( - 'consumer_key', 'Please enter your OAuth conumer key ' - 'which identifies your app', reuse=True) - - if oauth_type == HMAC: - consumer_secret = self.get_param( - 'consumer_secret', 'Please enter your OAuth conumer secret ' - 'which you share with the OAuth provider', True, reuse=False) - # Swap out this code once the client supports requesting an oauth - # token. - # Get a request token. - request_token = client.get_oauth_token( - scopes, 'http://gauthmachine.appspot.com/oauth', consumer_key, - consumer_secret=consumer_secret) - elif oauth_type == RSA: - rsa_private_key = self.get_param( - 'rsa_private_key', - 'Please provide the location of your RSA private key which\n' - ' corresponds to the certificate you have uploaded for your' - ' domain.', - reuse=True) - try: - private_key_file = open(rsa_private_key, 'rb') - private_key = private_key_file.read() - private_key_file.close() - except IOError: - print 'Unable to read private key from file' - - request_token = client.get_oauth_token( - scopes, 'http://gauthmachine.appspot.com/oauth', consumer_key, - rsa_private_key=private_key) - else: - print 'Invalid OAuth signature type' - return None - - # Authorize the request token in the browser. - print 'Visit the following URL in your browser to authorize this app:' - print str(request_token.generate_authorization_url()) - print 'After agreeing to authorize the app, copy URL from the browser\'s' - print ' address bar.' - url = raw_input('Please enter the url: ') - gdata.gauth.authorize_request_token(request_token, url) - # Exchange for an access token. - client.auth_token = client.get_access_token(request_token) - else: - print 'Invalid authorization type.' - return None - if client.auth_token: - self.prefs['client_auth_token'] = gdata.gauth.token_to_blob( - client.auth_token) - - -def get_param(name, prompt='', secret=False, ask=True): - settings = SettingsUtil() - return settings.get_param(name=name, prompt=prompt, secret=secret, ask=ask) - - -def authorize_client(client, auth_type=None, service=None, source=None, - scopes=None, oauth_type=None, consumer_key=None, - consumer_secret=None): - """Uses command line arguments, or prompts user for token values.""" - settings = SettingsUtil() - return settings.authorize_client(client=client, auth_type=auth_type, - service=service, source=source, - scopes=scopes, oauth_type=oauth_type, - consumer_key=consumer_key, - consumer_secret=consumer_secret) - - -def print_options(): - """Displays usage information, available command line params.""" - # TODO: fill in the usage description for authorizing the client. - print '' - diff --git a/gdata/sites/__init__.py b/gdata/sites/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/gdata/sites/client.py b/gdata/sites/client.py deleted file mode 100644 index 4c38722f87..0000000000 --- a/gdata/sites/client.py +++ /dev/null @@ -1,461 +0,0 @@ -#!/usr/bin/python -# -# Copyright 2009 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. - -"""SitesClient extends gdata.client.GDClient to streamline Sites API calls.""" - - -__author__ = 'e.bidelman (Eric Bidelman)' - -import atom.data -import gdata.client -import gdata.sites.data -import gdata.gauth - - -# Feed URI templates -CONTENT_FEED_TEMPLATE = '/feeds/content/%s/%s/' -REVISION_FEED_TEMPLATE = '/feeds/revision/%s/%s/' -ACTIVITY_FEED_TEMPLATE = '/feeds/activity/%s/%s/' -SITE_FEED_TEMPLATE = '/feeds/site/%s/' -ACL_FEED_TEMPLATE = '/feeds/acl/site/%s/%s/' - - -class SitesClient(gdata.client.GDClient): - - """Client extension for the Google Sites API service.""" - - host = 'sites.google.com' # default server for the API - domain = 'site' # default site domain name - api_version = '1.1' # default major version for the service. - auth_service = 'jotspot' - auth_scopes = gdata.gauth.AUTH_SCOPES['jotspot'] - - def __init__(self, site=None, domain=None, auth_token=None, **kwargs): - """Constructs a new client for the Sites API. - - Args: - site: string (optional) Name (webspace) of the Google Site - domain: string (optional) Domain of the (Google Apps hosted) Site. - If no domain is given, the Site is assumed to be a consumer Google - Site, in which case the value 'site' is used. - auth_token: (optional) gdata.gauth.ClientLoginToken, AuthSubToken, or - OAuthToken which 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) - self.site = site - if domain is not None: - self.domain = domain - - def __make_kind_category(self, label): - if label is None: - return None - return atom.data.Category( - scheme=gdata.sites.data.SITES_KIND_SCHEME, - term='%s#%s' % (gdata.sites.data.SITES_NAMESPACE, label), label=label) - - __MakeKindCategory = __make_kind_category - - def __upload(self, entry, media_source, auth_token=None, **kwargs): - """Uploads an attachment file to the Sites API. - - Args: - entry: gdata.sites.data.ContentEntry The Atom XML to include. - media_source: gdata.data.MediaSource The file payload to be uploaded. - auth_token: (optional) gdata.gauth.ClientLoginToken, AuthSubToken, or - OAuthToken which authorizes this client to edit the user's data. - kwargs: Other parameters to pass to gdata.client.post(). - - Returns: - The created entry. - """ - uri = self.make_content_feed_uri() - return self.post(entry, uri, media_source=media_source, - auth_token=auth_token, **kwargs) - - def _get_file_content(self, uri): - """Fetches the file content from the specified URI. - - Args: - uri: string The full URL to fetch the file contents from. - - Returns: - The binary file content. - - Raises: - gdata.client.RequestError: on error response from server. - """ - server_response = self.request('GET', uri) - if server_response.status != 200: - raise gdata.client.RequestError, {'status': server_response.status, - 'reason': server_response.reason, - 'body': server_response.read()} - return server_response.read() - - _GetFileContent = _get_file_content - - def make_content_feed_uri(self): - return CONTENT_FEED_TEMPLATE % (self.domain, self.site) - - MakeContentFeedUri = make_content_feed_uri - - def make_revision_feed_uri(self): - return REVISION_FEED_TEMPLATE % (self.domain, self.site) - - MakeRevisionFeedUri = make_revision_feed_uri - - def make_activity_feed_uri(self): - return ACTIVITY_FEED_TEMPLATE % (self.domain, self.site) - - MakeActivityFeedUri = make_activity_feed_uri - - def make_site_feed_uri(self, site_name=None): - if site_name is not None: - return (SITE_FEED_TEMPLATE % self.domain) + site_name - else: - return SITE_FEED_TEMPLATE % self.domain - - MakeSiteFeedUri = make_site_feed_uri - - def make_acl_feed_uri(self): - return ACL_FEED_TEMPLATE % (self.domain, self.site) - - MakeAclFeedUri = make_acl_feed_uri - - def get_content_feed(self, uri=None, auth_token=None, **kwargs): - """Retrieves the content feed containing the current state of site. - - Args: - uri: string (optional) A full URI to query the Content feed with. - auth_token: (optional) gdata.gauth.ClientLoginToken, AuthSubToken, or - OAuthToken which authorizes this client to edit the user's data. - kwargs: Other parameters to pass to self.get_feed(). - - Returns: - gdata.sites.data.ContentFeed - """ - if uri is None: - uri = self.make_content_feed_uri() - return self.get_feed(uri, desired_class=gdata.sites.data.ContentFeed, - auth_token=auth_token, **kwargs) - - GetContentFeed = get_content_feed - - def get_revision_feed(self, entry_or_uri_or_id, auth_token=None, **kwargs): - """Retrieves the revision feed containing the revision history for a node. - - Args: - entry_or_uri_or_id: string or gdata.sites.data.ContentEntry A full URI, - content entry node ID, or a content entry object of the entry to - retrieve revision information for. - auth_token: (optional) gdata.gauth.ClientLoginToken, AuthSubToken, or - OAuthToken which authorizes this client to edit the user's data. - kwargs: Other parameters to pass to self.get_feed(). - - Returns: - gdata.sites.data.RevisionFeed - """ - uri = self.make_revision_feed_uri() - if isinstance(entry_or_uri_or_id, gdata.sites.data.ContentEntry): - uri = entry_or_uri_or_id.FindRevisionLink() - elif entry_or_uri_or_id.find('/') == -1: - uri += entry_or_uri_or_id - else: - uri = entry_or_uri_or_id - return self.get_feed(uri, desired_class=gdata.sites.data.RevisionFeed, - auth_token=auth_token, **kwargs) - - GetRevisionFeed = get_revision_feed - - def get_activity_feed(self, uri=None, auth_token=None, **kwargs): - """Retrieves the activity feed containing recent Site activity. - - Args: - uri: string (optional) A full URI to query the Activity feed. - auth_token: (optional) gdata.gauth.ClientLoginToken, AuthSubToken, or - OAuthToken which authorizes this client to edit the user's data. - kwargs: Other parameters to pass to self.get_feed(). - - Returns: - gdata.sites.data.ActivityFeed - """ - if uri is None: - uri = self.make_activity_feed_uri() - return self.get_feed(uri, desired_class=gdata.sites.data.ActivityFeed, - auth_token=auth_token, **kwargs) - - GetActivityFeed = get_activity_feed - - def get_site_feed(self, uri=None, auth_token=None, **kwargs): - """Retrieves the site feed containing a list of sites a user has access to. - - Args: - uri: string (optional) A full URI to query the site feed. - auth_token: (optional) gdata.gauth.ClientLoginToken, AuthSubToken, or - OAuthToken which authorizes this client to edit the user's data. - kwargs: Other parameters to pass to self.get_feed(). - - Returns: - gdata.sites.data.SiteFeed - """ - if uri is None: - uri = self.make_site_feed_uri() - return self.get_feed(uri, desired_class=gdata.sites.data.SiteFeed, - auth_token=auth_token, **kwargs) - - GetSiteFeed = get_site_feed - - def get_acl_feed(self, uri=None, auth_token=None, **kwargs): - """Retrieves the acl feed containing a site's sharing permissions. - - Args: - uri: string (optional) A full URI to query the acl feed. - auth_token: (optional) gdata.gauth.ClientLoginToken, AuthSubToken, or - OAuthToken which authorizes this client to edit the user's data. - kwargs: Other parameters to pass to self.get_feed(). - - Returns: - gdata.sites.data.AclFeed - """ - if uri is None: - uri = self.make_acl_feed_uri() - return self.get_feed(uri, desired_class=gdata.sites.data.AclFeed, - auth_token=auth_token, **kwargs) - - GetAclFeed = get_acl_feed - - def create_site(self, title, description=None, source_site=None, - theme=None, uri=None, auth_token=None, **kwargs): - """Creates a new Google Site. - - Note: This feature is only available to Google Apps domains. - - Args: - title: string Title for the site. - description: string (optional) A description/summary for the site. - source_site: string (optional) The site feed URI of the site to copy. - This parameter should only be specified when copying a site. - theme: string (optional) The name of the theme to create the site with. - uri: string (optional) A full site feed URI to override where the site - is created/copied. By default, the site will be created under - the currently set domain (e.g. self.domain). - auth_token: (optional) gdata.gauth.ClientLoginToken, AuthSubToken, or - OAuthToken which authorizes this client to edit the user's data. - kwargs: Other parameters to pass to gdata.client.post(). - - Returns: - gdata.sites.data.SiteEntry of the created site. - """ - new_entry = gdata.sites.data.SiteEntry(title=atom.data.Title(text=title)) - - if description is not None: - new_entry.summary = gdata.sites.data.Summary(text=description) - - # Add the source link if we're making a copy of a site. - if source_site is not None: - source_link = atom.data.Link(rel=gdata.sites.data.SITES_SOURCE_LINK_REL, - type='application/atom+xml', - href=source_site) - new_entry.link.append(source_link) - - if theme is not None: - new_entry.theme = gdata.sites.data.Theme(text=theme) - - if uri is None: - uri = self.make_site_feed_uri() - - return self.post(new_entry, uri, auth_token=auth_token, **kwargs) - - CreateSite = create_site - - def create_page(self, kind, title, html='', page_name=None, parent=None, - auth_token=None, **kwargs): - """Creates a new page (specified by kind) on a Google Site. - - Args: - kind: string The type of page/item to create. For example, webpage, - listpage, comment, announcementspage, filecabinet, etc. The full list - of supported kinds can be found in gdata.sites.gdata.SUPPORT_KINDS. - title: string Title for the page. - html: string (optional) XHTML for the page's content body. - page_name: string (optional) The URL page name to set. If not set, the - title will be normalized and used as the page's URL path. - parent: string or gdata.sites.data.ContentEntry (optional) The parent - entry or parent link url to create the page under. - auth_token: (optional) gdata.gauth.ClientLoginToken, AuthSubToken, or - OAuthToken which authorizes this client to edit the user's data. - kwargs: Other parameters to pass to gdata.client.post(). - - Returns: - gdata.sites.data.ContentEntry of the created page. - """ - new_entry = gdata.sites.data.ContentEntry( - title=atom.data.Title(text=title), kind=kind, - content=gdata.sites.data.Content(text=html)) - - if page_name is not None: - new_entry.page_name = gdata.sites.data.PageName(text=page_name) - - # Add parent link to entry if it should be uploaded as a subpage. - if isinstance(parent, gdata.sites.data.ContentEntry): - parent_link = atom.data.Link(rel=gdata.sites.data.SITES_PARENT_LINK_REL, - type='application/atom+xml', - href=parent.GetSelfLink().href) - new_entry.link.append(parent_link) - elif parent is not None: - parent_link = atom.data.Link(rel=gdata.sites.data.SITES_PARENT_LINK_REL, - type='application/atom+xml', - href=parent) - new_entry.link.append(parent_link) - - return self.post(new_entry, self.make_content_feed_uri(), - auth_token=auth_token, **kwargs) - - CreatePage = create_page - - def create_webattachment(self, src, content_type, title, parent, - description=None, auth_token=None, **kwargs): - """Creates a new webattachment within a filecabinet. - - Args: - src: string The url of the web attachment. - content_type: string The MIME type of the web attachment. - title: string The title to name the web attachment. - parent: string or gdata.sites.data.ContentEntry (optional) The - parent entry or url of the filecabinet to create the attachment under. - description: string (optional) A summary/description for the attachment. - auth_token: (optional) gdata.gauth.ClientLoginToken, AuthSubToken, or - OAuthToken which authorizes this client to edit the user's data. - kwargs: Other parameters to pass to gdata.client.post(). - - Returns: - gdata.sites.data.ContentEntry of the created page. - """ - new_entry = gdata.sites.data.ContentEntry( - title=atom.data.Title(text=title), kind='webattachment', - content=gdata.sites.data.Content(src=src, type=content_type)) - - if isinstance(parent, gdata.sites.data.ContentEntry): - link = atom.data.Link(rel=gdata.sites.data.SITES_PARENT_LINK_REL, - type='application/atom+xml', - href=parent.GetSelfLink().href) - elif parent is not None: - link = atom.data.Link(rel=gdata.sites.data.SITES_PARENT_LINK_REL, - type='application/atom+xml', href=parent) - - new_entry.link.append(link) - - # Add file decription if it was specified - if description is not None: - new_entry.summary = gdata.sites.data.Summary(type='text', - text=description) - - return self.post(new_entry, self.make_content_feed_uri(), - auth_token=auth_token, **kwargs) - - CreateWebAttachment = create_webattachment - - def upload_attachment(self, file_handle, parent, content_type=None, - title=None, description=None, folder_name=None, - auth_token=None, **kwargs): - """Uploads an attachment to a parent page. - - Args: - file_handle: MediaSource or string A gdata.data.MediaSource object - containing the file to be uploaded or the full path name to the - file on disk. - parent: gdata.sites.data.ContentEntry or string The parent page to - upload the file to or the full URI of the entry's self link. - content_type: string (optional) The MIME type of the file - (e.g 'application/pdf'). This should be provided if file is not a - MediaSource object. - title: string (optional) The title to name the attachment. If not - included, the filepath or media source's filename is used. - description: string (optional) A summary/description for the attachment. - folder_name: string (optional) The name of an existing folder to upload - the attachment to. This only applies when the parent parameter points - to a filecabinet entry. - auth_token: (optional) gdata.gauth.ClientLoginToken, AuthSubToken, or - OAuthToken which authorizes this client to edit the user's data. - kwargs: Other parameters to pass to self.__upload(). - - Returns: - A gdata.sites.data.ContentEntry containing information about the created - attachment. - """ - if isinstance(parent, gdata.sites.data.ContentEntry): - link = atom.data.Link(rel=gdata.sites.data.SITES_PARENT_LINK_REL, - type='application/atom+xml', - href=parent.GetSelfLink().href) - else: - link = atom.data.Link(rel=gdata.sites.data.SITES_PARENT_LINK_REL, - type='application/atom+xml', - href=parent) - - if not isinstance(file_handle, gdata.data.MediaSource): - ms = gdata.data.MediaSource(file_path=file_handle, - content_type=content_type) - else: - ms = file_handle - - # If no title specified, use the file name - if title is None: - title = ms.file_name - - new_entry = gdata.sites.data.ContentEntry(kind='attachment') - new_entry.title = atom.data.Title(text=title) - new_entry.link.append(link) - - # Add file decription if it was specified - if description is not None: - new_entry.summary = gdata.sites.data.Summary(type='text', - text=description) - - # Upload the attachment to a filecabinet folder? - if parent.Kind() == 'filecabinet' and folder_name is not None: - folder_category = atom.data.Category( - scheme=gdata.sites.data.FOLDER_KIND_TERM, term=folder_name) - new_entry.category.append(folder_category) - - return self.__upload(new_entry, ms, auth_token=auth_token, **kwargs) - - UploadAttachment = upload_attachment - - def download_attachment(self, uri_or_entry, file_path): - """Downloads an attachment file to disk. - - Args: - uri_or_entry: string The full URL to download the file from. - file_path: string The full path to save the file to. - - Raises: - gdata.client.RequestError: on error response from server. - """ - uri = uri_or_entry - if isinstance(uri_or_entry, gdata.sites.data.ContentEntry): - uri = uri_or_entry.content.src - - f = open(file_path, 'wb') - try: - f.write(self._get_file_content(uri)) - except gdata.client.RequestError, e: - f.close() - raise e - f.flush() - f.close() - - DownloadAttachment = download_attachment diff --git a/gdata/sites/data.py b/gdata/sites/data.py deleted file mode 100644 index dc8dfb2db6..0000000000 --- a/gdata/sites/data.py +++ /dev/null @@ -1,376 +0,0 @@ -#!/usr/bin/python -# -# Copyright 2009 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 the Sites Data API.""" - -__author__ = 'e.bidelman (Eric Bidelman)' - - -import atom.core -import atom.data -import gdata.acl.data -import gdata.data - -# XML Namespaces used in Google Sites entities. -SITES_NAMESPACE = 'http://schemas.google.com/sites/2008' -SITES_TEMPLATE = '{http://schemas.google.com/sites/2008}%s' -SPREADSHEETS_NAMESPACE = 'http://schemas.google.com/spreadsheets/2006' -SPREADSHEETS_TEMPLATE = '{http://schemas.google.com/spreadsheets/2006}%s' -DC_TERMS_TEMPLATE = '{http://purl.org/dc/terms}%s' -THR_TERMS_TEMPLATE = '{http://purl.org/syndication/thread/1.0}%s' -XHTML_NAMESPACE = 'http://www.w3.org/1999/xhtml' -XHTML_TEMPLATE = '{http://www.w3.org/1999/xhtml}%s' - -SITES_PARENT_LINK_REL = SITES_NAMESPACE + '#parent' -SITES_REVISION_LINK_REL = SITES_NAMESPACE + '#revision' -SITES_SOURCE_LINK_REL = SITES_NAMESPACE + '#source' - -SITES_KIND_SCHEME = 'http://schemas.google.com/g/2005#kind' -ANNOUNCEMENT_KIND_TERM = SITES_NAMESPACE + '#announcement' -ANNOUNCEMENT_PAGE_KIND_TERM = SITES_NAMESPACE + '#announcementspage' -ATTACHMENT_KIND_TERM = SITES_NAMESPACE + '#attachment' -COMMENT_KIND_TERM = SITES_NAMESPACE + '#comment' -FILECABINET_KIND_TERM = SITES_NAMESPACE + '#filecabinet' -LISTITEM_KIND_TERM = SITES_NAMESPACE + '#listitem' -LISTPAGE_KIND_TERM = SITES_NAMESPACE + '#listpage' -WEBPAGE_KIND_TERM = SITES_NAMESPACE + '#webpage' -WEBATTACHMENT_KIND_TERM = SITES_NAMESPACE + '#webattachment' -FOLDER_KIND_TERM = SITES_NAMESPACE + '#folder' - -SUPPORT_KINDS = [ - 'announcement', 'announcementspage', 'attachment', 'comment', 'filecabinet', - 'listitem', 'listpage', 'webpage', 'webattachment' - ] - - -class Revision(atom.core.XmlElement): - """Google Sites <sites:revision>.""" - _qname = SITES_TEMPLATE % 'revision' - - -class PageName(atom.core.XmlElement): - """Google Sites <sites:pageName>.""" - _qname = SITES_TEMPLATE % 'pageName' - - -class SiteName(atom.core.XmlElement): - """Google Sites <sites:siteName>.""" - _qname = SITES_TEMPLATE % 'siteName' - - -class Theme(atom.core.XmlElement): - """Google Sites <sites:theme>.""" - _qname = SITES_TEMPLATE % 'theme' - - -class Deleted(atom.core.XmlElement): - """Google Sites <gd:deleted>.""" - _qname = gdata.data.GDATA_TEMPLATE % 'deleted' - - -class Publisher(atom.core.XmlElement): - """Google Sites <dc:pulisher>.""" - _qname = DC_TERMS_TEMPLATE % 'publisher' - - -class Worksheet(atom.core.XmlElement): - """Google Sites List Page <gs:worksheet>.""" - - _qname = SPREADSHEETS_TEMPLATE % 'worksheet' - name = 'name' - - -class Header(atom.core.XmlElement): - """Google Sites List Page <gs:header>.""" - - _qname = SPREADSHEETS_TEMPLATE % 'header' - row = 'row' - - -class Column(atom.core.XmlElement): - """Google Sites List Page <gs:column>.""" - - _qname = SPREADSHEETS_TEMPLATE % 'column' - index = 'index' - name = 'name' - - -class Data(atom.core.XmlElement): - """Google Sites List Page <gs:data>.""" - - _qname = SPREADSHEETS_TEMPLATE % 'data' - startRow = 'startRow' - column = [Column] - - -class Field(atom.core.XmlElement): - """Google Sites List Item <gs:field>.""" - - _qname = SPREADSHEETS_TEMPLATE % 'field' - index = 'index' - name = 'name' - - -class InReplyTo(atom.core.XmlElement): - """Google Sites List Item <thr:in-reply-to>.""" - - _qname = THR_TERMS_TEMPLATE % 'in-reply-to' - href = 'href' - ref = 'ref' - source = 'source' - type = 'type' - - -class Content(atom.data.Content): - """Google Sites version of <atom:content> that encapsulates XHTML.""" - - def __init__(self, html=None, type=None, **kwargs): - if type is None and html: - type = 'xhtml' - super(Content, self).__init__(type=type, **kwargs) - if html is not None: - self.html = html - - def _get_html(self): - if self.children: - return self.children[0] - else: - return '' - - def _set_html(self, html): - if not html: - self.children = [] - return - - if type(html) == str: - html = atom.core.parse(html) - if not html.namespace: - html.namespace = XHTML_NAMESPACE - - self.children = [html] - - html = property(_get_html, _set_html) - - -class Summary(atom.data.Summary): - """Google Sites version of <atom:summary>.""" - - def __init__(self, html=None, type=None, text=None, **kwargs): - if type is None and html: - type = 'xhtml' - - super(Summary, self).__init__(type=type, text=text, **kwargs) - if html is not None: - self.html = html - - def _get_html(self): - if self.children: - return self.children[0] - else: - return '' - - def _set_html(self, html): - if not html: - self.children = [] - return - - if type(html) == str: - html = atom.core.parse(html) - if not html.namespace: - html.namespace = XHTML_NAMESPACE - - self.children = [html] - - html = property(_get_html, _set_html) - - -class BaseSiteEntry(gdata.data.GDEntry): - """Google Sites Entry.""" - - def __init__(self, kind=None, **kwargs): - super(BaseSiteEntry, self).__init__(**kwargs) - if kind is not None: - self.category.append( - atom.data.Category(scheme=SITES_KIND_SCHEME, - term='%s#%s' % (SITES_NAMESPACE, kind), - label=kind)) - - def __find_category_scheme(self, scheme): - for category in self.category: - if category.scheme == scheme: - return category - return None - - def kind(self): - kind = self.__find_category_scheme(SITES_KIND_SCHEME) - if kind is not None: - return kind.term[len(SITES_NAMESPACE) + 1:] - else: - return None - - Kind = kind - - def get_node_id(self): - return self.id.text[self.id.text.rfind('/') + 1:] - - GetNodeId = get_node_id - - def find_parent_link(self): - return self.find_url(SITES_PARENT_LINK_REL) - - FindParentLink = find_parent_link - - def is_deleted(self): - return self.deleted is not None - - IsDeleted = is_deleted - - -class ContentEntry(BaseSiteEntry): - """Google Sites Content Entry.""" - content = Content - deleted = Deleted - publisher = Publisher - in_reply_to = InReplyTo - worksheet = Worksheet - header = Header - data = Data - field = [Field] - revision = Revision - page_name = PageName - feed_link = gdata.data.FeedLink - - def find_revison_link(self): - return self.find_url(SITES_REVISION_LINK_REL) - - FindRevisionLink = find_revison_link - - -class ContentFeed(gdata.data.GDFeed): - """Google Sites Content Feed. - - The Content feed is a feed containing the current, editable site content. - """ - entry = [ContentEntry] - - def __get_entry_type(self, kind): - matches = [] - for entry in self.entry: - if entry.Kind() == kind: - matches.append(entry) - return matches - - def get_announcements(self): - return self.__get_entry_type('announcement') - - GetAnnouncements = get_announcements - - def get_announcement_pages(self): - return self.__get_entry_type('announcementspage') - - GetAnnouncementPages = get_announcement_pages - - def get_attachments(self): - return self.__get_entry_type('attachment') - - GetAttachments = get_attachments - - def get_comments(self): - return self.__get_entry_type('comment') - - GetComments = get_comments - - def get_file_cabinets(self): - return self.__get_entry_type('filecabinet') - - GetFileCabinets = get_file_cabinets - - def get_list_items(self): - return self.__get_entry_type('listitem') - - GetListItems = get_list_items - - def get_list_pages(self): - return self.__get_entry_type('listpage') - - GetListPages = get_list_pages - - def get_webpages(self): - return self.__get_entry_type('webpage') - - GetWebpages = get_webpages - - def get_webattachments(self): - return self.__get_entry_type('webattachment') - - GetWebattachments = get_webattachments - - -class ActivityEntry(BaseSiteEntry): - """Google Sites Activity Entry.""" - summary = Summary - - -class ActivityFeed(gdata.data.GDFeed): - """Google Sites Activity Feed. - - The Activity feed is a feed containing recent Site activity. - """ - entry = [ActivityEntry] - - -class RevisionEntry(BaseSiteEntry): - """Google Sites Revision Entry.""" - content = Content - - -class RevisionFeed(gdata.data.GDFeed): - """Google Sites Revision Feed. - - The Activity feed is a feed containing recent Site activity. - """ - entry = [RevisionEntry] - - -class SiteEntry(gdata.data.GDEntry): - """Google Sites Site Feed Entry.""" - site_name = SiteName - theme = Theme - - def find_source_link(self): - return self.find_url(SITES_SOURCE_LINK_REL) - - FindSourceLink = find_source_link - - -class SiteFeed(gdata.data.GDFeed): - """Google Sites Site Feed. - - The Site feed can be used to list a user's sites and create new sites. - """ - entry = [SiteEntry] - - -class AclEntry(gdata.acl.data.AclEntry): - """Google Sites ACL Entry.""" - - -class AclFeed(gdata.acl.data.AclFeed): - """Google Sites ACL Feed. - - The ACL feed can be used to modify the sharing permissions of a Site. - """ - entry = [AclEntry] diff --git a/gdata/spreadsheet/__init__.py b/gdata/spreadsheet/__init__.py deleted file mode 100644 index e9a0fb3dc7..0000000000 --- a/gdata/spreadsheet/__init__.py +++ /dev/null @@ -1,474 +0,0 @@ -#!/usr/bin/python -# -# Copyright (C) 2007 Google 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. - -"""Contains extensions to Atom objects used with Google Spreadsheets. -""" - -__author__ = 'api.laurabeth@gmail.com (Laura Beth Lincoln)' - - -try: - from xml.etree import cElementTree as ElementTree -except ImportError: - try: - import cElementTree as ElementTree - except ImportError: - try: - from xml.etree import ElementTree - except ImportError: - from elementtree import ElementTree -import atom -import gdata -import re -import string - - -# XML namespaces which are often used in Google Spreadsheets entities. -GSPREADSHEETS_NAMESPACE = 'http://schemas.google.com/spreadsheets/2006' -GSPREADSHEETS_TEMPLATE = '{http://schemas.google.com/spreadsheets/2006}%s' - -GSPREADSHEETS_EXTENDED_NAMESPACE = ('http://schemas.google.com/spreadsheets' - '/2006/extended') -GSPREADSHEETS_EXTENDED_TEMPLATE = ('{http://schemas.google.com/spreadsheets' - '/2006/extended}%s') - - -class ColCount(atom.AtomBase): - """The Google Spreadsheets colCount element """ - - _tag = 'colCount' - _namespace = GSPREADSHEETS_NAMESPACE - _children = atom.AtomBase._children.copy() - _attributes = atom.AtomBase._attributes.copy() - - def __init__(self, text=None, extension_elements=None, - extension_attributes=None): - self.text = text - self.extension_elements = extension_elements or [] - self.extension_attributes = extension_attributes or {} - - -def ColCountFromString(xml_string): - return atom.CreateClassFromXMLString(ColCount, xml_string) - - -class RowCount(atom.AtomBase): - """The Google Spreadsheets rowCount element """ - - _tag = 'rowCount' - _namespace = GSPREADSHEETS_NAMESPACE - _children = atom.AtomBase._children.copy() - _attributes = atom.AtomBase._attributes.copy() - - def __init__(self, text=None, extension_elements=None, - extension_attributes=None): - self.text = text - self.extension_elements = extension_elements or [] - self.extension_attributes = extension_attributes or {} - -def RowCountFromString(xml_string): - return atom.CreateClassFromXMLString(RowCount, xml_string) - - -class Cell(atom.AtomBase): - """The Google Spreadsheets cell element """ - - _tag = 'cell' - _namespace = GSPREADSHEETS_NAMESPACE - _children = atom.AtomBase._children.copy() - _attributes = atom.AtomBase._attributes.copy() - _attributes['row'] = 'row' - _attributes['col'] = 'col' - _attributes['inputValue'] = 'inputValue' - _attributes['numericValue'] = 'numericValue' - - def __init__(self, text=None, row=None, col=None, inputValue=None, - numericValue=None, extension_elements=None, extension_attributes=None): - self.text = text - self.row = row - self.col = col - self.inputValue = inputValue - self.numericValue = numericValue - self.extension_elements = extension_elements or [] - self.extension_attributes = extension_attributes or {} - - -def CellFromString(xml_string): - return atom.CreateClassFromXMLString(Cell, xml_string) - - -class Custom(atom.AtomBase): - """The Google Spreadsheets custom element""" - - _namespace = GSPREADSHEETS_EXTENDED_NAMESPACE - _children = atom.AtomBase._children.copy() - _attributes = atom.AtomBase._attributes.copy() - - def __init__(self, column=None, text=None, extension_elements=None, - extension_attributes=None): - self.column = column # The name of the column - self.text = text - self.extension_elements = extension_elements or [] - self.extension_attributes = extension_attributes or {} - - def _BecomeChildElement(self, tree): - new_child = ElementTree.Element('') - tree.append(new_child) - new_child.tag = '{%s}%s' % (self.__class__._namespace, - self.column) - self._AddMembersToElementTree(new_child) - - def _ToElementTree(self): - new_tree = ElementTree.Element('{%s}%s' % (self.__class__._namespace, - self.column)) - self._AddMembersToElementTree(new_tree) - return new_tree - - def _HarvestElementTree(self, tree): - namespace_uri, local_tag = string.split(tree.tag[1:], "}", 1) - self.column = local_tag - # Fill in the instance members from the contents of the XML tree. - for child in tree: - self._ConvertElementTreeToMember(child) - for attribute, value in tree.attrib.iteritems(): - self._ConvertElementAttributeToMember(attribute, value) - self.text = tree.text - - -def CustomFromString(xml_string): - element_tree = ElementTree.fromstring(xml_string) - return _CustomFromElementTree(element_tree) - - -def _CustomFromElementTree(element_tree): - namespace_uri, local_tag = string.split(element_tree.tag[1:], "}", 1) - if namespace_uri == GSPREADSHEETS_EXTENDED_NAMESPACE: - new_custom = Custom() - new_custom._HarvestElementTree(element_tree) - new_custom.column = local_tag - return new_custom - return None - - - - - -class SpreadsheetsSpreadsheet(gdata.GDataEntry): - """A Google Spreadsheets flavor of a Spreadsheet Atom Entry """ - - _tag = 'entry' - _namespace = atom.ATOM_NAMESPACE - _children = gdata.GDataEntry._children.copy() - _attributes = gdata.GDataEntry._attributes.copy() - - def __init__(self, author=None, category=None, content=None, - contributor=None, atom_id=None, link=None, published=None, rights=None, - source=None, summary=None, title=None, control=None, updated=None, - text=None, extension_elements=None, extension_attributes=None): - self.author = author or [] - self.category = category or [] - self.content = content - self.contributor = contributor or [] - self.id = atom_id - self.link = link or [] - self.published = published - self.rights = rights - self.source = source - self.summary = summary - self.control = control - self.title = title - self.updated = updated - self.text = text - self.extension_elements = extension_elements or [] - self.extension_attributes = extension_attributes or {} - - -def SpreadsheetsSpreadsheetFromString(xml_string): - return atom.CreateClassFromXMLString(SpreadsheetsSpreadsheet, - xml_string) - - -class SpreadsheetsWorksheet(gdata.GDataEntry): - """A Google Spreadsheets flavor of a Worksheet Atom Entry """ - - _tag = 'entry' - _namespace = atom.ATOM_NAMESPACE - _children = gdata.GDataEntry._children.copy() - _attributes = gdata.GDataEntry._attributes.copy() - _children['{%s}rowCount' % GSPREADSHEETS_NAMESPACE] = ('row_count', - RowCount) - _children['{%s}colCount' % GSPREADSHEETS_NAMESPACE] = ('col_count', - ColCount) - - def __init__(self, author=None, category=None, content=None, - contributor=None, atom_id=None, link=None, published=None, rights=None, - source=None, summary=None, title=None, control=None, updated=None, - row_count=None, col_count=None, text=None, extension_elements=None, - extension_attributes=None): - self.author = author or [] - self.category = category or [] - self.content = content - self.contributor = contributor or [] - self.id = atom_id - self.link = link or [] - self.published = published - self.rights = rights - self.source = source - self.summary = summary - self.control = control - self.title = title - self.updated = updated - self.row_count = row_count - self.col_count = col_count - self.text = text - self.extension_elements = extension_elements or [] - self.extension_attributes = extension_attributes or {} - - -def SpreadsheetsWorksheetFromString(xml_string): - return atom.CreateClassFromXMLString(SpreadsheetsWorksheet, - xml_string) - - -class SpreadsheetsCell(gdata.BatchEntry): - """A Google Spreadsheets flavor of a Cell Atom Entry """ - - _tag = 'entry' - _namespace = atom.ATOM_NAMESPACE - _children = gdata.BatchEntry._children.copy() - _attributes = gdata.BatchEntry._attributes.copy() - _children['{%s}cell' % GSPREADSHEETS_NAMESPACE] = ('cell', Cell) - - def __init__(self, author=None, category=None, content=None, - contributor=None, atom_id=None, link=None, published=None, rights=None, - source=None, summary=None, title=None, control=None, updated=None, - cell=None, batch_operation=None, batch_id=None, batch_status=None, - text=None, extension_elements=None, extension_attributes=None): - self.author = author or [] - self.category = category or [] - self.content = content - self.contributor = contributor or [] - self.id = atom_id - self.link = link or [] - self.published = published - self.rights = rights - self.source = source - self.summary = summary - self.control = control - self.title = title - self.batch_operation = batch_operation - self.batch_id = batch_id - self.batch_status = batch_status - self.updated = updated - self.cell = cell - self.text = text - self.extension_elements = extension_elements or [] - self.extension_attributes = extension_attributes or {} - - -def SpreadsheetsCellFromString(xml_string): - return atom.CreateClassFromXMLString(SpreadsheetsCell, - xml_string) - - -class SpreadsheetsList(gdata.GDataEntry): - """A Google Spreadsheets flavor of a List Atom Entry """ - - _tag = 'entry' - _namespace = atom.ATOM_NAMESPACE - _children = gdata.GDataEntry._children.copy() - _attributes = gdata.GDataEntry._attributes.copy() - - def __init__(self, author=None, category=None, content=None, - contributor=None, atom_id=None, link=None, published=None, rights=None, - source=None, summary=None, title=None, control=None, updated=None, - custom=None, - text=None, extension_elements=None, extension_attributes=None): - self.author = author or [] - self.category = category or [] - self.content = content - self.contributor = contributor or [] - self.id = atom_id - self.link = link or [] - self.published = published - self.rights = rights - self.source = source - self.summary = summary - self.control = control - self.title = title - self.updated = updated - self.custom = custom or {} - self.text = text - self.extension_elements = extension_elements or [] - self.extension_attributes = extension_attributes or {} - - # We need to overwrite _ConvertElementTreeToMember to add special logic to - # convert custom attributes to members - def _ConvertElementTreeToMember(self, child_tree): - # Find the element's tag in this class's list of child members - if self.__class__._children.has_key(child_tree.tag): - member_name = self.__class__._children[child_tree.tag][0] - member_class = self.__class__._children[child_tree.tag][1] - # If the class member is supposed to contain a list, make sure the - # matching member is set to a list, then append the new member - # instance to the list. - if isinstance(member_class, list): - if getattr(self, member_name) is None: - setattr(self, member_name, []) - getattr(self, member_name).append(atom._CreateClassFromElementTree( - member_class[0], child_tree)) - else: - setattr(self, member_name, - atom._CreateClassFromElementTree(member_class, child_tree)) - elif child_tree.tag.find('{%s}' % GSPREADSHEETS_EXTENDED_NAMESPACE) == 0: - # If this is in the custom namespace, make add it to the custom dict. - name = child_tree.tag[child_tree.tag.index('}')+1:] - custom = _CustomFromElementTree(child_tree) - if custom: - self.custom[name] = custom - else: - atom.ExtensionContainer._ConvertElementTreeToMember(self, child_tree) - - # We need to overwtite _AddMembersToElementTree to add special logic to - # convert custom members to XML nodes. - def _AddMembersToElementTree(self, tree): - # Convert the members of this class which are XML child nodes. - # This uses the class's _children dictionary to find the members which - # should become XML child nodes. - member_node_names = [values[0] for tag, values in - self.__class__._children.iteritems()] - for member_name in member_node_names: - member = getattr(self, member_name) - if member is None: - pass - elif isinstance(member, list): - for instance in member: - instance._BecomeChildElement(tree) - else: - member._BecomeChildElement(tree) - # Convert the members of this class which are XML attributes. - for xml_attribute, member_name in self.__class__._attributes.iteritems(): - member = getattr(self, member_name) - if member is not None: - tree.attrib[xml_attribute] = member - # Convert all special custom item attributes to nodes - for name, custom in self.custom.iteritems(): - custom._BecomeChildElement(tree) - # Lastly, call the ExtensionContainers's _AddMembersToElementTree to - # convert any extension attributes. - atom.ExtensionContainer._AddMembersToElementTree(self, tree) - - -def SpreadsheetsListFromString(xml_string): - return atom.CreateClassFromXMLString(SpreadsheetsList, - xml_string) - element_tree = ElementTree.fromstring(xml_string) - return _SpreadsheetsListFromElementTree(element_tree) - - -class SpreadsheetsSpreadsheetsFeed(gdata.GDataFeed): - """A feed containing Google Spreadsheets Spreadsheets""" - - _tag = 'feed' - _namespace = atom.ATOM_NAMESPACE - _children = gdata.GDataFeed._children.copy() - _attributes = gdata.GDataFeed._attributes.copy() - _children['{%s}entry' % atom.ATOM_NAMESPACE] = ('entry', - [SpreadsheetsSpreadsheet]) - - -def SpreadsheetsSpreadsheetsFeedFromString(xml_string): - return atom.CreateClassFromXMLString(SpreadsheetsSpreadsheetsFeed, - xml_string) - - -class SpreadsheetsWorksheetsFeed(gdata.GDataFeed): - """A feed containing Google Spreadsheets Spreadsheets""" - - _tag = 'feed' - _namespace = atom.ATOM_NAMESPACE - _children = gdata.GDataFeed._children.copy() - _attributes = gdata.GDataFeed._attributes.copy() - _children['{%s}entry' % atom.ATOM_NAMESPACE] = ('entry', - [SpreadsheetsWorksheet]) - - -def SpreadsheetsWorksheetsFeedFromString(xml_string): - return atom.CreateClassFromXMLString(SpreadsheetsWorksheetsFeed, - xml_string) - - -class SpreadsheetsCellsFeed(gdata.BatchFeed): - """A feed containing Google Spreadsheets Cells""" - - _tag = 'feed' - _namespace = atom.ATOM_NAMESPACE - _children = gdata.BatchFeed._children.copy() - _attributes = gdata.BatchFeed._attributes.copy() - _children['{%s}entry' % atom.ATOM_NAMESPACE] = ('entry', - [SpreadsheetsCell]) - _children['{%s}rowCount' % GSPREADSHEETS_NAMESPACE] = ('row_count', - RowCount) - _children['{%s}colCount' % GSPREADSHEETS_NAMESPACE] = ('col_count', - ColCount) - - def __init__(self, author=None, category=None, contributor=None, - generator=None, icon=None, atom_id=None, link=None, logo=None, - rights=None, subtitle=None, title=None, updated=None, - entry=None, total_results=None, start_index=None, - items_per_page=None, extension_elements=None, - extension_attributes=None, text=None, row_count=None, - col_count=None, interrupted=None): - gdata.BatchFeed.__init__(self, author=author, category=category, - contributor=contributor, generator=generator, - icon=icon, atom_id=atom_id, link=link, - logo=logo, rights=rights, subtitle=subtitle, - title=title, updated=updated, entry=entry, - total_results=total_results, - start_index=start_index, - items_per_page=items_per_page, - extension_elements=extension_elements, - extension_attributes=extension_attributes, - text=text, interrupted=interrupted) - self.row_count = row_count - self.col_count = col_count - - def GetBatchLink(self): - for link in self.link: - if link.rel == 'http://schemas.google.com/g/2005#batch': - return link - return None - - -def SpreadsheetsCellsFeedFromString(xml_string): - return atom.CreateClassFromXMLString(SpreadsheetsCellsFeed, - xml_string) - - -class SpreadsheetsListFeed(gdata.GDataFeed): - """A feed containing Google Spreadsheets Spreadsheets""" - - _tag = 'feed' - _namespace = atom.ATOM_NAMESPACE - _children = gdata.GDataFeed._children.copy() - _attributes = gdata.GDataFeed._attributes.copy() - _children['{%s}entry' % atom.ATOM_NAMESPACE] = ('entry', - [SpreadsheetsList]) - - -def SpreadsheetsListFeedFromString(xml_string): - return atom.CreateClassFromXMLString(SpreadsheetsListFeed, - xml_string) diff --git a/gdata/spreadsheet/service.py b/gdata/spreadsheet/service.py deleted file mode 100644 index 7449c18565..0000000000 --- a/gdata/spreadsheet/service.py +++ /dev/null @@ -1,484 +0,0 @@ -#!/usr/bin/python -# -# Copyright (C) 2007 Google 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. - -"""SpreadsheetsService extends the GDataService to streamline Google -Spreadsheets operations. - - SpreadsheetService: Provides methods to query feeds and manipulate items. - Extends GDataService. - - DictionaryToParamList: Function which converts a dictionary into a list of - URL arguments (represented as strings). This is a - utility function used in CRUD operations. -""" - -__author__ = 'api.laurabeth@gmail.com (Laura Beth Lincoln)' - - -import gdata -import atom.service -import gdata.service -import gdata.spreadsheet -import atom - - -class Error(Exception): - """Base class for exceptions in this module.""" - pass - - -class RequestError(Error): - pass - - -class SpreadsheetsService(gdata.service.GDataService): - """Client for the Google Spreadsheets service.""" - - def __init__(self, email=None, password=None, source=None, - server='spreadsheets.google.com', additional_headers=None, - **kwargs): - """Creates a client for the Google Spreadsheets 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: 'spreadsheets.google.com'. - **kwargs: The other parameters to pass to gdata.service.GDataService - constructor. - """ - gdata.service.GDataService.__init__( - self, email=email, password=password, service='wise', source=source, - server=server, additional_headers=additional_headers, **kwargs) - - def GetSpreadsheetsFeed(self, key=None, query=None, visibility='private', - projection='full'): - """Gets a spreadsheets feed or a specific entry if a key is defined - Args: - key: string (optional) The spreadsheet key defined in /ccc?key= - query: DocumentQuery (optional) Query parameters - - Returns: - If there is no key, then a SpreadsheetsSpreadsheetsFeed. - If there is a key, then a SpreadsheetsSpreadsheet. - """ - - uri = ('http://%s/feeds/spreadsheets/%s/%s' - % (self.server, visibility, projection)) - - if key is not None: - uri = '%s/%s' % (uri, key) - - if query != None: - query.feed = uri - uri = query.ToUri() - - if key: - return self.Get(uri, - converter=gdata.spreadsheet.SpreadsheetsSpreadsheetFromString) - else: - return self.Get(uri, - converter=gdata.spreadsheet.SpreadsheetsSpreadsheetsFeedFromString) - - def GetWorksheetsFeed(self, key, wksht_id=None, query=None, - visibility='private', projection='full'): - """Gets a worksheets feed or a specific entry if a wksht is defined - Args: - key: string The spreadsheet key defined in /ccc?key= - wksht_id: string (optional) The id for a specific worksheet entry - query: DocumentQuery (optional) Query parameters - - Returns: - If there is no wksht_id, then a SpreadsheetsWorksheetsFeed. - If there is a wksht_id, then a SpreadsheetsWorksheet. - """ - - uri = ('http://%s/feeds/worksheets/%s/%s/%s' - % (self.server, key, visibility, projection)) - - if wksht_id != None: - uri = '%s/%s' % (uri, wksht_id) - - if query != None: - query.feed = uri - uri = query.ToUri() - - if wksht_id: - return self.Get(uri, - converter=gdata.spreadsheet.SpreadsheetsWorksheetFromString) - else: - return self.Get(uri, - converter=gdata.spreadsheet.SpreadsheetsWorksheetsFeedFromString) - - def AddWorksheet(self, title, row_count, col_count, key): - """Creates a new worksheet in the desired spreadsheet. - - The new worksheet is appended to the end of the list of worksheets. The - new worksheet will only have the available number of columns and cells - specified. - - Args: - title: str The title which will be displayed in the list of worksheets. - row_count: int or str The number of rows in the new worksheet. - col_count: int or str The number of columns in the new worksheet. - key: str The spreadsheet key to the spreadsheet to which the new - worksheet should be added. - - Returns: - A SpreadsheetsWorksheet if the new worksheet was created succesfully. - """ - new_worksheet = gdata.spreadsheet.SpreadsheetsWorksheet( - title=atom.Title(text=title), - row_count=gdata.spreadsheet.RowCount(text=str(row_count)), - col_count=gdata.spreadsheet.ColCount(text=str(col_count))) - return self.Post(new_worksheet, - 'http://%s/feeds/worksheets/%s/private/full' % (self.server, key), - converter=gdata.spreadsheet.SpreadsheetsWorksheetFromString) - - def UpdateWorksheet(self, worksheet_entry, url=None): - """Changes the size and/or title of the desired worksheet. - - Args: - worksheet_entry: SpreadsheetWorksheet The new contents of the - worksheet. - url: str (optional) The URL to which the edited worksheet entry should - be sent. If the url is None, the edit URL from the worksheet will - be used. - - Returns: - A SpreadsheetsWorksheet with the new information about the worksheet. - """ - target_url = url or worksheet_entry.GetEditLink().href - return self.Put(worksheet_entry, target_url, - converter=gdata.spreadsheet.SpreadsheetsWorksheetFromString) - - def DeleteWorksheet(self, worksheet_entry=None, url=None): - """Removes the desired worksheet from the spreadsheet - - Args: - worksheet_entry: SpreadsheetWorksheet (optional) The worksheet to - be deleted. If this is none, then the DELETE reqest is sent to - the url specified in the url parameter. - url: str (optaional) The URL to which the DELETE request should be - sent. If left as None, the worksheet's edit URL is used. - - Returns: - True if the worksheet was deleted successfully. - """ - if url: - target_url = url - else: - target_url = worksheet_entry.GetEditLink().href - return self.Delete(target_url) - - def GetCellsFeed(self, key, wksht_id='default', cell=None, query=None, - visibility='private', projection='full'): - """Gets a cells feed or a specific entry if a cell is defined - Args: - key: string The spreadsheet key defined in /ccc?key= - wksht_id: string The id for a specific worksheet entry - cell: string (optional) The R1C1 address of the cell - query: DocumentQuery (optional) Query parameters - - Returns: - If there is no cell, then a SpreadsheetsCellsFeed. - If there is a cell, then a SpreadsheetsCell. - """ - - uri = ('http://%s/feeds/cells/%s/%s/%s/%s' - % (self.server, key, wksht_id, visibility, projection)) - - if cell != None: - uri = '%s/%s' % (uri, cell) - - if query != None: - query.feed = uri - uri = query.ToUri() - - if cell: - return self.Get(uri, - converter=gdata.spreadsheet.SpreadsheetsCellFromString) - else: - return self.Get(uri, - converter=gdata.spreadsheet.SpreadsheetsCellsFeedFromString) - - def GetListFeed(self, key, wksht_id='default', row_id=None, query=None, - visibility='private', projection='full'): - """Gets a list feed or a specific entry if a row_id is defined - Args: - key: string The spreadsheet key defined in /ccc?key= - wksht_id: string The id for a specific worksheet entry - row_id: string (optional) The row_id of a row in the list - query: DocumentQuery (optional) Query parameters - - Returns: - If there is no row_id, then a SpreadsheetsListFeed. - If there is a row_id, then a SpreadsheetsList. - """ - - uri = ('http://%s/feeds/list/%s/%s/%s/%s' - % (self.server, key, wksht_id, visibility, projection)) - - if row_id is not None: - uri = '%s/%s' % (uri, row_id) - - if query is not None: - query.feed = uri - uri = query.ToUri() - - if row_id: - return self.Get(uri, - converter=gdata.spreadsheet.SpreadsheetsListFromString) - else: - return self.Get(uri, - converter=gdata.spreadsheet.SpreadsheetsListFeedFromString) - - def UpdateCell(self, row, col, inputValue, key, wksht_id='default'): - """Updates an existing cell. - - Args: - row: int The row the cell to be editted is in - col: int The column the cell to be editted is in - inputValue: str the new value of the cell - key: str The key of the spreadsheet in which this cell resides. - wksht_id: str The ID of the worksheet which holds this cell. - - Returns: - The updated cell entry - """ - row = str(row) - col = str(col) - # make the new cell - new_cell = gdata.spreadsheet.Cell(row=row, col=col, inputValue=inputValue) - # get the edit uri and PUT - cell = 'R%sC%s' % (row, col) - entry = self.GetCellsFeed(key, wksht_id, cell) - for a_link in entry.link: - if a_link.rel == 'edit': - entry.cell = new_cell - return self.Put(entry, a_link.href, - converter=gdata.spreadsheet.SpreadsheetsCellFromString) - - def _GenerateCellsBatchUrl(self, spreadsheet_key, worksheet_id): - return ('http://spreadsheets.google.com/feeds/cells/%s/%s/' - 'private/full/batch' % (spreadsheet_key, worksheet_id)) - - def ExecuteBatch(self, batch_feed, url=None, spreadsheet_key=None, - worksheet_id=None, - converter=gdata.spreadsheet.SpreadsheetsCellsFeedFromString): - """Sends a batch request feed to the server. - - The batch request needs to be sent to the batch URL for a particular - worksheet. You can specify the worksheet by providing the spreadsheet_key - and worksheet_id, or by sending the URL from the cells feed's batch link. - - Args: - batch_feed: gdata.spreadsheet.SpreadsheetsCellFeed A feed containing - BatchEntry elements which contain the desired CRUD operation and - any necessary data to modify a cell. - url: str (optional) The batch URL for the cells feed to which these - changes should be applied. This can be found by calling - cells_feed.GetBatchLink().href. - spreadsheet_key: str (optional) Used to generate the batch request URL - if the url argument is None. If using the spreadsheet key to - generate the URL, the worksheet id is also required. - worksheet_id: str (optional) Used if the url is not provided, it is - oart of the batch feed target URL. This is used with the spreadsheet - key. - converter: Function (optional) Function to be executed on the server's - response. This function should take one string as a parameter. The - default value is SpreadsheetsCellsFeedFromString which will turn the result - into a gdata.spreadsheet.SpreadsheetsCellsFeed object. - - Returns: - A gdata.BatchFeed containing the results. - """ - - if url is None: - url = self._GenerateCellsBatchUrl(spreadsheet_key, worksheet_id) - return self.Post(batch_feed, url, converter=converter) - - def InsertRow(self, row_data, key, wksht_id='default'): - """Inserts a new row with the provided data - - Args: - uri: string The post uri of the list feed - row_data: dict A dictionary of column header to row data - - Returns: - The inserted row - """ - new_entry = gdata.spreadsheet.SpreadsheetsList() - for k, v in row_data.iteritems(): - new_custom = gdata.spreadsheet.Custom() - new_custom.column = k - new_custom.text = v - new_entry.custom[new_custom.column] = new_custom - # Generate the post URL for the worksheet which will receive the new entry. - post_url = 'http://spreadsheets.google.com/feeds/list/%s/%s/private/full'%( - key, wksht_id) - return self.Post(new_entry, post_url, - converter=gdata.spreadsheet.SpreadsheetsListFromString) - - def UpdateRow(self, entry, new_row_data): - """Updates a row with the provided data - - If you want to add additional information to a row, it is often - easier to change the values in entry.custom, then use the Put - method instead of UpdateRow. This UpdateRow method will replace - the contents of the row with new_row_data - it will change all columns - not just the columns specified in the new_row_data dict. - - Args: - entry: gdata.spreadsheet.SpreadsheetsList The entry to be updated - new_row_data: dict A dictionary of column header to row data - - Returns: - The updated row - """ - entry.custom = {} - for k, v in new_row_data.iteritems(): - new_custom = gdata.spreadsheet.Custom() - new_custom.column = k - new_custom.text = v - entry.custom[k] = new_custom - for a_link in entry.link: - if a_link.rel == 'edit': - return self.Put(entry, a_link.href, - converter=gdata.spreadsheet.SpreadsheetsListFromString) - - def DeleteRow(self, entry): - """Deletes a row, the provided entry - - Args: - entry: gdata.spreadsheet.SpreadsheetsList The row to be deleted - - Returns: - The delete response - """ - for a_link in entry.link: - if a_link.rel == 'edit': - return self.Delete(a_link.href) - - -class DocumentQuery(gdata.service.Query): - - def _GetTitleQuery(self): - return self['title'] - - def _SetTitleQuery(self, document_query): - self['title'] = document_query - - title = property(_GetTitleQuery, _SetTitleQuery, - doc="""The title query parameter""") - - def _GetTitleExactQuery(self): - return self['title-exact'] - - def _SetTitleExactQuery(self, document_query): - self['title-exact'] = document_query - - title_exact = property(_GetTitleExactQuery, _SetTitleExactQuery, - doc="""The title-exact query parameter""") - - -class CellQuery(gdata.service.Query): - - def _GetMinRowQuery(self): - return self['min-row'] - - def _SetMinRowQuery(self, cell_query): - self['min-row'] = cell_query - - min_row = property(_GetMinRowQuery, _SetMinRowQuery, - doc="""The min-row query parameter""") - - def _GetMaxRowQuery(self): - return self['max-row'] - - def _SetMaxRowQuery(self, cell_query): - self['max-row'] = cell_query - - max_row = property(_GetMaxRowQuery, _SetMaxRowQuery, - doc="""The max-row query parameter""") - - def _GetMinColQuery(self): - return self['min-col'] - - def _SetMinColQuery(self, cell_query): - self['min-col'] = cell_query - - min_col = property(_GetMinColQuery, _SetMinColQuery, - doc="""The min-col query parameter""") - - def _GetMaxColQuery(self): - return self['max-col'] - - def _SetMaxColQuery(self, cell_query): - self['max-col'] = cell_query - - max_col = property(_GetMaxColQuery, _SetMaxColQuery, - doc="""The max-col query parameter""") - - def _GetRangeQuery(self): - return self['range'] - - def _SetRangeQuery(self, cell_query): - self['range'] = cell_query - - range = property(_GetRangeQuery, _SetRangeQuery, - doc="""The range query parameter""") - - def _GetReturnEmptyQuery(self): - return self['return-empty'] - - def _SetReturnEmptyQuery(self, cell_query): - self['return-empty'] = cell_query - - return_empty = property(_GetReturnEmptyQuery, _SetReturnEmptyQuery, - doc="""The return-empty query parameter""") - - -class ListQuery(gdata.service.Query): - - def _GetSpreadsheetQuery(self): - return self['sq'] - - def _SetSpreadsheetQuery(self, list_query): - self['sq'] = list_query - - sq = property(_GetSpreadsheetQuery, _SetSpreadsheetQuery, - doc="""The sq query parameter""") - - def _GetOrderByQuery(self): - return self['orderby'] - - def _SetOrderByQuery(self, list_query): - self['orderby'] = list_query - - orderby = property(_GetOrderByQuery, _SetOrderByQuery, - doc="""The orderby query parameter""") - - def _GetReverseQuery(self): - return self['reverse'] - - def _SetReverseQuery(self, list_query): - self['reverse'] = list_query - - reverse = property(_GetReverseQuery, _SetReverseQuery, - doc="""The reverse query parameter""") diff --git a/gdata/spreadsheet/text_db.py b/gdata/spreadsheet/text_db.py deleted file mode 100644 index a8de5463c2..0000000000 --- a/gdata/spreadsheet/text_db.py +++ /dev/null @@ -1,559 +0,0 @@ -#!/usr/bin/python -# -# Copyright Google 2007-2008, 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. - - -import StringIO -import gdata -import gdata.service -import gdata.spreadsheet -import gdata.spreadsheet.service -import gdata.docs -import gdata.docs.service - - -"""Make the Google Documents API feel more like using a database. - -This module contains a client and other classes which make working with the -Google Documents List Data API and the Google Spreadsheets Data API look a -bit more like working with a heirarchical database. Using the DatabaseClient, -you can create or find spreadsheets and use them like a database, with -worksheets representing tables and rows representing records. - -Example Usage: -# Create a new database, a new table, and add records. -client = gdata.spreadsheet.text_db.DatabaseClient(username='jo@example.com', - password='12345') -database = client.CreateDatabase('My Text Database') -table = database.CreateTable('addresses', ['name','email', - 'phonenumber', 'mailingaddress']) -record = table.AddRecord({'name':'Bob', 'email':'bob@example.com', - 'phonenumber':'555-555-1234', 'mailingaddress':'900 Imaginary St.'}) - -# Edit a record -record.content['email'] = 'bob2@example.com' -record.Push() - -# Delete a table -table.Delete - -Warnings: -Care should be exercised when using this module on spreadsheets -which contain formulas. This module treats all rows as containing text and -updating a row will overwrite any formula with the output of the formula. -The intended use case is to allow easy storage of text data in a spreadsheet. - - Error: Domain specific extension of Exception. - BadCredentials: Error raised is username or password was incorrect. - CaptchaRequired: Raised if a login attempt failed and a CAPTCHA challenge - was issued. - DatabaseClient: Communicates with Google Docs APIs servers. - Database: Represents a spreadsheet and interacts with tables. - Table: Represents a worksheet and interacts with records. - RecordResultSet: A list of records in a table. - Record: Represents a row in a worksheet allows manipulation of text data. -""" - - -__author__ = 'api.jscudder (Jeffrey Scudder)' - - -class Error(Exception): - pass - - -class BadCredentials(Error): - pass - - -class CaptchaRequired(Error): - pass - - -class DatabaseClient(object): - """Allows creation and finding of Google Spreadsheets databases. - - The DatabaseClient simplifies the process of creating and finding Google - Spreadsheets and will talk to both the Google Spreadsheets API and the - Google Documents List API. - """ - - def __init__(self, username=None, password=None): - """Constructor for a Database Client. - - If the username and password are present, the constructor will contact - the Google servers to authenticate. - - Args: - username: str (optional) Example: jo@example.com - password: str (optional) - """ - self.__docs_client = gdata.docs.service.DocsService() - self.__spreadsheets_client = ( - gdata.spreadsheet.service.SpreadsheetsService()) - self.SetCredentials(username, password) - - def SetCredentials(self, username, password): - """Attempts to log in to Google APIs using the provided credentials. - - If the username or password are None, the client will not request auth - tokens. - - Args: - username: str (optional) Example: jo@example.com - password: str (optional) - """ - self.__docs_client.email = username - self.__docs_client.password = password - self.__spreadsheets_client.email = username - self.__spreadsheets_client.password = password - if username and password: - try: - self.__docs_client.ProgrammaticLogin() - self.__spreadsheets_client.ProgrammaticLogin() - except gdata.service.CaptchaRequired: - raise CaptchaRequired('Please visit https://www.google.com/accounts/' - 'DisplayUnlockCaptcha to unlock your account.') - except gdata.service.BadAuthentication: - raise BadCredentials('Username or password incorrect.') - - def CreateDatabase(self, name): - """Creates a new Google Spreadsheet with the desired name. - - Args: - name: str The title for the spreadsheet. - - Returns: - A Database instance representing the new spreadsheet. - """ - # Create a Google Spreadsheet to form the foundation of this database. - # Spreadsheet is created by uploading a file to the Google Documents - # List API. - virtual_csv_file = StringIO.StringIO(',,,') - virtual_media_source = gdata.MediaSource(file_handle=virtual_csv_file, content_type='text/csv', content_length=3) - db_entry = self.__docs_client.UploadSpreadsheet(virtual_media_source, name) - return Database(spreadsheet_entry=db_entry, database_client=self) - - def GetDatabases(self, spreadsheet_key=None, name=None): - """Finds spreadsheets which have the unique key or title. - - If querying on the spreadsheet_key there will be at most one result, but - searching by name could yield multiple results. - - Args: - spreadsheet_key: str The unique key for the spreadsheet, this - usually in the the form 'pk23...We' or 'o23...423.12,,,3'. - name: str The title of the spreadsheets. - - Returns: - A list of Database objects representing the desired spreadsheets. - """ - if spreadsheet_key: - db_entry = self.__docs_client.GetDocumentListEntry( - r'/feeds/documents/private/full/spreadsheet%3A' + spreadsheet_key) - return [Database(spreadsheet_entry=db_entry, database_client=self)] - else: - title_query = gdata.docs.service.DocumentQuery() - title_query['title'] = name - db_feed = self.__docs_client.QueryDocumentListFeed(title_query.ToUri()) - matching_databases = [] - for entry in db_feed.entry: - matching_databases.append(Database(spreadsheet_entry=entry, - database_client=self)) - return matching_databases - - def _GetDocsClient(self): - return self.__docs_client - - def _GetSpreadsheetsClient(self): - return self.__spreadsheets_client - - -class Database(object): - """Provides interface to find and create tables. - - The database represents a Google Spreadsheet. - """ - - def __init__(self, spreadsheet_entry=None, database_client=None): - """Constructor for a database object. - - Args: - spreadsheet_entry: gdata.docs.DocumentListEntry The - Atom entry which represents the Google Spreadsheet. The - spreadsheet's key is extracted from the entry and stored as a - member. - database_client: DatabaseClient A client which can talk to the - Google Spreadsheets servers to perform operations on worksheets - within this spreadsheet. - """ - self.entry = spreadsheet_entry - if self.entry: - id_parts = spreadsheet_entry.id.text.split('/') - self.spreadsheet_key = id_parts[-1].replace('spreadsheet%3A', '') - self.client = database_client - - def CreateTable(self, name, fields=None): - """Add a new worksheet to this spreadsheet and fill in column names. - - Args: - name: str The title of the new worksheet. - fields: list of strings The column names which are placed in the - first row of this worksheet. These names are converted into XML - tags by the server. To avoid changes during the translation - process I recommend using all lowercase alphabetic names. For - example ['somelongname', 'theothername'] - - Returns: - Table representing the newly created worksheet. - """ - worksheet = self.client._GetSpreadsheetsClient().AddWorksheet(title=name, - row_count=1, col_count=len(fields), key=self.spreadsheet_key) - return Table(name=name, worksheet_entry=worksheet, - database_client=self.client, - spreadsheet_key=self.spreadsheet_key, fields=fields) - - def GetTables(self, worksheet_id=None, name=None): - """Searches for a worksheet with the specified ID or name. - - The list of results should have one table at most, or no results - if the id or name were not found. - - Args: - worksheet_id: str The ID of the worksheet, example: 'od6' - name: str The title of the worksheet. - - Returns: - A list of length 0 or 1 containing the desired Table. A list is returned - to make this method feel like GetDatabases and GetRecords. - """ - if worksheet_id: - worksheet_entry = self.client._GetSpreadsheetsClient().GetWorksheetsFeed( - self.spreadsheet_key, wksht_id=worksheet_id) - return [Table(name=worksheet_entry.title.text, - worksheet_entry=worksheet_entry, database_client=self.client, - spreadsheet_key=self.spreadsheet_key)] - else: - matching_tables = [] - query = None - if name: - query = gdata.spreadsheet.service.DocumentQuery() - query.title = name - - worksheet_feed = self.client._GetSpreadsheetsClient().GetWorksheetsFeed( - self.spreadsheet_key, query=query) - for entry in worksheet_feed.entry: - matching_tables.append(Table(name=entry.title.text, - worksheet_entry=entry, database_client=self.client, - spreadsheet_key=self.spreadsheet_key)) - return matching_tables - - def Delete(self): - """Deletes the entire database spreadsheet from Google Spreadsheets.""" - entry = self.client._GetDocsClient().Get( - r'http://docs.google.com/feeds/documents/private/full/spreadsheet%3A' + - self.spreadsheet_key) - self.client._GetDocsClient().Delete(entry.GetEditLink().href) - - -class Table(object): - - def __init__(self, name=None, worksheet_entry=None, database_client=None, - spreadsheet_key=None, fields=None): - self.name = name - self.entry = worksheet_entry - id_parts = worksheet_entry.id.text.split('/') - self.worksheet_id = id_parts[-1] - self.spreadsheet_key = spreadsheet_key - self.client = database_client - self.fields = fields or [] - if fields: - self.SetFields(fields) - - def LookupFields(self): - """Queries to find the column names in the first row of the worksheet. - - Useful when you have retrieved the table from the server and you don't - know the column names. - """ - if self.entry: - first_row_contents = [] - query = gdata.spreadsheet.service.CellQuery() - query.max_row = '1' - query.min_row = '1' - feed = self.client._GetSpreadsheetsClient().GetCellsFeed( - self.spreadsheet_key, wksht_id=self.worksheet_id, query=query) - for entry in feed.entry: - first_row_contents.append(entry.content.text) - # Get the next set of cells if needed. - next_link = feed.GetNextLink() - while next_link: - feed = self.client._GetSpreadsheetsClient().Get(next_link.href, - converter=gdata.spreadsheet.SpreadsheetsCellsFeedFromString) - for entry in feed.entry: - first_row_contents.append(entry.content.text) - next_link = feed.GetNextLink() - # Convert the contents of the cells to valid headers. - self.fields = ConvertStringsToColumnHeaders(first_row_contents) - - def SetFields(self, fields): - """Changes the contents of the cells in the first row of this worksheet. - - Args: - fields: list of strings The names in the list comprise the - first row of the worksheet. These names are converted into XML - tags by the server. To avoid changes during the translation - process I recommend using all lowercase alphabetic names. For - example ['somelongname', 'theothername'] - """ - # TODO: If the table already had fields, we might want to clear out the, - # current column headers. - self.fields = fields - i = 0 - for column_name in fields: - i = i + 1 - # TODO: speed this up by using a batch request to update cells. - self.client._GetSpreadsheetsClient().UpdateCell(1, i, column_name, - self.spreadsheet_key, self.worksheet_id) - - def Delete(self): - """Deletes this worksheet from the spreadsheet.""" - worksheet = self.client._GetSpreadsheetsClient().GetWorksheetsFeed( - self.spreadsheet_key, wksht_id=self.worksheet_id) - self.client._GetSpreadsheetsClient().DeleteWorksheet( - worksheet_entry=worksheet) - - def AddRecord(self, data): - """Adds a new row to this worksheet. - - Args: - data: dict of strings Mapping of string values to column names. - - Returns: - Record which represents this row of the spreadsheet. - """ - new_row = self.client._GetSpreadsheetsClient().InsertRow(data, - self.spreadsheet_key, wksht_id=self.worksheet_id) - return Record(content=data, row_entry=new_row, - spreadsheet_key=self.spreadsheet_key, worksheet_id=self.worksheet_id, - database_client=self.client) - - def GetRecord(self, row_id=None, row_number=None): - """Gets a single record from the worksheet based on row ID or number. - - Args: - row_id: The ID for the individual row. - row_number: str or int The position of the desired row. Numbering - begins at 1, which refers to the second row in the worksheet since - the first row is used for column names. - - Returns: - Record for the desired row. - """ - if row_id: - row_entry = self.client._GetSpreadsheetsClient().GetListFeed( - self.spreadsheet_key, wksht_id=self.worksheet_id, row_id=row_id) - return Record(content=None, row_entry=row_entry, - spreadsheet_key=self.spreadsheet_key, - worksheet_id=self.worksheet_id, database_client=self.client) - else: - row_query = gdata.spreadsheet.service.ListQuery() - row_query.start_index = str(row_number) - row_query.max_results = '1' - row_feed = self.client._GetSpreadsheetsClient().GetListFeed( - self.spreadsheet_key, wksht_id=self.worksheet_id, query=row_query) - if len(row_feed.entry) >= 1: - return Record(content=None, row_entry=row_feed.entry[0], - spreadsheet_key=self.spreadsheet_key, - worksheet_id=self.worksheet_id, database_client=self.client) - else: - return None - - def GetRecords(self, start_row, end_row): - """Gets all rows between the start and end row numbers inclusive. - - Args: - start_row: str or int - end_row: str or int - - Returns: - RecordResultSet for the desired rows. - """ - start_row = int(start_row) - end_row = int(end_row) - max_rows = end_row - start_row + 1 - row_query = gdata.spreadsheet.service.ListQuery() - row_query.start_index = str(start_row) - row_query.max_results = str(max_rows) - rows_feed = self.client._GetSpreadsheetsClient().GetListFeed( - self.spreadsheet_key, wksht_id=self.worksheet_id, query=row_query) - return RecordResultSet(rows_feed, self.client, self.spreadsheet_key, - self.worksheet_id) - - def FindRecords(self, query_string): - """Performs a query against the worksheet to find rows which match. - - For details on query string syntax see the section on sq under - http://code.google.com/apis/spreadsheets/reference.html#list_Parameters - - Args: - query_string: str Examples: 'name == john' to find all rows with john - in the name column, '(cost < 19.50 and name != toy) or cost > 500' - - Returns: - RecordResultSet with the first group of matches. - """ - row_query = gdata.spreadsheet.service.ListQuery() - row_query.sq = query_string - matching_feed = self.client._GetSpreadsheetsClient().GetListFeed( - self.spreadsheet_key, wksht_id=self.worksheet_id, query=row_query) - return RecordResultSet(matching_feed, self.client, - self.spreadsheet_key, self.worksheet_id) - - -class RecordResultSet(list): - """A collection of rows which allows fetching of the next set of results. - - The server may not send all rows in the requested range because there are - too many. Using this result set you can access the first set of results - as if it is a list, then get the next batch (if there are more results) by - calling GetNext(). - """ - - def __init__(self, feed, client, spreadsheet_key, worksheet_id): - self.client = client - self.spreadsheet_key = spreadsheet_key - self.worksheet_id = worksheet_id - self.feed = feed - list(self) - for entry in self.feed.entry: - self.append(Record(content=None, row_entry=entry, - spreadsheet_key=spreadsheet_key, worksheet_id=worksheet_id, - database_client=client)) - - def GetNext(self): - """Fetches the next batch of rows in the result set. - - Returns: - A new RecordResultSet. - """ - next_link = self.feed.GetNextLink() - if next_link and next_link.href: - new_feed = self.client._GetSpreadsheetsClient().Get(next_link.href, - converter=gdata.spreadsheet.SpreadsheetsListFeedFromString) - return RecordResultSet(new_feed, self.client, self.spreadsheet_key, - self.worksheet_id) - - -class Record(object): - """Represents one row in a worksheet and provides a dictionary of values. - - Attributes: - custom: dict Represents the contents of the row with cell values mapped - to column headers. - """ - - def __init__(self, content=None, row_entry=None, spreadsheet_key=None, - worksheet_id=None, database_client=None): - """Constructor for a record. - - Args: - content: dict of strings Mapping of string values to column names. - row_entry: gdata.spreadsheet.SpreadsheetsList The Atom entry - representing this row in the worksheet. - spreadsheet_key: str The ID of the spreadsheet in which this row - belongs. - worksheet_id: str The ID of the worksheet in which this row belongs. - database_client: DatabaseClient The client which can be used to talk - the Google Spreadsheets server to edit this row. - """ - self.entry = row_entry - self.spreadsheet_key = spreadsheet_key - self.worksheet_id = worksheet_id - if row_entry: - self.row_id = row_entry.id.text.split('/')[-1] - else: - self.row_id = None - self.client = database_client - self.content = content or {} - if not content: - self.ExtractContentFromEntry(row_entry) - - def ExtractContentFromEntry(self, entry): - """Populates the content and row_id based on content of the entry. - - This method is used in the Record's contructor. - - Args: - entry: gdata.spreadsheet.SpreadsheetsList The Atom entry - representing this row in the worksheet. - """ - self.content = {} - if entry: - self.row_id = entry.id.text.split('/')[-1] - for label, custom in entry.custom.iteritems(): - self.content[label] = custom.text - - def Push(self): - """Send the content of the record to spreadsheets to edit the row. - - All items in the content dictionary will be sent. Items which have been - removed from the content may remain in the row. The content member - of the record will not be modified so additional fields in the row - might be absent from this local copy. - """ - self.entry = self.client._GetSpreadsheetsClient().UpdateRow(self.entry, self.content) - - def Pull(self): - """Query Google Spreadsheets to get the latest data from the server. - - Fetches the entry for this row and repopulates the content dictionary - with the data found in the row. - """ - if self.row_id: - self.entry = self.client._GetSpreadsheetsClient().GetListFeed( - self.spreadsheet_key, wksht_id=self.worksheet_id, row_id=self.row_id) - self.ExtractContentFromEntry(self.entry) - - def Delete(self): - self.client._GetSpreadsheetsClient().DeleteRow(self.entry) - - -def ConvertStringsToColumnHeaders(proposed_headers): - """Converts a list of strings to column names which spreadsheets accepts. - - When setting values in a record, the keys which represent column names must - fit certain rules. They are all lower case, contain no spaces or special - characters. If two columns have the same name after being sanitized, the - columns further to the right have _2, _3 _4, etc. appended to them. - - If there are column names which consist of all special characters, or if - the column header is blank, an obfuscated value will be used for a column - name. This method does not handle blank column names or column names with - only special characters. - """ - headers = [] - for input_string in proposed_headers: - # TODO: probably a more efficient way to do this. Perhaps regex. - sanitized = input_string.lower().replace('_', '').replace( - ':', '').replace(' ', '') - # When the same sanitized header appears multiple times in the first row - # of a spreadsheet, _n is appended to the name to make it unique. - header_count = headers.count(sanitized) - if header_count > 0: - headers.append('%s_%i' % (sanitized, header_count+1)) - else: - headers.append(sanitized) - return headers diff --git a/gdata/spreadsheets/__init__.py b/gdata/spreadsheets/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/gdata/spreadsheets/client.py b/gdata/spreadsheets/client.py deleted file mode 100644 index e00b75c2ef..0000000000 --- a/gdata/spreadsheets/client.py +++ /dev/null @@ -1,451 +0,0 @@ -#!/usr/bin/env python -# -# Copyright (C) 2009 Google 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. - - -"""Contains a client to communicate with the Google Spreadsheets servers. - -For documentation on the Spreadsheets API, see: -http://code.google.com/apis/spreadsheets/ -""" - - -__author__ = 'j.s@google.com (Jeff Scudder)' - - -import gdata.client -import gdata.gauth -import gdata.spreadsheets.data -import atom.data -import atom.http_core - - -SPREADSHEETS_URL = ('http://spreadsheets.google.com/feeds/spreadsheets' - '/private/full') -WORKSHEETS_URL = ('http://spreadsheets.google.com/feeds/worksheets/' - '%s/private/full') -WORKSHEET_URL = ('http://spreadsheets.google.com/feeds/worksheets/' - '%s/private/full/%s') -TABLES_URL = 'http://spreadsheets.google.com/feeds/%s/tables' -RECORDS_URL = 'http://spreadsheets.google.com/feeds/%s/records/%s' -RECORD_URL = 'http://spreadsheets.google.com/feeds/%s/records/%s/%s' - - -class SpreadsheetsClient(gdata.client.GDClient): - api_version = '3' - auth_service = 'wise' - auth_scopes = gdata.gauth.AUTH_SCOPES['wise'] - - def get_spreadsheets(self, auth_token=None, - desired_class=gdata.spreadsheets.data.SpreadsheetsFeed, - **kwargs): - """Obtains a feed with the spreadsheets belonging to the current user. - - Args: - auth_token: An object which sets the Authorization HTTP header in its - modify_request method. Recommended classes include - gdata.gauth.ClientLoginToken and gdata.gauth.AuthSubToken - among others. Represents the current user. Defaults to None - and if None, this method will look for a value in the - auth_token member of SpreadsheetsClient. - desired_class: class descended from atom.core.XmlElement to which a - successful response should be converted. If there is no - converter function specified (converter=None) then the - desired_class will be used in calling the - atom.core.parse function. If neither - the desired_class nor the converter is specified, an - HTTP reponse object will be returned. Defaults to - gdata.spreadsheets.data.SpreadsheetsFeed. - """ - return self.get_feed(SPREADSHEETS_URL, auth_token=auth_token, - desired_class=desired_class, **kwargs) - - GetSpreadsheets = get_spreadsheets - - def get_worksheets(self, spreadsheet_key, auth_token=None, - desired_class=gdata.spreadsheets.data.WorksheetsFeed, - **kwargs): - """Finds the worksheets within a given spreadsheet. - - Args: - spreadsheet_key: str, The unique ID of this containing spreadsheet. This - can be the ID from the URL or as provided in a - Spreadsheet entry. - auth_token: An object which sets the Authorization HTTP header in its - modify_request method. Recommended classes include - gdata.gauth.ClientLoginToken and gdata.gauth.AuthSubToken - among others. Represents the current user. Defaults to None - and if None, this method will look for a value in the - auth_token member of SpreadsheetsClient. - desired_class: class descended from atom.core.XmlElement to which a - successful response should be converted. If there is no - converter function specified (converter=None) then the - desired_class will be used in calling the - atom.core.parse function. If neither - the desired_class nor the converter is specified, an - HTTP reponse object will be returned. Defaults to - gdata.spreadsheets.data.WorksheetsFeed. - """ - return self.get_feed(WORKSHEETS_URL % spreadsheet_key, - auth_token=auth_token, desired_class=desired_class, - **kwargs) - - GetWorksheets = get_worksheets - - def add_worksheet(self, spreadsheet_key, title, rows, cols, - auth_token=None, **kwargs): - """Creates a new worksheet entry in the spreadsheet. - - Args: - spreadsheet_key: str, The unique ID of this containing spreadsheet. This - can be the ID from the URL or as provided in a - Spreadsheet entry. - title: str, The title to be used in for the worksheet. - rows: str or int, The number of rows this worksheet should start with. - cols: str or int, The number of columns this worksheet should start with. - auth_token: An object which sets the Authorization HTTP header in its - modify_request method. Recommended classes include - gdata.gauth.ClientLoginToken and gdata.gauth.AuthSubToken - among others. Represents the current user. Defaults to None - and if None, this method will look for a value in the - auth_token member of SpreadsheetsClient. - """ - new_worksheet = gdata.spreadsheets.data.WorksheetEntry( - title=atom.data.Title(text=title), - row_count=gdata.spreadsheets.data.RowCount(text=str(rows)), - col_count=gdata.spreadsheets.data.ColCount(text=str(cols))) - return self.post(new_worksheet, WORKSHEETS_URL % spreadsheet_key, - auth_token=auth_token, **kwargs) - - AddWorksheet = add_worksheet - - def get_worksheet(self, spreadsheet_key, worksheet_id, - desired_class=gdata.spreadsheets.data.WorksheetEntry, - auth_token=None, **kwargs): - """Retrieves a single worksheet. - - Args: - spreadsheet_key: str, The unique ID of this containing spreadsheet. This - can be the ID from the URL or as provided in a - Spreadsheet entry. - worksheet_id: str, The unique ID for the worksheet withing the desired - spreadsheet. - auth_token: An object which sets the Authorization HTTP header in its - modify_request method. Recommended classes include - gdata.gauth.ClientLoginToken and gdata.gauth.AuthSubToken - among others. Represents the current user. Defaults to None - and if None, this method will look for a value in the - auth_token member of SpreadsheetsClient. - desired_class: class descended from atom.core.XmlElement to which a - successful response should be converted. If there is no - converter function specified (converter=None) then the - desired_class will be used in calling the - atom.core.parse function. If neither - the desired_class nor the converter is specified, an - HTTP reponse object will be returned. Defaults to - gdata.spreadsheets.data.WorksheetEntry. - - """ - return self.get_entry(WORKSHEET_URL % (spreadsheet_key, worksheet_id,), - auth_token=auth_token, desired_class=desired_class, - **kwargs) - - GetWorksheet = get_worksheet - - def add_table(self, spreadsheet_key, title, summary, worksheet_name, - header_row, num_rows, start_row, insertion_mode, - column_headers, auth_token=None, **kwargs): - """Creates a new table within the worksheet. - - Args: - spreadsheet_key: str, The unique ID of this containing spreadsheet. This - can be the ID from the URL or as provided in a - Spreadsheet entry. - title: str, The title for the new table within a worksheet. - summary: str, A description of the table. - worksheet_name: str The name of the worksheet in which this table - should live. - header_row: int or str, The number of the row in the worksheet which - will contain the column names for the data in this table. - num_rows: int or str, The number of adjacent rows in this table. - start_row: int or str, The number of the row at which the data begins. - insertion_mode: str - column_headers: dict of strings, maps the column letters (A, B, C) to - the desired name which will be viewable in the - worksheet. - auth_token: An object which sets the Authorization HTTP header in its - modify_request method. Recommended classes include - gdata.gauth.ClientLoginToken and gdata.gauth.AuthSubToken - among others. Represents the current user. Defaults to None - and if None, this method will look for a value in the - auth_token member of SpreadsheetsClient. - """ - data = gdata.spreadsheets.data.Data( - insertion_mode=insertion_mode, num_rows=str(num_rows), - start_row=str(start_row)) - for index, name in column_headers.iteritems(): - data.column.append(gdata.spreadsheets.data.Column( - index=index, name=name)) - new_table = gdata.spreadsheets.data.Table( - title=atom.data.Title(text=title), summary=atom.data.Summary(summary), - worksheet=gdata.spreadsheets.data.Worksheet(name=worksheet_name), - header=gdata.spreadsheets.data.Header(row=str(header_row)), data=data) - return self.post(new_table, TABLES_URL % spreadsheet_key, - auth_token=auth_token, **kwargs) - - AddTable = add_table - - def get_tables(self, spreadsheet_key, - desired_class=gdata.spreadsheets.data.TablesFeed, - auth_token=None, **kwargs): - """Retrieves a feed listing the tables in this spreadsheet. - - Args: - spreadsheet_key: str, The unique ID of this containing spreadsheet. This - can be the ID from the URL or as provided in a - Spreadsheet entry. - desired_class: class descended from atom.core.XmlElement to which a - successful response should be converted. If there is no - converter function specified (converter=None) then the - desired_class will be used in calling the - atom.core.parse function. If neither - the desired_class nor the converter is specified, an - HTTP reponse object will be returned. Defaults to - gdata.spreadsheets.data.TablesFeed. - auth_token: An object which sets the Authorization HTTP header in its - modify_request method. Recommended classes include - gdata.gauth.ClientLoginToken and gdata.gauth.AuthSubToken - among others. Represents the current user. Defaults to None - and if None, this method will look for a value in the - auth_token member of SpreadsheetsClient. - """ - return self.get_feed(TABLES_URL % spreadsheet_key, - desired_class=desired_class, auth_token=auth_token, - **kwargs) - - GetTables = get_tables - - def add_record(self, spreadsheet_key, table_id, fields, - title=None, auth_token=None, **kwargs): - """Adds a new row to the table. - - Args: - spreadsheet_key: str, The unique ID of this containing spreadsheet. This - can be the ID from the URL or as provided in a - Spreadsheet entry. - table_id: str, The ID of the table within the worksheet which should - receive this new record. The table ID can be found using the - get_table_id method of a gdata.spreadsheets.data.Table. - fields: dict of strings mapping column names to values. - title: str, optional The title for this row. - auth_token: An object which sets the Authorization HTTP header in its - modify_request method. Recommended classes include - gdata.gauth.ClientLoginToken and gdata.gauth.AuthSubToken - among others. Represents the current user. Defaults to None - and if None, this method will look for a value in the - auth_token member of SpreadsheetsClient. - """ - new_record = gdata.spreadsheets.data.Record() - if title is not None: - new_record.title = atom.data.Title(text=title) - for name, value in fields.iteritems(): - new_record.field.append(gdata.spreadsheets.data.Field( - name=name, text=value)) - return self.post(new_record, RECORDS_URL % (spreadsheet_key, table_id), - auth_token=auth_token, **kwargs) - - AddRecord = add_record - - def get_records(self, spreadsheet_key, table_id, - desired_class=gdata.spreadsheets.data.RecordsFeed, - auth_token=None, **kwargs): - """Retrieves the records in a table. - - Args: - spreadsheet_key: str, The unique ID of this containing spreadsheet. This - can be the ID from the URL or as provided in a - Spreadsheet entry. - table_id: str, The ID of the table within the worksheet whose records - we would like to fetch. The table ID can be found using the - get_table_id method of a gdata.spreadsheets.data.Table. - desired_class: class descended from atom.core.XmlElement to which a - successful response should be converted. If there is no - converter function specified (converter=None) then the - desired_class will be used in calling the - atom.core.parse function. If neither - the desired_class nor the converter is specified, an - HTTP reponse object will be returned. Defaults to - gdata.spreadsheets.data.RecordsFeed. - auth_token: An object which sets the Authorization HTTP header in its - modify_request method. Recommended classes include - gdata.gauth.ClientLoginToken and gdata.gauth.AuthSubToken - among others. Represents the current user. Defaults to None - and if None, this method will look for a value in the - auth_token member of SpreadsheetsClient. - """ - return self.get_feed(RECORDS_URL % (spreadsheet_key, table_id), - desired_class=desired_class, auth_token=auth_token, - **kwargs) - - GetRecords = get_records - - def get_record(self, spreadsheet_key, table_id, record_id, - desired_class=gdata.spreadsheets.data.Record, - auth_token=None, **kwargs): - """Retrieves a single record from the table. - - Args: - spreadsheet_key: str, The unique ID of this containing spreadsheet. This - can be the ID from the URL or as provided in a - Spreadsheet entry. - table_id: str, The ID of the table within the worksheet whose records - we would like to fetch. The table ID can be found using the - get_table_id method of a gdata.spreadsheets.data.Table. - record_id: str, The ID of the record within this table which we want to - fetch. You can find the record ID using get_record_id() on - an instance of the gdata.spreadsheets.data.Record class. - desired_class: class descended from atom.core.XmlElement to which a - successful response should be converted. If there is no - converter function specified (converter=None) then the - desired_class will be used in calling the - atom.core.parse function. If neither - the desired_class nor the converter is specified, an - HTTP reponse object will be returned. Defaults to - gdata.spreadsheets.data.RecordsFeed. - auth_token: An object which sets the Authorization HTTP header in its - modify_request method. Recommended classes include - gdata.gauth.ClientLoginToken and gdata.gauth.AuthSubToken - among others. Represents the current user. Defaults to None - and if None, this method will look for a value in the - auth_token member of SpreadsheetsClient.""" - return self.get_entry(RECORD_URL % (spreadsheet_key, table_id, record_id), - desired_class=desired_class, auth_token=auth_token, - **kwargs) - - GetRecord = get_record - - -class SpreadsheetQuery(gdata.client.Query): - - def __init__(self, title=None, title_exact=None, **kwargs): - """Adds Spreadsheets feed query parameters to a request. - - Args: - title: str Specifies the search terms for the title of a document. - This parameter used without title-exact will only submit partial - queries, not exact queries. - title_exact: str Specifies whether the title query should be taken as an - exact string. Meaningless without title. Possible values are - 'true' and 'false'. - """ - gdata.client.Query.__init__(self, **kwargs) - self.title = title - self.title_exact = title_exact - - def modify_request(self, http_request): - gdata.client._add_query_param('title', self.title, http_request) - gdata.client._add_query_param('title-exact', self.title_exact, - http_request) - gdata.client.Query.modify_request(self, http_request) - - ModifyRequest = modify_request - - -class WorksheetQuery(SpreadsheetQuery): - pass - - -class ListQuery(gdata.client.Query): - - def __init__(self, order_by=None, reverse=None, sq=None, **kwargs): - """Adds List-feed specific query parameters to a request. - - Args: - order_by: str Specifies what column to use in ordering the entries in - the feed. By position (the default): 'position' returns - rows in the order in which they appear in the GUI. Row 1, then - row 2, then row 3, and so on. By column: - 'column:columnName' sorts rows in ascending order based on the - values in the column with the given columnName, where - columnName is the value in the header row for that column. - reverse: str Specifies whether to sort in descending or ascending order. - Reverses default sort order: 'true' results in a descending - sort; 'false' (the default) results in an ascending sort. - sq: str Structured query on the full text in the worksheet. - [columnName][binaryOperator][value] - Supported binaryOperators are: - - (), for overriding order of operations - - = or ==, for strict equality - - <> or !=, for strict inequality - - and or &&, for boolean and - - or or ||, for boolean or - """ - gdata.client.Query.__init__(self, **kwargs) - self.order_by = order_by - self.reverse = reverse - self.sq = sq - - def modify_request(self, http_request): - gdata.client._add_query_param('orderby', self.order_by, http_request) - gdata.client._add_query_param('reverse', self.reverse, http_request) - gdata.client._add_query_param('sq', self.sq, http_request) - gdata.client.Query.modify_request(self, http_request) - - ModifyRequest = modify_request - - -class TableQuery(ListQuery): - pass - - -class CellQuery(gdata.client.Query): - - def __init__(self, min_row=None, max_row=None, min_col=None, max_col=None, - range=None, return_empty=None, **kwargs): - """Adds Cells-feed specific query parameters to a request. - - Args: - min_row: str or int Positional number of minimum row returned in query. - max_row: str or int Positional number of maximum row returned in query. - min_col: str or int Positional number of minimum column returned in query. - max_col: str or int Positional number of maximum column returned in query. - range: str A single cell or a range of cells. Use standard spreadsheet - cell-range notations, using a colon to separate start and end of - range. Examples: - - 'A1' and 'R1C1' both specify only cell A1. - - 'D1:F3' and 'R1C4:R3C6' both specify the rectangle of cells with - corners at D1 and F3. - return_empty: str If 'true' then empty cells will be returned in the feed. - If omitted, the default is 'false'. - """ - gdata.client.Query.__init__(self, **kwargs) - self.min_row = min_row - self.max_row = max_row - self.min_col = min_col - self.max_col = max_col - self.range = range - self.return_empty = return_empty - - def modify_request(self, http_request): - gdata.client._add_query_param('min-row', self.min_row, http_request) - gdata.client._add_query_param('max-row', self.max_row, http_request) - gdata.client._add_query_param('min-col', self.min_col, http_request) - gdata.client._add_query_param('max-col', self.max_col, http_request) - gdata.client._add_query_param('range', self.range, http_request) - gdata.client._add_query_param('return-empty', self.return_empty, - http_request) - gdata.client.Query.modify_request(self, http_request) - - ModifyRequest = modify_request diff --git a/gdata/spreadsheets/data.py b/gdata/spreadsheets/data.py deleted file mode 100644 index efb729f49a..0000000000 --- a/gdata/spreadsheets/data.py +++ /dev/null @@ -1,317 +0,0 @@ -#!/usr/bin/env python -# -# Copyright (C) 2009 Google 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. - - -# This module is used for version 2 of the Google Data APIs. - - -"""Provides classes and constants for the XML in the Google Spreadsheets API. - -Documentation for the raw XML which these classes represent can be found here: -http://code.google.com/apis/spreadsheets/docs/3.0/reference.html#Elements -""" - - -__author__ = 'j.s@google.com (Jeff Scudder)' - - -import atom.core -import gdata.data - - -GS_TEMPLATE = '{http://schemas.google.com/spreadsheets/2006}%s' -GSX_NAMESPACE = 'http://schemas.google.com/spreadsheets/2006/extended' - - -INSERT_MODE = 'insert' -OVERWRITE_MODE = 'overwrite' - - -WORKSHEETS_REL = 'http://schemas.google.com/spreadsheets/2006#worksheetsfeed' - - -class Error(Exception): - pass - - -class FieldMissing(Exception): - pass - - -class HeaderNotSet(Error): - """The desired column header had no value for the row in the list feed.""" - - -class Cell(atom.core.XmlElement): - """The gs:cell element. - - A cell in the worksheet. The <gs:cell> element can appear only as a child - of <atom:entry>. - """ - _qname = GS_TEMPLATE % 'cell' - col = 'col' - input_value = 'inputValue' - numeric_value = 'numericValue' - row = 'row' - - -class ColCount(atom.core.XmlElement): - """The gs:colCount element. - - Indicates the number of columns in the worksheet, including columns that - contain only empty cells. The <gs:colCount> element can appear as a child - of <atom:entry> or <atom:feed> - """ - _qname = GS_TEMPLATE % 'colCount' - - -class Field(atom.core.XmlElement): - """The gs:field element. - - A field single cell within a record. Contained in an <atom:entry>. - """ - _qname = GS_TEMPLATE % 'field' - index = 'index' - name = 'name' - - -class Column(Field): - """The gs:column element.""" - _qname = GS_TEMPLATE % 'column' - - -class Data(atom.core.XmlElement): - """The gs:data element. - - A data region of a table. Contained in an <atom:entry> element. - """ - _qname = GS_TEMPLATE % 'data' - column = [Column] - insertion_mode = 'insertionMode' - num_rows = 'numRows' - start_row = 'startRow' - - -class Header(atom.core.XmlElement): - """The gs:header element. - - Indicates which row is the header row. Contained in an <atom:entry>. - """ - _qname = GS_TEMPLATE % 'header' - row = 'row' - - -class RowCount(atom.core.XmlElement): - """The gs:rowCount element. - - Indicates the number of total rows in the worksheet, including rows that - contain only empty cells. The <gs:rowCount> element can appear as a - child of <atom:entry> or <atom:feed>. - """ - _qname = GS_TEMPLATE % 'rowCount' - - -class Worksheet(atom.core.XmlElement): - """The gs:worksheet element. - - The worksheet where the table lives.Contained in an <atom:entry>. - """ - _qname = GS_TEMPLATE % 'worksheet' - name = 'name' - - -class Spreadsheet(gdata.data.GDEntry): - """An Atom entry which represents a Google Spreadsheet.""" - - def find_worksheets_feed(self): - return self.find_url(WORKSHEETS_REL) - - FindWorksheetsFeed = find_worksheets_feed - - -class SpreadsheetsFeed(gdata.data.GDFeed): - """An Atom feed listing a user's Google Spreadsheets.""" - entry = [Spreadsheet] - - -class WorksheetEntry(gdata.data.GDEntry): - """An Atom entry representing a single worksheet in a spreadsheet.""" - row_count = RowCount - col_count = ColCount - - -class WorksheetsFeed(gdata.data.GDFeed): - """A feed containing the worksheets in a single spreadsheet.""" - entry = [WorksheetEntry] - - -class Table(gdata.data.GDEntry): - """An Atom entry that represents a subsection of a worksheet. - - A table allows you to treat part or all of a worksheet somewhat like a - table in a database that is, as a set of structured data items. Tables - don't exist until you explicitly create them before you can use a table - feed, you have to explicitly define where the table data comes from. - """ - data = Data - header = Header - worksheet = Worksheet - - def get_table_id(self): - if self.id.text: - return self.id.text.split('/')[-1] - return None - - GetTableId = get_table_id - - -class TablesFeed(gdata.data.GDFeed): - """An Atom feed containing the tables defined within a worksheet.""" - entry = [Table] - - -class Record(gdata.data.GDEntry): - """An Atom entry representing a single record in a table. - - Note that the order of items in each record is the same as the order of - columns in the table definition, which may not match the order of - columns in the GUI. - """ - field = [Field] - - def value_for_index(self, column_index): - for field in self.field: - if field.index == column_index: - return field.text - raise FieldMissing('There is no field for %s' % column_index) - - ValueForIndex = value_for_index - - def value_for_name(self, name): - for field in self.field: - if field.name == name: - return field.text - raise FieldMissing('There is no field for %s' % name) - - ValueForName = value_for_name - - def get_record_id(self): - if self.id.text: - return self.id.text.split('/')[-1] - return None - - -class RecordsFeed(gdata.data.GDFeed): - """An Atom feed containing the individuals records in a table.""" - entry = [Record] - - -class ListRow(atom.core.XmlElement): - """A gsx column value within a row. - - The local tag in the _qname is blank and must be set to the column - name. For example, when adding to a ListEntry, do: - col_value = ListRow(text='something') - col_value._qname = col_value._qname % 'mycolumnname' - """ - _qname = '{http://schemas.google.com/spreadsheets/2006/extended}%s' - - -class ListEntry(gdata.data.GDEntry): - """An Atom entry representing a worksheet row in the list feed. - - The values for a particular column can be get and set using - x.get_value('columnheader') and x.set_value('columnheader', 'value'). - See also the explanation of column names in the ListFeed class. - """ - - def get_value(self, column_name): - """Returns the displayed text for the desired column in this row. - - The formula or input which generated the displayed value is not accessible - through the list feed, to see the user's input, use the cells feed. - - If a column is not present in this spreadsheet, or there is no value - for a column in this row, this method will return None. - """ - values = self.get_elements(column_name, GSX_NAMESPACE) - if len(values) == 0: - return None - return values[0].text - - def set_value(self, column_name, value): - """Changes the value of cell in this row under the desired column name. - - Warning: if the cell contained a formula, it will be wiped out by setting - the value using the list feed since the list feed only works with - displayed values. - - No client side checking is performed on the column_name, you need to - ensure that the column_name is the local tag name in the gsx tag for the - column. For example, the column_name will not contain special characters, - spaces, uppercase letters, etc. - """ - # Try to find the column in this row to change an existing value. - values = self.get_elements(column_name, GSX_NAMESPACE) - if len(values) > 0: - values[0].text = value - else: - # There is no value in this row for the desired column, so add a new - # gsx:column_name element. - new_value = ListRow(text=value) - new_value._qname = new_value._qname % (column_name,) - self._other_elements.append(new_value) - - -class ListsFeed(gdata.data.GDFeed): - """An Atom feed in which each entry represents a row in a worksheet. - - The first row in the worksheet is used as the column names for the values - in each row. If a header cell is empty, then a unique column ID is used - for the gsx element name. - - Spaces in a column name are removed from the name of the corresponding - gsx element. - - Caution: The columnNames are case-insensitive. For example, if you see - a <gsx:e-mail> element in a feed, you can't know whether the column - heading in the original worksheet was "e-mail" or "E-Mail". - - Note: If two or more columns have the same name, then subsequent columns - of the same name have _n appended to the columnName. For example, if the - first column name is "e-mail", followed by columns named "E-Mail" and - "E-mail", then the columnNames will be gsx:e-mail, gsx:e-mail_2, and - gsx:e-mail_3 respectively. - """ - entry = [ListEntry] - - -class CellEntry(gdata.data.BatchEntry): - """An Atom entry representing a single cell in a worksheet.""" - cell = Cell - - -class CellsFeed(gdata.data.BatchFeed): - """An Atom feed contains one entry per cell in a worksheet. - - The cell feed supports batch operations, you can send multiple cell - operations in one HTTP request. - """ - entry = [CellEntry] - - def batch_set_cell(row, col, input): - pass - diff --git a/gdata/test_config.py b/gdata/test_config.py deleted file mode 100644 index 3aef1eed0b..0000000000 --- a/gdata/test_config.py +++ /dev/null @@ -1,408 +0,0 @@ -#!/usr/bin/env python - -# Copyright (C) 2009 Google 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. - - -import sys -import unittest -import getpass -import inspect -import atom.mock_http_core -import gdata.gauth - - -"""Loads configuration for tests which connect to Google servers. - -Settings used in tests are stored in a ConfigCollection instance in this -module called options. If your test needs to get a test related setting, -use - -import gdata.test_config -option_value = gdata.test_config.options.get_value('x') - -The above will check the command line for an '--x' argument, and if not -found will either use the default value for 'x' or prompt the user to enter -one. - -Your test can override the value specified by the user by performing: - -gdata.test_config.options.set_value('x', 'y') - -If your test uses a new option which you would like to allow the user to -specify on the command line or via a prompt, you can use the register_option -method as follows: - -gdata.test_config.options.register( - 'option_name', 'Prompt shown to the user', secret=False #As for password. - 'This is the description of the option, shown when help is requested.', - 'default value, provide only if you do not want the user to be prompted') -""" - - -class Option(object): - - def __init__(self, name, prompt, secret=False, description=None, default=None): - self.name = name - self.prompt = prompt - self.secret = secret - self.description = description - self.default = default - - def get(self): - value = self.default - # Check for a command line parameter. - for i in xrange(len(sys.argv)): - if sys.argv[i].startswith('--%s=' % self.name): - value = sys.argv[i].split('=')[1] - elif sys.argv[i] == '--%s' % self.name: - value = sys.argv[i + 1] - # If the param was not on the command line, ask the user to input the - # value. - # In order for this to prompt the user, the default value for the option - # must be None. - if value is None: - prompt = '%s: ' % self.prompt - if self.secret: - value = getpass.getpass(prompt) - else: - print 'You can specify this on the command line using --%s' % self.name - value = raw_input(prompt) - return value - - -class ConfigCollection(object): - - def __init__(self, options=None): - self.options = options or {} - self.values = {} - - def register_option(self, option): - self.options[option.name] = option - - def register(self, *args, **kwargs): - self.register_option(Option(*args, **kwargs)) - - def get_value(self, option_name): - if option_name in self.values: - return self.values[option_name] - value = self.options[option_name].get() - if value is not None: - self.values[option_name] = value - return value - - def set_value(self, option_name, value): - self.values[option_name] = value - - def render_usage(self): - message_parts = [] - for opt_name, option in self.options.iteritems(): - message_parts.append('--%s: %s' % (opt_name, option.description)) - return '\n'.join(message_parts) - - -options = ConfigCollection() - - -# Register the default options. -options.register( - 'username', - 'Please enter the email address of your test account', - description=('The email address you want to sign in with. ' - 'Make sure this is a test account as these tests may edit' - ' or delete data.')) -options.register( - 'password', - 'Please enter the password for your test account', - secret=True, description='The test account password.') -options.register( - 'clearcache', - 'Delete cached data? (enter true or false)', - description=('If set to true, any temporary files which cache test' - ' requests and responses will be deleted.'), - default='true') -options.register( - 'savecache', - 'Save requests and responses in a temporary file? (enter true or false)', - description=('If set to true, requests to the server and responses will' - ' be saved in temporary files.'), - default='false') -options.register( - 'runlive', - 'Run the live tests which contact the server? (enter true or false)', - description=('If set to true, the tests will make real HTTP requests to' - ' the servers. This slows down test execution and may' - ' modify the users data, be sure to use a test account.'), - default='true') -options.register( - 'ssl', - 'Run the live tests over SSL (enter true or false)', - description='If set to true, all tests will be performed over HTTPS (SSL)', - default='false') -options.register( - 'appsusername', - 'Please enter the email address of your test Apps domain account', - description=('The email address you want to sign in with. ' - 'Make sure this is a test account on your Apps domain as ' - 'these tests may edit or delete data.')) -options.register( - 'appspassword', - 'Please enter the password for your test Apps domain account', - secret=True, description='The test Apps account password.') - -# Other options which may be used if needed. -BLOG_ID_OPTION = Option( - 'blogid', - 'Please enter the ID of your test blog', - description=('The blog ID for the blog which should have test posts added' - ' to it. Example 7682659670455539811')) -TEST_IMAGE_LOCATION_OPTION = Option( - 'imgpath', - 'Please enter the full path to a test image to upload', - description=('This test image will be uploaded to a service which' - ' accepts a media file, it must be a jpeg.')) -SPREADSHEET_ID_OPTION = Option( - 'spreadsheetid', - 'Please enter the ID of a spreadsheet to use in these tests', - description=('The spreadsheet ID for the spreadsheet which should be' - ' modified by theses tests.')) -APPS_DOMAIN_OPTION = Option( - 'appsdomain', - 'Please enter your Google Apps domain', - description=('The domain the Google Apps is hosted on or leave blank' - ' if n/a')) -SITES_NAME_OPTION = Option( - 'sitename', - 'Please enter name of your Google Site', - description='The webspace name of the Site found in its URL.') -PROJECT_NAME_OPTION = Option( - 'project_name', - 'Please enter the name of your project hosting project', - description=('The name of the project which should have test issues added' - ' to it. Example gdata-python-client')) -ISSUE_ASSIGNEE_OPTION = Option( - 'issue_assignee', - 'Enter the email address of the target owner of the updated issue.', - description=('The email address of the user a created issue\'s owner will ' - ' become. Example testuser2@gmail.com')) -GA_TABLE_ID = Option( - 'table_id', - 'Enter the Table ID of the Google Analytics profile to test', - description=('The Table ID of the Google Analytics profile to test.' - ' Example ga:1174')) -TARGET_USERNAME_OPTION = Option( - 'targetusername', - 'Please enter the username (without domain) of the user which will be' - ' affected by the tests', - description=('The username of the user to be tested')) - -# Functions to inject a cachable HTTP client into a service client. -def configure_client(client, case_name, service_name, use_apps_auth=False): - """Sets up a mock client which will reuse a saved session. - - Should be called during setUp of each unit test. - - Handles authentication to allow the GDClient to make requests which - require an auth header. -where - Args: - client: a gdata.GDClient whose http_client member should be replaced - with a atom.mock_http_core.MockHttpClient so that repeated - executions can used cached responses instead of contacting - the server. - case_name: str The name of the test case class. Examples: 'BloggerTest', - 'ContactsTest'. Used to save a session - for the ClientLogin auth token request, so the case_name - should be reused if and only if the same username, password, - and service are being used. - service_name: str The service name as used for ClientLogin to identify - the Google Data API being accessed. Example: 'blogger', - 'wise', etc. - use_apps_auth: bool (optional) If set to True, use appsusername and - appspassword command-line args instead of username and - password respectively. - """ - # Use a mock HTTP client which will record and replay the HTTP traffic - # from these tests. - client.http_client = atom.mock_http_core.MockHttpClient() - client.http_client.cache_case_name = case_name - # Getting the auth token only needs to be done once in the course of test - # runs. - auth_token_key = '%s_auth_token' % service_name - if (auth_token_key not in options.values - and options.get_value('runlive') == 'true'): - client.http_client.cache_test_name = 'client_login' - cache_name = client.http_client.get_cache_file_name() - if options.get_value('clearcache') == 'true': - client.http_client.delete_session(cache_name) - client.http_client.use_cached_session(cache_name) - if not use_apps_auth: - username = options.get_value('username') - password = options.get_value('password') - else: - username = options.get_value('appsusername') - password = options.get_value('appspassword') - auth_token = client.request_client_login_token(username, password, - case_name, service=service_name) - options.values[auth_token_key] = gdata.gauth.token_to_blob(auth_token) - client.http_client.close_session() - # Allow a config auth_token of False to prevent the client's auth header - # from being modified. - if auth_token_key in options.values: - client.auth_token = gdata.gauth.token_from_blob( - options.values[auth_token_key]) - - -def configure_cache(client, test_name): - """Loads or begins a cached session to record HTTP traffic. - - Should be called at the beginning of each test method. - - Args: - client: a gdata.GDClient whose http_client member has been replaced - with a atom.mock_http_core.MockHttpClient so that repeated - executions can used cached responses instead of contacting - the server. - test_name: str The name of this test method. Examples: - 'TestClass.test_x_works', 'TestClass.test_crud_operations'. - This is used to name the recording of the HTTP requests and - responses, so it should be unique to each test method in the - test case. - """ - # Auth token is obtained in configure_client which is called as part of - # setUp. - client.http_client.cache_test_name = test_name - cache_name = client.http_client.get_cache_file_name() - if options.get_value('clearcache') == 'true': - client.http_client.delete_session(cache_name) - client.http_client.use_cached_session(cache_name) - - -def close_client(client): - """Saves the recoded responses to a temp file if the config file allows. - - This should be called in the unit test's tearDown method. - - Checks to see if the 'savecache' option is set to 'true', to make sure we - only save sessions to repeat if the user desires. - """ - if client and options.get_value('savecache') == 'true': - # If this was a live request, save the recording. - client.http_client.close_session() - - -def configure_service(service, case_name, service_name): - """Sets up a mock GDataService v1 client to reuse recorded sessions. - - Should be called during setUp of each unit test. This is a duplicate of - configure_client, modified to handle old v1 service classes. - """ - service.http_client.v2_http_client = atom.mock_http_core.MockHttpClient() - service.http_client.v2_http_client.cache_case_name = case_name - # Getting the auth token only needs to be done once in the course of test - # runs. - auth_token_key = 'service_%s_auth_token' % service_name - if (auth_token_key not in options.values - and options.get_value('runlive') == 'true'): - service.http_client.v2_http_client.cache_test_name = 'client_login' - cache_name = service.http_client.v2_http_client.get_cache_file_name() - if options.get_value('clearcache') == 'true': - service.http_client.v2_http_client.delete_session(cache_name) - service.http_client.v2_http_client.use_cached_session(cache_name) - service.ClientLogin(options.get_value('username'), - options.get_value('password'), - service=service_name, source=case_name) - options.values[auth_token_key] = service.GetClientLoginToken() - service.http_client.v2_http_client.close_session() - if auth_token_key in options.values: - service.SetClientLoginToken(options.values[auth_token_key]) - - -def configure_service_cache(service, test_name): - """Loads or starts a session recording for a v1 Service object. - - Duplicates the behavior of configure_cache, but the target for this - function is a v1 Service object instead of a v2 Client. - """ - service.http_client.v2_http_client.cache_test_name = test_name - cache_name = service.http_client.v2_http_client.get_cache_file_name() - if options.get_value('clearcache') == 'true': - service.http_client.v2_http_client.delete_session(cache_name) - service.http_client.v2_http_client.use_cached_session(cache_name) - - -def close_service(service): - if service and options.get_value('savecache') == 'true': - # If this was a live request, save the recording. - service.http_client.v2_http_client.close_session() - - -def build_suite(classes): - """Creates a TestSuite for all unit test classes in the list. - - Assumes that each of the classes in the list has unit test methods which - begin with 'test'. Calls unittest.makeSuite. - - Returns: - A new unittest.TestSuite containing a test suite for all classes. - """ - suites = [unittest.makeSuite(a_class, 'test') for a_class in classes] - return unittest.TestSuite(suites) - - -def check_data_classes(test, classes): - import inspect - for data_class in classes: - test.assert_(data_class.__doc__ is not None, - 'The class %s should have a docstring' % data_class) - if hasattr(data_class, '_qname'): - qname_versions = None - if isinstance(data_class._qname, tuple): - qname_versions = data_class._qname - else: - qname_versions = (data_class._qname,) - for versioned_qname in qname_versions: - test.assert_(isinstance(versioned_qname, str), - 'The class %s has a non-string _qname' % data_class) - test.assert_(not versioned_qname.endswith('}'), - 'The _qname for class %s is only a namespace' % ( - data_class)) - - for attribute_name, value in data_class.__dict__.iteritems(): - # Ignore all elements that start with _ (private members) - if not attribute_name.startswith('_'): - try: - if not (isinstance(value, str) or inspect.isfunction(value) - or (isinstance(value, list) - and issubclass(value[0], atom.core.XmlElement)) - or type(value) == property # Allow properties. - or inspect.ismethod(value) # Allow methods. - or issubclass(value, atom.core.XmlElement)): - test.fail( - 'XmlElement member should have an attribute, XML class,' - ' or list of XML classes as attributes.') - - except TypeError: - test.fail('Element %s in %s was of type %s' % ( - attribute_name, data_class._qname, type(value))) - - -def check_clients_with_auth(test, classes): - for client_class in classes: - test.assert_(hasattr(client_class, 'api_version')) - test.assert_(isinstance(client_class.auth_service, (str, unicode, int))) - test.assert_(hasattr(client_class, 'auth_service')) - test.assert_(isinstance(client_class.auth_service, (str, unicode))) - test.assert_(hasattr(client_class, 'auth_scopes')) - test.assert_(isinstance(client_class.auth_scopes, (list, tuple))) diff --git a/gdata/test_config_template.py b/gdata/test_config_template.py deleted file mode 100644 index e27f71f068..0000000000 --- a/gdata/test_config_template.py +++ /dev/null @@ -1,96 +0,0 @@ -#!/usr/bin/env python - - -# Copyright (C) 2009 Google 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. - - -"""Fill in this module with configuration data to use in tests. - -See comments in the source code for explanations of the settings. -""" - -import os - - -# To actually run the tests which use this configuration information you must -# change RUN_LIVE_TESTS to True. -RUN_LIVE_TESTS = False - - -# If set to True, the client will save responses from the server and reuse -# them in future runs of the test. -CACHE_RESPONSES = True - -# If set to True, the client will make HTTP requests to the server regardless -# of a cache file. If True, caches from previous sessions will be deleted. -# If False (the default) cached sessions will be reused if they exist. -CLEAR_CACHE = True - - -GOOGLE_ACCOUNT_EMAIL = '<your email>' -GOOGLE_ACCOUNT_PASSWORD = '<your password>' -# For example, the TEST_FILES_DIR might be -# '/home/username/svn/gdata-python-client/tests' -TEST_FILES_DIR = '<location of the tests directory>' - - -class NoAuthConfig(object): - auth_token = False - - -class TestConfig(object): - service = None - auth_token = None - - def email(cls): - """Provides email to log into the test account for this service. - - By default uses GOOGLE_ACCOUNT_EMAIL, so overwrite this function if you - have a service-specific test account. - """ - return GOOGLE_ACCOUNT_EMAIL - - email = classmethod(email) - - def password(cls): - """Provides password to log into the test account for this service. - - By default uses GOOGLE_ACCOUNT_PASSWORD, so overwrite this function if - you have a service-specific test account. - """ - return GOOGLE_ACCOUNT_PASSWORD - - password = classmethod(password) - - -class BloggerConfig(TestConfig): - service = 'blogger' - title = 'A Test Post' - content = 'This is a <b>test</b>.' - blog_id = '<your test blog\'s id>' - - -class ContactsConfig(TestConfig): - service = 'cp' - - def get_image_location(cls): - return os.path.join(TEST_FILES_DIR, 'files', 'testimage.jpg') - - get_image_location = classmethod(get_image_location) - -class MapsConfig(TestConfig): - service = 'local' - map_title = 'Some test map' - map_summary = 'A test description' diff --git a/gdata/test_data.py b/gdata/test_data.py deleted file mode 100644 index d9f62c5db7..0000000000 --- a/gdata/test_data.py +++ /dev/null @@ -1,5397 +0,0 @@ -#!/usr/bin/python -# -# Copyright (C) 2006 Google 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. - - - -XML_ENTRY_1 = """<?xml version='1.0'?> -<entry xmlns='http://www.w3.org/2005/Atom' - xmlns:g='http://base.google.com/ns/1.0'> - <category scheme="http://base.google.com/categories/itemtypes" - term="products"/> - <id> http://www.google.com/test/id/url </id> - <title type='text'>Testing 2000 series laptop - -

A Testing Laptop
-
- - - Computer - Laptop - testing laptop - products -""" - - -TEST_BASE_ENTRY = """ - - - Testing 2000 series laptop - -
A Testing Laptop
-
- - yes - - - - Computer - Laptop - testing laptop - products -
""" - - -BIG_FEED = """ - - dive into mark - - A <em>lot</em> of effort - went into making this effortless - - 2005-07-31T12:29:29Z - tag:example.org,2003:3 - - - Copyright (c) 2003, Mark Pilgrim - - Example Toolkit - - - Atom draft-07 snapshot - - - tag:example.org,2003:3.2397 - 2005-07-31T12:29:29Z - 2003-12-13T08:29:29-04:00 - - Mark Pilgrim - http://example.org/ - f8dy@example.com - - - Sam Ruby - - - Joe Gregorio - - -
-

[Update: The Atom draft is finished.]

-
-
-
-
-""" - -SMALL_FEED = """ - - Example Feed - - 2003-12-13T18:30:02Z - - John Doe - - urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6 - - Atom-Powered Robots Run Amok - - urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a - 2003-12-13T18:30:02Z - Some text. - - -""" - -GBASE_FEED = """ - -http://www.google.com/base/feeds/snippets -2007-02-08T23:18:21.935Z -Items matching query: digital camera - - - - - - - - -GoogleBase -2171885 -1 -25 - -http://www.google.com/base/feeds/snippets/13246453826751927533 -2007-02-08T13:23:27.000Z -2007-02-08T16:40:57.000Z - - -Digital Camera Battery Notebook Computer 12v DC Power Cable - 5.5mm x 2.5mm (Center +) Camera Connecting Cables -Notebook Computer 12v DC Power Cable - 5.5mm x 2.1mm (Center +) This connection cable will allow any Digital Pursuits battery pack to power portable computers that operate with 12v power and have a 2.1mm power connector (center +) Digital ... - - - - - -B&H Photo-Video -anon-szot0wdsq0at@base.google.com - -PayPal & Bill Me Later credit available online only. -new -420 9th Ave. 10001 -305668-REG -Products -Digital Camera Battery -2007-03-10T13:23:27.000Z -1172711 -34.95 usd -Digital Photography>Camera Connecting Cables -EN -DCB5092 -US -1.0 -http://base.google.com/base_image?q=http%3A%2F%2Fwww.bhphotovideo.com%2Fimages%2Fitems%2F305668.jpg&dhm=ffffffff84c9a95e&size=6 - - -http://www.google.com/base/feeds/snippets/10145771037331858608 -2007-02-08T13:23:27.000Z -2007-02-08T16:40:57.000Z - - -Digital Camera Battery Electronic Device 5v DC Power Cable - 5.5mm x 2.5mm (Center +) Camera Connecting Cables -Electronic Device 5v DC Power Cable - 5.5mm x 2.5mm (Center +) This connection cable will allow any Digital Pursuits battery pack to power any electronic device that operates with 5v power and has a 2.5mm power connector (center +) Digital ... - - - - - -B&H Photo-Video -anon-szot0wdsq0at@base.google.com - -420 9th Ave. 10001 -new -0.18 -US -Digital Photography>Camera Connecting Cables -PayPal & Bill Me Later credit available online only. -305656-REG -http://base.google.com/base_image?q=http%3A%2F%2Fwww.bhphotovideo.com%2Fimages%2Fitems%2F305656.jpg&dhm=7315bdc8&size=6 -DCB5108 -838098005108 -34.95 usd -EN -Digital Camera Battery -1172711 -Products -2007-03-10T13:23:27.000Z - - -http://www.google.com/base/feeds/snippets/3128608193804768644 -2007-02-08T02:21:27.000Z -2007-02-08T15:40:13.000Z - - -Digital Camera Battery Power Cable for Kodak 645 Pro-Back ProBack & DCS-300 Series Camera Connecting Cables -Camera Connection Cable - to Power Kodak 645 Pro-Back DCS-300 Series Digital Cameras This connection cable will allow any Digital Pursuits battery pack to power the following digital cameras: Kodak DCS Pro Back 645 DCS-300 series Digital Photography ... - - - - - -B&H Photo-Video -anon-szot0wdsq0at@base.google.com - -0.3 -DCB6006 -http://base.google.com/base_image?q=http%3A%2F%2Fwww.bhphotovideo.com%2Fimages%2Fitems%2F305685.jpg&dhm=72f0ca0a&size=6 -420 9th Ave. 10001 -PayPal & Bill Me Later credit available online only. -Products -US -digital kodak camera -Digital Camera Battery -2007-03-10T02:21:27.000Z -EN -new -34.95 usd -1172711 -Digital Photography>Camera Connecting Cables -305685-REG - -""" - -EXTENSION_TREE = """ - - - John Doe - Bar - - - -""" - -TEST_AUTHOR = """ - - John Doe - johndoes@someemailadress.com - http://www.google.com - -""" - -TEST_LINK = """ - -""" - -TEST_GBASE_ATTRIBUTE = """ - Digital Camera Battery -""" - - -CALENDAR_FEED = """ - - http://www.google.com/calendar/feeds/default - 2007-03-20T22:48:57.833Z - GData Ops Demo's Calendar List - - - - - GData Ops Demo - gdata.ops.demo@gmail.com - - - Google Calendar - 1 - - - http://www.google.com/calendar/feeds/default/gdata.ops.demo%40gmail.com - 2007-03-20T22:48:57.837Z - 2007-03-20T22:48:52.000Z - GData Ops Demo - - - - - - GData Ops Demo - gdata.ops.demo@gmail.com - - - - - - - - - http://www.google.com/calendar/feeds/default/jnh21ovnjgfph21h32gvms2758%40group.calendar.google.com - 2007-03-20T22:48:57.837Z - 2007-03-20T22:48:53.000Z - GData Ops Demo Secondary Calendar - - - - - - - GData Ops Demo Secondary Calendar - - - - - - - - -""" - -CALENDAR_FULL_EVENT_FEED = """ - - - http://www.google.com/calendar/feeds/default/private/full - 2007-03-20T21:29:57.000Z - - GData Ops Demo - GData Ops Demo - - - - - - - - GData Ops Demo - gdata.ops.demo@gmail.com - - - Google Calendar - 10 - 1 - 25 - - - - http://www.google.com/calendar/feeds/default/private/full/o99flmgmkfkfrr8u745ghr3100 - 2007-03-20T21:29:52.000Z - 2007-03-20T21:29:57.000Z - - test deleted - - - - - - - - GData Ops Demo - gdata.ops.demo@gmail.com - - - - - - - - - - - - - - - - - - - - - http://www.google.com/calendar/feeds/default/private/full/2qt3ao5hbaq7m9igr5ak9esjo0 - 2007-03-20T21:26:04.000Z - 2007-03-20T21:28:46.000Z - - Afternoon at Dolores Park with Kim - - - - - - - - GData Ops Demo - gdata.ops.demo@gmail.com - - - - - - - - - - - - - - - - - - - - - - - - - - - - - http://www.google.com/calendar/feeds/default/private/full/uvsqhg7klnae40v50vihr1pvos - 2007-03-20T21:28:37.000Z - 2007-03-20T21:28:37.000Z - - Team meeting - - - - - - - - GData Ops Demo - gdata.ops.demo@gmail.com - - DTSTART;TZID=America/Los_Angeles:20070323T090000 - DTEND;TZID=America/Los_Angeles:20070323T100000 - RRULE:FREQ=WEEKLY;BYDAY=FR;UNTIL=20070817T160000Z;WKST=SU - BEGIN:VTIMEZONE TZID:America/Los_Angeles - X-LIC-LOCATION:America/Los_Angeles BEGIN:STANDARD - TZOFFSETFROM:-0700 TZOFFSETTO:-0800 TZNAME:PST - DTSTART:19701025T020000 RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU - END:STANDARD BEGIN:DAYLIGHT TZOFFSETFROM:-0800 TZOFFSETTO:-0700 - TZNAME:PDT DTSTART:19700405T020000 - RRULE:FREQ=YEARLY;BYMONTH=4;BYDAY=1SU END:DAYLIGHT - END:VTIMEZONE - - - - - - - - - - - - - - http://www.google.com/calendar/feeds/default/private/full/st4vk9kiffs6rasrl32e4a7alo - 2007-03-20T21:25:46.000Z - 2007-03-20T21:25:46.000Z - - Movie with Kim and danah - - - - - - - - GData Ops Demo - gdata.ops.demo@gmail.com - - - - - - - - - - - - - - - - - - - - - http://www.google.com/calendar/feeds/default/private/full/ofl1e45ubtsoh6gtu127cls2oo - 2007-03-20T21:24:43.000Z - 2007-03-20T21:25:08.000Z - - Dinner with Kim and Sarah - - - - - - - - GData Ops Demo - gdata.ops.demo@gmail.com - - - - - - - - - - - - - - - - - - - - - http://www.google.com/calendar/feeds/default/private/full/b69s2avfi2joigsclecvjlc91g - 2007-03-20T21:24:19.000Z - 2007-03-20T21:25:05.000Z - - Dinner with Jane and John - - - - - - - - GData Ops Demo - gdata.ops.demo@gmail.com - - - - - - - - - - - - - - - - - - - - - http://www.google.com/calendar/feeds/default/private/full/u9p66kkiotn8bqh9k7j4rcnjjc - 2007-03-20T21:24:33.000Z - 2007-03-20T21:24:33.000Z - - Tennis with Elizabeth - - - - - - - - GData Ops Demo - gdata.ops.demo@gmail.com - - - - - - - - - - - - - - - - - - - - - http://www.google.com/calendar/feeds/default/private/full/76oj2kceidob3s708tvfnuaq3c - 2007-03-20T21:24:00.000Z - 2007-03-20T21:24:00.000Z - - Lunch with Jenn - - - - - - - - GData Ops Demo - gdata.ops.demo@gmail.com - - - - - - - - - - - - - - - - - - - - - http://www.google.com/calendar/feeds/default/private/full/5np9ec8m7uoauk1vedh5mhodco - 2007-03-20T07:50:02.000Z - 2007-03-20T20:39:26.000Z - - test entry - test desc - - - - - - - GData Ops Demo - gdata.ops.demo@gmail.com - - - - - - - - - - - - - - - - - - - - - - - - - - - - - http://www.google.com/calendar/feeds/default/private/full/fu6sl0rqakf3o0a13oo1i1a1mg - 2007-02-14T23:23:37.000Z - 2007-02-14T23:25:30.000Z - - test - - - - - - - - - - - GData Ops Demo - gdata.ops.demo@gmail.com - - - - - - - - - - - - - - - - - - - - - http://www.google.com/calendar/feeds/default/private/full/h7a0haa4da8sil3rr19ia6luvc - 2007-07-16T22:13:28.000Z - 2007-07-16T22:13:29.000Z - - - - - - - - - - - - - GData Ops Demo - gdata.ops.demo@gmail.com - - - - - - - - - - - - - -""" - -CALENDAR_BATCH_REQUEST = """ - - - - 1 - - - Event inserted via batch - - - 2 - - http://www.google.com/calendar/feeds/default/private/full/glcs0kv2qqa0gf52qi1jo018gc - - Event queried via batch - - - 3 - - http://www.google.com/calendar/feeds/default/private/full/ujm0go5dtngdkr6u91dcqvj0qs - - Event updated via batch - - - - - - 4 - - http://www.google.com/calendar/feeds/default/private/full/d8qbg9egk1n6lhsgq1sjbqffqc - - Event deleted via batch - - - - - -""" - -CALENDAR_BATCH_RESPONSE = """ - - http://www.google.com/calendar/feeds/default/private/full - 2007-09-21T23:01:00.380Z - - Batch Feed - - - - - 1 - - - http://www.google.com/calendar/feeds/default/private/full/n9ug78gd9tv53ppn4hdjvk68ek - - Event inserted via batch - - - - - - 2 - - - http://www.google.com/calendar/feeds/default/private/full/glsc0kv2aqa0ff52qi1jo018gc - - Event queried via batch - - - - - - 3 - - - http://www.google.com/calendar/feeds/default/private/full/ujm0go5dtngdkr6u91dcqvj0qs - - Event updated via batch - - - - 3 - - - - - 4 - - - http://www.google.com/calendar/feeds/default/private/full/d8qbg9egk1n6lhsgq1sjbqffqc - - Event deleted via batch - Deleted - - -""" - -GBASE_ATTRIBUTE_FEED = """ - - http://www.google.com/base/feeds/attributes - 2006-11-01T20:35:59.578Z - - - Attribute histogram for query: [item type:jobs] - - - - GoogleBase - 16 - 1 - 16 - - http://www.google.com/base/feeds/attributes/job+industry%28text%29N%5Bitem+type%3Ajobs%5D - 2006-11-01T20:36:00.100Z - job industry(text) - Attribute"job industry" of type text. - - - - it internet - healthcare - information technology - accounting - clerical and administrative - other - sales and sales management - information systems - engineering and architecture - sales - - - -""" - - -GBASE_ATTRIBUTE_ENTRY = """ - - http://www.google.com/base/feeds/attributes/job+industry%28text%29N%5Bitem+type%3Ajobs%5D - 2006-11-01T20:36:00.100Z - job industry(text) - Attribute"job industry" of type text. - - - - it internet - healthcare - information technology - accounting - clerical and administrative - other - sales and sales management - information systems - engineering and architecture - sales - - -""" - -GBASE_LOCALES_FEED = """ - - http://www.google.com/base/feeds/locales/ - 2006-06-13T18:11:40.120Z - Locales - - - - - Google Inc. - base@google.com - - GoogleBase - 3 - 25 - - - http://www.google.com/base/feeds/locales/en_US - 2006-03-27T22:27:36.658Z - - - en_US - en_US - - - - - - http://www.google.com/base/feeds/locales/en_GB - 2006-06-13T18:14:18.601Z - - en_GB - en_GB - - - - - http://www.google.com/base/feeds/locales/de_DE - 2006-06-13T18:14:18.601Z - - de_DE - de_DE - - - -""" - -GBASE_STRING_ENCODING_ENTRY = """ - - http://www.google.com/base/feeds/snippets/17495780256183230088 - 2007-12-09T03:13:07.000Z - 2008-01-07T03:26:46.000Z - - Digital Camera Cord Fits SONY Cybershot DSC-R1 S40 - SONY \xC2\xB7 Cybershot Digital Camera Usb Cable DESCRIPTION - This is a 2.5 USB 2.0 A to Mini B (5 Pin) high quality digital camera - cable used for connecting your Sony Digital Cameras and Camcoders. Backward - Compatible with USB 2.0, 1.0 and 1.1. Fully ... - - - - eBay - - Products - EN - US - 0.99 usd - http://thumbs.ebaystatic.com/pict/270195049057_1.jpg - Cameras & Photo>Digital Camera Accessories>Cables - Cords & Connectors>USB Cables>For Other Brands - 11729 - 270195049057 - 2008-02-06T03:26:46Z -""" - - -RECURRENCE_EXCEPTION_ENTRY = """ - - http://www.google.com/calendar/feeds/default/private/composite/i7lgfj69mjqjgnodklif3vbm7g - 2007-04-05T21:51:49.000Z - 2007-04-05T21:51:49.000Z - - testDavid - - - - - - gdata ops - gdata.ops.test@gmail.com - - - - - - - - - - DTSTART;TZID=America/Anchorage:20070403T100000 - DTEND;TZID=America/Anchorage:20070403T110000 - RRULE:FREQ=DAILY;UNTIL=20070408T180000Z;WKST=SU - EXDATE;TZID=America/Anchorage:20070407T100000 - EXDATE;TZID=America/Anchorage:20070405T100000 - EXDATE;TZID=America/Anchorage:20070404T100000 BEGIN:VTIMEZONE - TZID:America/Anchorage X-LIC-LOCATION:America/Anchorage - BEGIN:STANDARD TZOFFSETFROM:-0800 TZOFFSETTO:-0900 TZNAME:AKST - DTSTART:19701025T020000 RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU - END:STANDARD BEGIN:DAYLIGHT TZOFFSETFROM:-0900 TZOFFSETTO:-0800 - TZNAME:AKDT DTSTART:19700405T020000 - RRULE:FREQ=YEARLY;BYMONTH=4;BYDAY=1SU END:DAYLIGHT - END:VTIMEZONE - - - - - - i7lgfj69mjqjgnodklif3vbm7g_20070407T180000Z - 2007-04-05T21:51:49.000Z - 2007-04-05T21:52:58.000Z - - testDavid - - - - gdata ops - gdata.ops.test@gmail.com - - - - - - - - - - - - - - - - - - - 2007-04-05T21:54:09.285Z - - - Comments for: testDavid - - - - - - - - - - - - """ - -NICK_ENTRY = """ - - https://apps-apis.google.com/a/feeds/example.com/nickname/2.0/Foo - 1970-01-01T00:00:00.000Z - - Foo - - - - -""" - -NICK_FEED = """ - - - http://apps-apis.google.com/a/feeds/example.com/nickname/2.0 - - 1970-01-01T00:00:00.000Z - - Nicknames for user SusanJones - - - - 1 - 2 - - - http://apps-apis.google.com/a/feeds/example.com/nickname/2.0/Foo - - - Foo - - - - - - - - http://apps-apis.google.com/a/feeds/example.com/nickname/2.0/suse - - - suse - - - - - -""" - -USER_ENTRY = """ - - https://apps-apis.google.com/a/feeds/example.com/user/2.0/TestUser - 1970-01-01T00:00:00.000Z - - TestUser - - - - - - - -""" - -USER_FEED = """ - - - http://apps-apis.google.com/a/feeds/example.com/user/2.0 - - 1970-01-01T00:00:00.000Z - - Users - """ - -EMAIL_LIST_ENTRY = """ - - - https://apps-apis.google.com/a/feeds/example.com/emailList/2.0/testlist - - 1970-01-01T00:00:00.000Z - - testlist - - - - -""" - -EMAIL_LIST_FEED = """ - - - http://apps-apis.google.com/a/feeds/example.com/emailList/2.0 - - 1970-01-01T00:00:00.000Z - - EmailLists - """ - -EMAIL_LIST_RECIPIENT_ENTRY = """ - - https://apps-apis.google.com/a/feeds/example.com/emailList/2.0/us-sales/recipient/TestUser%40example.com - 1970-01-01T00:00:00.000Z - - TestUser - - - -""" - -EMAIL_LIST_RECIPIENT_FEED = """ - - - http://apps-apis.google.com/a/feeds/example.com/emailList/2.0/us-sales/recipient - - 1970-01-01T00:00:00.000Z - - Recipients for email list us-sales - """ - -ACL_FEED = """ - - http://www.google.com/calendar/feeds/liz%40gmail.com/acl/full - 2007-04-21T00:52:04.000Z - Elizabeth Bennet's access control list - - - - - - - - - Google Calendar - 2 - 1 - - http://www.google.com/calendar/feeds/liz%40gmail.com/acl/full/user%3Aliz%40gmail.com - 2007-04-21T00:52:04.000Z - - - owner - - - - - - - Elizabeth Bennet - liz@gmail.com - - - - - - - http://www.google.com/calendar/feeds/liz%40gmail.com/acl/full/default - 2007-04-21T00:52:04.000Z - - - read - - - - - - - Elizabeth Bennet - liz@gmail.com - - - - - - """ - -ACL_ENTRY = """ - - http://www.google.com/calendar/feeds/liz%40gmail.com/acl/full/user%3Aliz%40gmail.com - 2007-04-21T00:52:04.000Z - - - owner - - - - - - - Elizabeth Bennet - liz@gmail.com - - - - - """ - -DOCUMENT_LIST_FEED = """ -21test.usertest.user@gmail.comhttp://docs.google.com/feeds/documents/private/full/spreadsheet%3AsupercalifragilisticexpeadociousTest Spreadsheet2007-07-03T18:03:32.045Z - -document:dfrkj84g_3348jbxpxcd - - test.user - test.user@gmail.com - -2009-03-05T07:48:21.493Z - -test.usertest.user@gmail.comhttp://docs.google.com/feeds/documents/private/full/document%3Agr00vyTest Document2007-07-03T18:02:50.338Z - - - test.user - test.user@gmail.com - - - 2009-03-05T07:48:21.493Z -http://docs.google.com/feeds/documents/private/fullAvailable -Documents - -test.user@gmail.com2007-07-09T23:07:21.898Z - -""" - -DOCUMENT_LIST_ENTRY = """ - -test.usertest.user@gmail.com -http://docs.google.com/feeds/documents/private/full/spreadsheet%3Asupercalifragilisticexpealidocious - -Test Spreadsheet2007-07-03T18:03:32.045Z -spreadsheet:supercalifragilisticexpealidocious - - test.user - test.user@gmail.com - -2009-03-05T07:48:21.493Z - - -""" - -DOCUMENT_LIST_ENTRY_V3 = """ - -test.usertest.user@gmail.com -http://docs.google.com/feeds/documents/private/full/spreadsheet%3Asupercalifragilisticexpealidocious - - -Test Spreadsheet2007-07-03T18:03:32.045Z -spreadsheet:supercalifragilisticexpealidocious - - test.user - test.user@gmail.com - -2009-03-05T07:48:21.493Z - -1000 - - - -""" - -DOCUMENT_LIST_ACL_ENTRY = """ - - - - -""" - -DOCUMENT_LIST_ACL_FEED = """ - -http://docs.google.com/feeds/acl/private/full/spreadsheet%3ApFrmMi8feTQYCgZpwUQ -2009-02-22T03:48:25.895Z - -Document Permissions - - - - -2 -1 - - http://docs.google.com/feeds/acl/private/full/spreadsheet%3ApFrmMi8feTQp4pwUwUQ/user%3Auser%40gmail.com - 2009-02-22T03:48:25.896Z - - Document Permission - user@gmail.com - - - - - - - http://docs.google.com/feeds/acl/private/full/spreadsheet%3ApFrmMi8fCgZp4pwUwUQ/user%3Auser2%40google.com - 2009-02-22T03:48:26.257Z - - Document Permission - user2@google.com - - - - - -""" - -DOCUMENT_LIST_REVISION_FEED = """ - -https://docs.google.com/feeds/default/private/full/resource_id/revisions -2009-08-17T04:22:10.378Z -Document Revisions - - - -6 -1 - - https://docs.google.com/feeds/id/resource_id/revisions/2 - 2009-08-17T04:22:10.440Z - 2009-08-14T07:11:34.197Z - Revision 2 - - - - - - another_user - another_user@gmail.com - - - - - - -""" - -BATCH_ENTRY = """ - - http://www.google.com/base/feeds/items/2173859253842813008 - 2006-07-11T14:51:43.560Z - 2006-07-11T14:51: 43.560Z - title - content - - - recipes - - itemB - -""" - -BATCH_FEED_REQUEST = """ - - My Batch Feed - - http://www.google.com/base/feeds/items/13308004346459454600 - - - - http://www.google.com/base/feeds/items/17437536661927313949 - - - - ... - ... - itemA - - recipes - - - ... - ... - itemB - - recipes - -""" - -BATCH_FEED_RESULT = """ - - http://www.google.com/base/feeds/items - 2006-07-11T14:51:42.894Z - My Batch - - - - - http://www.google.com/base/feeds/items/2173859253842813008 - 2006-07-11T14:51:43.560Z - 2006-07-11T14:51: 43.560Z - ... - ... - - - recipes - - itemB - - - - http://www.google.com/base/feeds/items/11974645606383737963 - 2006-07-11T14:51:43.247Z - 2006-07-11T14:51: 43.247Z - ... - ... - - - recipes - - itemA - - - - http://www.google.com/base/feeds/items/13308004346459454600 - 2006-07-11T14:51:42.894Z - Error - Bad request - - - - - - - - http://www.google.com/base/feeds/items/17437536661927313949 - 2006-07-11T14:51:43.246Z - Deleted - - - -""" - -ALBUM_FEED = """ - - http://picasaweb.google.com/data/feed/api/user/sample.user/albumid/1 - 2007-09-21T18:23:05.000Z - - Test - - public - http://lh6.google.com/sample.user/Rt8WNoDZEJE/AAAAAAAAABk/HQGlDhpIgWo/s160-c/Test.jpg - - - - - - sample - http://picasaweb.google.com/sample.user - - Picasaweb 4 - 1 - 500 - 1 - Test - - public 1188975600000 - 2 - sample.user - sample - true - 0 - http://picasaweb.google.com/data/entry/api/user/sample.user/albumid/1/photoid/2 - 2007-09-05T20:49:23.000Z - 2007-09-21T18:23:05.000Z - - Aqua Blue.jpg - Blue - - - - 2 - 1190398985145172 - 0.0 - 1 2560 - 1600 - 883405 - - - 1189025362000 - true - c041ce17aaa637eb656c81d9cf526c24 - - true - 1 - - Aqua Blue.jpg Blue - tag, test - - - - - sample - - - - http://picasaweb.google.com/data/entry/api/user/sample.user/albumid/1/photoid/3 - 2007-09-05T20:49:24.000Z - 2007-09-21T18:19:38.000Z - - Aqua Graphite.jpg - Gray - - - - - 3 - 1190398778006402 - 1.0 - 1 - 2560 - 1600 - 798334 - - - 1189025363000 - - true - a5ce2e36b9df7d3cb081511c72e73926 - - true - 0 - - Aqua Graphite.jpg - Gray - - - - - - sample - - - - http://picasaweb.google.com/data/entry/api/user/sample.user/albumid/1/tag/tag - 2007-09-05T20:49:24.000Z - - tag - tag - - - - sample - http://picasaweb.google.com/sample.user - - - - http://picasaweb.google.com/data/entry/api/user/sample.user/albumid/1/tag/test - 2007-09-05T20:49:24.000Z - - test - test - - - - sample - http://picasaweb.google.com/sample.user - - -""" - -CODE_SEARCH_FEED = """ - -http://www.google.com/codesearch/feeds/search?q=malloc -2007-12-19T16:08:04Z -Google Code Search -Google Code Search -2530000 -1 - -Google Code Search - -http://www.google.com/codesearch - - - - - -http://www.google.com/codesearch?hl=en&q=+malloc+show:LDjwp-Iqc7U:84hEYaYsZk8:xDGReDhvNi0&sa=N&ct=rx&cd=1&cs_p=http://www.gnu.org&cs_f=software/autoconf/manual/autoconf-2.60/autoconf.html-002&cs_p=http://www.gnu.org&cs_f=software/autoconf/manual/autoconf-2.60/autoconf.html-002#first2007-12-19T16:08:04ZCode owned by external author.software/autoconf/manual/autoconf-2.60/autoconf.html<pre> 8: void *<b>malloc</b> (); - - -</pre><pre> #undef <b>malloc</b> -</pre><pre> void *<b>malloc</b> (); - -</pre><pre> rpl_<b>malloc</b> (size_t n) -</pre><pre> return <b>malloc</b> (n); - -</pre> -http://www.google.com/codesearch?hl=en&q=+malloc+show:h4hfh-fV-jI:niBq_bwWZNs:H0OhClf0HWQ&sa=N&ct=rx&cd=2&cs_p=ftp://ftp.gnu.org/gnu/guile/guile-1.6.8.tar.gz&cs_f=guile-1.6.8/libguile/mallocs.c&cs_p=ftp://ftp.gnu.org/gnu/guile/guile-1.6.8.tar.gz&cs_f=guile-1.6.8/libguile/mallocs.c#first2007-12-19T16:08:04ZCode owned by external author.guile-1.6.8/libguile/mallocs.c<pre> 86: { - scm_t_bits mem = n ? (scm_t_bits) <b>malloc</b> (n) : 0; - if (n &amp;&amp; !mem) - -</pre><pre>#include &lt;<b>malloc</b>.h&gt; -</pre><pre>scm_t_bits scm_tc16_<b>malloc</b>; - -</pre><pre><b>malloc</b>_free (SCM ptr) -</pre><pre><b>malloc</b>_print (SCM exp, SCM port, scm_print_state *pstate SCM_UNUSED) - -</pre><pre> scm_puts(&quot;#&lt;<b>malloc</b> &quot;, port); -</pre><pre> scm_t_bits mem = n ? (scm_t_bits) <b>malloc</b> (n) : 0; - -</pre><pre> SCM_RETURN_NEWSMOB (scm_tc16_<b>malloc</b>, mem); -</pre><pre> scm_tc16_<b>malloc</b> = scm_make_smob_type (&quot;<b>malloc</b>&quot;, 0); - -</pre><pre> scm_set_smob_free (scm_tc16_<b>malloc</b>, <b>malloc</b>_free); -</pre>GPL - -http://www.google.com/codesearch?hl=en&q=+malloc+show:9wyZUG-N_30:7_dFxoC1ZrY:C0_iYbFj90M&sa=N&ct=rx&cd=3&cs_p=http://ftp.gnu.org/gnu/bash/bash-3.0.tar.gz&cs_f=bash-3.0/lib/malloc/alloca.c&cs_p=http://ftp.gnu.org/gnu/bash/bash-3.0.tar.gz&cs_f=bash-3.0/lib/malloc/alloca.c#first2007-12-19T16:08:04ZCode owned by external author.bash-3.0/lib/malloc/alloca.c<pre> 78: #ifndef emacs - #define <b>malloc</b> x<b>malloc</b> - extern pointer x<b>malloc</b> (); - -</pre><pre> <b>malloc</b>. The Emacs executable needs alloca to call x<b>malloc</b>, because -</pre><pre> ordinary <b>malloc</b> isn&#39;t protected from input signals. On the other - -</pre><pre> hand, the utilities in lib-src need alloca to call <b>malloc</b>; some of -</pre><pre> them are very simple, and don&#39;t have an x<b>malloc</b> routine. - -</pre><pre> Callers below should use <b>malloc</b>. */ -</pre><pre>#define <b>malloc</b> x<b>malloc</b> - -</pre><pre>extern pointer x<b>malloc</b> (); -</pre><pre> It is very important that sizeof(header) agree with <b>malloc</b> - -</pre><pre> register pointer new = <b>malloc</b> (sizeof (header) + size); -</pre>GPL -http://www.google.com/codesearch?hl=en&q=+malloc+show:uhVCKyPcT6k:8juMxxzmUJw:H7_IDsTB2L4&sa=N&ct=rx&cd=4&cs_p=http://ftp.mozilla.org/pub/mozilla.org/mozilla/releases/mozilla1.7b/src/mozilla-source-1.7b-source.tar.bz2&cs_f=mozilla/xpcom/build/malloc.c&cs_p=http://ftp.mozilla.org/pub/mozilla.org/mozilla/releases/mozilla1.7b/src/mozilla-source-1.7b-source.tar.bz2&cs_f=mozilla/xpcom/build/malloc.c#first2007-12-19T16:08:04ZCode owned by external author.mozilla/xpcom/build/malloc.c<pre> 54: http://gee.cs.oswego.edu/dl/html/<b>malloc</b>.html - - You may already by default be using a c library containing a <b>malloc</b> - -</pre><pre>/* ---------- To make a <b>malloc</b>.h, start cutting here ------------ */ -</pre><pre> Note: There may be an updated version of this <b>malloc</b> obtainable at - -</pre><pre> ftp://gee.cs.oswego.edu/pub/misc/<b>malloc</b>.c -</pre><pre>* Why use this <b>malloc</b>? - -</pre><pre> most tunable <b>malloc</b> ever written. However it is among the fastest -</pre><pre> allocator for <b>malloc</b>-intensive programs. - -</pre><pre> http://gee.cs.oswego.edu/dl/html/<b>malloc</b>.html -</pre><pre> You may already by default be using a c library containing a <b>malloc</b> - -</pre><pre> that is somehow based on some version of this <b>malloc</b> (for example in -</pre>Mozilla -http://www.google.com/codesearch?hl=en&q=+malloc+show:4n1P2HVOISs:Ybbpph0wR2M:OhIN_sDrG0U&sa=N&ct=rx&cd=5&cs_p=http://regexps.srparish.net/src/hackerlab/hackerlab-1.0pre2.tar.gz&cs_f=hackerlab-1.0pre2/src/hackerlab/tests/mem-tests/unit-must-malloc.sh&cs_p=http://regexps.srparish.net/src/hackerlab/hackerlab-1.0pre2.tar.gz&cs_f=hackerlab-1.0pre2/src/hackerlab/tests/mem-tests/unit-must-malloc.sh#first2007-12-19T16:08:04ZCode owned by external author.hackerlab-1.0pre2/src/hackerlab/tests/mem-tests/unit-must-malloc.sh<pre> 11: echo ================ unit-must-<b>malloc</b> tests ================ - ./unit-must-<b>malloc</b> - echo ...passed - -</pre><pre># tag: Tom Lord Tue Dec 4 14:54:29 2001 (mem-tests/unit-must-<b>malloc</b>.sh) -</pre><pre>echo ================ unit-must-<b>malloc</b> tests ================ - -</pre><pre>./unit-must-<b>malloc</b> -</pre>GPL -http://www.google.com/codesearch?hl=en&q=+malloc+show:GzkwiWG266M:ykuz3bG00ws:2sTvVSif08g&sa=N&ct=rx&cd=6&cs_p=http://ftp.gnu.org/gnu/tar/tar-1.14.tar.bz2&cs_f=tar-1.14/lib/malloc.c&cs_p=http://ftp.gnu.org/gnu/tar/tar-1.14.tar.bz2&cs_f=tar-1.14/lib/malloc.c#first2007-12-19T16:08:04ZCode owned by external author.tar-1.14/lib/malloc.c<pre> 22: #endif - #undef <b>malloc</b> - - -</pre><pre>/* Work around bug on some systems where <b>malloc</b> (0) fails. -</pre><pre>#undef <b>malloc</b> - -</pre><pre>rpl_<b>malloc</b> (size_t n) -</pre><pre> return <b>malloc</b> (n); - -</pre>GPL -http://www.google.com/codesearch?hl=en&q=+malloc+show:o_TFIeBY6dY:ktI_dt8wPao:AI03BD1Dz0Y&sa=N&ct=rx&cd=7&cs_p=http://ftp.gnu.org/gnu/tar/tar-1.16.1.tar.gz&cs_f=tar-1.16.1/lib/malloc.c&cs_p=http://ftp.gnu.org/gnu/tar/tar-1.16.1.tar.gz&cs_f=tar-1.16.1/lib/malloc.c#first2007-12-19T16:08:04ZCode owned by external author.tar-1.16.1/lib/malloc.c<pre> 21: #include &lt;config.h&gt; - #undef <b>malloc</b> - - -</pre><pre>/* <b>malloc</b>() function that is glibc compatible. -</pre><pre>#undef <b>malloc</b> - -</pre><pre>rpl_<b>malloc</b> (size_t n) -</pre><pre> return <b>malloc</b> (n); - -</pre>GPL -http://www.google.com/codesearch?hl=en&q=+malloc+show:_ibw-VLkMoI:jBOtIJSmFd4:-0NUEVeCwfY&sa=N&ct=rx&cd=8&cs_p=http://freshmeat.net/redir/uclibc/20616/url_bz2/uClibc-0.9.28.1.tar.bz2&cs_f=uClibc-0.9.29/include/malloc.h&cs_p=http://freshmeat.net/redir/uclibc/20616/url_bz2/uClibc-0.9.28.1.tar.bz2&cs_f=uClibc-0.9.29/include/malloc.h#first2007-12-19T16:08:04ZCode owned by external author.uClibc-0.9.29/include/malloc.h<pre> 1: /* Prototypes and definition for <b>malloc</b> implementation. - Copyright (C) 1996, 1997, 1999, 2000 Free Software Foundation, Inc. - -</pre><pre>/* Prototypes and definition for <b>malloc</b> implementation. -</pre><pre> `pt<b>malloc</b>&#39;, a <b>malloc</b> implementation for multiple threads without - -</pre><pre> See the files `pt<b>malloc</b>.c&#39; or `COPYRIGHT&#39; for copying conditions. -</pre><pre> This work is mainly derived from <b>malloc</b>-2.6.4 by Doug Lea - -</pre><pre> ftp://g.oswego.edu/pub/misc/<b>malloc</b>.c -</pre><pre> `pt<b>malloc</b>.c&#39;. - -</pre><pre># define __<b>malloc</b>_ptr_t void * -</pre><pre># define __<b>malloc</b>_ptr_t char * - -</pre><pre># define __<b>malloc</b>_size_t size_t -</pre>LGPL -http://www.google.com/codesearch?hl=en&q=+malloc+show:F6qHcZ9vefo:bTX7o9gKfks:hECF4r_eKC0&sa=N&ct=rx&cd=9&cs_p=http://ftp.gnu.org/gnu/glibc/glibc-2.0.1.tar.gz&cs_f=glibc-2.0.1/hurd/hurdmalloc.h&cs_p=http://ftp.gnu.org/gnu/glibc/glibc-2.0.1.tar.gz&cs_f=glibc-2.0.1/hurd/hurdmalloc.h#first2007-12-19T16:08:04ZCode owned by external author.glibc-2.0.1/hurd/hurdmalloc.h<pre> 15: #define <b>malloc</b> _hurd_<b>malloc</b> - #define realloc _hurd_realloc - -</pre><pre> All hurd-internal code which uses <b>malloc</b> et al includes this file so it -</pre><pre> will use the internal <b>malloc</b> routines _hurd_{<b>malloc</b>,realloc,free} - -</pre><pre> of <b>malloc</b> et al is the unixoid one using sbrk. -</pre><pre>extern void *_hurd_<b>malloc</b> (size_t); - -</pre><pre>#define <b>malloc</b> _hurd_<b>malloc</b> -</pre>GPL - -http://www.google.com/codesearch?hl=en&q=+malloc+show:CHUvHYzyLc8:pdcAfzDA6lY:wjofHuNLTHg&sa=N&ct=rx&cd=10&cs_p=ftp://apache.mirrors.pair.com/httpd/httpd-2.2.4.tar.bz2&cs_f=httpd-2.2.4/srclib/apr/include/arch/netware/apr_private.h&cs_p=ftp://apache.mirrors.pair.com/httpd/httpd-2.2.4.tar.bz2&cs_f=httpd-2.2.4/srclib/apr/include/arch/netware/apr_private.h#first2007-12-19T16:08:04ZCode owned by external author.httpd-2.2.4/srclib/apr/include/arch/netware/apr_private.h<pre> 173: #undef <b>malloc</b> - #define <b>malloc</b>(x) library_<b>malloc</b>(gLibHandle,x) - -</pre><pre>/* Redefine <b>malloc</b> to use the library <b>malloc</b> call so -</pre><pre>#undef <b>malloc</b> - -</pre><pre>#define <b>malloc</b>(x) library_<b>malloc</b>(gLibHandle,x) -</pre>Apache - -""" - -YOUTUBE_VIDEO_FEED = """http://gdata.youtube.com/feeds/api/standardfeeds/top_rated2008-05-14T02:24:07.000-07:00Top Ratedhttp://www.youtube.com/img/pic_youtubelogo_123x63.gifYouTubehttp://www.youtube.com/YouTube data API100125 -http://gdata.youtube.com/feeds/api/videos/C71ypXYGho82008-03-20T10:17:27.000-07:002008-05-14T04:26:37.000-07:00Me odeio por te amar - KARYN GARCIAhttp://www.karyngarcia.com.brTvKarynGarciahttp://gdata.youtube.com/feeds/api/users/tvkaryngarciaMe odeio por te amar - KARYN GARCIAhttp://www.karyngarcia.com.bramar, boyfriend, garcia, karyn, me, odeio, por, teMusictest111test222 -http://gdata.youtube.com/feeds/api/videos/gsVaTyb1tBw2008-02-15T04:31:45.000-08:002008-05-14T05:09:42.000-07:00extreme helmet cam Kani, Keil and Patotrimmedperaltamagichttp://gdata.youtube.com/feeds/api/users/peraltamagicextreme helmet cam Kani, Keil and Patotrimmedalcala, cam, campillo, dirt, extreme, helmet, kani, patoSports -""" - -YOUTUBE_ENTRY_PRIVATE = """ - - http://gdata.youtube.com/feeds/videos/UMFI1hdm96E - 2007-01-07T01:50:15.000Z - 2007-01-07T01:50:15.000Z - - - - - - - - - "Crazy (Gnarles Barkley)" - Acoustic Cover - <div style="color: #000000;font-family: - Arial, Helvetica, sans-serif; font-size:12px; font-size: 12px; - width: 555px;"><table cellspacing="0" cellpadding="0" - border="0"><tbody><tr><td width="140" - valign="top" rowspan="2"><div style="border: 1px solid - #999999; margin: 0px 10px 5px 0px;"><a - href="http://www.youtube.com/watch?v=UMFI1hdm96E"><img - alt="" - src="http://img.youtube.com/vi/UMFI1hdm96E/2.jpg"></a></div></td> - <td width="256" valign="top"><div style="font-size: - 12px; font-weight: bold;"><a style="font-size: 15px; - font-weight: bold; font-decoration: none;" - href="http://www.youtube.com/watch?v=UMFI1hdm96E">&quot;Crazy - (Gnarles Barkley)&quot; - Acoustic Cover</a> - <br></div> <div style="font-size: 12px; margin: - 3px 0px;"><span>Gnarles Barkley acoustic cover - http://www.myspace.com/davidchoimusic</span></div></td> - <td style="font-size: 11px; line-height: 1.4em; padding-left: - 20px; padding-top: 1px;" width="146" - valign="top"><div><span style="color: #666666; - font-size: 11px;">From:</span> <a - href="http://www.youtube.com/profile?user=davidchoimusic">davidchoimusic</a></div> - <div><span style="color: #666666; font-size: - 11px;">Views:</span> 113321</div> <div - style="white-space: nowrap;text-align: left"><img - style="border: 0px none; margin: 0px; padding: 0px; - vertical-align: middle; font-size: 11px;" align="top" alt="" - src="http://gdata.youtube.com/static/images/icn_star_full_11x11.gif"> - <img style="border: 0px none; margin: 0px; padding: 0px; - vertical-align: middle; font-size: 11px;" align="top" alt="" - src="http://gdata.youtube.com/static/images/icn_star_full_11x11.gif"> - <img style="border: 0px none; margin: 0px; padding: 0px; - vertical-align: middle; font-size: 11px;" align="top" alt="" - src="http://gdata.youtube.com/static/images/icn_star_full_11x11.gif"> - <img style="border: 0px none; margin: 0px; padding: 0px; - vertical-align: middle; font-size: 11px;" align="top" alt="" - src="http://gdata.youtube.com/static/images/icn_star_full_11x11.gif"> - <img style="border: 0px none; margin: 0px; padding: 0px; - vertical-align: middle; font-size: 11px;" align="top" alt="" - src="http://gdata.youtube.com/static/images/icn_star_half_11x11.gif"></div> - <div style="font-size: 11px;">1005 <span style="color: - #666666; font-size: - 11px;">ratings</span></div></td></tr> - <tr><td><span style="color: #666666; font-size: - 11px;">Time:</span> <span style="color: #000000; - font-size: 11px; font-weight: - bold;">04:15</span></td> <td style="font-size: - 11px; padding-left: 20px;"><span style="color: #666666; - font-size: 11px;">More in</span> <a - href="http://www.youtube.com/categories_portal?c=10">Music</a></td></tr></tbody></table></div> - - - - - - davidchoimusic - http://gdata.youtube.com/feeds/users/davidchoimusic - - - "Crazy (Gnarles Barkley)" - Acoustic Cover - Gnarles Barkley acoustic cover http://www.myspace.com/davidchoimusic - music, singing, gnarls, barkley, acoustic, cover - - - Music - - DeveloperTag1 - - - - - - - - - - - - - 37.398529052734375 -122.0635986328125 - - - - - - - - yes - - The content of this video may violate the terms of use. - -""" - -YOUTUBE_COMMENT_FEED = """ -http://gdata.youtube.com/feeds/videos/2Idhz9ef5oU/comments2008-05-19T21:45:45.261ZCommentshttp://www.youtube.com/img/pic_youtubelogo_123x63.gifYouTubehttp://www.youtube.com/YouTube data API0125 - - http://gdata.youtube.com/feeds/videos/2Idhz9ef5oU/comments/91F809A3DE2EB81B - 2008-02-22T15:27:15.000-08:002008-02-22T15:27:15.000-08:00 - - test66 - test66 - - - - apitestjhartmannhttp://gdata.youtube.com/feeds/users/apitestjhartmann - - - http://gdata.youtube.com/feeds/videos/2Idhz9ef5oU/comments/A261AEEFD23674AA - 2008-02-22T15:27:01.000-08:002008-02-22T15:27:01.000-08:00 - - test333 - test333 - - - - apitestjhartmannhttp://gdata.youtube.com/feeds/users/apitestjhartmann - - - http://gdata.youtube.com/feeds/videos/2Idhz9ef5oU/comments/0DCF1E3531B3FF85 - 2008-02-22T15:11:06.000-08:002008-02-22T15:11:06.000-08:00 - - test2 - test2 - - - - apitestjhartmannhttp://gdata.youtube.com/feeds/users/apitestjhartmann - -""" - -YOUTUBE_PLAYLIST_FEED = """ - - http://gdata.youtube.com/feeds/users/andyland74/playlists?start-index=1&max-results=25 - 2008-02-26T00:26:15.635Z - - andyland74's Playlists - http://www.youtube.com/img/pic_youtubelogo_123x63.gif - - - - - - andyland74 - http://gdata.youtube.com/feeds/users/andyland74 - - YouTube data API - 1 - 1 - 25 - - My new playlist Description - - http://gdata.youtube.com/feeds/users/andyland74/playlists/8BCDD04DE8F771B2 - 2007-11-04T17:30:27.000-08:00 - 2008-02-22T09:55:14.000-08:00 - - My New Playlist Title - My new playlist Description - - - - - andyland74 - http://gdata.youtube.com/feeds/users/andyland74 - - -""" - -YOUTUBE_PLAYLIST_VIDEO_FEED = """http://gdata.youtube.com/feeds/api/playlists/BCB3BB96DF51B5052008-05-16T12:03:17.000-07:00Test PlaylistTest playlist 1http://www.youtube.com/img/pic_youtubelogo_123x63.gifgdpythonhttp://gdata.youtube.com/feeds/api/users/gdpythonYouTube data API1125Test PlaylistTest playlist 1http://gdata.youtube.com/feeds/api/playlists/BCB3BB96DF51B505/B0F29389E537F8882008-05-16T20:54:08.520ZUploading YouTube Videos with the PHP Client LibraryJochen Hartmann demonstrates the basics of how to use the PHP Client Library with the YouTube Data API. - -PHP Developer's Guide: -http://code.google.com/apis/youtube/developers_guide_php.html - -Other documentation: -http://code.google.com/apis/youtube/GoogleDevelopershttp://gdata.youtube.com/feeds/api/users/googledevelopersUploading YouTube Videos with the PHP Client LibraryJochen Hartmann demonstrates the basics of how to use the PHP Client Library with the YouTube Data API. - -PHP Developer's Guide: -http://code.google.com/apis/youtube/developers_guide_php.html - -Other documentation: -http://code.google.com/apis/youtube/api, data, demo, php, screencast, tutorial, uploading, walkthrough, youtubeEducationundefined1""" - -YOUTUBE_SUBSCRIPTION_FEED = """ - - http://gdata.youtube.com/feeds/users/andyland74/subscriptions?start-index=1&max-results=25 - 2008-02-26T00:26:15.635Z - - andyland74's Subscriptions - http://www.youtube.com/img/pic_youtubelogo_123x63.gif - - - - - - andyland74 - http://gdata.youtube.com/feeds/users/andyland74 - - YouTube data API - 1 - 1 - 25 - - http://gdata.youtube.com/feeds/users/andyland74/subscriptions/d411759045e2ad8c - 2007-11-04T17:30:27.000-08:00 - 2008-02-22T09:55:14.000-08:00 - - - Videos published by : NBC - - - - - andyland74 - http://gdata.youtube.com/feeds/users/andyland74 - - NBC - - -""" - -YOUTUBE_VIDEO_RESPONSE_FEED = """ - - http://gdata.youtube.com/feeds/videos/2c3q9K4cHzY/responses2008-05-19T22:37:34.076ZVideos responses to 'Giant NES controller coffee table'http://www.youtube.com/img/pic_youtubelogo_123x63.gifYouTubehttp://www.youtube.com/YouTube data API8125 - - http://gdata.youtube.com/feeds/videos/7b9EnRI9VbY2008-03-11T19:08:53.000-07:002008-05-18T21:33:10.000-07:00 - - - - - - - - - - - - Catnip Partysnipped - - - - - PismoBeachhttp://gdata.youtube.com/feeds/users/pismobeach - - Catnip Party - Uncle, Hillary, Hankette, and B4 all but overdose on the patioBrattman, cat, catmint, catnip, cats, chat, drug, gato, gatto, kat, kato, katt, Katze, kedi, kissa, OD, overdose, party, sex, Uncle - - Animals - - - - - - - - - - - - - - - - -""" - - -YOUTUBE_PROFILE = """ - - http://gdata.youtube.com/feeds/users/andyland74 - 2006-10-16T00:09:45.000-07:00 - 2008-02-26T11:48:21.000-08:00 - - - andyland74 Channel - - - - andyland74 - http://gdata.youtube.com/feeds/users/andyland74 - - 33 - andyland74 - andy - example - Catch-22 - m - Google - Testing YouTube APIs - Somewhere - US - Aqua Teen Hungerforce - Elliott Smith - Technical Writer - University of North Carolina - - - - - - - - -""" - -YOUTUBE_CONTACTS_FEED = """ - http://gdata.youtube.com/feeds/users/apitestjhartmann/contacts2008-05-16T19:24:34.916Zapitestjhartmann's Contactshttp://www.youtube.com/img/pic_youtubelogo_123x63.gifapitestjhartmannhttp://gdata.youtube.com/feeds/users/apitestjhartmannYouTube data API2125 - - http://gdata.youtube.com/feeds/users/apitestjhartmann/contacts/test898990902008-02-04T11:27:54.000-08:002008-05-16T19:24:34.916Ztest89899090apitestjhartmannhttp://gdata.youtube.com/feeds/users/apitestjhartmanntest89899090requested - - http://gdata.youtube.com/feeds/users/apitestjhartmann/contacts/testjfisher2008-02-26T14:13:03.000-08:002008-05-16T19:24:34.916Ztestjfisherapitestjhartmannhttp://gdata.youtube.com/feeds/users/apitestjhartmanntestjfisherpending -""" - -NEW_CONTACT = """ - - http://www.google.com/m8/feeds/contacts/liz%40gmail.com/base/8411573 - 2008-02-28T18:47:02.303Z - - Fitzgerald - Notes - - - - - (206)555-1212 - 456-123-2133 - (206)555-1213 - - - - - - - 1600 Amphitheatre Pkwy Mountain View -""" - -CONTACTS_FEED = """ - - http://www.google.com/m8/feeds/contacts/liz%40gmail.com/base - 2008-03-05T12:36:38.836Z - - Contacts - - - - - - Elizabeth Bennet - liz@gmail.com - - - Contacts - - 1 - 1 - 25 - - - http://www.google.com/m8/feeds/contacts/liz%40gmail.com/base/c9012de - - 2008-03-05T12:36:38.835Z - - Fitzgerald - - - - - - 456 - - - - -""" - - -CONTACT_GROUPS_FEED = """ - - jo@gmail.com - 2008-05-21T21:11:25.237Z - - Jo's Contact Groups - - - - - - - Jo Brown - jo@gmail.com - - Contacts - 3 - 1 - 25 - - http://google.com/m8/feeds/groups/jo%40gmail.com/base/270f - 2008-05-14T13:10:19.070Z - - joggers - joggers - - - -""" - -CONTACT_GROUP_ENTRY = """ - - - http://www.google.com/feeds/groups/jo%40gmail.com/base/1234 - 2005-01-18T21:00:00Z - 2006-01-01T00:00:00Z - Salsa group - Salsa group - - - - Very nice people. - -""" - -CALENDAR_RESOURCE_ENTRY = """ - - - - - -""" - -CALENDAR_RESOURCES_FEED = """ - - https://apps-apis.google.com/a/feeds/calendar/resource/2.0/yourdomain.com - 2008-10-17T15:29:21.064Z - - - - - 1 - - https://apps-apis.google.com/a/feeds/calendar/resource/2.0/yourdomain.com/CR-NYC-14-12-BR - 2008-10-17T15:29:21.064Z - - - - - - - - - - https://apps-apis.google.com/a/feeds/calendar/resource/2.0/yourdomain.com/?start=(Bike)-London-43-Lobby-Bike-1 - 2008-10-17T15:29:21.064Z - - - - - - - - -""" - -BLOG_ENTRY = """ - tag:blogger.com,1999:blog-blogID.post-postID - 2006-08-02T18:44:43.089-07:00 - 2006-11-08T18:10:23.020-08:00 - Lizzy's Diary - Being the journal of Elizabeth Bennet - - - - - - - - - - - - Elizabeth Bennet - liz@gmail.com - -""" - -BLOG_POST = """ - Marriage! - -
-

Mr. Darcy has proposed marriage to me!

-

He is the last man on earth I would ever desire to marry.

-

Whatever shall I do?

-
-
- - Elizabeth Bennet - liz@gmail.com - -
""" - -BLOG_POSTS_FEED = """ - tag:blogger.com,1999:blog-blogID - 2006-11-08T18:10:23.020-08:00 - Lizzy's Diary - - - - - - - - Elizabeth Bennet - liz@gmail.com - - Blogger - - tag:blogger.com,1999:blog-blogID.post-postID - 2006-11-08T18:10:00.000-08:00 - 2006-11-08T18:10:14.954-08:00 - Quite disagreeable - <p>I met Mr. Bingley's friend Mr. Darcy - this evening. I found him quite disagreeable.</p> - - - - - - - - Elizabeth Bennet - liz@gmail.com - - -""" - -BLOG_COMMENTS_FEED = """ - tag:blogger.com,1999:blog-blogID.postpostID..comments - 2007-04-04T21:56:29.803-07:00 - My Blog : Time to relax - - - - - Blog Author name - - Blogger - 1 - 1 - - tag:blogger.com,1999:blog-blogID.post-commentID - 2007-04-04T21:56:00.000-07:00 - 2007-04-04T21:56:29.803-07:00 - This is my first comment - This is my first comment - - - - - Blog Author name - - - -""" - - -SITES_FEED = """ - https://www.google.com/webmasters/tools/feeds/sites - Sites - 1 - - - - - 2008-10-02T07:26:51.833Z - - http://www.example.com - http://www.example.com - - - - 2007-11-17T18:27:32.543Z - - - - true - 2008-09-14T08:59:28.000 - US - none - normal - true - false - - - 456456-google.html - -""" - - -SITEMAPS_FEED = """ - http://www.example.com - http://www.example.com/ - 2006-11-17T18:27:32.543Z - - - - HTML - WAP - - - Value1 - Value2 - Value3 - - - http://www.example.com/sitemap-index.xml - http://www.example.com/sitemap-index.xml - - 2006-11-17T18:27:32.543Z - WEB - StatusValue - 2006-11-18T19:27:32.543Z - 102 - - - http://www.example.com/mobile/sitemap-index.xml - http://www.example.com/mobile/sitemap-index.xml - - 2006-11-17T18:27:32.543Z - StatusValue - 2006-11-18T19:27:32.543Z - 102 - HTML - - - http://www.example.com/news/sitemap-index.xml - http://www.example.com/news/sitemap-index.xml - - 2006-11-17T18:27:32.543Z - StatusValue - 2006-11-18T19:27:32.543Z - 102 - LabelValue - -""" - -HEALTH_CCR_NOTICE_PAYLOAD = """ - - - - - Start date - 2007-04-04T07:00:00Z - - - Aortic valve disorders - - 410.10 - ICD9 - 2004 - - - Active - - - -""" - -HEALTH_PROFILE_ENTRY_DIGEST = """ - - https://www.google.com/health/feeds/profile/default/vneCn5qdEIY_digest - 2008-09-29T07:52:17.176Z - - - - - - vneCn5qdEIY - - English - - en - ISO-639-1 - - - V1.0 - - 2008-09-29T07:52:17.176Z - - - Google Health Profile - - - - - - Pregnancy status - - - Not pregnant - - - - - user@google.com - - Patient - - - - - - - Breastfeeding status - - - Not breastfeeding - - - - - user@gmail.com - - Patient - - - - - - - - Hn0FE0IlcY-FMFFgSTxkvA/CONDITION/0 - - - Start date - - 2007-04-04T07:00:00Z - - - Aortic valve disorders - - 410.10 - ICD9 - 2004 - - - - Active - - - - example.com - - Information Provider - - - - - - - - Malaria - - 136.9 - ICD9_Broader - - - 084.6 - ICD9 - - - - ACTIVE - - - - user@gmail.com - - Patient - - - - - - - - - - - - Race - - S15814 - HL7 - - - - White - - - - - user@gmail.com - - Patient - - - - - - - - - - - - - - Allergy - - - A-Fil - - - ACTIVE - - - - user@gmail.com - - Patient - - - - - - - Severe - - - - - - Allergy - - - A.E.R Traveler - - - ACTIVE - - - - user@gmail.com - - Patient - - - - - - - Severe - - - - - - - - - - ACTIVE - - - - user@gmail.com - - Patient - - - - - - A& D - - - - 0 - - - - - - - - - - 0 - - - - To skin - - C38305 - FDA - - 0 - - - - - - - - - - - ACTIVE - - - - user@gmail.com - - Patient - - - - - - A-Fil - - - - 0 - - - - - - - - - - 0 - - - - To skin - - C38305 - FDA - - 0 - - - - - - - - - - - ACTIVE - - - - user@gmail.com - - Patient - - - - - - Lipitor - - - - 0 - - - - - - - - - - 0 - - - - By mouth - - C38288 - FDA - - 0 - - - - - - - - - - - - - - - user@gmail.com - - Patient - - - - - - Chickenpox Vaccine - - 21 - HL7 - - - - - - - - - - - - - - - - - - - - user@gmail.com - - Patient - - - - - - - - Height - - - - 0 - - 70 - - inches - - - - - - - - - - - - user@gmail.com - - Patient - - - - - - - - Weight - - - - 0 - - 2480 - - ounces - - - - - - - - - - - - user@gmail.com - - Patient - - - - - - - - Blood Type - - - - 0 - - O+ - - - - - - - - - - - - - - user@gmail.com - - Patient - - - - - - - - Collection start date - - 2008-09-03 - - - - Acetaldehyde - Blood - - - - 0 - - - - - - - - - - - - Abdominal Ultrasound - - - - - user@gmail.com - - Patient - - - - - - - - Abdominoplasty - - - - - user@gmail.com - - Patient - - - - - - - - - Google Health Profile - - - - - - - - 1984-07-22 - - - Male - - - - - - user@gmail.com - - Patient - - - - - - -""" - -HEALTH_PROFILE_FEED = """ -https://www.google.com/health/feeds/profile/default -2008-09-30T01:07:17.888Z - -Profile Feed - - - - -1 - - https://www.google.com/health/feeds/profile/default/DysasdfARnFAao - 2008-09-29T03:12:50.850Z - 2008-09-29T03:12:50.850Z - - - - - <content type="html"/> - <link rel="http://schemas.google.com/health/data#complete" type="application/atom+xml" href="https://www.google.com/health/feeds/profile/default/-/MEDICATION/%7Bhttp%3A%2F%2Fschemas.google.com%2Fg%2F2005%23kind%7Dhttp%3A%2F%2Fschemas.google.com%2Fhealth%2Fkinds%23profile/%7Bhttp%3A%2F%2Fschemas.google.com%2Fhealth%2Fitem%7DA%26+D"/> - <link rel="self" type="application/atom+xml" href="https://www.google.com/health/feeds/profile/default/DysasdfARnFAao"/> - <link rel="edit" type="application/atom+xml" href="https://www.google.com/health/feeds/profile/default/DysasdfARnFAao"/> - <author> - <name>User Name</name> - <email>user@gmail.com</email> - </author> - <ContinuityOfCareRecord xmlns="urn:astm-org:CCR"> - <CCRDocumentObjectID>hiD9sEigSzdk8nNT0evR4g</CCRDocumentObjectID> - <Language/> - <DateTime> - <Type/> - </DateTime> - <Patient/> - <Body> - <Medications> - <Medication> - <Type/> - <Description/> - <Status> - <Text>ACTIVE</Text> - </Status> - <Source> - <Actor> - <ActorID>user@gmail.com</ActorID> - <ActorRole> - <Text>Patient</Text> - </ActorRole> - </Actor> - </Source> - <Product> - <ProductName> - <Text>A& D</Text> - </ProductName> - <Strength> - <Units/> - <StrengthSequencePosition>0</StrengthSequencePosition> - <VariableStrengthModifier/> - </Strength> - </Product> - <Directions> - <Direction> - <Description/> - <DeliveryMethod/> - <Dose> - <Units/> - <DoseSequencePosition>0</DoseSequencePosition> - <VariableDoseModifier/> - </Dose> - <Route> - <Text>To skin</Text> - <Code> - <Value>C38305</Value> - <CodingSystem>FDA</CodingSystem> - </Code> - <RouteSequencePosition>0</RouteSequencePosition> - <MultipleRouteModifier/> - </Route> - </Direction> - </Directions> - <Refills/> - </Medication> - </Medications> - </Body> - </ContinuityOfCareRecord> -</entry> -<entry> - <id>https://www.google.com/health/feeds/profile/default/7I1WQzZrgp4</id> - <published>2008-09-29T03:27:14.909Z</published> - <updated>2008-09-29T03:27:14.909Z</updated> - <category scheme="http://schemas.google.com/g/2005#kind" term="http://schemas.google.com/health/kinds#profile"/> - <category scheme="http://schemas.google.com/health/item" term="A-Fil"/> - <category term="ALLERGY"/> - <title type="text"/> - <content type="html"/> - <link rel="http://schemas.google.com/health/data#complete" type="application/atom+xml" href="https://www.google.com/health/feeds/profile/default/-/%7Bhttp%3A%2F%2Fschemas.google.com%2Fg%2F2005%23kind%7Dhttp%3A%2F%2Fschemas.google.com%2Fhealth%2Fkinds%23profile/%7Bhttp%3A%2F%2Fschemas.google.com%2Fhealth%2Fitem%7DA-Fil/ALLERGY"/> - <link rel="self" type="application/atom+xml" href="https://www.google.com/health/feeds/profile/default/7I1WQzZrgp4"/> - <link rel="edit" type="application/atom+xml" href="https://www.google.com/health/feeds/profile/default/7I1WQzZrgp4"/> - <author> - <name>User Name</name> - <email>user@gmail.com</email> - </author> - <ContinuityOfCareRecord xmlns="urn:astm-org:CCR"> - <CCRDocumentObjectID>YOyHDxQUiECCPgnsjV8SlQ</CCRDocumentObjectID> - <Language/> - <DateTime> - <Type/> - </DateTime> - <Patient/> - <Body> - <Alerts> - <Alert> - <Type> - <Text>Allergy</Text> - </Type> - <Description> - <Text>A-Fil</Text> - </Description> - <Status> - <Text>ACTIVE</Text> - </Status> - <Source> - <Actor> - <ActorID>user@gmail.com</ActorID> - <ActorRole> - <Text>Patient</Text> - </ActorRole> - </Actor> - </Source> - <Reaction> - <Description/> - <Severity> - <Text>Severe</Text> - </Severity> - </Reaction> - </Alert> - </Alerts> - </Body> - </ContinuityOfCareRecord> -</entry> -<entry> - <id>https://www.google.com/health/feeds/profile/default/Dz9wV83sKFg</id> - <published>2008-09-29T03:12:52.166Z</published> - <updated>2008-09-29T03:12:52.167Z</updated> - <category term="MEDICATION"/> - <category scheme="http://schemas.google.com/g/2005#kind" term="http://schemas.google.com/health/kinds#profile"/> - <category scheme="http://schemas.google.com/health/item" term="A-Fil"/> - <title type="text"/> - <content type="html"/> - <link rel="http://schemas.google.com/health/data#complete" type="application/atom+xml" href="https://www.google.com/health/feeds/profile/default/-/MEDICATION/%7Bhttp%3A%2F%2Fschemas.google.com%2Fg%2F2005%23kind%7Dhttp%3A%2F%2Fschemas.google.com%2Fhealth%2Fkinds%23profile/%7Bhttp%3A%2F%2Fschemas.google.com%2Fhealth%2Fitem%7DA-Fil"/> - <link rel="self" type="application/atom+xml" href="https://www.google.com/health/feeds/profile/default/Dz9wV83sKFg"/> - <link rel="edit" type="application/atom+xml" href="https://www.google.com/health/feeds/profile/default/Dz9wV83sKFg"/> - <author> - <name>User Name</name> - <email>user@gmail.com</email> - </author> - <ContinuityOfCareRecord xmlns="urn:astm-org:CCR"> - <CCRDocumentObjectID>7w.XFEPeuIYN3Rn32pUiUw</CCRDocumentObjectID> - <Language/> - <DateTime> - <Type/> - </DateTime> - <Patient/> - <Body> - <Medications> - <Medication> - <Type/> - <Description/> - <Status> - <Text>ACTIVE</Text> - </Status> - <Source> - <Actor> - <ActorID>user@gmail.com</ActorID> - <ActorRole> - <Text>Patient</Text> - </ActorRole> - </Actor> - </Source> - <Product> - <ProductName> - <Text>A-Fil</Text> - </ProductName> - <Strength> - <Units/> - <StrengthSequencePosition>0</StrengthSequencePosition> - <VariableStrengthModifier/> - </Strength> - </Product> - <Directions> - <Direction> - <Description/> - <DeliveryMethod/> - <Dose> - <Units/> - <DoseSequencePosition>0</DoseSequencePosition> - <VariableDoseModifier/> - </Dose> - <Route> - <Text>To skin</Text> - <Code> - <Value>C38305</Value> - <CodingSystem>FDA</CodingSystem> - </Code> - <RouteSequencePosition>0</RouteSequencePosition> - <MultipleRouteModifier/> - </Route> - </Direction> - </Directions> - <Refills/> - </Medication> - </Medications> - </Body> - </ContinuityOfCareRecord> -</entry> -<entry> - <id>https://www.google.com/health/feeds/profile/default/lzsxVzqZUyw</id> - <published>2008-09-29T03:13:07.496Z</published> - <updated>2008-09-29T03:13:07.497Z</updated> - <category scheme="http://schemas.google.com/health/item" term="A.E.R Traveler"/> - <category scheme="http://schemas.google.com/g/2005#kind" term="http://schemas.google.com/health/kinds#profile"/> - <category term="ALLERGY"/> - <title type="text"/> - <content type="html"/> - <link rel="http://schemas.google.com/health/data#complete" type="application/atom+xml" href="https://www.google.com/health/feeds/profile/default/-/%7Bhttp%3A%2F%2Fschemas.google.com%2Fhealth%2Fitem%7DA.E.R+Traveler/%7Bhttp%3A%2F%2Fschemas.google.com%2Fg%2F2005%23kind%7Dhttp%3A%2F%2Fschemas.google.com%2Fhealth%2Fkinds%23profile/ALLERGY"/> - <link rel="self" type="application/atom+xml" href="https://www.google.com/health/feeds/profile/default/lzsxVzqZUyw"/> - <link rel="edit" type="application/atom+xml" href="https://www.google.com/health/feeds/profile/default/lzsxVzqZUyw"/> - <author> - <name>User Name</name> - <email>user@gmail.com</email> - </author> - <ContinuityOfCareRecord xmlns="urn:astm-org:CCR"> - <CCRDocumentObjectID>5efFB0J2WgEHNUvk2z3A1A</CCRDocumentObjectID> - <Language/> - <DateTime> - <Type/> - </DateTime> - <Patient/> - <Body> - <Alerts> - <Alert> - <Type> - <Text>Allergy</Text> - </Type> - <Description> - <Text>A.E.R Traveler</Text> - </Description> - <Status> - <Text>ACTIVE</Text> - </Status> - <Source> - <Actor> - <ActorID>user@gmail.com</ActorID> - <ActorRole> - <Text>Patient</Text> - </ActorRole> - </Actor> - </Source> - <Reaction> - <Description/> - <Severity> - <Text>Severe</Text> - </Severity> - </Reaction> - </Alert> - </Alerts> - </Body> - </ContinuityOfCareRecord> -</entry> -<entry> - <id>https://www.google.com/health/feeds/profile/default/6PvhfKAXyYw</id> - <published>2008-09-29T03:13:02.123Z</published> - <updated>2008-09-29T03:13:02.124Z</updated> - <category scheme="http://schemas.google.com/g/2005#kind" term="http://schemas.google.com/health/kinds#profile"/> - <category term="PROCEDURE"/> - <category scheme="http://schemas.google.com/health/item" term="Abdominal Ultrasound"/> - <title type="text"/> - <content type="html"/> - <link rel="http://schemas.google.com/health/data#complete" type="application/atom+xml" href="https://www.google.com/health/feeds/profile/default/-/%7Bhttp%3A%2F%2Fschemas.google.com%2Fg%2F2005%23kind%7Dhttp%3A%2F%2Fschemas.google.com%2Fhealth%2Fkinds%23profile/PROCEDURE/%7Bhttp%3A%2F%2Fschemas.google.com%2Fhealth%2Fitem%7DAbdominal+Ultrasound"/> - <link rel="self" type="application/atom+xml" href="https://www.google.com/health/feeds/profile/default/6PvhfKAXyYw"/> - <link rel="edit" type="application/atom+xml" href="https://www.google.com/health/feeds/profile/default/6PvhfKAXyYw"/> - <author> - <name>User Name</name> - <email>user@gmail.com</email> - </author> - <ContinuityOfCareRecord xmlns="urn:astm-org:CCR"> - <CCRDocumentObjectID>W3Wbvx_QHwG5pxVchpuF1A</CCRDocumentObjectID> - <Language/> - <DateTime> - <Type/> - </DateTime> - <Patient/> - <Body> - <Procedures> - <Procedure> - <Type/> - <Description> - <Text>Abdominal Ultrasound</Text> - </Description> - <Status/> - <Source> - <Actor> - <ActorID>user@gmail.com</ActorID> - <ActorRole> - <Text>Patient</Text> - </ActorRole> - </Actor> - </Source> - </Procedure> - </Procedures> - </Body> - </ContinuityOfCareRecord> -</entry> -<entry> - <id>https://www.google.com/health/feeds/profile/default/r2zGPGewCeU</id> - <published>2008-09-29T03:13:03.434Z</published> - <updated>2008-09-29T03:13:03.435Z</updated> - <category scheme="http://schemas.google.com/health/item" term="Abdominoplasty"/> - <category scheme="http://schemas.google.com/g/2005#kind" term="http://schemas.google.com/health/kinds#profile"/> - <category term="PROCEDURE"/> - <title type="text"/> - <content type="html"/> - <link rel="http://schemas.google.com/health/data#complete" type="application/atom+xml" href="https://www.google.com/health/feeds/profile/default/-/%7Bhttp%3A%2F%2Fschemas.google.com%2Fhealth%2Fitem%7DAbdominoplasty/%7Bhttp%3A%2F%2Fschemas.google.com%2Fg%2F2005%23kind%7Dhttp%3A%2F%2Fschemas.google.com%2Fhealth%2Fkinds%23profile/PROCEDURE"/> - <link rel="self" type="application/atom+xml" href="https://www.google.com/health/feeds/profile/default/r2zGPGewCeU"/> - <link rel="edit" type="application/atom+xml" href="https://www.google.com/health/feeds/profile/default/r2zGPGewCeU"/> - <author> - <name>User Name</name> - <email>user@gmail.com</email> - </author> - <ContinuityOfCareRecord xmlns="urn:astm-org:CCR"> - <CCRDocumentObjectID>OUKgj5X0KMnbkC5sDL.yHA</CCRDocumentObjectID> - <Language/> - <DateTime> - <Type/> - </DateTime> - <Patient/> - <Body> - <Procedures> - <Procedure> - <Type/> - <Description> - <Text>Abdominoplasty</Text> - </Description> - <Status/> - <Source> - <Actor> - <ActorID>user@gmail.com</ActorID> - <ActorRole> - <Text>Patient</Text> - </ActorRole> - </Actor> - </Source> - </Procedure> - </Procedures> - </Body> - </ContinuityOfCareRecord> -</entry> -<entry> - <id>https://www.google.com/health/feeds/profile/default/_cCCbQ0O3ug</id> - <published>2008-09-29T03:13:29.041Z</published> - <updated>2008-09-29T03:13:29.042Z</updated> - <category scheme="http://schemas.google.com/g/2005#kind" term="http://schemas.google.com/health/kinds#profile"/> - <category scheme="http://schemas.google.com/health/item" term="Acetaldehyde - Blood"/> - <category term="LABTEST"/> - <title type="text"/> - <content type="html"/> - <link rel="http://schemas.google.com/health/data#complete" type="application/atom+xml" href="https://www.google.com/health/feeds/profile/default/-/%7Bhttp%3A%2F%2Fschemas.google.com%2Fg%2F2005%23kind%7Dhttp%3A%2F%2Fschemas.google.com%2Fhealth%2Fkinds%23profile/%7Bhttp%3A%2F%2Fschemas.google.com%2Fhealth%2Fitem%7DAcetaldehyde+-+Blood/LABTEST"/> - <link rel="self" type="application/atom+xml" href="https://www.google.com/health/feeds/profile/default/_cCCbQ0O3ug"/> - <link rel="edit" type="application/atom+xml" href="https://www.google.com/health/feeds/profile/default/_cCCbQ0O3ug"/> - <author> - <name>User Name</name> - <email>user@gmail.com</email> - </author> - <ContinuityOfCareRecord xmlns="urn:astm-org:CCR"> - <CCRDocumentObjectID>YWtomFb8aG.DueZ7z7fyug</CCRDocumentObjectID> - <Language/> - <DateTime> - <Type/> - </DateTime> - <Patient/> - <Body> - <Results> - <Result> - <Type/> - <Description/> - <Status/> - <Source> - <Actor> - <ActorID>user@gmail.com</ActorID> - <ActorRole> - <Text>Patient</Text> - </ActorRole> - </Actor> - </Source> - <Substance/> - <Test> - <DateTime> - <Type> - <Text>Collection start date</Text> - </Type> - <ExactDateTime>2008-09-03</ExactDateTime> - </DateTime> - <Type/> - <Description> - <Text>Acetaldehyde - Blood</Text> - </Description> - <Status/> - <TestResult> - <ResultSequencePosition>0</ResultSequencePosition> - <VariableResultModifier/> - <Units/> - </TestResult> - <ConfidenceValue/> - </Test> - </Result> - </Results> - </Body> - </ContinuityOfCareRecord> -</entry> -<entry> - <id>https://www.google.com/health/feeds/profile/default/BdyA3iJZyCc</id> - <published>2008-09-29T03:00:45.915Z</published> - <updated>2008-09-29T03:00:45.915Z</updated> - <category scheme="http://schemas.google.com/health/item" term="Aortic valve disorders"/> - <category scheme="http://schemas.google.com/g/2005#kind" term="http://schemas.google.com/health/kinds#profile"/> - <category term="CONDITION"/> - <title type="text">Aortic valve disorders - - - - - example.com - example.com - - - h1ljpoeKJ85li.1FHsG9Gw - - - - Hn0FE0IlcY-FMFFgSTxkvA/CONDITION/0 - - - Start date - - 2007-04-04T07:00:00Z - - - Aortic valve disorders - - 410.10 - ICD9 - 2004 - - - - Active - - - - example.com - - Information Provider - - - - - - - - - - https://www.google.com/health/feeds/profile/default/Cl.aMWIH5VA - 2008-09-29T03:13:34.996Z - 2008-09-29T03:13:34.997Z - - - - - <content type="html"/> - <link rel="http://schemas.google.com/health/data#complete" type="application/atom+xml" href="https://www.google.com/health/feeds/profile/default/-/%7Bhttp%3A%2F%2Fschemas.google.com%2Fg%2F2005%23kind%7Dhttp%3A%2F%2Fschemas.google.com%2Fhealth%2Fkinds%23profile/%7Bhttp%3A%2F%2Fschemas.google.com%2Fhealth%2Fitem%7DChickenpox+Vaccine/IMMUNIZATION"/> - <link rel="self" type="application/atom+xml" href="https://www.google.com/health/feeds/profile/default/Cl.aMWIH5VA"/> - <link rel="edit" type="application/atom+xml" href="https://www.google.com/health/feeds/profile/default/Cl.aMWIH5VA"/> - <author> - <name>User Name</name> - <email>user@gmail.com</email> - </author> - <ContinuityOfCareRecord xmlns="urn:astm-org:CCR"> - <CCRDocumentObjectID>KlhUqfftgELIitpKbqYalw</CCRDocumentObjectID> - <Language/> - <DateTime> - <Type/> - </DateTime> - <Patient/> - <Body> - <Immunizations> - <Immunization> - <Type/> - <Description/> - <Status/> - <Source> - <Actor> - <ActorID>user@gmail.com</ActorID> - <ActorRole> - <Text>Patient</Text> - </ActorRole> - </Actor> - </Source> - <Product> - <ProductName> - <Text>Chickenpox Vaccine</Text> - <Code> - <Value>21</Value> - <CodingSystem>HL7</CodingSystem> - </Code> - </ProductName> - </Product> - <Directions> - <Direction> - <Description/> - <DeliveryMethod/> - </Direction> - </Directions> - <Refills/> - </Immunization> - </Immunizations> - </Body> - </ContinuityOfCareRecord> -</entry> -<entry> - <id>https://www.google.com/health/feeds/profile/default/l0a7.FlX3_0</id> - <published>2008-09-29T03:14:47.461Z</published> - <updated>2008-09-29T03:14:47.461Z</updated> - <category scheme="http://schemas.google.com/g/2005#kind" term="http://schemas.google.com/health/kinds#profile"/> - <category term="DEMOGRAPHICS"/> - <category scheme="http://schemas.google.com/health/item" term="Demographics"/> - <title type="text">Demographics - - - - - - User Name - user@gmail.com - - - U5GDAVOxFbexQw3iyvqPYg - - - - - - - - - - - - - - - - 1984-07-22 - - - Male - - - - - - user@gmail.com - - Patient - - - - - - - - - https://www.google.com/health/feeds/profile/default/oIBDdgwFLyo - 2008-09-29T03:14:47.690Z - 2008-09-29T03:14:47.691Z - - - - FunctionalStatus - - - - - - User Name - user@gmail.com - - - W.EJcnhxb7W5M4eR4Tr1YA - - - - - - - - - - Pregnancy status - - - Not pregnant - - - - - user@gmail.com - - Patient - - - - - - - Breastfeeding status - - - Not breastfeeding - - - - - user@gmail.com - - Patient - - - - - - - - - - https://www.google.com/health/feeds/profile/default/wwljIlXuTVg - 2008-09-29T03:26:10.080Z - 2008-09-29T03:26:10.081Z - - - - - <content type="html"/> - <link rel="http://schemas.google.com/health/data#complete" type="application/atom+xml" href="https://www.google.com/health/feeds/profile/default/-/MEDICATION/%7Bhttp%3A%2F%2Fschemas.google.com%2Fg%2F2005%23kind%7Dhttp%3A%2F%2Fschemas.google.com%2Fhealth%2Fkinds%23profile/%7Bhttp%3A%2F%2Fschemas.google.com%2Fhealth%2Fitem%7DLipitor"/> - <link rel="self" type="application/atom+xml" href="https://www.google.com/health/feeds/profile/default/wwljIlXuTVg"/> - <link rel="edit" type="application/atom+xml" href="https://www.google.com/health/feeds/profile/default/wwljIlXuTVg"/> - <author> - <name>User Name</name> - <email>user@gmail.com</email> - </author> - <ContinuityOfCareRecord xmlns="urn:astm-org:CCR"> - <CCRDocumentObjectID>OrpghzvvbG_YaO5koqT2ug</CCRDocumentObjectID> - <Language/> - <DateTime> - <Type/> - </DateTime> - <Patient/> - <Body> - <Medications> - <Medication> - <Type/> - <Description/> - <Status> - <Text>ACTIVE</Text> - </Status> - <Source> - <Actor> - <ActorID>user@gmail.com</ActorID> - <ActorRole> - <Text>Patient</Text> - </ActorRole> - </Actor> - </Source> - <Product> - <ProductName> - <Text>Lipitor</Text> - </ProductName> - <Strength> - <Units/> - <StrengthSequencePosition>0</StrengthSequencePosition> - <VariableStrengthModifier/> - </Strength> - </Product> - <Directions> - <Direction> - <Description/> - <DeliveryMethod/> - <Dose> - <Units/> - <DoseSequencePosition>0</DoseSequencePosition> - <VariableDoseModifier/> - </Dose> - <Route> - <Text>By mouth</Text> - <Code> - <Value>C38288</Value> - <CodingSystem>FDA</CodingSystem> - </Code> - <RouteSequencePosition>0</RouteSequencePosition> - <MultipleRouteModifier/> - </Route> - </Direction> - </Directions> - <Refills/> - </Medication> - </Medications> - </Body> - </ContinuityOfCareRecord> -</entry> -<entry> - <id>https://www.google.com/health/feeds/profile/default/dd09TR12SiY</id> - <published>2008-09-29T07:52:17.175Z</published> - <updated>2008-09-29T07:52:17.176Z</updated> - <category scheme="http://schemas.google.com/g/2005#kind" term="http://schemas.google.com/health/kinds#profile"/> - <category scheme="http://schemas.google.com/health/item" term="Malaria"/> - <category term="CONDITION"/> - <title type="text"/> - <content type="html"/> - <link rel="http://schemas.google.com/health/data#complete" type="application/atom+xml" href="https://www.google.com/health/feeds/profile/default/-/%7Bhttp%3A%2F%2Fschemas.google.com%2Fg%2F2005%23kind%7Dhttp%3A%2F%2Fschemas.google.com%2Fhealth%2Fkinds%23profile/%7Bhttp%3A%2F%2Fschemas.google.com%2Fhealth%2Fitem%7DMalaria/CONDITION"/> - <link rel="self" type="application/atom+xml" href="https://www.google.com/health/feeds/profile/default/dd09TR12SiY"/> - <link rel="edit" type="application/atom+xml" href="https://www.google.com/health/feeds/profile/default/dd09TR12SiY"/> - <author> - <name>User Name</name> - <email>user@gmail.com</email> - </author> - <ContinuityOfCareRecord xmlns="urn:astm-org:CCR"> - <CCRDocumentObjectID>XF99N6X4lpy.jfPUPLMMSQ</CCRDocumentObjectID> - <Language/> - <DateTime> - <Type/> - </DateTime> - <Patient/> - <Body> - <Problems> - <Problem> - <Type/> - <Description> - <Text>Malaria</Text> - <Code> - <Value>136.9</Value> - <CodingSystem>ICD9_Broader</CodingSystem> - </Code> - <Code> - <Value>084.6</Value> - <CodingSystem>ICD9</CodingSystem> - </Code> - </Description> - <Status> - <Text>ACTIVE</Text> - </Status> - <Source> - <Actor> - <ActorID>user@gmail.com</ActorID> - <ActorRole> - <Text>Patient</Text> - </ActorRole> - </Actor> - </Source> - <HealthStatus> - <Description/> - </HealthStatus> - </Problem> - </Problems> - </Body> - </ContinuityOfCareRecord> -</entry> -<entry> - <id>https://www.google.com/health/feeds/profile/default/aS0Cf964DPs</id> - <published>2008-09-29T03:14:47.463Z</published> - <updated>2008-09-29T03:14:47.463Z</updated> - <category scheme="http://schemas.google.com/g/2005#kind" term="http://schemas.google.com/health/kinds#profile"/> - <category term="DEMOGRAPHICS"/> - <category scheme="http://schemas.google.com/health/item" term="SocialHistory (Drinking, Smoking)"/> - <title type="text">SocialHistory (Drinking, Smoking) - - - - - - User Name - user@gmail.com - - - kXylGU5YXLBzriv61xPGZQ - - - - - - - - - - Race - - S15814 - HL7 - - - - White - - - - - user@gmail.com - - Patient - - - - - - - - - - - - - - - https://www.google.com/health/feeds/profile/default/s5lII5xfj_g - 2008-09-29T03:14:47.544Z - 2008-09-29T03:14:47.545Z - - - - VitalSigns - - - - - - User Name - user@gmail.com - - - FTTIiY0TVVj35kZqFFjPjQ - - - - - - - - - - - - - - user@gmail.com - - Patient - - - - - - - - Height - - - - 0 - - 70 - - inches - - - - - - - - - - - - user@gmail.com - - Patient - - - - - - - - Weight - - - - 0 - - 2480 - - ounces - - - - - - - - - - - - user@gmail.com - - Patient - - - - - - - - Blood Type - - - - 0 - - O+ - - - - - - - - - -""" - -HEALTH_PROFILE_LIST_ENTRY = """ - - https://www.google.com/health/feeds/profile/list/vndCn5sdfwdEIY - 1970-01-01T00:00:00.000Z - profile name - vndCn5sdfwdEIY - - - - user@gmail.com - -""" - -BOOK_ENTRY = """"""\ - """"""\ - """http://www.google.com/books/feeds/volumes/b7GZr5Btp30C"""\ - """2009-04-24T23:35:16.000Z"""\ - """"""\ - """A theory of justice"""\ - """"""\ - """"""\ - """"""\ - """"""\ - """"""\ - """"""\ - """"""\ - """"""\ - """"""\ - """John Rawls"""\ - """1999"""\ - """p Since it appeared in 1971, John Rawls's i A Theory of Justice /i has become a classic. The author has now revised the original edition to clear up a number of difficulties he and others have found in the original book. /p p Rawls aims to express an essential part of the common core of the democratic tradition--justice as fairness--and to provide an alternative to utilitarianism, which had dominated the Anglo-Saxon tradition of political thought since the nineteenth century. Rawls substitutes the ideal of the social contract as a more satisfactory account of the basic rights and liberties of citizens as free and equal persons. "Each person," writes Rawls, "possesses an inviolability founded on justice that even the welfare of society as a whole cannot override." Advancing the ideas of Rousseau, Kant, Emerson, and Lincoln, Rawls's theory is as powerful today as it was when first published. /p"""\ - """538 pages"""\ - """b7GZr5Btp30C"""\ - """ISBN:0198250541"""\ - """ISBN:9780198250548"""\ - """en"""\ - """Oxford University Press"""\ - """A theory of justice"""\ -"""""" - -BOOK_FEED = """"""\ - """"""\ - """http://www.google.com/books/feeds/volumes"""\ - """2009-04-24T23:39:47.000Z"""\ - """"""\ - """Search results for 9780198250548"""\ - """"""\ - """"""\ - """"""\ - """"""\ - """Google Books Search"""\ - """http://www.google.com"""\ - """"""\ - """Google Book Search data API"""\ - """1"""\ - """1"""\ - """20"""\ - """"""\ - """http://www.google.com/books/feeds/volumes/b7GZr5Btp30C"""\ - """2009-04-24T23:39:47.000Z"""\ - """"""\ - """A theory of justice"""\ - """"""\ - """"""\ - """"""\ - """"""\ - """"""\ - """"""\ - """"""\ - """"""\ - """"""\ - """John Rawls"""\ - """1999"""\ - """... 9780198250548 ..."""\ - """538 pages"""\ - """b7GZr5Btp30C"""\ - """ISBN:0198250541"""\ - """ISBN:9780198250548"""\ - """Law"""\ - """A theory of justice"""\ - """"""\ -"""""" - -MAP_FEED = """ - - http://maps.google.com/maps/feeds/maps/208825816854482607313 - 2009-07-27T18:48:29.631Z - - My maps - - - - - - - Roman - - 1 - 1 - 1 - - http://maps.google.com/maps/feeds/maps/208825816854482607313/00046fb45f88fa910bcea - 2009-07-27T18:46:34.451Z - 2009-07-27T18:48:29.631Z - 2009-07-27T18:48:29.631Z - - yes - - - Untitled - - - - - - - Roman - - - -""" - -MAP_ENTRY = """ - - http://maps.google.com/maps/feeds/maps/208825816854482607313/00046fb45f88fa910bcea - 2009-07-27T18:46:34.451Z - 2009-07-27T18:48:29.631Z - 2009-07-27T18:48:29.631Z - - yes - - - Untitled - - - - - - - Roman - - -""" - -MAP_FEATURE_FEED = """ - - http://maps.google.com/maps/feeds/features/208825816854482607313/00046fb45f88fa910bcea - 2009-07-27T18:48:29.631Z - - Untitled - - - - - 4 - 1 - 4 - - http://maps.google.com/maps/feeds/features/208825816854482607313/00046fb45f88fa910bcea/00046fb4632573b19e0b7 - 2009-07-27T18:47:35.037Z - 2009-07-27T18:47:35.037Z - 2009-07-27T18:47:35.037Z - - Some feature title - - - Some feature title - Some feature content]]> - - - -113.818359,41.442726,0.0 - - - - - - - Roman - - - Roman - - - - http://maps.google.com/maps/feeds/features/208825816854482607313/00046fb45f88fa910bcea/00046fb46325e839a11e6 - 2009-07-27T18:47:35.067Z - 2009-07-27T18:48:22.184Z - 2009-07-27T18:48:22.184Z - - A cool poly! - - - A cool poly! - And a description]]> - - - - - 1 - -109.775391,47.457809,0.0 -99.755859,51.508742,0.0 -92.900391,48.04871,0.0 -92.8125,44.339565,0.0 -95.273437,44.402392,0.0 -97.207031,46.619261,0.0 -100.898437,46.073231,0.0 -102.480469,43.068888,0.0 -110.742187,45.274886,0.0 -109.775391,47.457809,0.0 - - - - - - - - - Roman - - - Roman - - - - http://maps.google.com/maps/feeds/features/208825816854482607313/00046fb45f88fa910bcea/00046fb465f5002e56b7a - 2009-07-27T18:48:22.194Z - 2009-07-27T18:48:22.194Z - 2009-07-27T18:48:22.194Z - - New Mexico - - - New Mexico - Word.]]> - - - 1 - -110.039062,37.788081,0.0 -103.183594,37.926868,0.0 -103.183594,32.472695,0.0 -108.896484,32.026706,0.0 -109.863281,31.203405,0.0 -110.039062,37.788081,0.0 - - - - - - - Roman - - - Roman - - - -""" - -MAP_FEATURE_ENTRY = """ - - http://maps.google.com/maps/feeds/features/208825816854482607313/00046fb45f88fa910bcea/00046fb4632573b19e0b7 - 2009-07-27T18:47:35.037Z - 2009-07-27T18:47:35.037Z - 2009-07-27T18:47:35.037Z - - Some feature title - - - Some feature title - Some feature content]]> - - - -113.818359,41.442726,0.0 - - - - - - - Roman - - - Roman - - -""" - -MAP_FEATURE_KML = """ - Some feature title - Some feature content]]> - - - -113.818359,41.442726,0.0 - - -""" - -SITES_LISTPAGE_ENTRY = ''' - - http:///sites.google.com/feeds/content/site/gdatatestsite/1712987567114738703 - 2009-06-16T00:37:37.393Z - - ListPagesTitle - -
- -
stuff go here
asdf
-
sdf
-
-
-
-
-
-
-
- - - - Test User - test@gmail.com - - - - - - - - - - - -
''' - -SITES_COMMENT_ENTRY = ''' - - http://sites.google.com/feeds/content/site/gdatatestsite/abc123 - 2009-06-15T18:40:22.407Z - - - <content type="xhtml"> - <div xmlns="http://www.w3.org/1999/xhtml">first comment</div> - </content> - <link rel="http://schemas.google.com/sites/2008#parent" type="application/atom+xml" href="http://sites.google.com/feeds/content/site/gdatatestsite/abc123parent"/> - <link rel="self" type="application/atom+xml" href="http://sites.google.com/feeds/content/site/gdatatestsite/abc123"/> - <link rel="edit" type="application/atom+xml" href="http://sites.google.com/feeds/content/site/gdatatestsite/abc123"/> - <author> - <name>Test User</name> - <email>test@gmail.com</email> - </author> - <thr:in-reply-to xmlns:thr="http://purl.org/syndication/thread/1.0" href="http://sites.google.com/site/gdatatestsite/annoucment/testpost" ref="http://sites.google.com/feeds/content/site/gdatatestsite/abc123" source="http://sites.google.com/feeds/content/site/gdatatestsite" type="text/html"/> -</entry>''' - -SITES_LISTITEM_ENTRY = '''<?xml version="1.0" encoding="UTF-8"?> -<entry xmlns="http://www.w3.org/2005/Atom"> - <id>http://sites.google.com/feeds/content/site/gdatatestsite/abc123</id> - <updated>2009-06-16T00:34:55.633Z</updated> - <category scheme="http://schemas.google.com/g/2005#kind" term="http://schemas.google.com/sites/2008#listitem"/> - <title type="text"/> - <link rel="http://schemas.google.com/sites/2008#parent" type="application/atom+xml" href="http://sites.google.com/feeds/content/site/gdatatestsite/abc123def"/> - <link rel="self" type="application/atom+xml" href="http://sites.google.com/feeds/content/site/gdatatestsite/abc123"/> - <link rel="edit" type="application/atom+xml" href="http://sites.google.com/feeds/content/site/gdatatestsite/abc123"/> - <author> - <name>Test User</name> - <email>test@gmail.com</email> - </author> - <gs:field xmlns:gs="http://schemas.google.com/spreadsheets/2006" index="A" name="Owner">test value</gs:field> - <gs:field xmlns:gs="http://schemas.google.com/spreadsheets/2006" index="B" name="Description">test</gs:field> - <gs:field xmlns:gs="http://schemas.google.com/spreadsheets/2006" index="C" name="Resolution">90</gs:field> - <gs:field xmlns:gs="http://schemas.google.com/spreadsheets/2006" index="D" name="Complete"/> - <gs:field xmlns:gs="http://schemas.google.com/spreadsheets/2006" index="E" name="MyCo">2009-05-31</gs:field> -</entry>''' - -SITES_CONTENT_FEED = '''<?xml version="1.0" encoding="UTF-8"?> -<feed xmlns="http://www.w3.org/2005/Atom" xmlns:openSearch="http://a9.com/-/spec/opensearch/1.1/" -xmlns:sites="http://schemas.google.com/sites/2008" xmlns:gs="http://schemas.google.com/spreadsheets/2006" -xmlns:dc="http://purl.org/dc/terms" xmlns:batch="http://schemas.google.com/gdata/batch" -xmlns:gd="http://schemas.google.com/g/2005" xmlns:thr="http://purl.org/syndication/thread/1.0"> -<id>http://sites.google.com/feeds/content/site/gdatatestsite</id> -<updated>2009-06-15T21:35:43.282Z</updated> -<link rel="http://schemas.google.com/g/2005#feed" type="application/atom+xml" href="http://sites.google.com/feeds/content/site/gdatatestsite"/> -<link rel="http://schemas.google.com/g/2005#post" type="application/atom+xml" href="http://sites.google.com/feeds/content/site/gdatatestsite"/> -<link rel="self" type="application/atom+xml" href="http://sites.google.com/feeds/content/site/gdatatestsite"/> -<generator version="1" uri="http://sites.google.com">Google Sites</generator> -<openSearch:startIndex>1</openSearch:startIndex> -<entry> - <id>http:///sites.google.com/feeds/content/site/gdatatestsite/1712987567114738703</id> - <updated>2009-06-16T00:37:37.393Z</updated> - <category scheme="http://schemas.google.com/g/2005#kind" term="http://schemas.google.com/sites/2008#listpage"/> - <title type="text">ListPagesTitle - -
- -
stuff go here
asdf
-
sdf
-
-
-
-
-
-
-
- - - - - - Test User - test@gmail.com - - - - - - - - - - - 2 - - home - -
- - http://sites.google.com/feeds/content/site/gdatatestsite/abc123 - 2009-06-17T00:40:37.082Z - - filecabinet - -
- -
sdf
-
-
-
- - - - - - Test User - test@gmail.com - - -
- - http://sites.google.com/feeds/content/site/gdatatestsite/abc123 - 2009-06-16T00:34:55.633Z - - - <link rel="http://schemas.google.com/sites/2008#parent" type="application/atom+xml" href="http://sites.google.com/feeds/content/site/gdatatestsite/abc123def"/> - <link rel="self" type="application/atom+xml" href="http://sites.google.com/feeds/content/site/gdatatestsite/abc123"/> - <link rel="edit" type="application/atom+xml" href="http://sites.google.com/feeds/content/site/gdatatestsite/abc123"/> - <link rel="http://schemas.google.com/sites/2008#revision" type="application/atom+xml" href="http:///sites.google.com/feeds/content/site/gdatatestsite/abc123"/> - <author> - <name>Test User</name> - <email>test@gmail.com</email> - </author> - <gs:field xmlns:gs="http://schemas.google.com/spreadsheets/2006" index="A" name="Owner">test value</gs:field> - <gs:field xmlns:gs="http://schemas.google.com/spreadsheets/2006" index="B" name="Description">test</gs:field> - <gs:field xmlns:gs="http://schemas.google.com/spreadsheets/2006" index="C" name="Resolution">90</gs:field> - <gs:field xmlns:gs="http://schemas.google.com/spreadsheets/2006" index="D" name="Complete"/> - <gs:field xmlns:gs="http://schemas.google.com/spreadsheets/2006" index="E" name="MyCo">2009-05-31</gs:field> -</entry> -<entry> - <id>http://sites.google.com/feeds/content/site/gdatatestsite/abc123</id> - <updated>2009-06-15T18:40:32.922Z</updated> - <category scheme="http://schemas.google.com/g/2005#kind" term="http://schemas.google.com/sites/2008#attachment"/> - <title type="text">testFile.ods - - - - - - - Test User - test@gmail.com - - - something else - - - http://sites.google.com/feeds/content/site/gdatatestsite/abc123 - 2009-06-15T18:40:22.407Z - - - <content type="xhtml"> - <div xmlns="http://www.w3.org/1999/xhtml">first comment</div> - </content> - <link rel="http://schemas.google.com/sites/2008#parent" type="application/atom+xml" href="http://sites.google.com/feeds/content/site/gdatatestsite/abc123"/> - <link rel="self" type="application/atom+xml" href="http://sites.google.com/feeds/content/site/gdatatestsite/abc123"/> - <link rel="edit" type="application/atom+xml" href="http://sites.google.com/feeds/content/site/gdatatestsite/abc123"/> - <link rel="http://schemas.google.com/sites/2008#revision" type="application/atom+xml" href="http:///sites.google.com/feeds/content/site/gdatatestsite/abc123"/> - <author> - <name>Test User</name> - <email>test@gmail.com</email> - </author> - <thr:in-reply-to xmlns:thr="http://purl.org/syndication/thread/1.0" href="http://sites.google.com/site/gdatatestsite/annoucment/testpost" ref="http://sites.google.com/feeds/content/site/gdatatestsite/abc123" source="http://sites.google.com/feeds/content/site/gdatatestsite" type="text/html"/> -</entry> -<entry> - <id>http://sites.google.com/feeds/content/site/gdatatestsite/abc123</id> - <updated>2009-06-15T18:40:16.388Z</updated> - <category scheme="http://schemas.google.com/g/2005#kind" term="http://schemas.google.com/sites/2008#announcement"/> - <title type="text">TestPost - -
- -
content goes here
-
-
-
- - - - - - Test User - test@gmail.com - -
- - http://sites.google.com/feeds/content/site/gdatatestsite/abc123 - 2009-06-12T23:37:59.417Z - - Home - -
- -
Some Content goes here
-
-
-
- -
-
-
-
-
-
- - - - - Test User - test@gmail.com - -
- - http://sites.google.com/feeds/content/site/gdatatestsite/2639323850129333500 - 2009-06-12T23:32:09.191Z - - annoucment - -
-
-
- - - - - Test User - test@gmail.com - - -
-''' - -SITES_ACTIVITY_FEED = ''' - -http://sites.google.com/feeds/activity/site/siteName -2009-08-19T05:46:01.503Z -Activity - - - -Google Sites -1 - -http://sites.google.com/feeds/activity/site/siteName/197441951793148343 -2009-08-17T00:08:19.387Z - -NewWebpage3 - -
User deleted NewWebpage3 -
-
- - - - - User - user@gmail.com - -
- -http://sites.google.com/feeds/activity/site/siteName/7299542210274956360 -2009-08-17T00:08:03.711Z - -NewWebpage3 - -
User edited NewWebpage3 -
-
- - - - - User - user@gmail.com - -
-
''' - -SITES_REVISION_FEED = ''' - -http://sites.google.com/feeds/revision/site/siteName/2947510322163358574 -2009-08-19T06:20:18.151Z -Revisions - - -Google Sites -1 - -http://sites.google.com/feeds/revision/site/siteName/2947510322163358574/1 -2009-08-19T04:33:14.856Z - - -<content type="xhtml"> - <div xmlns="http://www.w3.org/1999/xhtml"> - <table cellspacing="0" class="sites-layout-name-one-column sites-layout-hbox"> - <tbody> - <tr> - <td class="sites-layout-tile sites-tile-name-content-1">testcomment</td> - </tr> - </tbody> - </table> -</div> -</content> -<link rel="http://schemas.google.com/sites/2008#parent" type="application/atom+xml" href="http://sites.google.com/feeds/content/site/siteName/54395424125706119"/> -<link rel="alternate" type="text" href="http://sites.google.com/site/system/app/pages/admin/compare?wuid=wuid%3Agx%3A28e7a9057c581b6e&rev1=1"/> -<link rel="self" type="application/atom+xml" href="http://sites.google.com/feeds/revision/site/siteName/2947510322163358574/1"/> -<author> - <name>User</name> - <email>user@gmail.com</email> -</author> -<thr:in-reply-to href="http://sites.google.com/site/siteName/code/js" ref="http://sites.google.com/feeds/content/site/siteName/54395424125706119" source="http://sites.google.com/feeds/content/google.com/siteName" type="text/html;charset=UTF-8"/> -<sites:revision>1</sites:revision> -</entry> -</feed>''' - -SITES_SITE_FEED = ''' -<feed xmlns="http://www.w3.org/2005/Atom" xmlns:openSearch="http://a9.com/-/spec/opensearch/1.1/" xmlns:gAcl="http://schemas.google.com/acl/2007" xmlns:sites="http://schemas.google.com/sites/2008" xmlns:gs="http://schemas.google.com/spreadsheets/2006" xmlns:dc="http://purl.org/dc/terms" xmlns:batch="http://schemas.google.com/gdata/batch" xmlns:gd="http://schemas.google.com/g/2005" xmlns:thr="http://purl.org/syndication/thread/1.0"> -<id>https://sites.google.com/feeds/site/example.com</id> -<updated>2009-12-09T01:05:54.631Z</updated> -<title>Site - - - -Google Sites -1 - -https://sites.google.com/feeds/site/example.com/new-test-site -2009-12-02T22:55:31.040Z -2009-12-02T22:55:31.040Z -New Test Site -A new site to hold memories - - - - - -new-test-site -iceberg - - -https://sites.google.com/feeds/site/example.com/newautosite2 -2009-12-05T00:28:01.077Z -2009-12-05T00:28:01.077Z -newAutoSite3 -A new site to hold memories2 - - - - -newautosite2 -default - -''' - -SITES_ACL_FEED = ''' - -https://sites.google.comsites.google.com/feeds/acl/site/example.com/new-test-site -2009-12-09T01:24:59.080Z - -Acl - - - -Google Sites -1 - - https://sites.google.com/feeds/acl/site/google.com/new-test-site/user%3Auser%40example.com - 2009-12-09T01:24:59.080Z - 2009-12-09T01:24:59.080Z - - - - - - -''' - -ANALYTICS_ACCOUNT_FEED_old = ''' - -http://www.google.com/analytics/feeds/accounts/abc@test.com -2009-06-25T03:55:22.000-07:00 -Profile list for abc@test.com - - -Google Analytics - -Google Analytics -12 -1 -12 - -http://www.google.com/analytics/feeds/accounts/ga:1174 -2009-06-25T03:55:22.000-07:00 -www.googlestore.com - -ga:1174 - - - - - - - -''' - -ANALYTICS_ACCOUNT_FEED = ''' - - http://www.google.com/analytics/feeds/accounts/api.nickm@google.com - 2009-10-14T09:14:25.000-07:00 - Profile list for abc@test.com - - - Google Analytics - - Google Analytics - 37 - 1 - 37 - - ga:operatingSystem==iPhone - - - http://www.google.com/analytics/feeds/accounts/ga:1174 - 2009-10-14T09:14:25.000-07:00 - www.googlestore.com - - - - - - - - - - - - - - - - - - - - - - ga:1174 - -''' - -ANALYTICS_DATA_FEED = ''' - - http://www.google.com/analytics/feeds/data?ids=ga:1174&dimensions=ga:medium,ga:source&metrics=ga:bounces,ga:visits&filters=ga:medium%3D%3Dreferral&start-date=2008-10-01&end-date=2008-10-31 - 2008-10-31T16:59:59.999-07:00 - Google Analytics Data for Profile 1174 - - - - Google Analytics - - Google Analytics - 6451 - 1 - 2 - 2008-10-01 - 2008-10-31 - - ga:operatingSystem==iPhone - - - - - - - ga:1174 - www.googlestore.com - - - - - - http://www.google.com/analytics/feeds/data?ids=ga:1174&ga:medium=referral&ga:source=blogger.com&filters=ga:medium%3D%3Dreferral&start-date=2008-10-01&end-date=2008-10-31 - 2008-10-30T17:00:00.001-07:00 - ga:source=blogger.com | ga:medium=referral - - - - - - -''' diff --git a/gdata/webmastertools/__init__.py b/gdata/webmastertools/__init__.py deleted file mode 100644 index 7ad20ff35e..0000000000 --- a/gdata/webmastertools/__init__.py +++ /dev/null @@ -1,544 +0,0 @@ -#!/usr/bin/python -# -# Copyright (C) 2008 Yu-Jie Lin -# -# 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. - -"""Contains extensions to Atom objects used with Google Webmaster Tools.""" - - -__author__ = 'livibetter (Yu-Jie Lin)' - - -try: - from xml.etree import cElementTree as ElementTree -except ImportError: - try: - import cElementTree as ElementTree - except ImportError: - try: - from xml.etree import ElementTree - except ImportError: - from elementtree import ElementTree -import atom -import gdata - - -# XML namespaces which are often used in Google Webmaster Tools entities. -GWEBMASTERTOOLS_NAMESPACE = 'http://schemas.google.com/webmasters/tools/2007' -GWEBMASTERTOOLS_TEMPLATE = '{http://schemas.google.com/webmasters/tools/2007}%s' - - -class Indexed(atom.AtomBase): - _tag = 'indexed' - _namespace = GWEBMASTERTOOLS_NAMESPACE - - -def IndexedFromString(xml_string): - return atom.CreateClassFromXMLString(Indexed, xml_string) - - -class Crawled(atom.Date): - _tag = 'crawled' - _namespace = GWEBMASTERTOOLS_NAMESPACE - - -def CrawledFromString(xml_string): - return atom.CreateClassFromXMLString(Crawled, xml_string) - - -class GeoLocation(atom.AtomBase): - _tag = 'geolocation' - _namespace = GWEBMASTERTOOLS_NAMESPACE - - -def GeoLocationFromString(xml_string): - return atom.CreateClassFromXMLString(GeoLocation, xml_string) - - -class PreferredDomain(atom.AtomBase): - _tag = 'preferred-domain' - _namespace = GWEBMASTERTOOLS_NAMESPACE - - -def PreferredDomainFromString(xml_string): - return atom.CreateClassFromXMLString(PreferredDomain, xml_string) - - -class CrawlRate(atom.AtomBase): - _tag = 'crawl-rate' - _namespace = GWEBMASTERTOOLS_NAMESPACE - - -def CrawlRateFromString(xml_string): - return atom.CreateClassFromXMLString(CrawlRate, xml_string) - - -class EnhancedImageSearch(atom.AtomBase): - _tag = 'enhanced-image-search' - _namespace = GWEBMASTERTOOLS_NAMESPACE - - -def EnhancedImageSearchFromString(xml_string): - return atom.CreateClassFromXMLString(EnhancedImageSearch, xml_string) - - -class Verified(atom.AtomBase): - _tag = 'verified' - _namespace = GWEBMASTERTOOLS_NAMESPACE - - -def VerifiedFromString(xml_string): - return atom.CreateClassFromXMLString(Verified, xml_string) - - -class VerificationMethodMeta(atom.AtomBase): - _tag = 'meta' - _namespace = atom.ATOM_NAMESPACE - _children = atom.AtomBase._children.copy() - _attributes = atom.AtomBase._attributes.copy() - _attributes['name'] = 'name' - _attributes['content'] = 'content' - - def __init__(self, text=None, name=None, content=None, - extension_elements=None, extension_attributes=None): - self.text = text - self.name = name - self.content = content - self.extension_elements = extension_elements or [] - self.extension_attributes = extension_attributes or {} - - -def VerificationMethodMetaFromString(xml_string): - return atom.CreateClassFromXMLString(VerificationMethodMeta, xml_string) - - -class VerificationMethod(atom.AtomBase): - _tag = 'verification-method' - _namespace = GWEBMASTERTOOLS_NAMESPACE - _children = atom.Text._children.copy() - _attributes = atom.Text._attributes.copy() - _children['{%s}meta' % atom.ATOM_NAMESPACE] = ( - 'meta', VerificationMethodMeta) - _attributes['in-use'] = 'in_use' - _attributes['type'] = 'type' - - def __init__(self, text=None, in_use=None, meta=None, type=None, - extension_elements=None, extension_attributes=None): - self.text = text - self.in_use = in_use - self.meta = meta - self.type = type - self.extension_elements = extension_elements or [] - self.extension_attributes = extension_attributes or {} - - -def VerificationMethodFromString(xml_string): - return atom.CreateClassFromXMLString(VerificationMethod, xml_string) - - -class MarkupLanguage(atom.AtomBase): - _tag = 'markup-language' - _namespace = GWEBMASTERTOOLS_NAMESPACE - - -def MarkupLanguageFromString(xml_string): - return atom.CreateClassFromXMLString(MarkupLanguage, xml_string) - - -class SitemapMobile(atom.AtomBase): - _tag = 'sitemap-mobile' - _namespace = GWEBMASTERTOOLS_NAMESPACE - _children = atom.AtomBase._children.copy() - _attributes = atom.AtomBase._attributes.copy() - _children['{%s}markup-language' % GWEBMASTERTOOLS_NAMESPACE] = ( - 'markup_language', [MarkupLanguage]) - - def __init__(self, markup_language=None, - extension_elements=None, extension_attributes=None, text=None): - - self.markup_language = markup_language or [] - self.text = text - self.extension_elements = extension_elements or [] - self.extension_attributes = extension_attributes or {} - - -def SitemapMobileFromString(xml_string): - return atom.CreateClassFromXMLString(SitemapMobile, xml_string) - - -class SitemapMobileMarkupLanguage(atom.AtomBase): - _tag = 'sitemap-mobile-markup-language' - _namespace = GWEBMASTERTOOLS_NAMESPACE - - -def SitemapMobileMarkupLanguageFromString(xml_string): - return atom.CreateClassFromXMLString(SitemapMobileMarkupLanguage, xml_string) - - -class PublicationLabel(atom.AtomBase): - _tag = 'publication-label' - _namespace = GWEBMASTERTOOLS_NAMESPACE - - -def PublicationLabelFromString(xml_string): - return atom.CreateClassFromXMLString(PublicationLabel, xml_string) - - -class SitemapNews(atom.AtomBase): - _tag = 'sitemap-news' - _namespace = GWEBMASTERTOOLS_NAMESPACE - _children = atom.AtomBase._children.copy() - _attributes = atom.AtomBase._attributes.copy() - _children['{%s}publication-label' % GWEBMASTERTOOLS_NAMESPACE] = ( - 'publication_label', [PublicationLabel]) - - def __init__(self, publication_label=None, - extension_elements=None, extension_attributes=None, text=None): - - self.publication_label = publication_label or [] - self.text = text - self.extension_elements = extension_elements or [] - self.extension_attributes = extension_attributes or {} - - -def SitemapNewsFromString(xml_string): - return atom.CreateClassFromXMLString(SitemapNews, xml_string) - - -class SitemapNewsPublicationLabel(atom.AtomBase): - _tag = 'sitemap-news-publication-label' - _namespace = GWEBMASTERTOOLS_NAMESPACE - - -def SitemapNewsPublicationLabelFromString(xml_string): - return atom.CreateClassFromXMLString(SitemapNewsPublicationLabel, xml_string) - - -class SitemapLastDownloaded(atom.Date): - _tag = 'sitemap-last-downloaded' - _namespace = GWEBMASTERTOOLS_NAMESPACE - - -def SitemapLastDownloadedFromString(xml_string): - return atom.CreateClassFromXMLString(SitemapLastDownloaded, xml_string) - - -class SitemapType(atom.AtomBase): - _tag = 'sitemap-type' - _namespace = GWEBMASTERTOOLS_NAMESPACE - - -def SitemapTypeFromString(xml_string): - return atom.CreateClassFromXMLString(SitemapType, xml_string) - - -class SitemapStatus(atom.AtomBase): - _tag = 'sitemap-status' - _namespace = GWEBMASTERTOOLS_NAMESPACE - - -def SitemapStatusFromString(xml_string): - return atom.CreateClassFromXMLString(SitemapStatus, xml_string) - - -class SitemapUrlCount(atom.AtomBase): - _tag = 'sitemap-url-count' - _namespace = GWEBMASTERTOOLS_NAMESPACE - - -def SitemapUrlCountFromString(xml_string): - return atom.CreateClassFromXMLString(SitemapUrlCount, xml_string) - - -class LinkFinder(atom.LinkFinder): - """An "interface" providing methods to find link elements - - SitesEntry elements often contain multiple links which differ in the rel - attribute or content type. Often, developers are interested in a specific - type of link so this class provides methods to find specific classes of links. - - This class is used as a mixin in SitesEntry. - """ - - def GetSelfLink(self): - """Find the first link with rel set to 'self' - - Returns: - An atom.Link or none if none of the links had rel equal to 'self' - """ - - for a_link in self.link: - if a_link.rel == 'self': - return a_link - return None - - def GetEditLink(self): - for a_link in self.link: - if a_link.rel == 'edit': - return a_link - return None - - def GetPostLink(self): - """Get a link containing the POST target URL. - - The POST target URL is used to insert new entries. - - Returns: - A link object with a rel matching the POST type. - """ - for a_link in self.link: - if a_link.rel == 'http://schemas.google.com/g/2005#post': - return a_link - return None - - def GetFeedLink(self): - for a_link in self.link: - if a_link.rel == 'http://schemas.google.com/g/2005#feed': - return a_link - return None - - -class SitesEntry(atom.Entry, LinkFinder): - """A Google Webmaster Tools meta Entry flavor of an Atom Entry """ - - _tag = atom.Entry._tag - _namespace = atom.Entry._namespace - _children = atom.Entry._children.copy() - _attributes = atom.Entry._attributes.copy() - _children['{%s}entryLink' % gdata.GDATA_NAMESPACE] = ( - 'entry_link', [gdata.EntryLink]) - _children['{%s}indexed' % GWEBMASTERTOOLS_NAMESPACE] = ('indexed', Indexed) - _children['{%s}crawled' % GWEBMASTERTOOLS_NAMESPACE] = ( - 'crawled', Crawled) - _children['{%s}geolocation' % GWEBMASTERTOOLS_NAMESPACE] = ( - 'geolocation', GeoLocation) - _children['{%s}preferred-domain' % GWEBMASTERTOOLS_NAMESPACE] = ( - 'preferred_domain', PreferredDomain) - _children['{%s}crawl-rate' % GWEBMASTERTOOLS_NAMESPACE] = ( - 'crawl_rate', CrawlRate) - _children['{%s}enhanced-image-search' % GWEBMASTERTOOLS_NAMESPACE] = ( - 'enhanced_image_search', EnhancedImageSearch) - _children['{%s}verified' % GWEBMASTERTOOLS_NAMESPACE] = ( - 'verified', Verified) - _children['{%s}verification-method' % GWEBMASTERTOOLS_NAMESPACE] = ( - 'verification_method', [VerificationMethod]) - - def __GetId(self): - return self.__id - - # This method was created to strip the unwanted whitespace from the id's - # text node. - def __SetId(self, id): - self.__id = id - if id is not None and id.text is not None: - self.__id.text = id.text.strip() - - id = property(__GetId, __SetId) - - def __init__(self, category=None, content=None, - atom_id=None, link=None, title=None, updated=None, - entry_link=None, indexed=None, crawled=None, - geolocation=None, preferred_domain=None, crawl_rate=None, - enhanced_image_search=None, - verified=None, verification_method=None, - extension_elements=None, extension_attributes=None, text=None): - atom.Entry.__init__(self, category=category, - content=content, atom_id=atom_id, link=link, - title=title, updated=updated, text=text) - - self.entry_link = entry_link or [] - self.indexed = indexed - self.crawled = crawled - self.geolocation = geolocation - self.preferred_domain = preferred_domain - self.crawl_rate = crawl_rate - self.enhanced_image_search = enhanced_image_search - self.verified = verified - self.verification_method = verification_method or [] - - -def SitesEntryFromString(xml_string): - return atom.CreateClassFromXMLString(SitesEntry, xml_string) - - -class SitesFeed(atom.Feed, LinkFinder): - """A Google Webmaster Tools meta Sites feed flavor of an Atom Feed""" - - _tag = atom.Feed._tag - _namespace = atom.Feed._namespace - _children = atom.Feed._children.copy() - _attributes = atom.Feed._attributes.copy() - _children['{%s}startIndex' % gdata.OPENSEARCH_NAMESPACE] = ( - 'start_index', gdata.StartIndex) - _children['{%s}entry' % atom.ATOM_NAMESPACE] = ('entry', [SitesEntry]) - del _children['{%s}generator' % atom.ATOM_NAMESPACE] - del _children['{%s}author' % atom.ATOM_NAMESPACE] - del _children['{%s}contributor' % atom.ATOM_NAMESPACE] - del _children['{%s}logo' % atom.ATOM_NAMESPACE] - del _children['{%s}icon' % atom.ATOM_NAMESPACE] - del _children['{%s}rights' % atom.ATOM_NAMESPACE] - del _children['{%s}subtitle' % atom.ATOM_NAMESPACE] - - def __GetId(self): - return self.__id - - def __SetId(self, id): - self.__id = id - if id is not None and id.text is not None: - self.__id.text = id.text.strip() - - id = property(__GetId, __SetId) - - def __init__(self, start_index=None, atom_id=None, title=None, entry=None, - category=None, link=None, updated=None, - extension_elements=None, extension_attributes=None, text=None): - """Constructor for Source - - Args: - category: list (optional) A list of Category instances - id: Id (optional) The entry's Id element - link: list (optional) A list of Link instances - title: Title (optional) the entry's title element - updated: Updated (optional) the entry's updated element - entry: list (optional) A list of the Entry instances contained in the - feed. - text: String (optional) The text contents of the element. This is the - contents of the Entry's XML text node. - (Example: This is the text) - extension_elements: list (optional) A list of ExtensionElement instances - which are children of this element. - extension_attributes: dict (optional) A dictionary of strings which are - the values for additional XML attributes of this element. - """ - - self.start_index = start_index - self.category = category or [] - self.id = atom_id - self.link = link or [] - self.title = title - self.updated = updated - self.entry = entry or [] - self.text = text - self.extension_elements = extension_elements or [] - self.extension_attributes = extension_attributes or {} - - -def SitesFeedFromString(xml_string): - return atom.CreateClassFromXMLString(SitesFeed, xml_string) - - -class SitemapsEntry(atom.Entry, LinkFinder): - """A Google Webmaster Tools meta Sitemaps Entry flavor of an Atom Entry """ - - _tag = atom.Entry._tag - _namespace = atom.Entry._namespace - _children = atom.Entry._children.copy() - _attributes = atom.Entry._attributes.copy() - _children['{%s}sitemap-type' % GWEBMASTERTOOLS_NAMESPACE] = ( - 'sitemap_type', SitemapType) - _children['{%s}sitemap-status' % GWEBMASTERTOOLS_NAMESPACE] = ( - 'sitemap_status', SitemapStatus) - _children['{%s}sitemap-last-downloaded' % GWEBMASTERTOOLS_NAMESPACE] = ( - 'sitemap_last_downloaded', SitemapLastDownloaded) - _children['{%s}sitemap-url-count' % GWEBMASTERTOOLS_NAMESPACE] = ( - 'sitemap_url_count', SitemapUrlCount) - _children['{%s}sitemap-mobile-markup-language' % GWEBMASTERTOOLS_NAMESPACE] \ - = ('sitemap_mobile_markup_language', SitemapMobileMarkupLanguage) - _children['{%s}sitemap-news-publication-label' % GWEBMASTERTOOLS_NAMESPACE] \ - = ('sitemap_news_publication_label', SitemapNewsPublicationLabel) - - def __GetId(self): - return self.__id - - # This method was created to strip the unwanted whitespace from the id's - # text node. - def __SetId(self, id): - self.__id = id - if id is not None and id.text is not None: - self.__id.text = id.text.strip() - - id = property(__GetId, __SetId) - - def __init__(self, category=None, content=None, - atom_id=None, link=None, title=None, updated=None, - sitemap_type=None, sitemap_status=None, sitemap_last_downloaded=None, - sitemap_url_count=None, sitemap_mobile_markup_language=None, - sitemap_news_publication_label=None, - extension_elements=None, extension_attributes=None, text=None): - atom.Entry.__init__(self, category=category, - content=content, atom_id=atom_id, link=link, - title=title, updated=updated, text=text) - - self.sitemap_type = sitemap_type - self.sitemap_status = sitemap_status - self.sitemap_last_downloaded = sitemap_last_downloaded - self.sitemap_url_count = sitemap_url_count - self.sitemap_mobile_markup_language = sitemap_mobile_markup_language - self.sitemap_news_publication_label = sitemap_news_publication_label - - -def SitemapsEntryFromString(xml_string): - return atom.CreateClassFromXMLString(SitemapsEntry, xml_string) - - -class SitemapsFeed(atom.Feed, LinkFinder): - """A Google Webmaster Tools meta Sitemaps feed flavor of an Atom Feed""" - - _tag = atom.Feed._tag - _namespace = atom.Feed._namespace - _children = atom.Feed._children.copy() - _attributes = atom.Feed._attributes.copy() - _children['{%s}entry' % atom.ATOM_NAMESPACE] = ('entry', [SitemapsEntry]) - _children['{%s}sitemap-mobile' % GWEBMASTERTOOLS_NAMESPACE] = ( - 'sitemap_mobile', SitemapMobile) - _children['{%s}sitemap-news' % GWEBMASTERTOOLS_NAMESPACE] = ( - 'sitemap_news', SitemapNews) - del _children['{%s}generator' % atom.ATOM_NAMESPACE] - del _children['{%s}author' % atom.ATOM_NAMESPACE] - del _children['{%s}contributor' % atom.ATOM_NAMESPACE] - del _children['{%s}logo' % atom.ATOM_NAMESPACE] - del _children['{%s}icon' % atom.ATOM_NAMESPACE] - del _children['{%s}rights' % atom.ATOM_NAMESPACE] - del _children['{%s}subtitle' % atom.ATOM_NAMESPACE] - - def __GetId(self): - return self.__id - - def __SetId(self, id): - self.__id = id - if id is not None and id.text is not None: - self.__id.text = id.text.strip() - - id = property(__GetId, __SetId) - - def __init__(self, category=None, content=None, - atom_id=None, link=None, title=None, updated=None, - entry=None, sitemap_mobile=None, sitemap_news=None, - extension_elements=None, extension_attributes=None, text=None): - - self.category = category or [] - self.id = atom_id - self.link = link or [] - self.title = title - self.updated = updated - self.entry = entry or [] - self.text = text - self.sitemap_mobile = sitemap_mobile - self.sitemap_news = sitemap_news - self.extension_elements = extension_elements or [] - self.extension_attributes = extension_attributes or {} - - -def SitemapsFeedFromString(xml_string): - return atom.CreateClassFromXMLString(SitemapsFeed, xml_string) diff --git a/gdata/webmastertools/data.py b/gdata/webmastertools/data.py deleted file mode 100644 index 8b50a47a89..0000000000 --- a/gdata/webmastertools/data.py +++ /dev/null @@ -1,217 +0,0 @@ -#!/usr/bin/python -# -# Copyright (C) 2009 Google 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. - - -"""Contains the data classes of the Google Webmaster Tools Data API""" - - -__author__ = 'j.s@google.com (Jeff Scudder)' - - -import atom.core -import atom.data -import gdata.data -import gdata.opensearch.data - - -WT_TEMPLATE = '{http://schemas.google.com/webmaster/tools/2007/}%s' - - -class CrawlIssueCrawlType(atom.core.XmlElement): - """Type of crawl of the crawl issue""" - _qname = WT_TEMPLATE % 'crawl-type' - - -class CrawlIssueDateDetected(atom.core.XmlElement): - """Detection date for the issue""" - _qname = WT_TEMPLATE % 'date-detected' - - -class CrawlIssueDetail(atom.core.XmlElement): - """Detail of the crawl issue""" - _qname = WT_TEMPLATE % 'detail' - - -class CrawlIssueIssueType(atom.core.XmlElement): - """Type of crawl issue""" - _qname = WT_TEMPLATE % 'issue-type' - - -class CrawlIssueLinkedFromUrl(atom.core.XmlElement): - """Source URL that links to the issue URL""" - _qname = WT_TEMPLATE % 'linked-from' - - -class CrawlIssueUrl(atom.core.XmlElement): - """URL affected by the crawl issue""" - _qname = WT_TEMPLATE % 'url' - - -class CrawlIssueEntry(gdata.data.GDEntry): - """Describes a crawl issue entry""" - date_detected = CrawlIssueDateDetected - url = CrawlIssueUrl - detail = CrawlIssueDetail - issue_type = CrawlIssueIssueType - crawl_type = CrawlIssueCrawlType - linked_from = [CrawlIssueLinkedFromUrl] - - -class CrawlIssuesFeed(gdata.data.GDFeed): - """Feed of crawl issues for a particular site""" - entry = [CrawlIssueEntry] - - -class Indexed(atom.core.XmlElement): - """Describes the indexing status of a site""" - _qname = WT_TEMPLATE % 'indexed' - - -class Keyword(atom.core.XmlElement): - """A keyword in a site or in a link to a site""" - _qname = WT_TEMPLATE % 'keyword' - source = 'source' - - -class KeywordEntry(gdata.data.GDEntry): - """Describes a keyword entry""" - - -class KeywordsFeed(gdata.data.GDFeed): - """Feed of keywords for a particular site""" - entry = [KeywordEntry] - keyword = [Keyword] - - -class LastCrawled(atom.core.XmlElement): - """Describes the last crawled date of a site""" - _qname = WT_TEMPLATE % 'last-crawled' - - -class MessageBody(atom.core.XmlElement): - """Message body""" - _qname = WT_TEMPLATE % 'body' - - -class MessageDate(atom.core.XmlElement): - """Message date""" - _qname = WT_TEMPLATE % 'date' - - -class MessageLanguage(atom.core.XmlElement): - """Message language""" - _qname = WT_TEMPLATE % 'language' - - -class MessageRead(atom.core.XmlElement): - """Indicates if the message has already been read""" - _qname = WT_TEMPLATE % 'read' - - -class MessageSubject(atom.core.XmlElement): - """Message subject""" - _qname = WT_TEMPLATE % 'subject' - - -class SiteId(atom.core.XmlElement): - """Site URL""" - _qname = WT_TEMPLATE % 'id' - - -class MessageEntry(gdata.data.GDEntry): - """Describes a message entry""" - wt_id = SiteId - subject = MessageSubject - date = MessageDate - body = MessageBody - language = MessageLanguage - read = MessageRead - - -class MessagesFeed(gdata.data.GDFeed): - """Describes a messages feed""" - entry = [MessageEntry] - - -class SitemapEntry(gdata.data.GDEntry): - """Describes a sitemap entry""" - indexed = Indexed - wt_id = SiteId - - -class SitemapMobileMarkupLanguage(atom.core.XmlElement): - """Describes a markup language for URLs in this sitemap""" - _qname = WT_TEMPLATE % 'sitemap-mobile-markup-language' - - -class SitemapMobile(atom.core.XmlElement): - """Lists acceptable mobile markup languages for URLs in this sitemap""" - _qname = WT_TEMPLATE % 'sitemap-mobile' - sitemap_mobile_markup_language = [SitemapMobileMarkupLanguage] - - -class SitemapNewsPublicationLabel(atom.core.XmlElement): - """Specifies the publication label for this sitemap""" - _qname = WT_TEMPLATE % 'sitemap-news-publication-label' - - -class SitemapNews(atom.core.XmlElement): - """Lists publication labels for this sitemap""" - _qname = WT_TEMPLATE % 'sitemap-news' - sitemap_news_publication_label = [SitemapNewsPublicationLabel] - - -class SitemapType(atom.core.XmlElement): - """Indicates the type of sitemap. Not used for News or Mobile Sitemaps""" - _qname = WT_TEMPLATE % 'sitemap-type' - - -class SitemapUrlCount(atom.core.XmlElement): - """Indicates the number of URLs contained in the sitemap""" - _qname = WT_TEMPLATE % 'sitemap-url-count' - - -class SitemapsFeed(gdata.data.GDFeed): - """Describes a sitemaps feed""" - entry = [SitemapEntry] - - -class VerificationMethod(atom.core.XmlElement): - """Describes a verification method that may be used for a site""" - _qname = WT_TEMPLATE % 'verification-method' - in_use = 'in-use' - type = 'type' - - -class Verified(atom.core.XmlElement): - """Describes the verification status of a site""" - _qname = WT_TEMPLATE % 'verified' - - -class SiteEntry(gdata.data.GDEntry): - """Describes a site entry""" - indexed = Indexed - wt_id = SiteId - verified = Verified - last_crawled = LastCrawled - verification_method = [VerificationMethod] - - -class SitesFeed(gdata.data.GDFeed): - """Describes a sites feed""" - entry = [SiteEntry] - - diff --git a/gdata/webmastertools/service.py b/gdata/webmastertools/service.py deleted file mode 100644 index 8c3286db40..0000000000 --- a/gdata/webmastertools/service.py +++ /dev/null @@ -1,516 +0,0 @@ -#!/usr/bin/python -# -# Copyright (C) 2008 Yu-Jie Lin -# -# 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. - -"""GWebmasterToolsService extends the GDataService to streamline -Google Webmaster Tools operations. - - GWebmasterToolsService: Provides methods to query feeds and manipulate items. - Extends GDataService. -""" - -__author__ = 'livibetter (Yu-Jie Lin)' - -import urllib -import gdata -import atom.service -import gdata.service -import gdata.webmastertools as webmastertools -import atom - - -FEED_BASE = 'https://www.google.com/webmasters/tools/feeds/' -SITES_FEED = FEED_BASE + 'sites/' -SITE_TEMPLATE = SITES_FEED + '%s' -SITEMAPS_FEED_TEMPLATE = FEED_BASE + '%(site_id)s/sitemaps/' -SITEMAP_TEMPLATE = SITEMAPS_FEED_TEMPLATE + '%(sitemap_id)s' - - -class Error(Exception): - pass - - -class RequestError(Error): - pass - - -class GWebmasterToolsService(gdata.service.GDataService): - """Client for the Google Webmaster Tools service.""" - - def __init__(self, email=None, password=None, source=None, - server='www.google.com', **kwargs): - """Creates a client for the Google Webmaster Tools 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: 'www.google.com'. - **kwargs: The other parameters to pass to gdata.service.GDataService - constructor. - """ - gdata.service.GDataService.__init__( - self, email=email, password=password, service='sitemaps', source=source, - server=server, **kwargs) - - def GetSitesFeed(self, uri=SITES_FEED, - converter=webmastertools.SitesFeedFromString): - """Gets sites feed. - - Args: - uri: str (optional) URI to retrieve sites feed. - converter: func (optional) Function which is executed on the server's - response before it is returned. Usually this is a function like - SitesFeedFromString which will parse the response and turn it into - an object. - - Returns: - If converter is defined, the results of running converter on the server's - response. Otherwise, it will be a SitesFeed object. - """ - return self.Get(uri, converter=converter) - - def AddSite(self, site_uri, uri=SITES_FEED, - url_params=None, escape_params=True, converter=None): - """Adds a site to Google Webmaster Tools. - - Args: - site_uri: str URI of which site to add. - uri: str (optional) URI to add a site. - url_params: dict (optional) Additional URL parameters to be included - in the insertion request. - escape_params: boolean (optional) If true, the url_parameters will be - escaped before they are included in the request. - converter: func (optional) Function which is executed on the server's - response before it is returned. Usually this is a function like - SitesEntryFromString which will parse the response and turn it into - an object. - - Returns: - If converter is defined, the results of running converter on the server's - response. Otherwise, it will be a SitesEntry object. - """ - - site_entry = webmastertools.SitesEntry() - site_entry.content = atom.Content(src=site_uri) - response = self.Post(site_entry, uri, - url_params=url_params, - escape_params=escape_params, converter=converter) - if not converter and isinstance(response, atom.Entry): - return webmastertools.SitesEntryFromString(response.ToString()) - return response - - def DeleteSite(self, site_uri, uri=SITE_TEMPLATE, - url_params=None, escape_params=True): - """Removes a site from Google Webmaster Tools. - - Args: - site_uri: str URI of which site to remove. - uri: str (optional) A URI template to send DELETE request. - Default SITE_TEMPLATE. - url_params: dict (optional) Additional URL parameters to be included - in the insertion request. - escape_params: boolean (optional) If true, the url_parameters will be - escaped before they are included in the request. - - Returns: - True if the delete succeeded. - """ - - return self.Delete( - uri % urllib.quote_plus(site_uri), - url_params=url_params, escape_params=escape_params) - - def VerifySite(self, site_uri, verification_method, uri=SITE_TEMPLATE, - url_params=None, escape_params=True, converter=None): - """Requests a verification of a site. - - Args: - site_uri: str URI of which site to add sitemap for. - verification_method: str The method to verify a site. Valid values are - 'htmlpage', and 'metatag'. - uri: str (optional) URI template to update a site. - Default SITE_TEMPLATE. - url_params: dict (optional) Additional URL parameters to be included - in the insertion request. - escape_params: boolean (optional) If true, the url_parameters will be - escaped before they are included in the request. - converter: func (optional) Function which is executed on the server's - response before it is returned. Usually this is a function like - SitemapsEntryFromString which will parse the response and turn it into - an object. - - Returns: - If converter is defined, the results of running converter on the server's - response. Otherwise, it will be a SitesEntry object. - """ - - site_entry = webmastertools.SitesEntry( - atom_id=atom.Id(text=site_uri), - category=atom.Category( - scheme='http://schemas.google.com/g/2005#kind', - term='http://schemas.google.com/webmasters/tools/2007#sites-info'), - verification_method=webmastertools.VerificationMethod( - type=verification_method, in_use='true') - ) - response = self.Put( - site_entry, - uri % urllib.quote_plus(site_uri), - url_params=url_params, - escape_params=escape_params, converter=converter) - if not converter and isinstance(response, atom.Entry): - return webmastertools.SitesEntryFromString(response.ToString()) - return response - - - def UpdateGeoLocation(self, site_uri, geolocation, uri=SITE_TEMPLATE, - url_params=None, escape_params=True, converter=None): - """Updates geolocation setting of a site. - - Args: - site_uri: str URI of which site to add sitemap for. - geolocation: str The geographic location. Valid values are listed in - http://en.wikipedia.org/wiki/ISO_3166-1_alpha-2 - uri: str (optional) URI template to update a site. - Default SITE_TEMPLATE. - url_params: dict (optional) Additional URL parameters to be included - in the insertion request. - escape_params: boolean (optional) If true, the url_parameters will be - escaped before they are included in the request. - converter: func (optional) Function which is executed on the server's - response before it is returned. Usually this is a function like - SitemapsEntryFromString which will parse the response and turn it into - an object. - - Returns: - If converter is defined, the results of running converter on the server's - response. Otherwise, it will be a SitesEntry object. - """ - - site_entry = webmastertools.SitesEntry( - atom_id=atom.Id(text=site_uri), - category=atom.Category( - scheme='http://schemas.google.com/g/2005#kind', - term='http://schemas.google.com/webmasters/tools/2007#sites-info'), - geolocation=webmastertools.GeoLocation(text=geolocation) - ) - response = self.Put( - site_entry, - uri % urllib.quote_plus(site_uri), - url_params=url_params, - escape_params=escape_params, converter=converter) - if not converter and isinstance(response, atom.Entry): - return webmastertools.SitesEntryFromString(response.ToString()) - return response - - def UpdateCrawlRate(self, site_uri, crawl_rate, uri=SITE_TEMPLATE, - url_params=None, escape_params=True, converter=None): - """Updates crawl rate setting of a site. - - Args: - site_uri: str URI of which site to add sitemap for. - crawl_rate: str The crawl rate for a site. Valid values are 'slower', - 'normal', and 'faster'. - uri: str (optional) URI template to update a site. - Default SITE_TEMPLATE. - url_params: dict (optional) Additional URL parameters to be included - in the insertion request. - escape_params: boolean (optional) If true, the url_parameters will be - escaped before they are included in the request. - converter: func (optional) Function which is executed on the server's - response before it is returned. Usually this is a function like - SitemapsEntryFromString which will parse the response and turn it into - an object. - - Returns: - If converter is defined, the results of running converter on the server's - response. Otherwise, it will be a SitesEntry object. - """ - - site_entry = webmastertools.SitesEntry( - atom_id=atom.Id(text=site_uri), - category=atom.Category( - scheme='http://schemas.google.com/g/2005#kind', - term='http://schemas.google.com/webmasters/tools/2007#sites-info'), - crawl_rate=webmastertools.CrawlRate(text=crawl_rate) - ) - response = self.Put( - site_entry, - uri % urllib.quote_plus(site_uri), - url_params=url_params, - escape_params=escape_params, converter=converter) - if not converter and isinstance(response, atom.Entry): - return webmastertools.SitesEntryFromString(response.ToString()) - return response - - def UpdatePreferredDomain(self, site_uri, preferred_domain, uri=SITE_TEMPLATE, - url_params=None, escape_params=True, converter=None): - """Updates preferred domain setting of a site. - - Note that if using 'preferwww', will also need www.example.com in account to - take effect. - - Args: - site_uri: str URI of which site to add sitemap for. - preferred_domain: str The preferred domain for a site. Valid values are 'none', - 'preferwww', and 'prefernowww'. - uri: str (optional) URI template to update a site. - Default SITE_TEMPLATE. - url_params: dict (optional) Additional URL parameters to be included - in the insertion request. - escape_params: boolean (optional) If true, the url_parameters will be - escaped before they are included in the request. - converter: func (optional) Function which is executed on the server's - response before it is returned. Usually this is a function like - SitemapsEntryFromString which will parse the response and turn it into - an object. - - Returns: - If converter is defined, the results of running converter on the server's - response. Otherwise, it will be a SitesEntry object. - """ - - site_entry = webmastertools.SitesEntry( - atom_id=atom.Id(text=site_uri), - category=atom.Category( - scheme='http://schemas.google.com/g/2005#kind', - term='http://schemas.google.com/webmasters/tools/2007#sites-info'), - preferred_domain=webmastertools.PreferredDomain(text=preferred_domain) - ) - response = self.Put( - site_entry, - uri % urllib.quote_plus(site_uri), - url_params=url_params, - escape_params=escape_params, converter=converter) - if not converter and isinstance(response, atom.Entry): - return webmastertools.SitesEntryFromString(response.ToString()) - return response - - def UpdateEnhancedImageSearch(self, site_uri, enhanced_image_search, - uri=SITE_TEMPLATE, url_params=None, escape_params=True, converter=None): - """Updates enhanced image search setting of a site. - - Args: - site_uri: str URI of which site to add sitemap for. - enhanced_image_search: str The enhanced image search setting for a site. - Valid values are 'true', and 'false'. - uri: str (optional) URI template to update a site. - Default SITE_TEMPLATE. - url_params: dict (optional) Additional URL parameters to be included - in the insertion request. - escape_params: boolean (optional) If true, the url_parameters will be - escaped before they are included in the request. - converter: func (optional) Function which is executed on the server's - response before it is returned. Usually this is a function like - SitemapsEntryFromString which will parse the response and turn it into - an object. - - Returns: - If converter is defined, the results of running converter on the server's - response. Otherwise, it will be a SitesEntry object. - """ - - site_entry = webmastertools.SitesEntry( - atom_id=atom.Id(text=site_uri), - category=atom.Category( - scheme='http://schemas.google.com/g/2005#kind', - term='http://schemas.google.com/webmasters/tools/2007#sites-info'), - enhanced_image_search=webmastertools.EnhancedImageSearch( - text=enhanced_image_search) - ) - response = self.Put( - site_entry, - uri % urllib.quote_plus(site_uri), - url_params=url_params, - escape_params=escape_params, converter=converter) - if not converter and isinstance(response, atom.Entry): - return webmastertools.SitesEntryFromString(response.ToString()) - return response - - def GetSitemapsFeed(self, site_uri, uri=SITEMAPS_FEED_TEMPLATE, - converter=webmastertools.SitemapsFeedFromString): - """Gets sitemaps feed of a site. - - Args: - site_uri: str (optional) URI of which site to retrieve its sitemaps feed. - uri: str (optional) URI to retrieve sites feed. - converter: func (optional) Function which is executed on the server's - response before it is returned. Usually this is a function like - SitemapsFeedFromString which will parse the response and turn it into - an object. - - Returns: - If converter is defined, the results of running converter on the server's - response. Otherwise, it will be a SitemapsFeed object. - """ - return self.Get(uri % {'site_id': urllib.quote_plus(site_uri)}, - converter=converter) - - def AddSitemap(self, site_uri, sitemap_uri, sitemap_type='WEB', - uri=SITEMAPS_FEED_TEMPLATE, - url_params=None, escape_params=True, converter=None): - """Adds a regular sitemap to a site. - - Args: - site_uri: str URI of which site to add sitemap for. - sitemap_uri: str URI of sitemap to add to a site. - sitemap_type: str Type of added sitemap. Valid types: WEB, VIDEO, or CODE. - uri: str (optional) URI template to add a sitemap. - Default SITEMAP_FEED_TEMPLATE. - url_params: dict (optional) Additional URL parameters to be included - in the insertion request. - escape_params: boolean (optional) If true, the url_parameters will be - escaped before they are included in the request. - converter: func (optional) Function which is executed on the server's - response before it is returned. Usually this is a function like - SitemapsEntryFromString which will parse the response and turn it into - an object. - - Returns: - If converter is defined, the results of running converter on the server's - response. Otherwise, it will be a SitemapsEntry object. - """ - - sitemap_entry = webmastertools.SitemapsEntry( - atom_id=atom.Id(text=sitemap_uri), - category=atom.Category( - scheme='http://schemas.google.com/g/2005#kind', - term='http://schemas.google.com/webmasters/tools/2007#sitemap-regular'), - sitemap_type=webmastertools.SitemapType(text=sitemap_type)) - response = self.Post( - sitemap_entry, - uri % {'site_id': urllib.quote_plus(site_uri)}, - url_params=url_params, - escape_params=escape_params, converter=converter) - if not converter and isinstance(response, atom.Entry): - return webmastertools.SitemapsEntryFromString(response.ToString()) - return response - - def AddMobileSitemap(self, site_uri, sitemap_uri, - sitemap_mobile_markup_language='XHTML', uri=SITEMAPS_FEED_TEMPLATE, - url_params=None, escape_params=True, converter=None): - """Adds a mobile sitemap to a site. - - Args: - site_uri: str URI of which site to add sitemap for. - sitemap_uri: str URI of sitemap to add to a site. - sitemap_mobile_markup_language: str Format of added sitemap. Valid types: - XHTML, WML, or cHTML. - uri: str (optional) URI template to add a sitemap. - Default SITEMAP_FEED_TEMPLATE. - url_params: dict (optional) Additional URL parameters to be included - in the insertion request. - escape_params: boolean (optional) If true, the url_parameters will be - escaped before they are included in the request. - converter: func (optional) Function which is executed on the server's - response before it is returned. Usually this is a function like - SitemapsEntryFromString which will parse the response and turn it into - an object. - - Returns: - If converter is defined, the results of running converter on the server's - response. Otherwise, it will be a SitemapsEntry object. - """ - # FIXME - sitemap_entry = webmastertools.SitemapsEntry( - atom_id=atom.Id(text=sitemap_uri), - category=atom.Category( - scheme='http://schemas.google.com/g/2005#kind', - term='http://schemas.google.com/webmasters/tools/2007#sitemap-mobile'), - sitemap_mobile_markup_language=\ - webmastertools.SitemapMobileMarkupLanguage( - text=sitemap_mobile_markup_language)) - print sitemap_entry - response = self.Post( - sitemap_entry, - uri % {'site_id': urllib.quote_plus(site_uri)}, - url_params=url_params, - escape_params=escape_params, converter=converter) - if not converter and isinstance(response, atom.Entry): - return webmastertools.SitemapsEntryFromString(response.ToString()) - return response - - def AddNewsSitemap(self, site_uri, sitemap_uri, - sitemap_news_publication_label, uri=SITEMAPS_FEED_TEMPLATE, - url_params=None, escape_params=True, converter=None): - """Adds a news sitemap to a site. - - Args: - site_uri: str URI of which site to add sitemap for. - sitemap_uri: str URI of sitemap to add to a site. - sitemap_news_publication_label: str, list of str Publication Labels for - sitemap. - uri: str (optional) URI template to add a sitemap. - Default SITEMAP_FEED_TEMPLATE. - url_params: dict (optional) Additional URL parameters to be included - in the insertion request. - escape_params: boolean (optional) If true, the url_parameters will be - escaped before they are included in the request. - converter: func (optional) Function which is executed on the server's - response before it is returned. Usually this is a function like - SitemapsEntryFromString which will parse the response and turn it into - an object. - - Returns: - If converter is defined, the results of running converter on the server's - response. Otherwise, it will be a SitemapsEntry object. - """ - - sitemap_entry = webmastertools.SitemapsEntry( - atom_id=atom.Id(text=sitemap_uri), - category=atom.Category( - scheme='http://schemas.google.com/g/2005#kind', - term='http://schemas.google.com/webmasters/tools/2007#sitemap-news'), - sitemap_news_publication_label=[], - ) - if isinstance(sitemap_news_publication_label, str): - sitemap_news_publication_label = [sitemap_news_publication_label] - for label in sitemap_news_publication_label: - sitemap_entry.sitemap_news_publication_label.append( - webmastertools.SitemapNewsPublicationLabel(text=label)) - print sitemap_entry - response = self.Post( - sitemap_entry, - uri % {'site_id': urllib.quote_plus(site_uri)}, - url_params=url_params, - escape_params=escape_params, converter=converter) - if not converter and isinstance(response, atom.Entry): - return webmastertools.SitemapsEntryFromString(response.ToString()) - return response - - def DeleteSitemap(self, site_uri, sitemap_uri, uri=SITEMAP_TEMPLATE, - url_params=None, escape_params=True): - """Removes a sitemap from a site. - - Args: - site_uri: str URI of which site to remove a sitemap from. - sitemap_uri: str URI of sitemap to remove from a site. - uri: str (optional) A URI template to send DELETE request. - Default SITEMAP_TEMPLATE. - url_params: dict (optional) Additional URL parameters to be included - in the insertion request. - escape_params: boolean (optional) If true, the url_parameters will be - escaped before they are included in the request. - - Returns: - True if the delete succeeded. - """ - - return self.Delete( - uri % {'site_id': urllib.quote_plus(site_uri), - 'sitemap_id': urllib.quote_plus(sitemap_uri)}, - url_params=url_params, escape_params=escape_params) diff --git a/gdata/youtube/__init__.py b/gdata/youtube/__init__.py deleted file mode 100644 index c41aaea528..0000000000 --- a/gdata/youtube/__init__.py +++ /dev/null @@ -1,684 +0,0 @@ -#!/usr/bin/python -# -# Copyright (C) 2008 Google 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. - -__author__ = ('api.stephaniel@gmail.com (Stephanie Liu)' - ', api.jhartmann@gmail.com (Jochen Hartmann)') - -import atom -import gdata -import gdata.media as Media -import gdata.geo as Geo - -YOUTUBE_NAMESPACE = 'http://gdata.youtube.com/schemas/2007' -YOUTUBE_FORMAT = '{http://gdata.youtube.com/schemas/2007}format' -YOUTUBE_DEVELOPER_TAG_SCHEME = '%s/%s' % (YOUTUBE_NAMESPACE, - 'developertags.cat') -YOUTUBE_SUBSCRIPTION_TYPE_SCHEME = '%s/%s' % (YOUTUBE_NAMESPACE, - 'subscriptiontypes.cat') - -class Username(atom.AtomBase): - """The YouTube Username element""" - _tag = 'username' - _namespace = YOUTUBE_NAMESPACE - -class QueryString(atom.AtomBase): - """The YouTube QueryString element""" - _tag = 'queryString' - _namespace = YOUTUBE_NAMESPACE - - -class FirstName(atom.AtomBase): - """The YouTube FirstName element""" - _tag = 'firstName' - _namespace = YOUTUBE_NAMESPACE - - -class LastName(atom.AtomBase): - """The YouTube LastName element""" - _tag = 'lastName' - _namespace = YOUTUBE_NAMESPACE - - -class Age(atom.AtomBase): - """The YouTube Age element""" - _tag = 'age' - _namespace = YOUTUBE_NAMESPACE - - -class Books(atom.AtomBase): - """The YouTube Books element""" - _tag = 'books' - _namespace = YOUTUBE_NAMESPACE - - -class Gender(atom.AtomBase): - """The YouTube Gender element""" - _tag = 'gender' - _namespace = YOUTUBE_NAMESPACE - - -class Company(atom.AtomBase): - """The YouTube Company element""" - _tag = 'company' - _namespace = YOUTUBE_NAMESPACE - - -class Hobbies(atom.AtomBase): - """The YouTube Hobbies element""" - _tag = 'hobbies' - _namespace = YOUTUBE_NAMESPACE - - -class Hometown(atom.AtomBase): - """The YouTube Hometown element""" - _tag = 'hometown' - _namespace = YOUTUBE_NAMESPACE - - -class Location(atom.AtomBase): - """The YouTube Location element""" - _tag = 'location' - _namespace = YOUTUBE_NAMESPACE - - -class Movies(atom.AtomBase): - """The YouTube Movies element""" - _tag = 'movies' - _namespace = YOUTUBE_NAMESPACE - - -class Music(atom.AtomBase): - """The YouTube Music element""" - _tag = 'music' - _namespace = YOUTUBE_NAMESPACE - - -class Occupation(atom.AtomBase): - """The YouTube Occupation element""" - _tag = 'occupation' - _namespace = YOUTUBE_NAMESPACE - - -class School(atom.AtomBase): - """The YouTube School element""" - _tag = 'school' - _namespace = YOUTUBE_NAMESPACE - - -class Relationship(atom.AtomBase): - """The YouTube Relationship element""" - _tag = 'relationship' - _namespace = YOUTUBE_NAMESPACE - - -class Recorded(atom.AtomBase): - """The YouTube Recorded element""" - _tag = 'recorded' - _namespace = YOUTUBE_NAMESPACE - - -class Statistics(atom.AtomBase): - """The YouTube Statistics element.""" - _tag = 'statistics' - _namespace = YOUTUBE_NAMESPACE - _attributes = atom.AtomBase._attributes.copy() - _attributes['viewCount'] = 'view_count' - _attributes['videoWatchCount'] = 'video_watch_count' - _attributes['subscriberCount'] = 'subscriber_count' - _attributes['lastWebAccess'] = 'last_web_access' - _attributes['favoriteCount'] = 'favorite_count' - - def __init__(self, view_count=None, video_watch_count=None, - favorite_count=None, subscriber_count=None, last_web_access=None, - extension_elements=None, extension_attributes=None, text=None): - - self.view_count = view_count - self.video_watch_count = video_watch_count - self.subscriber_count = subscriber_count - self.last_web_access = last_web_access - self.favorite_count = favorite_count - - atom.AtomBase.__init__(self, extension_elements=extension_elements, - extension_attributes=extension_attributes, text=text) - - -class Status(atom.AtomBase): - """The YouTube Status element""" - _tag = 'status' - _namespace = YOUTUBE_NAMESPACE - - -class Position(atom.AtomBase): - """The YouTube Position element. The position in a playlist feed.""" - _tag = 'position' - _namespace = YOUTUBE_NAMESPACE - - -class Racy(atom.AtomBase): - """The YouTube Racy element.""" - _tag = 'racy' - _namespace = YOUTUBE_NAMESPACE - -class Description(atom.AtomBase): - """The YouTube Description element.""" - _tag = 'description' - _namespace = YOUTUBE_NAMESPACE - - -class Private(atom.AtomBase): - """The YouTube Private element.""" - _tag = 'private' - _namespace = YOUTUBE_NAMESPACE - - -class NoEmbed(atom.AtomBase): - """The YouTube VideoShare element. Whether a video can be embedded or not.""" - _tag = 'noembed' - _namespace = YOUTUBE_NAMESPACE - - -class Comments(atom.AtomBase): - """The GData Comments element""" - _tag = 'comments' - _namespace = gdata.GDATA_NAMESPACE - _children = atom.AtomBase._children.copy() - _attributes = atom.AtomBase._attributes.copy() - _children['{%s}feedLink' % gdata.GDATA_NAMESPACE] = ('feed_link', - [gdata.FeedLink]) - - def __init__(self, feed_link=None, extension_elements=None, - extension_attributes=None, text=None): - - self.feed_link = feed_link - atom.AtomBase.__init__(self, extension_elements=extension_elements, - extension_attributes=extension_attributes, text=text) - - -class Rating(atom.AtomBase): - """The GData Rating element""" - _tag = 'rating' - _namespace = gdata.GDATA_NAMESPACE - _attributes = atom.AtomBase._attributes.copy() - _attributes['min'] = 'min' - _attributes['max'] = 'max' - _attributes['numRaters'] = 'num_raters' - _attributes['average'] = 'average' - - def __init__(self, min=None, max=None, - num_raters=None, average=None, extension_elements=None, - extension_attributes=None, text=None): - - self.min = min - self.max = max - self.num_raters = num_raters - self.average = average - - atom.AtomBase.__init__(self, extension_elements=extension_elements, - extension_attributes=extension_attributes, text=text) - - -class YouTubePlaylistVideoEntry(gdata.GDataEntry): - """Represents a YouTubeVideoEntry on a YouTubePlaylist.""" - _tag = gdata.GDataEntry._tag - _namespace = gdata.GDataEntry._namespace - _children = gdata.GDataEntry._children.copy() - _attributes = gdata.GDataEntry._attributes.copy() - _children['{%s}feedLink' % gdata.GDATA_NAMESPACE] = ('feed_link', - [gdata.FeedLink]) - _children['{%s}description' % YOUTUBE_NAMESPACE] = ('description', - Description) - _children['{%s}rating' % gdata.GDATA_NAMESPACE] = ('rating', Rating) - _children['{%s}comments' % gdata.GDATA_NAMESPACE] = ('comments', Comments) - _children['{%s}statistics' % YOUTUBE_NAMESPACE] = ('statistics', Statistics) - _children['{%s}location' % YOUTUBE_NAMESPACE] = ('location', Location) - _children['{%s}position' % YOUTUBE_NAMESPACE] = ('position', Position) - _children['{%s}group' % gdata.media.MEDIA_NAMESPACE] = ('media', Media.Group) - - def __init__(self, author=None, category=None, content=None, - atom_id=None, link=None, published=None, title=None, - updated=None, feed_link=None, description=None, - rating=None, comments=None, statistics=None, - location=None, position=None, media=None, - extension_elements=None, extension_attributes=None): - - self.feed_link = feed_link - self.description = description - self.rating = rating - self.comments = comments - self.statistics = statistics - self.location = location - self.position = position - self.media = media - - gdata.GDataEntry.__init__(self, author=author, category=category, - content=content, atom_id=atom_id, - link=link, published=published, title=title, - updated=updated, - extension_elements=extension_elements, - extension_attributes=extension_attributes) - - -class YouTubeVideoCommentEntry(gdata.GDataEntry): - """Represents a comment on YouTube.""" - _tag = gdata.GDataEntry._tag - _namespace = gdata.GDataEntry._namespace - _children = gdata.GDataEntry._children.copy() - _attributes = gdata.GDataEntry._attributes.copy() - - -class YouTubeSubscriptionEntry(gdata.GDataEntry): - """Represents a subscription entry on YouTube.""" - _tag = gdata.GDataEntry._tag - _namespace = gdata.GDataEntry._namespace - _children = gdata.GDataEntry._children.copy() - _attributes = gdata.GDataEntry._attributes.copy() - _children['{%s}username' % YOUTUBE_NAMESPACE] = ('username', Username) - _children['{%s}queryString' % YOUTUBE_NAMESPACE] = ( - 'query_string', QueryString) - _children['{%s}feedLink' % gdata.GDATA_NAMESPACE] = ('feed_link', - [gdata.FeedLink]) - - def __init__(self, author=None, category=None, content=None, - atom_id=None, link=None, published=None, title=None, - updated=None, username=None, query_string=None, feed_link=None, - extension_elements=None, extension_attributes=None): - - gdata.GDataEntry.__init__(self, author=author, category=category, - content=content, atom_id=atom_id, link=link, - published=published, title=title, updated=updated) - - self.username = username - self.query_string = query_string - self.feed_link = feed_link - - - def GetSubscriptionType(self): - """Retrieve the type of this subscription. - - Returns: - A string that is either 'channel, 'query' or 'favorites' - """ - for category in self.category: - if category.scheme == YOUTUBE_SUBSCRIPTION_TYPE_SCHEME: - return category.term - - -class YouTubeVideoResponseEntry(gdata.GDataEntry): - """Represents a video response. """ - _tag = gdata.GDataEntry._tag - _namespace = gdata.GDataEntry._namespace - _children = gdata.GDataEntry._children.copy() - _attributes = gdata.GDataEntry._attributes.copy() - _children['{%s}rating' % gdata.GDATA_NAMESPACE] = ('rating', Rating) - _children['{%s}noembed' % YOUTUBE_NAMESPACE] = ('noembed', NoEmbed) - _children['{%s}statistics' % YOUTUBE_NAMESPACE] = ('statistics', Statistics) - _children['{%s}racy' % YOUTUBE_NAMESPACE] = ('racy', Racy) - _children['{%s}group' % gdata.media.MEDIA_NAMESPACE] = ('media', Media.Group) - - def __init__(self, author=None, category=None, content=None, atom_id=None, - link=None, published=None, title=None, updated=None, rating=None, - noembed=None, statistics=None, racy=None, media=None, - extension_elements=None, extension_attributes=None): - - gdata.GDataEntry.__init__(self, author=author, category=category, - content=content, atom_id=atom_id, link=link, - published=published, title=title, updated=updated) - - self.rating = rating - self.noembed = noembed - self.statistics = statistics - self.racy = racy - self.media = media or Media.Group() - - -class YouTubeContactEntry(gdata.GDataEntry): - """Represents a contact entry.""" - _tag = gdata.GDataEntry._tag - _namespace = gdata.GDataEntry._namespace - _children = gdata.GDataEntry._children.copy() - _attributes = gdata.GDataEntry._attributes.copy() - _children['{%s}username' % YOUTUBE_NAMESPACE] = ('username', Username) - _children['{%s}status' % YOUTUBE_NAMESPACE] = ('status', Status) - - - def __init__(self, author=None, category=None, content=None, atom_id=None, - link=None, published=None, title=None, updated=None, - username=None, status=None, extension_elements=None, - extension_attributes=None, text=None): - - gdata.GDataEntry.__init__(self, author=author, category=category, - content=content, atom_id=atom_id, link=link, - published=published, title=title, updated=updated) - - self.username = username - self.status = status - - -class YouTubeVideoEntry(gdata.GDataEntry): - """Represents a video on YouTube.""" - _tag = gdata.GDataEntry._tag - _namespace = gdata.GDataEntry._namespace - _children = gdata.GDataEntry._children.copy() - _attributes = gdata.GDataEntry._attributes.copy() - _children['{%s}rating' % gdata.GDATA_NAMESPACE] = ('rating', Rating) - _children['{%s}comments' % gdata.GDATA_NAMESPACE] = ('comments', Comments) - _children['{%s}noembed' % YOUTUBE_NAMESPACE] = ('noembed', NoEmbed) - _children['{%s}statistics' % YOUTUBE_NAMESPACE] = ('statistics', Statistics) - _children['{%s}recorded' % YOUTUBE_NAMESPACE] = ('recorded', Recorded) - _children['{%s}racy' % YOUTUBE_NAMESPACE] = ('racy', Racy) - _children['{%s}group' % gdata.media.MEDIA_NAMESPACE] = ('media', Media.Group) - _children['{%s}where' % gdata.geo.GEORSS_NAMESPACE] = ('geo', Geo.Where) - - def __init__(self, author=None, category=None, content=None, atom_id=None, - link=None, published=None, title=None, updated=None, rating=None, - noembed=None, statistics=None, racy=None, media=None, geo=None, - recorded=None, comments=None, extension_elements=None, - extension_attributes=None): - - self.rating = rating - self.noembed = noembed - self.statistics = statistics - self.racy = racy - self.comments = comments - self.media = media or Media.Group() - self.geo = geo - self.recorded = recorded - - gdata.GDataEntry.__init__(self, author=author, category=category, - content=content, atom_id=atom_id, link=link, - published=published, title=title, updated=updated, - extension_elements=extension_elements, - extension_attributes=extension_attributes) - - def GetSwfUrl(self): - """Return the URL for the embeddable Video - - Returns: - URL of the embeddable video - """ - if self.media.content: - for content in self.media.content: - if content.extension_attributes[YOUTUBE_FORMAT] == '5': - return content.url - else: - return None - - def AddDeveloperTags(self, developer_tags): - """Add a developer tag for this entry. - - Developer tags can only be set during the initial upload. - - Arguments: - developer_tags: A list of developer tags as strings. - - Returns: - A list of all developer tags for this video entry. - """ - for tag_text in developer_tags: - self.media.category.append(gdata.media.Category( - text=tag_text, label=tag_text, scheme=YOUTUBE_DEVELOPER_TAG_SCHEME)) - - return self.GetDeveloperTags() - - def GetDeveloperTags(self): - """Retrieve developer tags for this video entry.""" - developer_tags = [] - for category in self.media.category: - if category.scheme == YOUTUBE_DEVELOPER_TAG_SCHEME: - developer_tags.append(category) - if len(developer_tags) > 0: - return developer_tags - - def GetYouTubeCategoryAsString(self): - """Convenience method to return the YouTube category as string. - - YouTubeVideoEntries can contain multiple Category objects with differing - schemes. This method returns only the category with the correct - scheme, ignoring developer tags. - """ - for category in self.media.category: - if category.scheme != YOUTUBE_DEVELOPER_TAG_SCHEME: - return category.text - -class YouTubeUserEntry(gdata.GDataEntry): - """Represents a user on YouTube.""" - _tag = gdata.GDataEntry._tag - _namespace = gdata.GDataEntry._namespace - _children = gdata.GDataEntry._children.copy() - _attributes = gdata.GDataEntry._attributes.copy() - _children['{%s}username' % YOUTUBE_NAMESPACE] = ('username', Username) - _children['{%s}firstName' % YOUTUBE_NAMESPACE] = ('first_name', FirstName) - _children['{%s}lastName' % YOUTUBE_NAMESPACE] = ('last_name', LastName) - _children['{%s}age' % YOUTUBE_NAMESPACE] = ('age', Age) - _children['{%s}books' % YOUTUBE_NAMESPACE] = ('books', Books) - _children['{%s}gender' % YOUTUBE_NAMESPACE] = ('gender', Gender) - _children['{%s}company' % YOUTUBE_NAMESPACE] = ('company', Company) - _children['{%s}description' % YOUTUBE_NAMESPACE] = ('description', - Description) - _children['{%s}hobbies' % YOUTUBE_NAMESPACE] = ('hobbies', Hobbies) - _children['{%s}hometown' % YOUTUBE_NAMESPACE] = ('hometown', Hometown) - _children['{%s}location' % YOUTUBE_NAMESPACE] = ('location', Location) - _children['{%s}movies' % YOUTUBE_NAMESPACE] = ('movies', Movies) - _children['{%s}music' % YOUTUBE_NAMESPACE] = ('music', Music) - _children['{%s}occupation' % YOUTUBE_NAMESPACE] = ('occupation', Occupation) - _children['{%s}school' % YOUTUBE_NAMESPACE] = ('school', School) - _children['{%s}relationship' % YOUTUBE_NAMESPACE] = ('relationship', - Relationship) - _children['{%s}statistics' % YOUTUBE_NAMESPACE] = ('statistics', Statistics) - _children['{%s}feedLink' % gdata.GDATA_NAMESPACE] = ('feed_link', - [gdata.FeedLink]) - _children['{%s}thumbnail' % gdata.media.MEDIA_NAMESPACE] = ('thumbnail', - Media.Thumbnail) - - def __init__(self, author=None, category=None, content=None, atom_id=None, - link=None, published=None, title=None, updated=None, - username=None, first_name=None, last_name=None, age=None, - books=None, gender=None, company=None, description=None, - hobbies=None, hometown=None, location=None, movies=None, - music=None, occupation=None, school=None, relationship=None, - statistics=None, feed_link=None, extension_elements=None, - extension_attributes=None, text=None): - - self.username = username - self.first_name = first_name - self.last_name = last_name - self.age = age - self.books = books - self.gender = gender - self.company = company - self.description = description - self.hobbies = hobbies - self.hometown = hometown - self.location = location - self.movies = movies - self.music = music - self.occupation = occupation - self.school = school - self.relationship = relationship - self.statistics = statistics - self.feed_link = feed_link - - gdata.GDataEntry.__init__(self, author=author, category=category, - content=content, atom_id=atom_id, - link=link, published=published, - title=title, updated=updated, - extension_elements=extension_elements, - extension_attributes=extension_attributes, - text=text) - - -class YouTubeVideoFeed(gdata.GDataFeed, gdata.LinkFinder): - """Represents a video feed on YouTube.""" - _tag = gdata.GDataFeed._tag - _namespace = gdata.GDataFeed._namespace - _children = gdata.GDataFeed._children.copy() - _attributes = gdata.GDataFeed._attributes.copy() - _children['{%s}entry' % atom.ATOM_NAMESPACE] = ('entry', [YouTubeVideoEntry]) - -class YouTubePlaylistEntry(gdata.GDataEntry): - """Represents a playlist in YouTube.""" - _tag = gdata.GDataEntry._tag - _namespace = gdata.GDataEntry._namespace - _children = gdata.GDataEntry._children.copy() - _attributes = gdata.GDataEntry._attributes.copy() - _children['{%s}description' % YOUTUBE_NAMESPACE] = ('description', - Description) - _children['{%s}private' % YOUTUBE_NAMESPACE] = ('private', - Private) - _children['{%s}feedLink' % gdata.GDATA_NAMESPACE] = ('feed_link', - [gdata.FeedLink]) - - def __init__(self, author=None, category=None, content=None, - atom_id=None, link=None, published=None, title=None, - updated=None, private=None, feed_link=None, - description=None, extension_elements=None, - extension_attributes=None): - - self.description = description - self.private = private - self.feed_link = feed_link - - gdata.GDataEntry.__init__(self, author=author, category=category, - content=content, atom_id=atom_id, - link=link, published=published, title=title, - updated=updated, - extension_elements=extension_elements, - extension_attributes=extension_attributes) - - - -class YouTubePlaylistFeed(gdata.GDataFeed, gdata.LinkFinder): - """Represents a feed of a user's playlists """ - _tag = gdata.GDataFeed._tag - _namespace = gdata.GDataFeed._namespace - _children = gdata.GDataFeed._children.copy() - _attributes = gdata.GDataFeed._attributes.copy() - _children['{%s}entry' % atom.ATOM_NAMESPACE] = ('entry', - [YouTubePlaylistEntry]) - - -class YouTubePlaylistVideoFeed(gdata.GDataFeed, gdata.LinkFinder): - """Represents a feed of video entry on a playlist.""" - _tag = gdata.GDataFeed._tag - _namespace = gdata.GDataFeed._namespace - _children = gdata.GDataFeed._children.copy() - _attributes = gdata.GDataFeed._attributes.copy() - _children['{%s}entry' % atom.ATOM_NAMESPACE] = ('entry', - [YouTubePlaylistVideoEntry]) - - -class YouTubeContactFeed(gdata.GDataFeed, gdata.LinkFinder): - """Represents a feed of a users contacts.""" - _tag = gdata.GDataFeed._tag - _namespace = gdata.GDataFeed._namespace - _children = gdata.GDataFeed._children.copy() - _attributes = gdata.GDataFeed._attributes.copy() - _children['{%s}entry' % atom.ATOM_NAMESPACE] = ('entry', - [YouTubeContactEntry]) - - -class YouTubeSubscriptionFeed(gdata.GDataFeed, gdata.LinkFinder): - """Represents a feed of a users subscriptions.""" - _tag = gdata.GDataFeed._tag - _namespace = gdata.GDataFeed._namespace - _children = gdata.GDataFeed._children.copy() - _attributes = gdata.GDataFeed._attributes.copy() - _children['{%s}entry' % atom.ATOM_NAMESPACE] = ('entry', - [YouTubeSubscriptionEntry]) - - -class YouTubeVideoCommentFeed(gdata.GDataFeed, gdata.LinkFinder): - """Represents a feed of comments for a video.""" - _tag = gdata.GDataFeed._tag - _namespace = gdata.GDataFeed._namespace - _children = gdata.GDataFeed._children.copy() - _attributes = gdata.GDataFeed._attributes.copy() - _children['{%s}entry' % atom.ATOM_NAMESPACE] = ('entry', - [YouTubeVideoCommentEntry]) - - -class YouTubeVideoResponseFeed(gdata.GDataFeed, gdata.LinkFinder): - """Represents a feed of video responses.""" - _tag = gdata.GDataFeed._tag - _namespace = gdata.GDataFeed._namespace - _children = gdata.GDataFeed._children.copy() - _attributes = gdata.GDataFeed._attributes.copy() - _children['{%s}entry' % atom.ATOM_NAMESPACE] = ('entry', - [YouTubeVideoResponseEntry]) - - -def YouTubeVideoFeedFromString(xml_string): - return atom.CreateClassFromXMLString(YouTubeVideoFeed, xml_string) - - -def YouTubeVideoEntryFromString(xml_string): - return atom.CreateClassFromXMLString(YouTubeVideoEntry, xml_string) - - -def YouTubeContactFeedFromString(xml_string): - return atom.CreateClassFromXMLString(YouTubeContactFeed, xml_string) - - -def YouTubeContactEntryFromString(xml_string): - return atom.CreateClassFromXMLString(YouTubeContactEntry, xml_string) - - -def YouTubeVideoCommentFeedFromString(xml_string): - return atom.CreateClassFromXMLString(YouTubeVideoCommentFeed, xml_string) - - -def YouTubeVideoCommentEntryFromString(xml_string): - return atom.CreateClassFromXMLString(YouTubeVideoCommentEntry, xml_string) - - -def YouTubeUserFeedFromString(xml_string): - return atom.CreateClassFromXMLString(YouTubeVideoFeed, xml_string) - - -def YouTubeUserEntryFromString(xml_string): - return atom.CreateClassFromXMLString(YouTubeUserEntry, xml_string) - - -def YouTubePlaylistFeedFromString(xml_string): - return atom.CreateClassFromXMLString(YouTubePlaylistFeed, xml_string) - - -def YouTubePlaylistVideoFeedFromString(xml_string): - return atom.CreateClassFromXMLString(YouTubePlaylistVideoFeed, xml_string) - - -def YouTubePlaylistEntryFromString(xml_string): - return atom.CreateClassFromXMLString(YouTubePlaylistEntry, xml_string) - - -def YouTubePlaylistVideoEntryFromString(xml_string): - return atom.CreateClassFromXMLString(YouTubePlaylistVideoEntry, xml_string) - - -def YouTubeSubscriptionFeedFromString(xml_string): - return atom.CreateClassFromXMLString(YouTubeSubscriptionFeed, xml_string) - - -def YouTubeSubscriptionEntryFromString(xml_string): - return atom.CreateClassFromXMLString(YouTubeSubscriptionEntry, xml_string) - - -def YouTubeVideoResponseFeedFromString(xml_string): - return atom.CreateClassFromXMLString(YouTubeVideoResponseFeed, xml_string) - - -def YouTubeVideoResponseEntryFromString(xml_string): - return atom.CreateClassFromXMLString(YouTubeVideoResponseEntry, xml_string) diff --git a/gdata/youtube/data.py b/gdata/youtube/data.py deleted file mode 100644 index b75cf79d78..0000000000 --- a/gdata/youtube/data.py +++ /dev/null @@ -1,477 +0,0 @@ -#!/usr/bin/python -# -# Copyright (C) 2009 Google 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. - - -"""Contains the data classes of the YouTube Data API""" - - -__author__ = 'j.s@google.com (Jeff Scudder)' - - -import atom.core -import atom.data -import gdata.data -import gdata.geo.data -import gdata.media.data -import gdata.opensearch.data -import gdata.youtube.data - - -YT_TEMPLATE = '{http://gdata.youtube.com/schemas/2007/}%s' - - -class ComplaintEntry(gdata.data.GDEntry): - """Describes a complaint about a video""" - - -class ComplaintFeed(gdata.data.GDFeed): - """Describes complaints about a video""" - entry = [ComplaintEntry] - - -class RatingEntry(gdata.data.GDEntry): - """A rating about a video""" - rating = gdata.data.Rating - - -class RatingFeed(gdata.data.GDFeed): - """Describes ratings for a video""" - entry = [RatingEntry] - - -class YouTubeMediaContent(gdata.media.data.MediaContent): - """Describes a you tube media content""" - _qname = gdata.media.data.MEDIA_TEMPLATE % 'content' - format = 'format' - - -class YtAge(atom.core.XmlElement): - """User's age""" - _qname = YT_TEMPLATE % 'age' - - -class YtBooks(atom.core.XmlElement): - """User's favorite books""" - _qname = YT_TEMPLATE % 'books' - - -class YtCompany(atom.core.XmlElement): - """User's company""" - _qname = YT_TEMPLATE % 'company' - - -class YtDescription(atom.core.XmlElement): - """Description""" - _qname = YT_TEMPLATE % 'description' - - -class YtDuration(atom.core.XmlElement): - """Video duration""" - _qname = YT_TEMPLATE % 'duration' - seconds = 'seconds' - - -class YtFirstName(atom.core.XmlElement): - """User's first name""" - _qname = YT_TEMPLATE % 'firstName' - - -class YtGender(atom.core.XmlElement): - """User's gender""" - _qname = YT_TEMPLATE % 'gender' - - -class YtHobbies(atom.core.XmlElement): - """User's hobbies""" - _qname = YT_TEMPLATE % 'hobbies' - - -class YtHometown(atom.core.XmlElement): - """User's hometown""" - _qname = YT_TEMPLATE % 'hometown' - - -class YtLastName(atom.core.XmlElement): - """User's last name""" - _qname = YT_TEMPLATE % 'lastName' - - -class YtLocation(atom.core.XmlElement): - """Location""" - _qname = YT_TEMPLATE % 'location' - - -class YtMovies(atom.core.XmlElement): - """User's favorite movies""" - _qname = YT_TEMPLATE % 'movies' - - -class YtMusic(atom.core.XmlElement): - """User's favorite music""" - _qname = YT_TEMPLATE % 'music' - - -class YtNoEmbed(atom.core.XmlElement): - """Disables embedding for the video""" - _qname = YT_TEMPLATE % 'noembed' - - -class YtOccupation(atom.core.XmlElement): - """User's occupation""" - _qname = YT_TEMPLATE % 'occupation' - - -class YtPlaylistId(atom.core.XmlElement): - """Playlist id""" - _qname = YT_TEMPLATE % 'playlistId' - - -class YtPosition(atom.core.XmlElement): - """Video position on the playlist""" - _qname = YT_TEMPLATE % 'position' - - -class YtPrivate(atom.core.XmlElement): - """Flags the entry as private""" - _qname = YT_TEMPLATE % 'private' - - -class YtQueryString(atom.core.XmlElement): - """Keywords or query string associated with a subscription""" - _qname = YT_TEMPLATE % 'queryString' - - -class YtRacy(atom.core.XmlElement): - """Mature content""" - _qname = YT_TEMPLATE % 'racy' - - -class YtRecorded(atom.core.XmlElement): - """Date when the video was recorded""" - _qname = YT_TEMPLATE % 'recorded' - - -class YtRelationship(atom.core.XmlElement): - """User's relationship status""" - _qname = YT_TEMPLATE % 'relationship' - - -class YtSchool(atom.core.XmlElement): - """User's school""" - _qname = YT_TEMPLATE % 'school' - - -class YtStatistics(atom.core.XmlElement): - """Video and user statistics""" - _qname = YT_TEMPLATE % 'statistics' - favorite_count = 'favoriteCount' - video_watch_count = 'videoWatchCount' - view_count = 'viewCount' - last_web_access = 'lastWebAccess' - subscriber_count = 'subscriberCount' - - -class YtStatus(atom.core.XmlElement): - """Status of a contact""" - _qname = YT_TEMPLATE % 'status' - - -class YtUserProfileStatistics(YtStatistics): - """User statistics""" - _qname = YT_TEMPLATE % 'statistics' - - -class YtUsername(atom.core.XmlElement): - """Youtube username""" - _qname = YT_TEMPLATE % 'username' - - -class FriendEntry(gdata.data.BatchEntry): - """Describes a contact in friend list""" - username = YtUsername - status = YtStatus - email = gdata.data.Email - - -class FriendFeed(gdata.data.BatchFeed): - """Describes user's friends""" - entry = [FriendEntry] - - -class YtVideoStatistics(YtStatistics): - """Video statistics""" - _qname = YT_TEMPLATE % 'statistics' - - -class ChannelEntry(gdata.data.GDEntry): - """Describes a video channel""" - - -class ChannelFeed(gdata.data.GDFeed): - """Describes channels""" - entry = [ChannelEntry] - - -class FavoriteEntry(gdata.data.BatchEntry): - """Describes a favorite video""" - - -class FavoriteFeed(gdata.data.BatchFeed): - """Describes favorite videos""" - entry = [FavoriteEntry] - - -class YouTubeMediaCredit(gdata.media.data.MediaCredit): - """Describes a you tube media credit""" - _qname = gdata.media.data.MEDIA_TEMPLATE % 'credit' - type = 'type' - - -class YouTubeMediaRating(gdata.media.data.MediaRating): - """Describes a you tube media rating""" - _qname = gdata.media.data.MEDIA_TEMPLATE % 'rating' - country = 'country' - - -class YtAboutMe(atom.core.XmlElement): - """User's self description""" - _qname = YT_TEMPLATE % 'aboutMe' - - -class UserProfileEntry(gdata.data.BatchEntry): - """Describes an user's profile""" - relationship = YtRelationship - description = YtDescription - location = YtLocation - statistics = YtUserProfileStatistics - school = YtSchool - music = YtMusic - first_name = YtFirstName - gender = YtGender - occupation = YtOccupation - hometown = YtHometown - company = YtCompany - movies = YtMovies - books = YtBooks - username = YtUsername - about_me = YtAboutMe - last_name = YtLastName - age = YtAge - thumbnail = gdata.media.data.MediaThumbnail - hobbies = YtHobbies - - -class UserProfileFeed(gdata.data.BatchFeed): - """Describes a feed of user's profile""" - entry = [UserProfileEntry] - - -class YtAspectRatio(atom.core.XmlElement): - """The aspect ratio of a media file""" - _qname = YT_TEMPLATE % 'aspectRatio' - - -class YtBasePublicationState(atom.core.XmlElement): - """Status of an unpublished entry""" - _qname = YT_TEMPLATE % 'state' - help_url = 'helpUrl' - - -class YtPublicationState(YtBasePublicationState): - """Status of an unpublished video""" - _qname = YT_TEMPLATE % 'state' - name = 'name' - reason_code = 'reasonCode' - - -class YouTubeAppControl(atom.data.Control): - """Describes a you tube app control""" - _qname = (atom.data.APP_TEMPLATE_V1 % 'control', - atom.data.APP_TEMPLATE_V2 % 'control') - state = YtPublicationState - - -class YtCaptionPublicationState(YtBasePublicationState): - """Status of an unpublished caption track""" - _qname = YT_TEMPLATE % 'state' - reason_code = 'reasonCode' - name = 'name' - - -class YouTubeCaptionAppControl(atom.data.Control): - """Describes a you tube caption app control""" - _qname = atom.data.APP_TEMPLATE_V2 % 'control' - state = YtCaptionPublicationState - - -class CaptionTrackEntry(gdata.data.GDEntry): - """Describes a caption track""" - - -class CaptionTrackFeed(gdata.data.GDFeed): - """Describes caption tracks""" - entry = [CaptionTrackEntry] - - -class YtCountHint(atom.core.XmlElement): - """Hint as to how many entries the linked feed contains""" - _qname = YT_TEMPLATE % 'countHint' - - -class PlaylistLinkEntry(gdata.data.BatchEntry): - """Describes a playlist""" - description = YtDescription - playlist_id = YtPlaylistId - count_hint = YtCountHint - private = YtPrivate - - -class PlaylistLinkFeed(gdata.data.BatchFeed): - """Describes list of playlists""" - entry = [PlaylistLinkEntry] - - -class YtModerationStatus(atom.core.XmlElement): - """Moderation status""" - _qname = YT_TEMPLATE % 'moderationStatus' - - -class YtPlaylistTitle(atom.core.XmlElement): - """Playlist title""" - _qname = YT_TEMPLATE % 'playlistTitle' - - -class SubscriptionEntry(gdata.data.BatchEntry): - """Describes user's channel subscritpions""" - count_hint = YtCountHint - playlist_title = YtPlaylistTitle - thumbnail = gdata.media.data.MediaThumbnail - username = YtUsername - query_string = YtQueryString - playlist_id = YtPlaylistId - - -class SubscriptionFeed(gdata.data.BatchFeed): - """Describes list of user's video subscriptions""" - entry = [SubscriptionEntry] - - -class YtSpam(atom.core.XmlElement): - """Indicates that the entry probably contains spam""" - _qname = YT_TEMPLATE % 'spam' - - -class CommentEntry(gdata.data.BatchEntry): - """Describes a comment for a video""" - spam = YtSpam - - -class CommentFeed(gdata.data.BatchFeed): - """Describes comments for a video""" - entry = [CommentEntry] - - -class YtUploaded(atom.core.XmlElement): - """Date/Time at which the video was uploaded""" - _qname = YT_TEMPLATE % 'uploaded' - - -class YtVideoId(atom.core.XmlElement): - """Video id""" - _qname = YT_TEMPLATE % 'videoid' - - -class YouTubeMediaGroup(gdata.media.data.MediaGroup): - """Describes a you tube media group""" - _qname = gdata.media.data.MEDIA_TEMPLATE % 'group' - videoid = YtVideoId - private = YtPrivate - duration = YtDuration - aspect_ratio = YtAspectRatio - uploaded = YtUploaded - - -class VideoEntryBase(gdata.data.GDEntry): - """Elements that describe or contain videos""" - group = YouTubeMediaGroup - statistics = YtVideoStatistics - racy = YtRacy - recorded = YtRecorded - where = gdata.geo.data.GeoRssWhere - rating = gdata.data.Rating - noembed = YtNoEmbed - location = YtLocation - comments = gdata.data.Comments - - -class PlaylistEntry(gdata.data.BatchEntry): - """Describes a video in a playlist""" - description = YtDescription - position = YtPosition - - -class PlaylistFeed(gdata.data.BatchFeed): - """Describes videos in a playlist""" - private = YtPrivate - group = YouTubeMediaGroup - playlist_id = YtPlaylistId - entry = [PlaylistEntry] - - -class VideoEntry(gdata.data.BatchEntry): - """Describes a video""" - - -class VideoFeed(gdata.data.BatchFeed): - """Describes a video feed""" - entry = [VideoEntry] - - -class VideoMessageEntry(gdata.data.BatchEntry): - """Describes a video message""" - description = YtDescription - - -class VideoMessageFeed(gdata.data.BatchFeed): - """Describes videos in a videoMessage""" - entry = [VideoMessageEntry] - - -class UserEventEntry(gdata.data.GDEntry): - """Describes a user event""" - playlist_id = YtPlaylistId - videoid = YtVideoId - username = YtUsername - query_string = YtQueryString - rating = gdata.data.Rating - - -class UserEventFeed(gdata.data.GDFeed): - """Describes list of events""" - entry = [UserEventEntry] - - -class VideoModerationEntry(gdata.data.GDEntry): - """Describes video moderation""" - moderation_status = YtModerationStatus - videoid = YtVideoId - - -class VideoModerationFeed(gdata.data.GDFeed): - """Describes a video moderation feed""" - entry = [VideoModerationEntry] - - diff --git a/gdata/youtube/service.py b/gdata/youtube/service.py deleted file mode 100644 index c98201b15c..0000000000 --- a/gdata/youtube/service.py +++ /dev/null @@ -1,1563 +0,0 @@ -#!/usr/bin/python -# -# Copyright (C) 2008 Google 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. - -"""YouTubeService extends GDataService to streamline YouTube operations. - - YouTubeService: Provides methods to perform CRUD operations on YouTube feeds. - Extends GDataService. -""" - -__author__ = ('api.stephaniel@gmail.com (Stephanie Liu), ' - 'api.jhartmann@gmail.com (Jochen Hartmann)') - -try: - from xml.etree import cElementTree as ElementTree -except ImportError: - try: - import cElementTree as ElementTree - except ImportError: - try: - from xml.etree import ElementTree - except ImportError: - from elementtree import ElementTree -import os -import atom -import gdata -import gdata.service -import gdata.youtube - -YOUTUBE_SERVER = 'gdata.youtube.com' -YOUTUBE_SERVICE = 'youtube' -YOUTUBE_CLIENTLOGIN_AUTHENTICATION_URL = 'https://www.google.com/youtube/accounts/ClientLogin' -YOUTUBE_SUPPORTED_UPLOAD_TYPES = ('mov', 'avi', 'wmv', 'mpg', 'quicktime', - 'flv', 'mp4', 'x-flv') -YOUTUBE_QUERY_VALID_TIME_PARAMETERS = ('today', 'this_week', 'this_month', - 'all_time') -YOUTUBE_QUERY_VALID_ORDERBY_PARAMETERS = ('published', 'viewCount', 'rating', - 'relevance') -YOUTUBE_QUERY_VALID_RACY_PARAMETERS = ('include', 'exclude') -YOUTUBE_QUERY_VALID_FORMAT_PARAMETERS = ('1', '5', '6') -YOUTUBE_STANDARDFEEDS = ('most_recent', 'recently_featured', - 'top_rated', 'most_viewed','watch_on_mobile') -YOUTUBE_UPLOAD_URI = 'http://uploads.gdata.youtube.com/feeds/api/users' -YOUTUBE_UPLOAD_TOKEN_URI = 'http://gdata.youtube.com/action/GetUploadToken' -YOUTUBE_VIDEO_URI = 'http://gdata.youtube.com/feeds/api/videos' -YOUTUBE_USER_FEED_URI = 'http://gdata.youtube.com/feeds/api/users' -YOUTUBE_PLAYLIST_FEED_URI = 'http://gdata.youtube.com/feeds/api/playlists' - -YOUTUBE_STANDARD_FEEDS = 'http://gdata.youtube.com/feeds/api/standardfeeds' -YOUTUBE_STANDARD_TOP_RATED_URI = '%s/%s' % (YOUTUBE_STANDARD_FEEDS, 'top_rated') -YOUTUBE_STANDARD_MOST_VIEWED_URI = '%s/%s' % (YOUTUBE_STANDARD_FEEDS, - 'most_viewed') -YOUTUBE_STANDARD_RECENTLY_FEATURED_URI = '%s/%s' % (YOUTUBE_STANDARD_FEEDS, - 'recently_featured') -YOUTUBE_STANDARD_WATCH_ON_MOBILE_URI = '%s/%s' % (YOUTUBE_STANDARD_FEEDS, - 'watch_on_mobile') -YOUTUBE_STANDARD_TOP_FAVORITES_URI = '%s/%s' % (YOUTUBE_STANDARD_FEEDS, - 'top_favorites') -YOUTUBE_STANDARD_MOST_RECENT_URI = '%s/%s' % (YOUTUBE_STANDARD_FEEDS, - 'most_recent') -YOUTUBE_STANDARD_MOST_DISCUSSED_URI = '%s/%s' % (YOUTUBE_STANDARD_FEEDS, - 'most_discussed') -YOUTUBE_STANDARD_MOST_LINKED_URI = '%s/%s' % (YOUTUBE_STANDARD_FEEDS, - 'most_linked') -YOUTUBE_STANDARD_MOST_RESPONDED_URI = '%s/%s' % (YOUTUBE_STANDARD_FEEDS, - 'most_responded') -YOUTUBE_SCHEMA = 'http://gdata.youtube.com/schemas' - -YOUTUBE_RATING_LINK_REL = '%s#video.ratings' % YOUTUBE_SCHEMA - -YOUTUBE_COMPLAINT_CATEGORY_SCHEME = '%s/%s' % (YOUTUBE_SCHEMA, - 'complaint-reasons.cat') -YOUTUBE_SUBSCRIPTION_CATEGORY_SCHEME = '%s/%s' % (YOUTUBE_SCHEMA, - 'subscriptiontypes.cat') - -YOUTUBE_COMPLAINT_CATEGORY_TERMS = ('PORN', 'VIOLENCE', 'HATE', 'DANGEROUS', - 'RIGHTS', 'SPAM') -YOUTUBE_CONTACT_STATUS = ('accepted', 'rejected') -YOUTUBE_CONTACT_CATEGORY = ('Friends', 'Family') - -UNKOWN_ERROR = 1000 -YOUTUBE_BAD_REQUEST = 400 -YOUTUBE_CONFLICT = 409 -YOUTUBE_INTERNAL_SERVER_ERROR = 500 -YOUTUBE_INVALID_ARGUMENT = 601 -YOUTUBE_INVALID_CONTENT_TYPE = 602 -YOUTUBE_NOT_A_VIDEO = 603 -YOUTUBE_INVALID_KIND = 604 - - -class Error(Exception): - """Base class for errors within the YouTube service.""" - pass - -class RequestError(Error): - """Error class that is thrown in response to an invalid HTTP Request.""" - pass - -class YouTubeError(Error): - """YouTube service specific error class.""" - pass - -class YouTubeService(gdata.service.GDataService): - - """Client for the YouTube service. - - Performs all documented Google Data YouTube API functions, such as inserting, - updating and deleting videos, comments, playlist, subscriptions etc. - YouTube Service requires authentication for any write, update or delete - actions. - - Attributes: - email: An optional string identifying the user. Required only for - authenticated actions. - password: An optional string identifying the user's password. - source: An optional string identifying the name of your application. - server: An optional address of the YouTube API server. gdata.youtube.com - is provided as the default value. - additional_headers: An optional dictionary containing additional headers - to be passed along with each request. Use to store developer key. - client_id: An optional string identifying your application, required for - authenticated requests, along with a developer key. - developer_key: An optional string value. Register your application at - http://code.google.com/apis/youtube/dashboard to obtain a (free) key. - """ - - def __init__(self, email=None, password=None, source=None, - server=YOUTUBE_SERVER, additional_headers=None, client_id=None, - developer_key=None, **kwargs): - """Creates a client for the YouTube 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: 'gdata.youtube.com'. - client_id: string (optional) Identifies your application, required for - authenticated requests, along with a developer key. - developer_key: string (optional) Register your application at - http://code.google.com/apis/youtube/dashboard to obtain a (free) key. - **kwargs: The other parameters to pass to gdata.service.GDataService - constructor. - """ - - gdata.service.GDataService.__init__( - self, email=email, password=password, service=YOUTUBE_SERVICE, - source=source, server=server, additional_headers=additional_headers, - **kwargs) - - if client_id is not None: - self.additional_headers['X-Gdata-Client'] = client_id - - if developer_key is not None: - self.additional_headers['X-GData-Key'] = 'key=%s' % developer_key - - self.auth_service_url = YOUTUBE_CLIENTLOGIN_AUTHENTICATION_URL - - def GetYouTubeVideoFeed(self, uri): - """Retrieve a YouTubeVideoFeed. - - Args: - uri: A string representing the URI of the feed that is to be retrieved. - - Returns: - A YouTubeVideoFeed if successfully retrieved. - """ - return self.Get(uri, converter=gdata.youtube.YouTubeVideoFeedFromString) - - def GetYouTubeVideoEntry(self, uri=None, video_id=None): - """Retrieve a YouTubeVideoEntry. - - Either a uri or a video_id must be provided. - - Args: - uri: An optional string representing the URI of the entry that is to - be retrieved. - video_id: An optional string representing the ID of the video. - - Returns: - A YouTubeVideoFeed if successfully retrieved. - - Raises: - YouTubeError: You must provide at least a uri or a video_id to the - GetYouTubeVideoEntry() method. - """ - if uri is None and video_id is None: - raise YouTubeError('You must provide at least a uri or a video_id ' - 'to the GetYouTubeVideoEntry() method') - elif video_id and not uri: - uri = '%s/%s' % (YOUTUBE_VIDEO_URI, video_id) - return self.Get(uri, converter=gdata.youtube.YouTubeVideoEntryFromString) - - def GetYouTubeContactFeed(self, uri=None, username='default'): - """Retrieve a YouTubeContactFeed. - - Either a uri or a username must be provided. - - Args: - uri: An optional string representing the URI of the contact feed that - is to be retrieved. - username: An optional string representing the username. Defaults to the - currently authenticated user. - - Returns: - A YouTubeContactFeed if successfully retrieved. - - Raises: - YouTubeError: You must provide at least a uri or a username to the - GetYouTubeContactFeed() method. - """ - if uri is None: - uri = '%s/%s/%s' % (YOUTUBE_USER_FEED_URI, username, 'contacts') - return self.Get(uri, converter=gdata.youtube.YouTubeContactFeedFromString) - - def GetYouTubeContactEntry(self, uri): - """Retrieve a YouTubeContactEntry. - - Args: - uri: A string representing the URI of the contact entry that is to - be retrieved. - - Returns: - A YouTubeContactEntry if successfully retrieved. - """ - return self.Get(uri, converter=gdata.youtube.YouTubeContactEntryFromString) - - def GetYouTubeVideoCommentFeed(self, uri=None, video_id=None): - """Retrieve a YouTubeVideoCommentFeed. - - Either a uri or a video_id must be provided. - - Args: - uri: An optional string representing the URI of the comment feed that - is to be retrieved. - video_id: An optional string representing the ID of the video for which - to retrieve the comment feed. - - Returns: - A YouTubeVideoCommentFeed if successfully retrieved. - - Raises: - YouTubeError: You must provide at least a uri or a video_id to the - GetYouTubeVideoCommentFeed() method. - """ - if uri is None and video_id is None: - raise YouTubeError('You must provide at least a uri or a video_id ' - 'to the GetYouTubeVideoCommentFeed() method') - elif video_id and not uri: - uri = '%s/%s/%s' % (YOUTUBE_VIDEO_URI, video_id, 'comments') - return self.Get( - uri, converter=gdata.youtube.YouTubeVideoCommentFeedFromString) - - def GetYouTubeVideoCommentEntry(self, uri): - """Retrieve a YouTubeVideoCommentEntry. - - Args: - uri: A string representing the URI of the comment entry that is to - be retrieved. - - Returns: - A YouTubeCommentEntry if successfully retrieved. - """ - return self.Get( - uri, converter=gdata.youtube.YouTubeVideoCommentEntryFromString) - - def GetYouTubeUserFeed(self, uri=None, username=None): - """Retrieve a YouTubeVideoFeed of user uploaded videos - - Either a uri or a username must be provided. This will retrieve list - of videos uploaded by specified user. The uri will be of format - "http://gdata.youtube.com/feeds/api/users/{username}/uploads". - - Args: - uri: An optional string representing the URI of the user feed that is - to be retrieved. - username: An optional string representing the username. - - Returns: - A YouTubeUserFeed if successfully retrieved. - - Raises: - YouTubeError: You must provide at least a uri or a username to the - GetYouTubeUserFeed() method. - """ - if uri is None and username is None: - raise YouTubeError('You must provide at least a uri or a username ' - 'to the GetYouTubeUserFeed() method') - elif username and not uri: - uri = '%s/%s/%s' % (YOUTUBE_USER_FEED_URI, username, 'uploads') - return self.Get(uri, converter=gdata.youtube.YouTubeUserFeedFromString) - - def GetYouTubeUserEntry(self, uri=None, username=None): - """Retrieve a YouTubeUserEntry. - - Either a uri or a username must be provided. - - Args: - uri: An optional string representing the URI of the user entry that is - to be retrieved. - username: An optional string representing the username. - - Returns: - A YouTubeUserEntry if successfully retrieved. - - Raises: - YouTubeError: You must provide at least a uri or a username to the - GetYouTubeUserEntry() method. - """ - if uri is None and username is None: - raise YouTubeError('You must provide at least a uri or a username ' - 'to the GetYouTubeUserEntry() method') - elif username and not uri: - uri = '%s/%s' % (YOUTUBE_USER_FEED_URI, username) - return self.Get(uri, converter=gdata.youtube.YouTubeUserEntryFromString) - - def GetYouTubePlaylistFeed(self, uri=None, username='default'): - """Retrieve a YouTubePlaylistFeed (a feed of playlists for a user). - - Either a uri or a username must be provided. - - Args: - uri: An optional string representing the URI of the playlist feed that - is to be retrieved. - username: An optional string representing the username. Defaults to the - currently authenticated user. - - Returns: - A YouTubePlaylistFeed if successfully retrieved. - - Raises: - YouTubeError: You must provide at least a uri or a username to the - GetYouTubePlaylistFeed() method. - """ - if uri is None: - uri = '%s/%s/%s' % (YOUTUBE_USER_FEED_URI, username, 'playlists') - return self.Get(uri, converter=gdata.youtube.YouTubePlaylistFeedFromString) - - def GetYouTubePlaylistEntry(self, uri): - """Retrieve a YouTubePlaylistEntry. - - Args: - uri: A string representing the URI of the playlist feed that is to - be retrieved. - - Returns: - A YouTubePlaylistEntry if successfully retrieved. - """ - return self.Get(uri, converter=gdata.youtube.YouTubePlaylistEntryFromString) - - def GetYouTubePlaylistVideoFeed(self, uri=None, playlist_id=None): - """Retrieve a YouTubePlaylistVideoFeed (a feed of videos on a playlist). - - Either a uri or a playlist_id must be provided. - - Args: - uri: An optional string representing the URI of the playlist video feed - that is to be retrieved. - playlist_id: An optional string representing the Id of the playlist whose - playlist video feed is to be retrieved. - - Returns: - A YouTubePlaylistVideoFeed if successfully retrieved. - - Raises: - YouTubeError: You must provide at least a uri or a playlist_id to the - GetYouTubePlaylistVideoFeed() method. - """ - if uri is None and playlist_id is None: - raise YouTubeError('You must provide at least a uri or a playlist_id ' - 'to the GetYouTubePlaylistVideoFeed() method') - elif playlist_id and not uri: - uri = '%s/%s' % (YOUTUBE_PLAYLIST_FEED_URI, playlist_id) - return self.Get( - uri, converter=gdata.youtube.YouTubePlaylistVideoFeedFromString) - - def GetYouTubeVideoResponseFeed(self, uri=None, video_id=None): - """Retrieve a YouTubeVideoResponseFeed. - - Either a uri or a playlist_id must be provided. - - Args: - uri: An optional string representing the URI of the video response feed - that is to be retrieved. - video_id: An optional string representing the ID of the video whose - response feed is to be retrieved. - - Returns: - A YouTubeVideoResponseFeed if successfully retrieved. - - Raises: - YouTubeError: You must provide at least a uri or a video_id to the - GetYouTubeVideoResponseFeed() method. - """ - if uri is None and video_id is None: - raise YouTubeError('You must provide at least a uri or a video_id ' - 'to the GetYouTubeVideoResponseFeed() method') - elif video_id and not uri: - uri = '%s/%s/%s' % (YOUTUBE_VIDEO_URI, video_id, 'responses') - return self.Get( - uri, converter=gdata.youtube.YouTubeVideoResponseFeedFromString) - - def GetYouTubeVideoResponseEntry(self, uri): - """Retrieve a YouTubeVideoResponseEntry. - - Args: - uri: A string representing the URI of the video response entry that - is to be retrieved. - - Returns: - A YouTubeVideoResponseEntry if successfully retrieved. - """ - return self.Get( - uri, converter=gdata.youtube.YouTubeVideoResponseEntryFromString) - - def GetYouTubeSubscriptionFeed(self, uri=None, username='default'): - """Retrieve a YouTubeSubscriptionFeed. - - Either the uri of the feed or a username must be provided. - - Args: - uri: An optional string representing the URI of the feed that is to - be retrieved. - username: An optional string representing the username whose subscription - feed is to be retrieved. Defaults to the currently authenticted user. - - Returns: - A YouTubeVideoSubscriptionFeed if successfully retrieved. - """ - if uri is None: - uri = '%s/%s/%s' % (YOUTUBE_USER_FEED_URI, username, 'subscriptions') - return self.Get( - uri, converter=gdata.youtube.YouTubeSubscriptionFeedFromString) - - def GetYouTubeSubscriptionEntry(self, uri): - """Retrieve a YouTubeSubscriptionEntry. - - Args: - uri: A string representing the URI of the entry that is to be retrieved. - - Returns: - A YouTubeVideoSubscriptionEntry if successfully retrieved. - """ - return self.Get( - uri, converter=gdata.youtube.YouTubeSubscriptionEntryFromString) - - def GetYouTubeRelatedVideoFeed(self, uri=None, video_id=None): - """Retrieve a YouTubeRelatedVideoFeed. - - Either a uri for the feed or a video_id is required. - - Args: - uri: An optional string representing the URI of the feed that is to - be retrieved. - video_id: An optional string representing the ID of the video for which - to retrieve the related video feed. - - Returns: - A YouTubeRelatedVideoFeed if successfully retrieved. - - Raises: - YouTubeError: You must provide at least a uri or a video_id to the - GetYouTubeRelatedVideoFeed() method. - """ - if uri is None and video_id is None: - raise YouTubeError('You must provide at least a uri or a video_id ' - 'to the GetYouTubeRelatedVideoFeed() method') - elif video_id and not uri: - uri = '%s/%s/%s' % (YOUTUBE_VIDEO_URI, video_id, 'related') - return self.Get( - uri, converter=gdata.youtube.YouTubeVideoFeedFromString) - - def GetTopRatedVideoFeed(self): - """Retrieve the 'top_rated' standard video feed. - - Returns: - A YouTubeVideoFeed if successfully retrieved. - """ - return self.GetYouTubeVideoFeed(YOUTUBE_STANDARD_TOP_RATED_URI) - - def GetMostViewedVideoFeed(self): - """Retrieve the 'most_viewed' standard video feed. - - Returns: - A YouTubeVideoFeed if successfully retrieved. - """ - return self.GetYouTubeVideoFeed(YOUTUBE_STANDARD_MOST_VIEWED_URI) - - def GetRecentlyFeaturedVideoFeed(self): - """Retrieve the 'recently_featured' standard video feed. - - Returns: - A YouTubeVideoFeed if successfully retrieved. - """ - return self.GetYouTubeVideoFeed(YOUTUBE_STANDARD_RECENTLY_FEATURED_URI) - - def GetWatchOnMobileVideoFeed(self): - """Retrieve the 'watch_on_mobile' standard video feed. - - Returns: - A YouTubeVideoFeed if successfully retrieved. - """ - return self.GetYouTubeVideoFeed(YOUTUBE_STANDARD_WATCH_ON_MOBILE_URI) - - def GetTopFavoritesVideoFeed(self): - """Retrieve the 'top_favorites' standard video feed. - - Returns: - A YouTubeVideoFeed if successfully retrieved. - """ - return self.GetYouTubeVideoFeed(YOUTUBE_STANDARD_TOP_FAVORITES_URI) - - def GetMostRecentVideoFeed(self): - """Retrieve the 'most_recent' standard video feed. - - Returns: - A YouTubeVideoFeed if successfully retrieved. - """ - return self.GetYouTubeVideoFeed(YOUTUBE_STANDARD_MOST_RECENT_URI) - - def GetMostDiscussedVideoFeed(self): - """Retrieve the 'most_discussed' standard video feed. - - Returns: - A YouTubeVideoFeed if successfully retrieved. - """ - return self.GetYouTubeVideoFeed(YOUTUBE_STANDARD_MOST_DISCUSSED_URI) - - def GetMostLinkedVideoFeed(self): - """Retrieve the 'most_linked' standard video feed. - - Returns: - A YouTubeVideoFeed if successfully retrieved. - """ - return self.GetYouTubeVideoFeed(YOUTUBE_STANDARD_MOST_LINKED_URI) - - def GetMostRespondedVideoFeed(self): - """Retrieve the 'most_responded' standard video feed. - - Returns: - A YouTubeVideoFeed if successfully retrieved. - """ - return self.GetYouTubeVideoFeed(YOUTUBE_STANDARD_MOST_RESPONDED_URI) - - def GetUserFavoritesFeed(self, username='default'): - """Retrieve the favorites feed for a given user. - - Args: - username: An optional string representing the username whose favorites - feed is to be retrieved. Defaults to the currently authenticated user. - - Returns: - A YouTubeVideoFeed if successfully retrieved. - """ - favorites_feed_uri = '%s/%s/%s' % (YOUTUBE_USER_FEED_URI, username, - 'favorites') - return self.GetYouTubeVideoFeed(favorites_feed_uri) - - def InsertVideoEntry(self, video_entry, filename_or_handle, - youtube_username='default', - content_type='video/quicktime'): - """Upload a new video to YouTube using the direct upload mechanism. - - Needs authentication. - - Args: - video_entry: The YouTubeVideoEntry to upload. - filename_or_handle: A file-like object or file name where the video - will be read from. - youtube_username: An optional string representing the username into whose - account this video is to be uploaded to. Defaults to the currently - authenticated user. - content_type: An optional string representing internet media type - (a.k.a. mime type) of the media object. Currently the YouTube API - supports these types: - o video/mpeg - o video/quicktime - o video/x-msvideo - o video/mp4 - o video/x-flv - - Returns: - The newly created YouTubeVideoEntry if successful. - - Raises: - AssertionError: video_entry must be a gdata.youtube.VideoEntry instance. - YouTubeError: An error occurred trying to read the video file provided. - gdata.service.RequestError: An error occurred trying to upload the video - to the API server. - """ - - # We need to perform a series of checks on the video_entry and on the - # file that we plan to upload, such as checking whether we have a valid - # video_entry and that the file is the correct type and readable, prior - # to performing the actual POST request. - - try: - assert(isinstance(video_entry, gdata.youtube.YouTubeVideoEntry)) - except AssertionError: - raise YouTubeError({'status':YOUTUBE_INVALID_ARGUMENT, - 'body':'`video_entry` must be a gdata.youtube.VideoEntry instance', - 'reason':'Found %s, not VideoEntry' % type(video_entry) - }) - #majtype, mintype = content_type.split('/') - # - #try: - # assert(mintype in YOUTUBE_SUPPORTED_UPLOAD_TYPES) - #except (ValueError, AssertionError): - # raise YouTubeError({'status':YOUTUBE_INVALID_CONTENT_TYPE, - # 'body':'This is not a valid content type: %s' % content_type, - # 'reason':'Accepted content types: %s' % - # ['video/%s' % (t) for t in YOUTUBE_SUPPORTED_UPLOAD_TYPES]}) - - if (isinstance(filename_or_handle, (str, unicode)) - and os.path.exists(filename_or_handle)): - mediasource = gdata.MediaSource() - mediasource.setFile(filename_or_handle, content_type) - elif hasattr(filename_or_handle, 'read'): - import StringIO - if hasattr(filename_or_handle, 'seek'): - filename_or_handle.seek(0) - file_handle = StringIO.StringIO(filename_or_handle.read()) - name = 'video' - if hasattr(filename_or_handle, 'name'): - name = filename_or_handle.name - mediasource = gdata.MediaSource(file_handle, content_type, - content_length=file_handle.len, file_name=name) - else: - raise YouTubeError({'status':YOUTUBE_INVALID_ARGUMENT, 'body': - '`filename_or_handle` must be a path name or a file-like object', - 'reason': ('Found %s, not path name or object ' - 'with a .read() method' % type(filename_or_handle))}) - upload_uri = '%s/%s/%s' % (YOUTUBE_UPLOAD_URI, youtube_username, - 'uploads') - self.additional_headers['Slug'] = mediasource.file_name - - # Using a nested try statement to retain Python 2.4 compatibility - try: - try: - return self.Post(video_entry, uri=upload_uri, media_source=mediasource, - converter=gdata.youtube.YouTubeVideoEntryFromString) - except gdata.service.RequestError, e: - raise YouTubeError(e.args[0]) - finally: - del(self.additional_headers['Slug']) - - def CheckUploadStatus(self, video_entry=None, video_id=None): - """Check upload status on a recently uploaded video entry. - - Needs authentication. Either video_entry or video_id must be provided. - - Args: - video_entry: An optional YouTubeVideoEntry whose upload status to check - video_id: An optional string representing the ID of the uploaded video - whose status is to be checked. - - Returns: - A tuple containing (video_upload_state, detailed_message) or None if - no status information is found. - - Raises: - YouTubeError: You must provide at least a video_entry or a video_id to the - CheckUploadStatus() method. - """ - if video_entry is None and video_id is None: - raise YouTubeError('You must provide at least a uri or a video_id ' - 'to the CheckUploadStatus() method') - elif video_id and not video_entry: - video_entry = self.GetYouTubeVideoEntry(video_id=video_id) - - control = video_entry.control - if control is not None: - draft = control.draft - if draft is not None: - if draft.text == 'yes': - yt_state = control.extension_elements[0] - if yt_state is not None: - state_value = yt_state.attributes['name'] - message = '' - if yt_state.text is not None: - message = yt_state.text - - return (state_value, message) - - def GetFormUploadToken(self, video_entry, uri=YOUTUBE_UPLOAD_TOKEN_URI): - """Receives a YouTube Token and a YouTube PostUrl from a YouTubeVideoEntry. - - Needs authentication. - - Args: - video_entry: The YouTubeVideoEntry to upload (meta-data only). - uri: An optional string representing the URI from where to fetch the - token information. Defaults to the YOUTUBE_UPLOADTOKEN_URI. - - Returns: - A tuple containing the URL to which to post your video file, along - with the youtube token that must be included with your upload in the - form of: (post_url, youtube_token). - """ - try: - response = self.Post(video_entry, uri) - except gdata.service.RequestError, e: - raise YouTubeError(e.args[0]) - - tree = ElementTree.fromstring(response) - - for child in tree: - if child.tag == 'url': - post_url = child.text - elif child.tag == 'token': - youtube_token = child.text - return (post_url, youtube_token) - - def UpdateVideoEntry(self, video_entry): - """Updates a video entry's meta-data. - - Needs authentication. - - Args: - video_entry: The YouTubeVideoEntry to update, containing updated - meta-data. - - Returns: - An updated YouTubeVideoEntry on success or None. - """ - for link in video_entry.link: - if link.rel == 'edit': - edit_uri = link.href - return self.Put(video_entry, uri=edit_uri, - converter=gdata.youtube.YouTubeVideoEntryFromString) - - def DeleteVideoEntry(self, video_entry): - """Deletes a video entry. - - Needs authentication. - - Args: - video_entry: The YouTubeVideoEntry to be deleted. - - Returns: - True if entry was deleted successfully. - """ - for link in video_entry.link: - if link.rel == 'edit': - edit_uri = link.href - return self.Delete(edit_uri) - - def AddRating(self, rating_value, video_entry): - """Add a rating to a video entry. - - Needs authentication. - - Args: - rating_value: The integer value for the rating (between 1 and 5). - video_entry: The YouTubeVideoEntry to be rated. - - Returns: - True if the rating was added successfully. - - Raises: - YouTubeError: rating_value must be between 1 and 5 in AddRating(). - """ - if rating_value < 1 or rating_value > 5: - raise YouTubeError('rating_value must be between 1 and 5 in AddRating()') - - entry = gdata.GDataEntry() - rating = gdata.youtube.Rating(min='1', max='5') - rating.extension_attributes['name'] = 'value' - rating.extension_attributes['value'] = str(rating_value) - entry.extension_elements.append(rating) - - for link in video_entry.link: - if link.rel == YOUTUBE_RATING_LINK_REL: - rating_uri = link.href - - return self.Post(entry, uri=rating_uri) - - def AddComment(self, comment_text, video_entry): - """Add a comment to a video entry. - - Needs authentication. Note that each comment that is posted must contain - the video entry that it is to be posted to. - - Args: - comment_text: A string representing the text of the comment. - video_entry: The YouTubeVideoEntry to be commented on. - - Returns: - True if the comment was added successfully. - """ - content = atom.Content(text=comment_text) - comment_entry = gdata.youtube.YouTubeVideoCommentEntry(content=content) - comment_post_uri = video_entry.comments.feed_link[0].href - - return self.Post(comment_entry, uri=comment_post_uri) - - def AddVideoResponse(self, video_id_to_respond_to, video_response): - """Add a video response. - - Needs authentication. - - Args: - video_id_to_respond_to: A string representing the ID of the video to be - responded to. - video_response: YouTubeVideoEntry to be posted as a response. - - Returns: - True if video response was posted successfully. - """ - post_uri = '%s/%s/%s' % (YOUTUBE_VIDEO_URI, video_id_to_respond_to, - 'responses') - return self.Post(video_response, uri=post_uri) - - def DeleteVideoResponse(self, video_id, response_video_id): - """Delete a video response. - - Needs authentication. - - Args: - video_id: A string representing the ID of video that contains the - response. - response_video_id: A string representing the ID of the video that was - posted as a response. - - Returns: - True if video response was deleted succcessfully. - """ - delete_uri = '%s/%s/%s/%s' % (YOUTUBE_VIDEO_URI, video_id, 'responses', - response_video_id) - return self.Delete(delete_uri) - - def AddComplaint(self, complaint_text, complaint_term, video_id): - """Add a complaint for a particular video entry. - - Needs authentication. - - Args: - complaint_text: A string representing the complaint text. - complaint_term: A string representing the complaint category term. - video_id: A string representing the ID of YouTubeVideoEntry to - complain about. - - Returns: - True if posted successfully. - - Raises: - YouTubeError: Your complaint_term is not valid. - """ - if complaint_term not in YOUTUBE_COMPLAINT_CATEGORY_TERMS: - raise YouTubeError('Your complaint_term is not valid') - - content = atom.Content(text=complaint_text) - category = atom.Category(term=complaint_term, - scheme=YOUTUBE_COMPLAINT_CATEGORY_SCHEME) - - complaint_entry = gdata.GDataEntry(content=content, category=[category]) - post_uri = '%s/%s/%s' % (YOUTUBE_VIDEO_URI, video_id, 'complaints') - - return self.Post(complaint_entry, post_uri) - - def AddVideoEntryToFavorites(self, video_entry, username='default'): - """Add a video entry to a users favorite feed. - - Needs authentication. - - Args: - video_entry: The YouTubeVideoEntry to add. - username: An optional string representing the username to whose favorite - feed you wish to add the entry. Defaults to the currently - authenticated user. - Returns: - The posted YouTubeVideoEntry if successfully posted. - """ - post_uri = '%s/%s/%s' % (YOUTUBE_USER_FEED_URI, username, 'favorites') - - return self.Post(video_entry, post_uri, - converter=gdata.youtube.YouTubeVideoEntryFromString) - - def DeleteVideoEntryFromFavorites(self, video_id, username='default'): - """Delete a video entry from the users favorite feed. - - Needs authentication. - - Args: - video_id: A string representing the ID of the video that is to be removed - username: An optional string representing the username of the user's - favorite feed. Defaults to the currently authenticated user. - - Returns: - True if entry was successfully deleted. - """ - edit_link = '%s/%s/%s/%s' % (YOUTUBE_USER_FEED_URI, username, 'favorites', - video_id) - return self.Delete(edit_link) - - def AddPlaylist(self, playlist_title, playlist_description, - playlist_private=None): - """Add a new playlist to the currently authenticated users account. - - Needs authentication. - - Args: - playlist_title: A string representing the title for the new playlist. - playlist_description: A string representing the description of the - playlist. - playlist_private: An optional boolean, set to True if the playlist is - to be private. - - Returns: - The YouTubePlaylistEntry if successfully posted. - """ - playlist_entry = gdata.youtube.YouTubePlaylistEntry( - title=atom.Title(text=playlist_title), - description=gdata.youtube.Description(text=playlist_description)) - if playlist_private: - playlist_entry.private = gdata.youtube.Private() - - playlist_post_uri = '%s/%s/%s' % (YOUTUBE_USER_FEED_URI, 'default', - 'playlists') - return self.Post(playlist_entry, playlist_post_uri, - converter=gdata.youtube.YouTubePlaylistEntryFromString) - - def UpdatePlaylist(self, playlist_id, new_playlist_title, - new_playlist_description, playlist_private=None, - username='default'): - """Update a playlist with new meta-data. - - Needs authentication. - - Args: - playlist_id: A string representing the ID of the playlist to be updated. - new_playlist_title: A string representing a new title for the playlist. - new_playlist_description: A string representing a new description for the - playlist. - playlist_private: An optional boolean, set to True if the playlist is - to be private. - username: An optional string representing the username whose playlist is - to be updated. Defaults to the currently authenticated user. - - Returns: - A YouTubePlaylistEntry if the update was successful. - """ - updated_playlist = gdata.youtube.YouTubePlaylistEntry( - title=atom.Title(text=new_playlist_title), - description=gdata.youtube.Description(text=new_playlist_description)) - if playlist_private: - updated_playlist.private = gdata.youtube.Private() - - playlist_put_uri = '%s/%s/playlists/%s' % (YOUTUBE_USER_FEED_URI, username, - playlist_id) - - return self.Put(updated_playlist, playlist_put_uri, - converter=gdata.youtube.YouTubePlaylistEntryFromString) - - def DeletePlaylist(self, playlist_uri): - """Delete a playlist from the currently authenticated users playlists. - - Needs authentication. - - Args: - playlist_uri: A string representing the URI of the playlist that is - to be deleted. - - Returns: - True if successfully deleted. - """ - return self.Delete(playlist_uri) - - def AddPlaylistVideoEntryToPlaylist( - self, playlist_uri, video_id, custom_video_title=None, - custom_video_description=None): - """Add a video entry to a playlist, optionally providing a custom title - and description. - - Needs authentication. - - Args: - playlist_uri: A string representing the URI of the playlist to which this - video entry is to be added. - video_id: A string representing the ID of the video entry to add. - custom_video_title: An optional string representing a custom title for - the video (only shown on the playlist). - custom_video_description: An optional string representing a custom - description for the video (only shown on the playlist). - - Returns: - A YouTubePlaylistVideoEntry if successfully posted. - """ - playlist_video_entry = gdata.youtube.YouTubePlaylistVideoEntry( - atom_id=atom.Id(text=video_id)) - if custom_video_title: - playlist_video_entry.title = atom.Title(text=custom_video_title) - if custom_video_description: - playlist_video_entry.description = gdata.youtube.Description( - text=custom_video_description) - - return self.Post(playlist_video_entry, playlist_uri, - converter=gdata.youtube.YouTubePlaylistVideoEntryFromString) - - def UpdatePlaylistVideoEntryMetaData( - self, playlist_uri, playlist_entry_id, new_video_title, - new_video_description, new_video_position): - """Update the meta data for a YouTubePlaylistVideoEntry. - - Needs authentication. - - Args: - playlist_uri: A string representing the URI of the playlist that contains - the entry to be updated. - playlist_entry_id: A string representing the ID of the entry to be - updated. - new_video_title: A string representing the new title for the video entry. - new_video_description: A string representing the new description for - the video entry. - new_video_position: An integer representing the new position on the - playlist for the video. - - Returns: - A YouTubePlaylistVideoEntry if the update was successful. - """ - playlist_video_entry = gdata.youtube.YouTubePlaylistVideoEntry( - title=atom.Title(text=new_video_title), - description=gdata.youtube.Description(text=new_video_description), - position=gdata.youtube.Position(text=str(new_video_position))) - - playlist_put_uri = playlist_uri + '/' + playlist_entry_id - - return self.Put(playlist_video_entry, playlist_put_uri, - converter=gdata.youtube.YouTubePlaylistVideoEntryFromString) - - def DeletePlaylistVideoEntry(self, playlist_uri, playlist_video_entry_id): - """Delete a playlist video entry from a playlist. - - Needs authentication. - - Args: - playlist_uri: A URI representing the playlist from which the playlist - video entry is to be removed from. - playlist_video_entry_id: A string representing id of the playlist video - entry that is to be removed. - - Returns: - True if entry was successfully deleted. - """ - delete_uri = '%s/%s' % (playlist_uri, playlist_video_entry_id) - return self.Delete(delete_uri) - - def AddSubscriptionToChannel(self, username_to_subscribe_to, - my_username = 'default'): - """Add a new channel subscription to the currently authenticated users - account. - - Needs authentication. - - Args: - username_to_subscribe_to: A string representing the username of the - channel to which we want to subscribe to. - my_username: An optional string representing the name of the user which - we want to subscribe. Defaults to currently authenticated user. - - Returns: - A new YouTubeSubscriptionEntry if successfully posted. - """ - subscription_category = atom.Category( - scheme=YOUTUBE_SUBSCRIPTION_CATEGORY_SCHEME, - term='channel') - subscription_username = gdata.youtube.Username( - text=username_to_subscribe_to) - - subscription_entry = gdata.youtube.YouTubeSubscriptionEntry( - category=subscription_category, - username=subscription_username) - - post_uri = '%s/%s/%s' % (YOUTUBE_USER_FEED_URI, my_username, - 'subscriptions') - - return self.Post(subscription_entry, post_uri, - converter=gdata.youtube.YouTubeSubscriptionEntryFromString) - - def AddSubscriptionToFavorites(self, username, my_username = 'default'): - """Add a new subscription to a users favorites to the currently - authenticated user's account. - - Needs authentication - - Args: - username: A string representing the username of the user's favorite feed - to subscribe to. - my_username: An optional string representing the username of the user - that is to be subscribed. Defaults to currently authenticated user. - - Returns: - A new YouTubeSubscriptionEntry if successful. - """ - subscription_category = atom.Category( - scheme=YOUTUBE_SUBSCRIPTION_CATEGORY_SCHEME, - term='favorites') - subscription_username = gdata.youtube.Username(text=username) - - subscription_entry = gdata.youtube.YouTubeSubscriptionEntry( - category=subscription_category, - username=subscription_username) - - post_uri = '%s/%s/%s' % (YOUTUBE_USER_FEED_URI, my_username, - 'subscriptions') - - return self.Post(subscription_entry, post_uri, - converter=gdata.youtube.YouTubeSubscriptionEntryFromString) - - def AddSubscriptionToQuery(self, query, my_username = 'default'): - """Add a new subscription to a specific keyword query to the currently - authenticated user's account. - - Needs authentication - - Args: - query: A string representing the keyword query to subscribe to. - my_username: An optional string representing the username of the user - that is to be subscribed. Defaults to currently authenticated user. - - Returns: - A new YouTubeSubscriptionEntry if successful. - """ - subscription_category = atom.Category( - scheme=YOUTUBE_SUBSCRIPTION_CATEGORY_SCHEME, - term='query') - subscription_query_string = gdata.youtube.QueryString(text=query) - - subscription_entry = gdata.youtube.YouTubeSubscriptionEntry( - category=subscription_category, - query_string=subscription_query_string) - - post_uri = '%s/%s/%s' % (YOUTUBE_USER_FEED_URI, my_username, - 'subscriptions') - - return self.Post(subscription_entry, post_uri, - converter=gdata.youtube.YouTubeSubscriptionEntryFromString) - - - - def DeleteSubscription(self, subscription_uri): - """Delete a subscription from the currently authenticated user's account. - - Needs authentication. - - Args: - subscription_uri: A string representing the URI of the subscription that - is to be deleted. - - Returns: - True if deleted successfully. - """ - return self.Delete(subscription_uri) - - def AddContact(self, contact_username, my_username='default'): - """Add a new contact to the currently authenticated user's contact feed. - - Needs authentication. - - Args: - contact_username: A string representing the username of the contact - that you wish to add. - my_username: An optional string representing the username to whose - contact the new contact is to be added. - - Returns: - A YouTubeContactEntry if added successfully. - """ - contact_category = atom.Category( - scheme = 'http://gdata.youtube.com/schemas/2007/contact.cat', - term = 'Friends') - contact_username = gdata.youtube.Username(text=contact_username) - contact_entry = gdata.youtube.YouTubeContactEntry( - category=contact_category, - username=contact_username) - - contact_post_uri = '%s/%s/%s' % (YOUTUBE_USER_FEED_URI, my_username, - 'contacts') - - return self.Post(contact_entry, contact_post_uri, - converter=gdata.youtube.YouTubeContactEntryFromString) - - def UpdateContact(self, contact_username, new_contact_status, - new_contact_category, my_username='default'): - """Update a contact, providing a new status and a new category. - - Needs authentication. - - Args: - contact_username: A string representing the username of the contact - that is to be updated. - new_contact_status: A string representing the new status of the contact. - This can either be set to 'accepted' or 'rejected'. - new_contact_category: A string representing the new category for the - contact, either 'Friends' or 'Family'. - my_username: An optional string representing the username of the user - whose contact feed we are modifying. Defaults to the currently - authenticated user. - - Returns: - A YouTubeContactEntry if updated succesfully. - - Raises: - YouTubeError: New contact status must be within the accepted values. Or - new contact category must be within the accepted categories. - """ - if new_contact_status not in YOUTUBE_CONTACT_STATUS: - raise YouTubeError('New contact status must be one of %s' % - (' '.join(YOUTUBE_CONTACT_STATUS))) - if new_contact_category not in YOUTUBE_CONTACT_CATEGORY: - raise YouTubeError('New contact category must be one of %s' % - (' '.join(YOUTUBE_CONTACT_CATEGORY))) - - contact_category = atom.Category( - scheme='http://gdata.youtube.com/schemas/2007/contact.cat', - term=new_contact_category) - - contact_status = gdata.youtube.Status(text=new_contact_status) - contact_entry = gdata.youtube.YouTubeContactEntry( - category=contact_category, - status=contact_status) - - contact_put_uri = '%s/%s/%s/%s' % (YOUTUBE_USER_FEED_URI, my_username, - 'contacts', contact_username) - - return self.Put(contact_entry, contact_put_uri, - converter=gdata.youtube.YouTubeContactEntryFromString) - - def DeleteContact(self, contact_username, my_username='default'): - """Delete a contact from a users contact feed. - - Needs authentication. - - Args: - contact_username: A string representing the username of the contact - that is to be deleted. - my_username: An optional string representing the username of the user's - contact feed from which to delete the contact. Defaults to the - currently authenticated user. - - Returns: - True if the contact was deleted successfully - """ - contact_edit_uri = '%s/%s/%s/%s' % (YOUTUBE_USER_FEED_URI, my_username, - 'contacts', contact_username) - return self.Delete(contact_edit_uri) - - def _GetDeveloperKey(self): - """Getter for Developer Key property. - - Returns: - If the developer key has been set, a string representing the developer key - is returned or None. - """ - if 'X-GData-Key' in self.additional_headers: - return self.additional_headers['X-GData-Key'][4:] - else: - return None - - def _SetDeveloperKey(self, developer_key): - """Setter for Developer Key property. - - Sets the developer key in the 'X-GData-Key' header. The actual value that - is set is 'key=' plus the developer_key that was passed. - """ - self.additional_headers['X-GData-Key'] = 'key=' + developer_key - - developer_key = property(_GetDeveloperKey, _SetDeveloperKey, - doc="""The Developer Key property""") - - def _GetClientId(self): - """Getter for Client Id property. - - Returns: - If the client_id has been set, a string representing it is returned - or None. - """ - if 'X-Gdata-Client' in self.additional_headers: - return self.additional_headers['X-Gdata-Client'] - else: - return None - - def _SetClientId(self, client_id): - """Setter for Client Id property. - - Sets the 'X-Gdata-Client' header. - """ - self.additional_headers['X-Gdata-Client'] = client_id - - client_id = property(_GetClientId, _SetClientId, - doc="""The ClientId property""") - - def Query(self, uri): - """Performs a query and returns a resulting feed or entry. - - Args: - uri: A string representing the URI of the feed that is to be queried. - - Returns: - On success, a tuple in the form: - (boolean succeeded=True, ElementTree._Element result) - On failure, a tuple in the form: - (boolean succeeded=False, {'status': HTTP status code from server, - 'reason': HTTP reason from the server, - 'body': HTTP body of the server's response}) - """ - result = self.Get(uri) - return result - - def YouTubeQuery(self, query): - """Performs a YouTube specific query and returns a resulting feed or entry. - - Args: - query: A Query object or one if its sub-classes (YouTubeVideoQuery, - YouTubeUserQuery or YouTubePlaylistQuery). - - Returns: - Depending on the type of Query object submitted returns either a - YouTubeVideoFeed, a YouTubeUserFeed, a YouTubePlaylistFeed. If the - Query object provided was not YouTube-related, a tuple is returned. - On success the tuple will be in this form: - (boolean succeeded=True, ElementTree._Element result) - On failure, the tuple will be in this form: - (boolean succeeded=False, {'status': HTTP status code from server, - 'reason': HTTP reason from the server, - 'body': HTTP body of the server response}) - """ - result = self.Query(query.ToUri()) - if isinstance(query, YouTubeVideoQuery): - return gdata.youtube.YouTubeVideoFeedFromString(result.ToString()) - elif isinstance(query, YouTubeUserQuery): - return gdata.youtube.YouTubeUserFeedFromString(result.ToString()) - elif isinstance(query, YouTubePlaylistQuery): - return gdata.youtube.YouTubePlaylistFeedFromString(result.ToString()) - else: - return result - -class YouTubeVideoQuery(gdata.service.Query): - - """Subclasses gdata.service.Query to represent a YouTube Data API query. - - Attributes are set dynamically via properties. Properties correspond to - the standard Google Data API query parameters with YouTube Data API - extensions. Please refer to the API documentation for details. - - Attributes: - vq: The vq parameter, which is only supported for video feeds, specifies a - search query term. Refer to API documentation for further details. - orderby: The orderby parameter, which is only supported for video feeds, - specifies the value that will be used to sort videos in the search - result set. Valid values for this parameter are relevance, published, - viewCount and rating. - time: The time parameter, which is only available for the top_rated, - top_favorites, most_viewed, most_discussed, most_linked and - most_responded standard feeds, restricts the search to videos uploaded - within the specified time. Valid values for this parameter are today - (1 day), this_week (7 days), this_month (1 month) and all_time. - The default value for this parameter is all_time. - format: The format parameter specifies that videos must be available in a - particular video format. Refer to the API documentation for details. - racy: The racy parameter allows a search result set to include restricted - content as well as standard content. Valid values for this parameter - are include and exclude. By default, restricted content is excluded. - lr: The lr parameter restricts the search to videos that have a title, - description or keywords in a specific language. Valid values for the lr - parameter are ISO 639-1 two-letter language codes. - restriction: The restriction parameter identifies the IP address that - should be used to filter videos that can only be played in specific - countries. - location: A string of geo coordinates. Note that this is not used when the - search is performed but rather to filter the returned videos for ones - that match to the location entered. - feed: str (optional) The base URL which is the beginning of the query URL. - defaults to 'http://%s/feeds/videos' % (YOUTUBE_SERVER) - """ - - def __init__(self, video_id=None, feed_type=None, text_query=None, - params=None, categories=None, feed=None): - - if feed_type in YOUTUBE_STANDARDFEEDS and feed is None: - feed = 'http://%s/feeds/standardfeeds/%s' % (YOUTUBE_SERVER, feed_type) - elif (feed_type is 'responses' or feed_type is 'comments' and video_id - and feed is None): - feed = 'http://%s/feeds/videos/%s/%s' % (YOUTUBE_SERVER, video_id, - feed_type) - elif feed is None: - feed = 'http://%s/feeds/videos' % (YOUTUBE_SERVER) - - gdata.service.Query.__init__(self, feed, text_query=text_query, - params=params, categories=categories) - - def _GetVideoQuery(self): - if 'vq' in self: - return self['vq'] - else: - return None - - def _SetVideoQuery(self, val): - self['vq'] = val - - vq = property(_GetVideoQuery, _SetVideoQuery, - doc="""The video query (vq) query parameter""") - - def _GetOrderBy(self): - if 'orderby' in self: - return self['orderby'] - else: - return None - - def _SetOrderBy(self, val): - if val not in YOUTUBE_QUERY_VALID_ORDERBY_PARAMETERS: - if val.startswith('relevance_lang_') is False: - raise YouTubeError('OrderBy must be one of: %s ' % - ' '.join(YOUTUBE_QUERY_VALID_ORDERBY_PARAMETERS)) - self['orderby'] = val - - orderby = property(_GetOrderBy, _SetOrderBy, - doc="""The orderby query parameter""") - - def _GetTime(self): - if 'time' in self: - return self['time'] - else: - return None - - def _SetTime(self, val): - if val not in YOUTUBE_QUERY_VALID_TIME_PARAMETERS: - raise YouTubeError('Time must be one of: %s ' % - ' '.join(YOUTUBE_QUERY_VALID_TIME_PARAMETERS)) - self['time'] = val - - time = property(_GetTime, _SetTime, - doc="""The time query parameter""") - - def _GetFormat(self): - if 'format' in self: - return self['format'] - else: - return None - - def _SetFormat(self, val): - if val not in YOUTUBE_QUERY_VALID_FORMAT_PARAMETERS: - raise YouTubeError('Format must be one of: %s ' % - ' '.join(YOUTUBE_QUERY_VALID_FORMAT_PARAMETERS)) - self['format'] = val - - format = property(_GetFormat, _SetFormat, - doc="""The format query parameter""") - - def _GetRacy(self): - if 'racy' in self: - return self['racy'] - else: - return None - - def _SetRacy(self, val): - if val not in YOUTUBE_QUERY_VALID_RACY_PARAMETERS: - raise YouTubeError('Racy must be one of: %s ' % - ' '.join(YOUTUBE_QUERY_VALID_RACY_PARAMETERS)) - self['racy'] = val - - racy = property(_GetRacy, _SetRacy, - doc="""The racy query parameter""") - - def _GetLanguageRestriction(self): - if 'lr' in self: - return self['lr'] - else: - return None - - def _SetLanguageRestriction(self, val): - self['lr'] = val - - lr = property(_GetLanguageRestriction, _SetLanguageRestriction, - doc="""The lr (language restriction) query parameter""") - - def _GetIPRestriction(self): - if 'restriction' in self: - return self['restriction'] - else: - return None - - def _SetIPRestriction(self, val): - self['restriction'] = val - - restriction = property(_GetIPRestriction, _SetIPRestriction, - doc="""The restriction query parameter""") - - def _GetLocation(self): - if 'location' in self: - return self['location'] - else: - return None - - def _SetLocation(self, val): - self['location'] = val - - location = property(_GetLocation, _SetLocation, - doc="""The location query parameter""") - - - -class YouTubeUserQuery(YouTubeVideoQuery): - - """Subclasses YouTubeVideoQuery to perform user-specific queries. - - Attributes are set dynamically via properties. Properties correspond to - the standard Google Data API query parameters with YouTube Data API - extensions. - """ - - def __init__(self, username=None, feed_type=None, subscription_id=None, - text_query=None, params=None, categories=None): - - uploads_favorites_playlists = ('uploads', 'favorites', 'playlists') - - if feed_type is 'subscriptions' and subscription_id and username: - feed = "http://%s/feeds/users/%s/%s/%s" % (YOUTUBE_SERVER, username, - feed_type, subscription_id) - elif feed_type is 'subscriptions' and not subscription_id and username: - feed = "http://%s/feeds/users/%s/%s" % (YOUTUBE_SERVER, username, - feed_type) - elif feed_type in uploads_favorites_playlists: - feed = "http://%s/feeds/users/%s/%s" % (YOUTUBE_SERVER, username, - feed_type) - else: - feed = "http://%s/feeds/users" % (YOUTUBE_SERVER) - - YouTubeVideoQuery.__init__(self, feed, text_query=text_query, - params=params, categories=categories) - - -class YouTubePlaylistQuery(YouTubeVideoQuery): - - """Subclasses YouTubeVideoQuery to perform playlist-specific queries. - - Attributes are set dynamically via properties. Properties correspond to - the standard Google Data API query parameters with YouTube Data API - extensions. - """ - - def __init__(self, playlist_id, text_query=None, params=None, - categories=None): - if playlist_id: - feed = "http://%s/feeds/playlists/%s" % (YOUTUBE_SERVER, playlist_id) - else: - feed = "http://%s/feeds/playlists" % (YOUTUBE_SERVER) - - YouTubeVideoQuery.__init__(self, feed, text_query=text_query, - params=params, categories=categories)