source: LinkExchange/trunk/linkexchange/platform.py @ 175

Revision 175, 7.1 KB checked in by lostclus, 13 months ago (diff)

Clients doctests and Platform doctests rewritten to use the unittest framework, and placed in tests/test_client.py and tests/test_platform.py respectively. Changes in SapeArticlesClient?: allow template URL refer to local file only if file is located in temporary directory; don't try to insert check code or sape_noindex tag if check code is not defined; use new parse_param() method when parsing data.

Line 
1# LinkExchange - Universal link exchange service client
2# Copyright (C) 2009 Konstantin Korikov
3#
4# This library is free software; you can redistribute it and/or
5# modify it under the terms of the GNU Lesser General Public
6# License as published by the Free Software Foundation; either
7# version 2.1 of the License, or (at your option) any later version.
8#
9# This library is distributed in the hope that it will be useful,
10# but WITHOUT ANY WARRANTY; without even the implied warranty of
11# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12# Lesser General Public License for more details.
13#
14# You should have received a copy of the GNU Lesser General Public
15# License along with this library; if not, write to the Free Software
16# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
17#
18# NOTE: In the context of the Python environment, I interpret "dynamic
19# linking" as importing -- thus the LGPL applies to the contents of
20# the modules, but make no requirements on code importing these
21# modules.
22
23try:
24    set
25except NameError:
26    from sets import Set as set
27
28from linkexchange.utils import is_plugin_specifier, load_plugin
29from linkexchange.clients.base import ClientError, PageRequest, PageResponse
30
31class RawLink(object):
32    def __init__(self, link_code, client, client_no):
33        self.link_code = link_code
34        self.client = client
35        self.client_no = client_no
36
37    def __unicode__(self):
38        return self.link_code
39
40class Platform(object):
41    """
42    Platform combines various clients into one object. It have methods to get
43    total list of raw links, to get links divided by blocks with custom
44    formatting rules.
45    """
46
47    def __init__(self, clients = None):
48        """
49        @param clients: list of clients instances or clients specifiers
50
51        """
52        self.clients = []
53        if clients:
54            for cl in clients:
55                self.add_client(cl)
56
57    def add_client(self, client):
58        """
59        Add link exchange service client to the platform.
60
61        The client parameter is instance of BaseClient or client specifier.
62        Client specifier is (name, args, kwargs) tuple where name is client
63        name (link exchange service name), args and kwargs is list of
64        positional arguments and dictionary of keyword arguments for the client
65        constructor respectively.
66
67        @param client: instance of BaseClient or client specifier
68        """
69        if is_plugin_specifier(client):
70            client = load_plugin('linkexchange.clients', client)
71        self.clients.append(client)
72
73    def get_raw_links(self, request):
74        """
75        Returns list of links to place on page identified by request. Catches
76        clients errors and use HTML comments for errors reporting.
77
78        @param request: PageRequest object or URL string
79        @return: list of links as RawLink objects
80        """
81        if isinstance(request, basestring):
82            request = PageRequest(url = request)
83        result = []
84        cl_no = 0
85        for cl in self.clients:
86            cl_no += 1
87            try:
88                links = cl.get_raw_links(request)
89            except ClientError, e:
90                links = [u'<!-- %s -->' % str(e)]
91            raw_links = [RawLink(link_code=l,
92                client=cl, client_no=cl_no) for l in links]
93            result.extend(raw_links)
94        return result
95
96    def get_blocks(self, request, formatters):
97        """
98        Returns links grouped by blocks. Catches clients errors and use HTML
99        comments for errors reporting.
100
101        The request parameter is a PageRequest object or URL string. The
102        formatters parameter is a sequence of BaseFormatter instances. The
103        result is list of unicode strings with HTML formatted blocks of links.
104
105        @param request: PageRequest object or URL string
106        @param formatters: sequence of blocks formatters
107        @return: list of links blocks as unicode strings
108        """
109        def load_formatter(fmt):
110            if is_plugin_specifier(fmt):
111                return load_plugin('linkexchange.formatters', fmt)
112            return fmt
113
114        def allocate_link(link, tag_list, link_list):
115            link = unicode(link)
116            if '<a ' in link:
117                link_list.append(link)
118            else:
119                tag_list.append(link)
120
121        formatters = map(load_formatter, formatters)
122        links_pool = self.get_raw_links(request)
123        blocks = [None for fmt in formatters]
124
125        for i in range(len(formatters)):
126            fmt = formatters[i]
127            if fmt.client is None:
128                continue
129            if isinstance(fmt.client, basestring):
130                client_no_set = set([int(x.strip())
131                    for x in fmt.client.split(',')])
132            else:
133                client_no_set = set([fmt.client])
134            tag_list = []
135            link_list = []
136            j = 0
137            while j < len(links_pool) and ((fmt.count and
138                len(link_list) < fmt.count) or (not fmt.count)):
139                if links_pool[j].client_no in client_no_set:
140                    allocate_link(links_pool.pop(j), tag_list, link_list)
141                    continue
142                j += 1
143            blocks[i] = fmt.format(tag_list, link_list)
144
145        for i in range(len(formatters)):
146            if blocks[i] is not None:
147                continue
148            fmt = formatters[i]
149            tag_list = []
150            link_list = []
151            while links_pool and ((fmt.count and
152                len(link_list) < fmt.count) or (not fmt.count)):
153                allocate_link(links_pool.pop(0), tag_list, link_list)
154            blocks[i] = fmt.format(tag_list, link_list)
155
156        return blocks
157
158    def content_filter(self, request, content):
159        """
160        @param request: PageRequest object or URL string
161        @param content: HTML content (full page or fragment) as unicode string
162        @return: filtered content as unicode string
163        """
164        if isinstance(request, basestring):
165            request = PageRequest(url = request)
166        for cl in self.clients:
167            try:
168                content = cl.content_filter(request, content)
169            except ClientError, e:
170                pass
171        return content
172
173    def handle_request(self, request):
174        """
175        @param request: PageRequest object or URL string
176        @return: PageResponse object
177        """
178        if isinstance(request, basestring):
179            request = PageRequest(url=request)
180        for cl in self.clients:
181            try:
182                response = cl.handle_request(request)
183            except ClientError, e:
184                continue
185            if response.status != 404:
186                return response
187        return PageResponse(status=404)
188
189    def refresh_db(self, request):
190        """
191        Force to refresh clients databases.
192
193        @param request: PageRequest object or URL string
194        """
195        if isinstance(request, basestring):
196            request = PageRequest(url = request)
197        for cl in self.clients:
198            try:
199                cl.refresh_db(request)
200            except ClientError, e:
201                pass
202
203if __name__ == "__main__":
204    import doctest
205    doctest.testmod()
Note: See TracBrowser for help on using the repository browser.