Index: applications/mobileblur/controllers/default.py ================================================================== --- applications/mobileblur/controllers/default.py +++ applications/mobileblur/controllers/default.py @@ -21,11 +21,18 @@ if login_form.accepts(request): try: results = newsblur.login(login_form.vars["username"], login_form.vars["password"]) response.cookies["nb_cookie"] = newsblur.cookies["newsblur_sessionid"] response.cookies["nb_cookie"]["path"] = "/" - print "cookie =", newsblur.cookies redirect(URL("index")) except Exception as ex: login_form.insert(-1, ex.message) + login_form._class = "alert-message block-message error" return dict(login_form=login_form) + + +def logout(): + response.cookies["nb_cookie"] = "" + response.cookies["nb_cookie"]["expires"] = -10 + response.cookies["nb_cookie"]["path"] = "/" + redirect(URL("index")) Index: applications/mobileblur/controllers/feeds.py ================================================================== --- applications/mobileblur/controllers/feeds.py +++ applications/mobileblur/controllers/feeds.py @@ -1,13 +1,32 @@ # -*- coding: utf-8 -*- from pprint import pprint +import time def view(): - stories = newsblur.feed(request.args[0])["stories"] - feeds = newsblur.feeds(flat=True)["feeds"] - feed = [feed for feed in feeds.itervalues() if feed["id"]==int(request.args[0])][0] + print "" + s = time.time() + feed = newsblur.feed(request.args[0]) + stories = feed["stories"] + print time.time() - s + + print feed.keys() + + if not feed.has_key("feed_title"): + s = time.time() + feeds = newsblur.feeds(flat=True)["feeds"] + print time.time() - s + + s = time.time() + feed = [feed for feed in feeds.itervalues() if feed["id"]==int(request.args[0])][0] + print time.time() - s + + response.title = feed["feed_title"] + return dict(stories=stories, feed=feed) + def mark_read(): - newsblur.mark_feed_as_read(request.vars["feed"]) + if len(request.args) > 0: + newsblur.mark_feed_as_read(request.args[0]) redirect(URL("default", "index")) Index: applications/mobileblur/controllers/stories.py ================================================================== --- applications/mobileblur/controllers/stories.py +++ applications/mobileblur/controllers/stories.py @@ -3,6 +3,10 @@ from pprint import pprint def view(): stories = newsblur.feed(request.vars["feed_id"])["stories"] story = [story for story in stories if story["id"]==request.vars["story"]][0] - return dict(story=story) + return dict(story=story, feed_id=request.vars["feed_id"]) + +def mark_read(): + results = newsblur.mark_story_as_read(request.vars["story_id"], request.vars["feed_id"]) + redirect(URL("default", "index")) Index: applications/mobileblur/models/0_helpers.py ================================================================== --- applications/mobileblur/models/0_helpers.py +++ applications/mobileblur/models/0_helpers.py @@ -2,11 +2,10 @@ newsblur = newsblur.NewsBlur() threshold = 0 thresholds = ["nt", "ps", "ng"] # indices -1, 0, 1 for negative, neutral, and positive intelligence filters -print request.cookies if [request.application, request.controller, request.function] != [request.application, "default", "login"]: if "nb_cookie" not in request.cookies.keys(): redirect(URL("default", "login")) else: newsblur.cookies["newsblur_sessionid"] = request.cookies["nb_cookie"].value Index: applications/mobileblur/modules/newsblur.py ================================================================== --- applications/mobileblur/modules/newsblur.py +++ applications/mobileblur/modules/newsblur.py @@ -21,12 +21,10 @@ Required parameters, username and password, must be of string type. ''' url = nb_url + 'api/login' results = requests.post(url, data={"username": username, "password": password}) - print "results.cookies =", results.cookies - print type(results.cookies) self.cookies = results.cookies results = simplejson.loads(results.content) if results["authenticated"] is False: raise Exception("The newsblur credentials you provided are invalid") return results Index: applications/mobileblur/static/css/base.css ================================================================== --- applications/mobileblur/static/css/base.css +++ applications/mobileblur/static/css/base.css @@ -25,17 +25,17 @@ - form and table padding - code blocks - left and right padding to quoted text - page layout alignment, width and padding (change this for spaces) - column widths (change this to use left_sidebar and right_sidebar) -- backrgound images and colors (change this for colors) +- background images and colors (change this for colors) - web2py specific (.flash, .error) Notice: - even if you use a different layout/css you may need classes .flash and .error - this is all color neutral except for #349C01 (header, links, lines) -- there are two backrgound images: images/background.png and images/header.png +- there are two background images: images/background.png and images/header.png License: This file is released under BSD and MIT */ @@ -143,13 +143,14 @@ /* always force a scrollbar in non-IE */ html { overflow-y: scroll; } /* Accessible focus treatment: people.opera.com/patrickl/experiments/keyboard/test */ a:hover, a:active { outline: none; } - +/* a, a:active, a:visited { color:#607890; } a:hover { color:#036; } +*/ ul, ol { margin-left: 1.8em; } ol { list-style-type: decimal; } /* Remove margins for navigation lists */ @@ -327,63 +328,18 @@ fieldset { border: 1px solid #dedede; padding: 6px; } legend { font-weight: bold; } input:focus, textarea:focus { background: #fafafa; } -p {text-indent:30px;} - -p, blockquote { - margin-bottom: 10px; -} - h1,h2,h3,h4,h5,h6 { line-height: 170%; } h1 {font-size: 2.0em;} h2 {font-size: 1.8em;} h3 {font-size: 1.4em;} h4 {font-size: 1.2em;} h5 {font-size: 1.0em;} h6 {font-size: 0.8em;} -/*********** page layout alignment, width and padding ***********/ -/*body {background-color: #000;}*/ -#container, #header, #page, #content, #statusbar, -#footer, #wrapper { display:block; line-height: 170%; } -#wrapper {width: 900px;} -#container { - margin: 0 auto; - padding: 0; -} -#wrapper {margin: 0 auto;} -#wrapper {background-color: #fff; padding: 5px;} -#statusbar { margin: 5px 0px 20px 0px;} -#footer { - margin-top: 30px; - padding: 5px; -} -#statusbar, #footer { - background: #eaeaea; - border-top: 1px #aaa solid; -} -#logo { - width: 68px; - height: 62px; - background: url(../images/logo.png); -} -#appname { - color: #cccccc; -} - -#right_sidebar { width: 160px; float:right; display: none; } -#left_sidebar { width: 160px; float:left; display: none; } -#content { float: left; /*width: 740px;*//*width: 63%;*/ /*width: 640px; float:left;*/ } /* uncomment this if you are going to use sidebars */ - -.auth_navbar { - top: 0px; - float: right; - padding: 3px 10px 3px 10px; -} - /*********** web2py specific ***********/ div.flash { font-weight: bold; display: none; position: fixed; @@ -533,10 +489,15 @@ /* Uncomment if you don't want iOS and WinMobile to mobile-optimize the text for you j.mp/textsizeadjust html { -webkit-text-size-adjust:none; -ms-text-size-adjust:none; } */ } +@media handheld { + body { + } +} + /* * print styles * inlined to avoid required HTTP connection www.phpied.com/delay-loading-your-print-css/ */ @@ -552,5 +513,82 @@ @page { margin: 0.5cm; } p, h2, h3 { orphans: 3; widows: 3; } h2, h3{ page-break-after: avoid; } } +body { +/* background-color: #f2c84b;*/ + color: #493F3E +} + +header { + background-color: #95392E; + padding: 1%; + border-bottom: 1px solid #95392E; + color: white; +} +header > h1 > a { + text-decoration: none; + color: white; + font-weight: bold; +} + +#story > header > a { + color: white; +} + +header > a> h2 { +/* border-top: 1px solid white;*/ + color: white; +} + +#story-list { +/* background-color: #f2c84b;*/ +} + +#story-list > .story > a, .feed > a { + text-decoration: none; + font-weight: bold; + margin-top: 0; +} +#story-list > .story > p { + font-size: .75em; +} + +#story-list > .story > .read { + color: #D99B48; +} +#story-list > .story > .unread, .feed > a { + color: #C57E3C; +} + +#story-list > .story, .feed { + padding: .5%; + border-top: 1px solid #95392E; + margin-top: .33%; +} + +span.ps, span.nt, span.ng { + padding: .05em; + border-radius: 5px; +} +span.ps { + background-color: #ABAA7A; +} +span.nt { + background-color: #D99B48; +} +span.ng { + background-color: red; +} + +footer { + background-color: #95392E; + padding: 1%; + border-top: 1px solid #95392E; + color: white; +} +footer > a { + text-decoration: none; + color: white; + font-weight: bold; +} Index: applications/mobileblur/views/default/index.html ================================================================== --- applications/mobileblur/views/default/index.html +++ applications/mobileblur/views/default/index.html @@ -1,12 +1,10 @@ -{{left_sidebar_enabled=right_sidebar_enabled=False}} {{extend 'layout.html'}} {{ for feed in feeds.itervalues(): }} - {{ if threshold == -1 and feed["ng"] > 0: }} [ {{= feed["ng"] }} ] {{ pass }} - {{ if threshold <= 0 and feed["nt"] > 0: }} [ {{= feed["nt"] }} ] {{ pass }} - {{if feed["ps"] > 0: }}[ {{= feed["ps"] }} ] {{ pass }} - {{= feed["feed_title"] }}
+
+ {{ if threshold == -1 and feed["ng"] > 0: }} {{= feed["ng"] }} {{ pass }} + {{ if threshold <= 0 and feed["nt"] > 0: }} {{= feed["nt"] }} {{ pass }} + {{if feed["ps"] > 0: }}{{= feed["ps"] }} {{ pass }} + {{= feed["feed_title"] }}
+
{{ pass }} - -{{block left_sidebar}}New Left Sidebar Content{{end}} -{{block right_sidebar}}New Right Sidebar Content{{end}} Index: applications/mobileblur/views/feeds/view.html ================================================================== --- applications/mobileblur/views/feeds/view.html +++ applications/mobileblur/views/feeds/view.html @@ -1,12 +1,28 @@ -{{left_sidebar_enabled=right_sidebar_enabled=False}} {{extend 'layout.html'}} -

{{= feed["feed_title"] }}

- Mark feed as read - -{{ for story in stories: }} -

{{= story["story_title"] }}

-{{ pass }} - -{{block left_sidebar}}New Left Sidebar Content{{end}} -{{block right_sidebar}}New Right Sidebar Content{{end}} +{{ block header_bonus }} +

{{= feed["feed_title"] }}

+ Mark feed as read +{{ end }} + +
+{{ +import time +s = time.time() +}} + {{ + for story in stories: + if sum([v for k,v in story["intelligence"].iteritems()]) < threshold: + continue + }} + + {{ print story["read_status"] }} +
+ + {{= story["story_title"] }} + +

{{= story["story_date"] }}

+
+ {{ pass }} +{{ print time.time() - s }} +
Index: applications/mobileblur/views/layout.html ================================================================== --- applications/mobileblur/views/layout.html +++ applications/mobileblur/views/layout.html @@ -8,11 +8,11 @@ - {{=response.title or request.application}} + {{ block title }}{{= " - ".join([response.title, request.application]) }}{{ end }} - + @@ -57,10 +57,12 @@ else: left_sidebar_style = 'style="display: none;"' if right_sidebar_enabled: right_sidebar_style = 'style="display: block;"' else: right_sidebar_style = 'style="display: none;"' style_content = 'style="width: %s"' % width_content }} + + @@ -68,92 +70,39 @@
{{=response.flash or ''}}
-
- -
- - - -
- {{block statusbar}} - {{#------ superfish menu ------}} - {{=MENU(response.menu,_class='sf-menu')}} - -
- {{end}} -
- -
- - {{if left_sidebar_enabled:}} - - {{pass}} - - -
- {{include}} -
- - - {{if right_sidebar_enabled:}} - - {{pass}} - - -
- -
- - -
-
+
+ {{block header}} +

MobileBlur

+ {{end}} + {{ block header_bonus }}{{end}} +
+ +
+ {{include}} +
+ + - Index: applications/mobileblur/views/stories/view.html ================================================================== --- applications/mobileblur/views/stories/view.html +++ applications/mobileblur/views/stories/view.html @@ -1,9 +1,13 @@ -{{left_sidebar_enabled=right_sidebar_enabled=False}} {{extend 'layout.html'}} -

{{= story["story_title"] }}

- -{{= XML(story["story_content"]) }} - -{{block left_sidebar}}New Left Sidebar Content{{end}} -{{block right_sidebar}}New Right Sidebar Content{{end}} +{{ block header_bonus }} + +

{{= story["story_title"] }}

+
+ + Mark story as read + +{{ end }} +
+ {{= XML(story["story_content"]) }} +
ADDED routes.py Index: routes.py ================================================================== --- routes.py +++ routes.py @@ -0,0 +1,166 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# default_application, default_controller, default_function +# are used when the respective element is missing from the +# (possibly rewritten) incoming URL +# +default_application = 'mobileblur' # ordinarily set in base routes.py +default_controller = 'default' # ordinarily set in app-specific routes.py +default_function = 'index' # ordinarily set in app-specific routes.py + +# routes_app is a tuple of tuples. The first item in each is a regexp that will +# be used to match the incoming request URL. The second item in the tuple is +# an applicationname. This mechanism allows you to specify the use of an +# app-specific routes.py. This entry is meaningful only in the base routes.py. +# +# Example: support welcome, admin, app and myapp, with myapp the default: + + +routes_app = ((r'/(?Pwelcome|admin|app)\b.*', r'\g'), + (r'(.*)', r'myapp'), + (r'/?(.*)', r'myapp')) + +# routes_in is a tuple of tuples. The first item in each is a regexp that will +# be used to match the incoming request URL. The second item in the tuple is +# what it will be replaced with. This mechanism allows you to redirect incoming +# routes to different web2py locations +# +# Example: If you wish for your entire website to use init's static directory: +# +# routes_in=( (r'/static/(?P[\w./-]+)', r'/init/static/\g') ) +# + +routes_in = ((r'.*:/favicon.ico', r'/examples/static/favicon.ico'), + (r'.*:/robots.txt', r'/examples/static/robots.txt'), + ((r'.*http://otherdomain.com.* (?P.*)', r'/app/ctr\g'))) + +# routes_out, like routes_in translates URL paths created with the web2py URL() +# function in the same manner that route_in translates inbound URL paths. +# + +routes_out = ((r'.*http://otherdomain.com.* /app/ctr(?P.*)', r'\g'), + (r'/app(?P.*)', r'\g')) + +# Error-handling redirects all HTTP errors (status codes >= 400) to a specified +# path. If you wish to use error-handling redirects, uncomment the tuple +# below. You can customize responses by adding a tuple entry with the first +# value in 'appName/HTTPstatusCode' format. ( Only HTTP codes >= 400 are +# routed. ) and the value as a path to redirect the user to. You may also use +# '*' as a wildcard. +# +# The error handling page is also passed the error code and ticket as +# variables. Traceback information will be stored in the ticket. +# +# routes_onerror = [ +# (r'init/400', r'/init/default/login') +# ,(r'init/*', r'/init/static/fail.html') +# ,(r'*/404', r'/init/static/cantfind.html') +# ,(r'*/*', r'/init/error/index') +# ] + +# specify action in charge of error handling +# +# error_handler = dict(application='error', +# controller='default', +# function='index') + +# In the event that the error-handling page itself returns an error, web2py will +# fall back to its old static responses. You can customize them here. +# ErrorMessageTicket takes a string format dictionary containing (only) the +# "ticket" key. + +# error_message = '

%s

' +# error_message_ticket = '

Internal error

Ticket issued: %(ticket)s' + +# specify a list of apps that bypass args-checking and use request.raw_args +# +#routes_apps_raw=['myapp'] +#routes_apps_raw=['myapp', 'myotherapp'] + +def __routes_doctest(): + ''' + Dummy function for doctesting routes.py. + + Use filter_url() to test incoming or outgoing routes; + filter_err() for error redirection. + + filter_url() accepts overrides for method and remote host: + filter_url(url, method='get', remote='0.0.0.0', out=False) + + filter_err() accepts overrides for application and ticket: + filter_err(status, application='app', ticket='tkt') + + >>> import os + >>> import gluon.main + >>> from gluon.rewrite import regex_select, load, filter_url, regex_filter_out, filter_err, compile_regex + >>> regex_select() + >>> load(routes=os.path.basename(__file__)) + + >>> os.path.relpath(filter_url('http://domain.com/favicon.ico')) + 'applications/examples/static/favicon.ico' + >>> os.path.relpath(filter_url('http://domain.com/robots.txt')) + 'applications/examples/static/robots.txt' + >>> filter_url('http://domain.com') + '/init/default/index' + >>> filter_url('http://domain.com/') + '/init/default/index' + >>> filter_url('http://domain.com/init/default/fcn') + '/init/default/fcn' + >>> filter_url('http://domain.com/init/default/fcn/') + '/init/default/fcn' + >>> filter_url('http://domain.com/app/ctr/fcn') + '/app/ctr/fcn' + >>> filter_url('http://domain.com/app/ctr/fcn/arg1') + "/app/ctr/fcn ['arg1']" + >>> filter_url('http://domain.com/app/ctr/fcn/arg1/') + "/app/ctr/fcn ['arg1']" + >>> filter_url('http://domain.com/app/ctr/fcn/arg1//') + "/app/ctr/fcn ['arg1', '']" + >>> filter_url('http://domain.com/app/ctr/fcn//arg1') + "/app/ctr/fcn ['', 'arg1']" + >>> filter_url('HTTP://DOMAIN.COM/app/ctr/fcn') + '/app/ctr/fcn' + >>> filter_url('http://domain.com/app/ctr/fcn?query') + '/app/ctr/fcn ?query' + >>> filter_url('http://otherdomain.com/fcn') + '/app/ctr/fcn' + >>> regex_filter_out('/app/ctr/fcn') + '/ctr/fcn' + >>> filter_url('https://otherdomain.com/app/ctr/fcn', out=True) + '/ctr/fcn' + >>> filter_url('https://otherdomain.com/app/ctr/fcn/arg1//', out=True) + '/ctr/fcn/arg1//' + >>> filter_url('http://otherdomain.com/app/ctr/fcn', out=True) + '/fcn' + >>> filter_url('http://otherdomain.com/app/ctr/fcn?query', out=True) + '/fcn?query' + >>> filter_url('http://otherdomain.com/app/ctr/fcn#anchor', out=True) + '/fcn#anchor' + >>> filter_err(200) + 200 + >>> filter_err(399) + 399 + >>> filter_err(400) + 400 + >>> filter_url('http://domain.com/welcome', app=True) + 'welcome' + >>> filter_url('http://domain.com/', app=True) + 'myapp' + >>> filter_url('http://domain.com', app=True) + 'myapp' + >>> compile_regex('.*http://otherdomain.com.* (?P.*)', '/app/ctr\g')[0].pattern + '^.*http://otherdomain.com.* (?P.*)$' + >>> compile_regex('.*http://otherdomain.com.* (?P.*)', '/app/ctr\g')[1] + '/app/ctr\\\\g' + >>> compile_regex('/$c/$f', '/init/$c/$f')[0].pattern + '^.*?:https?://[^:/]+:[a-z]+ /(?P\\\\w+)/(?P\\\\w+)$' + >>> compile_regex('/$c/$f', '/init/$c/$f')[1] + '/init/\\\\g/\\\\g' + ''' + pass + +if __name__ == '__main__': + import doctest + doctest.testmod() +