Newb Help!

Hey guys,

 

Decided to dive head first into channel development for something new to learn, and I've very quickly found myself overwhelmed.   :(  I am using a relatively easy video streaming website as my first project.  (I say easy because direct video links are available from the html and I've read that makes it "easy" lol).  While I have a basic understanding of Python, I'm attempting to learn about xpath etc as I go.

 

I know its probably me doing something retarded, but can someone take a quick look at this code and explain to me what I'm doing wrong.  The idea here is that I load the page, use the xpath query to create a list (I've created/tested the xpath query with a chrome plugin), and then display that list as my menu.

 

def MainMenu():
	oc = ObjectContainer()
	search_page = XML.ElementFromURL("http://kissanime.com/AnimeList/MostPopular")
	item = search_page.xpath("//table[@class='listing']/tbody/tr/td[1]")
	for each in item:
		oc.Append(each)
return oc

 

However, I immediately get this error from the log:

 

013-08-08 18:43:13,974 (1820) :  INFO (core:598) - Started plug-in
2013-08-08 18:43:13,974 (1820) :  DEBUG (socketinterface:144) - Starting socket server
2013-08-08 18:43:13,976 (1820) :  DEBUG (runtime:1104) - Created a thread named 'start'
2013-08-08 18:43:13,976 (1820) :  INFO (socketinterface:168) - Socket server started on port 63762
2013-08-08 18:43:13,976 (1820) :  INFO (pipeinterface:25) - Entering run loop
2013-08-08 18:43:13,976 (1820) :  DEBUG (runtime:714) - Handling request GET /:/prefixes
2013-08-08 18:43:13,977 (1820) :  DEBUG (runtime:811) - Found route matching /:/prefixes
2013-08-08 18:43:14,059 (658) :  DEBUG (services:362) - Loaded services
2013-08-08 18:43:14,062 (1820) :  DEBUG (runtime:911) - Response: [200] MediaContainer, 455 bytes
2013-08-08 18:43:14,065 (8c4) :  DEBUG (services:438) - No shared code to load
2013-08-08 18:43:14,118 (1a94) :  DEBUG (runtime:714) - Handling request GET /video/kissanime
2013-08-08 18:43:14,119 (1a94) :  DEBUG (runtime:811) - Found route matching /video/kissanime
2013-08-08 18:43:14,121 (1a94) :  DEBUG (networking:172) - Requesting 'http://kissanime.com/AnimeList/MostPopular'
2013-08-08 18:43:14,789 (1a94) :  CRITICAL (core:561) - Exception (most recent call last):
  File "C:\Users\Jamieson\AppData\Local\Plex Media Server\Plug-ins\Framework.bundle\Contents\Resources\Versions\2\Python\Framework\components\runtime.py", line 837, in handle_request
    result = f(**d)
  File "C:\Users\Jamieson\AppData\Local\Plex Media Server\Plug-ins\Framework.bundle\Contents\Resources\Versions\2\Python\Framework\handlers\base.py", line 119, in call
    result = self.func(*args, **kwargs)
  File "C:\Users\Jamieson\AppData\Local\Plex Media Server\Plug-ins\KissAnime.bundle\Contents\Code\__init__.py", line 30, in MainMenu
    search_page = XML.ElementFromURL("http://kissanime.com/AnimeList/MostPopular")
  File "C:\Users\Jamieson\AppData\Local\Plex Media Server\Plug-ins\Framework.bundle\Contents\Resources\Versions\2\Python\Framework\api\parsekit.py", line 345, in ElementFromURL
    ).content, encoding=encoding, max_size=max_size)
  File "C:\Users\Jamieson\AppData\Local\Plex Media Server\Plug-ins\Framework.bundle\Contents\Resources\Versions\2\Python\Framework\api\parsekit.py", line 301, in ElementFromString
    return self._core.data.xml.from_string(string, encoding = encoding)
  File "C:\Users\Jamieson\AppData\Local\Plex Media Server\Plug-ins\Framework.bundle\Contents\Resources\Versions\2\Python\Framework\components\data.py", line 169, in from_string
    return etree.fromstring(markup)
  File "lxml.etree.pyx", line 2743, in lxml.etree.fromstring (..\src\lxml\lxml.etree.c:52665)
  File "parser.pxi", line 1573, in lxml.etree._parseMemoryDocument (..\src\lxml\lxml.etree.c:79932)
  File "parser.pxi", line 1452, in lxml.etree._parseDoc (..\src\lxml\lxml.etree.c:78774)
  File "parser.pxi", line 960, in lxml.etree._BaseParser._parseDoc (..\src\lxml\lxml.etree.c:75389)
  File "parser.pxi", line 564, in lxml.etree._ParserContext._handleParseResultDoc (..\src\lxml\lxml.etree.c:71739)
  File "parser.pxi", line 645, in lxml.etree._handleParseResult (..\src\lxml\lxml.etree.c:72614)
  File "parser.pxi", line 585, in lxml.etree._raiseParseError (..\src\lxml\lxml.etree.c:71955)
XMLSyntaxError: Opening and ending tag mismatch: link line 18 and head, line 40, column 8

2013-08-08 18:43:14,795 (1a94) : DEBUG (runtime:911) - Response: [500] 2336 bytes

 

Am I not using the API correctly?

 

Cheers in advance,

Jamie

First, congrats on deciding to dive in to channel development :slight_smile:

Second, the first error I see is that you’re trying to parse a HTML page as XML. While they are similar, XML is much more strict than HTML. You’d be better off using HTML.ElementFromURL(). From our viewpoint it behaves pretty much the same and you can execute xpath queries on the result just as easily. Underneath, the parser is entirely different.

Third, it looks like you’re trying to add the xpath elements directly to your ObjectContainer. That won’t work even once the you’ve implemented the HTML.ElementFromURL(). You need to build objects to add to your ObjectContainer. In your case, the standard VideoClipObject should work just fine. You can read more about that here, http://dev.plexapp.com/docs/api/objectkit.html#VideoClipObject

Thanks for the quick reply!  I initially tried HTML.ElementFromURL() which produced a similar error, prompting me to try XML on a whim.  I'll fix that up.  I think I understand the concept of build a VideoClipObject from pulling apart examples and other channels, however the data I am trying to pass in this instance, is actually supposed to be used for top level navigation.  The idea being that once selected, these items will in turn lead to a list of VideoClipOpjects.  I can see from the documentation that I should be using DirectoryObjects for this.  Is there a way to store a url with the data in my DirectoryObject?  

For example: my xpath query currently pulls a list of show names.  What I am thinking is that I need to write a function to store a corresponding url with each show name so that when the user selects a DirectoryObject, I can pass the correct url to another function to process for VideoClipObjects.  Am I on the right track?  How do I store a url with my DirectoryObject?  I don't see url as arg/parameter/whatever in the documentation for DirectoryObject.

Thanks again for your help!

When you create your DirectoryObject, the normal procedure is to make the key of the object a Callback to a function that you define. In your function definition, you can decide what arguments (mandatory or optional) to pass in.



For example:

...
    oc.add(DirectoryObject(key=Callback(VideoMenu, url=url_variable), title=menu_title))
...

def VideoMenu(url):
  page_data = HTML.ElementFromURL(url)
  ...

Awesome. That makes sense. Sorry to be a pain and ask so many questions but I'm stuck yet again lol.

So here is what I've got now. I no longer get the api errors in the log. However I don't exactly get the desired results either. I've setup my top level navigation with the function MostPopular and moved the code to call the Directory Objects to there, however when executing the plugin, I don't get any results.

EDIT: After a little debugging, I've worked out that my lists that are supposed to be populated by the xpath queries are empty and this is where the error lies. I think I'm doing them right? Is there a better way to test xpath queries other than the chrome plugin I'm using?

Heres my new code:
 

def MostPopular():
	oc = ObjectContainer()
	search_page = HTML.ElementFromURL("http://kissanime.com/AnimeList/MostPopular/")
	showtitle = search_page.xpath("//table/tbody/tr/td[1]/a/text()")
	showurl = search_page.xpath("//table[@class='listing']/tbody/tr/td/a[not(contains(text(), 'Episode'))]/@href")
Log (search_page)	
Log (showtitle)
Log (showurl)

count = 0
for each in showtitle:
	each.strip()
	oc.add(DirectoryObject(
			key=Callback(ListEpisodes,url),
			title=each
		)
	)
	url=showurl[count]
	count = count + 1

if len(oc) < 1:
	Log ('still no value for objects')
	return ObjectContainer(header="Error", message="Something has gone horribly wrong...")  
return oc

And the new log:
 

2013-08-09 18:06:56,466 (1530) :  INFO (core:598) - Started plug-in
2013-08-09 18:06:56,466 (1530) :  DEBUG (socketinterface:144) - Starting socket server
2013-08-09 18:06:56,467 (1530) :  DEBUG (runtime:1104) - Created a thread named 'start'
2013-08-09 18:06:56,467 (1530) :  INFO (socketinterface:168) - Socket server started on port 52538
2013-08-09 18:06:56,467 (1530) :  INFO (pipeinterface:25) - Entering run loop
2013-08-09 18:06:56,467 (1530) :  DEBUG (runtime:714) - Handling request GET /:/prefixes
2013-08-09 18:06:56,469 (1530) :  DEBUG (runtime:811) - Found route matching /:/prefixes
2013-08-09 18:06:56,548 (1814) :  DEBUG (services:362) - Loaded services
2013-08-09 18:06:56,551 (1530) :  DEBUG (runtime:911) - Response: [200] MediaContainer, 455 bytes
2013-08-09 18:06:56,552 (1164) :  DEBUG (services:438) - No shared code to load
2013-08-09 18:06:56,582 (ff8) :  DEBUG (runtime:714) - Handling request GET /video/kissanime
2013-08-09 18:06:56,585 (ff8) :  DEBUG (runtime:811) - Found route matching /video/kissanime
2013-08-09 18:06:56,585 (ff8) :  WARNING (runtime:1052) - Generating a callback path for a function with no route: 
2013-08-09 18:06:56,585 (ff8) :  DEBUG (base:123) - Checking if com.plexapp.plugins.kissanime is broken
2013-08-09 18:06:56,585 (ff8) :  DEBUG (networking:172) - Requesting 'http://127.0.0.1:32400/:/plugins/com.plexapp.system/messaging/function/X1N0b3JlU2VydmljZTpJc0NoYW5uZWxCcm9rZW4_/Y2VyZWFsMQoxCmxpc3QKMApyMAo_/Y2VyZWFsMQoxCmRpY3QKMQpzMjkKY29tLnBsZXhhcHAucGx1Z2lucy5raXNzYW5pbWVzMTAKaWRlbnRpZmllcnIwCg__'
2013-08-09 18:06:56,601 (ff8) :  DEBUG (runtime:911) - Response: [200] MediaContainer, 606 bytes
2013-08-09 18:06:58,463 (94c) :  DEBUG (runtime:714) - Handling request GET /video/kissanime/:/function/MostPopular?function_args=Y2VyZWFsMQoxCmRpY3QKMApyMAo_
2013-08-09 18:06:58,473 (94c) :  DEBUG (runtime:811) - Found route matching /video/kissanime/:/function/MostPopular
2013-08-09 18:06:58,474 (94c) :  DEBUG (runtime:143) - Calling function 'MostPopular'
2013-08-09 18:06:58,474 (94c) :  DEBUG (networking:172) - Requesting 'http://kissanime.com/AnimeList/MostPopular/'
2013-08-09 18:06:59,523 (94c) :  INFO (logkit:16) - 
2013-08-09 18:06:59,523 (94c) :  INFO (logkit:16) - []
2013-08-09 18:06:59,523 (94c) :  INFO (logkit:16) - []
2013-08-09 18:06:59,523 (94c) :  INFO (logkit:16) - still no value for objects
2013-08-09 18:06:59,523 (94c) :  DEBUG (runtime:911) - Response: [200] MediaContainer, 351 bytes

Further to this, it appears my xpath queries are working (albeit with a fair amount of whitespace).  I just cant understand why this isn't translating to a list under the variables in my plugin.   :wacko:

Hi TehCrucible!

I'm no expert in xpath expressions so if you're looking for an explanation someone else have to comment.

I think this works for what you're trying to accomplish though:

    search_page = HTML.ElementFromURL("http://kissanime.com/AnimeList/MostPopular/")

    for item in search_page.xpath("//table[@class='listing']//td//a"):
        showurl   = item.xpath("./@href")[0]
        showtitle = item.xpath("./text()")[0].strip()
        
        if showurl.count("/") <= 2:
            oc.add(
               ...
            ) 

Well thats certainly alot cleaner than my sloppy work but unfortunately still not quite there.  I'm now getting a "list index is out of range" error from the lines that set the "showurl" and "showtitle" variable, which would lead you to believe my lists still aren't poplulating right?  EXCEPT, out of curiosity, I removed the [0] indices from both lines and the code does generate 60 (blank) directory objects:  which matches the number of results from the xpath query.  (Well... technically it should be 50 but thats what the "if showurl.count..." statement is for, yeah?)

Really weird.  But getting closer.  If I log the variables without the indices, it still shows a blank list though....

def MostPopular(title):

	oc = ObjectContainer(title1=title)
	data = HTML.ElementFromURL("http://kissanime.com/AnimeList/MostPopular/")
	
	for each in data.xpath("//table[@class='listing']//td//a"):
		showurl   = data.xpath("./@href")[0]
		showtitle = data.xpath("./text()")[0].strip
		Log (showurl)
		Log (showtitle)
		
		if showurl.count("/") <= 2:
			oc.add(DirectoryObject(key = Callback(ListEpisodes, url = showurl), title = showtitle, ))

	if len(oc) < 1:
		Log ('STILL no value for objects')
		return ObjectContainer(header="Error", message="Something has gone horribly wrong...")  
	
	return oc
2013-08-10 07:42:03,167 (1860) :  INFO (core:598) - Started plug-in
2013-08-10 07:42:03,167 (1860) :  DEBUG (socketinterface:144) - Starting socket server
2013-08-10 07:42:03,168 (1860) :  DEBUG (runtime:1104) - Created a thread named 'start'
2013-08-10 07:42:03,168 (1860) :  INFO (socketinterface:168) - Socket server started on port 63008
2013-08-10 07:42:03,170 (1860) :  INFO (pipeinterface:25) - Entering run loop
2013-08-10 07:42:03,170 (1860) :  DEBUG (runtime:714) - Handling request GET /:/prefixes
2013-08-10 07:42:03,171 (1860) :  DEBUG (runtime:811) - Found route matching /:/prefixes
2013-08-10 07:42:03,250 (164c) :  DEBUG (services:362) - Loaded services
2013-08-10 07:42:03,253 (1860) :  DEBUG (runtime:911) - Response: [200] MediaContainer, 455 bytes
2013-08-10 07:42:03,256 (1b14) :  DEBUG (services:438) - No shared code to load
2013-08-10 07:42:15,535 (61c) :  DEBUG (runtime:714) - Handling request GET /video/kissanime
2013-08-10 07:42:15,536 (61c) :  DEBUG (runtime:811) - Found route matching /video/kissanime
2013-08-10 07:42:15,536 (61c) :  WARNING (runtime:1052) - Generating a callback path for a function with no route: 
2013-08-10 07:42:15,536 (61c) :  WARNING (runtime:1052) - Generating a callback path for a function with no route: 
2013-08-10 07:42:15,536 (61c) :  DEBUG (base:123) - Checking if com.plexapp.plugins.kissanime is broken
2013-08-10 07:42:15,538 (61c) :  DEBUG (networking:172) - Requesting 'http://127.0.0.1:32400/:/plugins/com.plexapp.system/messaging/function/X1N0b3JlU2VydmljZTpJc0NoYW5uZWxCcm9rZW4_/Y2VyZWFsMQoxCmxpc3QKMApyMAo_/Y2VyZWFsMQoxCmRpY3QKMQpzMjkKY29tLnBsZXhhcHAucGx1Z2lucy5raXNzYW5pbWVzMTAKaWRlbnRpZmllcnIwCg__'
2013-08-10 07:42:15,553 (61c) :  DEBUG (runtime:911) - Response: [200] MediaContainer, 962 bytes
2013-08-10 07:42:16,283 (15e0) :  DEBUG (runtime:714) - Handling request GET /video/kissanime/:/function/MostPopular?function_args=Y2VyZWFsMQoxCmRpY3QKMQpzMTIKTW9zdCBQb3B1bGFyczUKdGl0bGVyMAo_
2013-08-10 07:42:16,295 (15e0) :  DEBUG (runtime:811) - Found route matching /video/kissanime/:/function/MostPopular
2013-08-10 07:42:16,295 (15e0) :  DEBUG (runtime:143) - Calling function 'MostPopular'
2013-08-10 07:42:16,305 (15e0) :  DEBUG (networking:172) - Requesting 'http://kissanime.com/AnimeList/MostPopular/'
2013-08-10 07:42:17,915 (15e0) :  CRITICAL (core:561) - Exception when calling function 'MostPopular' (most recent call last):
  File "C:\Users\Jamieson\AppData\Local\Plex Media Server\Plug-ins\Framework.bundle\Contents\Resources\Versions\2\Python\Framework\code\sandbox.py", line 294, in call_named_function
    result = f(*args, **kwargs)
  File "C:\Users\Jamieson\AppData\Local\Plex Media Server\Plug-ins\KissAnime.bundle\Contents\Code\__init__.py", line 41, in MostPopular
    showurl   = data.xpath("./@href")[0]
  File "C:\Users\Jamieson\AppData\Local\Plex Media Server\Plug-ins\Framework.bundle\Contents\Resources\Versions\2\Python\Framework\code\sandbox.py", line 110, in 
    _getitem_           = lambda x, y: x.__getitem__(y),
IndexError: list index out of range

2013-08-10 07:42:17,917 (15e0) :  DEBUG (runtime:911) - Response: [404] NoneType, 0 bytes

SUCCESS!!

While it's not quite as elegant as what you suggested, meo, this code works as desired.  I'll mark this thread as solved and post more specific questions in the future if I get stuck with something else.  Thanks a million guys.

def MostPopular(title):

	oc = ObjectContainer(title1=title)
	data = HTML.ElementFromURL("http://kissanime.com/AnimeList/MostPopular/")
	count = 0
	
	Log (data.xpath("//table[@class='listing']//td//a/text()")[0].strip())
	
	for each in data.xpath("//table[@class='listing']//td//a"):
		showurl = data.xpath("//table[@class='listing']//td//a/@href")[count]
		showtitle = data.xpath("//table[@class='listing']//td//a/text()")[count].strip()
		Log (showurl)
		Log (showtitle)
		count = count + 1
		
		if showurl.count("/") <= 2:
			oc.add(DirectoryObject(key = Callback(ListEpisodes, url = showurl), title = showtitle, ))

	if len(oc) < 1:
		Log ("STILL no value for objects")
		return ObjectContainer(header="Error", message="Something has gone horribly wrong...")  
	
	return oc

Sorry, my bad, I've changed the name of one of the variables

Well thats certainly alot cleaner than my sloppy work but unfortunately still not quite there.  I'm now getting a "list index is out of range" error from the lines that set the "showurl" and "showtitle" variable, which would lead you to believe my lists still aren't poplulating right?  EXCEPT, out of curiosity, I removed the [0] indices from both lines and the code does generate 60 (blank) directory objects:  which matches the number of results from the xpath query.  (Well... technically it should be 50 but thats what the "if showurl.count..." statement is for, yeah?)

Really weird.  But getting closer.  If I log the variables without the indices, it still shows a blank list though....

def MostPopular(title):

	oc = ObjectContainer(title1=title)
	data = HTML.ElementFromURL("http://kissanime.com/AnimeList/MostPopular/")
	
	for each in data.xpath("//table[@class='listing']//td//a"):
		showurl   = data.xpath("./@href")[0]
		showtitle = data.xpath("./text()")[0].strip
		Log (showurl)
		Log (showtitle)
		
		if showurl.count("/") <= 2:
			oc.add(DirectoryObject(key = Callback(ListEpisodes, url = showurl), title = showtitle, ))

	if len(oc) < 1:
		Log ('STILL no value for objects')
		return ObjectContainer(header="Error", message="Something has gone horribly wrong...")  
	
	return oc
2013-08-10 07:42:03,167 (1860) :  INFO (core:598) - Started plug-in
2013-08-10 07:42:03,167 (1860) :  DEBUG (socketinterface:144) - Starting socket server
2013-08-10 07:42:03,168 (1860) :  DEBUG (runtime:1104) - Created a thread named 'start'
2013-08-10 07:42:03,168 (1860) :  INFO (socketinterface:168) - Socket server started on port 63008
2013-08-10 07:42:03,170 (1860) :  INFO (pipeinterface:25) - Entering run loop
2013-08-10 07:42:03,170 (1860) :  DEBUG (runtime:714) - Handling request GET /:/prefixes
2013-08-10 07:42:03,171 (1860) :  DEBUG (runtime:811) - Found route matching /:/prefixes
2013-08-10 07:42:03,250 (164c) :  DEBUG (services:362) - Loaded services
2013-08-10 07:42:03,253 (1860) :  DEBUG (runtime:911) - Response: [200] MediaContainer, 455 bytes
2013-08-10 07:42:03,256 (1b14) :  DEBUG (services:438) - No shared code to load
2013-08-10 07:42:15,535 (61c) :  DEBUG (runtime:714) - Handling request GET /video/kissanime
2013-08-10 07:42:15,536 (61c) :  DEBUG (runtime:811) - Found route matching /video/kissanime
2013-08-10 07:42:15,536 (61c) :  WARNING (runtime:1052) - Generating a callback path for a function with no route: 
2013-08-10 07:42:15,536 (61c) :  WARNING (runtime:1052) - Generating a callback path for a function with no route: 
2013-08-10 07:42:15,536 (61c) :  DEBUG (base:123) - Checking if com.plexapp.plugins.kissanime is broken
2013-08-10 07:42:15,538 (61c) :  DEBUG (networking:172) - Requesting 'http://127.0.0.1:32400/:/plugins/com.plexapp.system/messaging/function/X1N0b3JlU2VydmljZTpJc0NoYW5uZWxCcm9rZW4_/Y2VyZWFsMQoxCmxpc3QKMApyMAo_/Y2VyZWFsMQoxCmRpY3QKMQpzMjkKY29tLnBsZXhhcHAucGx1Z2lucy5raXNzYW5pbWVzMTAKaWRlbnRpZmllcnIwCg__'
2013-08-10 07:42:15,553 (61c) :  DEBUG (runtime:911) - Response: [200] MediaContainer, 962 bytes
2013-08-10 07:42:16,283 (15e0) :  DEBUG (runtime:714) - Handling request GET /video/kissanime/:/function/MostPopular?function_args=Y2VyZWFsMQoxCmRpY3QKMQpzMTIKTW9zdCBQb3B1bGFyczUKdGl0bGVyMAo_
2013-08-10 07:42:16,295 (15e0) :  DEBUG (runtime:811) - Found route matching /video/kissanime/:/function/MostPopular
2013-08-10 07:42:16,295 (15e0) :  DEBUG (runtime:143) - Calling function 'MostPopular'
2013-08-10 07:42:16,305 (15e0) :  DEBUG (networking:172) - Requesting 'http://kissanime.com/AnimeList/MostPopular/'
2013-08-10 07:42:17,915 (15e0) :  CRITICAL (core:561) - Exception when calling function 'MostPopular' (most recent call last):
  File "C:\Users\Jamieson\AppData\Local\Plex Media Server\Plug-ins\Framework.bundle\Contents\Resources\Versions\2\Python\Framework\code\sandbox.py", line 294, in call_named_function
    result = f(*args, **kwargs)
  File "C:\Users\Jamieson\AppData\Local\Plex Media Server\Plug-ins\KissAnime.bundle\Contents\Code\__init__.py", line 41, in MostPopular
    showurl   = data.xpath("./@href")[0]
  File "C:\Users\Jamieson\AppData\Local\Plex Media Server\Plug-ins\Framework.bundle\Contents\Resources\Versions\2\Python\Framework\code\sandbox.py", line 110, in 
    _getitem_           = lambda x, y: x.__getitem__(y),
IndexError: list index out of range

2013-08-10 07:42:17,917 (15e0) :  DEBUG (runtime:911) - Response: [404] NoneType, 0 bytes

Sorry, my bad. I've changed the name of one of the variables and that's why it is not working. Try this:

def MostPopular(title):

	oc = ObjectContainer(title1=title)
	data = HTML.ElementFromURL("http://kissanime.com/AnimeList/MostPopular/")
	
	for each in data.xpath("//table[@class='listing']//td//a"):
		showurl   = each.xpath("./@href")[0]
		showtitle = each.xpath("./text()")[0].strip
		Log (showurl)
		Log (showtitle)
		
		if showurl.count("/") <= 2:
			oc.add(DirectoryObject(key = Callback(ListEpisodes, url = showurl), title = showtitle, ))

	if len(oc) < 1:
		Log ('STILL no value for objects')
		return ObjectContainer(header="Error", message="Something has gone horribly wrong...")  
	
	return oc

i.e. the above code change is only to change name of "data" to "each" inside the for loop

Thanks mate.  Works great and cleans up the code nicely.   :)

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.