# -*- python -*- import dbagg, logging, iso8601, time, dbagg.web from spambayes import hammie import __main__ from __main__ import main_app DEFAULT_MAX_SOURCES = 200 DEFAULT_OFFSET_SOURCES = 0 DEFAULT_MAX_PER_SOURCE = 15 DEFAULT_MAX_AGE = 12.0 # DONE: Do not show already viewed items # DONE: Need a way to not show the description disclosure triangle if no description present # TODO: Consider iframe/css driven on-demand display of archived items # TODO: Display sources without new items, provide drill-down to archived items # TODO: Make Bayesian scores a part of a plugin, think about plugin contributions to item display # TODO: Think about factory scheme for item/source models for iterators, to allow plugin hooks class BayesItemModel(dbagg.web.ItemModel): def wmfactory_trained(self, request): trained = self.item.get_meta("bayes:trained") if not trained: trained = 'none' return trained def wmfactory_score(self, request): score = self.item.get_meta("bayes:score") if score: score = "%0.3f" % (float(score)) return self.default_blank(score) class BayesSourceIndexPage(dbagg.web.AggPage): templateFile = 'bysources_bayes.html.tmpl' def initialize(self, *args, **kwargs): # TODO: Use Twisted logging self.log = logging.getLogger("%s"%self.__class__) # TODO: Figure out how to access main_app cleanly without importing __main__ self.sources = main_app.getServiceNamed('sources') self.items = main_app.getServiceNamed('items') # TODO: Abstract iterator counting away so that this doesn't need a DB connection self.conn = __main__.conn # TODO: Bayes classifier belongs in a plugin self.classifier = __main__.classifier def wvupdate_item_title_truncate(self, request, widget, model): txt = widget.getData() if len(txt) > 100: txt = txt[:100] + "..." widget.clearNode = 1 widget.appendChild(request.d.createTextNode(txt)) def wmfactory_total_pages(self, request): max_per = int(self.wmfactory_max_sources(request)) return int(self.wmfactory_fsources_count(request)) / max_per def wmfactory_pages(self, request): max_per = int(self.wmfactory_max_sources(request)) pages = [] for n in xrange(self.wmfactory_total_pages(request)): pages.append({'page':n+1, 'offset':n*max_per}) return pages def wmfactory_max_sources(self, request): return int(request.args.get('max_sources', [DEFAULT_MAX_SOURCES])[0]) def wmfactory_offset_sources(self, request): return int(request.args.get('offset_sources', [DEFAULT_OFFSET_SOURCES])[0]) def wmfactory_max_per_source(self, request): return int(request.args.get('max_per_source', [DEFAULT_MAX_PER_SOURCE])[0]) def wmfactory_max_age(self, request): return float(request.args.get('max_age', [DEFAULT_MAX_AGE])[0]) def wmfactory_since(self, request): since = request.args.get('since', [None])[0] if since is not None: return since max_age = self.wmfactory_max_age(request) return iso8601.ctime(time.time() - (60*60*max_age)) def wmfactory_fsources_count(self, request): since = self.wmfactory_since(request) cursor = self.conn.cursor() cursor.execute( \ """ SELECT count(*) FROM sources_meta WHERE (name="last_updated" AND value > %s) """, ( since ) ); count = cursor.fetchone()[0] return count def wmfactory_fitems_count(self, request): since = self.wmfactory_since(request) cursor = self.conn.cursor() cursor.execute( \ """ SELECT count(items.id) FROM items LEFT JOIN items_meta AS b ON (b.item=items.id AND b.name="hidden") WHERE (b.value IS null OR b.value='0') AND items.created>%s """, ( since ) ); count = cursor.fetchone()[0] return count def wmfactory_fsources(self, request): max_sources = self.wmfactory_max_sources(request) offset_sources = self.wmfactory_offset_sources(request) max_per_source = self.wmfactory_max_per_source(request) since = self.wmfactory_since(request) # TODO: Handle limit and offset for sources def source_cmp(a,b): a_hits = int(a.get_meta('item_shown_at_count', 0)) + \ int(a.get_meta('item_visited_at_count', 0)) b_hits = int(b.get_meta('item_shown_at_count', 0)) + \ int(b.get_meta('item_visited_at_count', 0)) c = b_hits - a_hits if not c==0: return c else: a_updated = a.get_meta('last_updated', '') b_updated = b.get_meta('last_updated', '') return cmp(b, a) filtered_sources = filter(lambda x: x.get_meta('last_updated') > since, self.sources.__iter__()) filtered_sources.sort(source_cmp) filtered_sources = filtered_sources[int(offset_sources):int(offset_sources+max_sources)] pair_list = [] curr_source = None curr_pair = None # TODO: Needs to be an option whether shown/visited items are hidden for curr_source in filtered_sources: curr_max_per_source = int(curr_source.get_meta('max_items_displayed', max_per_source)) curr_pair = { 'source':dbagg.web.SourceModel(sources=self.sources, \ items=self.items, \ source=curr_source), 'items':[] } curr_items = self.items.sql_iter( \ """ SELECT * FROM items LEFT JOIN items_meta AS t_hidden ON (t_hidden.item=items.id AND t_hidden.name="hidden") LEFT JOIN items_meta AS t_shown_at ON (t_shown_at.item=items.id AND t_shown_at.name="shown_at") LEFT JOIN items_meta AS t_visited_at ON (t_visited_at.item=items.id AND t_visited_at.name="visited_at") LEFT JOIN items_meta AS t_read_at ON (t_read_at.item=items.id AND t_read_at.name="read_at") WHERE (source=%s AND created>%s) AND (t_hidden.value IS null OR t_hidden.value='0') AND (t_shown_at.value IS null) AND (t_visited_at.value IS null) AND (t_read_at.value IS null) ORDER BY items.created DESC LIMIT %s """, ( curr_source.id, since, curr_max_per_source)) #curr_items = filter(lambda x: x.created > since, # self.items.items_for_source(curr_source))\ # [:curr_max_per_source] for curr_item in curr_items: curr_pair['items'].append( BayesItemModel(item=curr_item, sources=self.sources)) if len(curr_pair['items']) > 0: pair_list.append(curr_pair) return pair_list ################################################################################ resource = BayesSourceIndexPage()