Welcome to TiddlyWiki created by Jeremy Ruston, Copyright © 2007 UnaMesa Association
!Afternoon session (from 15:10 - 17:10)
#Travelled from Euston to Chester, met the group and learnt about what Avox and Citigroup's relevant businesses do
!Morning session (from 11:15 to 14:30)
#Checking out the globe preload
** looks like it is fine actually, so we're bumping yesterday's units up to 2
#Spiking footer
** Legal requirements for company website information: http://www.out-law.com/page-7594
{{{
For websites, contrary to the fears of some, the specified information does not need to appear on every page. Again, many websites will already list the required information, perhaps on their 'About us' or 'Legal info' pages.
The name, geographic address and email address of the service provider. The name of the organisation with which the customer is contracting must be given. This might differ from the trading name. Any such difference should be explained – e.g. "XYZ.com is the trading name of XYZ Enterprises Limited."
It is not sufficient to include a 'contact us' form without also providing an email address and geographic address somewhere easily accessible on the site. A PO Box is unlikely to suffice as a geographic address; but a registered office address would. If the business is a company, the registered office address must be included.
If a company, the company's registration number should be given and, under the Companies Act, the place of registration should be stated (e.g. "XYZ Enterprises Limited is a company registered in England and Wales with company number 1234567")
If the business is a member of a trade or professional association, membership details, including any registration number, should be provided.
If the business has a VAT number, it should be stated – even if the website is not being used for e-commerce transactions.
Prices on the website must be clear and unambiguous. Also, state whether prices are inclusive of tax and delivery costs.
}}}
** emailed Neil for company details
** going to wait and see whether a footer is needed to balance the page or not; this legal info can go in the About page
#Sorting [[online GoogSheet|http://spreadsheets.google.com/ccc?key=0AgQJ7FGUGIp_dHlYX3JUUGdOQlhSbHBZZU1nZTFmNUE&hl=en]] so it provides estimates and tracks how they change as you do work
#Spiking tag river
** want to know what to do when you mouseover a tag
** looking at some examples of JS tag cloud-type things
** decided it's 5 units
#Notes for spike of Accordion AJAX load
** http://net.tutsplus.com/javascript-ajax/how-to-load-in-and-animate-content-with-jquery/
#Josh did Accordion layout & appearance
!Afternoon session (from 14:45 to 16:55; 17:10 to 18:00)
#Spiking consultants gallery
** problem is: how to add contextual information to a consultant to persuade you to click to see their profile
** scrolling internal nav: http://digitalmash.com/journal/articles/portfolio-building/, http://www.huddle.net/take-the-tour/
** gonna have column scrolling down on hover to reveal tags for consultant
** when you click on a consultant, the whole block scrolls sideways to reveal profile; you can click on something to get back to gallery
#Spiking contact form
** going to be some or all of the following: email address, mail address, phone number, map, contact form
** Josh is emailing Neil about this
** agreed 1.5 units
#Spiking AJAX loading
#Josh adding logo
#Bit more on the tag-river
!Morning session (from 12:40 to 14:30; 14:50 to 18:35)
* wiring in the context column
** need to populate on initialising filmreel - done
** tags shouldn't link to tag page, but act like tagriver tags - done
* shrinking nav column
** moved context column to the right so it doesn't move
** commented .expander width setting, as we're not sure it's needed
* make tagriver start full - done
* Josh made sticky footer stick
* make consultant profiles load by Ajax - done
!Evening session (from 19:05 to 20:15)
* Figuring out how consultant assets are going to work
** Installed WP-Filebase plugin
*** ...and then deleted it to use the simple media uploader instead
* Added consultant assets to #centrepane and then moved them into context column
* Centrepane close transition
!Morning session (from 11:20 to 12:40)
* Merging Chris' mappingsql/wiki-data access fix
** had a Python crash when testing with MAD. Chris recommended Spawner, but that's not working for me yet
!Evening session (from 20:50 to 00:20)
* Trying to fix the MAD server problem - in the absence of cherrpy, sorting out spawner complaint about paste.deploy not being there
** turns out there's a package called pastedeploy
* to look at:
{{{
cdent: did you get a chance to look at the create_user changes and did they make sense?
cdent: i presume you'll probably want to tweak the presentation
}}}
** testing: checking you can add accounts, and looking at behaviour when you add a duplicate or don't fill in the complete form
*** you get told about it when you're duplicating and if there's missing data, you're redirected to home when you have success
** testing: then look at challenge/suggest/RMI when you're logged in as someone who isn't an admin
!Afternoon session (from 12:45 to 15:45; 16:15 to 18:35)
* Picking up list from yesterday
** index page - HALF-DONE (still fiddling with sticky footer) - given this to Josh
*** DONE and integrated
** hiding initial table size on results page
*** looks like DataTables doesn't set the correct width when there is padding on header cells
*** doing an experiment to see if ordinary tables spill beyond their container when you have padding - also checking to see if setting a width on the table helps with that
**** conclusion: make the wrapper wider (to 1024px) to accommodate table - later, combine address fields so the table default columns take up less space
**** found that the FixedHeader being used doesn't work very well when the table ends up spilling beyond its container - [[reported|http://datatables.net/forums/comments.php?DiscussionID=979&page=1#Comment_5318]] on DataTables forum
***** I've left it so that the cloned header is always positioned absolute rather than fixed; this way, I can set a width on the cloned table and it will not spill out (see DataTables forum link above for explanation)
** underlining links on results table - DONE
** suggest new - DONE
** company record page
* Committing Chris' search bug fix (#93)
* More on list
** make cursor into pointer when hovering over active column controls - DONE
** colours for results table - DONE
** ajax_search - DONE
** make cursor into pointer when hovering over table headers - DONE
** where have the results status messages gone?
* Talking to Chris about how to get the re-design previewed
** Chris set up a new instance on tw.wiki-data.com
!Evening session (from 22:55 to 23:40; 23:50 to 01:50)
* What's left?
** company record page - DONE
** challenge page
** request more information page
** MAD login
** search results table doesn't have alternating colours nor a background in IE6/7
** company page has footer half-way up page in IE6/7
** company page has no space between tabs and action buttons in IE6/7
** search results table borders are not collapsed in IE6/7
** where have the results status message gone?
** updating the header to use the new logo and search icon
** updating the icon to not be a transparent PNG, but a GIF - gave this to Josh
** MAD data panel
** background colour on links in footer (not light enough)
* Amit's list
** little box asking people to check before suggesting and challenging
** header - giving more space
* Sorting index page for Amit
!Afternoon session (from 14:45 to 15:25)
* Testing Chris' changes
* Adding to login_form so it handles expired users
** found problem with my local MAD users not having any roles set in their user_sign - asked Chris on IRC
!Evening session (from 20:20 to 20:45)
* What I need to test:
** self-service create new account -> verify email sent and account set to tier 1 - this works
** upgrade account -> verify account upgraded to tier 2 (#162) - this works
** admin create new account -> verify email sent and account set to tier 2 - this works
** account expiration works - you're told about it if you try to login (#159, #160) - this half-works
** check tier 1 and tier 2 access work as expected (#157)
* #152 is an overarching ticket for much of this
<<readLog>>
!Afternoon session (from 16:45 to 17:55)
# Sorting out bags permission so dickon can edit YOUS and KIDSCO bags
** turns out I need to change the {{{server.workspace}}} field on tiddlers so the current recipe is referred to, which will let people save to whichever bag they have access to.
** I've posted on the TiddlyWeb group about this:
*** http://groups.google.com/group/tiddlyweb/browse_thread/thread/7a11f256e3703e45
# Responding to emails
!Morning session (from 8:30 to 9:00; 11:30 to 12:30)
# Fixed IE banner layout problem
# Added some more explanatory text to [[wiki-data matcher]]
# FIMA presentation
!Morning session (from 10:15 to 12:00)
#Chris optimized the whoosh index last night
#Moving onto investigating the live server performance
** There are three situations I want to test
### The first request the server receives after a period of no activity
### The first request the server receives for a search it has not performed before
### A request for a search that has been carried out previously during a period of standard activity - this is the easiest to test and reflects the common usage pattern
** Initially I thought the third scenario looked like it was taking under 2 seconds to complete the HTTP request - later, I found a range of times: 11.5s (first after some inactivity), 7.4s, 2.5s, 10.7s, 1s, 1s, 1s
*** Here's a log of one of the much slower requests (10.7s) - I've emboldened the problems areas (note that the search is ''not'' highlighted):
**** 2009-10-10 10:50:03,534 DEBUG starting "GET" request with uri "/search?q=bank&avid=", script_name "", path_info "/search" and query "q=bank&avid="
**** ''2009-10-10 10:50:03,829'' DEBUG overriding GET method to GET
**** ''2009-10-10 10:50:05,157'' DEBUG negotiating for accept and extensions ['*/*', '*/*']
**** 2009-10-10 10:50:05,174 DEBUG starting whoosh_search
**** ''2009-10-10 10:50:05,179'' DEBUG parsed query
**** ''2009-10-10 10:50:08,759'' DEBUG got searcher
**** 2009-10-10 10:50:08,763 DEBUG got config
**** 2009-10-10 10:50:08,766 DEBUG query to parse: bank
**** 2009-10-10 10:50:09,101 DEBUG query parsed to Or([Term('legal_name', u'bank', boost=1.0), Term('previous_name(s)', u'bank', boost=1.0), Term('trades_as_name(s)', u'bank', boost=1.0)])
**** 2009-10-10 10:50:10,009 DEBUG did search
**** 2009-10-10 10:50:10,061 DEBUG finishing whoosh_search
**** ''2009-10-10 10:50:10,071'' DEBUG in bag_get
**** ''2009-10-10 10:50:14,258'' DEBUG in list_tiddlers
**** 2009-10-10 10:50:14,362 INFO 80.79.44.110 - GUEST [10/Oct/2009:10:50:14 ] "GET /search?q=bank&avid= HTTP/1.1" 200 - "-" "curl/7.16.3 (powerpc-apple-darwin8.0) libcurl/7.16.3 OpenSSL/0.9.7l zlib/1.2.3"
** Out of interest, I'm trying the {{{/search.txt?q=bank&avid=}}} request
*** This only returns the titles
*** However, it takes only around 1 second to complete
** On the other hand {{{/search.json?q=bank&avid=}}} returns everything - is it as fast?
*** Took around 2.5 seconds to complete, but spent 1 second making 51 calls to {{{bag_get}}} - definitely room for optimisation there - compared to 0.7 seconds for the HTML case where only one call to {{{bag_get}}} is made
** Testing the second scenario
*** Searched for {{{/search?q=limited&avid=}}}
**** Spent 10.6 seconds doing the whoosh search (from "query parsed..." to "did search"); total 14s; 3s from "bag_get" to "in list_tiddlers"
**** Second time round, it took 5.5 seconds for the search; total 6.4s; 0.9s from "bag_get" to "in list_tiddlers"
**** Third time, 5.5 seconds for the search; total 6.5s; 0.8s from "bag_get" to "in list_tiddlers"
**** That's worrying... going to test with an even longer query to see if that has any effect; maybe time is related to the number of search results in the index
*** Searched for {{{/search?q=international&avid=}}}
**** First request took 3 seconds, with 530ms doing the search and 2200ms between {{{bag_get}}} and {{{in list_tiddlers}}}
**** Second request took 1.2 seconds, with 360ms doing the search and 720ms between {{{bag_get}}} and {{{in list_tiddlers}}}
**** Third request took 1.2 seconds, with 360ms doing the search and 620ms between {{{bag_get}}} and {{{in list_tiddlers}}}
** I don't have a proper test of the first scenario, as I made a request to {{{/index.html}}} first, but this is the log from the first search I did of the day, which took 14.7 seconds - I've enboldened the problem areas:
***2009-10-10 09:23:04,282 DEBUG starting "GET" request with uri "/search?q=bank&avid=", script_name "", path_info "/search" and query "q=bank&avid="
***2009-10-10 09:23:04,290 DEBUG overriding GET method to GET
***2009-10-10 09:23:04,430 DEBUG simple_cookie looking at cookie string: __utma=268702580.1890721885.1254743048.1255023900.1255162919.11; __utmz=268702580.1254743048.1.1.utmcsr=(direct)|utmccn=(direct)|utmcmd=(none); __utmc=268702580; __utmb=268702580.1.10.1255162919
***2009-10-10 09:23:04,430 DEBUG negotiating for accept and extensions ['text/html', 'application/xhtml+xml', 'application/xml', '*/*', '*/*']
***2009-10-10 09:23:04,445 DEBUG starting whoosh_search
***''2009-10-10 09:23:04,450'' DEBUG parsed query
***''2009-10-10 09:23:09,706'' DEBUG got searcher
***2009-10-10 09:23:09,706 DEBUG got config
***2009-10-10 09:23:09,706 DEBUG query to parse: bank
***''2009-10-10 09:23:10,118'' DEBUG query parsed to Or([Term('legal_name', u'bank', boost=1.0), Term('previous_name(s)', u'bank', boost=1.0), Term('trades_as_name(s)', u'bank', boost=1.0)])
***''2009-10-10 09:23:13,088'' DEBUG did search
***2009-10-10 09:23:13,354 DEBUG finishing whoosh_search
***''2009-10-10 09:23:13,364'' DEBUG in bag_get
***''2009-10-10 09:23:18,904'' INFO 80.79.44.110 - GUEST [10/Oct/2009:09:23:18 ] "GET /search?q=bank&avid= HTTP/1.1" 304 - "-" "Mozilla/5.0 (Macintosh; U; PPC Mac OS X 10.4; en-US; rv:1.9.1.3) Gecko/20090824 Firefox/3.5.3"
#Letting Chris know about the results
!Afternoon session (from 12:15 to 13:15; from 17:00 to 17:45; from 18:15 to 20:30)
#Trying out asynchronous search using {{{/search.json}}}
#Moved ALL JavaScript files to the bottom of the body tag and modified the [[FOUC trick|http://www.learningjquery.com/2008/10/1-way-to-avoid-the-flash-of-unstyled-content]] to not use jQuery - this ought to enhance perceived performance by making JS calls on index page and then caching them (''need to check server caching is long-term, which has consequences for JS file names'')
#Using DataTables' async-load of data
** Have to map returned format to DataTables' format
*** Posted a [[feature suggestion|http://datatables.net/forums/comments.php?DiscussionID=732]] on the DataTables forum that this mapping ability is supported as standard - provided my modification as an example
*** It just needs {{{aaData}}} as a 2D-array, with each member of the outer array representing a row and each member of the inner arrays representing a cell:
{{{
"aaData": [
[
"Gecko",
"Firefox 1.0",
"Win 98+ / OSX.2+",
"1.7",
"A"
],
[
"Gecko",
"Firefox 1.5",
"Win 98+ / OSX.2+",
"1.8",
"A"
],
...
]
}}}
The JSON returned from {{{/search.json}}} is an array, with an element for each tiddler, which is an object containing a bunch of properties:
{{{
[{
"revision": 1,
"created": "20090915220334",
"fields": {
"legal_name": "Vist Bank",
"avid": "3612562",
"trading_status": "Active",
"operational_country": "USA",
"previous_name(s)": "Leesport Bank",
"operational_state": "PA",
"country_of_registration": "USA",
"operational_postcode": "19533-8646",
"operational_street_1": "133 North Centre Avenue",
"operational_city": "Leesport"
},
"tags": [],
"recipe": null,
"modified": "20090915220334",
"bag": "avox",
"title": "3612562",
"modifier": null,
"type": null,
"permissions": ["read", "write", "create", "delete"]
}, ... ]
}}}
#The records table generation needs to have all the post-data-load stuff moved into the {{{fnInitComplete}}} callback
#All search links overridden to use ajax_search
** This seems to be working ok, but it really needs to happen as the very first thing after the DOM is loaded
*** I think things will be better when I'm not using so many HTTP calls
#''must remember to change "there are x results" to show the actual number of results''
#''also, must remember to include the search.html template in the ajax_search rendering''
#Loading Google Analytics asynchronously to improve performance
** I found some examples on the web, but thought they were a bit verbose or weren't for jQuery, so wrote my own:
*** [[jQuery gaTracker plugin|http://plugins.jquery.com/node/969]]
*** [[Taylor Hughers|http://www.taylor-hughes.com/2009/mar/load_google_analytics_asynchronously.html]]
*** [[nbga.js|http://925html.com/code/non-blocking-google-analytics-integration/]]
#Taking stock:
** We're down to about 1.7s for the server response for {{{/ajax_search}}}; although we also seem to be down to 1.1s for {{{/search}}}, so this is good, but strange
** However, total page load times (as shown by Firebug net tab) are still bad:
*** ajax_search: about 7s
*** search: about 10s
#Trying to reduce the number of HTTP requests made during page load
** We have 8 script tags and 2 stylsheet link tags
*** Things are much better after combining all JS files:
*** search: about 6.5s
*** ajax_search: something like 4s
** ''can also do modular loading of e.g. the records table handling if it's needed''
** ''can also re-arrange JS loading so some stuff happens async and doesn't block triggering page load''
** ''there are other loading tricks, like taking some stuff out of {{{$(document).ready()}}}''
#Tweaking the "hide/show columns" style to give it a higher z-index than the records table or the FixedHeader clone table
** Trying the solution suggested in comment 8 of [[this post|http://www.quirksmode.org/bugreports/archives/2006/01/Explorer_z_index_bug.html]]
!Morning session (from 10:00 - 13:00)
#Citigroup workshop
** Introduction to how Avox operates and demonstration of their software
** Few thoughts about ERA application
*** could be made asynchronous for speed
*** fields not being validated e.g. URL
*** could automate logging-in to sites that require it to speed up research
*** collides could be auto-flagged
*** good use of colours
** Thoughts about EPOCH application
*** transitions are slow and annoying
!Afternoon session (from 13:45 - 17:45; 20:00 - 21:00)
# Design workshop for V1 of wiki-data application
** Added acceptance criteria to selected user stories
** Chose high-level user stories for V2 (client-application) and added acceptance criteria to some of them (Ken said he would complete the last couple on Wednesday)
** User stories copied to [[Avox Wiki-data release 1 user stories]]
!Afternoon session (from 12:15 to 14:25; from 15:40 to 19:15)
#Plan for getting tag content
** Josh emailing Neil to get transcriptions of videos
** We'll Wordalize them and create a list of tags for each consultant
** We'll send this to Neil for BP to review and edit
#Tag-river - getting it animating smoothly
** the animation stops when you hover over a tag
** also added a rollover effect on the tags, where the color fades back the original color after you mouseout
** problem on IE6: "geo-political" word-wraps across two lines, messing up the height and width of that line
*** looking into stopping it word-wrapping
**** fixed with CSS {{{white-space: nowrap}}} on 'a' tag - see [[here|http://www.cs.tut.fi/~jkorpela/html/nobr.html#prevent]]
#Identifying what tag-river needs to do that it doesn't
**bug: if you hover, move off, hover again before fade has finished, it slips to off-hover colour; then only flashes hover colour when you hover - fixed
**bug: feedback not inside overflow:hidden div - fixed
**colour fading as they move down - done
**Josh to look at using font-face to deliver Rockwell font for tags
**order of tags should be random
**when you run out of tags, more should come, randomised from the first order
**when a line moves off the bottom of the river, its div should be pulled from the river so as not to create a massive memory hog if left for a while
#Sorting weird IE6 behaviour
** when you get the color of an element in a jQuery hover handler, IE6 reports the color before any {{{:hover}}} pseudo-class changes it
*** I'm disabling the hoverColor fade in IE6 and making the tag feedback color the same as the non-hover color
*** actually, turns out the way to sort this was to allow the pseudo-class to shine through by effectively removing the CSS color declaration after the fade animation has finished:
{{{
$a.css("color", "");
}}}
*** still has the problem that the feedback tag will be the non-hover color in IE6, but this is ok
#Stopped working on tag-river at 18:00
#Helping Josh with AJAX accordion
** sorted out accordion behaviour
#Getting a page loading AJAXy
!Morning session (from 12:00 to 13:20)
* Sorting out what to do
* Putting fragID nav in
* Josh kept going when I stopped
!Afternoon session (from 14:20 to 16:00; 16:20 to 17:40)
* Putting more hashNav in
** turns out back/foward navigation doesn't cause any event to fire (except in IE8), so you need to poll the hash
** changing all ajax nav to be a response to frag ID changing
*** instead of doing nav behaviours in click handlers, just change the hash
!Morning session (from 9:00 to 9:35; 9:55 to 12:00)
* Getting live servers up and running
* Small changes to content and fixing unnoticed bugs
!Afternoon session (from 13:20 to 14:20)
* Updating site copy based on Adam's provision
* Fixing a logic bug
!Morning session (from 7:30 to 9:35; 11:00 to 13:30)
* The list
** challenge page - DONE
** request more information page - DONE
** MAD login - DONE
** updating the header to use the new logo and search icon - DONE
** search results table doesn't have alternating colours nor a background in IE6/7
** company page has footer half-way up page in IE6/7 - HALF-FIXED
*** moved footer down, needs checking in IE6/7
** company page has no space between tabs and action buttons in IE6/7
** search results table borders are not collapsed in IE6/7
** where have the results status message gone?
** updating the icon to not be a transparent PNG, but a GIF - gave this to Josh - DONE
** MAD data panel - DONE
** background colour on links in footer (not light enough)
** fix the colours on the static page tables - the text should be black
** there's a problem with searching when logged-in - FIXED
!Afternoon session (from 14:15 to 18:30)
* The list
** search results table doesn't have alternating colours nor a background in IE6/7 - DONE
** hide/show button has tiny text in IE6/7 - FIXED
** move hide/show button up in IE6/7 - DONE
** index tagline link is not visible in IE6/7 - FIXED
*** I had to give the span a line-height of 2em and give it hasLayout with {{{display: inline-block}}}
** company page has footer half-way up page in IE6/7 - HALF-FIXED
*** moved footer down, needs checking in IE6/7
**** it's all totally messed up
** company page has no space between tabs and action buttons in IE6/7
** search results table borders are not collapsed in IE6/7 - DONE
** where have the results status message gone? - FIXED
*** it looks like some JS is setting the CSS visibility to hidden, investigating...
**** turns out I wasn't making the right parent visible (just {{{#recordsTable}}}, not {{{#table}}})
** in IE6, company page is really all over the place layout-wise
** background colour on links in footer (not light enough)
** fix the colours on the static page tables - the text should be black
** ajax_search doesn't stop showing "loading results..."
** map shouldn't load when it's not visible (otherwise it ends up the wrong size)
** in IE6, company page tabs are all yellow (the private fields colour), even though you're not logged-in
** company records tabs don't make the cursor a pointer
!Morning session (from 6:40 to 8:35)
*Going through tests written yesterday
** found out that Python default arguments are shared between subsequent calls of a function
*Working on my tickets
!Afternoon session (from 13:05 to 13:15)
* Review call with Adam
<<readLog>>
!Things to find out about at Avox
# what subscription models they'd like to put in place, as this affects the way we set up how people have access to fields and records, and what usage data we track
# more detail about user stories that are not making sense e.g. what clients expect to see when they log-in
!Afternoon session (from 12:30 to 15:30)
# Writing up [[Avox Wiki-data: review meeting, 9th September]]
# Exploratory work necessary to write [[Avox Wiki-data: estimate of development gaps to V1 launch]]
# Writing that estimate
This is Phase 1:
- setting up the nav structure, styling and animation - 1hr - DONE
- image pagination - 15mins - DONE
- animated transitions between pages - 2hrs - DONE
-- got to add hash-tag nav too
- setting common typography - 30mins - DONE
- homepage image rotation animation and removal of pagination - 1hr - DONE
- typography for blog-like pages (news/projects) - 1hr - DONE
- setting columned text on columned pages (people, at least) - 15mins - DONE
- communication - 1hr - DONE
Done, pending a bit of IE transition fixing, which Ben is telling me more about
-----
I'm looking at the nav structure first.
Added animated image transitions.
Etherpad for this phase: http://ietherpad.com/4NAlpXYD6F
- automatically handling letter-boxing of images - 20mins
- setting up WordPress structure for page images - 1hr
- setting up WordPress structure for pages and sub-pages - 30mins
- creating nav structure from WordPress page structure - 1hr
- composing a new WordPress theme to display content - 3hrs
- setting homepage up as WordPress homepage - 20mins
- setting up category-based structure for news/projects posts - 30mins
- image treatment for inline images in news/projects column - 20mins
- section of the theme for the news/projects list - 30mins
- section of the theme for the columned pages (people, at least) - 15mins
- WordPress installation and configuration - 30mins
- seed content in WordPress so there is something to show - 30mins
- training CSA (with Ben?) - 2hrs
- communication - 1hr
Looking into payment processors e.g. PayPal and how we take deposit payments directly from a webpage - do they send confirmations somewhere for example?
Q's for Nick:
- how much should the holding deposit be?
PayPal
Supports IPN - using http://www.postbin.org/p5e6ay
"You can partially or fully refund a buyer within 60 days of receiving their payment. "
Can use own button
Some variables are exclusively for your own use, such as order management.PayPal returns the values that you send through Instant Payment Notification exactly as you sent them. For this reason, they are called passthrough variables. Their values are not recorded or used by PayPal.
The following are passthrough variables:
- custom
- item_number or item_number_ x
- invoice
- ''are there any more?''
''now, how do I test in the sandbox??''
Amazon Simple Payments also supports IPN
Sorting the Myriad Pro problem - it just doesn't look good in grey on Windows.
Reviewing SS process maps and making suggestions.
Reviewing Glenn's property manager doc in advance of conversation.
Setting up local wordpress on DynDNS to test the setting up of PHP WP plugin to accept IPN's from PayPal
Conversation with Glenn.
- mail-merge is a really helpful PMS thing (time, error-rate)
- events in calendar: annual reminders e.g. gas checks, setting reminders for himself e.g. confirming cleaner bookings
- pain: getting paperwork back signed (new tenancies or renewals), rent payments upfront, needing to remember to send out reminders to tenants (said that automated reminders would be great e.g. automated SMS to students "you have a booking today!")
- going to ring back on Wednesday with some more thoughts
Looking into how to refund deposits
Sorting out API docs on MAD and explaining how to login over email.
Fixed up the applicant dash to look like the admin dash and show the booked viewings and paid deposits within a property's box.
Making sure accounts get created when a PayPal IPN is received, if they do not already exist.
Testing. Failed. Deposit payer has new account created, but deposit doesn't show up on their dash. Fixed.
partly fixed stickyfooter on dash - the jbase stuff is creating too much space at the footer
added property-dash.js
removed applicant dash redundant sections
added pay deposit button & sliding link thing to applicant dash
added "your name" field to property page
amended inbox_save_post to use deposit-payer's name when creating account - using "display_name" attribute of user account
amended booking system to send booker's name
amended processViewingBooking to use booker's name as when creating account
added client-side validation of person's name when paying deposit
modified:
dash.php
functions.php
header.php
properties-page.php
js/sprinkle.js
property-dash/
-- applicant-dash.php
-- footer.php
-- js/ (new)
---- property-dash.js
Thinking and sketching for the interface
Sorting install problems.
Getting the test page running in a browser (it was maven-qunit'ised before).
Reviewing use-cases, adding to sketches and ideas. Asking more questions.
Getting going with new demo participants.
Getting going with the demo web app.
TO-DO: handle /checkauth response; make sure admin URL's not accessible without logging in
Security for the booking system, so people can't get in without logging in.
http://code.google.com/apis/accounts/docs/OpenID.html
Getting OpenID endpoint:
{{{
GET https://www.google.com/accounts/o8/id, set Accept header to "application/xrds+xml"
response:
<?xml version="1.0" encoding="UTF-8"?>
<xrds:XRDS xmlns:xrds="xri://$xrds" xmlns="xri://$xrd*($v*2.0)">
<XRD>
<Service priority="0">
<Type>http://specs.openid.net/auth/2.0/server</Type>
<Type>http://openid.net/srv/ax/1.0</Type>
<Type>http://specs.openid.net/extensions/ui/1.0/mode/popup</Type>
<Type>http://specs.openid.net/extensions/ui/1.0/icon</Type>
<Type>http://specs.openid.net/extensions/pape/1.0</Type>
<URI>https://www.google.com/accounts/o8/ud</URI>
</Service>
</XRD>
</xrds:XRDS>
}}}
Endpoint is the <URI> node.
Sending an authentication request:
{{{
var url = "https://www.google.com/accounts/o8/ud";
httpReq('post',url,null,null,null,'openid.ns='+encodeURIComponent("http://specs.openid.net/auth/2.0")+'&openid.return_to='+encodeURIComponent("http://example.com")+'&openid.mode='+encodeURIComponent("checkid_setup")+'&openid.claimed_id='+encodeURIComponent("http://specs.openid.net/auth/2.0/identifier_select")+'&openid.identity='+encodeURIComponent("http://specs.openid.net/auth/2.0/identifier_select"));
}}}
The response (sample):
{{{
http://www.example.com/checkauth
?openid.ns=http://specs.openid.net/auth/2.0
&openid.mode=id_res
&openid.op_endpoint=https://www.google.com/accounts/o8/ud
&openid.response_nonce=2008-09-18T04:14:41Zt6shNlcz-MBdaw
&openid.return_to=http://www.example.com:8080/checkauth
&openid.assoc_handle=ABSmpf6DNMw
&openid.signed=op_endpoint,claimed_id,identity,return_to,response_nonce,assoc_handle
&openid.sig=s/gfiWSVLBQcmkjvsKvbIShczH2NOisjzBLZOsfizkI=
&openid.identity=https://www.google.com/accounts/o8/id/id=ACyQatixLeLODscWvwqsCXWQ2sa3RRaBhaKTkcsvUElI6tNHIQ1_egX_wt1x3fAY983DpW4UQV_U
&openid.claimed_id=https://www.google.com/accounts/o8/id/id=ACyQatixLeLODscWvwqsCXWQ2sa3RRaBhaKTkcsvUElI6tNHIQ1_egX_wt1x3fAY983DpW4UQV_U
}}}
Looks like openid.identity is the value you want to keep track of people. So this lets you know who is creating accounts - you can then make sure that they can only administrate their own SweetSoft account.
If we also want the person's email address, we can add:
{{{
&openid.ns.ax=http://openid.net/srv/ax/1.0
&openid.ax.mode=fetch_request
&openid.ax.required=email
&openid.ax.type.email=http://axschema.org/contact/email
}}}
In the response will be:
{{{
&openid.ns.ax=http://openid.net/srv/ax/1.0
&openid.ax.mode=fetch_response
&openid.ax.type.email=http://axschema.org/contact/email
&openid.ax.value.email=fred.example@gmail.com
}}}
See [[FCC Paragon robot]]
Trying out SauceLabs with soda.js - works. Exciting.
!Pre-afternoon session
#Unmeasured amounts of email during the day
!Afternoon session (from 16:10 - 17:30; 20:20 - 20:50)
#Adding detail to [[Avox Wiki-data release 1 user stories]] so I can estimate the amount of time needed
!Morning session (from 9:50 to 17:50)
* Planning
** Get H-CAMA/AIM exporting to CSV
** Want to add metadata to the form when filled in: name/unique ID, date of assessment, name of assessor
*** If we were exporting to a new file, we could use these in the filename
*** If we are online and exporting to a popup, we might want to just display these fields
* Adding ExportAIMPlugin to get the AIM form results into CSV
** Found that Chrome doesn't set the title of popup windows that don't have URL's, and displays "about:blank" at the top of the popup
** Was wondering why option macro wasn't working in H-CAMA form - looks like I've interrupted the onchange event...
*** what's happening is the input is not getting an onchange handler...
*** it's the TemlpatePlugins' fault
**** [[this|http://stackoverflow.com/questions/595808/is-it-possible-to-append-to-innerhtml-without-destroying-descendants-onclick-fun/595825]] is why - innerHTML += text removes the event handlers from the elements in innerHTML
** If you're online, you'd need to copy the csv into a text file and import into Excel
** If you're offline, the export can save directly to a new file for you
** Reminded myself: when opening a child window, {{{window.opener}}} accesses the parent window (document object at {{{window.opener.document}}})
** commas are escaped in CSV by enclosing a field in quotes
* Adding a disclaimer to the bottom of each tiddler (used [[MPTWTheme#Stylesheet]])
* Reset the AIM options when clicking the reset AIM/H-CAMA button
* Next:
** Default option for key problem on AIM to be "No", remove "Please select"
***
** Allow no more than 6 key problems
*** done
!Afternoon session (from 15:15 to 15:40)
* Adding placeholder on homepage to say that there is a new design coming on 14th
!Morning session (from 9:45 to 10:20; 12:00 to 12:40)
* Meeting preparation
* Call with Paul to reschedule
* MAD API demo
** add link to [[wiki-data matcher]] - done
* Updating record count
!Afternoon session (from 14:30 to 15:30; 15:40 to 18:45)
* Expanding RMI/Challenge/Request pages and making personal information work as a modal dialogue
** this is all fine, but moving the ReCAPTCHA around is proving difficult
*** after experimenting with colorbox and shadowbox, I'm trying a plain modal dialogue - tutorial [[here|http://foohack.com/tests/vertical-align/dialog.html]]
** having sorted loading the personal info panel into a modal dialogue box, I also solved a problem of the input fields not being inside the form by copying them back to the form as hidden fields
*** I'm now getting an exception in DependentInputs
**** turns out I get this even in wiki-data.com...
**** the problem is that the labels and inputs in the right panel weren't grouped into rows - fixed
** ''must remember to check the URL for failed captcha and display the modal dialogue if I need to''
** ''having problem getting form to submit using JS now''
<<readLog>>
Day.
!stories, writing from the point of view of a customer:
* Extra records: as someone in the onboarding process, I'd want to be able to get full access to records I'm not subscribed to
** I'd want to put my internal ID against that
* Search: as someone searching, I'd like to be able to use my internal ID and other fields I know I am subscribed to to search by
* Audit: I'd like to see the latest thing that happened to a company and what the corporate action was that caused that (audit sources and ordered-by-time)
* Hierarchy viewer: I like having colour in hierarchy viewer; we have our own
** I wouldn't expect percentage ownership, as that's difficult
* Suggesting changes: I'd like to be able to tell you about a change
** I'd like the common changes to be available as selections
** I think the changes of the hierarchies are more common
* Managing: as a super-user, I'd like to see the outcomes of challenges to see whether we were correct and how many challenges did we file
** I don't imagine challenging many - maybe 4 or 5 a month
* Statistics: Directors would like to be able to see statistics proving value to keep Avox and us on their toes
* Benchmarking: I'd like to know where I stand with respect to the rest of the clients
* Statistics: as Avox, I'd like to have better management information about maintaining records now that a lot of our business is to do with maintaining data rather than bringing on new data
* Feedback: as Avox, I'd like to know if clients are applying feedback
* Impact: will ask analysts
* Resourcing problems: I don't have tools to say that things like different addresses are the same - we look at Afrikaans addresses and English addresses which are different formats (number at back for Afrikaans address, which is like the Dutch format)
* Loading: I'd like a uploading mechanism on system... I have to email a team of analysts to update a number, then there's another set of eyes that approve change... If I could grab some files from Avox, I could send them straight for approval
** Here are all our GB records - if I can select them, of these records we are missing say 100 VAT numbers, can we ask for those from Avox, populate them and then upload them; we don't want to pull out records that we don't subscribe to
*** this would be a direct interaction through the API
* Security: as a client of Avox, I don't want my competitors to see the information I am watching
* Holes: as a salesman, I don't want people to see that we have unsightly holes in the data
Seems like the phase 2 version is online access to an equivalent of the weekly dumps. There is evidence that clients will not continue to receiving files. First bit of phase 2 is login access to everything we have now. A couple of weeks. We'll need more complete fields from Greg to get the shiny thing live
!Some design feedback
taking post-code out makes less sense than taking out the country
logo too busy
colour contrast
white on a dark background is harder to read than dark on a light background, particularly for results display (alternating is good)
E.g. jigsaw.com - predominately white, bands of colour
Adam is going to get hooked in
Tagline "open source, open data"
Map is taking up too much space
Primarily, the complaint is a contrast problem
logo - new brief
matches the tagline
for now, take the logo out
!Actions - next two weeks
Make a login work for Ollie, Brett and Ken in the next two weeks, so they can show the full search
Figuring out with Greg what the architecture is going to look like
Working with Adam on the way things look
The second bit of phase 2 is the user stories "access", "extra fields", "info from public site" and "search other fields" (which came today) - get the time scales sorted for this
!Write-up
(sent to Chris Dent)
Hey,
I came into the workshop today wanting to understand more about what
Avox are wanting their clients to be able to do on the client site
(named "My Avox Data"). I had a session with Greg and a Java coder
called Martin, and then a session with the management team and a
client called Anna.
From the point of view of the clients, they can see benefit in being
able to log-in to a site and update their Avox "portfolio" i.e. what
they have access to. This looks like access to a set of fields on a
record that are not visible on the public site. This carries with it
the idea that the interaction with wiki-data.com is similar to when
they're not logged in, but they can search across a greater number of
fields and see more information on companies. This story about
logging-in as a member of a bank and getting extra data about a
company you have an interest in is the focus of the next phase of
development.
There are two things that will be allowed to happen before serious
work to fulfil that story starts: figuring out how hard it is to get
logged-in individuals access to their own "fat" records, where the
definition of "fat" can be different for each company; and making
sample logins for three Avox sales guys (Ken, Ollie and Brett) that
show what things would look like if you had full access to all 70+
fields on all records. I am aware that the combination of record-level
and field-level access controls is tricky business, and I have agreed
that in the face of high technical barriers, we will structure the
developments so that the product can be used to test what customers
want and what the market wants. This compromise is designed to help
avoid making technical decisions that are hard to reverse.
My session with Greg and Martin produced some interesting artifacts:
# "Avox XML"
** Martin suggested that the export format for Avox data be
standardised into a XML structure that would be useful for more than
just the wiki-data project, which provides an incentive to do it; an
XSL could transform this into a format suitable for import into
wiki-data (Martin uses XSL's internally); they have made a start on
XML export for a different project
# CSV export of more data fields, including client identifiers and
audit trail sources
** Greg had previously produced a flat-file extract for another
project, containing many more fields than we have now and linking
records to clients. There is not a complete breakdown of records for
each client, but it seems like the basis of something useful for
showing what clients should get
I suggested an option where we create separate instances of the
wiki-data database for each client, with each database containing only
the data that client has access to. Greg expressed distaste for this
idea and said the duplication of the data would be hard to maintain. I
think it is a structure worth contemplating, given the potential for
securing each database against data leakage to other clients.
On the subject of data leakage, everyone said it was very important
that clients are not able to see what companies other clients are
watching.
J.
!Morning session (from 11:30 to 15:40)
#It turned out FogBugz was not appropriate for this project, since there is no way to make the site public. I reviewed alternative options and have settled for this setup:
** This site remains the place for overal project planning and development log
*** We'll evolve what stays on here and what moves
** Lighthouse is being used for milestones and development tracking - http://wiki-data.lighthouseapp.com
** The source remains on tiddlywiki.org's subversion repository (although may migrate to git to integrate with Lighthouse)
#Updating the lighthouse site with the phase 1 performance improvement tickets and outstanding phase 1 development tickets
#Writing about the phase 2 planning
** [[Avox Wiki-data phase 2]]
#Updating team
#Emailing IDSI to get process started of sorting out disk read problems
!Morning session (from 11:50 - 12:15)
#Reviewing emails from Avox with project information
!Morning session (from 11:00 to 12:40; 13:15 to 14:30; 14:45 to 14:55)
* Catching up on status of test server
* Addressing prioritised bug list from Adam
** fix results table background in IE6/7 - DONE
** added padding to thanks on suggest, challenge and request pages; removed duplication from request page - DONE
** made sure backnav button only appears on JS version - DONE
** fixed request page - DONE
* Other problems found
** JS errors thrown on request page - DONE
*** fixed by not trying to make table use DependentInputs when not necessary
* The ongoing list (should make this Lighthouse tickets if this goes on beyond today)
** overlapping links on home page - DONE
** add Home and Back buttons to company page - DONE
** in IE6, company page is really all over the place layout-wise - FIXED
** fix the colours on the static page tables - the text should be black - DONE
** in IE6, company page tabs are all yellow (the private fields colour), even though you're not logged-in - FIXED
*** turns out this is because IE6 doesn't recognise CSS selectors like {{{ .private.tab }}} - it just uses the final class in the rule
** ajax_search doesn't stop showing "loading results..." - FIXED
** background colour on links in footer (not light enough) - FIXED
** company records tabs don't make the cursor a pointer
** company page has no space between tabs and action buttons in IE6/7
** company page has footer half-way up page in IE6/7 - HALF-FIXED
*** moved footer down, needs checking in IE6/7
**** it's all totally messed up
*** have just pushed footer down a fixed height as a short-term fix
** map shouldn't load when it's not visible (otherwise it ends up the wrong size) - HALF-FIXED
*** there is a poll to check if the map is visible before trying to load
** add text when no results are found encouraging people to suggest
** add text when people suggest encouraging people to search first
!Afternoon session (from 15:10 to 17:30)
* Talking to Chris about test server technical problems
* The list
** operational state is not being populated - FIXED
** google map doesn't come up for all records, even records that do show maps on the current site - CAN'T CONFIRM
** company record layout NOT fixed - relative positioning of record means that left-side is aligned to tab, not left-side - FIXED
*** in IE, after removing a class that had specified "visibility: hidden", the element didn't become visible again even though visibility went to "inherit"
** company records tabs don't make the cursor a pointer - FIXED
** company page has no space between tabs and action buttons in IE6/7 - FIXED
** company page has footer half-way up page in IE6/7 - HALF-FIXED
*** moved footer down, needs checking in IE6/7
**** it's all totally messed up
*** have just pushed footer down a fixed height as a short-term fix
** map shouldn't load when it's not visible (otherwise it ends up the wrong size) - HALF-FIXED
*** there is a poll to check if the map is visible before trying to load
** add text when no results are found encouraging people to suggest - DONE
** add text when people suggest encouraging people to search first - DONE
* Speaking to Chris Morton about the server
** Chris is going to get in touch with someone who is versed in Linux for help
!Afternoon session (from 14:55 to 15:10)
* Fixing form submission problem
** problem is if you have an input named 'submit' - then {{{document.forms[0].submit}}} refers to the input, not the submit function
*** this is a problem even if you use jQuery to trigger the submit event
** this works, ''must handle errors visibly on page''
Site is live! http://www.bellpottinger-sansfrontieres.com
From 18:25 - 18:55
*Trying to fix any IE7 problems - key problem is that the tagriver lines can spill onto two lines, which is annoying as typically this causes the tag separator to show up on the line below and the left-side of the line is indented by about a tag separator's width.
!Afternoon session (from 14:30 to 18:15)
#Cataloguing some more bugs that Ken found
#Syncing SVN repos (bug [[#11|http://wiki-data.lighthouseapp.com/projects/39178/tickets/11]])
#Closed:
**[[#6|http://wiki-data.lighthouseapp.com/projects/39178/tickets/6]]
**[[#31|http://wiki-data.lighthouseapp.com/projects/39178/tickets/31]]
**[[#1|http://wiki-data.lighthouseapp.com/projects/39178/tickets/1]], [[#28|http://wiki-data.lighthouseapp.com/projects/39178/tickets/28]] (duplicate)
**[[#12|http://wiki-data.lighthouseapp.com/projects/39178-wiki-data/tickets/12]]
**[[#27|http://wiki-data.lighthouseapp.com/projects/39178-wiki-data/tickets/27]]
**[[#25|http://wiki-data.lighthouseapp.com/projects/39178-wiki-data/tickets/25]]
!Evening session (from 18:45 to 19:10)
#Continuing with clearing tickets...
**[[#2|http://wiki-data.lighthouseapp.com/projects/39178-wiki-data/tickets/2]]
!Morning session (from 9:40 - 12:50)
#Installing TiddlyWeb on Avox's Ubuntu machine
** Useful guide here: http://tiddlyweb.peermore.com/wiki/#%5B%5BInstalling%20on%20Ubuntu%5D%5D
** Installed subversion so I could sync with the [[wiki-data repository|http://svn.tiddlywiki.org/Trunk/association/serversides/tiddlyweb/verticals/wikidata]]
** Development server now up-and-running at http://217.9.192.9:8080/
#Looking into how to best structure the store for performance
** Spoke to FND about searching the SQLstore - he pointed out that only /search is handled by the store implementation, filters are not. I think filters are a key part of the search experience here, and they need to be performant, so I think they'd have to be supported by the store (to take advantage of SQL query speed, for example)
** On the subject of which database to use:
*** "A lot of developers in my area [...] use Postgres for local development and test, due to it being fairly lightweight compared to Oracle." and "MySql does have some problems though, and personally, if I had a choice: postgres would be the chosen FOSS DB." - from BT discussion
** Reading sqlstore source and sqlalchemy introduction to understand what the capacity is for the sqlstore to directly search by various fields
*** I now know that sqlalchemy allows me to create queries that filter using extended fields
*** I'm thinking that a query is going to look like a query for the most recent "srevision" of a "stiddler" that match a particular set of "sfield's". I'm not quite sure at this point whether a query can be issued across properties that have joined by a relation. Something like:
{{{
collection of sTiddlers with most recent sRevisions:
tiddlers = session.query(sTiddler).join(sTiddler.revisions).filter(sRevision.rev == sTiddler.revision()).all()
collection of sRevisions with a matching sField
matchField = "country"
matchValue = "UK"
tiddlers = session.query(sRevision).join(sRevision.fields).filter(and_(sField.name == matchField, sField.value == matchValue)).all()
}}}
** I need to figure out whether the above works (i.e. test it) and also figure out how to make the query in one go, since the first query will return loads of results and I'd rather not handle that
** It looks like {{{subquery()}}} could be useful along with an alias for the results - see [[here|http://www.sqlalchemy.org/docs/05/ormtutorial.html#selecting-entities-from-subqueries]] - something like:
{{{
stmt = session.query(sRevision).join(sRevision.fields).filter(and_(sField.name == matchField, sField.value == matchValue)).subquery()
fieldAlias = aliased(sRevision, stmt)
sTiddlers = session.query(sTiddlers).join(sTiddlers.revisions).filter(sRevision.rev == sTiddler.revision()).join(fieldAlias, sRevision.fields).all()
}}}
!Afternoon session (from 13:30 to 17:35)
#Improving the AJAX loading of the accordion
** Josh improving hover behaviour of nav links
** Animating the appearance of the loaded content so it smoothly pushes the left column smaller and the right column to the right
*** animations working fine
*** discovered that AJAX page loading in IE6 is really slow
**** looks like a problem with async requests that load pages that request other resources (e.g. a stylesheet) just 404, whether or not the resource is there, which causes IE6 not to respond (see [[here|http://stackoverflow.com/questions/1790911/ie6-updatepanel-must-move-mouse-to-refresh-after-async-call]]
**** ''should address this at some point'' - could just strip out resource calls from the requested page (which would just be a CSS link at this point) - ''fixed''
** Josh putting together HTML/CSS for consultants gallery
#Plan for the week (see below)
#Tweaking AJAX content pane-opening transition, so main pane slides down from black line
** sorted
#Looking into problem with jump in nav slidedown
** presuming something to do with width not being set on thing sliding
** sorted by setting widths as soon as page loads (I wasn't setting them correctly anyhow, just getting it out of the way early now)
#Adding the tag cloud in to the main page
** The line-heights for the tags are not correct once they've been added back into the tag river
!Plan for the week (assuming we're going at 4 units a day)
** Release 1:
*** Contact page content (1.5)
*** Consultants content / hover effect (5.5)
*** About content (2.5)
*** Dummy tag-river w/ infinite loop effect (1)
** Release 2:
*** Wordpress setup, enables tag infrastructure and CMS for consultants gallery (5.5 of consultants gallery; tag-river using CMS for tags (1)
*** finish AJAX accordion for other pages (2)
*** try to get something on real server
!Evening session (from 19:20 to 20:20; from 21:55 to 22:15)
#Figuring out what's wrong with the tag river
** Putting the {{{reset.css}}} on to the tag river experiment to see if that is the cause of the problem
** Looks like adding {{{body {line-height: 1} }}} causes some problems, will have a look into this
** Looks like setting {{{line-height: normal}}} to the river fixes things
#Making the tag-river an infinite loop
** ''not working''
#Consultant gallery not quite working
** ''need to sort out heights on hover effect''
!Afternoon session (from 14:35, with Josh to 16:10)
* Figuring out how to get the tags we need for the different pages out of Wordpress
** all tags (for tag river)
*** {{{ get_tags(); }}}
** tags for a video (for film reel)
*** {{{ the_tags(); }}}
** all tags for a consultant's videos (for profile)
*** done by uniquifying an array of the tags
**** learnt about Wordpress' {{{ get_the_tags() }}} function
* How do we get the videos in as playable vids?
!Afternoon session (from 12:55 to 14:20; 16:10 to 16:30; 16:40 to 18:40)
* Two things to do before launch
** resolution
*** sort header in 1024 x 768 resolution - remove developer link? - DONE
*** sort results table in 1024 x 768 - reduce padding? - DONE
**** just reduced width of table (from {{{.bigwrap}}} to {{{.wrap}}}), it seems to cope
** private data display
*** When the ‘Private Details’ tab is accessed, as a logged in user, the table holding the data exceeds the height of the screen and there is no vertical scroll. Is it a case of moving the footer further down the page? Here is an example. Note that this scenario seems to be dependant on the volume of additional data available: http://tw.wiki-data.com/bags/avox/tiddlers/16835057.html - FIXED
* Why does the page scroll right in IE? - FIXED
** I don't know why having inline h2 and span in {{{.searchtitle}}} were causing a big horizontal scroll in IE, but making them into floats fixed that
** I don't know why having a overflow:auto container without any padding causes the vertical scroll bars to appear when I have a h2 and a span floated left inside it - this is in FF
* Make the results table not overflow its container when table contains long strings
** Is this a problem caused by the cloned header?
** no. I've looked at this in a simplified page with only the raw table and minimal style in and the overflow still happens. I'm having a look to see what causes this overflow
*** it looks like it is just a problem with too much text in too small a space
* Made the re-design live
* Found a problem: in IE6, when no results are found, everything in a {{{.wrap}}} jumps to the right before resetting itself
!Afternoon session (from 12:30 to 13:05)
* Making sure CAPTCHA and form errors are visible on suggest page
* Making personal info section into sub_template for sharing on RMI/suggest/challenge pages
** ''will need a rethink for request page''
** ''also, on challenge (and request) page, the overflowTable function needs to take the section headers into account''
!Evening session (from 21:00 to 21:45; 22:40 to 23:20; 23:50 to 00:50)
* Sorting RMI/challenge page styles
* Testing cross-browser
** IE7 problem: "Can't move focus to the control because it is invisible, not enabled, or of a type that does not accept the focus."
*** ''will sort this later'', prioritising form verification
* form validation
** trying this: http://docs.jquery.com/Plugins/Validation
** I've found that ''the error fields from the validator mess with my dependentInputs hidden fields, causing exceptions (maybe) - see by typing into a text box on the suggest page''
** first thing to check tomorrow is the validation of the CATPCHA form: {{{$('#tempForm').validate();}}}
<<readLog>>
!Morning session (from 9:45 to 13:25)
#Asking Osmosoft mailing list for help finding a workshop venue and people to speak to about it
#Speaking to Wallacespace about hiring for wiki-data workshop
** They called back - £200 per person
#Continuing clearing tickets from [[Phase 1 complete|http://wiki-data.lighthouseapp.com/projects/39178-wiki-data/milestones/52540-phase-1-complete]]:
** [[#33|http://wiki-data.lighthouseapp.com/projects/39178-wiki-data/tickets/33]]
** Noticed [[#26|http://wiki-data.lighthouseapp.com/projects/39178-wiki-data/tickets/26]] had been fixed when closing [[#2|http://wiki-data.lighthouseapp.com/projects/39178-wiki-data/tickets/2]]
** [[#22|http://wiki-data.lighthouseapp.com/projects/39178-wiki-data/tickets/22]]
#Call with Amit about the next iteration of his design
** Going to concentrate on data-led design and move from there to slick
** He's building from the table data outwards
#Continuing with development:
** [[#37|http://wiki-data.lighthouseapp.com/projects/39178-wiki-data/tickets/37]]
** [[#19|http://wiki-data.lighthouseapp.com/projects/39178-wiki-data/tickets/19]] - invalid
** Fixed problem with handling of query strings that have an advanced field name but no value to go with it
** [[#21|http://wiki-data.lighthouseapp.com/projects/39178-wiki-data/tickets/21]]
** [[#20|http://wiki-data.lighthouseapp.com/projects/39178-wiki-data/tickets/20]]
#Speaking to Chris about next things for him to be doing
** I would like to make concrete the plan for future-proofing the database<->tiddlyweb interactions
#More dev:
** [[#24|http://wiki-data.lighthouseapp.com/projects/39178-wiki-data/tickets/24]]
!Afternoon session (from 14:00 to 15:30; 16:15 to 16:45)
#Tackling state drop-down mapping to state names
** [[#3|http://wiki-data.lighthouseapp.com/projects/39178-wiki-data/tickets/3]] - still open
#Made table default to sort on "Legal Name"
#Off to Calvert Avenue gallery to see about workshop
!Evening session (from 18:05 to 4:35 (only billing 18:05 - 1:35))
#Fixing problem introduced when standardising country of registration fields to registered country
** Noted in comment to [[#40|http://wiki-data.lighthouseapp.com/projects/39178-wiki-data/tickets/40]]
#Setting up VNC with Nick to test out IE7
#Tackling [[#3|http://wiki-data.lighthouseapp.com/projects/39178-wiki-data/tickets/3]]
** Hard!
!Afternoon session (from 14:15 to 18:15)
#Fixing IE header layout
#Adding default serializer override so browsers not presenting "text/html" Accept header (IE) still get wikidataSerializer-rendered content
** Problem: localhost now giving '400 Bad Request: Search system not implemented'
*** Fixed by updating TiddlyWeb to 0.9.64
** New problem: searches on localhost not returning any results
*** I think that's because Whoosh hasn't indexed the database on localhost yet
*** Found a new twanager command to do this: {{{twanager wreindex}}}
*** Sorted
** Emailed Ken and Brett to check, as I think it's fixed
# Fixing "Blank searches should not be triggered by blank company name query when other fields e.g. City of Incorporation are present - a blank search returns no results"
** This seems to have been fixed by recent updates from Chris
** Emailed Chris to confirm
# Fixing "If you click to drag a column, clicking a header to sort stops working"
** Fixed initial problem
** New problem: have found that when moving more than one column, the column being replaced doesn't end up in the right spot in you shuffle from right to left
*** That's because the mechanism is for the column picked up to be pushed into the array and the column being replaced gets shuffled to the left, whereas it needs to shuffle to the right if the new column is coming from the right
*** Have fixed with use of splice
** New problem: not all sorting works correctly after moving columns; the headers are set correctly
*** looking into this
*** patched DataTables to allow sorting to work when column headers array is not in the same order as the data array
**** have reported on the DataTables forum at: http://datatables.net/forums/comments.php?DiscussionID=615&page=1#Item_1
** Seems to work
!Evening session (from 21:30 to 22:30)
# Creating an interface for hiding/showing columns
!Afternoon session (from 13:00 to 13:30; from 15:30 to 19:00)
#Composing summary email of thoughts about direction for wiki-data and MAD
#Catch up call with Paul and Adam (15:30 to 15:40)
**not happening at the end of this week
**2hrs per day this week for amit
**looking not much past this weekend for either of us
**avox break on 24th Dec
**little activity in data community over xmas - happy if everything done by end of week 1 in jan
**''send Adam the link to the public folder''
#Making the search results page with Josh (from 16:00 to 18:45)
** good progress, need to check in IE6
!Morning session (from 11:00 to 12:15)
* Updating record count
* Getting revision history of index page to extract previous record counts
** http://spreadsheets.google.com/ccc?key=0AgQJ7FGUGIp_dGlBN1AxMnNidmRldzRoUFdfTHIzQ0E&hl=en
* Speaking to Greg about Linux support in the future
!Afternoon session (from 14:30 to 15:00)
* Making RMI/Challenge/Suggest pages tighter
!Morning session (from 8:20 to 9:30)
* fixing tempForm validation
* more detail on dependentInputs / validate interference: when form is validated, the operational_country dropdown goes to the blank option from "Please select...". Also, the generated error label is for the hidden input that is generated by dependentInputs
* current problems:
** the dependentInputs dropdown interference
*** fixed
** the not validating dropdowns - it accepts "Please select..."
*** fixed
*** In the process realised that I've missed a trick with dependentInputs - I can use {{{value="..."}}} on an option element, which overrides the text inside. I think this means I can do without the hidden input fields...
!Morning session (from 11:30-11:45)
#Looking into IE bugs
#Spoke to Paul about workshop ideas:
** put me in touch with @iand - he's interested and wants an email
** join #SWIG IRC group - lots of semantic web people hang out there
!Evening session (from 21:10 - 21:30; from 21:50 - 23:35; from 23:55 - 1:10)
# Improving interface for hiding/showing columns
# Figuring out what are going to be the fields displayed by default
** Gone with:
*** avid
*** legal name
*** trading status
*** (country of registration, when available)
*** operational street 1
*** operational city
*** operational state
*** operational country
*** operational postcode
*** challenge
*** request more info
** Altered table to only show these columns
** Figuring out how to get the table to not display until DataTables has finished processing
*** I think DataTables needs it 'visible' in some way to process things correctly
*** Ah, if you hide the table before processing, you can't use auto-width columns
**** Have reported here: http://datatables.net/forums/comments.php?DiscussionID=622
# Improving layout of hide/show box
** ''There is a problem in Safari where the last column entry has its controls pushed down a line (but still right-aligned)''
*** I wonder if it's something to do with possible different ways Safari and Firefox treat floated elements - they both seem to apply {{{display: block}}} when an element is floated, but they might treat the block elements differently
# Adjusting column footer hide buttons to work with the new hide/show system
** I'd changed the way the function that hides columns works so as not to count visible columns, but do a lookup for a titles match
!Morning session (from 10:55 to 14:05; 14:45 to 16:15; 17:00 to 18:00)
* JS problems
* BPSF review meeting with Neil
* Debrief
* More bug action
** trying a new technique for removing the pauses in-between filmreel scrolls - use setTimeout rather than animation queue (reference [[here|http://groups.google.com/group/jquery-en/browse_thread/thread/5411dba1385a287d/8fe71a7e0030c6e9?lnk=gst&q=.animate%28%29+queue+without+pause+between+animations#8fe71a7e0030c6e9]])
** turns out using a ms timer rather than "slow" actually helps
!Morning session (from 11:00 to 13:00)
# Talking to Dickon about the plan for the next 6 months
** Several groups to start using manual; big Plymouth group in January
# Fixing saving problems (identified in email from Dickon)
** Error thrown when trying to create a new tiddler: "Etag incorrect for new tiddler"
** The TiddlyWiki plugins were really out-of-date, so I've upgraded to see if that helps
*** This has caused its own problem - the new TiddlyWebConfig plugin depends on the TiddlyWeb status plugin - I'll install this when I get the password to the server from Chris
** I had another problem where I had a rogue tiddler called "tmp" containing an old version of the TiddlyWebAdaptor - this stopped me being able to save any changes to tiddlers
*** I had to filter out the tiddler using TiddlyWeb's title filter and then save over it: {{{?select=title:!tmp}}}
# Looking into delete problems
** Two tiddlers are appearing on the client with slashes as the last characer of the title e.g. "Balancing \" - however, tiddlers don't appear on the server
** In the case of "Balancing \", there are two tiddlers on the server called 'Balancing "inside and outside" work' and 'Balancing "inside" and "outside" change work'; the "Balancing \" tiddler has these spurious fields:
*** \, and, change, inside\, outside\, work
!Afternoon session (from 14:00 to 15:10; from 16:20 to 18:45)
# Had an idea for how to solve the problem with the tiddlers with '\' characters
** Find the tiddlers on the server that are not being shown in the client and DELETE them using JavaScript
*** Seems to have worked for "Balancing \"
** For "Maintain \", the tiddlers that are on the server, which are not showing up on the client are:
*** Maintain "mind-mindedness"
*** Maintain "Mind-mindedness"
*** Maintain \
**** Funny that the original "Maintain \" is on the server when "Balancing \" wasn't
** The "Maintain \" tiddler has a spurious field of "mind-mindedness\"
** Ok, having deleted "Maintain \" from the server, the tiddler "Maintain \" is still showing up in the client, which is pretty much the same situation we were in for "Balancing \"
*** Will try deleting the unwanted two tiddlers 'Maintain "mind-mindedness"' and 'Maintain "Mind-mindedness"'
*** This fixes the problem
** What I've just noticed is that the only tiddlers causing problems are those with '"' characters in their titles
*** A quick search on tiddlyweb.peermore and trac reveal nothing
*** An experiment: create a tiddler with quotes in the title; see if anything spurious turns up on the server
**** I've found it's happening because the tiddler title is not being correctly escaped in the HTML serializer - I've reported this in Trac, [[ticket 1146|http://trac.tiddlywiki.org/ticket/1146]]
# Setting up multiple bags
** Any new manualisations should go into a personal bag
** We'll use filters in a team's recipe to keep the number of bags down
** We'll have an "Interactive" bag for anything tagged with ClientNotes
*** ClientNotes tiddlers could be double-tagged with any of RiskAssessment, PrescribedMedication, Allergies, OtherWarnings, TakeHomeMessages, ClinicalThemes, ClinicalContactNotes, PresentingProblems, SubstanceUseHistory, DevelopmentalHistory, FamilyInformalNetworkMembers, FamilyHistory, ProfessionalNetworkMembers, PastPsychHistory, PastMedicalHistory or AssessmentOutcomes
***the tiddlers "Formulation and treatments aims", "Care Plan" and "Care Plan - Advice" are also tagged with ClientNotes
** We'll have a "SpecificInterventions" bag, where anything tagged with SpecificInterventions goes in as well as any of the tiddlers those tiddlers tag
*** We'll use filters in teams' recipes to make sure they only get the content for subjects we want them to (each tiddler is tagged with its subject)
** We'll have a bag for everything left over
** Later, we'd have a central bag for Mentalization, one for MBT, and some more, assuming that if people are getting less stuff, we can have more bags
** We'd have one bag for each team
*** YOUS
*** KidsCo
** I've created these bags:
*** editorial
*** interactive
*** specificinterventions
*** yous
*** kidsco
*** amass
*** plymouth
*** derry
#Creating recipes
** The first thing to do is to create the editorial recipe, which represents Dickon's view of the world
*** We'll give Dickon two logins, so he can view as a practitioner and as an editor
*** If he writes a tiddler when looking through this recipe, the tiddler should go to his personal bag if he is a practioner and to the editorial bag if he is an editor
*** If anyone else writes a tiddler, it should go to their personal bag
** When you PUT a tiddler, it will go into the first bag you have access rights to
*** This suggests that the correct structure of the recipe for write purposes is to have the user bag first and the other bags afterwards, since if you can write to a higher-level bag, it makes sense to PUT the tiddler there (probably)
*** However, this gets the structure for read-purposes incorrect, since you want to have the user bag last, so as to pull out content that is particular to you
**** I think the answer is the publishing mechanism (as used on [[ILGA]]) to move content from an editor's bag into the main editorial bag
*** Ah, but we can have recipes that don't load a personal bag e.g. "editorial" and then editors can use these to edit that higher-level content
** This should be sufficient for top-level and personal-level content, ''but not for team-level content - we'll work that out later''
** With the "editorial" recipe done, we need to set the permissions on the editorial bag so that only the "EDITOR" role can edit it
*** Have given dickon the role of "EDITOR"
*** Set the permissions on the bag
** Ought to figure out ''how to create user bags on the fly if they don't already exist'' (we did it for ILGA)
** Locked down the 'system' and 'common' bags so only the 'ADMIN' role could edit (no-one has that role yet)
** Tested - jonl couldn't create a new tiddler - yay!
** Tested - dickon could create a new tiddler - yay!
** ''Since updating the plugins, we have less useful error messages when saving goes wrong - they just do console logs''
# Talking about what to do next
** We'll set up the 'imp' recipe so it saves to Dickon's personal bag (which will need to be created)
** We'll create an interface to show when content is over-writing content from a bag higher up the recipe cascade
** We'll use dickon's account, going through the recipes 'imp' and 'editorial', as a test-bed for this
!Afternoon session (from 15:00 to 19:20)
#Fixing the consultants gallery so it loads all the way after you've opened it for the first time (images not loading means the height is not correctly reported)
** done, but I'm not sure whether it will jump fully open after reaching the restricted height - hard to see!
#Fixing the tag-river so it doesn't mess up if you close its bit of the accordion
** done
#Making the tag-river infinite
** update: I have the tags duplicating themselves, but they only seem to do it once; I am putting new block of tags into a new child div of the river, which might not be the best thing, as I want to be pushing them all down at once (having thought about it, I think the thing might be to create two sets of tags right from the word go). Plus, I need to remove lines that fade away and randomise the flow.
*** Finding quite a bit problem trying to get extra tags to appear in the top... needs more thinking to figure out how to do so and get the increasing opacity correct
Working with Prem.
We did some work on allowing anyone to login and have the app store their credentials in an EncryptedLocalStore.
(If we store the session, how long does that last?)
Left to do on the login bit:
* use a chromeless window
* handle a failed login
* check that storing the password in the keychain is ok
!Morning session (from 11:00 to 14:20)
# Tackling the improved filter description
** "I'd like it to filter immediately."
*** A constraints to begin with: we're going to assume there is a full data set on the client
**** This means we can make the filters work client-side without worrying about loading more data, which is good because the server is not yet sending the total number of results with the response
*** I'll make it so the default field to filter on is "Any field"
**** ''I've asked Chris if it would be possible to support "Any field" as an option for searching'' - at the moment, the interface suggests to you that you can search by "Any field"
**** I've removed the "Any field" search until we can support it so the interface makes sense
*** Ok, the filter filters as you type now, and filters-by-field if you select a field
**** Found by accident that if the main document can pick up the keyup event, which of course it can... perhaps I can neaten the code later by picking up events on containers
*** I've noticed that when you get down to results where the fields are not very long, the table head, foot and body are not long enough to fit the whole width of the table - checking the filter code to see if I can see why
**** I've fixed the problem by using auto-width columns on the DataTable - Allan posted [[a reply|http://datatables.net/forums/comments.php?DiscussionID=622]] explaining how to use {{{visibility:hidden}}} to allow me to hide the table while processing it but keeping it flow so I could use auto-width
**** Turns out that didn't fix the problem, because the table sometimes spilled out from its containing div
***** Setting the table's width to 100% and going back to not auto-width solved the problem
*** I haven't put anything to auto-load more results if they are available on the server - ''I've written to Chris asking what it would take to support this''
** "The filter did not work when I typed in a single term"
*** This works now
** "if there is an existing search, filter it; if there isn't, treat it like an advanced search and submit a fresh search"
*** If someone hits enter, it triggers a new search whether or not we've just done one. I think this is the better behaviour, because it matches people's expectations about how search systems work
*** ''I'm not sure it's clear to someone that pressing enter after typing in a filter triggers the search''
**** This could be due to the placement of the search box making it look like the filters are separate - perhaps a label that says "type to filter the results; hit enter to search again with these fields"
*** I've added a label as suggested above
** "Would it not be logical to leave the filter you have applied visible on the screen and to have a “clear” button next to it?"
*** I'll examine the page query-string and use that to pre-load the search box and filters
*** I'll have to figure out how to get the advancedSearchContainer to appear open if there is a filter
!Afternoon session (from 14:40 to 15:40)
# Adding the query string parameters to the search box and filter boxes
** Done
# Added new [[FixedHeaders|http://datatables.net/release-datatables/extras/FixedHeader/]] plugin from DataTables (Allan bumped it up the priority list after I asked!)
** The scrolling of the headers is not very smooth in Firefox (and IE apparently)
** Spoke to Jeremy - he suggests comparing to Facebook presence bar
!Afternoon session (from 15:30 to 16:45)
#Tag-river
** Having thought about this, I'm going to go for making each line appear as a new, absolutely placed div in the river; all lines will move down at once - experiments on simultaneously animating divs look like the performance is fine
** For a new line, first I'll move any existing divs down and reduce their opacity; if there are more divs than the number of lines in the river, then I'll remove and delete the now invisible div; then I'll prepend a new div, set its position absolute and move it left so it's lined up ready for the words to move in
** done
!Afternoon session (from 12:40 to 13:10; 13:25 to 14:50)
* Changing congal animation so on hover text appears inside image
* Fixing nav/filmreel bugs
* Making filmreel work at lower resolutions
!Morning session (from 10:40 to 12:00)
* Adding gAnalytics to MAD
* Updating contact details for MAD and wiki-data
* Adding email handling for myavoxdata.com
** This is useful - http://www.debian-administration.org/articles/243
!Afternoon session (from 15:15 to 15:40)
* Setting up mail server to route myavoxdata mail to Adam
* Looking into setting up distribution lists with Postfix
** Using [[these instructions|http://ecm.matrix.tuwien.ac.at/flamm/linux/SetupMailman.html]] to set up mailman
*** haven't configured mailman to handle both domains separately
** Web interface at http://217.9.192.9/cgi-bin/mailman/admin/mailman
<<readLog>>
!Morning session (from 12:15 to 13:30)
* Helping Josh fix FlowPlayer
!Afternoon session (from 15:30 to 17:30)
* What does the filmreel need?
** There'll be a set of film div's, with some things in, like title, film URL, consultant, tags, icon
** There'll be the scrolling fisheye-style thing
** There'll be the replacement of the icon with the player
* Looking into existing fisheyes
** [[9 open source fisheye menus|http://www.snap2objects.com/2008/05/03/9-opensource-fisheye-menus/]]
*** [[IconDock|http://icon.cat/software/iconDock/0.8b/dock.html]] has horizontally aligned zooming, which is what I want:
{{{ var confDock2 = {
iconMinSide : 20, iconMaxSide : 100,
distAttDock : 100, coefAttDock : 3,
veloOutDock : 500, valign: 'middle'
}
}}}
** Found [[this description|http://blogs.nitobi.com/alexei/?p=37]] of the process of making a Fisheye component on the Nitobi blog
** Demo [[here|http://blogs.nitobi.com/alexei/media/demos/feye1/2.html]] - very performant on FF, Safari, IE6/7, but source code hard to read as it has had whitespace removed
** Found [[this|http://marcgrabanski.com/pages/code/fisheye-menu]], which has source code available
*** Resizes images by changing the img tags' height and width styles - I think that's the same as how the Nitobi component did it
*** Resizing images like this pushes the other images around, but we'd want to move their positions to keep make the image resize around its centre
* How do I pre-load images correctly?
* PNG fix for IE6 - http://www.twinhelix.com/css/iepngfix/
!Afternoon session (from 12:10 to 13:35)
* Tickets
!Evening session (from 18:30 to 19:30; 19:50 to 20:05)
* Tickets
<<readLog>>
!Afternoon session (from 12:50 to 14:15; 14:45 to 15:05; 16:00 to 17:30)
#Building search page from Amit's redesign
!Afternoon session (from 12:00 to 15:25; 16:15 to 18:10)
#FixedColumnHeaders plugin has introduced a problem with the z-index of the columnPicker
** Fixed by setting z-index of columnPicker to 1
#Overnight, Chris has got the 220k records indexed and the web server started again
** Because the indexing took so long to run and produced massive files, ''the new database and the index hasn't been committed to svn''
#''Found more problems'' with the FixedHeaders plugin:
** cloned table head doesn't update when you hide/show columns
** header starts to move down the page too early
*** I think this is to do with the fixed header being initialised before the advanced search fields are created from the URL query string, which pushes everything down the page
**** Fixed this for the case where the advanced search drops down at page load, but if you add filters as you go, it doesn't update when the header starts scrolling
***** Looking into the FixedHeader code to find out how this works, and looking at the Facebook presence bar to see if I can make a temporary replacement
**** I can update the header after column changes with {{{fnUpdate}}} function, but it's annoying to have to remember to do this
**** fnUpdate doesn't seem to sort out the new offset - ''need to fix this''
***** Fixed with a hack, hope Allan sorts the plugin out...
** Wrote feedback to Allan on the forum about the above
** Also found that {{{fnUpdate}}} needs to be called after filtering...
*** Allan has suggested using {{{fnDrawCallback}}}, but I've detailed on the same forum post why this isn't quite right for me now, as it it fires at unexpected times and not at expected times
#Just noticed the search is not returning results for e.g. "Operational Building=Imex House" when a search for "Operational Building=Imex" reveals that a record exists with "Imex House" as its Operational Building
** Reported this to Chris
#Updated front-page to show 227,217 records being searched and changed main text to be about suggesting example searches rather than apologising for bad performance
#Improving the filtering interface
** At the moment, if you click "add a filter", this opens or closes the advancedSearch box
*** It should add a new filter line, opening the advancedSearch box if necessary
**** Sorted
#'contact form should contain: Email / Name / Country / Company (optional) and a note saying something like "we'd like to know who you are"'
** Made countries list into a variable in a separate template, which can be included in whatever page wants to use it
** Sorted, and improved style a bit so things are not as cramped
#Need to correct challenge and suggest_new pages, as they're trying to pull the wrong fields
** Ah no, they were just having their tables hidden accidentally by style meant to hide the resultsTable
*** Have made that style more specific
# Ken gave some feedback and found some holes:
**I've checked it out and fonts look much better although they also need to apply to the forms for challenging, requesting more data and a new record.
*** For me they do, I think it might be a caching problem on Ken's machine
**Note that the error message still pops up on googlemaps.
*** Have created a key for http://wiki-data.com/ (and all sub-domains)
**** It's worth noting that this key is only valid on pages that are free to access
**When I challenge or request more info on a record or suggest a new record I just get a load of text lines back: (traceback included)
*** countries.html file not committed to svn
**** fixed
**The column headings on the search results no longer let me sort them when I try to drag them.
*** the fixed header is sitting on top of the draggable header
**** seeing if I can pass the click event through
***** have passed event through by tweaking FixedHeaders; also exposed a bug with the columnPicker where I wasn't updating the column list when moving columns around - I am now
**"About wiki-data" and "Partners" sections not yet added (sorry if I'm nagging about stuff you are doing anyway at the moment).
*** Will do as part of gaps
**Need a hyperlink to the Avox website.
*** Will add into the above part of gaps
**Can you make the strings representing the sample searches look a bit nicer? For example, I currently see: "q=Limited "Operational City"=Newbury "Operational Country"=GBR". Why not just call it "Sample Search 1"? The criteria show up on the filter in the search results.
***I've changed the searches to be more fun and put natural language descriptions as the links
#'Links to "sample record", "field spec", "about wiki-data", "partners" and "avox" should be in the page footer'
** Have added static pages for "about wiki-data" and "partners" and added links for the rest
** ''I think "about wiki-data" should be made more concise and updated to reflect the changes in the project since it was written''
#Found a bug with the columnPicker not spilling out of enclosing div if the records table is too short
** Fixed by setting a div lower-down in the hierarchy to control the table overflow
13:30 to 17:00
Sorting out the Owners Direct advert - see [[27th June - Dad's IT problems]].
* improved text and photos
Website - see [[greenwoodservices.co.uk]]
* found [[this|http://www.seomoz.org/blog/making-the-most-of-meta-description-tags]] which says Google only looks at 160 characters of description meta tag (other search engines may do 200+)
* giving [[BaselineCSS|http://baselinecss.com/typography.html]] a go for type css
* built as static, used DropBox to get online, setup web forwarding to send to public DropBox URL
!Afternoon session (from 13:10 to 13:20; 13:30 to 16:10; 16:30 to 17:30)
* Experimental build of a fisheye-style filmreel, having read yesterday how others do fisheyes
* Process:
** get the film thumbs lined up (float: left) and hide their textual information
** give them a relative left position based on their index along the line
** multiply the group until it fills the screen
*** tried a method, in search of efficiency, where I multiplied innerHTML, adding to an array and eventually joining the strings together and whacking this into the wrapper - this fell down when I realised I couldn't get at the HTML of the film div itself, meaning I missed any classes applied (for example)
*** used a method where I cloned the nodes and inserted them one-by-one - this was much less code, although it involves a lot of DOM manipulation (but not THAT much, given that I'm working with a couple of dozen films on a page)
** Now looking at getting the images zoomed up at the correct place
*** I haven't figured out how the images are going to move as some get bigger
** putting a marker in the page which marks the centre of the image when it's at maximum
** have written a function called {{{growToMax}}} to make an image appear at its maximum size, with the images adjacent to it be the appropriate size (the average of their max and min size)
*** need to figure out which images are affected and how their sizes should change if I make an adjacent image grow to max
** I have modified growToMax to animate the necessary images - the image to be maximised, its adjacent images and their adjacent images (which need to be reset to the minimum size)
*** this looks smooth and good. I need to introduce some sideways scrolling and sync that up to the resizing so that the images don't move net-sideways when an adjacent image grows to max
*** when the left-most image grows from min to max, it's right-side moves to the right 150px - the difference between the minimum and maximum size
**** how do the centre-points shift around as images move from maximum to minimum?
**** 8: max - 735, min - 885 or 585; diff 150
**** realisation: the centre-points shift 150 left or right for each image, unless you're maximising the leftermost image, as then the total width of the wrapper changes since there are no more images to the left to grow to half-size
**** how do the centre-points shift around as images move from maximum to half-size?
**** 8: max - 735; half-size - 622.5 or 847.5; diff 112.5
**** this means centre-points only shift 37.5 between half-size and minimum
*** sanity check - am I reducing the height of each image by the same amount between max size and half-size?
**** yes - from 200 max, to 125 half, to 50 min
** I understand the transformation now: of the four images that change size during a move to the left or right, the outermost two maintain their left and right positions, respectively; the four effectively reflect around a position of symmetry in the middle of the combination
*** this position is half-way between a max width, two half-widths, a min width and three spacings - for me, that is (200 + 2*125 + 50 + 3*20) / 2 = 560 / 2 = 280
*** so in this model, when the block of four effectively reflects, the effect on the centre-point of the max->half-size image is that it moves from a position of a half-width + a spacing + half a max-width, to a position of a min-width + a spacing + half a half-width
**** for me, this means the difference i.e. the distance moved is: (125 + 20 + 200/2) - (50 + 20 + 125/2) = 245 - (132.5) = 112.5, which matches the measured amount above
** I've over-complicated this: the problem is that when I transform a block of four as I've described, the maximised image is short of where it needs to be by the width of a min-width + a spacer, so that's how far I need to move the wrapper
*** for me, this is (50 + 20) = 70
*** right, that's got the transformation sorted!
** ''I don't have the images being removed when they disappear off screen, nor appearing to the left when the reel moves right (this is a bit like the problem I had with the tagRiver)''
** Fixed up a little bug in Safari where the css left position was reporting initially as 'auto', rather than '0', so {{{parseInt}}} was returning {{{NaN}}}
* Call with Josh
!Afternoon session (from 16:15 to 18:15)
* Addition of seperators between tags on tag river - done
* Splitting of multi-word tags on tag river, so they come in one word at a time
** the trick here is to cope with multi-word tags, which potentially split onto two lines at the point when the tagriver is trying to figure out which tags go into which lines
*** you could replace spaces with {{{ }}}, which would make them effectively really long single-word tags, but this might lead to a lot of lines with not very many tags - however, it's worth trying as it's easy
*** you could also see whether you could spread the words of a tag between several lines...
** sorting out the overlapping of lines when they animate onto the river (the overlapping is too much with these longer tags)
*** done, made showLine wait for animation completion before calling itself
* Cross-Browser/OS testing
** total problems with IE6, problems with filmreel/tagriver interaction on Windows browers
* Missing fade out of juicy feedback on tagriver (not on experimental version)
** fixed (it was always there, original colour was not high enough contrast to see the effect)
* Problem with first movement of filmreel
** looked into it, haven't figured it out yet
!Morning session (from 7:35 to 8:25; 11:40 to 13:30)
* Tickets
* Resolving IE problem with the default width on an input or a button being wider than the text inside, figuring out what that is
** yes, IE default width for buttons has much wider left/right padding
** see [[this page|http://www.brownbatterystudios.com/sixthings/2006/02/14/standards-friendly-ie-button-width-fix/]] for fix - mainly {{{overflow:visible}}}
* Putting validation bug on hold - the Operational State on the suggest page is having its validator bound to the removed text input field - Josh suggests binding the new validator when submit button is pressed
<<readLog>>
Update from Chris to end of October - 9hrs 15mins, invoice ref yelllowcar-2, paid:
*20091002 12:30-14:00
**mail server setup, package updates, install mod-wsgi other administrative tasks
*20091002 22:15-23:15
**indexdir fix up, mod wsgi config
*20091007 11:30-12:00
**favicon fiddling
**upgrade tiddlyweb
**compile and install jinja2 speedups
*20091007 19:00-19:30
**sqlstore performance tweaks
*20091009 18:00-18:30
**optimize index
*20091010 12:45-13:30
**performance explorations and adjustments
*20091020 12:30-1:00
**utf-8 explorations: turns out it was a quote mark
**exploring database options
*20091021 15:00-18:00
**database import
**mappingsql test installation and configuration
*20091022 21:00-21:30
**freetext mysql experiment
*20091023 15:00-18:00
**setup and install new stuff with new data
**fix bugs
*20091023 22:00-23:00
**fix bugs
*20091030 14:30-15:00
**install the new data, start automation script
!Afternoon session (from 13:30 to 14:30; 14:45 to 15:20)
* Email discussion with Chris about how to develop access controls on the data
* Updated support email address in footer to support@wiki-data.com
* Looking into setting up logins and accounts
** From Chris: "This could be made to work with the existing simple form/cookie challenger mechanisms. It would require some tweaks so the cookie expiration works as desire, and there are password strength checks."
** This gets you to login page: {{{http://localhost:3000/challenge/cookie_form?tiddlyweb_redirect=%2F}}}
** This adds a user: {{{twanager adduser <user> <pass> [[ROLE]]}}}
* Upgraded python to 2.6.4 from 2.6.2 to fix crash problem
* Switching to using mysql setup
** Having many problems trying to compile things on snow leopard - seems that they all want to use 10.4 arch, which I don't have
*** will try and install 10.4 support from Snow Leopard DVD
!Afternoon session (from 16:15 to 20:30)
#Fixing IE bugs
** hide/show width problem - [[#16|http://wiki-data.lighthouseapp.com/projects/39178/tickets/16]]
** wide table flashing before DataTables kicks in - [[#16|http://wiki-data.lighthouseapp.com/projects/39178/tickets/16]]
** Your information block positioned below left-column - [[#18|http://wiki-data.lighthouseapp.com/projects/39178/tickets/18]]
** IE not liking the combination of dragtable and DataTables - [[#17|http://wiki-data.lighthouseapp.com/projects/39178-wiki-data/tickets/17]]
*** Investigations into the cause led to no conclusions
*** Rebuilding the feature as a DataTables extract
!Evening session (from 20:50 - 22:30; 23:45 - 00:45)
** Contiuing on dragtable replacement
!Afternoon session (from 14:15 to 14:20; 14:40 to 16:40)
* Prioritising
** not working in IE7 because of line breaking
*** problem is I can't show the tags at the point where they're added to the div in a non-breaking line for some reason
*** have found there's a problem when using {{{$('#river').tagriver(5);}}} vs {{{$('#river').tagriver();}}} - actually, that's not true
*** the problem is coming from the stylesheet - if I restore the experimental stylesheet, I can see the tags
**** got that problem - the a tags were having {{{text-indent:-9999px}}} set on them, which was pushing them out of the div, even though they are inline - can't find a reference on the web - ''should report on quirksmode or something''
*** now the problem is that the initial seed of tags are not line-breaking appropriately - they do so on the experimental index4.html but not when using the live style.css
*** observation - when creating lines in {{{addMoreLines}}}, in IE7, too many tags can get added to a line, so the effect is they will be pushed down when you flush lines queue to river
** IE6
*** Josh has put a redirect to a static version of the site in place - ''needs testing''
*** on dynamic version, problems:
**** clicking a video cycles the film reel without playing any video
**** clicking the accordion seems to trigger a JS error during the event handler
** randomise films
** IE - videos overflowing beyond bottom of some container before JS gets them in the right place
** <p> tag in wrong place on context column - fixed by Josh
!Morning session (from 11:45 - 12:05)
*call with Greg about UBS api desire
* check up the search criteria fuzziness - have experimented and emailed Chris some questions
!Evening session (from 20:15 to 20:45)
* Tidying up the Tender support tickets
* Attending to lighthouse tickets
* Updating record count to 266262
<<readLog>>
!Morning session (from 10:00 to 13:30)
#Going through existing designs
#Putting together document describing how designs' strengths and shortcomings with regard to main client themes
!Afternoon session (from 13:45 to 17:00)
#Writing more copy for the designs document
#Creating league table of the designs
#Meeting with Neil
** Agreed design direction and to deliver full design on Thursday
!Afternoon session (from 16:10 to 19:55)
* Discussion with Chris about his stuff
* MAD/wiki-data items (references from [[Google Spreadsheet|http://spreadsheets.google.com/ccc?key=0AgQJ7FGUGIp_dDdwX1dsUlFCWHBJREl5UjNaY1Z1NVE&hl=en]])
* Made AVID non-challengeable - put it in the header bar (ref #50)
* Removing field list from RMI (ref #71)
* Making company details text a consistent (smaller) size with other fields on the page (ref #40)
* Getting operational state to display properly (#36)
* Filters overlapping results table (#31)
* Glossary of field headings (#30)
** I have the mechanism for this working, just need the text of the tooltips
*** coming from http://www.avox.info/pdfs/wikidata_field_specs.pdf
** Have broken display of table when JS is on!
*** fixed! hadn't accounted in the JS for putting AVID back into the fields array
* Made the advancedSearch fields load from the recordFields object, not be hard-coded (ref #67)
* Making the advancedSearch filters not filter the table when you add a new one
** also trying to remove a filter if the field is changed - at the moment the previous field's filter stays active on the table
!Evening session (from 20:05 to 21:35; 21:40 to 22:00)
* Working on getting the filter update working
* Getting all mentions of "Avox" and "Avox Limited" to be links to avox.info
** done for all current content, have to do this for the new "about" content from Adam
* Getting MAD repo started
** need to update wiki-data to use cdent's method first
** this probably means merging my changes from today...
Hours: 6hrs 55mins
!Morning session (from 10:20 to 13:30)
#Add rest of modules to demo language file
#Found an image to use for the terminals
#Figuring out how to calculate a default layout
!!Where'd we get to yesterday?
The writings about this are getting a bit disparate and difficult to follow, which would be good to remedy at some point. However, the gist of what's happened to complicate things is that I realised quite late on that there are two internal data formats in WireIt - one, known as a language file, to describe the set up of the WiringEditor and one to describe a working (known in Streams as a wiring).
I have written a convertor from wirings to workings, which lets me load up wiring files into the WiringEditor, although the editor breaks if you don't have descriptions of all the modules in the language file. Adding the rest of the modules I need could be a manual job right now, although I expect that the production of the meat of this language file will be the job of the [[management API|Streams management API]].
!!What do we do with terminals?
Some modules have no type, they have a terminal attribute. I would imagine treating these a bit differently, perhaps by using another type of Container e.g. a WireIt.ImageContainer. We'd have to maintain the terminal information in a separate field and set the type as 'terminal'.
In addition, streams terminals do not have named WireIt terminals, so we need to create 'input' or 'output' terminals in both the language file and when creating the working's wires that connect to them.
!!How do you position terminals?
You have to set offsetPosition as part of creating a module's terminals. The thing to know is that a terminal is represented by an image 30px by 30px. It is offset from the top-left of the container, with [0,0] representing the image's top-left corner tucked into the top-left of the container. Therefore, if you want to, for example, put the terminal dead-centre of the top of a container 150px wide, you'd want to choose as offsetPosition like this:
{{{
offsetPosition: {left: 60, top: -15}
}}}
!!How do you position modules when they're loaded?
Workings have a position property, but wirings don't. This means we either need to calculate a layout once the wiring is loaded, or add a position property to the data format stored in CouchDB.
!!How do I lay out the modules if they don't have positions set?
I could lay them out in a ring...
!Afternoon session (from 15:00 to 18:45)
#Writing function to layout blocks
** Did the maths this morning...
#Trying to get a better container to use for terminals
** Figuring out how to use UneditableField after seeing this example:http://javascript.neyric.com/inputex/examples/uneditable_field.html
*** Needed to include the UneditableField js file!
*** Gave up after 45 minutes as the FormContainer-beta looked like it didn't support this and it wasn't easy to add it in; went back to using a FormContainer with a legend and no input fields
#Adding in saving in a similar way to the way I added loading - started with making working2wiring
** Found a problem that the type of the node is not saved in the module, even if you add it to the config, because each container's options are set explicity and they don't set any custom properties you've added
*** Fixed by adding a WireIt.customFields object to store properties against module names
#Now looking at how to actually save to CouchDB
** Fiddling about with the SMD stuff to see if I can use it to work with CouchDB
** I've managed to make a save to CouchDB, but I have two problems:
### I can't use PUT with the yui-rpc implementation I'm using
**** MikeB said that POST is fine, because they don't need to give names to the streams; however, ''I need to check that you can update a specific document in CouchDB with POST''
### I haven't figured out how to get it to send the properties I want to send by themselves, rather than wrapped up in a params object
**** You can send raw data by using 'JSON' as the envelope property of the method when defining it in the SMD
!!Can I get a list of feeds from CouchDB to use for the loading step?
Yes, see here: http://localhost:5984/feedshub_status/_design/feeds/_view/join
9:30 - 10:05; 18:50 - 19:00 - merging Chris' changes; tickets
<<readLog>>
!Afternoon session (from 13:35 to 15:15)
* Josh's list
** clicking the logo reloads the page - FIXED
** weirdly, the online version seems a lot smoother than my local one, in scrolling the filmreel along. Also on the offline one the play video button appears on the film thumbs as they pass through the focus column.
*** I think the offline one must have out-of-date JS folder
** it would be good if the video cinema closed again once the vid finishes - DONE
** the gradual fade on the tag river hover-effect, seems to have broken - DONE
* Cursor turns to pointer when you hover over films now
<<readLog>>
!Evening session (from 18:55 to 20:45)
#Writing some tests and library code
** wondering about how to make the alert windows the right height
*** probably set a modular height per room, with the contents truncated if necessary
** ''need to figure out how to make alert background semi-transparent''
** ''find out what this does'': {{{ alert.navigateInSystemBrowser = true; }}}
#Thinking about testing Adobe AIR JS with qunit
** Problem is that all the AIR runtime stuff is not available, so you'd want to run the runner.html inside the AIR environment.
*** I've put together a ClientTests application - aiming to get qunit running in the AIR environment - ''can't build it until I have an internet connection''
#have written the bits to create the content for the alerts, ''need to get the sample data in the correct format''
** I've created 5 chats worth of sample data (''not in the correct format yet'')
!Evening session (from 18:25 to 19:30)
#Catching up with Chris Dent's emails
** Query about displaying the total number of results for a search - Chris says that's going to be hard and suggests just displaying that there are more and that the search should be narrowed down
*** Have emailed Ken to ask
**** Do the easy way now, hard way later
** Dev server / instance
*** If we can make do with one server, that's great, otherwise I'll ask Greg about getting a dev VM
*** Have asked Chris about this
** Test data
*** I'd like a sub-set of the full dataset for testing purposes
**** have asked Ken about getting hold of 1000 out-of-date records
!Night session (from 22:00 to 22:50)
#Fixed incorrect submit button label on suggest a new record page
#Fixed mouse cursor not being a pointer on the wiki-data logo
** seem to come from the {{{h1}}} tag being inside the {{{a}}} tag
#Taken 'registered country' off the request more data page
#Fixed missing operational postcode on record pages
#Suggesting new copy for software/development partners section to Ken
#Adding contact link to consulting partner section
!Morning session (from 11:50 to 14:15)
#Figuring out how to test and build apps that spread over multiple directories
** Wrote build script to help
#Getting Paul's AJAX test into my test Adobe AIR app
** First problemo - how to make cross-domain requests in AIR
*** Quoting from the "AIR for JS Developers Pocket Guide", "Code running in the application sandbox can load data from any remote domain via XMLHttpRequest."
** Testing this out
*** Working with curl to figure out if I can use the same transactions from AIR as are made with JS API
*** Getting a login fault, which is apparently caused by invalid security token, prompted by trying to login from an unknown IP
**** Got a new security token for my account - works now
** Made a login call using jQuery - this works fine
** The SF JS lib isn't working yet though... returns status 0 and null responseText
*** Seeing if this is due to not using async request
**** that seems to fix the problem
*** It would be nice to use sync requests, as that is how the AJAX Toolkit is built...
**** Seeing if I can fix that
**** Turns out it's to do with when you execute the connection - you can't do it until the body's {{{onload}}} handler has finished executing - see here: http://labs.adobe.com/wiki/index.php/AIR:HTML_Security_FAQ
**** I put all the connection code in a function which is called after a timeout and it works
** Note about needing security token to login via the API (from the AJAX Toolkit docs):
*** "To avoid this, the administrator can make sure the client's IP address is added to the organization's list of trusted IP addresses."
!Afternoon session (from 15:00 to 16:40; from 18:00 to 18:30)
#Call with Paul and Stuart
** Diagnosing JS memory leak problems
** Adding user stories to Google Doc
** Prioritising stories
** Checking that clicking a link in a popup opens the browser
*** It does
** Agreeing what's in the first 3 releases
** Organising review meeting next Thursday
** Watching demo video
#Tweaking demo to work straight out of Firefox so I can use Firebug to see what we're working with
** I had to add in a line asking for permission to make cross-browser requests (which I'd want to take out for the AIR app)
#Seeing what data format is returned as chats
** appears to be no obvious field for telling whether a chat is new
*** I've emailed Paul to ask how he does that on the web interface
!Morning session (from 11:40 to 14:00)
#Design - this is technically work outside the user stories, but is central to the effectiveness of the site
** Thinking about the search interface - re-examining the advanced search/filter interface - they seem to be doing the same job, which is setting up a more specific search
** Would be cool if adding more fields, once you've searched, went away and did the new search automatically, like the instant update we have now
** Remembering that initial search box only matches on company name(s), this suggests that advanced search could be made a bit more every-day, so called something like 'search by field'; filter could be called the same thing, but offered as an option once you've searched
** Turning into HTML
** Update from Chris (12:00 to 13:00)
***read through the various docs and such
***fix mimetypes problem by including new copy if CWD from http://svn.python.org/view?view=rev&revision=72045
***get server running on port 8080, requires data in tiddlywebconfig.py but different info in twanager server command: {{{twanager server 192.168.103.10 8080}}}
****This is because external IP is different from internal. See http://tiddlyweb.peermore.com/wiki/recipes/docs/tiddlers/My%20server%20has%20an%20internal%20IP%2C%20how%20can%20I%20reach%20my%20TiddlyWeb%20from%20outside%3F for related info.
!Afternoon session (from 15:45 to 18:20)
#Continuing to transfer new design to HTML and CSS
#Note about revealing only a subset of fields to the public:
**cdent: is there some expectation that the database will store public and not public fields and return different things under different circumstances?
**jayfresh: yes
**jayfresh: if someone is logged in (next phase) they'll be able to see more fields, if those records are in their data set
**jayfresh: as in, if they're paying for them
**cdent: that's not an existing feature in tiddlyweb, so will require some tweaking of the store
**cdent: i don't see it as being particular complicated, but will require some code
**cdent: the generaly authorization model in tiddlyweb is bag based
**cdent: you either can or cannot get at stuff in a bag
# Converting flat HTML/CSS design into templates
** Leftover from this is moving some of the static elements into dynamically generated bits:
*** advanced search fields (at the moment, one text field is always there)
** I'd like some images to show how columns are being sorted
# Review of Chris' first pass at sqlstore search
** http://github.com/cdent/sqlsearch/tree/master
# Checking with Greg, Ken and Paul that non-public fields do not get sent in the extract
# Another update from Chris:
**START 20090901 15:45
**Import the data extract. Or rather tried to, the provided data is not right. Switching to looking into subclassing the search code.
**Created sqlsearch, now hosted on github: http://github.com/cdent/sqlsearch/tree/master
***Is the start at fulfilling the search API requirements.
**Some fine tuning is needed to deal with searching on fields and handling the resulting join. This now seems to be correct.
**STOP 20090901 20:15
!Afternoon session (from 12:00 to 13:10; 13:40 to 16:20; 16:35 to 17:50)
* Put real thumbnails into the filmreel - experimenting to see whether I can use one size of image or whether I need to swap images
** the effects of image size seem to be pixellation and animation performance:
*** Firefox:
*** small (60px) - fast, pixellated at large size
*** medium (140px) - smooth, only slightly pixellated at large size
*** large (220px) - slow, not pixellated
**** I assume that the slowness is coming from compressing the large image to show at small sizes
*** Safari: s, m, l all fast
*** IE7: s, m fast; l slow
* Going to try switching an image to medium before zooming it from small to medium
** done, looks good
* Calculating the correct film number to make big initially
** done
* Getting film reel animating on a loop
** thinking about removing images from the end of the container and pushing them on to the front - will it cause jerkiness?
*** this is causing me all sorts of problems, taking a moment to look at them:
**** I found I would have had to update all the relative positions of the films after moving one from the end to the beginning, so that the positions were correct again; to not have to do this, I exchanged relative positioning for using margin-right - this has meant that the rightmost float now wraps down under its neighbour, as there is not enough room in the wrapper. I am thinking about ways to stop this happening, but it looks like setting a new width on the wrapper is what I'll have to do - unfortunately, this creates scroll bars on the body, so I'd have to wrap the film wrapper in a container that used overflow:hidden, or use that on the body.
***** I've sorted this by wrapping filmwrapper with another container with overflow-x:hidden
**** the next problem I'm having is that the images are not resizing properly, which I think means the resizing is happening out-of-step with the movements of the films - I can test this by using a timeout
***** this problem was caused by changing the value of {{{isMaximised}}}, which tracked the index of the image at max size - this now changes each time one of the film div's is moved - the solution is to not change the value of {{{isMaximised}}}
**** I'm also finding that the callback of the last animation is being called twice
***** ok, this is because the callback is called once for each element being animated
** at this point, as a simplification, I've removed the handling for any left offset of #filmwrapper. That's because I've introduced #reel as the overall wrapper, so that can have any layout settings applied to it
** still having problems with the images wrapping down as they grow and shrink
*** seeing whether it has to do with the width of the wrapper changing
** ok, this is looking good now - one problem remains, which is that the film div's being moved from and to the front and the back of the line just appear or disappear from nowhere
*** I need to extend the scope of filmwrapper to sit one whole image on either end wider than #reel, then the images will pop into existence unseen
*** the technique should be to add two more small images in at the start, but position the filmwrapper one image and its padding's worth from the left
*** looking good in FF
**** real smooth in Safari, not bad even in IE7
**** problem in IE7 - overflow-x:hidden on #reel doesn't seem to work
***** found [[the fix|http://snook.ca/archives/html_and_css/position_relative_overflow_ie/]] on Snook.ca - add position:relative to the container, since anything inside with position:relative will spill out
* Getting film reel using the correct resolution images when it starts up
** the problem is that I don't call the moveLeft or moveRight functions, since I'm not actually moving
** I think I could do with an initialisation function that sets the sizes and images, rather than animating (it should also set isMaximised)
*** ok, that's done
* Now, looking at making the films line up with the contextcol, even if #reel has a left offset
** done
** Safari and IE have a flash of the images not being there before they appear - ''should pre-load images'' (I presume that will fix it...)
* Making the images aligned vertically along a horizontal centre-line
** Looking at the way icondock.js does it
*** the principle seems to be to use padding on the top and bottom of the small images to keep them in line - then you reduce the padding as they get bigger
*** have the initial state appearing correctly using this technique
*** need to get the padding changing as size changes
*** ok, that's done
**** noticed a bug: ''clicking too many times on left/right upsets things'' - should make sure that moves can't be triggered until existing ones are finished
* A list of what's left to be done on the film reel:
** fix image pre-loading (see iconDock.js)
** tags appearing when you hover over a mini-thumbnail
** when you click a tag on a page, the reel has to move to maximise the closest video tagged with that tag
** when you click a mini-thumbnail, the reel scrolls to maximise that video (and maybe it starts to play - we'll see how that works)
** when you click on a maximised thumbnail, it has to expand out into a player (landscape)
** an indicator e.g. "click to play + play icon" should appear on top of maximised images to make it obvious people can click them
** the context column has to be populated by the information for the maximised thumbnail
** Josh and I need to work out the behaviour for the appearing/disappearing of the context column information (partly in relation to the other content in that column, partly in general - the aim is to maximise the association between the content in that column and the content in the player)
From 11:55 to 14:00
* going through stories
* coming up with a few design ideas
!Afternoon session (from 14:10 to 16:30; from 17:00 to 20:35)
#Improving dragColumns library:
** Copying styles of original cells to copy
#Checking out Amit's proposed final design for results page
** having a conversation with him about it, suggesting some changes
#dragColumns improvements
** Adding drag animation
** Cancelling drag if you go beyond the table
** That's done now, ''need to add FixedHeader back in''
** Needed to rework things to be compatible with FixedHeader
*** Have done quite a bit, now breaking for dinner with a bug found
**** ''when sorting some columns only after a column re-order, an error is thrown during sort: a is null''
*** ''Now need to try with FixedHeader again''
!Evening session (from 21:50 to 22:30)
#dragColumns improvements
** works with FixedHeader now
** looking into that sorting bug
*** looks like it's nothing to do with the dragColumns
*** fixed - problem was to do with setting column data as null instead of ""
!Morning session (from 10:40 to 11:30; 11:40 to 13:10)
* Turning MAD/wiki-data priorities doc into Google Spreadsheet
** http://spreadsheets.google.com/ccc?key=0AgQJ7FGUGIp_dDdwX1dsUlFCWHBJREl5UjNaY1Z1NVE&hl=en
* Speaking to Adam (11:00 - 11:25) about getting time estimates for the MUST tasks done and sending them this afternoon
* Getting the spreadsheet to produce a list of MUST tasks
** discovered this was hard - posted on Google Help [[here|http://www.google.com/support/forum/p/Google+Docs/thread?tid=48f43b6f60c41a4d&hl=en]]
* Reviewing MUST tasks
** marked some up to ask Adam about
* Checking whether server logs give enough information to debug log-in (twice) problem
!Afternoon session (from 14:10 to 15:20; 15:30 to 16:30; 16:45 to 17:45)
* Speaking to Adam about a few items on the list
** checking server logs as he logs on
*** this shows that the cookie for {{{tiddlyweb_user}}} is not getting set the first time he logs in
* Updating the test server apache config to recognise test.wiki-data.com
** hasn't worked despite changing the apache config
** Chris told me how to restart apache properly:
{{{/etc/init.d/apache2 restart}}}
* Writing down the bits that I need estimation help from Chris with and sending to him
* Working out sizing estimates for Adam's list
** 26.5 units - I seem to do about 4 a day, so that's about 7 days. That means I'd need 3 Avox days a week minimum between now and week beginning 8th Feb (2*3 + 1 (Friday))
** 2 items needing more info from Avox
** 5 items waiting for more info from Chris
* Making a summary sheet in the Google Spreadsheet of the MUST tasks
** Ahab replied to my earlier question
* Responding to Chris' reply and incorporating estimates
** total: 29.5 units from me; 16hrs from Chris
*** 29 is still around 7 days
10:30 - 12:00 meeting with Dom Pascal
12:20 - 12:50 writing up notes and speaking to [[Jean-Phillipe Altier]] about getting a quote
Things to do:
* check out their app on iPod touch
* check out video intro on Nike City app
* find out off Dan Morris who made the iPhone app for fashion week last year
* provide several quotes: one for build contract, one for applying revenue share, one for offsetting payment until success criteria have been reached
They are looking for an iPad app that presents roughly 200 pages of images, where text is read by scrolling down the page. They would provide all the assets and do any design work.
Questions / areas to understand:
* feasibility for use of PhoneGap to keep cost down
** DoTank (DT): Sure, ithis would allow hyperlinking and it makes sense to easily externalise the content production.
* how would packaging up and selling updates (e.g. new issues) inside apps work for them?
** DT: New issues are zip downloaded into the app via "In App Purchase"
* can usage monitoring be included in App Store apps?
** DT: yes
* how long are apps taking to go through App Store approval process at the moment?
**DT: varies, roughly 14 days
* can we do in-app video?
**DT: yes
Important:
* articles will appear as images
* launch with Fashion Week in September, so submit start of August?
* they would like to release new paid-for issues and unscheduled updates without releasing a new app
**DT: Same as above, possible with "In App Purchase"
* thumbnail gallery for contents page, each one linking to the story
* video intro - around 5 seconds to do instruction and provide feel of the brand and app (like the Nike City app)
* 1 page of video (if feasible - download size and in-app video ability)
**DT: Can also provide video streaming
* Dom is capable of compiling new packages, which could help simplify things
Less important:
* to include video as part of articles
* to deploy to iPhone in addition to iPad
* to zoom in up to 3x to study articles close up
* to content-manage the content, rather than package up updates
* newsletter feature not wanted any more
----
Original email:
---------- Forwarded message ----------
From: Dom Pascal <dom@drama-magazine.com>
Date: Tue, May 18, 2010 at 2:48 PM
Subject: Re: [Fwd: iPad app]
To: jnthnlstr@googlemail.com
Cc: Jessica Tibbles <jessica@electricbluegallery.com>
Hi Johnathan,
My Name is Dom Pascal, I am the Art Director at DRAMA Magazine, we are basically looking to create a dynamic app for the iPad/iPhone, in which the content is linked to our server, where we could update the content using a CMS or just uploading images to the server. Ideally we would like to have each issue as a 1 issue subscription, so when we have uploaded the next issue to the server, the magazine subscribers would be prompted to buy the next issue.
I am not sure if you have had a look at our current iphone app, if you have not, its only 50p, you can download it here:
http://itunes.apple.com/WebObjects/MZStore.woa/wa/viewSoftware?id=334668760&mt=8
Please find below our requirements for the new app:
+ 200 dynamic pages of image & text content with a 3X (zoom in 3 times the original size)zoom feature linked to a CMS/DRAMA server
+ Hyperlinks to website urls within app pages
+ Instruction page on how to use the app at the start of the app
+ Video intro at the start of the app like on the DRAMA website. A good example of this is the NIKE True City app
http://itunes.apple.com/gb/app/nike-true-city/id340532377?mt=8
+ Article scroll similar to our last app with a 2-3X zoom feature
+ Interactive table of content with thumbnails accessible from any page in the app
+ Image & text content in the app linked to a CMS on the DRAMA website
+ Subscription facility for when we release a new app
+ Update of our current free app on iTunes to prompt the existing users to purchase the new app
+ 1 video page with link to DRAMA website
+ Development of the CMS on the DRAMA server for updating image & text content on the app
+ Development of newsletter system to send monthly newsletters & capture visitors email addresses & details
!Atfternoon session (from 12:20 - 14:20; 15:00 - 16:30)
#Today, first thing is to check whether FixedHeader works in IE without dragColumns
** Clicking through didn't seem to work
** Going to update FixedHeader to the new version and see if that fixes things
*** right, that's working fine now
** Checking that dragColumns works in isolation with IE/FF/Safari
*** This works now
** Tested combined dragColumns and FixedHeaders and the new event handling for the table wrapper doesn't pick up the drag - ''look into this''
** Fixing problem where columns are not sorting by the correct column after a re-order
*** Fixed
** Finding out how the events are bubbling
!Afternoon session (from 13:45 to 15:00; 16:50 to 17:50; 19:40 to 20:00; 20:15 to 20:45)
* Get the film reel to accept a choice of film to show maximised first
** this first film also only shows if you haven't visited before
** the film autoplays as well
* Adding credits and copyright information to header
** {{{<meta name="generator" content="Joshua Bradley & Jonathan Lister" />}}} is an interesting use of the generator tag to add credit
** added BPSF copyright meta tag
* fix: when you go back to issues & insights after viewing another page, clicking on tags doesn't move the filmreel
** can't find the problem
* filmreel now restarts after a video is played
* consultant gallery links need to load the video - in progress
!Afternoon session (from 12:40 to 13:20; 14:50 - 17:00)
* Making filmreel autoscroll by only one position
* Fixing problem with images wrapping round when filmreel animates
** I don't entirely understand why this is happening, but it would appear that during the animation the total width of all the images gets more than the wrapper can handle. It shouldn't do this, but it does, so maybe the growth/shrink animations are out of sync
* Pre-loading images so they don't flash when changing src from small to med to max
* Content meeting at Curzon Street
* Agreed project split with Harry and Josh - 35% to me for two of three cycles, 50% of third cycle
!Afternoon session (from 13:30 to 15:10; 16:30 to 16:50; 17:00 to 19:40)
* Planning with Josh
* Putting JS into WP site
** got the consultants gallery working - found that calling {{{$('#river').tagriver();}}} causes a problem if there is no river - FIXED
** got the tagriver working with the CMS-made tags
** getting AJAX-loading to work for nav - done
** Josh done thumbnail auto-generator for WP
*** Max Image Size Control plugin (works with Smart Image II)
** getting the sliders working now that the content loads using AJAX
*** done
* Putting the film-reel into the WP site
** adjusted thumbnail name-syntax to match the WP plugin mentioned above (e.g. image-220x220.jpg)
** done
** scrolling is causing flickering on Josh's MacBook Pro
** performance in Firefox better when Firebug is closed
* ''BUG: noticed tagriver bottom overlaps bottom of tags in Safari''
!Afternoon session (from 13:00 to 13:30; 15:10 to 16:20)
* Updating record count and spreadsheet
* call about MAD priorities (15:10 from 16:20)
** RMI feature on MAD
*** go with open text-box to allow people to give feedback on product
**** we'll need some text to say what the form is for (Brian could do that to ask for product-management input)
*** position/size/style of MAD link on wiki-data
**** diagonal band in top-corner of site saying "My Avox Data"
**** on hover, we can show a pop-up explaining what MAD is
** logo
*** until Avox produce something, just use same font as for wiki-data
** the question of how Avox manages the "taster list" of 3k records
*** Ken - can they be set up as just another client in the database?
*** what we want is another field in the data dump
** log-in problem:
*** Ken verified that logging-in once doesn't log-in on Firefox
** logging-in:
*** JRL hadn't realised the detail:
**** on wiki-data, you can still log-in; when logged-in, you'll not have to fill in personal details and CAPTCHA
**** on MAD, when logged-in, you'll also get access to full data set
** whether MAD runs on another database
*** Greg says there is not much security risk difference between having one or two databases
*** there's also the question of future-proofing for when wiki-data is a public-domain resource people collaborate on
*** to keep updating procedures simple, we should stay on one database for now; when we open up wiki-data as a collaboration platform, we can split to two
*** the number of records on wiki-data and MAD will be the same - Ken: yup... for now
** account registration process
*** Ken: how do we want to permission people?
**** single, concurrent login i.e. you can only log-in with one set of credentials at the same time
**** this is to avoid multiple people using the same license
*** Adam: restricting access to specific IP addresses
**** Ken: not too worried about that right now. For wiki-data, that's not so important, but we would like the wiki-data logins to be per individual (as above), from the point of view of collecting product-use analysis
*** Different logins for wiki-data and MAD
** Question of increasing information density:
*** use Bloomberg as inspiration: http://www.bloomberg.com/markets/mutualfunds/index.html
** Question of what you get when you're searching MAD and not logged-in
*** how do you hook people in to subscribe to MAD?
**** 3k sample records? how do you get overlap between what people search for?
**** mini-sites e.g. Fortune 500 full data. This gives an idea for what to put on the MAD homepage.
**** credits model e.g. 10 full records per month, subscribe to more (cf. Economist.com)
**** decision for now: only search the 3k records if you're not logged-on
** Searching wiki-data
*** don't limit the number of results people can page through
**** decision: remove restriction on wiki-data and MAD (since not logged-in on MAD is only 3k records)
** Monitoring access
*** In the future, we'd like to keep records of who is downloading what and at what volumes (IP address tracking?)
!Afternoon session (from 12:45 to 13:10; 13:15 to 18:00)
* Updating record count
* Tickets
* Call with Adam
** don't forget to move the optional "source" on challenge page to bottom of form
** they're going to be having some server downtime in a week or so, so Adam is going to ask Greg to ask them to provide a redirect to a holding page, which we'll make
* Found a problem with the Validate jQuery plugin - if you make an input dependent on a checkbox being unchecked, it doesn't update the field to remove the error when you check the checkbox
** posted on the [[support group|http://forum.jquery.com/topic/validate-plugin-validation-of-a-field-based-on-a-checkbox-field-doesn-t-update-when-you-toggle-the-checkbox]]
<<readLog>>
!Morning session (from 10:00 to 12:15)
#dragColumns improvements
** on IE, the cells are the wrong heights; not wrong on FF or Safari
*** IE box-model doesn't incorporate padding into height
*** cell height needs to have a height of row-height minus the cell-padding
*** fixed
** default action of selecting text on mousemove needs to be stopped
*** adding preventDefault/stopPropagation doesn't work
*** I think the problem is that the header mousemove event is fired before the wrapper mousemove - need to change the event handler to be on the header and preventDefault there
**** this might cause problems when integrating with the FixedHeader plugin, as that has to pass events through to original table... will have to keep an eye out
!Afternoon session (from 13:15 to 20:55)
#Fixing text-selection problem when dragging with dragColumns
** Confirmed event handlers are called on the bubbling phase in FF
*** seeing which handler needs to preventDefault to stop the text-highlighting
*** turns out that your have to preventDefault in the mousedown event at any element in the stack to stop the text-selection, ''except in IE, where you have to cancel during 'selectstart' event''
# have that all working, except now I have a problem with columns swapping on sort
** fixed - not updating dataTables property
# problem: after moving columns in IE, header stops responding to clicks
** have added {{{live}}} handler to thead, which registers both mousedown and mouseup events, but header not registering click event after move - ''need to fix''
# timed creation of floating column to see how it can be made faster (it's taking circa 2 seconds to appear)
** suspect it's to do with node cloning or height finding
** here's the report:
----
This is all measured with IE6.
Bottleneck is cloning all the td's in tbody
...
IE using clone(true):2674,2904,2894
IE using cloneNode (which loses event handlers):2234,2303,2394
-> save about 500ms, but need to add event handlers on
...
timing each td's copying suggests 20-30ms for copied nodes, vs. 0ms for non-copied
...
analysing copying operations a bit more closely
d5-d4c: 20, 50, 30, 30, 20, 10, 31, 30, 30, 30, 10, 30, 20, 31, 30, 30, 31 = 392
$newCell.appendTo($row);
d4c-d4b: 30, 30, 30, 30, 20, 30, 30, 20, 10, 20, 30, 30, 30, 20, 30, 30, 30, 20, 30, 20, 30 = 550
$newCell.width($(this).width());
d4b-d4: 10
$newCell = $(fullCopy(this,true));
...
changing functions around
d5-d4c = 331
$row.append($newCell);
d4d-d4c = 240
$newCell.width(width);
d4c-d4b = 530
width = $(this).width();
d4b-d4 = 10
$newCell = $(fullCopy(this,true));
...
seeing what I can do by getting the width some other way (cf. dragtable.js)
...
setting the width once by header width
d5-d4d = 330, 341 ($row.append($newCell))
d4d-d4c = 552, 803 ($newCell.width(width))
d4c-d4b = 0, 20 (fullCopy...)
d4b-d4 = 50, 30 (width = $(this).width (just once))
...
going to try setting width with css()
d5-d4d = 841, 901
d4d-d4c = 40, 40 ($newCell.css('width',width) (this is a lot better!))
d4c-d4b = 20, 0
d4b-d4 = 0, 0
...
trying a different way of appending the new cell to the row
d5-d4d = 0 ($row[0].appendChild($newCell[0]))
d4d-d4c = 20
d4c-d4b = 0
d4b-d4 = 40
...
now, d4-d3 is still at 2613 at that last test, so going back to testing other section
That all suggests that the only bottleneck outside the copySection function is this:
{{{
$dragCol = $("<div></div>").append($cloneTable).css({
'position': 'absolute',
'top': "0px",
'left': pos.left,
'opacity': 0.7,
'zIndex':"2" // TO-DO: would be good to figure this out automatically
});
}}}
The bits of copySection that I haven't profiled yet came in on the last test as 200ms of the 270ms it took.
...
Profiling those bits.
Focussing on {{{var $row = $(fullCopy(this,false)).appendTo($section);}}}, which took 120ms in the last test
Replaced with {{{var $row = $($section[0].appendChild(fullCopy(this,false)));}}} - went down to 0ms.
...
Now looking at {{{var rowHeight = $(this).height();}}} which took 140ms on last test.
Have replaced with {{{var rowHeight = this.offsetHeight;}}} - went down to 0ms.
...
Now looking at {{{colWidth = $(this).width();}}} in copySection, which is recording ridiculous measurements of 0,531,1060 - even when the total time for the copySection function is coming in at 521ms! (Looking at this in FF, the step takes just over 1/3 of the copySection execution time).
Replacing with {{{colWidth = this.offsetWidth;}}} reduced the time to 0ms.
...
There are 4 statements like {{{$it.css("paddingLeft")}}} that take up about 85ms each - going to try and replace them with direct style access
...
at the end, have brought the time to create the floating column down by about two-thirds (from ~3s-1s)
-----
#fixing the problem with click handler not registering after column re-order
** looking into this
*** fixed by putting event handler on wrapper not header - don't understand why the event wasn't being triggered on IE after changing the innerHTML...
** also discovered that ''after sorting, classnames are not migrated with the columns''
#posting updated code on DataTables forum
** at [[this page|http://datatables.net/forums/comments.php?DiscussionID=764]]
#seeing if this works with FixedHeader now
** it does
#found a bug in FixedHeader to do with the placement of the fixed header relative to the top of the screen if the DOM has increased in size
** the problem seems to come from not updating the fixed header after the DOM has changed - probably a loading order thing
** fixed
#reviewing changes to database and new mappingsqlstore
** email exchanges with CDent
!Evening session (from 22:00 to 23:30)
#Creating structure for country/state drop-downs logic
!Morning session (from 7:20 to 8:05)
#Inspecting caching situation
** Tiddlers are cached
** Static files are not cached (served by static plugin, rather than apache)
#Making font on company page consistent with rest of site
#Thinning content on company page to remove non-functional bits - comments, edit button
#"Fixed" company website display on company page to prepend "http:" to the href - ''assumes there are no records with that prefix already''
#Commented editing code in edit plugin, as we don't need it yet
#Some more requests from Ken:
** I tried filtering on country. Intuitively, I type in the country name but then I get no results because the system needs the 3 character ISO country code. This won’t work. When country is selected as the filter criteria, the user should be able to being typing the name and the value should autofill based on the first few characters entered. I think you have the meta data table linking the country name to the country code? One other problem with this is that some country names are not legally the same as what they are referred to as (Congo vs The Democratic Republic Of Congo). Is there any way to get around this? I would think this particular search/filter problem is pretty common. This one is very important from a user perspective. If the autofill is too complex, we should at least have a drop down list so that users can see the list of options Perhaps listing the country name with the 3 character ISO code in brackets).
*** Agreed - we discussed and we're going to go with auto-complete
** The same point above applies to “Operational State”.
** I don’t see Country of Registration (Registered Country) in the list of fields. This was a huge oversight on my part. It has to be in the production version.
** Would you mind also changing 2 of the sample searches to be non-UK? One US search and one Singapore search would be ideal. Search examples – I asked you last night to replace two of the UK examples. Here are the specific search example suggestions:
*** Company Name: Energy, Operational State: Texas
*** Company Name: Commodities, Operational Country: Singapore
!Call with Brett Hodge and Ken (from 8:05 to 9:00)
#Japanese mega-banks have experienced theft of data, so they are worried about giving away data
** They don't want Avox or anyone seeing you they are watching
** Discussed how you can get anonymity into data distribution - it basically comes down to giving away a lot so you can pick out the ones you're interested in
*** This can be fine- or roughly-grained e.g. all data, companies from a sector, a pick-list you don't use all of...
#Automating the upgrade of the database
** ''Get this problem worked out with Chris and Greg''
#Country/state filter auto-complete
** This is a priority before user-testing will begin
#Terms and conditions - precedent
** ''find examples of developer terms on other sites'' - for Ken
Hours: 7
!Morning session (from 10:00 - 13:30):
#Meeting with Mike Rowley, Mike Bridgen and Sophie (?) to determine scope of my involvement. Agreed five days over two weeks on day rate to create:
** prototypical interface of iPlayer user story - transforming LiveText to be appropriate for iPlayer
** sizing of the development of the interface
#Explored the architecture of a Stream. You have:
##inputs. These are made up of:
*** Gateways e.g. a RSS poller.
*** Servers e.g. a use of the poller to poll the LShift blog every 2 minutes.
##transformations. These use "components".
##outputs. These are the reverse of inputs, using servers and gateways.
** An noteworthy difference between inputs and outputs is the expectation about how they'd be used. Inputs are expected to be defined in advance to optimize the data feeds that you get e.g. the best Radio 4 feed; outputs are expected to be defined by each person creating a Stream, as they'd have their own particular uses for the information.
#Went through what Streams currently has exposed to HTTP with MikeB. To make the most basic prototype, which would be a 'Go' button, you'd need to:
## PUT/POST a JSON description of the pipeline to CouchDB e.g. /my_item
## POST a status JSON to e.g. /my_item_status
## have the orchestrator get the thing going
*** Mike hopes that the last two steps could be combined so that the action would be atomic from the client's point of view
*** Mike also hopes that this exercise will make it clear what the HTTP API needs to support
!Afternoon session (2:30pm - 6pm)
#Explored [[ReverseHTTP|http://reversehttp.net]] with Tony Garnock-Jones.
#Went through Sophie's diagram showing the features you'd need to support the BBC's user stories. Many are not core to the open source project; the scope at the moment is limited to the core stuff.
#Confirmed with Mike and Tony what I'd be doing over this timebox.
#Installed Streams on my mac with Mike and Tim.
!Morning session (from 11:10 to 12:00)
#Sorted the MySQL setup
** This helped - http://mdzyuba.blogspot.com/2007/11/mysql-macport-install-on-mac-os-x-tiger.html
** This tiddler has all the details about setting up the mysql database - [[27th October Avox Wiki-data]]
** To run mysql, {{{ sudo /opt/local/lib/mysql5/bin/mysqld_safe & }}}
#Created a new mini_wiki.txt file from the top 10k records from the full file (as per Greg's instructions)
** As I'm setup with macports, had to use copy file to this location for mysqlimport5 to find it: {{{ /opt/local/var/db/mysql5/avox/avox.txt }}}
# Now I'm in a position to answer the question - does logging in currently give you access to all the fields?
** Yes
!Afternoon session (from 12:25 to 13:00)
#Breaking down what I have to do to get the login experience done
** "As Ollie, I want to show a potential customer what extra data you'll be able to see if you're logged in, so that I can find out where they'd get value out of a logged-in experience"
*** Login / logout button - as part of a horizontal bar at top of page
*** Horizontal bar changes colour to show that you're logged in
*** The option to show the extra columns on the results table
*** A display of the extra information visible on a company's page
#Technical thoughts
** The available fields are hard-coded into:
*** tiddlywebconfig.py - list of open fields
*** the DataTable setup JavaScript in records.js - used to create table columns
*** recordFields.py - used in emailAvox.py to construct the body of the email - a duplication of labels.html
*** api.html - used in explanation of what data you get in the JSON format - DON'T KNOW if we want to list the available extra fields
*** company.html - used to display the company display format - would like to show MORE data on the page if you're logged in
*** labels.html - used in ajax_search, challenge and collection (standard search) pages to define what HTML gets created - in ajax_search, the labels.html template is used to do a similar job to the table bit in the records.js file, so this duplication is quite fragile as the defined data structures have to match up
*** suggest_new.html - used to create the form people fill in for suggested new companies - DOESN'T need to change as it's only public data they are submitting
** I don't know if we want to show the available extra fields - I suppose because the data structure is available in the open sourced data-set, and because we're trying to show how attractive this data is, we'd not want to hide the field names. This means I can put field name definitions in publicly-available files, such as JS files
**Summary of field definition duplications:
| file | type | duplication of | scope |
| tiddlywebconfig.py | py | public fields | environment |
| records.js | js | all displayed | client-side |
| recordFields.py | py | all displayed | route processing |
| api.html | txt | all displayed | template |
| company.html | py | all displayed | template |
| labels.html | py | all displayed | sub-template |
| suggest_new.html | py | all public | template |
** Other thoughts: one of the reasons why there is so much duplication at the templating layer is because there is not a standardized way for me to get at the field-name definitions. I think if my templates were interacting with resources over the web API, there would be a clearer answer i.e. my web site would be an app on the platform. In that situation, you'd probably have an API endpoint for getting the list of available field names.
** Aspiration short-term: there should be 1 Python and 1 JS file that contain the field definitions, with the difference between open and private made clear
** Aspiration long-term: there should be 1 Python file that defines the above (or 1 text file)
** Question: can I use 1 Python to define all available fields and use that in all the templated pages, where the public/private distinction is created by a fallback position defined in the configuration file i.e. part of the template simply doesn't get printed if you can't access the fields - the configuration file functions as a mask
*** does this sound like something that could continue to work if different clients have different access to different fields? I suppose so, given that the mask could just change rather than be removed on log-in
!2nd afternoon session (from 14:25 to 18:00)
#Having mulled over the question, here's a proposal
** put the field definitions in 1 Python file - recordFields.py - and change the templated pages to assume they have access to all the fields but degrade gracefully if they don't have access to some of them
** this looks like it means giving the field names to all templated files as a global, along with the mask of what is and isn't private (although the security will be enforced in what data is passed from the store)
*** ''changed my mind'' - I filter out the total list of fields to a sub-set based on login name during the templating step
**** this potentially has performance implications, but it keeps mention of inaccessible data out of the templates - this seems right now
# What this involves
** changed routes.py to pass in list of available fields
** expanded recordFields.py to list all the fields (see below) and provide a function to get the list of available fields
** to update, to remove dependency on labels.html:
| page | notes | done? |
| api | static | y |
| collection | dynamic | y |
| ajax_search | dynamic | y |
| records.js | creating a new endpoint for this, so it's not a static file - in won't be part of agg.js anymore | partial |
| challenge | dynamic | |
** Created {{{lib/fields.js}}} which is a dynamic endpoint, giving you a list of fields you have access to
** Having to change records.js to work with the fields you get given
** Tested the changes out when not logged-in - fine
*** Can't log in for some reason... throwing Store errors, think it's something to do with moving to MacPorts
#Record of all fields:
{{{
('avid', 'AVID'),
('avox_match_status', 'Avox Match Status'),
('avox_entity_class', 'Avox Entity Class'),
('avox_entity_type', 'Avox Entity Type'),
('record_source', 'Record Source'),
('legal_name', 'Legal Name'),
('previous_name_s_', 'Previous Name(s)'),
('trades_as_name_s_', 'Trades As Name(s)'),
('name_notes', 'Name Notes'),
('legal_form', 'Legal Form'),
('trading_status', 'Trading Status'),
('swift_bic', 'SWIFT BIC'),
('vat_number', 'VAT Number'),
('tax_payer_id', 'Tax Payer ID'),
('company_website', 'Company Website'),
('regulated_by', 'Registered By'),
('regulator_id', 'Regulator ID'),
('regulatory_status', 'Regulatory Status'),
('registration_authority', 'Registration Authority'),
('registration_number__operational_', 'Registration Number (Operational)'),
('registration_number__jurisdiction_', 'Registration Number (Jurisdiction)'),
('date_of_registration', 'Date Of Registration'),
('date_of_dissolution', 'Date Of Dissolution'),
('issuer_flag', 'Issuer Flag'),
('primary_listing_exchange', 'Primary Listing Exchange'),
('ticker_code', 'Ticker Code'),
('cabre', 'CABRE'),
('fiscal_year_end', 'Fiscal Year End'),
('mifid_source', 'MIFID Source'),
('balance_sheet_date', 'Balance Sheet Date'),
('balance_sheet_currency', 'Balance Sheet Currency'),
('balance_sheet_total', 'Balance Sheet Total'),
('annual_net_turnover', 'Balance Sheet Turnover'),
('own_funds', 'Own Funds'),
('operational_po_box', 'Operational PO Box'),
('operational_floor', 'Operational Floor'),
('operational_building', 'Operational Building'),
('operational_street_1', 'Operational Street 1'),
('operational_street_2', 'Operational Street 2'),
('operational_street_3', 'Operational Street 3'),
('operational_city', 'Operational City'),
('operational_state', 'Operational State'),
('operational_country', 'Operational Country'),
('operational_postcode', 'Operational Postcode'),
('operational_address_notes', 'Operational Address Notes'),
('registered_agent_name', 'Registered Agent Name'),
('registered_po_box', 'Registered PO Box'),
('registered_floor', 'Registered Floor'),
('registered_building', 'Registered Building'),
('registered_street_1', 'Registered Street 1'),
('registered_street_2', 'Registered Street 2'),
('registered_street_3', 'Registered Street 3'),
('registered_city', 'Registered City'),
('registered_state', 'Registered State'),
('registered_country', 'Registered Country'),
('registered_postcode', 'Registered Postcode'),
('registered_address_notes', 'Registered Address Notes'),
('naics_code', 'NAICS Code'),
('naics_description', 'NAICS Description'),
('us_sic_code', 'US SIC Code'),
('us_sic_description', 'US SIC Description'),
('nace_code', 'NACE Code'),
('nace_description', 'NACE Description'),
('entity_type', 'Entity Type'),
('immediate_parent_avid', 'Immediate Parent AVID'),
('immediate_parent_name', 'Immediate Parent Name'),
('immediate_parent_percentage_ownership', 'Immediate Parent Percentage Ownership'),
('immediate_parent_notes', 'Immediate Parent Notes'),
('ultimate_parent_avid', 'Ultimate Parent AVID'),
('ultimate_parent_name', 'Ultimate Parent Name'),
('ultimate_parent_notes', 'Ultimate Parent Notes'),
('general_notes', 'General Notes')
}}}
!Morning session (from 9:00 to 14:15)
#Plan
** Printing ClientNotes tiddlers
** Setting up comments
** (for another time) make TW update after you log in so your account name is used to edit
#Printing ClientNotes
** looking into TiddlyWiki refresh mechanism
** put {{{refresh='content' tiddler='Print ClientNotes'}}} in the ViewTemplate for the Print ClientNotes tiddler, then call {{{refreshDisplay("Print ClientNotes")}}} after setting 'print since' option in "Print History tiddler"
** Creating printClientNotesPlugin
!Afternoon session (from 16:15 to 17:35)
#Continuing adding tests and writing library code for dependentInputs
Hours: 6hrs 45mins
!Morning session (from 10am - 13:45)
#To 11:50 with Tim, getting Streams working on mac - couldn't resolve Python problems so Tim is going to create a demo that doesn't involve any Python
#Next, creating demo that POSTs directly to CouchDB the pipeline and the status documents
#Done, updated MikeB
!Afternoon session (from 14:45 - 17:45)
#Researching WireIt
#[[Comparing WireIt 'working' structure to a stream's 'wiring']]
#Have a go at making a WireIt editor
!Questions
#See if you can fit all the information you need for a stream into the WireIt data model; if you need to add fields, do they get passed along when you serlialize the working?
** Converting streams module to WireIt node:
###put key of module inside object as 'name';
###created 'container' property to store rest of data;
###made up a 'title' and an 'icon';
###assumed WireIt.FormContainer as 'xtype';
###added the 'type' property;
###made the configuration settings into 'fields' by:
###guessing the type for the field e.g. text, boolean;
###mapped the key to the 'name' property of the inputParams object;
###the value became the value property of inputParams;
###made up the label property of inputParams;
###made the terminals property of the container like this:
###looked in the stream's edges for all named channels of a node to get the 'name' property for a terminal;
###used whether the channels appeared in a "from" or "to" property of an edge to decide on the direction property (which appears to be [x,y] where 1 means bottom or right and -1 means top or left;
###chose the offsetPosition based on the module being 50x50px; added 'width' and 'height' properties of '50' each to the container.
At Tager.
!Morning session (from 11:00 to 14:00)
#Review of storyboards
#Development of design ideas
#Construction of paper prototypes
!Afternoon session (from 15:00 to 15:35)
#Review of Chris' [[suggestion|http://groups.google.com/group/tiddlyweb/browse_thread/thread/49d4fed67cf8c578]] about how to resolve adding user accounts
#Experimenting with OpenID logins
** looks like I need a way to store the information about which accounts get privileged access
#Now I can login (using OpenID), I can at least put together the login bar and page
** The login page looks like a case of copying and mod-ing the openid challenger python file and putting a new entry in tiddlywebconfig.py for it
#Reporting on status of data updates
!Evening session (from 18:55 to 20:25)
#On reflection, I think having a username and password to log-in with is going to be a better system for getting the Avox accounts going
** setting up a modification of the cookie_form challenger as a plugin
** later, will try to use the diststore to let another store handle the accounts
** have a "login_form" plugin set up and serving a login page
# Adding a login status bar to the top of the page
** Where is the login name coming from? I need the status plugin... (cf. the way it's done on TiddlyWebWiki, or the [[Anna Freud Centre]] project)
*** ...although that doesn't give a non-JS way of getting the login name
** If I passed the usersign environ var, or the whole environ var to the header template, I could get this (and more) information into the template...
** looking into this...
** have changed the way that templates are rendered so I'm using a common render function that provides commonly-used variables - fields, captcha and usersign (for now)
*** and then changed it to instead provide a function to collect the common vars, which are then passed as a single object to the template
** ok, that's set up - all the templates are now taking advantage of a commonVars variable, which gives me some flexibility in the future with what I pass through
*** it didn't feel like a totally generic (i.e. simple) way of doing templating - seems odd that I have to explicity discover the commonVars each time I want to render a template - seems that should be a config choice rather than a code choice
** mod'ing {{{header.html}}} to include login status
*** I'd like to say: "you are logged in as: <name> | logout" or "login"
!Evening session (from 18:45 to 20:30; from 21:50 to 00:30)
#Continuing adding library code and tests to dependentInputs
** All tests passing
** Going to add library to search box
** Done
Spoke to Jeremy yesterday for advice about the sqlstore searching. He recommends that indexing is going to be helpful in producing better performance. After my investigations, I think I am going to ask Chris to do the high-performance search function; for now, I'll make do with the existing search. I want the search API to handle searching and filtering in the same step, so whatever implementation of search I use, the /search endpoint is going to have to pull apart the query into filters. Actually, given that I'm searching on field values, I think all the searching is going to be done with filters (since existing search only searches tiddler titles and text) - actually, the tiddler title is the AVID, so that's the only field that will use existing search.
!Morning session (from 11:00 to 11:30)
#Drafted some interface designs for how the search and filtering is going to look and work
#At this point, my computer broke
!Afternoon session (from 14:30 to 16:20)
#Tacking the 'terms of use' user story
** Have provided link to existing terms of use, rather than provide static page - ''need to check whether existing static pages are going to remain available''
#Tackling the 'browsing' user story
** Cleanup of main page so superfluous elements are not on it (only the ones required to satisfy the user stories are to be used) - DONE
** Re-format of column display so it is dynamic
** Adding [[wserver|http://github.com/tiddlyweb/tiddlyweb-plugins/blob/b7705b1319ff99e5d717c59a70af8d39635c367c/reloader/wserver.py]] plugin and [[reloader.py|http://github.com/tiddlyweb/tiddlyweb-plugins/blob/b7705b1319ff99e5d717c59a70af8d39635c367c/reloader/reloader.py]] so TiddlyWeb reloads any python changes without having to restart the server
*** github being down makes this difficult right now...
** 'I can only browse having first done a search' - added index page which has form on and a welcome message
*** added routes.py plugin and tweaked templating system so it's independent and can handle lists of templates for a page
** Wanting to check that clicking on an AVID takes you to the page for a record, I've found that the system is not returning anything when you access a record... debugging
*** Have [[posted|http://groups.google.com/group/tiddlyweb/browse_thread/thread/b52803a1c68bfcc3]] on TiddlyWeb group asking about this - tiddler_as is not getting called in the Serializer, wondering whether it's something to do with my setup...
!Afternoon session (from 16:00 to 20:00)
* Making the film reel scroll more than one place
** done a basic version
*** the scrolling happens in staccato steps - can I make it smoother?
*** looking into how animation callbacks are triggered and how animations are queued
**** putting an animation into an animation's callback produces a smooth animation
**** each element has its own queue, usually called "fx"
**** there's a non-zero delay between setting up animations on different sets of elements
***** I have a stack of several animations - there is circa 40ms between the last animation finishing and starting again if the stack is repeated
***** It looks like any sort of pause between animations causes the repeated animation to be not smooth
****** I wonder if I should pre-calculate the amount of sideways-scroll I need and batch that animation...
*** have decided to side-step the animation-smoothness problem and explicitly put a gap between steps
*** changed scrolling animation to use "swing" easing
* Attached click handler to scroll clicked videos into focus
* Attaching hover handler to show video tags
** left unfinished (but not attached) - ''have to calculate position of thumb_tags differently for different sizes of image''
* Attaching click handler to tag river tags, to scroll to closest video with that tag
** done
* Attaching hover behaviour to tag river tags - highlights closest video with that tag
** wondering about what's a good visual thing to do - Josh suggests displaying the tags, as if you'd hovered over that video
* ''hovering over videos doesn't currently turn cursor into pointer''
!Morning session (from 10:15 to 13:30)
* Setting up dev environment for beta
** currently on http://antiphonal.codebasehq.com/
* Reviewing existing site from a UI point of view
** Homepage
*** Purpose of page is to welcome people to closed beta and direct them to logging in
*** At this point, remove link to registration to stop people registering without being asked
*** Change lorem ipsum text - I think we have pieces of this on the existing site, the beta site I made and Google Docs (I think)
*** Page text should be more like "welcome back" once you're logged in
** Register
*** We'd use this page to create accounts for people - they'd get an email asking them to verify the account and then they're away
*** Change lorem ipsum text
** Logging in
*** after login, should redirect to account homepage - '/'
*** login form doesn't auto-complete the email address - why?
*** we should ask for "email address", rather than "username", to prompt memory that your email address is your username
*** "please log in" form comes up when you try and access protected areas. There should not be the copy "To access this area of the site you need to log in." if you are on the '/login' page
** Request password
*** this page doesn't work - you get a login screen
** About Us
*** Change Lorem Ipsum text - We have snippets of this in several places - the beta page I made, the current site, Google Docs (I think)
*** Purpose of page is to tell people what the product is, what is can and can't do, and who the team are
** Terms and Conditions
*** Change Lorem Ipsum text - Sherene has written these in the past
*** Purpose of page is to explain how you can use the service and disclaim responsibility (is that true?)
** Privacy Policy
*** Purpose of page is to say how we treat your personal data
*** Change Lorem Ipsum text - Sherene has written these in the past
** 404
*** this is the default symfony 404 and needs overriding
** Messages
*** purpose of page is to show people what messages they've sent us and what we've done with them
*** there should be a little bit of text explaining what the list below shows and that we haven't stored email bodies or attachments
*** message ID doesn't make sense in the context of a person's account
*** there should be a "files contained" column
** Files
*** purpose of this page is to show a list of files that we've received and when
*** there should be a Details link, to mimic the layout of the Messages page
*** message ID doesn't make sense in the context of a person's account
*** there should be a little bit of text explaining that we haven't stored the files
*** It's confusing to show message zips as files, so let's not show them
** Message details
*** There should be a little bit of text explaining the hashes (or a link to a page explaining about them)
*** sp folowing/following
*** Don't show a file if it's only the zip of the message body
** File details
*** There should be a little bit of text explaining the hashes (or a link to a page explaining about them)
!Morning session (from 9:45 to 10:05)
* Adding phase 3 items to Lighthouse
!Afternoon session (from to 12:15 to 15:45)
* Fixing problem with emails not going to the correct addresses
* Wrote deploy_test script to copy package to test server, install it and restart the web server - saves time for me
* Tickets
From 11:55 to 12:10; 17:10 to 19:10
* Tickets on Lighthouse
<<readLog>>
!Afternoon session (from 15:15 to 16:45; 17:15 to 18:25)
#Adding the diststore to manage accounts
**Chris [[responded|http://groups.google.com/group/tiddlyweb/browse_thread/thread/49d4fed67cf8c578]] in more detail about how to use the diststore to let me manage accounts
** here's an example:
{{{
'server_store': ['diststore', {
'main': ['text', {'store_root': 'store'}],
'extras': [
(r'^avox$', ['mappingsql', {'db_config': 'sqlite:///
test.db'}]),
],
}],
}}}
** This has allowed me to add a user with {{{twanager adduser <name> <pass>}}}
** Now, need to check that the rest of the site still works...
** This seems to work
# Fixing bug where 'operational state' on ajax_search is not showing up right
** ...and some of the other fields look like they are not placed correctly
*** these are not a problem on the standard search
*** this has become fixed by fixing something else
** And headers don't stay the same size as columns when you add extra columns
*** this is also a problem on standard search
*** this has become fixed by fixing something else
** And ajax_search produces an error for each cell that the provided data is not the right length...
*** fixed by pushing an array into aaData for each row - wasn't pushing an array, just the raw fields
# Fixed bug where hide/show picker didn't show all the columns on ajax_search
** Had to include AVID explicitly in template as it is not returned in fields.js
# Back to diststore - testing login
** I can't login. I presume because of this code:
{{{
store = environ['tiddlyweb.store']
user = User(username)
user = store.get(user)
if user.check_password(password):
}}}
** I presume this is not finding the correct store - verifying against diststore.py code
** Narrowed things down to this throwing a {{{NoUserError}}}: {{{user = store.get(user)}}}
*** realised I'd been an idiot and put the accounts in a store in the wrong directory
# Now adding a logout button
** there's a plugin for this: http://github.com/tiddlyweb/tiddlyweb-plugins/blob/master/logout/logout.py
** Logging out seems to work now
#Responding to Avox mail
** Design updates
** Plan for cross-industry collaboration
#Phone call with Ken about usage data and direction
** Agreed to treat all data as confidential and ask about publishing any that seems useful to publish
** Agreed to talk further about the future development direction, with a balance between implementing some clearly valuable features and soliciting feedback from people who would/will use the system
#Putting additional data into a company page
** Have added all the private fields in a table on the right of the page, coloured the same as the login bar
*** Had to pass the open_fields through to the company template so I could make a dynamic decision about which fields are private
** Found that browser cacheing causes problems when viewing a page after login or logout - you see the same version as you saw before your change in login status
*** Posted about caching [[here|http://groups.google.com/group/tiddlyweb/browse_thread/thread/9e1ad2ed79e0c849]]
!Morning session (from 8:20 to 9:25; from 9:50 to 10:20; from 10:40 to 10:45)
#Trying out the email system on the server
** [[This page|http://www.wellho.net/mouth/1043_Sending-an-email-from-Python.html]] was useful
#Deploying dependentInputs code to server
#Writing a sendEmail plugin
** there are restrictions on what you can set as local recipient - seeing how I can change the recipients table
** this looks useful: http://www.boisseau.co.uk/2008/11/18/managing-postfix-mail-aliases-on-os-x-server/
!Evening session (from 17:10 to 18:10)
#Setting up no-reply address for wiki-data.com
** [[This page|http://www.boisseau.co.uk/2008/11/18/managing-postfix-mail-aliases-on-os-x-server/]] looks useful
** Hard-coded to send from 'avox@wiki-data.com' until can figure out how to get the no-reply set up properly
*** emailed Chris to ask for help
*** realised I had the 'to' and 'from' fields back to front...
#Attaching the email sending to the "suggest a new record", "challenge" and "request more information" pages
** Challenge:
---------- Forwarded message ----------
From: Webserver
To: Found An Error <foundanerror.wiki-data@avox.info>
Date: Mon, 7 Sep 2009 06:07:31 -0500
Subject: Avox Website Contact Form Information
Contact form information:
SPECIFIC REQUEST re. Correction
for Brian Coleman Enterprises Limited (AVID = 16453385)
Name: Brian Cole
Company: Avox Limited
Title:
Address: ,
City:
Province:
Postal Code:
Country: United Kingdom
Telephone:
Email: brian.cole@avox.info
Message: Generating sample e-mail for Jonathan.
**Suggest new:
---------- Forwarded message ----------
From: Webserver
To: Paul Barlow <paul.barlow@avox.info>, Kate Young <kate.young@avox.info>, Brian Cole <brian.cole@avox.info>, Ken Price <ken.price@avox.info>
Date: Mon, 7 Sep 2009 06:09:15 -0500
Subject: Avox Website AVID record suggestion
AVID record request:
Submittor Info
--------------
Name: Brian Cole
Company: Avox Limited
Email: brian.cole@avox.info
Questions/Comments: Generating a sample e-mail for Jonathan.
Record Info
--------------
LEGAL NAME: Test
PREVIOUS NAME(S):
TRADES AS NAME(S):
NAME NOTES:
TRADING STATUS:
SWIFT BIC:
COMPANY WEBSITE:
REGISTRATION NUMBER (JURISDICTION):
DATE OF REGISTRATION:
DATE OF DISSOLUTION:
ISSUER FLAG:
PRIMARY LISTING EXCHANGE:
TICKER CODE:
OPERATIONAL PO BOX:
OPERATIONAL FLOOR:
OPERATIONAL BUILDING:
OPERATIONAL STREET 1:
OPERATIONAL STREET 2:
OPERATIONAL STREET 3:
OPERATIONAL CITY:
OPERATIONAL STATE:
OPERATIONAL COUNTRY: United Kingdom
OPERATIONAL POSTCODE:
OPERATIONAL ADDRESS NOTES:
REGISTERED AGENT NAME:
REGISTERED PO BOX:
REGISTERED FLOOR:
REGISTERED BUILDING:
REGISTERED STREET 1:
REGISTERED STREET 2:
REGISTERED STREET 3:
REGISTERED CITY:
REGISTERED STATE:
REGISTERED COUNTRY:
REGISTERED POSTCODE:
REGISTERED ADDRESS NOTES:
NAICS CODE:
NAICS DESCRIPTION:
US SIC CODE:
US SIC DESCRIPTION:
ENTITY TYPE:
IMMEDIATE PARENT AVID:
IMMEDIATE PARENT NAME:
IMMEDIATE PARENT NOTES:
ULTIMATE PARENT AVID:
ULTIMATE PARENT NAME:
ULTIMATE PARENT NOTES:
**Request more information:
From: Webserver
To: Add A Data Record <addadatarecord.wiki-data@avox.info>
Date: Mon, 7 Sep 2009 06:10:59 -0500
Subject: Avox Website Contact Form Information
Contact form information:
SPECIFIC REQUEST re. Additional Information Request
for Brian Coleman Enterprises Limited (AVID = 16453385)
Name: Brian Cole
Company: Avox Limited
Title:
Address: ,
City:
Province:
Postal Code:
Country: United Kingdom
Telephone:
Email: brian.cole@avox.info
Message: Generating sample e-mail for Jonathan.
** added a step in the {{{/verify}}} handler to extract relevant information and send it to an email sending script if the CAPTCHA comes back positive
!Morning session (from 11:10 to 12:55)
#Further investigation into the bug preventing proper display of records after Chris responded on the TiddlyWeb group
#Going to tidy up the records table HTML and find a jQuery plugin that will give the dynamic features specified in the user story
**I can only browse having first done a search - DONE
**I can re-order columns - DONE
**I can hide columns
**I can sort rows by clicking on column title; first click sorts by ascending - HALF-DONE
*** first click sorts by descending
**Second click on a column title sorts by descending - HALF-DONE
*** see note above
**Sorting by a column sets the active sort; clicking on another column sets the secondary sort (and so on) – this is clearly indicated to me
**Clicking on a record takes me to the record’s page - DONE
**All the available fields are shown
!Afternoon session (from 13:30 to 17:25)
#Continuing with user story acceptance criteria shown above
** Looking at whether jQuery plugins e.g. Column Manager and DataTables will work with [[DragTable|http://www.danvk.org/wp/dragtable/]]
** Went for [[DataTables|http://www.datatables.net/]]
** Acceptance criteria:
***I can sort rows by clicking on column title; first click sorts by ascending - DONE
**** there's a problem with searching on the 'legal name' - it's not sorting sensibly - looking into this
***** fixed by setting 'sType' on the column to 'html', since they are links
***Second click on a column title sorts by descending - DONE
***I can hide columns - HALF-DONE
**** ''If I switch a column, the hiding hides the wrong one - that's because I need to update the DataTables array'' - TO-DO
**** There is a 'hide column' button at the bottom of each column - ''does this seem a sensible way to do this?''; ''I would suggest a reset button as another user story''
***Sorting by a column sets the active sort; clicking on another column sets the secondary sort (and so on) – this is clearly indicated to me - HALF-DONE
**** you have to ''shift-click'' on another column to add as another sort - I think this makes more sense - this should be explained on the table
***All the available fields are shown - HOLD
**** ''need to put some sample data in so I can change the template to display all fields''
** ''I suggest this would benefit from some design work''
#Arranging a filter box for the records table
** Adding a new filter creates a new box to search against a particular field - HALF-DONE
*** ''I suggest experimenting with a single, dynamic filter box - the interface is certainly simpler
#Some design tweaks
** Going for a broad block footer, simple search block at the top and a centrally aligned records table
10:15 - 13:00; 13:15 - 16:00
Meeting to review videos and select from them for the website. We have 11 consultants done, 6 to go.
<<readLog>>
!Afternoon session (from 14:30 to 18:25)
* Getting filmreel to play films
** Found that displaying the Longtail video player causes a white flash - however, fading it in doesn't trigger the flash
*** don't know whether this is down to the embed tag's behaviour or the Lontail video player's
* Longtail video player DOES NOT EXPOSE JS ACCESS WHEN OFFLINE
* Works
** there's a white flash when the player loads - ''why?''
** ''if you click on a tag, it doesn't hide the player''
Hours: 9hrs 20m
Agreed rate: £200/day as long as it's 8 hours (can carry from one day to the next)
!!Morning session (from 8:10am to 1pm)
#Figure out how to get Greenwood's email hosting on to the Google Account I set up
** Agreed to make the MX record transition at 5pm today so it can happen over the weekend
** Looking into how to use Google Apps mail with Outlook. There is an Outlook plugin for syncing, that works for Outlook 2003/07, but I'm looking into other options (possibly IMAP) for the office where Outlook 2000 is installed.
** Checking EasySpace MX record changing will be easy
#Do we set up IMAP or POP?
** I'm finding Google Apps support very short on information explaining about ways to get to email except through the web interface
** Finally, at the bottom of the email help page: [[set up POP and IMAP access|http://google.com/support/a/bin/answer.py?answer=105694]]
** Then, [[setting up POP|http://mail.google.com/support/bin/answer.py?answer=13273&cbid=75bc4jerj8zv&src=cb&lev=answer]], [[setting up IMAP|http://mail.google.com/support/bin/answer.py?answer=75725&cbid=-wkwbk66hofou&src=cb&lev=answer]]
** [[This is good too|http://www.google.com/support/a/bin/answer.py?hl=en&answer=33322]] and has videos on setting up IMAP (as well as info about Google Outlook sync and Calendar Sync)
** Ok, I'm convinced IMAP will be fine, having set up a trial account for me on Outlook 2000, even though Outlook 2000 is not listed as supported
#Find out why Google charged twice for Google Account set up
** It's because my account is set up separately
#Move greenwoodservices.co.uk to GoDaddy, if possible; otherwise, terminate email services with EasySpace
** Do this after MX changes have taken effect
#Getting rid of 553 error messages on BT Yahoo! email account
** Seems that account verification is not the problem, since it's the default email address that is being used, not an alternative
** Other suggested options include:
*** [[Upgrade Outlook|http://bt.custhelp.com/cgi-bin/bt.cfg/php/enduser/cci/bt_adp.php?cat_lvl1=346&cat_lvl2=401&cat_lvl3=412&cat_lvl4=418&p_cv=4.418&p_cats=346,401,412,418&p_faqid=10905]]
*** [[Deleting a stuck read receipt|http://www.howto-outlook.com/howto/deletereadreceipt.htm]] (on Outlook)
#Export Outlook 2000 contacts onto USB stick and copy onto home computer
#[[Critique of http://www.ccatf.org.uk]]
!!Afternoon session (from 1:30pm to 6:00pm)
#Finished off [[Critique of http://www.ccatf.org.uk]]
#Registered yellowcar ltd
#Migrating MX records from EasySpace to Google Apps
!Morning session (from 11:40 to 12:40)
#Creating the overlap map Google Spreadsheet from the data Flora collected on geo/sector presence of BPSF consultants
** From this: {{{ =COUNTIF(arrayFormula(IF(geo!B3:B27=1,IF(sector!B3:B27=1,1,0),0)),1) }}}
** To this: {{{ =COUNTIF(arrayFormula(IF(geo!B$3:B$27=1,IF(TRANSPOSE('sector transposed'!$C2:$AA2)=1,1,0),0)),1) }}}
** Published at: http://spreadsheets.google.com/pub?key=to9_cl4clUkW_bEDkY-reJQ&output=html
!Afternoon session (from 13:15 to 14:00)
#Consolidating region data
** Reference here: http://www.internetworldstats.com/list1.htm#geo
#Improving display of data
** Added colours to show geo's/sectors/overlaps with 0 or 1 consultant present
#Conversation with Josh about design suggestion from Neil
!Afternoon session (from 15:25 to 16:25)
#Looking back at login caching problems
** learning about [[HTTP caching|http://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html]]
** so the problem is that the browser holds a cached copy of the page you see after you've logged in and a cached copy of the page you see after you've logged out - i.e. switching states ought to change the page content, but it doesn't
*** one way to trigger a non-cached version is to put a random variable on the end of the URL
*** this prompts a question - do links from that page end up returning the cached version?
**** er, yeah
** how does Facebook do it?
*** looks like a combination of the 'no-cache' HTTP header and setting random vars after paths e.g {{{ /logout?random=12345 }}}
** getting on to IRC to ask about this
** #pubstandards:
{{{
jayfresh: hello, having some conceptual problems with browser caching - anyone able to help?
jayfresh: problem is that the browser is caching a non-logged-in version of a page, so it's displayed even post log-in
jayfresh: the converse is true when you decide to logout
russss: you want Vary: Cookie
jayfresh: russss: hi!
jayfresh: what does that do?
russss: it tells caches to use the cookies as part of the key it uses for caching
jayfresh: ah, nice
russss: so a page with no cookies set will be cached separately from pages with cookies set
jayfresh: is it widely supported?
russss: it's universally supported
jayfresh: coolio!
jayfresh: thanks
ashb: you might also want to set no-cache on certain logged in pages
16:05
jayfresh: ashb: what would make you choose to set that?
ashb: depending on how much you want your logged in pages to be cached
ashb: s/how much/how long/
jayfresh: ah! to protect privacy
jayfresh: ?
russss: yeah in my experience I've always had no-cache on logged in pages
jayfresh: makes sense
jayfresh: seems like setting no-cache on all logged-in pages is the best thing if I just want the browser not to cache logged-in pages?
russss: yep
jayfresh: cool
}}}
** going to try including {{{ Vary: Cookie }}} in the response headers of all pages
*** should do it for all pages with potential variants, but given that the header bar can change, this is all pages
** found an alternative idea from Chris about using Etags, [[posted|http://groups.google.com/group/tiddlyweb/browse_thread/thread/31922155c4357202]] on the TWeb group about the options
!Morning session (from 8:20 to 9:10; from 10:35 to 12:20)
#Testing the {{{/verify}}} changes from last night
** Have that set up now for the "request more information"
#Fixing the right-column layout bug in the request, et al. pages
** Had left a {{{position:relative}}} declaration off the "request" and "suggest_new" blocks
#Adding the email facility to the "suggest new" and "challange" pages
** thinking about how to make the public/private record fields available to a python script that needs to reference them (in this case, I want them so I can check the query against the list and populate the email body with the matches - if I don't have the list, I have to hard-code which fields are ir/relevant)
*** I know the public fields are stored in part of tiddlywebconfig
*** I think it's not a bad idea to have all the fields stored in a python script
**** At the moment, I have only stored the public fields until I have a need for the private fields
**** Unfortunately, ''this means I now have duplicate content in {{{labels.html}}} and {{{recordFields.py}}}''
** that seems to be working, but ''need to test with sending out the emails''
#Making sure sendEmail.py can cope with an array of to addresses
!Afternoon session (from 16:00 to 17:40)
#Talking to potentially interested people about wiki-data
** details in [[wiki-data workshop]]
# getting svn up-to-date
# testing email linkage now things are on the live server
** seems to work
# fixing bugs in country/state drop-down spotted by Ken
** detailed in [[this ticket comment|http://wiki-data.lighthouseapp.com/projects/39178/tickets/3-all-state-drop-downs-need-to-use-state-name-instead-of-two-letter-code#ticket-3-2]]
!Evening session (from 19:30 to 00:00)
# addressing dependentInputs bugs
** fixed problems with drop-downs not pre-populating correctly or generating correct query strings
** changing first drop-down option to be "Please select..."
*** added a blank option at the bottom so I can set the value to blank before the form is submitted
# adding a button to remove filters
# Going to use dependentInputs with the "suggest new" and "challenge" pages - ''this will mean that the non-JS version doesn't have drop-downs for these inputs'', but it does mean I don't need to duplicate the country information between JavaScript and Python
!Afternoon session (from 16:00 to 17:00)
* Preparation for presentation at BPSF
!!Major
hosting
limit height of tag river so rest of nav is visible when page loads
nav column should get thinner during AJAX loading
consultant profiles loading by AJAX
video content uploaded
context column
!!Tweaks
nav content to be better typographically
loading gif
bug: consultants gallery bounces on image hover in Firefox
bug: trying to move the filmreel while it's moving messes things up
highlighting of film when hovering over tag not sexy
cross-browser testing of filmreel
content column needs an out-transition
!!Questions
footer?
Hours: 3hrs 50mins
!Morning session (From 11:45 - 13:10)
#Installing Outlook Instant Search
#Reviewed OwnersDirect advert
#Migrating AutoText from Word 2000 to Word 2007
** [[This|http://office.microsoft.com/en-us/word/HA102552091033.aspx]] was very helpful
#Figuring out why BT Yahoo! keeps sending emails about receiving 553 errors
!Afternoon session (From 13:20 to 15:00; 17:20 to 18:05)
#Fixing 553 problem
#Fixing problem with Outlook not being able to save its standard NormalEmail.dotm
#Verified set up of Google Apps mail
#Set up home computer and laptop to receive Google Apps mail
#Improving OwnersDirect advert
** Is the property competitively priced?
*** Looks like most 2-beds are between £350 and £600 a week - this one is listed at £175 - £350 (245 - 490 euros) per week - I also note that most properties don't specify the price in euros
**** ''Action'': Took the euros listing off; Dad is going to think about how he could adjust the prices to be more in line with other apartments
** Could we say something to encourage people to check the availability calendar rather than email to ask?
*** ''Action'': added this to the "Accommodation description" section: "Please check the availability calendars for up-to-date information on apartment availability. We update these each time we receive a booking, so please save yourself time and check the calendars."
** What are the most eye-catching thumbnail photos and could we have a better one?
*** See below...
**** ''Action'': added a new thumbnail
** Are there better photos for the listing? Which 8 should be picked?
*** ''Action'': added four more photographs and updated some others
** What determines the order of the property listings?
*** "Positions on the list pages are automatically generated and pages rotate every 24 hours. For example, if there are 4 pages of properties in your region, you will be on the first page once every 4 days. Within the page,the properties generate randomly, again every 24 hours."
** Can the copy be improved?
** Is it sensible to split the advert into two, one for each apartment?
** What special offers are people doing?
** Could a special offer help with this apartment? When would it be economically sensible to place it?
!!Great photos
[img[http://images.ownersdirect.co.uk/thumbnails/t78.jpg]] [img[http://images.ownersdirect.co.uk/thumbnails/t45362.jpg]] [img[http://images.ownersdirect.co.uk/thumbnails/t993.jpg]] [img[http://images.ownersdirect.co.uk/thumbnails/t42469.jpg]] [img[http://images.ownersdirect.co.uk/thumbnails/t40924.jpg]] [img[http://images.ownersdirect.co.uk/thumbnails/t40341.jpg]] [img[http://images.ownersdirect.co.uk/thumbnails/t41334.jpg]] [img[http://images.ownersdirect.co.uk/thumbnails/t34986.jpg]] [img[http://images.ownersdirect.co.uk/thumbnails/t37613.jpg]] [img[http://images.ownersdirect.co.uk/thumbnails/t37222.jpg]]
!!Awful photos
[img[http://images.ownersdirect.co.uk/thumbnails/t118.jpg]] [img[http://images.ownersdirect.co.uk/thumbnails/t45693.jpg]] [img[http://images.ownersdirect.co.uk/thumbnails/t44954.jpg]] [img[http://images.ownersdirect.co.uk/thumbnails/t44920.jpg]] [img[http://images.ownersdirect.co.uk/thumbnails/t42920.jpg]] [img[http://images.ownersdirect.co.uk/thumbnails/t34926.jpg]]
!Morning session (from 11:00 - 12:15)
#Meeting with Neil and Josh to present designs so far and discuss constraints and different ideas
** Neil will sort out getting more data for the overlap map
** Josh will talk to Mark about getting paid
** Josh will arrange us all to meet with Mark to make sure he is happy with our approach and incorporation of his ideas
** I will send out the images we brought as print-outs and invite Neil to the overlap map sheet
!Afternoon session (from 15:00 to 18:30)
#Making a decision on caching
** To make sure that things work until I have a better solution, I'm going to send {{{Pragma: no-cache}}} with all responses
*** with the exception of static files, which are going to use the normal Etag-method of expiry
** This applies to:
*** login_form
*** logout
*** methodhack - can't fix in plugin
*** pathinfohack - can't fix in plugin
*** routes
*** status
** this seems to be working
** found problem with serving company page (and presumable search results page) - they're still cached
*** this is probably because they are served with a different method
*** looking into it
{{{
cdent: well, one option is to override tiddlyweb/web/handler/tiddler:_send_tiddler so it sends different headers
cdent: you'll want to make sure you _really_ understand the different between Pragma: no-cache and Cache-Control: no-cache
cdent: and the impact of Etags being present on both
cdent: because you may not get the results you want
jayfresh: cdent: I don't
jayfresh:
jayfresh: here's my plan
jayfresh: I've just tweaked logout.py
jayfresh: so it always redirected to '/'
jayfresh: this improves the immediate experience
jayfresh: since '/' is no-cached
jayfresh: this gives me breathing room to fix caching properly
jayfresh: sound, er, reasonable?
}}}
#Deployed changes to live server
#Created avox accounts
** Sent out account details
# Getting styles and JS working in IE
** found a problem where IE6 collapses a top-margin if it has a width set on it AND there is a div before it that has a position of absolute
*** the solution seems to be to use padding-top instead of margin-top
** fixed bug with IE not having Array.indexOf
** ''there is an unfixed bug: error when loading ajax_search results''
!Morning session (from 9:50 to 12:25)
# Getting field names correct against new database field names - [[#49|http://wiki-data.lighthouseapp.com/projects/39178/tickets/49]]
** this involves getting the dev setup using mysql - here is some info from Chris about importing the data (there is the mini_wiki.txt extract in svn):
{{{
As you'll have some seen from recent mails, I've imported the latest data.
Here's how you do it:
* become root in some fashion (this is necessary because of where the files have to be)
* copy the latest extract into '/var/lib/mysql/avox' naming the file avox.txt
* run the command
mysqlimport -u avox --default-character-set=utf8 --fields-terminated-by='~' -r avox avox.txt
avox.txt is the file and with the extension chopped off identifies the table 'avox'. The previous 'avox' identified the database avox.
In the dataextracts file there is a file called 'table.sql' which creates the table. If for some reason the table has gone away it is possible to recreate the table (without any data) by doing:
mysql avox < table.sql
(as the avox user).
}}}
** involved setting up mysql, creating an avox user and an avox database ({{{create database avox;}}}), then using the table.sql file (as shown above) to create the fields; then the import could happen
*** creating a user:
{{{
mysql --user=root mysql
CREATE USER 'monty'@'localhost' IDENTIFIED BY 'some_pass';
mysql> GRANT ALL PRIVILEGES ON *.* TO 'monty'@'localhost'
-> WITH GRANT OPTION;
}}}
** it turns out the mini_wiki.txt file didn't have enough fields in - have written to Greg to ask for the full-field version
# found out that 'avid' is no longer a field - it is the tiddler title, so have to change the references to {{{tiddler.fields.avid}}} to {{{tiddler.title}}}
** also changed the 'country_of_registration' occurrences to 'registered_country'
** fields on the site look up to date now, even though I can't test with my dev version
#adding CABRE field
** done
#taking the hard-coded drop-downs off the suggest et al. pages and replacing with use of dependentInputs.js
** means I can stop using 'countries.html', which is a bad version of a country list anyhow
*** removed all uses of 'countries.html' and that file
** thinking about the best way to use some JS to add the drop-downs in
!Afternoon session (from 15:20 to 16:00; 16:20 to 16:50; 17:20 to 17:45)
# adding the country/state drop-downs to the challenge et al. pages
** reckon the way to do it is to look for the id of the forms on those pages as an indicator that we're on those pages
** writing tests and library code
!Evening session (from 18:45 to 19:45)
#continuing with the enhancements to dependentInputs so I can use it on the suggest et al. pages
!Morning session (from 10:00 to 13:35)
# Want to have a review this afternoon - need to know how much is done
# Need to get the column-dragging working with the hide column function (at the moment the DataTables data array is not being updated when you drag a column, so clicking 'hide column' hides the wrong column)
** Have found the place in dragtable.js where you could put in a function to update the data arrays of the DataTable - around line 369
** Looking at DataTables, I need to know what arrays contain table data, so I can update them; we have (assuming oTable is your DataTables object):
### oTable.fnSettings().aoData - an array, where each element is a row
**** each row has a _aData property, which is an array of the row data, in order - ''you'd want to re-order this array when you move the column''
### oTable.fnSettings().aoColumns - an array, where each element represents a column - ''you'd want to re-order this array when you move a column''
### oTable.fnSettings().asDataSearch also has the row data in order, but it's just a plain-text string, so it shouldn't need changing
** Having played around with the live DataTables object, it looks like you only need to swap the aoColumns array items and the table then behaves as you'd expect
*** Having done this, I observe strange behaviour when hiding columns after a column swap - hiding a column triggers the dragtable moveColumn function, but normally with the start and end being the same; for some reason, the end index can end up wrong, which I think is caused by a problem with findColumn - looking into it
**** wild goose chase - mistaken one log message for another
*** Now I have the problem - if you hide a column that's in the middle of two others, then switch those outer two, the switching in the aoColumns array fails to switch over the hidden column, so if you hide the right-hand outer column, that column's title remains over the data that shuffles across
**** Tried fixing by counting invisible columns before deciding which columns in aoColumns to swap
*** And now I've realised I've been doing the swap in the wrong way - it's not a swap after all, it's a shuffle to the left for all the columns - I need to change the oTable adjustment so it doesn't swap columns, but shifts the array
**** Great, that works now - here's the function I used to shuffle array elements:
{{{
var arrayShuffle = function(array,from,to) {
if(from>to) {
return array;
}
var tmpArray = [];
var c = array[from];
for(var i=0;i<from;i++) {
tmpArray.push(array[i]);
}
for(i=from+1;i<=to;i++) {
tmpArray.push(array[i]);
}
tmpArray.push(c);
for(i=to+1;i<array.length;i++) {
tmpArray.push(array[i]);
}
for(i=0;i<array.length;i++) {
array[i] = tmpArray[i];
}
};
}}}
# Want to do some design work on the table
** Have simplified display and neatened it up
*** Left-aligned cells and headers
*** Tighter page layout
*** Filter box incorporated into body of table
*** Header applied to page
*** Links made into links (not just text)
** Want to:
*** Remove borders between cells (where background colour shows through)
*** Adjust columns that are unnecessarily wide - DONE
*** Make sure the 'filter' text appears in the filter box in Firefox (it does in Safari)
*** Find out why the heading and search box jump to the right when page starts loading
# Need to start figuring out why Firefox is causing the infinite recursion problem with the server - something to do with {{{mime_types.py.guess_type}}} not working properly
!Afternoon session (from 14:25 - 18:00)
# Caught up on replying to Avox emails
# Looking at the design items left over from this morning
** Borders removed between cells using {{{border-collapse: collapse}}} CSS property
** Fixed the problem where the default 'value' wasn't appearing inside a text input tag in FireFox - turns out you have to wrap it in a form tag
** Fixed the jumping on load - moved script tags into header
*** This is better development practice, although it makes the templates less independent
# Going to make the static pages work through templating
** Would like a url of e.g. {{{/request}}} to use the request template
*** This works now
# Making template placeholder pages for static pages
** This is the first step to satisfying all the user stories involving static pages i.e. terms of use, suggest a new record, challenge, request more data
# Have also put a placeholder in for the advanced search
# Seeing if my problem serving up record pages is fixed (checking Google Group)
** Fixed by replacing {{{type: text/x-json}}} in a tiddler with {{{type: None}}}
# Need to get the records table JavaScript out of the other pages' templates, as it causes errors
** Going against what I recorded above about having all scripts in header - going to try putting at bottom of templates
** Sorted
# Trying to debug infinite recursion error that the CherryPy server gets into when requesting from FireFox - [[posted|http://groups.google.com/group/tiddlyweb/browse_thread/thread/6eda31e60bb3d2a]] about this on TiddlyWeb Group
!Morning session (from 9:30 to 11:30)
* BPSF presentation
Hours: 1hr 35mins
!Morning session (from 9:10 to 9:30; 11:50 to 12:25)
#Collecting good photographs for Owners Direct advert
#Sorting out TalkTalk BB in Bridlington
!Afternoon session (from 17:30 to 18:10)
#Sorting out Owners Direct advert - details kept in [[yesterday's log|27th June - Dad's IT problems]]
!Afternoon session (from 14:30 to 18:45)
#made sure the dependentInputs lib works for the challenge, suggest and request pages
** written new tests and lib code
#making sure I can use the same lib for both the fields tables and the 'please tell us who you are' box
** checking I can add two sets of rows to DependentInputs
#IE testing
** found a bug in jQuery's {{{replaceWith}}} function
*** reported on [[jQuery trac|http://dev.jquery.com/ticket/4604#comment:3]]
** other IE bug I found (might not really be a bug) - adding input elements to the document has to be done as self-closing tags i.e. {{{$("<input />")}}}, not {{{$("<input></input>")}}} or the {{{</input>}}} tags are treated as separate tags
*** don't know whether this has anything to do with choice of doctype...
**** didn't make a difference switching between html-strict and xhtml-transitional
** also found that IE has difficulty re-starting the tests after a stop call
*** fortunately, found that I could test without the stop and start - event triggering doesn't appear to be asynchronous
** getting a doubling of drop-downs created in IE
** also, changing field to 'operational country' doesn't create the country drop-down
*** I've found that the change event handler is not getting fired when you change the field drop-down
*** as [[this page|http://stackoverflow.com/questions/1637503/jquery-change-event-on-select-not-firing-in-ie]] explains, change events don't bubble in IE
**** trying putting the change handlers on the select elements
**** fixed, which fixed tickets [[#57|http://wiki-data.lighthouseapp.com/projects/39178-wiki-data/tickets/57]] and [[#60|http://wiki-data.lighthouseapp.com/projects/39178-wiki-data/tickets/60]] too
!Afternoon session (from 14:40 to 16:20; 16:30 to 16:40)
* Call with Adam to give progress update and schedule call for next week - Wednesday
* Replying to emails
* Writing to Paul about a support contract for wiki-data/MAD
* Fixing login bug
** Chris found that it was caused by logging in from wiki-data.com rather than www.wiki-data.com
** Adding rewrite rule to map wiki-data.com to www.wiki-data.com
* Putting code on Github
** pruned repository first
** http://github.com/jayfresh/wiki-data
16:30 - 16:45
* hiding the film reel content before the page has loaded
!Evening session (from 23:15 to 23:45)
* Fixed bug with query not existing in wiki-data templates for challenge/RMI
* Email and checking Google Analytics
!Morning session (from 10:00 - 10:20; 11:10 to 15:30)
#Fixing [[#56|http://wiki-data.lighthouseapp.com/projects/39178/tickets/56]] - changing between countries didn't update the states
** fixed
#Found bugs when running test suite in Safari
** looking into that
*** it seems that triggerEvent specifically doesn't work on Safari
*** that's an old problem - updated my testrunner to not exclude Safari and it works
**** commented on the issue tracker [[here|http://code.google.com/p/jqunit/issues/detail?id=3]]
#Fixed [[#55|http://wiki-data.lighthouseapp.com/projects/39178/tickets/55]] and [[#61|http://wiki-data.lighthouseapp.com/projects/39178/tickets/61]] - both related to replacing state/country codes with human-readable names
# [[#45|http://wiki-data.lighthouseapp.com/projects/39178/tickets/45]] - making sure hide/show columnPicker is visible when the table is shorter than the columnPicker
** fixed
*** found weird bug where FF/Safari don't seem able to support {{{overflow-y:visible}}} in the presence of {{{overflow-x:auto}}}; you have to set {{{overflow-x:visible}}} to trigger the y-overflow
*** IE6 doesn't have this problem
# [[#47|http://wiki-data.lighthouseapp.com/projects/39178/tickets/47]] - made the non-JS results table able to scroll horizontally
# [[#34|http://wiki-data.lighthouseapp.com/projects/39178/tickets/34]] - styled buttons for IE6
# [[#54|http://wiki-data.lighthouseapp.com/projects/39178/tickets/54]] - simplified the request page so there aren't any options
!Afternoon session (from 16:25 to 17:25)
# Ken found a problem with the generation of addresses on the company page - the address wasn't being converted correctly into a string for the google maps lookup to use
** turns out IE6 ignores line-breaks between tags, but treats a line-break before the closing tag as a single white-space; FF on the other hand treats all line-breaks as white-space
!Evening session (from 11:50 - 00:10)
cdent: jayfresh: that's a good posting
cdent: the issue came up in a slightly different form in another thread
jayfresh: oh thank you
jayfresh: ah
cdent: but not in as direct a fashion
cdent: so is good to get it thought about
cdent: i think the fundamental problem is probably the server.perms attribute
cdent: i through that in for mahemoff_ fairly early on and while it still seems like a good idea, it is a big FAIL in the case of the situation you describe
cdent: threw
jayfresh: an obvious alternative is to list the user id's with access
jayfresh: but that's a bit of a bad pattern I think
jayfresh: not good for security or efficient
cdent: there's a fairly fundamental conflict between the tiddlywiki as a document (as tiddlywiki thinks of things) and how tiddlyweb thinks of tiddlers (as first class things)
jayfresh: You ought to be able to work out which tiddlers you have write access to based on what bags they are in
jayfresh: because you could be told what bags you had permission to
jayfresh: write to
jayfresh: you get me?
cdent: i do, but that info could go stale in the same way, when a new bag is created
cdent: it's a less bag problem, but still the same problem, moved somewhere else
cdent: s/bag/bad
0:00
jayfresh: how would a new bag get created inbetween you opening up the tiddlywiki and trying to PUT a tiddler? I don't get what you mean..
jayfresh: .
cdent: by some other user
cdent: in some other user agent
jayfresh: hmm
cdent: manipulating existing bag policies, or creating new bags
jayfresh: that could be a case where your user session is kinda not valid anymore
jayfresh: ...
cdent: tiddlyweb eschews the concept of session
jayfresh: I might have a go at tweaking Dickon's set up to show buttons depedning on what bag things are in and what user you are
cdent: as that would not be RESTful
jayfresh: ah
jayfresh: I guess I'd need some way of finding out which bags you had write access to
jayfresh: but a plugin akin to the status plugin could probably do that
cdent: right, because you can sometimes write to bags without being able to see their policies
cdent: or status itself
cdent: but imagine the situation where the server has a few thousand bags
cdent: every time you run /status you have to loop those bags to see if this user matches the policy on that bag
cdent: the way its supposed to work is that tiddlyweb would try to save a tiddler to a bag and if it got a permissions error, the tiddler's attributes would be kept, as dirty, and the user would get a permissions challenge, sort of in line, as you've kind of done
0:05
cdent: and then the tiddler would be put
cdent: the problem is the UI furniture
cdent: what you could do instead is after the first challenge, if a successful write happens, on the client side filter all the tiddlers for the bag of the one just saved
jayfresh: hmm
cdent: and update the server.perms so write is true
cdent: (or rather, present in the list)
cdent: on each of the tiddlers that have server.bag set to the bag
cdent: or some kind of mental fiddle like that
jayfresh: that seems to work for all the times except that one moment just after you log-in inline
cdent: or after an initial log in, asnynchronously update all the tiddlers without reloading the page
jayfresh: you do want the client to update the toolbar command
jayfresh: but how would you know you had write access to those tiddlers?
cdent: i believe the syncmachine has the power to do that
cdent: by asynchronous update, I mean reget them from the server (unless they are dirty of course)
jayfresh: big job...
jayfresh: but sorta makes sense
jayfresh: sorta
cdent: which would now get different server.perms information
jayfresh: HEAD requests?
cdent: well you could first do the ones that are being displayed
cdent: and then all the rest
cdent: no a head request won't tell you that information
cdent: (none of these ideas are ideal)
jayfresh: haha
cdent: (just tossing them out to see if anything sticks)
jayfresh: I think the async update is the best thing
jayfresh: 'cos then I'd only need to do it once
jayfresh: even if it did mean downloading everything
0:10
jayfresh: maybe a combo...
jayfresh: ok, I have enough half-sticky ideas to come up with something!
cdent: i _really_ think that proper way to host a tiddlywiki on tiddlyweb is to make most of the tiddlers lazy
cdent: which would make this process a bit more reasonable
cdent: oh hey
cdent: here's a slightly better way
cdent: i think I can demonstrate it
jayfresh: ?
cdent: one sec
jayfresh: (by the way, I agree about laziness - I intend to make my personal TiddlyWiki lazy when I port it to TWeb)
cdent: http://tiddlyweb.peermore.com/wiki/recipes/docs/tiddlers.json?fat=1
cdent: load that up and inspect it
cdent: oh wait not that
cdent: take off the ?fat-1
cdent: yeah, that includes the permissions info for the entire recipe
cdent: without the text, so loads pretty quick
cdent: you can compare the revision value with the local server.revision to see if you are out of sync
cdent: and if you are, load up the full tiddler to refresh
cdent: otherwise just update the perms data
0:15
cdent: the expectation with this stuff is that a tiddlywiki that is hosted by tiddlyweb can be talking to the server a _lot_, perhaps even constantly
cdent: to keep itself up to date
cdent: and the URL structure and serializations are such that that's pretty easy to do, if you get yourself in the right mindset
cdent: make sense?
jayfresh: just examining json
jayfresh: mmm
jayfresh: ok, i get it
jayfresh: It's quicker than async-refreshing the whole data set
cdent: it may or may not be
0:20
cdent: it's a one web request rather than multiple web request way to do it
jayfresh: aha, yes
!Afternoon session (from 15:15 to 17:15)
#Reading NDA
#Collating feedback on Phase 1
** [[Feedback on wiki-data phase 1]]
# Adding country of registration to displayed fields
# Call with Ken
** Smooth display in IE is top priority
** Workshop next Monday with Citigroup
** Call Wed afternoon with Paul and Wrexham team about feedback
# Fixing bug with column sorting
!Evening session (from 21:00 - 21:30; from 22:40 to 23:00)
# Diagnosing IE problems
** Found problem with FixedHeaders reading the CSS width of a {{{th}}} element and getting {{{auto}}} rather than the pixel value
*** Fixed by using jQuery's {{{width}}} function instead
*** Reported on DataTables [[forum|http://datatables.net/forums/comments.php?DiscussionID=625&page=1]]
!Morning session (from 10:30 to 11:45; 12:30 to 13:25)
#Conf call with Adam and Amit
** worked through some design considerations
** agreed to put together a time-scale for phase 1 build (where phase 1 here means building existing designs, with new designs - happening in parallel - considered phase 2)
#Putting project assets online
** Graphics in DropBox - Amit, me and Adam have access
#Breaking down build so we can estimate time to build
** Pages to build in Amit's pad
#Emailing Greg and Ken about test server
To-do:
#Putting tasks on Lighthouse
#Looking into setting up Bespin as collaborative development place for phase 1 build
!Afternoon session (from 15:15 to 17:00)
#Answering some emails
#Fixing bugs reported by Ken on MADv1
** Making the columnPicker wide enough so you can see all the private field names
** Allowing the results table to scroll horizontally...
*** If I set overflow:auto on the table wrapper before loading the content, a horizontal scroll bar appears. I could set overflow:auto //after// loading the content.
*** Also, I know that in IE there are problems if I have overflow:auto switched on - ''this will require some testing''
!Morning session (from 11:40 to 12:00; 12:30 to 13:15)
* Speaking to Tony about how we can analyse and improve security of MAD and wiki-data
** Writing recommendation email to Avox
* Packaging process experimentation based on Chris' work
!Afternoon session (from 14:40 to 16:30)
* Getting an active site working for www.myavoxdata.com
* Getting MAD site going locally
** using Chris' new tiddlywebplugins.wikidata package has turned out a little harder than I thought - I think I need to install it from a tarball, but I can't find it
*** have asked Chris
{{{
cdent: go into my checkout
jayfresh: yup
cdent: do a make clean ; make dist
cdent: the tarball is in the dist dir
}}}
* To get {{{py.test}}} working, I had to do this: {{{sudo ln -s /opt/local/Library/Frameworks/Python.framework/Versions/2.6/bin/py.test py.test}}}
** this is because I'm using MacPorts. I think if I had not been, I'd have needed to add this to my .bash_profile: {{{eval `python /usr/local/dist-py/py/env.py`}}} - see [[here|http://agiletesting.blogspot.com/2005/01/python-unit-testing-part-3-pytest-tool.html]]
* Thinking about setting up static: what I've done in my MAD repo is symlink to the static file in the existing wiki-data repo I've been using. Have to update apache config to reflect this, but the advantage is that the apache config always points to the MAD tiddlyweb instance
** this also means I can put the static plugin in the dev tiddlywebconfig and it will work
* I've proved I can put extra instance plugins into system_plugins
* Trying to override templates
** thought I could just create a templates directory with templates of the same name, but that doesn't work
** looking on github for tiddlywebplugins.templates docs
** ok, it does work like that: if you want to override a packaged template using a local template, you have to override all the templates for a particular top-level template to render - you can't just override a base template, that is ignored; if you override a top-level template, you need local copies of its base template and all other included templates.
*** posted about this [[on the group|http://groups.google.com/group/tiddlyweb/t/424f64ba20c73f87]]
12:00 - 12:30 phone call with Adam planning next milestone
16:15 - 16:45 plan and estimate
<<readLog>>
!Morning session (from 8:45 to 10:45)
#Got the qunit test suite running inside AIR
** put the tests into a directory at the same level as your app code
** add AIRAliases.js to the test runner.html
** change the test runner.html so the paths are correct
** run in debug mode: {{{ adl appTests/Tests-app.xml }}}
** see [[Testing Adobe AIR applications with qunit]]
#Written more code
** sample chat alerts pop up now
** having a problem in that I can't get the close function to call using {{{window.setTimeout}}}
*** found that it was because I'd mocked it...
!Evening session (from 21:15 to 22:30; from 23:00 to 23:20)
#Ken mentioned that many things are not working in IE last night; that was a surprise as I thought things had been fixed
#Looking into system problems
#Architectural emails
#There were some serious problems with data loss on the Avox VM - Chris has restored as well as moving to Apache and mod_wsgi - here is the transcript of a conversation about both these things:
**cdent: edit /etc/hosts and add:
**cdent: 217.9.192.9 tw.wiki-data.com
**cdent: and then go to that host
**jayfresh: ah
**jayfresh: fancy that
**jayfresh:
**jayfresh: there's the search results!
**cdent: that running under mod_wsgi
**jayfresh: that's one fast mother
**cdent: the search data is just lucky: I made a backup copy of the data at some point
**jayfresh: where'd it go then?
**cdent: I have _no idea_ where the real stuff went
**cdent: and that's scary
**jayfresh: eek
**jayfresh: I flicked back through the terminal commands - there wasn't any sign of rm DATA
**22:55
**cdent: around about 6pm today there appears to have been some kind of problem with the disks
**cdent: look in /var/log/messages and search forward for: attempting task abort
**cdent: it happened again at 21:59
**cdent: so i think it's a problem with the VM host (again/still) and not something we did
**jayfresh: I see
**cdent: anyway, I got to get to bed: i'm expecting a cousin to get born sometime tonight/in the morning and want to get some sleep while I can
**cdent: before I go I'll give you the quick update on what I did:
**jayfresh: 'k
**cdent: I copied the contents of indexdir.backup to indexdir
**cdent: i turned off the twanager server processes
**cdent: i configured apache and mod_wsgi (see /etc/apache2/sites-enable/wiki-data.com and /etc/hosts)
**cdent: i got a copy of apache.py and put it where it needs to be
**jayfresh: how do I get the Avox people to be able to see this?
**cdent: i copied tiddlywebconfig.py to devtwc.py and prodtwc.py
**23:00
**cdent: edited prodtwc.py to have the right hostname information in
**cdent: and rebooted apache
**cdent: to get them to see the log information, scp it to your machine and mail them edits of it
**jayfresh: how do I get the lay people to see the website?
**cdent: to get them to see the stuff mounted on mod_wsgi they either need to do the same host adding trick i told you
**cdent: or we need to get them to add DNS for tw.wiki-data.com pointing to 217.9.192.9
**jayfresh: ah
**jayfresh: ok
**cdent: going to 217.9.192.9 won't work because things are set up as a named virtual host, which works on http headers, not the ip that is contacted
**jayfresh: yup
**jayfresh: you are genius
**jayfresh: haha
**jayfresh: wonderful
**cdent: if they can't make those dns adjustments then we can switch back to using twanager server
**jayfresh: ok
**cdent: to do that what has to happen is that the tiddlywebconfig.py symlink needs to point to devtwc.py
**cdent: it's quite likely we can make them both run at the same time if that's desired, but is likely to cause things to be slow
**jayfresh: 'k
**cdent: you got what you need to know for the next little while?
**jayfresh: oh yeah
**cdent: I'll probably be on and off tomorrow and will answer your mails in the morning
**cdent: cool
**jayfresh: coolio
**cdent: have a good night
**jayfresh: have a good baby
#Letting Avox know about changes to server setup and data loss problems
!Morning session (from 11:55 to 14:55)
#Challenge user story
** Getting the data from the record loaded up into the before and after interface
** Have decided to follow the serializers pattern created during the hack-day and created a serializer for the challenge page - it's really a case of copying the editSerializer and changing the word 'edit' for 'challenge' and changing which template to use
*** Am keeping an eye open for a pattern to use to generalize serializers (although given that you have to update config.serializers and config.content_types right when you start the server, I'm not sure if that will be possible
*** Thinking something like {{{/tiddlers/123456.xyz}}} loads {{{123456}}} into template {{{xyz}}}
*** This is all an alternative to the URL path-based templating where e.g. {{{/tiddlers/123456/edit}}} loads {{{123456}}} into template {{{edit}}}
** The tiddlers don't store display names for the fields, which means I have to hand-select what I want the display names to be - this is also the case with the records table and individual records' pages
*** I think it might make sense to store a table of display names and field names somewhere, so I can iterate through that when displaying a record
*** Some questions
**** Would it go in JavaScript on the server-side?
***** Probably server-side, because then it could be used in templates
**** Would all pages use the same list?
***** I think so - although you might not use all the fields, you would want to be consistent in your labelling
**** How would you use the list?
***** Something like {{{ {% for field in tiddler.fields %} labels[field] is tiddler.fields[field] {% endfor %} }}}, where 'labels' is the list
**** How would you get access to the list?
***** Global variable set for templates? Passed in via each serializer? Not sure yet
***** I found this about global scope assignments on the jinja2 [[documentation|http://jinja.pocoo.org/2/documentation/templates#assignments]]: "Assignments at top level (outside of blocks, macros or loops) are exported from the template like top level macros and can be imported by other templates."
*** I have put the list into the challenge template as a variable
** Acceptance criteria:
*** I challenge by editing the data first – the original data is on the left and the right contains a copy of it for me to edit or delete - DONE
*** Submitting sends an email to Avox exactly as the current method does - NOT DONE
*** I can optionally provide the source of the conflicting information (either as a URL or a less specific indicator) e.g. “What is your source for this? (Please include URL and/or sources used) We will use this to validate your challenge.” - DONE
** ''need to add the proper contact form and the ReCAPTCHA''
*** emailed Scott Ion about getting the ReCAPTCHA account details
#''Need to style the footer''
!Afternoon session (from 16:10 to 20:10)
#Setting up ReCAPTCHA
** Set up account
** Installed python client
** Set up ReCAPTCHA on challenge page
*** I didn't figure out how to call the html-generating function from the challenge, so hard-coded {{{<script>}}} tag for ReCAPTCHA
*** On success, person is redirected to challenge page with {{{?success=1}}} in the query string, so ''I need to figure out how to read that''
**** The serializer doesn't have direct access to the environ, unlike a standard plugin
**** Ah, actually it does, through {{{self.environ}}}
*** Have added success message and error message to challenge page
#Applied mimetypes.py fix to local python, as per note from Chris [[yesterday|1st September Avox Wiki-data]]
** Having so done, started getting a threading.py error seeming to arise from cherrypy - see [[this ticket|http://www.cherrypy.org/ticket/598]]
*** Have upgraded cherrypy to v3.1.2 - problem seems to have gone away
# ''tomorrow, sort footer styling and the 'terms of use', 'request more info' and 'suggest new record' pages''
!Afternoon session (from 16:15 to 18:30)
* Updating record count
** to 261361
* Getting test server onto git
* Addressing wiki-data specific tickets
** The first question is: how do I have MAD and wiki-data existing as separate apps?
** Speaking to Chris about it
** The different elements are: tiddlywebconfig.py, templates, some JS, some CSS; common elements are: several TWeb plugins, some templates (at least to being with), some JS
* Removing private data field display from wiki-data.com
** removed mention of private fields - wiki-data fields now driven by list in mappingsql.open_fields in tiddlywebconfig.py
** done
* Ribbon for wiki-data.com, advertising MAD
** asked Amit to create it
** Make Poverty History's ribbon - http://www.makepovertyhistory.org/whiteband_big_right.gif
*** CSS for that: http://www.makepovertyhistory.org/whiteband.css
* Converting terms of use page to correct format
** done
Hours: 8hrs 25mins
!Morning (from 10am to 13:25)
#Caught up with Mike and Tony; showed the demo; discovered that Futon/CouchDB doesn't work on FF3.1
#Figuring out how to plug a data format converter and different saving API into WireIT demo
** There's a couple of slight complexities - I don't know how the language loaded into WireIt describes the edges; the format given by getValue for a module is {{{ {name: "", value: {}, config: {}} }}}, whereas the format in the WireIt language (loaded at the start) is {{{ {name: "", container: {} } }}}
!Questions
!!What do you need to do to get data in and out of a WireIT demo?
The flow will look like this:
#editor language loaded from //somewhere// - possibly exposed as part of Streams mgmt API
#wiring loaded from CouchDB
#wiring converted to working
#working messed about with in editor
#working created
#working converted into wiring
#wiring saved to CouchDB.
!! What are the differences between the two formats used by WireIt?
The language format (see below) is to describe the set up of the editor itself, which includes which modules are available for use; you have a second format to describe what the layout of a particular working is, which you get from e.g. WiringEditor.getValue() (see WireIt).
To convert from a wiring to a working:
* {{{ var working = { modules: [], properties: [], wires: [] }; }}}
* for each node in the wiring, push an object into working.modules;
** there's a need to remember what node name went with which module item, so you can use the index of that item later
* take the key of the wiring node and set it to the 'name' property of the module object you just created
** there is a choice whether to use the key or the type of the wiring node as the name of the working module - using the key implies you can define pre-configured blocks e.g. detect_iplayer; using the type implies you can only use the base types of blocks e.g. regexp_replace
** the names of the wiring nodes are unique as they are keys in an object, although you can have as many modules in a working with the same name as you like, as it is an array - the type of the wiring node is the unique descriptor
* copy all the properties of the node's configuration object into the module's 'value' property;
* copy all the properties of the node's 'wireItFields' object (if it exists) into the module's 'config' property;
* set the 'properties' object of the working to the properties object of the wiring (if it exists);
* for each edge in the wiring, there will be an object in the module's 'wires' array, with a 'src' and a 'tgt' property
* lookup the indices for the edge's to.node and from.node from the mapping we stored earlier to the modules array; copy index for to.node to tgt.moduleId of the wire and index for from.node to src.moduleId of the wire
* copy to.channel from the edge to tgt.terminal of the wire and from.channel from the edge to src.terminal of the wire; if a channel doesn't exist (e.g. for terminals), use 'input' for to.channel and 'output' for from.channel
I imagine that for Streams, we'd do something like: have the call to find the list of available modules send the appropriate language file; the call to load the wiring would load the appropriate document from CouchDB.
!! BAD - How do you convert between the CouchDB format and the WireIt language?
''[Turns out this is wrong - I hadn't realised the language file data format isn't the same as the working format that WireIt uses - this below explains how to convert from a Streams wiring to a language file module, which is invalid]''
When you load up a WireIT demo, a "language" file is loaded, which is a JSON object that contains: languageName, smdUrl, propertiesFields, modules
* the format of a module is:
{{{
{name:"", container:{}}
}}}
* a container has this structure:
{{{
{xtype:""[, image:""][, icon:""][, terminals:[]][, className:""][, outputTerminals:[]][, propertiesForm:[]][, title:""]}
}}}
* a terminal has this structure:
{{{
{direction: [-1,-1], offsetPosition: {left:-10, top:-10}, name: "tl" }
}}}
The documents in CouchDB need to be converted into this format. I have a description of how do this [[here|24th June 2009 - Streams]]. There are a few things that aren't simple data format conversions:
* You need to make up some fields
* You need to look in a stream's edges property to figure out the terminals array on a module; this could be a bit flakey (although it probably isn't if a wiring was created using the converse of this process)
When you save a WireIT working, you need to convert it to the format needed for CouchDB. I should write description of that:
* create wiring object, with property 'nodes' as an object and 'edges' as an array;
* set object in nodes with key set to name property of module; value is an object with properties 'type' and 'configuration';
* set type property to container.type of module;
* to set configuration properties, iterate through fields array: for each object with a name of 'inputParams', examine its value - take the 'name' property for the key of the property in the configuration object and the 'label' property for the value;
* here, we're adding fields to the CouchDB format that are required for WireIT but not for Streams:
** create a wireItFields object;
** set the 'icon' property to container.icon;
** set the 'xtype' property to container.xtype;
** set the 'title' property to container.title;
** set the 'width' and 'height' properties to container.width and container.height;
** create the 'labels' object; we're going to fill this with the labels to use for the configuration options;
** for each object in the fields array, use the inputParams.name property as the key and the inputParams.label property as the value of a property in the labels object;
!Afternoon (from 15:30 to 20:30)
#Finishing off a function to map a 'wiring' (Streams) to a working (WireIt)
#Poked this function into the loading process
#Set up sample data to load from a static file so that an entire wiring can be loaded into the editor
#Added modules to the language file so they could be loaded into the editor
** at the moment, the loading doesn't get through the modules to the edges, because not all the modules are defined - ''need to add the rest of the modules''
!Questions
!! How do you load and save workings in WireIt?
The backend calls functions as described in the .smd file provided in the language definition, with the url to call provided as the "target" property of the .smd file.
I ran into some problems getting YUI Connect to work loading from file uri's - I've tweaked the asyncRequest function to request the permissions (as standard), but also tweaked the response handler to understand that a HTTP response of 0 is ok from a file uri.
I also found that requesting the .smd document produces a "not well-formed" error in the firebug console. Fixed by {{{ o.conn.overrideMimeType("application/json"); }}}
I have changed the .smd file to GET a static file that contains the JSON data I want to load into the system. This is simpler than setting up an interactive service, although it will be cool to do that later. I guess this is part of the demand for the management API.
!! How do I get my hook in to do the conversion before the backend is called?
I've overridden WireIt.WiringEditor.prototype.onLoad/onSave.
!!What conversion is necessary between Stream wiring and WireIt working?
It turns out that the loading requests a list of workings, so I've used a list of documents, as returned from CouchDB, as the test data. My hijacking function (see above) checks to see that each document has a type of "feed" before attempting to convert it and pass it on. There is a little bit of extra information that has to be added to each module, such as ID and Language - I haven't figured out where these come from yet.
!!Where do I get the name, ID and language for each working from when loading CouchBD data?
* name - this can be the document name - appears in the list when you hit load
* ID - ?? - doesn't seem to be used in the client
* language - ?? - doesn't seem to be used in the client
!!What do I think I know about what the management API needs to support now?
There is this thing about having a separate definition of all the modules available in the language file. That is effectively a run-down of all the available blocks available for wiring. There is the option to present both generic blocks and pre-configured blocks, since we have both a 'type' and a 'name' identifier on each block. Terminals are included too, at the moment.
POSTing a new status to the management API should switch on (or off) the feed as requested, rather than require another call to trigger the orchestrator to do the necessaries.
The CouchDB API seems fine as a structure. The management API should pass along any 409 conflicts; it should also handle validation of the POSTed document's structure - can CouchDB already do this?
See [[Streams management API]]
!Morning session (from 8:45 to 9:00; 9:05 to 9:20)
* Made the two test sites live
* Added avox and avox2 accounts to the live MAD
* Figuring out why you don't get informed about account expiry if you have an expired account
* Tweaking login form so the labels didn't get cramped in IE and spill onto three lines, pushing the second row to the right
!Evening session (from 18:20 to 18:55)
* Tweaks and tickets
* Added {{{zoom:1}}} (to add hasLayout) to the blue panel enclosing the MAD registration box in IE6/7, as it had a height problem
<<readLog>>
!Afternoon session (from 15:45 to 16:20; 16:35 to 18:20)
#Configuring support site
#Making links look more contrasty
#Data import conversation with Chris and work on import script
** can we import just by taking the more recently updated file?
#Debugging email sending problems
** going to copy me in on everything to begin with
** seems ok now
#Finding out how postfix deals with me sending mail to avox@wiki-data.com
**it seems I can't do that from gmail, but a reply from an avox.info address came through that address to me
#Launched!
#Fixed some last minute bugs
** was changing all the hidden input field values when changing personal info country drop-down on challenge et al. screens
!Afternoon session (from 15:30 to 18:20)
* Adding new accounts to IMP
* Making login refresh the logged-in ID after you login
** doing this by eval'ing TiddlyWebConfig when you close the backstage
* Making people be able to make their own version of a tiddler
** this means switching the server.workspace field to be about the recipe, so they can post to their bag
!Evening session (from 19:00 to 19:40; 20:00 to 21:00)
* Making the "customise" button move into the toolbar
!Evening session (from 18:20 to 19:00)
* Figuring out deployment process with Chris
!Evening session (from 18:15 to 22:30)
* Film reel judders on first movement
** This is because the small images don't have a default height and width, so when they have their src changed, they expand
* Film reel gets too long when too many films are in it
** Film reel was assuming there weren't enough films to fill the page when it was working out how wide to make the #filmreel div
* IE testing
** IE7
*** there are no line-breaks in between tags in the tagriver
** IE7 and IE8
*** clicking a tag in the tagriver throws an exception
**** fixed - was using Array.indexOf which doesn't exist in IE; using $.inArray now
*** the feedback didn't have enough line-height
**** fixed - manually set lineHeight - other browsers were just spilling outside their line-height
*** the tagriver separators don't appear - just boxes
**** fixed - using unicode {{{·}}} instead of HTML entity {{{⋅}}}
*** feedback in black not red
*** lack of anti-aliasing on semi-transparent tagriver rows
**** fixed - removed use of opacity, it [[doesn't work in IE|http://stackoverflow.com/questions/2282576/ie-jquery-opacity-anti-aliasing-issue]]
*** videos overflowing beyond bottom of some container before JS gets them in the right place
* Multi-word tags are not matching against filmreel
** fixed - was not replacing the {{{ }}} before matching
* Randomise films
!Afternoon session (from 16:20 to 17:25)
#Writing some tests and library code
!Morning session (from 10:25 to 12:30)
#Continuing estimation
** About - 6.5
** Challenge Information - 17.5
** Company Record - 8.5
** Partners - 2
** Search Results - 17
** Suggest New Record - 9.5
** Terms Of Use - 3.5
** Developer - 3.5
** Total - 68 units
#Creating info tab on company record page to figure out how much "5" is worth
** Started at 10:45, I stopped at 12:30
!Afternoon session (from 14:10 to 15:30)
#Getting back on the design build for company record tab block
** total taken to complete design: 4hrs 15mins hours for Amit, 3hrs 5mins hours for me = 7hrs 20mins
** I think there's about 1 hour of JS to do, so total is 8hrs 20mins
** so 5 units is about 8hrs 20mins
#Estimate
** 68 / 5 = 13.6
** 8 1/3 * 13.6 = 113hrs 20mins elapsed development time
** time available per week: Amit has roughly 4.5 a day; Jon has roughly 3 a day = 7.5 a day
** so the estimated amount of days from now is 15 days
!Afternoon session (from 12:10 to 15:30; 15:45 to 17:00)
* Emailing Adam
* Thinking about how to change my local wiki-data repository to use the wiki-data package
** this involves merging my changes into Chris' wiki-data repo, which contains all the source code, then moving {{{/tmp/wiki-data}}} to {{{/wiki-data}}}, building a new wikidata package, then making {{{/tiddlywebs/wiki-data}}} and getting that to use the wiki-data package
** I'll do this when Chris is happy the packaging process is stable. I'm going to continue to make changes to my wiki-data repo in the mean-time. I can propagate them manually later.
* (ref #63) bypassing CAPTCHA if someone is logged-in
** making a change to dependentInputs so that addRow throws an error if container, field or val are empty
** changing the way that /verify works - we return formError in the URL if there is an empty field, we return captcha=0 if there is a problem with the captcha, captcha=1 if not
*** actually, the server-side validation doesn't operate yet, so formError is not going to be used
** that's mainly done - I need to get metadata about people into the system so I can pull out their name, email address, country and company
*** looking into [[userbag|http://github.com/tiddlyweb/tiddlyweb-plugins/blob/master/userbag/]]
*** a plan: create the "users" bag, put tiddlers in there that match usernames, amend userbag to return the fields/text of those tiddlers
**** problemo: diststore doesn't let you use twanager bag... I'll create the users bag manually
*** ok, have made a modification to userbag.py as described and posted [[on the group|http://groups.google.com/group/tiddlyweb/browse_thread/thread/cdbd93a7223790cc]]
*** have got fields out of the store for accounts and have put those into the page - ''need to update request and suggest to use the same mechanisms''
*** ''also have a JS bug on the challenge page''
!Evening session (from 18:30 to 19:10; 19:15 to 19:45)
* Putting Amit's ribbon and new word-mark in place (ref #20, #62)
Hours: 5hrs 35mins
!Morning session (From 10:15 to 13:20)
#Change the loading process to use CouchDB instead of a static file
** Modified yui-rpc slightly so it can handle not passing any data into a RPC service method (catches an exception rather than dying)
** Modified WireIt.customFields to store the key -> name mappings for CouchDB documents as well as the CouchDB-specific properties of nodes
** To save changes to a feed, I would have to enhance yui-rpc to support PUT or put in an explicit hack (see below about POSTing to CouchDB)
#Helped MikeB to get the prototype into the Streams management site (under /edit)
#Synced my development repository with the Streams managment site
!! Can I use POST to update a name document in CouchDB?
No, the CouchDB [[REST API docs|http://wiki.apache.org/couchdb/HTTP_Document_API]] say that POST always creates a new document.
!Afternoon session (from 15:15 to 17:45)
#Plan with MikeB to:
##make the saving use PUT
##populate the left column using the plugin and terminal enumerators: http://localhost:5984/feedshub_status/_design/plugins/_view/all
#Make the saving use PUT
** Did this by using the jquery-based function I did for the really simple prototype(tm)
** Realised I don't set the name of the stream in the properties window; plus, description is not being stored
#Populate the modules list with a list of plugins from CouchDB
!! How do I modify the modules list?
A combination of {{{ getOptions(options) }}} and {{{ buildModulesList() }}} ought to do it. I'm thinking I'd write a new WiringEditor method to update the modules list by loading up the list of modules and doing whatever format conversion necessary (two functions - one to update list, one to convert from Streams format to WireIt format?).
!! How do I deal with the fact that the WireIt module information is absent from the Streams documents?
The missing data is:
* xtype
* icon
* width
* height
* fields - label, type
* terminals - direction, offsetPosition
I think most of these can be inferred from the available data, although setting the layout for the combination of config options and input/output terminals could be fiddly. I wonder whether using the input/output containers could help (still waiting for a response to my question on the wireit group - http://groups.google.com/group/wireit/browse_thread/thread/bd6ed497b94acf24)
!! What is the format of the plugins list coming from CouchDB?
From http://localhost:5984/feedshub_status/_design/plugins/_view/all:
{{{
{
offset:0,
total_rows:16,
rows: [
{
id: <plugin_name>,
key: <plugin_name>,
value: {
_id: <plugin_name>,
_rev: <revision>,
author: { name: "a person", email: "email@example.com" },
configuration_specification: [
{ label: "", name: "", type: "" }, ...
],
database_specification: null,
global_configuration_specification: [],
harness: "java",
inputs_specification: [
{ label: "", name: "" }, ...
],
name: <label for plugin>,
outputs_specification: [
{ label: "", name: "" }, ...
],
subtype: "pipeline_component",
type: "plugin-specification"
}
},
...
]
}
}}}
!Afternoon session (from 15:00 to 16:00)
#Trying to get the app wired up to the real Salesforce chat feed
** can't get ajax working in AIR, looking into it
*** looks like using {{{async: false}}} on the jQuery ajax call is not a good idea
** got the basic POSTing to ChatLoop working, but the login details were wrong
*** also, the salesforce connector doesn't seem to work
!Morning session (from 12:15 to 13:00; 13:30 to 14:00)
#To 13:00, reading and replying to Chris' email updates
** Chris has imported the extract into a sqlite database and set up the sqlsearch plugin he has been writing
*** ''the field names are now different, so the templating code needs to be updated to use the new names''
** Update from Chris:
***START 20090902 14:00
***Add a README with examples and license info to sqlsearch.
***STOP 20090902 14:30
***START 20090902 21:15
***Importing the data extract.
***For now using sqlite3 (installed via apt-get) as the database. Can migrate easily later if desired.
***Created the import locally and then proceeded to copy it up to the avox server, but doing the scp is being _very_ slow so will import locally. I think there must be something rather funky with whatever firewall is in front of the server: even copying up the original extract file is painful.
***The fields have been imported with spaces changed to _ and letters lowercased. This appears to be different from what existing code is using.
***It's _incredibly_ slow, so some index tables will be needed.
*** *time passes*
***These indexes were created, this made a huge difference:
****sqlite> create index titles on tiddlers (title);
****sqlite> create index fields_names on fields (name);
****sqlite> create index fields_values on fields (value);
****sqlite> create index tiddler_id on revisions (tiddler_id);
****sqlite> create index revision_id on fields (revision_id);
***(Other indexes will probably be needed.)
***STOP 20090902 23:59
#Nick made a snagging list for CSS
**not enough space on the top margin
**not enough top/bpottom padding on the logo or pronounciation bit
**search box not far away enough from header and not close enough to table
**search field not wide enough leaving a large grey section to right
**no bold on table titles
**no footer as you mentioned
**not enough left padding on search box 'company name search' or the table first column
**columns not aligned - look at the far right column to see this clearly
**expected 'filter results' to also be called 'search by field' as we talked about as the functionality is the same
**needs the buttons from me
**inconsistent vertical alignment in the table
**did I miss the yellow / hot pink rollovers? ;)
#Committing Chris' server changes to svn
#Styling footer
#Installing sqlite so I can run TiddlyWeb with the new database store
** Setting up local environment to work with sqlsearch store
** Run into problem with organisation of repo - some plugins only symlinked
*** Chris re-organised
!Afternoon session (from 15:30 to 19:55)
# Posting on the TiddlyWeb group about security of TWeb-based systems, prompted by note from Ken about security being important
# Set up database on local version
** It's really slow to query to begin with
# Sorting the field names so they work with the new data
** Changing templating system so it uses a FileSystemLoader - this means I can import the global field names -> proper names map from a central template
** Ok, this is done, but I have created a dependency - ''the javascript loading the datatable has the number of columns hard-coded in its options, so if the number of labels changes, this will need to change''
# 'Terms of Use' story
** Acceptance criteria:
*** The terms of use on the site at the moment can be re-used - DONE
** Adding text from avox site
*** Processed to remove not UTF-8 characters and replace with HTML-encodings e.g. {{{ö}}}
# Sorting out index page so you can make a search query
** This is part of the search story
# Added AVID Search box to the search bar
# Made the filter button work properly
** Clicking on the plus button adds a new advanced search field
# 'Analytics' story
** Acceptance criteria:
*** Google Analytics installed on each page - DONE
** Added an extra bit of code to not trigger log if hostname is {{{localhost}}} i.e. you're developing
# Some feedback from Ken
** A bit of back seat driving here – I’ve checked the prototype and have one comment at the moment which is that we should ensure the column headings remain fixed on the page so the user always knows which column they are looking at.
** Note that there will be one additional column which will be called CABRE (a 10 character identifier). It’s not in our feed to you yet.
!Evening session (from 23:25 to 23:55)
# Discussing options with Chris for search plugin and interface interaction
** We agreed that the advanced search interface would continue to use dropdowns and the name of these inputs would be like <field>name with values <field>value, or something along these lines
** I can use JavaScript to set features of the advanced search, since in this interface you can't actually make an advanced search if you don't have JS enabled
13:15 - 13:45 Skype call with Josh, Paul presenting
Salesforce demo first
Looking at tabs.
Prescriptive flow: form display and edit
Flows can have steps
Things feel like objects - you create new of an object - objects relate to tabs
On SL app:
Home page is the same as for other apps
Build pages around the concept of pages
* Undesigned version (e.g. Web Site) looks a lot different to standard look and feel (e.g. Web Forms)
** desire is to move towards SF look and feel with losing usability
* thorns:
** TinyMCE integration quibbles
*** link popup is not so bad
**** should default to site page picker with external link secondary tab
*** image picker -> browse gallery is very simple, works fine when you have 5 images, won't when you have 100; it's also hard to tell from thumbnails which size you're looking at
**** picker should really open with the gallery as the front tab, if you want to link to an external image, you can go into MCE default
** Web Forms
*** forms expose objects e.g. SL exposes Lead object and lets you create a contact form that will automatically create a Lead when someone fills it in
**** it's not clear how extra fields will look when it's rendered on the site - dynamic preview would be good
**** you can't re-order fields with drag 'n' drop
* Other things: look and feel and usability problems
** one thing Paul particularly dislikes: go to Contact Us page editor - you have a link to the Web Form "ContactUsForm" rather than a display of the form - how would you lay out that page?
Types of pages people can make:
simple, blog (posting), change password, form, permissions-controlled doc gallery, right sidebar (but otherwise simple), login
Access question: Paul will send passwords on an instance all of our own to see it being built from scratch and also to avoid tripping over each other; we'll make a little site to see what it's like to use (how about a SL redesign site?).
There are lots of CSS hooks. The code for a page is done in Apex.
Generating some page content dynamically by getting data out of SF as JSON and rendering contents with JS (such as slot editors and templates). Not calling the SF API. When you create a page, a lot of possible outcomes are generated as JSON and the JS will either use it or not. Example is portal pages (access controlled) - before someone makes a choice about whether a new page is a portal page, all the user account data is loaded as JSON - just in case.
Josh found this: http://www.tyssendesign.com.au/articles/cms/file-and-image-management-plugins-for-tinymce/#phpletter
!Afternoon session (from 15:40 to 17:45)
#Figuring out how DropBox works for version control / conflicts:
** if Amit edits a file and I do at the same time, I get a new file with the suffix "Amit Dave's conflicted copy"; if Amit then re-edits the file, my changes get wiped (maybe this is dependent on the nature of the changes...) - not quite ideal - my file should be saved as the conflicted copy (maybe)
#Working on the company record page
** Amit did 45 mins earlier
** I'm adding the JS to get the tabbed interface working correctly
** Learnt an important lesson: ''z-index only works on positioned elements''
** I stopped at 17:25, Amit stopped at 17:45 - I did 1hr 45mins, Amit did 2hrs 50mins; total = 4hrs 35mins
#Fixing bugs with Suggest/Challenge pages
** Suggest throws an exception
*** fixed
** Challenge doesn't have any fields
*** fixed
** both problems caused by using incorrect names for variables
!Morning sesssion (from 11:00 to 12:00)
* Data structure conversations with Adam and Greg
* Answering emails
* Modifying About Us page content
!Evening session (from 22:00 to 0300)
* Scoping short-term work with Chris
* Editing about page content
* Improving static pages to include tables of contents
* Correcting display of operational state to only show if country is USA, Canada or Australia (ref #36)
* Fiters overlapping results table (ref #31)
** an IE problem
** stopped the default click action on the buttons - didn't fix the problem, but useful anyway as it stops the scroll jumping to the top
** tried making the event binding exactly the same for the two links
*** no effect
** trying taking the black '+' away
*** nope, although it does look nicer
** found [[a suggestion|http://groups.google.com/group/jquery-en/browse_thread/thread/98640dffacc753bc]] to inject some dummy element into the DOM to get the page to redraw
*** also found [[this on Ajaxian|http://ajaxian.com/archives/forcing-a-ui-redraw-from-javascript]]
*** the problem is with relatively positioned elements not being redrawn - this is consistent with what I see, as the hide/show box and the table wrapper are both relatively positioned
**** the question is: which element should be redrawn
***** the answer is: the advSearchContainer
***** I'm forcing display:none, display:block on the the advSearchContainer, which fixes things - problem is that when removing a filter, the row has disappeared before my click handler can kick-in, so it doesn't get executed (I think)
** found that jQuery click events [[don't bubble in IE|http://stackoverflow.com/questions/2179433/jquery-click-registers-in-firefox-not-in-ie]]
*** solved with a timeout with a zero ms pause (ref #31)
* talking paging with Chris
* talking data access with Chris
17:25 - 18:05
Looking over DoTank's cost breakdown and responding.
Responding to Nitobi.
Keeping Dom up to date.
9:15 - 11:00; 15:00 - 16:50
Meeting with Paul to go through updated interface design and produce description of what needs to be built:
*Homepage
** 7-column (1 per day) calendar view across 24 hours. Divisions = 15 mins
** "Go to current" button returns to the current week view
** Nav "<" & ">" move between weeks
** There's some sort of standard login - maybe their login system, maybe a standalone system
** Click on a programme to change the view to the Schedule Editor
** Click on a star to favourite a programme - needs to understand if programme is part of a series
* Schedule Editor
** 6-column (1 per stream) calendar view, division = 1 min
** Resize and move the blocks by clicking/dragging
** The carousels contain text - if it's too long it wraps
** Each channel has a slider, 0-11: override is needed, there would be stylistic changes to show that the override has happened, such as greying all the channels out and/or changing the colour of the overriding volume slider
** Reset button - sets levels to default
** When a volume slider changes, a request is made to the server
** On load, a request is made to the server to get the structure of the interface - number of streams, divisions, etc.
** Preview output is a display of the labels, shown in proportion to their volumes (scrolling in preference, perhaps flashing)
** Clicking a carousel opens a carousel editor
** Not all columns are editable - shown via a padlock icon on either the stream or the carousel (undecided)
* Carousel editor
** Not modal, a floating box
** There's a service to query which feeds are available; these are listed with appropriate icons, age and source
** You can drag feeds to the queue, either as new or to override one
** LiveText is linked to RDS and typed-in text is cloned to RDS. If you uncheck a checkbox in the middle, you can type freely in the RDS box. The lengths of the boxes are limited to 128/64 characters respectively
** There's a Save and Cancel option; on Save, carousel is updated, server is notified...
* Enhancements
** Graph
*** some sort of visual representation of labels, weighted by volume, over time - algorithm to be decided
*** updated when interface changes
** Carousel summary
*** When carousels are different sizes, they will display different summaries of the label content - from all of it down to icons
** Image support
*** A carousel needs to be able to show an image
*** Output preview needs to be able to show an image
** Schedule Editor zoom
*** You can zoom the timescale in and out, which would adjust the carousels as well
*What's big
**Calendar view, with the moving/resizing. There is a [[jQuery week calendar|http://github.com/robmonie/jquery-week-calendar]] plugin that could help with this
*What's medium
**Rendering/updating the interface based on communications with the server
*What's little
** Client-side label validation including an interface to write labels correctly
!!Timing
Go-ahead for project likely 1 month away. Foundation phase roughly 4 weeks, then the UI stuff starting early August.
!!Browsers
IE7/FF3.0
!!Client->server communications
Communication done via JSON.
#Client asks for structure of interface: time intervals, streams, default volumes
#Client asks for all carousels -> in each carousel, gives you all the labels, start/end time, which stream it's in
#Client asks for stream volumes -> all stream volumes returned
#For homepage, client asks for list of programmes in a week -> returns list, each has programme name, date, time
#Client can create, update and delete carousels -> response is a carousel object; if there is an error in the submission, returns error code e.g. 400 bad request
#Client asks for list of user-generated content (UGC) items -> returns list, each has type of UGC (from SMS, RSS, Twitter), content, age
#Client polls stream volumes endpoint to check for channel overrides - efficiency could be gained by using eTags/HTTP headers to indicate no change, rather than always sending all volumes
#Client can send volume changes -> response is volume
Endpoints:
* programmes: list
* carousels: list, create, update
* volumes: list, update
* structure: list
[[Scheduler 4 stories for interface]]
<<readLog>>
!Evening session (from 20:50 to 22:15)
* Fixing wiki-data matcher to use in demo on Friday
** found that wiki-data is not supporting jsonp as it used to... why?
*** seems that the JSONP serializer has been left out of the wiki-data package
** tried to add tiddlywebplugins.jsonp as a system_plugin in wiki-data package, but it's not working. I don't know if I can specify system_plugins in the config.py
*** I've emailed Chris about this
** I'll try adding tiddlywebplugins.jsonp directly to the tiddlywebconfig.py of test.wiki-data.com
*** this works
!Morning session (from 11:50 to 16:00)
#Finding out why the salesforce connector doesn't work, as using that would make things easier for me
** connector login function isn't async - this may be a problem - going to try and make it async
*** ok, I have the login working asynchronously - going to check that Adobe AIR ajax has to be async, or whether there's a trick to make it work sync
** there is this in the [[AIR security FAQ|http://labs.adobe.com/wiki/index.php/AIR:HTML_Security_FAQ#XMLHttpRequest]]:
> asynchronous XMLHttpRequest started during parsing time always finishes after parsing time
> synchronous XMLHttpRequest started during parsing time that are pointing to remote resources (outside application directory) don't return any data / these are restricted during parsing time.
** parsing time is the period up to the end of the last handler attached to the "onLoad" event
#Tweaking the connector library so it works async for the two functions I need (login and request)
** the SOAPTransport send method doesn't return anything if you use async
** so where does the returned JSON string get eval'ed?
*** ah, it's returned XML and it's run through an XML writer
** ok, it's working - a sample chat:
{{{
{
type:'ChatLoop__Chat__c',
Id:'xxxxxxxxxxxxxxxxxx',
CreatedBy: {
type:'User',
Id:null,
Name:'John Dory',
},
ChatLoop__Text__c:'this is a great chat',
ChatLoop__Timestamp__c:'1.256039043769E12',
ChatLoop__Chatroom__r: {
type:'ChatLoop__Chatroom__c',
Id:xxxxxxxxxxxxxxxxxx,
Name:'My chat room',
},
}
}}}
#Figuring out what that crazy timestamp is
** e.g. 1.256039043769E12
*** looks like a scientific format large number i.e. 1.256039043769E12 = 1256039043769, so maybe that's seconds since 1st Jan 1970
**** yup, that's right, so this works: {{{ var d = new Date(1256039043769); }}} = Tue Oct 20 2009, 12:44:03 GMT +0100 (BST)
** that's sorted now
#Mapping chat format into the format I want to use
** done
#Hooking everything together
** it's basically working - things I'd like to improve:
*** text flows out of box - would like to make text smaller and set a sensible limit on the number of characters
**** I've limited to 75 characters
*** style the chat alerts
**** this is proving tricky because linking to a stylesheet isn't working
**** wondering if it's because of the sandbox the nativeWindow is opened in
***** I can't figure out how to load the stylesheet
***** I've just written the styles inline, ''which is annoying''
*** click to go to the chat room
**** this works now
*** only show new chats
*** two of the windows seems to remain after calling {{{removeAlerts}}}
**** probable due to splicing an array whilst using it's length as the limit for a loop
**** fixed by using new variable that's the original length of the array
*** there's no second poll for new chats
**** fixed - used {{{this}}} in the wrong scope
#Packaging
** it works
#Would like to get app in taskbar - see [[Adobe AIR for Web Standards peeps]]
#''bug'': have found that if I install the app, then open it, close it, then open it again, I can't close it - it hangs
#Sent to Stuart and Paul
!Afternoon session (from 12:30 to 13:35)
#Changing example searches to international examples
** Noticed when constructing example searches that the dynamic filter is not working properly - typing into the "operational state" box filters on "operational city"
*** Fixed - hadn't added 'country of registration' to filter drop-down (where's my automatic test that tells me this?)
** New problem: searching for "operational state=T" returns results the same results as not searching by operational state at all
*** Looking into logs to see what's going on with the query
*** Here's the weirdness:
****2009-10-04 12:39:32,914 DEBUG query to parse: operational_state:t energy
****2009-10-04 12:39:32,991 DEBUG query parsed to Or([Term('legal_name', u'energy', boost=1.0), Term('previous_name(s)', u'energy', boost=1.0), Term('trades_as_name(s)', u'energy', boost=1.0)])
*** Looking at {{{whoosher.py}}} to see why this is happening
**** ''something is happening in {{{whoosher.qparser.MultifieldParser.parse}}} - need to look at source''
**** Looks like code is at {{{/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/site-packages/Whoosh-0.3.0b24-py2.6.egg/whoosh/}}}
#Showing when there are more than 50 results
#Fixing the header not linking to the index and showing a pointer when you hover over it
#Thinking about structure of country/state drop-down lists for filter
** Will need a special handler for the "country of registration", "operational country" and "operational state" filters
** Because the search API expects the 2/3-letter codes, will use JavaScript to convert selection before submitting search
*** This is ok because you can't use the advanced search unless you have JS turned on
!Evening session (from 17:00 to 17:25; 17:40 to 19:40)
#Mapping ISO codes to wiki-data country list
** There are (as usual when working with country lists) some differences between the list of countries in the ISO list and the list used on the wiki-data site's drop-downs. In particular:
### there are 246 ISO countries; there are 244 drop-down countries
### other differences
****ISO COUNTRY (ON WIKI-DATA.COM)
****Åland Islands (missing)
****Antigua and Barbuda ("Antigua And Barbuda")
****Bosnia and Herzegovina ("Bosnia And Herzegovina")
****Saint Barthélemy (missing)
****Bolivia, Plurinational State of ("Bolivia")
****Côte d'Ivoire ("Cote D Ivoire")
****Congo, the Democratic Republic of the ("Congo, The Democratic Republic Of The"; also present is "Congo")
****Micronesia, Federated States of ("Micronesia (Federated States Of)")
****Heard Island and McDonald Islands ("Heard And Mcdonald Islands")
****Isle of Man ("Isle Of Man")
****Iran, Islamic Republic of ("Iran (Islamic Republic Of)")
****Saint Kitts and Nevis ("Saint Kitts And Nevis")
****Korea, Republic of ("North Korea")
****Lao People's Democratic Republic ("Lao Peoples Democratic Republic")
****Saint Martin (French part) (missing)
****Moldova, Republic of ("Moldova, Republic Of")
****Macedonia, the former Yugoslav Republic of ("Macedonia, The Former Yugoslav Republ. Of")
****Montenegro ("Montenegro, Republic Of")
****Korea, Democratic People's Republic of ("Korea, Republic Of")
****Réunion ("Reunion")
****South Georgia and the South Sandwich Islands ("South Georgia And South Sandwich Islands")
****Svalbard and Jan Mayen ("Svalbard And Jan Mayen Islands")
****Saint Pierre and Miquelon ("Saint Pierre And Miquelon")
****Serbia ("Serbia, Republic Of")
****Sao Tome and Principe ("Sao Tome And Principe")
****Turks and Caicos Islands ("Turks And Caicos Islands")
****Trinidad and Tobago ("Trinidad And Tobago")
****Taiwan, Province of China ("Taiwan")
****Tanzania, United Republic of ("Tanzania, United Republic Of")
****United States Minor Outlying Islands ("United States Minor Outlaying Islands")
****Saint Vincent and the Grenadines ("Saint Vincent And The Grenadines")
****Venezuela, Bolivarian Republic of ("Venezuela")
****Wallis and Futuna ("Wallis And Futuna Islands")
#Changing the filter to use this mapping
** It's all fairly straightforward except the bit where we have to submit the correct country code after the search button is clicked
*** I can't submit a form field with a name that is not in the expected fields list
*** I can submit such a field with no value
**** so how do I remove the value (which is the drop-down's value) before submit without the viewer seeing it?
**** I can use {{{window.location}}} to submit the query instead of the form submit
**** Otherwise, I'll have to handle ignoring the field on the server side
**** I've gone for ignoring on the server if a field starts with {{{_ignore_}}}
** ''remember to change the population of the filter drop-downs to use the new mapping (maybe I can go from just triggering the change event)''
** changing from one mapped field to another triggers a change to a text field when it shouldn't
*** fixed
** ''when first changing to a drop-down, the hidden input field doesn't get populated with default value''
!Afternoon session (from 16:45 to 18:15)
# Changing search interface so it produces query strings compatible with search API
# Responding to email queries
# Completion of advanced search fields list
** At the moment, if you add an advanced search field but don't add a value, this causes the asearch plugin to crash
** The drop-down has the wrong values when you submit the form
*** ''need to change the values to lower-case and replace spaces with underscores'' - in progress
*** Have told Chris about it
# Testing queries
** Performance very slow if query is small
*** Chris mentioned that the use of 'like' searches is a cause of this
# 'Requesting additional data' story
** Acceptance criteria:
*** I can see which fields are available for request, although I don’t know in advance whether the fields are populated or not
*** It is free (in this version)
*** Avox gets an email with the AVID of the record they want more information for (exactly as it works at the moment)
** Added requestSerializer to handle {{{/tiddler.request}}}
!Evening session (from 21:45 to 00:10; 00:20 to 2:45)
# Changing the advanced search dropdowns so their value updates to the right format for the search API before submission
** This is lower-case with spaces replaced by '_'
** Had a problem with this, since the search function can't really deal with the extra fields that come from the select elements (the dropdowns)
*** Have changed tack to change the search plugin to convert all keys to lower-case with spaces replaced with '_' characters
# Testing some queries
** Found a bug where putting 'london' as a query string, with 'London' as the 'operational city' returned results where 'london' was not in the name fields
*** Reported to Chris
# Taking the 'stub' result off the list
** This is temporary until we don't need to return a stub tiddler any more
# Putting a list of faster executing queries on the index page
** Looks like avoiding London but pinning down to a smaller city is a good idea
# 'search' story:
** Criteria: If there are no (suitable) results, I am encouraged to use the advanced search - DONE
# Fixed the recently introduced problem where individual records were not being linked to
# Individual records are not showing the correct fields since the record names changed
** Fixed the individual records page and improved the layout and style
** Corrected some aspects of the hcard setup so the data is more exportable
# 'Requesting additional data story'
** Tweaking what I did earlier so it works
** Styling the fields table a bit better
** Adding label for CAPTCHA
# 'Adding a company' story
** Acceptance criteria:
*** Mandatory fields: legal name, operating country (drop-down), state (required for Canada, USA and Australia; drop-down if one of these three) - HALF-DONE
**** No country drop-down nor conditional state drop-down yet
**** ''I suggest adding help icons to explain what some things are''
*** Preferable fields: rest of the operating address, parentage, entity type (drop-down list), registered address, registration number - DONE
**** Assumed that the fields we request are never more than the sum of the public fields plus the requestable fields
**** Used order specified above
*** There are two sets of fields – the first for mandatory and others we’d like, the second for extra information - DONE
*** Address fields are collapsed into a single field to begin with, with a button to expand them
** The information to use for the state drop-down is as follows:
{{{
Operational / Registered State
The Operational / Registered State fields will only contain a drop-down list if the Operational / Registered Country is equal to Australia, Canada or United States. Otherwise it will be a free-text entry field.
If the Operational / Registered Country field is equal to Australia, then the Operational / Registered State field will be a drop-down list containing the following values:
Tasmania
Victoria
Western Australia
Australian Capital Territory
Northern Territory
South Australia
New South Wales
Queensland
If the Operational / Registered Country field is equal to Canada, then the Operational / Registered State field will be a drop-down list containing the following values:
Alberta
British Columbia
Manitoba
New Brunswick
Newfoundland and Labrador
Nova Scotia
Northwest Territories
Nunavut
Ontario
Prince Edward Island
Quebec
Saskatchewan
Yukon Territory
If the Operational / Registered Country field is equal to United States, then the Operational / Registered State field will be a drop-down list containing the following values:
Alaska
Alabama
Arkansas
Arizona
California
Colorado
Connecticut
District of Columbia
Delaware
Florida
Georgia
Hawaii
Iowa
Idaho
Illinois
Indiana
Kansas
Kentucky
Louisiana
Massachusetts
Maryland
Maine
Michigan
Minnesota
Missouri
Mississippi
Montana
North Carolina
North Dakota
Nebraska
New Hampshire
New Jersey
New Mexico
Nevada
New York
Ohio
Oklahoma
Oregon
Pennsylvania
Rhode Island
South Carolina
South Dakota
Tennessee
Texas
Utah
Virginia
Vermont
Washington
Wisconsin
West Virginia
Wyoming
}}}
10:15 - 11:25; 11:35 - 11:50
Fixing bug where logging in and viewing challenge causes crash.
What I'm finding is that any account is affected; sometimes logging in or accessing a challenge page (essentially any page that uses the commonVars.usersign.fields.name attribute) creates an error; there is no difference in the client-side requests for error-causing and non-error-causing requests.
Have patched the problem temporarily by checking for "fields" in "usersign" when commonVars is created, and adding a dict with an empty "name" attribute if necessary.
<<readLog>>
!Morning session (from 11:55 to 12:35)
* Getting all repos upgraded
** what's the current status?
*** www.wiki-data.com - 217.9.192.9 - SVN in /wikidata/avoxjson-s/
*** test.wiki-data.com - 217.9.192.10 - git in /wikidata/
**** app - using the tiddlywebplugins.wikidata package in /home/avox/tiddlywebs/test.wiki-data.com
**** static - checkout of git reop in /wikidata/cdent-wiki-data/
*** test.myavoxdata.com - 217.9.192.10
**** app - using the tiddlywebplugins.wikidata package and overriding locally in /home/avox/tiddlywebs/test.myavoxdata.com/
**** static - checkout of git repo in /wikidata/cdent-wiki-data/
** gaps:
*** www.myavoxdata.com doesn't exist as a tiddlyweb
*** www.wiki-data.com does not use the new packaging mechanism and still uses SVN
*** test.wiki-data.com does not use the new packaging mechanism, but does use git
*** there have been several changes to the jayfresh git repo since cdent cloned it, meaning anything using the cdent git repo is out of date - these changes need to be merged to the cdent repo before packages are built from that repo
** plan:
*** tiddlywebplugins.wikidata to be upgraded by merging from jayfresh repo to cdent repo and building package
*** both test.myavoxdata.com and test.wiki-data.com to existin in /tiddlywebs on 217.9.192.10 and use the tiddlywebplugins.wikidata package
*** both www.myavoxdata.com and www.wiki-data.com to exist in /tiddlywebs on 217.9.192.9 and use the tiddlywebplugins.wikidata package
*** after checking of test sites, deployment to be done by changing existing apache config on production to point at new /tiddlywebs directories and restarting web server
*** in the future, production deployment can be done by upgrading tiddlywebplugins.wikidata; MAD will become have a different deployment technique ''yet to be decided - need to ask Chris''
*** test deployment is done in the same way, since those instances are meant to be clones of the production instances
*** the path from local development to production is a little unclear - I can make changes to source, but I don't want to have to update and upgrade a package just to test, so there needs to be a different way to do this - ''also speak to Chris about this''
!Afternoon session (from 14:40 to 16:20; 16:35 to 17:50)
* Merging jayfresh git repo into cdent git repo
** in captcha.py, this has been added to cdent's:
{{{
def process_captcha(environ):
captcha = {}
try:
query = environ['tiddlyweb.query']
success = query['success'][0]
if success == '1':
captcha['success'] = True
elif success == '0':
captcha['failure'] = True
try:
captcha['error'] = query['error'][0]
except (KeyError, IndexError):
captcha['error'] = "Error not supplied"
except (KeyError, IndexError):
pass
return captcha
}}}
** in challengeSerializer.py I've copied my commented out my code that got user metadata from the store, because Chris is working on a different way of doing that - cdent's version is a lot smaller because the CAPTCHA handling code has been moved to captcha.py (see above)
*** I hadn't needed this is requestSerializer.py, so I've just allowed cdent's version to overwrite it
** ''Q: what has happened to dataimport.py?''
** using cdent's devtwc.py means losing the config for userbag, which is ok given the above point about userbag.py
** ''I think I can delete jsonhtml.py (cdent has), but it is referred to in prodtwc.py''
** ''noticed prodtwc.py is not updated in cdent's repo''
** routes.py has been merged in cdent's repo with tiddlywebplugins/wikidata/__init__.py - I've copied my version of verify across, because that contains all the logic that bypasses CAPTCHA if someone is logged-in
** Chris put this comment in get_fields: "# XXX unless the fields are changing often this is wrong - ('Pragma', 'no-cache')" - ''is this correct, given that the fields change when you login or logout? (except on MAD)''
** I left my "users" bag in the store, just in case we needed it; plus, it shows the kind of metadata I want - it's not moved to the cdent repo yet
*** I also left my test user in the store, as it's not added to the git repo and is useful for testing
** I removed templates/html.html from the cdent repo, as it's empty
** Removed temlpates/labels.html from cdent repo, as it's not used
** Removed static/demo.html as it is unused
** Removed static/tests as I've moved those to the [[dependentInputs|http://github.com/jayfresh/dependentInputs]] repo
** Merging involved copying all the files into my repo
*** to begin with, I copied all my files to cdent's repo and tried to replace the remote origin with mine - that didn't work, so I've swapped back into my repo
**** makes sense really, since I only have rights to alter my repo and should make a pull request on cdent's
*** for some reason, I have ended up with copies of tiddlywebconfig.py and the templates folder in my repo, when these should both be symlinks
**** fixed
* Testing newly merged repository
** server came up, broke when I visited page
*** fixed by clearing out .pyc files
* ''Need to email Chris these questions, upload built package to test server (I've built it but not uploaded because the Internet died), upgrade the test server tiddlywebs and make sure both domains are functioning''
Hours: 3hrs 25mins
!Afternoon session (from 16:05 to 19:30)
#Writing a plugin so the modules list is loaded from CouchDB
** I load from: http://localhost:5984/feedshub_status/_design/plugins/_view/all
** This means I'm not loading terminals too
#Recording the bits of the CouchDB format I don't know what to do with
## module type:
*** A module in CouchDB has a type when it is an instance of a plugin (or it has a terminal); I need a way to create these dynamically since they are not specified in plugin specifications, only in feed documents
*** Not all types are primitive e.g. 'destination_specification' has 'natural < 65536' as a type
## module configuration
*** Usually, a module has one of 'configuration_specification', 'source_specification' or 'destination_specification'; however, in the case of 'relay_in_plugin' and 'relay_out_plugin', there is only 'configuration' - the fields in this have non-primitive types
*** also, a field with type 'bitset' in 'relay_out_plugin' has an 'options' array - ''what should happen with that?''
*** 'relay_plugin' has no configuration-type property at all - ''why's that?''
## names vs. labels
*** see question below
#Checking I can save the newly imported modules
!! Are there any fields where I need to choose the same data as I've chosen for the wiring2working map, but where I could choose a different one?
#The module has a name and a label - I think I've always gone with the name in wiring2working because the label wasn't available, although the label is the human-readable bit.
#The name and label of input fields - I need to see if I used both in wiring2working
!! How do I set the position of the terminals?
My [[question on the group|http://groups.google.com/group/wireit/browse_thread/thread/bd6ed497b94acf24]] about the tarpipe workflow modules, which set the terminals for you, was answered by someone saying that they are not in the distribution yet. I had a look at the source code of Tarpipe and found http://tarpipe.com/js/workflow.js, which defines these extra modules, although not in a way that the code can just be lifted into this prototype (which possibly explains why they are not in the distribution yet). ''Maybe I can get something from jsBox example, or other examples.''
10:00 - 10:25
Getting the AIM to work as a standalone TW.
From 16:50 to 18:30
The list:
Questions:
* On a couple of the videos when you click on the link on the consultant page it occasionally brings up the video next to it on the stream rather than the actual one, but then doesn’t do it if you go back again later
** I can't re-create this - ''do you have a specific example?''
** Erin: The videos that were doing this have now ceased doing so, but it was Georgie’s Sustainability and Property one and I think Luke’s second winning elections video. I don’t think this is a big deal as it has only done it once or twice
* Video link to JWPlayer if you pause video at the off: will this be resolved with software purchase?
** still needs purchasing - ''Erin, can you lend us a credit card?''
** Erin: I have never had a problem with JWPlayer on my machine so not sure what this is about but we don’t have a company credit card – you would have to clear additional purchases with Mark, pay for it and then add any extra costs into the invoice
To-do (roughly prioritised):
* videos not streaming uninterrupted in IE - or indeed any browsers!
** I don't know why - ''needs research''!
** video size: width 294px, height 220px - 4:3 - looks like videos are bigger than they need to be, so streaming is hard work. Looking into compression options to help with this.
*** spoken to Harry, idea is to try and resize the FLV's, since they are are 960x540px - which is 16:9 - and they don't need to be; if resizing FLV's isn't going to work, we could export the originals from iMovie at a smaller size (but this involves getting the computer back off Josh with the videos on)
*** can't help that the vids are 16:9 and very big...
* black drop down boxes containing taglines on the Consultant profiles – these will be made to drop to bottom
** will set height on hover to height of box - ''15 mins estimate''
* the stream stops scrolling when you click on a post or watch one video – this will be changed so the stream keeps scrolling
** in Chrome, the stream starts again if you pause a video - test with other browsers - ''needs research''
** Erin: I think this also may be working in Firefox now
* Also, when you launch a consultant video it takes an age to scroll through to it through the other videos - can we speed this up?
** I could have a look at speeding this up, but I think it's quite cute - ''estimate 30 mins''
* The launch video - is that controlled by cookie? In Curzon St, it relaunches every time you turn on the computer. Acknowledge that's not going to change if its cookie-controlled. But was wondering whether it was a Firefox optimisation issue?
** Yes, controlled by a cookie that is not supposed to expire for a month - will check in other browsers - ''estimate 1 hour''
** Erin: In fairness nobody got the launch video when they clicked onto it today so either the cookie thing is working or that video is no longer playing automatically when the site starts – I’ll need to find a new computer to try it out on
* the video stream disappears if you click on one of the information pages running along the bottom – this will be fixed
** I think we could load the content into the main pane ok... will try it out - ''30 mins estimate''
Done/out of scope:
* Some of Marcie's images are inappropriate... Erin is feeling self-conscious about this. Shall I get in touch with Marce and ask her to change some of them? - DONE
* We need the photo attribution page - DONE
* putting buttons on either end of the video stream for manual scrolling – this is being discussed
** out of scope
From 18:35 to 18:55
Estimating effort for new tickets.
!Morning session (from 11:35 to 13:10)
#Sorting Amit out with necessary screenshots and feedback
#Backing up data prior to hardware move
** can't connect to server over ssh - have emailed David Brook at IDSI to see if he has already started the move
#Completing adding a drop-down to country and state choices when adding a search filter
** from yesterday:
***when first changing to a drop-down, the hidden input field doesn't get populated with default value
**** fixed
***remember to change the population of the filter drop-downs to use the new mapping (maybe I can go from just triggering the change event)
**** fixed
** ''now I need to do this for the state as well''
** ''should change drop-down on "suggest a record" and "challenge" to use this list''
*** can use JavaScript object to populate drop-down instead of the python template (which will speed things up as well as reduce duplication)
#After advice from Kate Young, changing "Korea, Republic of" to "South Korea" and "Korea, Democratic Republic of" to "North Korea"
!Afternoon session (from 16:40 to 18:15)
* Continuing with the IE7 debug
** tag river problem - fixed
** stopping the film reel from displaying until it's rendered correctly - seems ok
* making videos randomise initial order - done
* found a problem with Safari 3.2.1 - doesn't report marginRight correctly, so is not pushing tag river lines across enough
** fixed, by using inline-block on the tag/tagSeparator spans (see [[here|http://old.nabble.com/How-to-workaround-Safari-marginRight-issue-td20658944s27240.html]])
!Afternoon session (from 12:55 to 16:20; 16:30 to 17:25)
* Tweaking test servers so they are correct
** making MAD test a git repo and checking my stuff into it
*** in the future, we'll make MAD deployed in a better way
* Emailing Chris my notes from repo merge yesterday
* Figuring out what's left
* Paging interface (ref #65)
** Adding next/prev buttons and altering display message to be appropriate
** I don't seem to be able to compare two numbers in Jinja2
*** I also found that this doesn't work - it doesn't return true even when 'from' is 0:
{{{
{%- if from is sameas 0 -%}
}}}
* On the subject of testing - I'm developing directly in the source repo of wiki-data and then running a server from there to test
** when it gets deployed, it is packaged as a TiddlyWeb vertical that a new instance makes use of
** I can run a devtwc.py by copying the prodtwc.py and just overriding with local system plugins and templates
*** ...except I'm having a problem - the templates that are being used are the templates from the python package, not from my local filesystem
**** posted [[on the group|http://groups.google.com/group/tiddlyweb/browse_thread/thread/424f64ba20c73f87]]
**** in the meantime, copying local template directory into installed package to overwrite
**** found that plugins are overridden, so this is a templating problem
* Entity type to be published in full (ref #79)
** the two places where entity type are used are the search results table and in the company record page
** seems sensible to use a JS mapping like I did with the country names in ISO_3166.js
** adding the full set of records to recordFields.py, as there is only the wiki-data fields there at the moment
!Afternoon session (from 14:00 to 16:20; 16:40 to 17:20)
*Reviewing Amit's recent build changes
*Catching up on discussion between Amit and Adam to see what I need to do over the next few days
** Amit's telling me to focus on fixing Suggest a record page and the tabs' Javascript on the company record page
*Updating record count on index page
*Working on the company record page
** Have fixed the position problem where the buttons were obscured by the tabs
** Removed the "all entity data" tab as that is not useful - either we have all the information displayed or we put it in tabs
** Finding the correct content for the tabs
*** sticking with the structure on the Avox [[sample record|http://www.avox.info/pdfs/wikidata_sample_record.pdf]]
*** put correct content structure in for a public record
** Tweaking JavaScript on company record page to get tabs to work correctly
** Put a placeholder in for the map on operational country tab
** ''haven't put the content in for the private data''
** putting together the skeleton for the Suggest a New Record page
*** have realised I need a new server-side bit to handle multi-step suggest/request/challenge
!Evening session (from 18:00, with Josh, to 20:00)
** Josh working on the Suggest page and fixing some graphical things
** Checking we can stick with the single-page approach for suggest/request/challenge for phase 1, as that simplifies the server-side
** Taking graphical bit out of logo
** Putting together the Suggest page, experimenting with structure for top of design (see test.html)
Hours: 1hr
!Morning session (8:50 to 9:50)
#Added the rest of the modules from example feeds in CouchDB
** Found that I had to save the state of the internal modulesByName object before calling setOptions and then merge it afterwards, since the setOptions call wipes it
!Morning session (from 11:30 to 15:30)
# Working on [[wiki-data matcher]] with Chris in Newbury
** got a basic version working
!Afternoon session (from 16:30 to 18:30; 18:55 to 19:50)
#Adding some more code to [[wiki-data matcher]]
** up on http://wikidatamatcher.peermore.com
#Writing API documentation
** see [[wiki-data matcher]]
!Morning session (from 10:00 to 10:15; 10:30 to 13:05)
#Reviewing new designs from Amit
#Design conversations with Nick and Ben
#Sorting out problems with svn data-loss and out-of-date restore
** scp the changes to the avox server
#Tweaking filter mechanism
** Countries list is not in alphabetical order
*** fixed
** Countries list has some characters not being shown properly
*** the JavaScript file has dodgy characters in
**** this could be content-type problem or file character-encoding problem
***** ''should post to tiddlyweb group''
**** have changed characters to use HTML entity codes
***** I might have to re-address this when using the country value to filter table
** When a drop-down is replaced by a text box, it doesn't add the dynamic filtering event handler
*** Try "live" event handlers to fix this
**** don't think that will work and it definitely doesn't work for change events on drop-downs (yet)
*** fixed
** Changing a drop-down should also trigger dynamic filtering
*** it filters on the wrong value - either the table needs to show the country names or the filter needs to use the ISO code
** Changing away from a drop-down should "unfilter" the table
*** fixed
** From yesterday:
***''now I need to do this for the state as well''
*** "registered state" needs to not be a drop-down until it works
**** fixed
***''should change drop-down on "suggest a record" and "challenge" to use this list''
****can use JavaScript object to populate drop-down instead of the python template (which will speed things up as well as reduce duplication)
#Changing table country display to use country name not code
!Afternoon session (from 13:05 to 18:00)
#Travel to Citigroup workshop @ Canary Wharf with Ken and Ollie
#Workshop
** Interesting angles on security and data protection: it's better than what we have at the moment (Excel/email)
#Travel back from workshop with Ollie
#Evaluation of CouchDB as alternative database technology
** This is useful: [[view cookbook for SQL jockeys|http://books.couchdb.org/relax/reference/views-for-sql-jockeys]]
** Benchmarks against MySQL here: http://metalelf0dev.blogspot.com/2008/09/mysql-couchdb-performance-comparison.html
** Conclusion: not faster than MySQL
#Timing execution of a search via server log
**breakdown of a search HTTP GET
2009-10-06 17:05:56,903 DEBUG starting "GET" request with uri "None", script_name "", path_info "/search" and query "q=bank&avid="
2009-10-06 17:05:57,576 DEBUG start whoosh_search
2009-10-06 17:05:57,877 DEBUG query to parse: bank
2009-10-06 17:05:57,953 DEBUG query parsed to Or([Term('legal_name', u'bank', boost=1.0), Term('previous_name(s)', u'bank', boost=1.0), Term('trades_as_name(s)', u'bank', boost=1.0)])
2009-10-06 17:05:57,984 DEBUG end search
2009-10-06 17:05:57,996 DEBUG ending whoosh_search
2009-10-06 17:06:00,173 INFO ::1 - GUEST [06/Oct/2009:17:06:00 ] "GET /search?q=bank&avid= HTTP/1.1" 304 - "http://localhost:3000/index.html" "Mozilla/5.0 (Macintosh; U; PPC Mac OS X 10.4; en-US; rv:1.9.1.3) Gecko/20090824 Firefox/3.5.3"
|point|time since last point (ms)|elapsed time (ms)|what's been happening|
|start|0|0|n/a|
|start whoosh_search|673|673|don't know|
|query to parse: bank|301|974|query_dict_to_search_string|
|query parsed|76|1050|parsing to whoosh query|
|end search|31|1081|search.search|
|ending whoosh_search|12|1093|creating tiddler objects|
|response|2177|3270|don't know|
*Breakdown of rest of request:
|request|time until response (ms)|elapsed time until response since first request (ms)|
|/search?q=bank&avid=|3270|3270|
|jquery-min.1.3.2.js|94|3623|
|styles.css|22|3690|
|dragtable.js|23|3723|
|reset.css|220|4045|
|jquery.dataTables.js|440|4283|
|records.js|69|4024|
|app.js|33|3999|
|FixedHeader.js|156|4158|
|WikiDataLogo.gif|20|5385|
I know that a sample size of 1 is not very good statistics. However, it is something to start from. My conclusion (of limited value) is that searching the index is pretty snappy - it took 31ms. On either side of that, still inside the /search HTTP GET, there is 673ms+2177ms happening outside tiddlyweb.web.handler.search.get_tiddlers. I'd be interested to know if that can be trimmed.
The /search GET took 3270ms. The rest of the requests took 1668ms - I think there's scope for me to trim that down. We're aiming for 2000ms in total.
!Morning session (from 10:20 to 13:05)
#Figuring out what to do
** double-s interpreted as seconds on risk assessment titles (hijacked journal thing)
** default sliders closed
** printing out client notes
*** all tagged 'ClientNotes'
*** grouped by other tag
*** date-limited
** problem with having any tiddlers having a slash in the title - 'error saving x/y: not found'; if you rename it and try to save it still has a problem 'removing locally'
** multi-group use
*** each group needs their own bag
*** any overwrites of the common stuff needs to be flagged - indicator on the tiddler?
** drop-down arrows on MainMenu items are too small - can the drop-down happen when you click on the title itself?
#Installed the formeditor plugin to make it easier to edit online tiddlers
#Working on making it possible to save tiddlers with slashes in the titles
** Added the [[pathinfohack|http://github.com/tiddlyweb/tiddlyweb-plugins/tree/master]] to get around problem on apache with tiddlers with slashes in their titles
*** Except this doesn't help because the site is running on mod_wsgi...
*** CDent updated the apached config file to allow 'AllowEncodedSlashes On' and this fixed it
** Cleaning up existing tiddlers with slashes in their names
*** think that's done, ''need to check with Dickon that it's all good for him''
#Changing the behaviour and appearance of tags in the MainMenu
** hijacking the createTagButton function
!Afternoon session (from 14:00 - 15:30, 16:00)
#Still working on hijacking the createTagButton function
#Getting patient notes to print out as desired
** from above, order is determined by:
*** all tagged 'ClientNotes'
*** grouped by other tag
*** date-limited
** created 'printClientNotes' plugin
*** it's a simple plugin that opens all the tiddlers in the story, grouped (so tiddlers appearing in multiple sections only appear in the last section to claim them), and then print that story out, replacing the story afterwards, as it was before you hit 'print client notes'
*** discovered {{{window.print()}}} (it blocks, which is a surprise)
** showed to Dickon and Peter
*** black-list: 'H-CAMA form', 'H-CAMA results', 'MyKeyworker', 'Outcome measures - final', 'Outcomes measures - initial', plus anything tagged with 'to-do'
*** can we print just 1 tiddler? (Dickon says there's a simple print plugin, or something like that - it's in the MBFT manual: http://mbft-manual.tiddlyspot.com)
*** don't need section tiddlers, however put sections in date order
*** see why firefox crashes
** on the subject of URL's and recipes: something like {{{http://imp.peermore.com/imp/manuals/amass}}}
!Morning session (from 10:15 to 12:40)
#Getting Google wave set up
#Working on content doc
** At http://docs.google.com/Doc?docid=0AcIPKE2NHCDSZGdqd2NkYzNfNzdjMm1qeHNmZw&hl=en
#Creating a page layout
** used [[balsamiq]] online preview, exported as PNG, put in [[Dropbox]]
!Afternoon session (from 13:50 to 14:30; from 14:45 to 16:00)
#Looking again at Dropbox setup
** Lina found a problem - invites are 1-use only, so sending to the team mailing list was made useless once Jelle accepted it
** Dropbox's public folders don't have a browse experience, they are read-only at the file level
*** I wish there was something in the middle - public shared folder (or read-only at any rate)
**** added a comment on [[this feature request|https://www.dropbox.com/votebox/134/allow-giving-a-public-link-to-a-folder-inside-your-public-folder]] to that effect
#Starting the page build
!Evening session (from 17:10 to 18:30)
#Got the page built, styled and with JavaScript popup behaviour on the terms
#Uploaded to Dropbox public folder so everyone can see it, mailed to ask for feedback
!Wiki-data update call with Paul and Brian (14:30 to 14:45)
#Update on status of auto-updates
#Challenge and suggest func check
** confirmed
#Update on other bugs
** pointed out what I'm aware of
#Update on design build
#Update on support
** getting data automated
** bugs not at a volume where Chris and I can't cope with them
*** we can arrange something in terms of a guaranteed support arrangement e.g. response in 4/12 hours to bug reports
** Paul is going to look into the previously arranged support contract to do with keeping data up-to-date and let me know how we take that on (Ken mentioned it in an email)
!Evening session (from 18:40 to 19:20; 22:00 to 22:35)
* Making use of Chris' changes to mappingsql to fix the results displays on the search results:
** {{{ tiddlyweb.mappingsql.index }}} carries the index parameter from the URL, since it is deleted from tiddlywebconfig.query.
** updating JS to page to the correct pages
** two problems: mappingsql.limit is 51, which makes paging look weird (would prefer 50)
*** I can just set the JS to page 50 results a time
* Updating both test servers
** changed apache config for test.wiki-data.com to use the checkout of my repo as the static directory
** did the same for test.myavoxdata.com
*** trying to work out why test.myavoxdata.com/pages/fields.js.html doesn't return any fields
!Morning session (from 11:15, with Josh, to 14:20)
*Working on the structure of the Suggest page
** Found an interesting hasLayout problem with IE6 (see the test.html page): the idea is that a wrapper contains an absolutely positioned div, which fails to sit at top:0, left:0 in IE6, instead sitting level with its sibling, which is centred. In FF/Safari, there's no problem. It turns out that if you give the original wrapper hasLayout, the problem goes away. Setting width:100% is a [[documented|http://www.satzansatz.de/cssd/onhavinglayout.html]] way to give hasLayout, but it seems adding a border also gives hasLayout, as shown by the two correctly-displaying examples.
** Compromise on the half-width stripes either side of the columns - we can make sure the left-side never comes apart from its column, but I can't figure out how to make this true for the right-side, so we're going to remove the right-side, which we think will look good anyway
** Josh stopped at 13:10
** Writing to Chris about wiki-data work coming up
** Josh back 13:30
** Incorporating striped headers into page
** Checking in IE6 and fixing up
!Afternoon session (from 16:30 to 17:00; 17:15 to 19:00)
* Answering Avox emails
* Setting up a test repository in SVN to integrate the redesigned pages with
* Putting the Suggest New Record page into the test integration
* Finding a way to test IE7 in VirtualBox
!Morning session (from 11:30 to 11:45; 12:00 to 13:00)
#Further investigation into search performance
** Chris suggested using {{{search.txt}}} to see how much time is being used for the HTML representation
** Analysing the log:
2009-10-07 11:32:41,921 DEBUG starting "GET" request with uri "None", script_name "", path_info "/search.txt" and query "q=bank&avid="
2009-10-07 11:32:43,282 DEBUG start whoosh_search
2009-10-07 11:32:44,351 DEBUG query to parse: bank
2009-10-07 11:32:44,453 DEBUG query parsed to Or([Term('legal_name', u'bank', boost=1.0), Term('previous_name(s)', u'bank', boost=1.0), Term('trades_as_name(s)', u'bank', boost=1.0)])
2009-10-07 11:32:44,613 DEBUG end search
2009-10-07 11:32:44,671 DEBUG ending whoosh_search
2009-10-07 11:32:48,768 INFO ::1 - GUEST [07/Oct/2009:11:32:48 ] "GET /search.txt?q=bank&avid= HTTP/1.1" 200 - "-" "Mozilla/5.0 (Macintosh; U; PPC Mac OS X 10.4; en-US; rv:1.9.1.3) Gecko/20090824 Firefox/3.5.3"
|point|time elapsed (ms)|time elapsed since start (ms)|% of total|
|request|0|0|0|
|start whoosh_search|1361|1361|23%|
|query ready to parse|1069|2430|18%|
|query parsed|102|2532|2%|
|search done|160|2692|3%|
|whoosh_search done|58|2750|1%|
|response|3097|5847|53%|
How does this compare to the HTML search query?
|point|time since last point (ms)|elapsed time (ms)|what's been happening|% of total|
|start|0|0|n/a|0|
|start whoosh_search|673|673|don't know|21%|
|query to parse: bank|301|974|query_dict_to_search_string|10%|
|query parsed|76|1050|parsing to whoosh query|2%|
|end search|31|1081|search.search|1%|
|ending whoosh_search|12|1093|creating tiddler objects|0%|
|response|2177|3270|don't know|67%|
Realised the above is for a 304 HTTP response - let's do that bit again for a 200 response.
2009-10-07 12:10:51,543 DEBUG starting "GET" request with uri "None", script_name "", path_info "/search" and query "q=bank&avid="
2009-10-07 12:10:52,206 DEBUG start whoosh_search
2009-10-07 12:10:52,502 DEBUG query to parse: bank
2009-10-07 12:10:52,581 DEBUG query parsed to Or([Term('legal_name', u'bank', boost=1.0), Term('previous_name(s)', u'bank', boost=1.0), Term('trades_as_name(s)', u'bank', boost=1.0)])
2009-10-07 12:10:52,659 DEBUG end search
2009-10-07 12:10:52,672 DEBUG ending whoosh_search
2009-10-07 12:10:54,869 DEBUG in list_tiddlers
2009-10-07 12:10:55,179 INFO ::1 - GUEST [07/Oct/2009:12:10:55 ] "GET /search?q=bank&avid= HTTP/1.1" 200 - "-" "Mozilla/5.0 (Macintosh; U; PPC Mac OS X 10.4; en-US; rv:1.9.1.3) Gecko/20090824 Firefox/3.5.3"
|point|time since last point (ms)|elapsed time (ms)|what's been happening|% of total|
|start|0|0|n/a|0|
|start whoosh_search|663|663|don't know|18%|
|query to parse: bank|296|1059|query_dict_to_search_string|8%|
|query parsed|79|1138|parsing to whoosh query|2%|
|end search|78|1216|search.search|2%|
|ending whoosh_search|13|1229|creating tiddler objects|0%|
|in list_tiddlers|2197|3426|up to listing tiddlers|60%|
|response|310|3636|don't know|9%|
Summarising:
|point|.txt % of total|.html 200 % of total|.html 304 % of total|
|start|0|0|0|
|start whoosh_search|23%|18%|21%|
|query ready to parse|18%|8%|10%|
|query parsed|2%|2%|2%|
|end search|3%|2%|1%|
|ending whoosh_search|1%|0%|1%|
|in list_tiddlers|n/a|60%|n/a|
|response|53%|9%|67%|
|total time(ms)|5847|3636|3270|
Total times are not necessarily consistent.
#Going to put some logging in the store to see how much time is being spent in functions there
!Afternoon session (from 13:40 to 15:20; 17:00 to 17:10)
#Testing store logging
** Noting that HTML search request, when returning HTTP 304 still makes a lot of store function calls
** For HTML search request, returning HTTP 200:
*** Up to "ending whoosh_search": 1152ms
*** From bag_get to list_tiddlers: 2381ms
*** From list_tiddlers to response: 219ms
*** Total time to response: 3758ms
*** So 63% of the time is taken repeating tiddler_get -> _map_tiddler -> _map_tags for 50 returned tiddlers - average almost 48ms to make one tiddler object
** Checking that this time is not to do with all the logging by taking out the ~150 log lines for the tiddler mapping functions
*** From bag_get to list_tiddlers: 2360ms
*** From list_tiddlers to response: 234ms
*** Total time to response: 3891ms
*** So 61% of response is between bag_get and list_tiddlers
** Finding what's in-between the call to bag_get and the calls to tiddler_get
*** It's possible that's all happening in {{{http://github.com/tiddlyweb/tiddlyweb/blob/master/tiddlyweb/control.py}}}
** Timing calls inside tiddler_get
*** These calls show that the slow point are:
#### the SQL setting up {{{stiddler}}}
#### between the start of {{{_map_tiddler}}} and the start of {{{_map_tags}}}
#### after the end of {{{_map_tags}}} and the end of {{{_map_tiddler}}}
*** 308 - in tiddler_get
*** 308 - SQL: stiddler =
*** 320 - end SQL: stiddler =
*** 321 - in _map_tiddler
*** 337 - in _map_tags
*** 337 - end _map_tags
*** 349 - end _map_tiddler
*** 350 - end tiddler_get
*** 350 - in tiddler_get
** Finding out what is slow in the {{{_map_tiddler}}} function
*** I've added logging around all the interior function calls
*** I measured 5 tiddlers - here's how the timing breaks down (t1-t5 are the times in ms to get to that step for tiddlers 1-5):
|step|t1|t2|t3|t4|t5|average (ms)|average % of total|
|in tiddler_get|0|0|0|0|0|0|0%|
|SQL: stiddler =|0|0|1|0|1|0.4|0.5%|
|end SQL: stiddler =|25|9|6|11|9|12|17%|
|in _map_tiddler|7|3|0|0|1|2.2|3%|
|start stiddler.revision()|1|0|0|1|0|0.4|0.5%|
|end stiddler.revision()|39|12|12|11|7|16.2|22%|
|in _map_tags|1|0|1|1|0|0.6|0.8%|
|end _map_tags|1|1|0|2|1|1|1.4%|
|start stiddler.created()|104|25|13|17|29|37.6|52%|
|end stiddler.created()|0|1|1|0|1|0.6|0.8%|
|end _map_tiddler|5|0|0|1|0|1.2|1.7%|
|end tiddler_get|0|1|1|0|0|0.4|0.5%|
|total|183|52|35|44|49|72.6|100%|
This says that this the SQL is slow, {{{tiddler.revision()}}} is slow and whatever happens after {{{_map_tags}}} and {{{stiddler.created()}}} is //really// slow.
So what happens between those two points?
{{{
for sfield in revision.fields:
tiddler.fields[sfield.name.name] = sfield.value
}}}
#Checking out UKData - http://www.ukdata.com/creditreports/viewPage.do?id=advanced.search
** they offer some information Avox are not planning to, such as "Nature of business SIC"
** giving feedback to Ken and team
!Afternoon session (from 15:15 to 17:00)
#Improving beta site design on Windows / IE
** sorted
*** there were some weird behaviours with text getting hidden by I-don't-know-what - needed z-index pumping up (and "position" of course - see [[4th December wiki-data]])
#Improving design with Josh's feedback
** changed font stack to degrade better on Windows (using Palatino Linotype and Book Antiqua)
** reduced size of ordinals
** changed small text to Georgia and added some letter-spacing
** made the invite input and textarea the same length
#Fixed some more IE6 weirdness
** had to introduce two spaces at the end of a paragraph to stop IE6 duplicating the last two chracters on the next line
#Added replacement icons
** thanks Josh!
#Realised forms don't POST anywhere...
!Morning session (from 7:55 to 13:00)
* Fixing problem with MAD where it has no results
** first thing, updating dev data so it has the taster field, so I can test locally
** how to empty a MySQL table:
{{{
> mysql -u root avox;
drop table avox;
quit;
> sudo mysql avox < table.sql
}}}
** testing fields.js.html locally
*** got an answer from Chris about how to override templates locally, [[on the group|http://groups.google.com/group/tiddlyweb/browse_thread/thread/424f64ba20c73f87]]
** fields.js.html is still not showing any fields, which is confusing me since the logs are showing that the correct fields are being pulled from the database
** just discovered the route is /lib/fields.js, not /pages/fields.js - oops - it's working on test.myavoxdata.com, so there must be another problem
** If MAD has the private fields always coming through, then the JS needs to be updated to cope with that
*** Ok, I have this working now!
* Updating MAD about text based on Adam's copy
* Modifying header text and images to not mention MAD
** Have emailed Amit to ask him if he can do the images
* On MAD, the paging message should say that there are results you can't see unless you login.
** like: "there are x results" (this includes y records you can't see unless you log in)
** I've added a variable to environ called 'tiddlyweb.mappingsql.access_count', which gives you the number of records you can actually see; for MAD, I'll use this in place of 'count' and use 'count' to say how many records are available if you log in
* Making the company detail page on MAD use the extended format (ref #68)
** questions about company details:
*** are the missing fields intentional (there are lots)?
*** for registration number, I've picked "Registration Number (Operational)" - this correct?
* Getting a gMaps API key for myavoxdata.com
* Deploying seems to take this pattern now:
** commit wiki-data and MAD repos
** build wiki-data
** install locally (not strictly a deployment step)
** scp wiki-data package to test server
** ssh to test server and install package
** git pull wiki-data (for the static folder) and MAD repos
** restart apache
* Adam found a bug with the CAPTCHA bypass - he said the CAPTCHA is still visible and personal information is not provided if you are logged-in
** found that /challenge redirects to /challenge/<challenge package name>, which is not what I really want - posted [[on the group|http://groups.google.com/group/tiddlyweb/t/c3ef0925ccb9e7d9]]
!Afternoon session (from 14:30 to 20:00; 20:30 to 20:50)
* Fixing paging bugs
** removed notice about logging-in if you're logged-in
** made sure button to page backwards removes the correct number of results
* Investigating CAPTCHA bug
** there's a general problem - waiting on Chris to finish off handling user metadata
* Updating header images
** Amit made new ones
* Thinking about setting up GoTestIt tests for MAD and wiki-data - the journeys to test are:
** MAD & wiki-data
*** logging in and seeing the login text
*** searching and seeing a search results table
*** going to api/terms_of_use/about and seeing content
** MAD & wiki-data (logged-in)
*** going to challenge and not seeing filled-in personal information
*** going to challenge/request/suggest and seeing CAPTCHA
** MAD & wiki-data (logged-in as admin)
*** logging in and seeing register button
** MAD (not logged-in)
*** searching for a private avid and returning no results
*** going to a company record and seeing six tabs
** MAD (logged-in)
*** search for a private avid and returning one result
** wiki-data (not logged-in)
*** going to a company record and seeing four tabs
** wiki-data (logged-in)
* Avox conf call
** convert "avox" in footer into hyperlink - done
** search filter drop-down doesn't have all the fields in it (MAD at least) - fixed
** operational state not showing on results table - fixed
*** I find the column containing operational_country, instead of assuming it, for the mapping of operational state
** we've lost the six tabs on MAD - fixed
** hierarchy tab not showing up - fixed
*** the tab row is too wide
**** .tab in comprec.css
* Adding Chris' magicuser changes to my repo
* Adding "register" link to login bar if you have the role ADMIN
* Checking account creation works
** it does
* Wondering about checking in templates symlink
** Posted about this [[on group|http://groups.google.com/group/tiddlyweb/t/d9f52133ecf75b0e]]
* Checking account creation works on real servers
** Had to remove a line in wikidata init after getting error:
{{{
File "/usr/local/lib/python2.6/dist-packages/tiddlywebplugins/wikidata/__init__.py", line 206, in create_user
password = _random_pass()
File "/usr/local/lib/python2.6/dist-packages/tiddlywebplugins/wikidata/__init__.py", line 240, in _random_pass
print 'stuff ', stuff
IOError: sys.stdout access restricted by mod_wsgi
}}}
** Works on test.wiki-data.com
** Works on test.myavoxdata.com
** twanager lusers doesn't work on MAD - throws an error with mappingsql.table not existing
*** posted [[on group|http://groups.google.com/group/tiddlyweb/t/a6471d1273cc781]] about this
* Adding confirmation step to account registration
** amending init script for wikidata
** failed registrations work, successful ones don't (a MAGICUSER bag error is happening - I've written to Chris about this)
* Having a look at suggest_new.html to see how Chris did the user info integration; comparing to my challenge.html
** I've sorted out the bypass for challenge/RMI/suggest, I think
*** I'm going to wait for Chris to fix the no MAGICUSER bag error, as it's giving me problems registering local accounts and testing MAGICUSER
* Fixed up company record tab - the h3's were not showing in Safari
** added position:relative to the h3's
!Morning session (from 10:35, with Amit to 11:00; 12:30 to 14:00)
* Reviewing what needs to be done
* Amit kept going when I stopped
* Fixing Suggest page in IE
** links not inheriting page text colour - DONE
*** {{{ a { color: expression(this.parentNode.currentStyle.color); } }}} - from comments on [[this page|http://snook.ca/archives/html_and_css/inheriting_link]]
** stripe doesn't appear tall enough - DONE
*** removed the stripe from IE 6 and 7
* Trying to get the results page templated
** this is tricky because the serializers control how this page (and the company record page) is templated, so really I'd have to have another instance of TWeb which uses different templates...
*** trying to speak to Chris about this - how best to have a separate dev instance branched from the real one, where maybe the test server syncs to it (the test server has been set up very recently)
*** an alternative is to get a local dev setup working so VirtualBox can see my local files
!Afternoon session (from 14:45, with Amit, to 16:20; from 17:10 to 18:45)
* Working on the search results page
* Conference call to update Adam and Paul from 15:45 to 16:05)
** ''Can we validate email addresses on forms? We need to tell people that the email address the supply is the one that will be replied to''
** Amit keeping going when I stopped, until 16:45
* Tweaking the search results page to work with the JavaScript
!Afternoon session (from 12:15 to 13:30)
#API documentation
!Evening session (from 17:00 to 18:40)
#API documentation
** Chris pointed out there is no XML search and that individual records can be got
#Adding MMRF logo to wiki-data header
#Improving colour palette of [[wiki-data matcher]]
#Checking logic of [[wiki-data matcher]]
** committed some more code along the way to it working correctly
!Afternoon session (from 19:00 to 19:45)
#Chris sent new version of sql.py
#Testing a HTTP 200 request, comparing to these metrics from yesterday:
|point|yesterday (ms)|test 1 (ms)|test 2 (ms)|test 3 (ms)|test 4 (ms)|test 5 (ms)|test average (ms)|
|from bag_get to list_tiddlers|2360|532|558|644|796|610|628|
|list_tiddlers to response|234|191|202|202|197|207|200|
|total time to response|3891|2149|2814|2149|2226|2392|2346|
|% of response between bag_get and list_tiddlers|61%|25%|20%|30%|36%|26%|27%|
Looking at the reduction in time taken:
|point|% reduction since yesterday|
|from bag_get to list_tiddlers|73%|
|list_tiddlers to response|15%|
|total time to response|40%|
As an example of where things are taking time now, here is the log for test 3:
*2009-10-08 19:26:02,294 DEBUG starting "GET" request with uri "None", script_name "", path_info "/search" and query "q=bank&avid="
*2009-10-08 19:26:02,295 DEBUG overriding GET method to GET
*2009-10-08 19:26:03,110 DEBUG negotiating for accept and extensions ['text/html', 'application/xhtml+xml', 'application/xml', '*/*', '*/*']
*2009-10-08 19:26:03,111 DEBUG start whoosh_search
*2009-10-08 19:26:03,459 DEBUG query to parse: bank
*2009-10-08 19:26:03,554 DEBUG query parsed to Or([Term('legal_name', u'bank', boost=1.0), Term('previous_name(s)', u'bank', boost=1.0), Term('trades_as_name(s)', u'bank', boost=1.0)])
*2009-10-08 19:26:03,589 DEBUG end search
*2009-10-08 19:26:03,591 DEBUG ending whoosh_search
*2009-10-08 19:26:03,597 DEBUG in bag_get
*2009-10-08 19:26:04,241 DEBUG in list_tiddlers
*2009-10-08 19:26:04,443 INFO ::1 - GUEST [08/Oct/2009:19:26:04 ] "GET /search?q=bank&avid= HTTP/1.1" 200 - "-" "Mozilla/5.0 (Macintosh; U; PPC Mac OS X 10.4; en-US; rv:1.9.1.3) Gecko/20090824 Firefox/3.5.3"
!Morning session (from 8:45 to 9:45)
#Setting up testing environment - using qunit
#Writing tests for ChatAlerter library
#Moving JavaScript out of HTML file and setting up ChatAlerter library
#Writing some ChatAlert code to pass first test
!Afternoon session (from 16:00 to 18:20)
#Debrief of session this morning
** first cycle starts today
#Breaking pages down into building blocks and estimating relative sizes
** total: 53 points, 4 spikes
** spikes:
*** tag-river
*** AJAX loading from accordion links
*** footer (what is on it)
*** consultant gallery
*** contact content pane
#Started on "background" - 2 points, 17:35
** Vector map licensed under CC ShareAlike ByAttribution - comment for CSS:
{{{
Image licensed under Creative Commons Attribution-ShareAlike: http://creativecommons.org/licenses/by-sa/3.0/
Derived from: http://www.vecteezy.com/vf/482-World-Map-Vector-Graphic
}}}
** stopped at 18:15, estimate 75% of background done (2 units)
*** 40mins * 2 people
*** so that's 4/9 hr for 1 unit
*** so 53 units is 23.6 hrs; two people means ''1st estimate is 47hrs''
!Morning session (from 10:00 to 13:30)
* Plan for day with Josh
** sort out live server (J&J)
** bugs in filmreel (Jonny)
** footer - BPSF have sent over "creds" (which includes privacy policy) (Josh)
** context column (J&J)
** resizing of columns as you navigate (J&J)
** consultant gallery - FF bounce bug (Jonny)
** consultant gallery - "asset" box (J&J)
** consultant profiles not wired into AJAX-y (Josh)
** tags from transcripts (H)
** uploading film content (Erin, on Friday, with Josh)
* Looking at why the BPSF server isn't running WP
** problem with not all files uploaded - tried a zip upload but realised we couldn't unzip
*** emailed Andrew Rutter for help (shell access or him unzipping)
** permalinks don't work - turns out we're on IIS so need to use [[Microsoft URL Rewrite Module|http://learn.iis.net/page.aspx/466/enabling-pretty-permalinks-in-wordpress/]]
** turns out that BPSF use IIS6, so we're just going to use non-pretty URL's and make the bookmarking work by frag ID's e.g. /#about
* filmreel bugs
** clicking a tag should close the player - ready for testing
** when the filmreel is moving, no clicks should do anything - ready for testing
*** disabled any animation functions when filmreel is either:
**** in growToMax function
**** showing/hiding the player
* limit height of tag river so rest of nav is visible when page loads
** I think I need to choose a number of tags that fit into the correct number of lines as the tag cache, maintaining another cache of all the tags, to choose from
** I'm not sure how I'd ensure that the number of tags chosen fitted into three lines - I guess I could remove them one by one until I have the correct number of lines
** Turns out so long as I limit the height of the river correctly, it doesn't matter that the pending set of lines is too big, as long as the opacityStep is set correctly
* Put the fading tagriver effect back into the build - I had forgotten to include jquery.color.js
* Josh made the context column static version
!Evening session (from 18:45 to 20:25)
* Counting taster records for Adam
* Going through bug list
** Putting into Lighthouse
* Working on bugs
* Chris on emptying wiki-data database:
{{{
The process when doing things on the production server is:
in the mysql shell:
truncate table avox;
# that deletes all the records while preserving table structure
then in the command line shell to the normal import of the wiki_data.txt file (which will take about 5 minutes)
}}}
!Evening session (from 17:00 to 18:35; 18:55 to 20:20)
* Checking what's to do
** api - DONE
** make links have dotted underlines and a different colour - DONE
** about - DONE
** partners - DONE
** make table headers have more space in between them - DONE
** making logo in header link to index page - DONE
** index page - HALF-DONE (still fiddling with sticky footer)
** hiding initial table size on results page
** underlining links on results table
** suggest new
** company record page
!Afternoon session (from 12:30 to 13:15; 13:25 to 15:00; 15:15 to 16:45)
* Discussing tickets with Chris
** breaking down #152
* Discussing what it would take to let Avox update their own static content - you can put part of a template into a tiddler and then pull it out dynamically by interpreting the URL. We'll not do this this cycle because the deadline is Wednesday.
* Dealing with tickets
* Talking about account expiration
** a usersign will be returned as GUEST with a property "expired_user"
* Talking about upgrading tier 1 to tier 2 and registering tier 1 vs. registering tier 2
<<readLog>>
!Morning session (from 8:20 to 9:20; 11:25 to 11:50)
#Fixing [[wiki-data matcher]] logic
** there's something weird going on with the fuzzy searching
*** it's fine, just needs better explanation of what is searched
#Improving style of results table
** made results more readable and easier on the eye
#checking operational of {{{mergeUnique}}}
** made a little tweak, looks good
#spreading word about [[wiki-data matcher]]
** started conversation with Ken
#had a few [[wiki-data apps]] email conversations
** arranging to meet Leigh from Talis to talk about publishing RDFa/RDF - I think this is a good route to other people using the data
** let Andrew Dancy know
!Afternoon session (from 12:20 to 13:45; 16:40 to 17:20; 17:30 to 18:00)
#Going through wiki-data tickets
** fixed two
#Had style discussion with Nick
#Responding to Amit's latest designs
#Fixing bugs
** rattled through all but two
# Presentation run-through
!Evening session (from 18:45 to 19:15)
#Setting up https://wiki-data.fogbugz.com
Tracking some demos with bigger companies:
[[Wolf Frameworks]] - have had demo
[[Caspio Bridge]] - am talking to Mark Pedro, have trial, setting up demo
TrackVia - talking to Colin Casillas and Matt Strenz, discussing problem, had demo Thursday afternoon
[[Skelta]] - talking to Ratheesh Kumar, had demo
SnapLogic - talking to Vince Ko and Oliver Plattner, set up demo for Friday afternoon
BlankSlate - talking to Kael Goodman, discussing and setting up demo - Ning Zhou has set me up with a dev account
[[Zoho Creator]] - talking to Mack K Murthy, had demo
[[Rollbase]] - have demo, talking on their getsatisfaction forum
[[Iceberg]] - speaking to Wayne Byrne, arranging demo
[[LongJump]] - on trial, have spoken to Preeti Ahlawat, demo being set up in next couple of days
WorkXpress - speaking to Treff LaPlante, organising demo for Friday afternoon
This [[blog post|http://www.saasblogs.com/2010/07/12/todays-paas-offerings-pragmatic-or-unrealistic/#comment-117120]] compared PaaS companies to the WYSIWYG code editors (FrontPage/Homesite) and observed that we've ended up with code editors that are very simple, but make use of libraries and frameworks to help with common things. So I commented about that, and wondered what the PaaS versions of jQuery and Ruby on Rails would be like.
There's definitely a split of PaaS companies - some do their front-end by using JavaScript widgets that communicate through an API; some provide hard-wired portals that you can't customise; some let you access the template files (jsp, asp, whatever).
wyaworks
--------
import spreadsheet data or create new structure
use forms to edit and create data
permissions for access and edit
triggers: email notifications when creation and edit happens
shcheduler:
- notifications
- FTP data to a place
apps can be versioned
export to XML feed
export app to Java app (war) and MySQL db script
QuickBase - from $299/month
---------
data in and exposed from spreadsheets
notifications with logic-based criteria
TrackVia
--------
Data in from spreadsheets
Custom views on data
Real-time notifications with criteria
Scheduled data distribution
Permissioned access
De-dupe records
Email data in
Connectors - TrackVia will build custom connectors to different systems, such as QuickBooks
Can monitor any email address to collect data
Can create form response pages that look correct just by looking at the HTML of the form page, which is hosted on your site
Email campaigns straight from db
HTTP API
Zoho Creator - from Free to $175/month
-----------------
Form-driven database apps
Notifications
Scheduled actions, including GET/POST to URL
Custom actions - use the Deluge language to make add behaviours; uses a hand-holding script editor as well as letting you type what you like; you can GET/POST data and deal with the response
Ragic
--------
Form-driven Power database, that seems to be pretty much it
WaveMaker
----------------
An IDE that creates Java apps; uses a web browser.
BungeeConnect
----------------------
Web-based IDE. Looks like VisualStudio or Eclipse. It doesn't look simple to me... Uses WSDL.
ProcessMaker
-------------------
Open Source. Set up workflows, interaction done via forms.
Microsoft Dynamics
--------------------------
I can't get through the corporate chaff on the website to find out what this is.
Skelta
--------
Their demos are hidden behind registration pages.
AppPad
----------
HTML+JS apps, with a datastore API and integration with Yahoo! Pipes (which you can use for any other data feed)
Force.com
--------------
Event-driven, form-driven, based on Salesforce. Has a custom language used for writing scripts. Having tried a couple of the apps, they're a bit dodge. This might be great, though, I just can't really tell.
Google Scripts
--------------------
JavaScript-written apps for moving data around Google Apps. You can set triggers on events and times. Everything is done from the perspective of the person with the Google Account.
Caspio Bridge
-------------------
Form-based apps, with refreshingly flexible and customisable public-webpage creation.
DabbleDB
-------------
Form-based entry for database apps; interesting views of the data, picking up on location to do maps and dates to do calendars.
Rollbase - $49-$1,225/month
------------
Form-based database apps, with notifications, manual workflows and event-based triggers. Triggers let you send a RESTful request - what's an Integration Link? They also do ISV and reseller deals. I've applied for a trial and sent a detailed comment about what I want to do.
SnapLogic
--------------
Claims to wire together various online services. They have a marketplace model where anyone can sell a "Snap". I'm in a 2-day trial, but I don't get it yet.
Tarpipe
----------
Pretty handy for sending data around in response to HTTP calls or emails, but not very much more than that.
Boomi
---------
Hard to tell if it's useful. Could apply for a demo but haven't. Their Blueprint product looks like a nice way of describing processes visually, with documentation created automatically.
Informatica Cloud
-----------------------
Really have no idea what it can do, but it has a "block" marketplace and I don't recognise any of the services.
Hubspan
------------
Has a good example description of eCommerce integration. I've written asking about whether they'd be a good fit.
IBM Cast Iron
------------------
Another massive corp thing. Don't know how it does what it does.
ElasticApps - Faulkner Technologies
------------------------------------------------
"Early on, we held to an idea that most business processes and procedures are easily defined as a series of questions, answers, events and actions, the results of which can lead to specific outcomes, and which can be reported on in a variety of ways." Their demo is not understandable, so I've @'ed them on Twitter to ask for more info.
BlankSlate
--------------
Provides a set of APIs that are supposed to get an online business off the ground very quickly. I've applied for a beta account.
WorkXpress, from $15/month
----------------
Have a demo. Taking its sweet time to get the first app initialized. The editor is slow. This is not very usable.
Integrates with Google Maps, Amazon simple payments, and any WSDL web service.
Uses a sensible "blocks" structure for describing a business - concepts, fields, relationships, pages, actions. Reminds me of the ElasticApps thing about being able to describe a business as questions, answers, events and actions.
LongJump
-------------
Hard to know what they do, but their name keeps coming up. I have registered for the demo.
If your business does have an API, it could help to use a service that manages people's interactions with them.
http://mashery.com
Helping businesses stay on top of their administration without hassle.
| Name | Free? | Tried it? |
| InvoiceMachine | Yes; paid accounts from $12/month | No |
!What's the minimal file footprint I need to make an AIR app run on my desktop?
#Download Air from here: http://get.adobe.com/air/
#Download the Adobe AIR SDK from: http://www.adobe.com/products/air/tools/sdk/
** or use the Aptana / Dreamweaver plugins - bah
** There is no install script with the SDK - you have to copy the files to a folder and add that folder to your PATH, which for me meant adding this line to {{{.bash_profile}}} in my home directory:
*** {{{export PATH=/Applications/AIRSDK/bin:$PATH}}}
#The minimum files you need to create an application are:
** A project folder
** A HTML file
** A XML descriptor
** You can then use the Air Debug Launcher (ADL) to test the file without installing it and the Air Developer Tool (ADT) to package it
### What does the ADT do?
**** It helps you create a certificate for your application, to show it hasn't been changed since compilation and/or that you are a trusted source (all apps must be signed prior to installation)
**** It creates the correct structure for the AIR archive
** See [[this Adobe tutorial|http://help.adobe.com/en_US/AIR/1.5/devappshtml/WS5b3ccc516d4fbf351e63e3d118666ade46-7ecc.html]] for more information and a step-by-step guide
!! What does the smallest HTML file look like?
This and the the XML descriptor taken (and possibly modified) from the tutorial linked to above.
{{{
HelloWorld.html:
<html>
<head>
<title>Hello World</title>
</head>
<body>
<h1>Hello World</h1>
</body>
</html>
}}}
!! What does the smallest XML descriptor look like?
{{{
HelloWorld-app.xml:
<?xml version="1.0" encoding="UTF-8"?>
<application xmlns="http://ns.adobe.com/air/application/1.5">
<id>examples.html.HelloWorld</id>
<version>0.1</version>
<filename>HelloWorld</filename>
<initialWindow>
<content>HelloWorld.html</content>
<visible>true</visible>
<width>400</width>
<height>200</height>
</initialWindow>
</application>
}}}
Note that the {{{intialWindow}}}'s {{{visible}}}, {{{width}}} and {{{height}}} properties are not strictly necessary, it is sensible to add them (to prevent extra JavaScript to make the window visible and a sensible size).
!! Could you script the creation of the AIR application package?
Because the command to build an AIR application is quite long, but the same every time, it's tempting to write a script to do it. I think you'd want a folder with all the source-code in, with an optional sub-folder structure for assets; the signing certificate would sit outside of this, as would common AIR libraries (such as {{{AIRAliases.js}}}), as would the built package.
To launch an app in debug mode:
{{{
adl HelloWorld-app.xml
}}}
To build an app:
{{{
adt -package -storetype pkcs12 -keystore certs/sampleCert.pfx -storepass samplePassword HelloWorld.air src/HelloWorld-app.xml -C src .
}}}
!What does it take for a link-click to trigger the install of an AIR app?
This is called "Seamless install"
#You have to include a .swf called "badge.swf" in a HTML page, which sets everything up for you (badge.swf comes with the SDK)
** Details on how to set up the parameters are on [[this Adobe tutorial|http://help.adobe.com/en_US/AIR/1.5/devappshtml/WS5b3ccc516d4fbf351e63e3d118666ade46-7e15.html]]
** There is an example with parameters set [[here|http://www.adobe.com/devnet/air/flex/quickstart/seamless_install.html]]
** It looks like if you try and run the example {{{default_badge.html}}} from a file:/// URI when developing, the runtime throws a security error, I think due to trying to access a {{{.cab}}} file on a remote system
*** This might be different if I was serving it up through a local web server
!How do I make notification windows pop up?
** AIR provides access to "Native Windows", one type of which is the chromeless "lightweight" type, which is what we want
** A good guide to creating notification windows is on [[this blog post|http://blog.kevinhoyt.org/?p=286]]
** Instructions for specifying options and setting position on this window are on [[this blog post|http://www.adobe.com/cfusion/communityengine/index.cfm?event=showdetails&productId=4&postId=8226]]
!! How do I make the original app run in the system tray?
* See [[this post|http://blog.kevinhoyt.org/?p=288]] about the system tray and dock icons
* Setting {{{<visible>false</visible>}}} in the application descriptor hides the window from view.
!How do I open a permanent listener?
* For information about the differences between dock icons on Mac and system tray icons on Windows, see [[this article|http://www.adobe.com/devnet/air/flash/quickstart/stopwatch_dock_system_tray.html]]
!Can I make an application that will run in a browser and unchanged in an AIR app?
Advogato.org - open source hosting and diary service, uses peer-reviewed certification, with a trust metric, to rate people as Apprentices, Journeyers or Masters.
My thoughts: it's cool for people within the community to be able to rate each other, but all you get for being trusted is that you can syndicate your own blog into your Advogato diary. This is quite cool (and [[Stack Overflow]] has a similar thing, where your blog link is not nofollowed when you reach 2000 points).
Overview of trust metric: http://www.advogato.org/trust-metric.html
Salon article (2000) about Advogato: http://archive.salon.com/tech/feature/2000/07/18/advogato/
A web dev company with experience on the BBC's Forge platform.
[[Paul Goodenough]] is the person I've spoken to over there, about the [[Radio 1]] project in Sep/Oct 2010.
I have been trying to develop my version of Agile Development practices, mainly based around User Stories, full-team involvement and short cycles.
Caveat: User Stories and this design process are useful for "big" features. When you find yourself with something small, you can do it without using a User Story and just link the recording of the change to the original idea.
Second caveat: I observe that with [[Geekmap]], we never actually built the interesting tag-and-search-focussed interface that Dan originally presented to me as the germ of the idea. With the best intentions, we used user stories to figure out the most basic goals of the people using Geekmap and built from there. Whilst I think that our methodology was good, the eventual outcome was that the site received a fair amount of development attention but ran out of steam, crucially without any compelling features. I think it would have made the project more likely to have gathered momentum, instead of losing it, if we had started by building a prototype from the original, visionary ideas. With hindsight, it seems like this would have been very useful in bringing people other than Dan up to the point where they believed in what they were building and shared the vision.
Expanded this point into a blog post here: http://jaybyjayfresh.com/2009/06/09/a-warning-about-agile-methodology-in-early-stage-development/
I think in my head I have slightly revised my approach for early-stage web technology development - start with a prototype built by people who understand the vision; don't use any of it for the development of the application!
So far:
#Someone gets the vision or we do some brainstorming - it's all limits off at this point.
#You turn your ideas into user stories and prioritise them.
#You create Acceptance Criteria, which we've done as a set of things the person achieves in order to complete the story - these inform the design of the site.
#Using these steps, you can decide what you need on the site to let someone go through them. This is a combination of drawing wireframes that show the interface elements and flow diagrams that show anything part of the journey not obvious from the wireframe.
#This is the point at which a development iteration can begin. The first thing to do is to estimate the size of the User Stories so you can figure out how much time they will take. The estimation can be done by [[Planning Poker]].
#The wireframes, flow diagrams and acceptance criteria can be used to create a series of tasks for each User Story in turn; for unknown problems, an investigation can be launched, which is termed a "Spike".
#Development cycles progress and you clear your tasks.
#The planning for the next cycle should happen during the last few days of a cycle or with the retrospective (see next point)
#At the end of a development cycle, you have a retrospective (ideally on the day after the end of the cycle), where you review the cycle that has just happened, answering four questions:
** What went well?
** What didn't go so well?
** What would we do differently next time?
** What still puzzles us?
#Where we are now: at the start of the fourth iteration; yet to try: velocity measurement
Open-sourced at https://github.com/jayfresh/AjaxReq/tree
Runs the End-user programming group at the Cambridge Computer Laboratory
Runs [[WeMakeWebsite|http://alwaysmaking.com]].
Runs Bracket Projects. Met through the Cube.
<<SimileTimeline AllTiddlersTimelineSpec>>
|timelineHeight:|1000|
|bubbleWidth:|350|
|bubbleHeight:|200|
|band0.width:|80%|
|band0.intervalUnit:|MONTH|
|band0.intervalPixels:|100|
|band0.date:|Jan 1 2010|
|band0.eventSourceType:|tiddlerFields|
|band1.width:|5%|
|band1.intervalUnit:|WEEK|
|band1.intervalPixels:|100*7/31|
|band1.eventSourceType:|tiddlerFields|
|band2.width:|15%|
|band2.intervalUnit:|MONTH|
|band2.intervalPixels:|25|
|band2.showEventText:|false|
|band2.eventSourceType:|tiddlerFields|
Graphic designer, friend of Fabio's. Learning web design. Working with Osmosoft; also did the wiki-data design.
!Plan
[[Attention tracker prototype]]
command-line supporting "switch" & "off" (which includes TiddlyWeb infrastructure to store data, and allows for unambiguous period calculation); then summary for project; then on mobile too; then command-line helpers.
!Needs to be done
Figure out which wants are most important by looking at the way people actually track time and see what problems they have doing so with the tools available. Have emailed: PJ, Susie, Matthew (said he doesn't track time anymore, I asked more about how he used to do it, said it was by block or timers), Alex O'Byrne (he's making an app too!), Tom Lewis. Alison told me she doesn't track time yet. PJ (we had a conversation) said forgetting to change timers is the hard bit for him. Tom does it at the end of the week by looking back through his diary (I've asked if anything is hard).
!Tracking attention is better than tracking time
You really should be giving your attention to things - I don't think we've been made to be great at multi-tasking. Computers are a distracting tool, it's not easy to give your attention to things. Just look at the numerous apps that block facebook and twitter except for certain periods during the day.
It's good to acknowledge you are "giving your attention" to things. If this is what you are doing, that is more productive than "spending your time" on things.
You can take a break from giving your attention to things. That's healthy. It leaves room for spontaneity and a mental break. When you're tracking your time, that's the only metric you have, so you feel compelled to track every minute. When you're tracking what you are giving your attention to, what you spend your time on is a sub-set of the entire period of attention.
!Wants
!!Most important
* I want to be able to switch to another project just by starting a period of another project, so I can switch attention in one move
* I want to be able to set an attention switch to some minutes earlier than "now", so I can handle the many times when I don't realise I've switched until five minutes in
* I want to be able to switch attention or switch off from my mobile or my computer so I can make these changes when I remember about it
* I want to be able to add an entire period earlier on in the day, so that I don't lose a period if I've forgotten to log it at the time
* I want to be able to see my recent periods for a particular project so I can see if there are gaps where I should have added a period
* I want to be able to see the notes from the log in my project's tracker
* I want to be able to get data about my periods out of the system so that they can be used in reports
!!Rest
Access
* I want to be able to pump a change into the system without having to read and analyse the state of the system
Modifying periods
* I want to be able to modify the stop time to an earlier time after stopping the timer so I can make the stop time correct if I forget to stop the timer
* I want to be able to modify the stop time to a later time after stopping the timer so I can make the stop time correct if it turns out I haven't really stopped
* I want to be able to modify the start time to a later time after starting the time so I can reflect a real start time if I was distracted
* I want to be able to throw away a period if it turns out I didn't really use it
* I want to be able to change the project a period is attached to if it turns out I used it for something else
Analysis
* I want overlaps not to occur, even if I modify a start time to overlap the previous stop time, or vice versa, so I don't end up with an incorrect total for timings (such as 24hrs 40mins in a day, or claiming more minutes than I've done)
* I want to be able to see all my recent periods so I can get an overview of where my attention has been going
Period notes
* I want to be able to add a note when starting or ending a period of time so that I don't have to open up my notebook as well as the timer just to record something simple
* I want to be able to edit the period note while going along, so I can keep the note correct
* I want to be able to edit the period note after stopping a period, so I can correct it
* I want to be able to link to tiddlers in my notebook from period notes just by remembering the tiddler name, so I can link to broader explanations of what I've been doing
Attaching periods to projects
* I want to be able to choose a project to attach a period to
* I want to be able to add new projects to my list of projects that I can track time for
Other
* I want to be able to attach a period of non-attention before starting a period, so I can take into account breaks between attention switches
* I want to be able to push a gap into a period to take into account a break
!Architecture
Started out thinking about using tiddler revisions to track changes in an event time, note or project, but the problem with this is that you need to know about the existing revisions to do it, which means you have to a GET step before any PUTing. I want to be able to interact with the system just by sending data, so the mobile experience can be as lightweight as possible. I think it's better to store every pump of data in a new tiddler and provide enough information for the analysis to be done.
Technically, we have two bits of time-tracking data on a tiddler - the created and modified times. This means we could use the created time as the indicator of when the tiddler was created and the modified time to store the information about the timer. However, this would decouple the modified time from the created time, and you would easily end up with a modified time set earlier than the created time, which doesn't make sense. It also requires deliberate overriding of the handling of modified time.
An alternative is to have a field tracking "action time", which can be set to any time you like, and is what the analysis step looks at to figure out things like start and stop times. In fact, as we're only tracking start and stop times, you could name the field "start" or "stop", give it a value of the action time and do away with any need for a "type" field. That assumes that you don't need "type" to hold any value other than "start" or "stop".
Another option is to just use the title of the tiddler to designate the action time or created time - this is what Chris Dent does - he uses the epoch time.
Had been thinking that when a tiddler is successfully saved, it should return a 204 and the body of the tiddler it's replacing as the active tiddler. But that would require server-side logic, and I think I can get away without any if I don't do that. This means I'll need to request the other tiddlers to figure out what's being replaced, which means I can't feed back status with a single request. UNLESS the initial request to load the app collects the latest tiddler info. Hmm.
Breaks in an attention span and before an attention span are taken care of by issuing start or stop tiddlers with a "break" field.
Analysis:
* a period: from the first start tiddler after a stop tiddler to the next start tiddler after a stop tiddler. This leaves the possibility of tiddlers existing after a stop tiddler attached to the period that has been stopped. A period represents attention being focussed on project work
* action times: choose the most recent tiddler with a start or stop field to figure out that action time
* notes: aggregate all tiddler bodies in a period
* breaks: finding a tiddler with a "break" field indicates a break within the period that tiddler belongs to
* switching attention: if a start tiddler is found within a period that has a "project" field that is different to the previous start tiddler, there has been an attention switch
!Interface (reverse chronological)
* Alongside drawing out all the separate bits 'n' pieces I've thought necessary for the inteface (project chooser, time offset chooser, current status indicator, note adder, analysis), I've been thinking about what you could achieve just by using a command-line, which has helpers as people type stuff in
** you'd need commands: switch, off, note, change
** before you type, you could ghost the commands in the command line
** when typing switch or change, you get a pop-up project selector - this shows recent projects first; when you start typing, it suggests projects by showing matching projects, ordered reverse-chronologically or alphabetically
** after choosing a project in a switch command, you get a slider to help teach the time syntax - you can slide back into the past and the command auto-completes e.g. "switch DogsProject -10m"
*** this presupposes people don't want to choose specific dates - could support "switch DogsProject 15:00"
** after choosing a project in a change command, you get an analysis interface showing "today", "yesterday" and "day before" and their associated blocks of attention on this project; you can add new blocks by dragging within the day - the interior of the dragged area fills up with the amount of time selected e.g. "10mins"
*** don't know if the amount of time will always fit within the dragged-out space
** when typing "note", you get a textarea opening underneath, which you can use for multi-line notes. Alternatively, you can use the single line
*** if you type into the textarea, you need something to click on to submit the note
* There's a need to support adding periods when the actual time is long past. Originally, I was thinking of some enhancement to the draggable time-choosing bar, but on later reflection I realised that the time at which you realise you've missed a slot is when you are looking back at the analysis. So, I'm thinking that the analysis is where you should put the ability to add arbitrary periods. It's possible it might be useful to just add a period of time that is not attached to a particular point in time e.g. "2 hrs 20 mins"
* This is a drawing showing how you'd handle non-now time periods, and shows the up-to-date interface idea - http://docs.google.com/drawings/pub?id=1soDw35x7UCbVaf-l9v-oRlwMDcuZ1uap5W-KsZKsCmQ&w=960&h=720
* Have merged the "switch off" with other switches, so you're only ever pushing switches or notes
* Have pretty much thrown the previously mentioned drawing out. I don't think it's simple enough. Having been thinking about the most important thing about the interface, for me it was always having a non-confusing way to say you've switched off using the mobile client (say after you've left the building and forgot to switch off), and my constraint with that is that there should no requests to the server to find out some sort of status. Hence, I'm back on the pumping idea - "pump a switch!" being the current, ridiculous nomenclature. In fact, you can pump any of three things - a switch (which also counts as "on"), a note, a "switch off".
*Came up with this drawing - http://docs.google.com/drawings/pub?id=1cmze_jsfCKORbMI_vaEGIdLyDI_rt6nwfG1Bqbl_G98&w=869&h=1073
*Thinking about madlib style for the options to move start times or have a break; want an overall on/off switch for the attention light. I do like the light idea - on/off - I'm just not sure what to use as a widget for choosing new projects. Maybe the attention light is enough and I could call the system something to do with lamplight... which is nice and calm, but doesn't give the same as the idea of searchlights panning about to focus on something. My conflict is that I can't think of an interface that nicely represents a searchlight
*The thing about a searchlight idea is that it's proving hard to find an interface that fits into the idea of moving a searchlight around. For one thing, making attention switches at times other than "now" (probably earlier) doesn't really make sense if you're imagining moving a light around. Also, the concept of revolution between projects (well, I guess that is a lighthouse) doesn't seem right when you have an arbitrary number of projects - it would be hard to fit them all neatly into 360 degrees.
*We've now got the idea of tracking periods of time when your attention is "on". The attention can move about between projects, and take breaks, but the overarching concept is that we're acknowledging that the important thing to track is your attention, and that the time on each project is a sub-set of that
*Attention tracking: thinking more about your attention sweeping between different projects, like a searchlight
*Pump: thinking about etherpad-style revision timeline, like a snake/pump taking data in at different times; a push-button to send data rather than a flip-switch, to indicate that we're tracking point events rather than continuous periods
*Started out thinking about flip-switch and drop-down project list
!Log
16/7/10 15:40 - 15:50, 16:10 prototype
9/7/10 - 13:45 - 14:45; 15:00 - 15:15 interface design
8/7/10 - 18:00 - 18:50 some design
7/7/10 - forgot to log, around 2hrs, designing
6/7/10 11:00 - 12:45 interface and wants design; 14:00 - 14:20 looking at CDent's [[system|http://github.com/cdent/tiddlytoys/tree/master/timer]]
I created this pie chart from a month's worth of spending according to my HSBC online statement (so cash is not broken down), from 28th March to 28th April.
<html><img src="http://spreadsheets.google.com/pub?key=rHlGxqC2NBxOKukOSWmjeoA&oid=4&output=image" /></html>
Works at Brewer St. NCP. In charge of property, our main contact for [[NCP Alternative Use]], after [[Ben Heath]] put us in touch.
Description: Supporting a TiddlyWiki-based project with the [[Anna Freud Centre|http://www.annafreudcentre.org/]]. Creating a collaborative practice manual. Looking forward to talking more about this.
Rate: Standard, with a mix of pro bono
Invoices: [[Invoice 0005]]
As of June 2009, looking to start a more formal arrangement with the Anna Freud Centre, where I am paid to work on the Integrated Multimodal Practice (IMP) project.
!Log
*[[7th August 2009 Anna Freud Centre]]
*26/6/09 - Fixing edit/save problems where tiddlers could not be edited once they'd been created - 2 hours
** Turned out to be a discrepancy between the server-code and the client-code in the TiddlyWebAdaptor, requiring a 'twanager update' to fix
*[[16th September 2009 Anna Freud Centre]] - 5hrs 35mins
*[[Invoice 0005]]
*[[29th September 2009 Anna Freud Centre]]
*[[23rd October 2009 Anna Freud Centre]] - 5hrs 15mins
*[[Invoice 0007]]
*[[10th November Anna Freud Centre]]
*[[31st January 2010 Anna Freud Centre]]
*[[12th February 2010 Anna Freud Centre]]
*[[Invoice 0013]]
*[[5th July 2010 Anna Freud Centre]]
Found [[this plugin|http://trac.tiddlywiki.org/browser/Trunk/association/plugins/ServerSideToolbarCommands.js]] as a potential solution to tiddler toolbars not updating after login
No server-side logic.
Use tiddlers tagged with "project" as the project list
Use tiddlers tagged with "track" as the attention tracking tiddlers
Use epoch time as the tiddler title
Save note to tiddler body
Save project as project field
Create a command line that interprets "switch" and "off". Switch is followed by a project title, then a timing modifier.
<<attentionTracker>>
<<attentionTracker analysis>>
/*{{{*/
/*
v4
dependencies:
AttnViewTemplate
- TaggedTemplateTweak
AttnTab (optional), used in SideBarTabs
usage: <<attentionTracker [analysis|timeline [project]]>>
no options: creates the command line
analysis: shows the breakdown of how much time has been spent on projects
timeline [project]: shows a timeline of attn tiddlers broken down by day; adding [project] limits to that project
operation of command line:
if there is text in the cmd box, the first entry is the project name, or "off"; the next is the time modification.
time mod's:
supported:
"-10m" - a minus then any number of digits, plus an 'm' (or nothing, 'mins', 'minutes')
"10m" - as above, without the minus
want to support:
"5pm", "17:36", "yesterday 5pm", "two days ago 17:30" - custom time
limitations:
it's become tricky to know where to type notes, since you can have many tiddlers per day (the attn tiddlers) - maybe add link to the timeline, so it links to the project?
projects names can't have spaces (space is interpreted as moving on to time modification)
BUGS:
pumping doesn't trigger save changes, despite use of saveChanges() - except sometimes...
- pumping definitely doesn't trigger a tiddler to be saved back to the server if it's on TSpace
first part of the analysis doesn't use 'active' tiddlers (and it's not ideal anyway)
- when a notes popup opens that is big enough to hit the bottom of the page and so scroll the page, moving the mouse cursor off the sparkline, the popup disappears, which causes the page to shrink again and the mouse cursor to come back on top of the sparkline. This causes a bouncing behaviour.
from: "An attention tracking system" on http://jnthnlstr.tiddlyspot.com
!Wants
!!Most important
* I want to be able to switch to another project just by starting a period of another project, so I can switch attention in one move - done
* I want to be able to set an attention switch to some minutes earlier than "now", so I can handle the many times when I don't realise I've switched until five minutes in - done
* I want to be able to add an entire period earlier on in the day, so that I don't lose a period if I've forgotten to log it at the time - done
** note - you are restricted to the current day at the moment
* I want to be able to see my recent periods for a particular project so I can see if there are gaps where I should have added a period
* I want to be able to switch attention or switch off from my mobile or my computer so I can make these changes when I remember about it
** thoughts about the mobile-ness: assuming you can receive SMS to an app via HTTP, you could use a JS script to POST a new tiddler to a TiddlyWeb/Space, or just store it in a queue/RSS feed ready for collection by an offline client. You would have common JS code with this plugin in the interpretation of the cmd line, but not with the TW-specific API e.g. store.saveTiddler.
* I want to be able to see the notes from the log in my project's tracker
* I want to be able to get data about my periods out of the system so that they can be used in reports
style tweak suggestions:
initial text in input box lighter grey
style the status line
operation suggestions:
timeline should link to project page also? not sure whether it's best to keep notes on project page or attn tiddler yet
show the recent projects under the input box, like a tag cloud, clicking on one puts it in the input box
disable submit button after submitting until it's ready for new submit
once you've completed an action, add the click-to-clear behaviour back to the input - it's handy to see what you pumped most recently
ideas:
the project tiddlers should list the attn tiddlers that have them as their project field (and the links should be made readable)
use Attn as a IM-style status, which you can see for other people just by looking at their latest log
*/
(function() {
var $ = jQuery;
config.macros.attentionTracker = {};
var $tracker; // JRL: used to store reference to tracker. means we only support one tracker per document - do I need/want to support more?
var plugin = config.macros.attentionTracker;
var attnTag = "attn";
var boxHTML = '<div class="attentionTrackerContainer">' +
'<div class="attentionTrackerCommandLine">' +
'<form>' +
'<label for="cmd">switch</label><input type="text" name="cmd" id="cmd" size="20" value="project" autocomplete="off" />' +
'<input type="submit" value="attention!" />' +
'</form>' +
'</div>' +
'<div class="attentionTrackerStatus"></div>' +
'</div>';
plugin.handler = function(place,macroName,params,wikifier,paramString,tiddler) {
var p = params[0];
switch(p) {
case "analysis":
plugin.updateAnalysis(place);
break;
case "timeline":
plugin.listAttn(place,params[1]); // JRL: lists all if no project provided
break;
case "pretty": // JRL: for use in ViewTemplates
var prettyTitle = plugin.prettifyTitle(tiddler.title,params[1]);
wikify(prettyTitle, place);
break;
default:
plugin.renderInterface(place);
}
};
plugin.renderInterface = function(place) {
$tracker = $(place).html(boxHTML).find('.attentionTrackerContainer');
var $cmdLine = $tracker.find('input[type=text]');
$cmdLine.one('focus', function() {
$cmdLine.val(''); // JRL: could also use the placeholder attribute for browsers that support it
$cmdLine.keyup(function(e) {
e.preventDefault();
if(e.keyCode===13) { // "return"
return false;
}
var txt = $cmdLine.val();
var cmd = plugin.analyseCmd(txt);
plugin.updateStatus(cmd);
if(cmd.error) {
return;
}
plugin.currentCmd = cmd;
});
});
$tracker.submit(function(e) {
e.preventDefault();
var cmd = plugin.currentCmd;
if(!cmd || cmd.error) { // cmd does not exist if submit is hit before typing anything
plugin.updateStatus(cmd || { error: "nothing to do!"});
return false;
}
var actionTime;
if(cmd.timeMod) {
if(typeof cmd.timeMod.timeMod==="number") {
actionTime = Date.now() + (cmd.timeMod ? cmd.timeMod.timeMod : 0);
} else { // timeMod assumed as a valid date string
actionTime = new Date(cmd.timeMod.timeMod).getTime();
}
} else {
actionTime = new Date().getTime();
}
plugin.toAttention(cmd.cmd, cmd.proj, actionTime);
});
};
plugin.analyseCmd = function(txt) {
var out = {};
if(!txt) {
out.error = 'please type a project or "off"';
}
var bits = txt.toLowerCase().split(' ');
var proj = bits[0];
if(proj==="off") {
out.cmd = "off";
} else {
out.cmd = "switch";
out.proj = proj;
}
var time = bits.slice(1).join(' ');
if(time) {
var timeMod = plugin.analyseTimeMod(time);
if(!timeMod) {
out.error = "bad time modification - "+time;
} else {
out.timeMod = timeMod;
}
}
return out;
};
plugin.analyseTimeMod = function(txt) {
// analyse strings like "-10m", "10mins", "10 mins", "-10", "5pm", "17:36"
// TO-DO: "yesterday 5pm", "two days ago 17:30" - custom time
var out;
var timeMatchRegex = /(\d{1,2})([ap]m)|(\d{1,2}):(\d{1,2})|(-)?(\d+) ?(?:m|mins|minutes|min)?/gi; // JRL: only expecting one match
var matches = timeMatchRegex.exec(txt),
d,
hour,
minutes,
direction,
label;
while(matches) {
if(matches[1]) {
d = new Date();
hour = parseInt(matches[1],10);
if(matches[2]==="pm") {
hour += 12;
}
d.setHours(hour);
d.setMinutes(0);
d.setSeconds(0);
d.setMilliseconds(0);
out = {
timeMod: d.toLocaleString(),
timeModString: "at "+matches[0]+" today"
};
} else if(matches[3]) {
d = new Date();
hour = parseInt(matches[3],10);
minutes = parseInt(matches[4],10);
d.setHours(hour);
d.setMinutes(minutes);
d.setSeconds(0);
d.setMilliseconds(0);
out = {
timeMod: d.toLocaleString(),
timeModString: "at "+matches[3]+":"+matches[4]+" today"
};
} else if(matches[6]) {
direction = matches[5] ? -1 : 1,
minutes = matches[6];
label = minutes==="1" ? "minute" : "minutes";
out = {
timeMod: direction*minutes*60*1000,
timeModString: direction < 0 ? minutes+" "+label+" ago" : "in "+minutes+" "+label
};
}
matches = timeMatchRegex.exec(txt);
}
return out;
};
plugin.toAttention = function(cmd, proj, actionTime, note) {
if(!actionTime) {
actionTime = new Date.now(); // this is epoch time
}
var title = actionTime.toString();
var fields = {};
if(cmd==="switch") {
fields.project = proj;
} // we don't need to do anything special for "off" tiddlers as they are obvious through their lack of a "project" field
store.saveTiddler(
title,
title,
null,
config.options.txtUserName,
null,
[attnTag, "excludeLists"],
fields
);
plugin.updateStatus("pumped!");
saveChanges();
};
plugin.updateStatus = function(cmd) {
/* takes string or object inputs */
var txt;
if (typeof cmd==="string") {
txt = cmd;
} else {
if(cmd.error) {
txt = "error: "+cmd.error;
} else if(cmd.cmd==="off") {
txt = "switch off - you deserve it!";
} else {
txt = "command: "+cmd.cmd+" to project "+cmd.proj;
if(cmd.timeMod) {
txt += " "+cmd.timeMod.timeModString;
}
}
}
$tracker.find('.attentionTrackerStatus').html(txt);
};
plugin.updateAnalysis = function(elem) {
var tiddlers = store.getTaggedTiddlers(attnTag, "title"); // sorts to earliest first, as title is epoch time
var periods = [];
var period = [];
var activeProject, prevProject, prevTid;
$.each(tiddlers, function(i, tid) {
// as you're going through, maintain the active project -
// if you find a tiddler with the same project, compare the created dates -
// keep the most-recently created tiddler, make the rest inactive (don't lose them in case we want them for something else)
period.push(tid);
activeProject = tid.fields.project;
if(activeProject) {
if(prevProject && activeProject===prevProject) {
prevTid = period[period.length-2]; // to bypass this tiddler
if(tid.created.toString() > prevTid.created.toString()) {
prevTid.fields.inactive = true;
} else {
tid.fields.inactive = true;
}
} else {
prevProject = activeProject;
}
} else {
if(!tiddlers[i+1] || tiddlers[i+1].fields.project) { // if this is the last tiddler or the next tiddler isn't another "off" tiddler, end the period
periods.push(period);
period = [];
prevProject = null;
}
}
});
// check for unfinished last period
if(period.length) {
periods.push(period);
}
// display analysis - using <pre> for the moment
var out = "<pre>Found "+periods.length+" periods from a total of "+tiddlers.length+" tracking tiddlers\n",
activeTids,
duration,
proj,
periodProjs,
projects = {};
// JRL TO-DO: this entire section needs changing so it's neater
$.each(periods, function(i, period) { // JRL: TO-DO: should I be using the activeTids for this?
out += "Period "+i+": from "+plugin.prettifyTitle(period[0].title)+" to "+plugin.prettifyTitle(period[period.length-1].title)+" has "+period.length+" entries;"; // JRL: TO-DO change "entries" to count the switches
out += " on projects: ";
periodProjs = [];
activeTids = $.grep(period, function(tid, n) {
return tid.fields.inactive;
}, true);
$.each(activeTids, function(j, tid) {
proj = tid.fields.project;
if(proj) {
if(!projects[proj]) {
projects[proj] = [];
}
if(activeTids[j+1]) { // check the period isn't still open
duration = (parseInt(activeTids[j+1].title,10) - parseInt(tid.title,10)) / (1000);
projects[proj].push(duration);
}
periodProjs.pushUnique(proj);
}
});
out += periodProjs.join(", ");
if(period[period.length-1].fields.project) {
out += " (unfinished)";
}
out += "\n";
$.each(projects, function(proj, durations) {
durations.totalDuration = 0;
$.each(durations, function(k, duration) {
durations.totalDuration += duration;
});
});
});
out += "</pre>";
out += "<pre>Breakdown of project totals:\n";
var hours, minutes, seconds, timing;
$.each(projects, function(proj, durations) {
timing = [];
duration = Math.ceil(durations.totalDuration);
hours = Math.floor(duration / 3600);
duration %= 3600;
minutes = Math.floor(duration / 60);
seconds %= 60;
if(hours) {
timing.push(hours+" hours");
}
if(minutes) {
timing.push(minutes+" minutes");
}
if(seconds) {
timing.push(seconds+" seconds");
}
out += proj+": "+timing.join(", ")+"\n";
});
out += "</pre>";
$(elem).append(out);
};
plugin.listAttn = function(place, proj) {
var tiddlers = store.getTaggedTiddlers(attnTag,"title");
if(proj) {
tiddlers = $.grep(tiddlers, function(t) {
return t.fields.project===proj;
});
}
/* modelled on timeline macro */
var lastDay = "",
d = new Date(),
theDay;
for(var t = tiddlers.length - 1, noteLength, maxNote = 0; t >= 0; t--) {
tiddler = tiddlers[t];
noteLength = tiddler.text.length;
if (noteLength>maxNote) {
maxNote = noteLength;
}
}
for(var t = tiddlers.length - 1, tiddler, ul, li; t >= 0; t--) {
tiddler = tiddlers[t];
d.setTime(parseInt(tiddler.title,10));
theDay = d.convertToLocalYYYYMMDDHHMM().substr(0, 8);
if (theDay != lastDay) {
ul = document.createElement("ul");
addClass(ul, "timeline");
place.appendChild(ul);
createTiddlyElement(ul, "li", null, "listTitle", d.formatString('DDD DDth MMM YYYY'));
lastDay = theDay;
}
/*createTiddlyText(
createTiddlyElement(ul, "li", null, "listLink").appendChild(createTiddlyLink(place, tiddler.title, false)),
(tiddler.fields.project||"off")+" "+plugin.prettifyTitle(tiddler.title,"0hh:0mm:0ss")+wikifyStatic("<<sparkline 1 0 2 0 3>>")
);*/
var manualMax = 3;
var noteBin = Math.ceil((tiddler.text.length / maxNote) * manualMax); // 1, 2, or 3
var sparkLineTicks = [];
for(var i=1; i<=3; i++) {
if(i<=noteBin) {
sparkLineTicks.push(i);
} else {
sparkLineTicks.push(0);
}
sparkLineTicks.push(0);
}
sparkLineTicks.pop();
var li = createTiddlyElement(ul, "li", null, "listLink");
var link = createTiddlyLink(li, tiddler.title, false);
link.innerHTML = (tiddler.fields.project||"off")+" "+plugin.prettifyTitle(tiddler.title,"0hh:0mm:0ss");
plugin.generateSparkline(li, sparkLineTicks, tiddler.text, manualMax);
}
};
plugin.prettifyTitle = function(title, format) {
title = parseInt(title,10);
var d = new Date(title);
if(format) {
return d.formatString(format);
} else {
return d.toLocaleString();
}
};
plugin.generateSparkline = function(elem, series, title, manualMax) {
// Lovingly nicked from SparklinePlugin by FND, originally core TiddlyWiki
if(!series) {
return;
}
var tickHeight,
max = Math.max.apply(Math,series),
min = Math.min.apply(Math,series);
if(!max>0) {
return;
}
if(manualMax) {
max = manualMax;
}
var box = createTiddlyElement(elem,"span",null,"sparkline",String.fromCharCode(160));
// box.title = title || series.join(","); JRL: replacing with Popup
var w = box.offsetWidth;
var h = box.offsetHeight;
box.style.paddingRight = (series.length * 2 - w) + "px";
box.style.position = "relative";
for(var d=0; d<series.length; d++) {
var tick = document.createElement("img");
tick.border = 0;
tick.className = "sparktick";
tick.style.position = "absolute";
tick.src = "data:image/gif,GIF89a%01%00%01%00%91%FF%00%FF%FF%FF%00%00%00%C0%C0%C0%00%00%00!%F9%04%01%00%00%02%00%2C%00%00%00%00%01%00%01%00%40%02%02T%01%00%3B";
tick.style.left = d*2 + "px";
tick.style.width = "2px";
tickHeight = Math.floor(((series[d] - min)/(max-min)) * h);
tick.style.top = (h-tickHeight) + "px";
tick.style.height = tickHeight + "px";
box.appendChild(tick);
}
$(box).hover(function(e) {
var popup = Popup.create(box);
popup.innerHTML = wikifyStatic(title);
Popup.show();
}, function(e) {
Popup.onDocumentClick(e);
});
};
})();
/*}}}*/
! The problem
You want to know what people are saying about you on the web and how successful are your efforts to affect that.
! The approach
!! Mashing up
This problem can be described as: finding the chatter, aggregating the chatter, establishing the attitude of the chatter, analysing the chatter and archiving the chatter. It's been suggested that each piece of this can be handled with an existing web service.
Candidates so far:
* Finding the chatter: RSS feeds from search such as [[YouTube|http://www.youtube.com]], [[Google blog search|http://blogsearch.google.com]], [[Twitter|http://search.twitter.com]], [[Omgili forum search|http://www.omgili.com]], [[Backtype blog comments|http://backtype.com]], [[Technorati|http://feeds.technorati.com/search]], [[FindArticles|http://findarticles.com]]
* Aggregating the chatter: [[Yahoo! Pipes|http://pipes.yahoo.com]] (also for uniquifying the chatter)
* Establishing the attitude of the chatter: still looking. Matthias (from LShift) has a friend who did a PhD in sentiment analysis - have emailed to ask him for more information.
* Analysing the chatter: [[Google Spreadsheets|http://docs.google.com]] has been working quite well so far; has the nice feature of being able to POST information via a form (I imagine this being useful when we automate things)
* Archiving the chatter: not sure yet, not feeling much to pull me between online storage and online databases
!! Open source
It seems that this particular problem is generic to a wide range of companies, institutions and individuals. It's been suggested that the mechanism to pull together the various tools that do the pieces of the problem is made public and any code published under an open source license. This would enable collaboration with others interested in this problem, which makes it more likely that the software would be of a high quality. Tager is not a software house, but a demand-creator for this particular problem - they are likely to extract more benefit from a reputation of expertise in this area than from attempting to create a traditional product.
! Other doing this
http://radian6.com
<<attentionTracker>>
<<attentionTracker timeline>>
See [[An attention tracking system]].
<!--{{{-->
<div class='toolbar' macro='toolbar [[ToolbarCommands::ViewToolbar]]'></div>
<div class='title'><span macro='attentionTracker pretty "DDD DDth MMM YYYY, hh:mm:ss"'></span> - project: <span macro='view project link'></span></div>
<div class='subtitle'><span macro='view modifier link'></span>, <span macro='view modified date'></span> (<span macro='message views.wikified.createdPrompt'></span> <span macro='view created date'></span>)</div>
<div class='tagging' macro='tagging'></div>
<div class='tagged' macro='tags'></div>
<div class='viewer' macro='view text wikified'></div>
<div class='tagClear'></div>
<!--}}}-->
!The problem
It's a pain to manually write and send invoices. At the moment, yellowcar invoices are made by copying a previous google doc (there was not templating available at the time), which is a bit risky; then saving pdf's to the desktop and emailing to clients.
!The idea
A one-click method for creating a new invoice from a template and having the pdf saved online. Publishing optional.
!What information do we need on the invoice? Where does it come from?
On an invoice
* Invoice #
* Client Reference (they supply this e.g. from a purchase order, or not)
* Date
* FAO
* Client address
* Items:
** Title (required)
** Description (optional)
** Amount
* Sub-total (calculated)
* VAT (calculated)
* Total (calculated)
* From
There would be a client spreadsheet as well as an invoice spreadsheet. An example client row might look like:
| Client Ref | Contact name | Address | Notes |
| NOK1 | Mr. Barnes | 4, Otley Road, Leeds | He's lovely man who wants to spend lots of dollar |
An example invoice row might look like:
| Invoice # | Client ref | ... |
| 1 | NOK1 | ... |
!Progress (reverse chronological)
!!Approach 2 - HTML templating method
We have access to invoice and client data, as well as to an extra spreadsheet that calculates which ranges to pull from the invoice list for each invoice. Josh has created a HTML mockup of an invoice, I'm going to try pushing the information for an invoice into the template. I'm thinking about using TiddlyTemplating for this - see original idea at [[TiddlyInvoicing]].
The other options are: use jinja2 with TiddlyWeb to do something with any POSTed invoice information; use Smart and Sammy templates (no experience templating with Smart yet).
!!Approach 1 - prototyping
Decided to set up an invoice spreadsheet and a client spreadsheet, which someone would use to fill in details about invoices sent and clients. I fiddled about to make another spreadsheet which collects the items, descriptions and amounts for each invoice # (which involve figuring out which row each invoice # went up to) and then creates a string describing a range to import into a 3rd spreadsheet, which is where the actual invoice template lives. This invoice template spreadsheet has a single cell you type the invoice # into, a sheet to import the data range and a sheet to lay out the spreadsheet.
* It would be useful if sheets could be made read-only
We've set up an example invoice that pulls in information dynamically from both the client list and the invoice list.
I think the next thing is to simplify this:
* combine the invoice tracker and the client list into a single spreadsheet with two sheets (this spreadsheet doesn't contain any logic);
** have done this
* put as much of the working as possible into an intermediary spreadsheet (so people can't accidentally mess it up)
** have done this, made things quite a lot simpler
Then:
* make the invoice look like an invoice and not like a silly grid.
** we tried to get rid of the gridlines (which we could do inside Google Spreadsheets by importing an excel sheet that had white borders on the cells), but we couldn't get the sheet to export without showing the gridlines
!!! Where are we at?
* We have the beginnings of a way to store client and invoice information, that can be used to both generate reports and collate the information necessary for an invoice
* We are finding that the presentational problems when exporting from Google Spreadsheets are looking hard to get around
** We are going to explore a HTML-templating approach
!!Idea for an approach
Something like:
# put data into spreadsheet (invoice number autogen, with other fields like item, item desc, cost, vat, date, 'to' person, client address, account reference, maybe others whatever)
# export data from spreadsheet
# turn into HTML
# upload as a new Google Doc (via the API)
# data magically appears in a formatted invoice document (Gdoc or word or indesign) to be exported as a pdf
# pdf is saved to Google Docs Account
You might notice that this isn't automated, it's manual - you'd presumably press a button in an application that does those steps. If you wanted it to be automated, you'd need to:
* create a feed of the content of the spreadsheet
* set up a notifier for changes
* get the new information out of whatever text the notifier sends you
* send it to an app that does steps 2 - 4 above
Takes control of charging customers.
|Name|Free?|Notes|
|[[Recurly]]|From $30/month| does monthly subscription billing, you need your own payment gateway |
|[[Zuora]]|Apparently expensive| pretty comprehensive looking billing and payment processing (using PayPal X)|
|CheddarGetter|From $30/month| billing, not payment processing |
|[[Spreedly]]|?| has "super-simple API", provides webhooks (not sure what for yet) |
Often based on Selenium, for running functional tests across browsers and OS's.
| Name | Free? | Tried? | Notes |
| Go-Test.it | Plus paid | yes | closing down Spring 2010 |
| Gomez Reality Check XF | unsure | no | |
| SauceLabs | 30-day trial | no | |
| CloudTesting | 7-day trial | no | |
I've come to the conclusion that the reason to automate company workflows is to create automatically updated answers to questions you need to run your business. Although it might seem like an over-use of the idea, I am going to try describing these as [[User Stories|Agile Methodology]], as it will give each question a context and a specific reason for wanting to answer that question. Instead of writing questions from the point of view of a person, I'm writing them from the point of view of a business process.
# As cashflow control, I want to know who owes me what so I know who to chase
** Information I need:
*** Which invoices are unpaid
# As cashflow control, I want to know how long people have owed me money for, so I can prioritise who to chase
** Information I need:
*** How long ago I sent an unpaid invoice
# As new business, I want to know how much I've billed for on average each month, so I can figure out how much I need to have in the pipeline for the upcoming month
** Information I need:
*** Which month I sent an invoice in
*** How much each invoice was for
# As accounts, I want to know who I need to invoice, so I can send a timely invoice
** Information I need:
*** What jobs are done
*** Which client each job is for
*** How much to bill a job for
# As accounts, I want to know who I have invoiced so I don't send an invoice more than once
** Information I need:
*** What invoices I've sent
---------
It's gonna be a good thing to automate the things that are a pain.
In development:
#Invoice creation
** [[Auto-generation of invoices]]
** [[Visualising finances for Tager]]
Currently automating for yellowcar ltd:
# invoice tracking
** Recording the sent invoices and received payments through this form: http://tinyurl.com/39y4ef4
*** for 2009-2010, it was: http://tinyurl.com/yellowcarltdinvoicetracker2
** Creating a summary of paid and unpaid invoices here: http://tinyurl.com/yellowcarltdinvoicesstatus
** Current model:
*** Invoices are only 'sent' once (so any re-issued invoices need to be corrected manually)
*** Invoices can be paid in parts
Source -> git push to github -> post-commit hook to node app -> triggers saucelabs test. ??
Fine, except how do you reset the data on the test system? I suppose you want a super-button to reset the data on the test system, which means clearing the accounts (except for 'admin') and all the viewings. This could be a sauce test anyway...
Have made a test for clearing account data. Thinking it's appropriate to create a big reset button for testing, which kills all the property extended fields and the viewings. And the accounts too? Then I could just run one test for resetting the data.
It's possible to save a lot of time and energy by [[Automating company workflows]]. I'm trying to do this as much as possible with my company. It is also something I want to help others with.
You can also automate things that are useful for individuals, such as this: [[How to build your own TwitPic]]
Programming automated access to web sites that don't have accessible API's is another interest - see [[jsAutomatic]].
Rate: standard
Invoices: [[Invoice 0004]], [[Invoice 0006]]
Description: developing the "wiki-data" system produced at the [[Osmosoft/Avox hack-day|http://softwareas.com/wikidata-hackathon-wikidata-a-wiki-of-companies-data]]
Source: http://svn.tiddlywiki.org/Trunk/association/serversides/tiddlyweb/verticals/wikidata/avoxjson-s
Development tracker: http://wiki-data.lighthouseapp.com/
[[Citigroup workshop|Avox Wiki-data Citigroup workshop]]
[[Avox Wiki-data phase 1]]
[[wiki-data workshop]]
[[Avox Wiki-data phase 2]]
[[wiki-data phase 3]]
[[10th August Avox Wiki-data]] - 2 hours
[[11th August Avox Wiki-data]] - 8 hours - invoiced to this point [[Invoice 0004]]
!Planning
[[Avox Wiki-data release 1 user stories]]
[[Design|Avox Wiki-data phase 1 design]]
!Log
//Note that times attributed to Chris are maximum as he intends to offset part to Osmosoft//
[[12th August Avox Wiki-data]] - 1 hour 50 mins
[[13th August Avox Wiki-data]] - 25mins
[[14th August Avox Wiki-data]] - 3hours 10mins
[[25th August Avox Wiki-data]] - 3 hours 20mins
[[26th August Avox Wiki-data]] - 5 hours 40mins
[[28th August Avox Wiki-data]] - 7 hours 10mins
[[Avox Wiki-data: review of development to 28th August]]
[[1st September Avox Wiki-data]] - 4hrs 55mins (+ 5hrs 30mins Chris max)
[[2nd September Avox Wiki-data]] - 7hrs
[[3rd September Avox Wiki-data]] - 5hrs (+ 3hrs 15mins Chris max)
Sub-total: me: 38hrs 30mins, Chris: 8hrs 45mins; total: 47hrs 15mins
[[4th September Avox Wiki-data]] - 6hrs 40mins
New estimate from Chris - 6hrs 45mins; 20 hours offset to Osmosoft
This makes the sub-total: me: 38hrs 30mins, Chris: 6hrs 45mins; total: 45hrs 15mins
[[Avox Wiki-data: review of development to 4th September]]
[[Invoice 0006]]
[[11th September Avox Wiki-data]] - 3 hours
[[Avox Wiki-data: review meeting, 9th September]]
[[Avox Wiki-data: estimate of development gaps to V1 launch]]
[[14th September Avox Wiki-data]] - 5 hours
[[15th September Avox Wiki-data]] - 4 hours 20 mins
[[17th September Avox Wiki-data]] - 4 hours 20 mins
[[18th September Avox Wiki-data]] - 5 hours 20 mins
[[22nd September Avox Wiki-data]] - 1hr 40mins
[[29th September Avox Wiki-data]] - 2hrs 50mins
[[wiki-data phase 1 consolidated feedback call]]
[[Feedback on wiki-data phase 1]]
Invoice from Chris from 12 hours to 30th Sep - 5hrs 15mins are for since [[Invoice 0006]]
[[1st October Avox Wiki-data]] - 1hr 55mins
[[2nd October Avox Wiki-data]] - 1hr 35mins
[[4th October Avox Wiki-data]] - 3hrs 30mins
[[5th October Avox Wiki-data]] - 1hr 35mins
[[6th October Avox Wiki-data]] - 7hrs 45mins
[[7th October Avox Wiki-data]] - 3hrs 5mins
[[8th October Avox Wiki-data]] - 45mins
[[9th October Avox Wiki-data]] - 30mins
Sub-total since [[Invoice 0006]] - 47hrs 10mins + 5hrs 15mins = 52hrs 25mins
[[Invoice 0008]]
[[10th October Avox Wiki-data]] - 5hrs 45mins
[[12th October Avox Wiki-data]] - 4hrs 10mins
[[13th October Avox Wiki-data]] - 4hrs 10mins
[[14th October Avox Wiki-data]] - 13hrs 10mins
[[15th October Avox Wiki-data]] - 15mins
[[19th October Avox Wiki-data]] - 6hrs 55mins
[[20th October Avox Wiki-data]] - 7hrs 35mins
[[21st October Avox Wiki-data]] - 3hrs 30mins
[[22nd October Avox Wiki-data]] - 11hrs 25mins
[[23rd October Avox Wiki-data]] - 1hr 20mins
[[24th October Avox Wiki-data]] - 4hrs 25mins
[[25th October Avox Wiki-data]] - 2hrs 40mins
[[26th October Avox Wiki-data]] - 8hrs 45mins
[[27th October Avox Wiki-data]] - 5hrs 10mins
[[28th October Avox Wiki-data]] - 4hrs 15mins
[[29th October Avox Wiki-data]] - 5hrs 40mins
[[30th October Avox Wiki-data]] - 2hrs 20mins
[[6th November Avox Wiki-data]] - 6hrs 55mins
[[8th November Avox Wiki-data]] - 2hrs 55mins
[[9th November Avox Wiki-data]] - 4hrs
[[Invoice 0009]]
Having spoken to Ken about the current design, he is happy for some experiments with different designs to take place. We talked about boxy, modern-looking designs that take advantage of AJAX to improve the experience.
!Iteration 3
Amit is involved making new designs. Screenshots to come.
See here:
http://wiki-data.com/static/designs/
!Iteration 2
[img[http://img.skitch.com/20090904-g4cp7bd63i75eisutfinsps4sp.jpg]]
Nick Webb
!Iteration 1
[img[http://img.skitch.com/20090904-xhi2skea374t7s7cdy746pebrc.jpg]]
!Planning
[[Avox Wiki-data phase 2 user stories]]
!Log
[[10th November Avox Wiki-data]] - 1hrs 30mins
[[11th November Avox Wiki-data]]
[[12th November wiki-data workshop]] - day (8 hrs)
[[18th November wiki-data]] - 3hrs 15mins
Invoice from Chris for 9hrs 15mins to end of October (unbilled)
[[19th November wiki-data]] - 1hr 35mins
[[23rd November wiki-data]] - 5hrs
[[24th November wiki-data]] - 2hrs 5mins
[[25th November wiki-data]] - 2hrs 40mins
[[26th November wiki-data]] - 1hr
[[27th November wiki-data]] - 3hrs 30mins
[[2nd December wiki-data]] - 3hrs 55mins
[[3rd December wiki-data]] - 3hrs 25mins
[[4th December wiki-data]] - 2hrs 5mins
Update from Amit: 15 hrs and 13 mins from 3rd to 15th December = 70.23 mins a day, so 3rd to 9th inclusive is 492mins = 8hrs 12mins, say 8hrs 10mins, leaving 410mins up to 15th
Sub-total: Jon - 38hrs; Chris - 9hrs 15mins; Amit - 8hrs 10mins
[[Invoice 0010]]
[[15th December wiki-data]] - 4hrs
[[6th January 2010 wiki-data]] - 5hrs
[[7th January 2010 wiki-data]] - 5hrs 20mins
[[8th January 2010 wiki-data]] - 5hrs 5mins
[[9th January 2010 wiki-data]] - 3hrs
Amit sent his hours since 10th Dec - 36hrs and 45mins
[[10th January 2010 wiki-data]] - 8hrs 5mins
Sub-total since 10th Dec: Jon - 22hrs 25mins; Amit: 36hrs 45mins
[[Invoice 0012]]
[[11th January 2010 wiki-data]] - 8hrs 50mins
[[12th January 2010 wiki-data]] - 25mins
[[13th January 2010 wiki-data]] - 5hrs 25mins
[[14th January 2010 wiki-data]] - 3hrs 45mins
[[15th January 2010 wiki-data]] - 1hr 45mins
Sub-total: 20hrs 10mins
The user stories are recorded in this Google Doc: http://docs.google.com/View?id=dr44nfc_83xvr4jkfv
A detailed estimate needs to be performed.
Estimated hours: 41
There is a mix of planning notes and progress updates mixed in below, so it can be a little confusing to follow.
!Entity URI: As a user, I would like a stable URI for a company name so I can bookmark and link.
*I can copy the URL of the record from the browser and re-visit that record later using this URL - HALF-DONE
** Only 10k records imported so far
** All records need their own page, which is already the way the system is set up - I need to import all the ~218k records from the extract, and make sure the server is optimized so that searching this data doesn't take forever - I am speaking to Chris Dent about this. I suspect the SQL Store is going to help. I imagine ''3 hours'' to get it all sorted.
** Infrastructure set up - need to import extracts
** Also, some basic design work will need to be done to make sure pages are consistent with rest of the site
I need a test system setting up that has a reduced amount of the extract imported, for testing functions rather than performance. This should take ''2 hours''. - DONE
*Site is available at http://wiki-data.com
** Requires Avox to redirect to the server the site is installed on - nothing from me
!Search: As a user I would like to be able to find stuff by name, location, etc…
*No wildcards used - DONE
*A blank entry is not a valid search - DONE
*Can search by AVID or keywords - DONE
*A box for AVID and a box for keywords - DONE
*The search is an AND search by default - DONE
** TiddlyWeb filters work as AND searches by default
*Putting double-quotes around a set of words specifies a phrase - DONE
*Keywords are matched against legal name, trades-as name and previous name(s) - DONE
*Through the API, I want to be able to search and filter in the same step - DONE
*“Advanced” search is invisible by default - DONE
*If there are no (suitable) results, I am encouraged to use the advanced search - DONE
** As a temporary measure, a blank 'stub' tiddler is returned if there are no results
** It would be better to treat the case of no results by presenting a message suggesting use of the advanced search and explanation of what it does
I think the underlying technology is going to be a search API. As we want to support searching and filtering from this API, we need to support queries such as:
{{{
/search?keywords=a,b,c1+c2&filter=country+of+incorporation:UK
}}}
TiddlyWeb already supports searches such as this:
{{{
/bags/myBag/search?q=where+is+my+dinner
}}}
The implementation of search is overrideable and different for each type of store:
{{{
tiddlers = store.search(search_query)
}}}
TiddlyWeb already supports filters such as this:
{{{
/bags/myBag/tiddlers?select=tag:published;select=tag:blog;select=modified:>2009;sort=-created;limit=10
}}}
The filters are going to need to match when a selected attribute contains the text for that filter, rather than being exactly equal to the text, which is default behaviour for TiddlyWeb filters. According to [[this|http://tiddlyweb.peermore.com/wiki/#%5B%5BHow%20do%20I%20extend%20the%20filter%20syntax%3F%5D%5D]] description of how to extend the filter syntax, creating a plugin to support matching inside an attribute should be fairly straightforward.
From the looks of the [[SQL Store|http://github.com/tiddlyweb/tiddlyweb-plugins/blob/3fbc3727d0659295d9a0e031758915d345ab7f55/sqlstore/sql.py]], it should be doable to write a plugin to override the search mechanism to support the above requirements that are not met with the default behaviour:
* blank search string being invalid
* search a particular subset of tiddler fields (either default, or overridden by advanced search)
* splitting a search query into a list of keywords and matching against all of them
* custom behaviour if no results are found (in this case, suggesting use of advanced search)
I think writing a search plugin is about ''6 hours'' work.
I need to create a single-box Google-style search interface with an 'advanced' search that is hidden unless you click on a button. The advanced search has the option to search fields other than the default fields. I think this is ''2 hours'' work.
!Filtering: As a user I would like to filter my results so I can find the result I am looking for.
*Adding a new filter creates a new box to search against a particular field - DONE
This is a client-side piece of functionality. The interface needs to support providing another box when the 'add filter' button is clicked; you can select which field you are filtering on; as you type the results are updated automatically.
I think this requires some experimentation to get the design right. I think it will be ok if we just shift the results down as we add filter rows. I think the design will take ''2 hours''.
Building the filtering behaviour will take ''2 hours''.
See here for jQuery-based example: http://gregweber.info/projects/demo/flavorzoom.html
!Browsing: As a user I would like to easily browse the site by region, company, etc.
*I can only browse having first done a search - DONE
*I can re-order columns - DONE
*I can hide columns - DONE
*I can sort rows by clicking on column title; first click sorts by ascending - DONE
*Second click on a column title sorts by descending - DONE
*Sorting by a column sets the active sort; clicking on another column sets the secondary sort (and so on) – this is clearly indicate to me - DONE
*Clicking on a record takes me to the record’s page - DONE
*All the available fields are shown - DONE
There are a number of jQuery table-related plugins that ought to help out here e.g. [[Column Manager|http://plugins.jquery.com/project/columnmanager]] and [[Data Tables|http://plugins.jquery.com/project/DataTables]]. I think that the only feature I'm not confident about is dragging to re-order columns. I think this is about ''3 hours'' worth of work.
There is a list of some helpful-looking jQuery-based table plugins here: http://javabyexample.wisdomplug.com/web-programming/47-javascript/88-20-best-jquery-table-plugins.html
!Challenge: As a user I would like to be able to notify Avox that I believe one of their data records is wrong so they can fix it.
*I challenge by editing the data first – the original data is on the left and the right contains a copy of it for me to edit or delete - DONE
** Putting together a neat interface for this is about ''4 hours'' of work to design and create it.
*Submitting sends an email to Avox exactly as the current method does
*I can optionally provide the source of the conflicting information (either as a URL or a less specific indicator) e.g. “What is your source for this? (Please include URL and/or sources used) We will use this to validate your challenge.” - HALF-DONE
** The contact details form needs completing
I should be able to re-use the forms from the [[current|http://www.avox.info/emailform.php?contactReason=Correction&AVID=6248680]] Avox site to understand how to interact with the emailer. This is mainly a job of creating an easy-to-use UI.
!Requesting additional data: As a user I would like to be able to request more information about a record I’ve found on wiki-data.
*I can see which fields are available for request, although I don’t know in advance whether the fields are populated or not - DONE
*It is free (in this version) - DONE
*Avox gets an email with the AVID of the record they want more information for (exactly as it works at the moment)
I am assuming that the available fields do not change from record to record, which makes this simpler as I don't need to do any extra server-querying to display the page. I think that designing and building this page is ''3 hours'' work.
!Adding a company: As a user I would like to be able to add a company to wiki-data and be given an ID so I can link to it.
*Mandatory fields: legal name, operating country (drop-down), state (required for Canada, USA and Australia; drop-down if one of these three) - HALF-DONE
** Dynamic state drop-down isn't in yet
*Preferable fields: rest of the operating address, parentage, entity type (drop-down list), registered address, registration number - DONE
*There are two sets of fields – the first for mandatory and others we’d like, the second for extra information - DONE
*Address fields are collapsed into a single field to begin with, with a button to expand them
There is already a form to do this on the [[current|http://www.avox.info/suggestRecord.php]] Avox site. I think this is mainly a job of simplifying that interface so it is less intimidating. I think there is about ''3 hours work'' involved in this.
!Feedback: As Avox I would like to know what people think of wiki-data so I can improve it.
*Free-text form that sends an email to Avox
I need to add a link to all pages to the feedback form. I think a modal dialog box along the lines of the [[UserVoice|uservoice.com]] feedback widget would be really neat. I expect getting this into the pages in a non-obtrusive way and making sure the modal dialogue works is about ''3 hours'' work.
!Spam: As Avox I would like to deal with malicious users (e.g. spam).
*Uses the same mechanism as exists at the moment - DONE
[[ReCAPTCHA|http://recaptcha.net/]] is currently used to protect against spam. Having looked at the integration options available, I've found a Python client library, which ought to make the integration easier than hand-writing a plugin. I would like to speak to Scott to figure out how he integrated ReCAPTCHA and get access to his API key (as it is tied to the domain). I think this might be a ''5-hour job''.
!Site analytics: As Avox I would like to track wiki-data usage to facilitate market research.
*Google Analytics installed on each page - DONE
This needs a piece of code to be installed on all pages, whether they are static or dynamic. This suggests that the structure I'd want to use is that all pages are rendered through the templating engine, so I can re-use headers and footers and the like - static files are restricted to resources such as CSS and JS files.
That means that currently static pages need to be made dynamic (templated) - terms, challenge, request, search. I think that to make sure this is how things are set up is going to take ''2 hours''.
!Terms of use: As Avox I would like the wiki-data data to be clearly licensed so people don’t abuse the access.
*The terms of use [[on the site|http://www.avox.info/termsofuse.shtml]] at the moment can be re-used - DONE
This requires a link to the terms and conditions at the bottom of the page, which leads to a static page that replicates the existing static page - I think that with the static plugin installed, this will take ''1 hour''.
This estimate for the remaining work to get to V1 launch takes into account any bugs, unfinished user stories and extra detail on user stories revealed during the [[latest review|Avox Wiki-data: review meeting, 9th September]].
''Gap estimate:'' 24 hours
''Extras (all going in):'' 7 hours
''Total:'' 31 hours
The intention is to complete this work by Friday 18th September.
!Gaps
!! Bugs in existing work
* IE:
** search results not being presented as correct page - ''1 hour'' - ACCEPTED
** strange white shadow to input buttons - SPIKED - ''1 hour''
*** this looks helpful - http://stackoverflow.com/questions/150535/jagged-button-edges-in-internet-explorer
** Windows body font is not as nice - ''1 hour'' - READY FOR REVIEW
*** Everything changed to Tahoma Grande or Arial now
** right-hand section of header is shifted down so top is aligned with bottom of left section - they should be aligned top-to-top - SPIKED - ''1 hour'' - READY FOR REVIEW
*** via [[quirksmode|http://www.quirksmode.org/css/display.html#inlineblock]]: "In IE 6 and 7 inline-block works only on elements that have a natural display: inline."
*** can we use floating left to do the trick? testing now on FF
**** used with absolute positioning inside a relatively-positioned header, looks correct in FF and Safari and now doesn't rely on inline-block, so might work on IE
* Blank searches should not be triggered by blank company name query when other fields e.g. City of Incorporation are present - a blank search returns no results - ''1 hour'' - FIXED
** This suggests the search plugin is chucking out an empty results array if the primary query string is not present
* If you click to drag a column, clicking a header to sort stops working - SPIKED - ''1 hour'' - FIXED
** Further bug definition:
*** If you swap first right-to-left, then sort on any column, you get the error {{{oSettings.aoColumns[i] is undefined}}} at line 3186 in {{{jquery.dataTables.js}}}
*** If you swap first left-to-right, then sort, you can sort without error
*** If you sort first, then swap right-to-left, then try to sort the same column again, you get the same error
*** If you sort first, then swap left-to-right, you can sort the same column without error
*** I think I have pinned it down - my modification to dragTable.js only copes with left-to-right swapping
!! Unfinished user stories
* Filtering: here is a discussion between Ken and Jonathan, which better specifies how the filtering should work (in light of this, the filtering user story ought to be adjusted down from 100% complete)
##Once you add a filter, there is no obvious way to apply it (I believe hitting “enter” initiates the filter)
***I'd like it to filter immediately. So, if you type a few letters, the table updates to reflect that, loading results from the database if not all the results are currently being shown. What do you think? - ''3 hours'' - READY FOR REVIEW
**** DataTables can handle auto-filtering
**** Assuming the database is performing well, we can always query for extra data and load it into the table if needs be - DataTables handles server-side filtering of data, see here: http://www.datatables.net/usage/server-side
**** Until server supports the sending of the number of results, we're going to stick to only filtering results shown in the table; there is a clear message that hitting enter performs a new search with the current filters
##The filter did not work when I typed in a single term within the legal name (Holdings, Finance). It should be configures to return all results that have the term within the filter criteria
***I think this is related to the problem we found on Wednesday where you are required to input a name in the company name field, otherwise no results are returned - ''1 hour'' - READY FOR REVIEW
##Presumably the filter is applied only to the results from the previous search?
***Yes, that would be the idea, although that is not how it is set up at the moment. In the attempt to streamline the presentation of advanced search and filtering as the same interface, this has not been implemented - when you add a filter, it does the whole search again from scratch - ''3 hours'' - READY FOR REVIEW
**** We'll need to split the behaviour like this: if there is an existing search, filter it; if there isn't, treat it like an advanced search and submit a fresh search
##Once the filter is applied, it disappears. Would it not be logical to leave the filter you have applied visible on the screen and to have a “clear” button next to it?
***Agreed. I'd also expect the company name (or AVID) search term to appear in the box - ''1 hour'' - READY FOR REVIEW
* In 'adding a company'
** Mandatory fields: legal name, operating country (drop-down), state (required for Canada, USA and Australia; drop-down if one of these three) - dynamic state drop-down isn't in yet - ''1 hour''
* Email system not set up on the server (this is part of the outstanding user stories) - SPIKED - ''2 hours''
** This is an example of using a Python SMTP client library: http://mail.python.org/pipermail/python-list/2001-February/068757.html
** Chris has offered to set up the email server on the box
* 220k records set needs to be imported (also part of outstanding user stories) - ''1 hour'' - READY FOR REVIEW
* Records table layout: a sensible subset of fields, with a mechanism for showing others; intention is to keep default width within one page - SPIKED - ''3 hours'' - READY FOR REVIEW
** DataTables has a function, {{{fnSetColumnVis}}}, to set the visibility of a column on or off; I think a drop-down div could be created to left you toggle the status of a column on or off
** To fit on the page, you could limit display-at-once to 8 or thereabouts
* feedback system - need to check out costs and suitability of the UserVoice and GetSatisfaction options presented (also part of outstanding user stories) - ''2 hours''
** we'll need to add the choice to the site as well
* contact form should contain: Email / Name / Country / Company (optional) and a note saying something like "we'd like to know who you are" (also part of outstanding user stories) - ''1 hour'' - READY FOR REVIEW
!! Extras - all going in
* Performance - we're aiming for sub 2-second searches; currently, unfiltered company name searches are the slowest. Chris is continuing to work on improving performance and is expecting to have a prototype of a new set this Friday - ''5 hours'' (predicted to be offset as Osmosoft work) - READY FOR REVIEW
** Much improved, not down to 2 seconds for all searches, but I think quick enough to launch with
* Record set management - there should be something automated to refresh the database when new data is posted - SPIKED - ''2 hours'' - SUGGEST MOVE TO NEXT PHASE
** Have emailed Andrew about this
*** Andrew recommends scripting what you'd do manually and putting it in a cron job
*** Have opened a conversation with Chris and Greg to discuss how to do this
**** Suggest delaying until next phase to give Greg time to set up Avox side of things
* Links to "sample record", "field spec", "about wiki-data", "partners" and "avox" should be in the page footer - ''1 hour'' - READY FOR REVIEW
** We could copy the contents from the existing site and host on the new site, like with terms and conditions
* Freezing column headers so you can scroll down a table without losing sight of them - SPIKED - ''4 hours'' - READY FOR REVIEW
** Ways that look possible to do this include:
*** Copying table header on top of scolling table e.g. [[StupidFixedHeader|http://jackysee.googlepages.com/fixedheaders.html]] jQuery plugin
*** Making {{{tbody}}} scrollable - see http://bytes.com/topic/html-css/answers/577825-frozen-locked-html-table-header and http://stackoverflow.com/questions/486576/frozen-table-header-inside-scrollable-div - apparently trickier on IE and involves CSS expressions (or JS I guess)
*** Have also written to DataTables guy to ask if he knows a neat way to fix the headers
This meeting happened at Osmosoft Towers with Ken Price, Jeremy Ruston and Jonathan Lister.
The agenda was:
* a discussion of business models allowing for controlled greater openness of data
* discussion of Osmosoft/Ingres hack-day
* a review of the site against [[Avox Wiki-data release 1 user stories]]
* IE testing
* identifying blocks before launch of V1
* extra features or user stories
* plan up to launch of V1
* other notes
!Ingres hack-day
[[Ingres|ingres.com]] is an open-source database company. Jeremy intends to hold an Osmosoft/Ingres hack-day with a view to working with an Ingres consultant for a while after - the aim is to see if Ingres can be usefully made to work with TiddlyWeb; another aim will be to use the skills of the Ingres team to optimize an Ingres database holding the Avox data
!Review against release 1 user stories - gaps, including IE problems
* General agreement that what is claimed to be done is done
* Blank searches should not be triggered by blank company name query when other fields e.g. City of Incorporation are present - a blank search returns no results
* If you click to drag a column, clicking a header to sort stops working
* IE:
** search results not being presented as correct page
** strange white shadow to input buttons
** Windows body font is not as nice
** right-hand section of header is shifted down so top is aligned with bottom of left section - they should be aligned top-to-top
!Block before V1 launch
* Performance - Chris is continuing to work on improving performance and is expecting to have a prototype of a new set this Friday
* Email system not set up on the server (this is part of the outstanding user stories)
* 220k records set needs to be imported (also part of outstanding user stories)
* Records table layout: a sensible subset of fields, with a mechanism for showing others; intention is to keep default width within one page
* contact form should contain: Email / Name / Country / Company (optional) and a note saying something like "we'd like to know who you are" (also part of outstanding user stories)
* feedback system - need to check out costs and suitability of the UserVoice and GetSatisfaction options presented (also part of outstanding user stories)
! Extra features or user stories
* Record set management - there should be something automated to refresh the database when new data is posted
* Links to "sample record", "field spec" and "partners" should be in the page footer
* Freezing column headers so you can scroll down a table without losing sight of them
!Plan up to launch of V1
# Estimate for tying up loose ends
# Do it!
!Other notes
* Jonathan checked Ken is happy with the transparent development process, so we will continue to manage the project like this
* Invoicing monthly is fine with Ken
* Jonathan is to send out invoice to 10th September (last invoice was dated 10th August)
!Review of development to this point
With reference to [[Avox Wiki-data release 1 user stories]].
Always thinking in terms of user stories completed. I've calculated some metrics based on estimates of how through each user story I am. "Estimated total development time' is the previously estimated time to satisfy all user stories; 'elapsed time' is the sum of the development time so far; 'weighted estimate percent complete' is the amount of the total development done, based on the estimates of how much of each story is complete; 'risk time' is the time it would take to satisfy all user stories //assuming// the estimate of amount done is correct.
''Estimated total development time'': 41 hours
''Elapsed time'': 21hours 35mins
''Weighted estimate percent complete'': 34%
''Risk time'': 63hrs 15mins
These figures are only there to give an order of magnitude estimate about how the development is progressing. It is natural for estimates to get better as time progresses, and regular assessments like this one are a helpful tool.
| ''Story'' | ''Estimated time'' | ''Percent complete'' | ''Comments'' |
| entity URI | 5 hrs | 50% |Main infrastructure set up is complete |
| search | 8 hrs | 30% |Much exploration done into this area. Plan for two-phase introduction of search - first re-use what exists as much as possible; second, introduce new search with improved performance |
| filtering | 4 hrs | 66% |Interface there on the client-side; much of the rest will be completed by the search system supporting filters |
| browsing | 3hrs | 90% |All acceptance criteria ready for checking; only needs to adapt to the different format of the new extract |
| challenge | 4hrs | 25% |Infrastructure in place |
| request more data | 3hrs | 25% |Infrastructure in place |
| adding a company | 3 hrs | 25% |Infrastructure in place |
| feedback | 3hrs | 25% |Infrastructure in place |
| spam | 5hrs | 0% | |
| analytics | 2hrs | 0% | |
| terms | 1hr | 50% |Infrastructure in place |
!Review of development to this point
With reference to [[Avox Wiki-data release 1 user stories]].
Site at: http://217.9.192.9:8080/index.html
Always thinking in terms of user stories completed. I've revised the metrics from last time based on new estimates of amount complete of each user story.
* 'Estimated total development time' is the previously estimated time to satisfy all user stories
* 'Weighted estimate percent complete' is the amount of the total development done, based on the estimates of how much of each story is complete
* 'Elapsed time' is the sum of the development time so far
* 'Risk time' is the time it would take to satisfy all user stories //assuming// the estimate of amount done is correct, using the elapsed time as the base
''Estimated total development time'': 41 hours
''Weighted estimate percent complete'': 80%
''Elapsed time'': 51hours 55mins
''Risk time'': 64hours 55mins (last time: 63hrs 15mins)
These figures are likely to be closer to the truth than in the previous review, but they are still estimates.
!Considerations
#Performance: it is obvious that searching the database is not currently fast enough. This is something Chris Dent is working on and making good progress with. Due to the generic nature of the problem, much of this work is being charged to Osmosoft. Trying to keep the solution generic is part of the reason why progress has been slow and steady.
**The good news is that simple queries such as "q=London", which are currently the slowest, taking minutes, are down to around 20-25 seconds.
**Expect more on this subject.
#Extra stories not in Phase 1 scope
** Phase 2 is about a different aspect to the application, so I'd like to set down what I think would be useful additions to this public area of the site, particularly in light of Ken's goal of helping the technology community gain value from this work:
** I am particularly interested in the exposure of data in various formats (txt, json, xml, et al.) through the Search and Browse API's. This is the first step in securing the support of the web community. There will be also be a need for documentation, examples and the managing of the community e.g. the issuance of keys for use of the API's. Several data formats for individual records are already supported
** Enhancing the data through the use of RDF - this is an interesting development that would allow for particularly complex queries and to be run against the database, allowing the linking of this data with data from other places, and the overall system will understand the concept of a 'company'
** Embedded microformats - this is a combination of both points above and is already implemented: each company record has its information encoded as 'hcard' in the HTML, which means that anything sensitive to this data-format can read the meaning directly from the webpage
!Breakdown of User Stories
| ''Story'' | ''Estimated time'' | ''Percent complete'' | ''Comments'' |
| entity URI | 5 hrs | 66% |I think what is left is addition of the whole 220k records set |
| search | 8 hrs | 100% |All the acceptance criteria are ready for testing |
| filtering | 4 hrs | 100% |The acceptance criterion is ready for testing |
| browsing | 3hrs | 100% |All the acceptance criteria are ready for testing |
| challenge | 4hrs | 66% |The interface just needs a few extra fields in the contact details and a neater style; the sending of an email to Avox is not happening yet |
| request more data | 3hrs | 66% |As above |
| adding a company | 3 hrs | 66% |As above |
| feedback | 3hrs | 0% |I would like to suggest a different approach to simple email - would you consider the use of a public feedback/discussion forum such as GetSatisfaction.com or UserVoice.com? |
| spam | 5hrs | 100% |The acceptance criterion is ready for testing |
| analytics | 2hrs | 100% |The acceptance criterion is ready for testing |
| terms | 1hr | 100% |The acceptance criterion is ready for testing |
A travel company project, introduced by [[Gary Inwood]]. Main contact is Harish.
Ground up design and build of [[Bell Pottinger Sans Frontiéres|http://www.bellpottinger-sansfrontieres.com]] website
!!Pre-log (week beginning 16th November):
* meeting on Monday - 2 hours
* half-day Thursday afternoon
* 3 hours on Saturday
!!Log
[[24th November BPSF]] - 3 hours
[[26th November BPSF]] - 1hr 45mins
[[27th November BPSF]] - 1hr 15mins
[[1st December BPSF]] - 6hrs 45mins
[[9th December BPSF]] - 2hrs 20mins
[[10th December BPSF]] - 6hrs 5mins
[[11th December BPSF]] - 5hrs 45mins
[[14th December BPSF]] - 5hrs 25mins
[[17th December BPSF]] - 4hrs 20mins
[[18th December BPSF]] - 1hrs 15mins
[[14th January 2010 BPSF]]
[[Invoice 0011]] - for first two week period, up to Friday 4th December
[[18th January 2010 BPSF]] - 3hrs 15mins
[[19th January 2010 BPSF]] - 3hrs 50mins
[[20th January 2010 BPSF]] - 5hrs 5mins
[[22nd January 2010 BPSF]] - 4hrs 40mins
[[25th January 2010 BPSF]] - 4hrs
[[26th January 2010 BPSF]] - 3hrs 55mins
[[27th January 2010 BPSF]] - 1hr
[[28th January 2010 BPSF]] - 2hrs
[[9th February 2010 BPSF]] - 3hrs 30mins
[[10th February 2010 BPSF]] - 6hrs 45mins
[[11th February 2010 BPSF]] - 4hrs 20mins
[[16th February 2010 BPSF]] - 5hrs 40mins
[[18th February 2010 BPSF]] - 1hr 55mins
[[22nd February 2010 BPSF]] - 2hrs 50mins
[[26th February 2010 BSPF]] - 5hrs 30mins
[[1st March 2010 BPSF]] - 1hr 40mins
[[Invoice 0015]]
[[19th March 2010 BPSF]]
[[31st March 2010 BPSF]]
[[1st April 2010 BPSF]]
[[6th April 2010 BPSF]]
[[22nd April 2010 BPSF]]
[[5th May 2010 BPSF]]
[[13th May 2010 BPSF]]
[[29th June 2010 BPSF]]
[[Baselining my life (part 1, money)]]
[[Baselining my life (part 2, time)]]
!Introduction
I began to seriously contemplate the idea of going freelance back in May, and realised I wasn't very prepared. For one thing, I didn't really know how much money I spent each month or what I spent it on. Nor did I have a good handle on how I spent my time. I wanted to see how I was behaving financially, so as to know how much I'd need to earn as a freelancer to maintain my lifestyle. I also wanted to know what made up this lifestyle to see how much time I'd have available for doing this freelance thing. I did go freelance at the end of June and carried on measuring things for a while so I'd have enough data for a meaningful result.
On the 6th June, I started keeping a record of my spending; on the 1st July, I began to keep a diary of what I was doing all day. At the end of August, I stopped measuring. This means I have four months of records of how I've spent money and three of how I've spent time.
Having this information is very useful, I think, so I've published all the data and analysis spreadsheets for you to play with. If you wanted to do something similar, they might be helpful. (The data are all published as Google Spreadsheets - see the links at the bottom of this post.)
Of course, it would be remiss of me, as a one-time scientist, if I were to look at data and deduce hypotheses after the fact. So, without prejudicing my judgement too much by figuring out a collection of perhaps-useful things in advance, I've written down some questions that I want answering and hypotheses to test for each.
*Money
## How much money do I spend in a month?
*** Hypothesis: I think I spend £2400 a month
## What am I spending most of my money on?
*** I think the majority is being spent on rent, dining and boozing
## How much do I spend on booze?
*** I think I spend £400 a month on booze
*Time
## How much time do I spend sleeping?
*** I think I spend 1/3 of my time sleeping
## How much time do I have free in a week after I've done all the habitual activities?
*** I think I have 31 hours a week after estimating (recorded in [[Habits]])
## What is the most expensive activity per time spent on it?
*** I think it is boozing (although dining will be a close second)
This post is going to cover the questions about spending money.
!!Constraints
# The xxx effect - Change my behaviour by measuring it?
# Not splitting money and time spending when on holiday - it just goes down as 'holiday'
!Measuring money
!!Answering the questions
!!! How much money do I spend in a month?
Hypothesis: I think I spend £2400 a month
The data shows: Between May and August 2009, I spent an average of £2492.65 each month
!!! What am I spending most of my money on?
Hypothesis: I think the majority is being spent on rent, dining and boozing
The data shows: rent 21.7%, holiday 20.6%, dining 10%, boozing 7.7%
!!! How much do I spend on booze?
Hypothesis: I think I spend £100 a week or £400 a month on booze
The data shows: Between May and August 2009, I spent an average of £191.20 on booze each month
!!The data illuminates
The images below are generated from the [[summary spreadsheet| http://spreadsheets.google.com/pub?key=tKAvPvOQiFopk-e6hQTHTBA&output=html]], which contains live graphs you can play with - clicking on pie segments, for example, reveals the percentage and value attached to each.
|<html><img src="http://spreadsheets.google.com/pub?key=tKAvPvOQiFopk-e6hQTHTBA&oid=4&output=image" width="600px" /></html>|
|Pie of average spending, May-August 2009|
|<html><img src="http://spreadsheets.google.com/pub?key=tKAvPvOQiFopk-e6hQTHTBA&oid=2&output=image" width="600px" /></html>|
|Spending by tag, May-August 2009|
!!Methods
!!! Collecting data - iPod Touch & Notes
Online banking is a boon to the would-be data collector, as you can generally download a transaction history in [[csv|http://en.wikipedia.org/wiki/Comma-separated_values]] format. Unfortunately, plenty of entries for "cash" creates a big unknown in the results. I decided to keep a record of when I spent cash during the day, what I spent it on and how much I spent. I didn't start monitoring expenditure until the 6th May, so approximately £140 cash is unaccounted for over the four month period (I haven't been worried sufficiently to alter any calculations to take this into account, as my monthly spending has a standard deviation of more than this).
I'd done something like this before, when I was having a crisis about where all my money was going shortly after I started working for BT in 2005. Back then, I used a pencil and notepad, and the prospect of copying all the records into a computer was so daunting that I never did. This time, I had the benefit of an iPod Touch (thanks Dad) and its "Notes" application. You can mail yourself a note, which I did every month or so.
Dealing with the data after it fell into my inbox was a matter of some search-and-replace in a text editor, to make the fields tab-separated so that they could be copy-and-pasted into Google Spreadsheets.
!!! Analysing data - Google Spreadsheet
The first thing to do was enhance the data by tagging each expense with a chunky category like "rent" or "boozing".
Using Google Spreadsheets to analyse an amount of information is a task with a fairly steep learning curve, since the examples for using the more complicated functions are not always that enlightening (I imagine if you are already an Excel functions master, it won't be so hard). Nevertheless, the Google Docs forum is replete with the writings of two characters in particular - [[Otávio Alves Ribeiro|http://www.google.com/support/forum/p/Google+Docs/user?userid=18201904381707253103&hl=en]], the Spreadsheet Ninja from Brazil, and "[[ahab|http://www.google.com/support/forum/p/Google+Docs/user?userid=02518524355686694178&hl=en]]", the mysterious Google Docs Guru.
The performance of a Google Spreadsheet is generally quite good, although there is often slowdown in processing. I found, mainly through trial and error, that a good way to understand what is going on and get decent performance out of Google Spreadsheets is to separate worksheets between data collection and analysis; then perform any inter-spreadsheet aggregation by importing the plain data from each spreadsheet into a single worksheet and make all your calculations based on that.
!! Further work
I ended up with just over 20 different tags; if I were to do this again, I would split the 'holiday' tag into its constituent parts and I would split "dining" into "breakfast", "lunch" and "dinner".
It will be interesting to compare my monthly spend (which is the simplest data to measure) to the relatively steady pattern I have discovered over these four months to see how much effect measuring my spending had on dampening it; indeed, I could measure the historical monthly spends and perhaps see an effect that way.
!! Data
All the data for this experiment is published on various Google Spreadsheets. See the links below.
* Spending summary 2009 - http://spreadsheets.google.com/pub?key=tKAvPvOQiFopk-e6hQTHTBA&output=html
* Spending May 2009 - http://spreadsheets.google.com/pub?key=rzrgXw5XETMTd4pXitHfSQg&output=html
* Spending June 2009 - http://spreadsheets.google.com/pub?key=tLAkTSOMNEEU_mV8-Yg1_YQ&output=html
* Spending July 2009 - http://spreadsheets.google.com/pub?key=tuomBonxEQVWwBD5drYPlhw&output=html
* Spending August 2009 - http://spreadsheets.google.com/pub?key=tiH8mjkiX-C5UlClJh2pdhg&output=html
Operations manager, London East, NCP
Runs Digital Brand Creative (www.dbcuk.com)
Into Wordpress site builds. Met at the birthday party of We Make Websites through [[Alex O'Byrne]].
They're doing API's for commonly needed things. They haven't taken the Salesforce-clone approach, which is refreshing. You can build a multitenanted app on their multitenanted platform, which makes it easier to run a business off it. I'm not sure what would happen if we wanted to create an app that was sold to different customers, each of which isolated from the others - multitenancy inside multitenancy inside multitenancy? That's worth of Inception.
Edinburgh festival ideas: have Anna get students to flyer, put Blessed Blend on one side, TVBomb on the other; have Nico make a super slo-mo, narrow depth of field, sensuous film of Fabio making an espresso, make it a shareable youtube thing, put URL of festival-specific site on the end (and normal site perhaps), come up with some slogan about coffee at the festival ("watch the best in the world, drink the best in the world"); web site with map, live twitter feed, photos, coffee facts...
170609 - Moo fixing my cards when I complained
-------
Designing with user stories for Geekmap
Benefits of open working, meaning assuming public view
Motivation & common ground post
-------
[[Work for perks]] post - DONE
-------
getting Cecily to work
You need the CecilyPlugin and the PageTemplate
-------
http://24ways.org/2008/easing-the-path-from-design-to-development
This is about photoshop design to developer. I have a workflow that goes from photoshop design, to HTML mockup with sample data, through to whatever templating the system uses to inject content (and presumably logic). Working on this for Geekmap, probably using TiddlyTemplating at some point - the browser is the place to write things for the browser.
-------
geekmap.co.uk - the thing - example of doing business in public - outside of the scope of a formal business arrangement, tracking contributions
-------
Thoughts about proportional reward
Peer-voting to accrue "points"; thumb-in-the-air equivalence between different identifiable contributions e.g. setting up a server, designing a page, adding a user interface widget, something server-sidey; feedback from BT OSSIG mailing list
-------
OpenSource CSS
Gallery of CSS fragments that style HTML structures (inc. microformats) in a cross-browser way
-------
User stories and development
Techniques for tying any development to user stories
Use of tickets, sub-stories that capture functional elements (are these acceptance criteria?)
A web site for Bonnie Parsons to show her portfolio and provide information about her classes and private teaching.
http://bonnieparsons.com/
!!v3
[[Richard White]] redesign for us, April.
10th April, 19:00 - 21:00
13th April, 13:45 - 17:15
* total weirdness found with using Google Analytics with GoDaddy domain masking (via frames) - you can't do it because the JS makes some illegal calls to the parent document. I've used [[StatCounter]] instead, which is free and doesn't have this problem.
** actually, that's not true! StatCounter has that problem too - calling Location.toString apparently...
** StatCounter does do a HTML-only counter, which is less comprehensive
29th June, 12:10 - 13:15
* putting on proper hosting
* fixing too-wide problem
* adding quote images (thanks Josh)
* making footer taller to fit quotes in black area
-----
!!v2.5
Redesigned layout again, during March.
-----
!!v2
15th Feb, 20:40 - 23:10
22nd Feb, 9:20 - 11:20
* wondering how to get list of vids out of Bonnie's youtube account ([[BonnieP19|http://youtube.com/BonnieP19]])
** looked at the youtube API docs - JavaScript API is rather lacking - only an API for an embedded player. Thinking I'll use a manual system - link to Wordpress?
* CSS3 box-shadow example: {{{box-shadow:rgba(0,0,0,0.5) 0px 0px 24px;}}}
28th Feb, 15:00 - 18:00
* Redesigned layout
-----
!!v1
Made the first and second versions over the weekend of 9th/10th May 2009. Tried to do it without messing with server-side, as per my preference. Did this much:
# used dropbox to store and publish the folder of static files
# used GoDaddy to redirect bonnieparsons.com to the published dropbox index.html; used the domain masking (which turns out to be just wrapping with a frame) to hide the dropbox URL
# set up google apps for your domain (free version) to get mail going for bonnie@bonnieparsons.com; used godaddy to change MX records for this
An idea to create a store full of very limited-edition products sourced by London's best coffee makers.
In:
Phil from [[Grace]] - truffles, michelin-starred chef experiences
Random thoughts:
Any barista at a shop can suggest something, should remember to mention this in intro emails
Sell coffee maps (along the lines of the [[Freelancers' Map|http://londonist.com/2010/02/the_freelancers_map_of_london.php]]), produced for the WBC?
This is interesting - http://www.londoncoffeejobs.co.uk/
Relevant blogs:
http://youngandfoodish.com/ - Daniel Young food blog
http://www.jimseven.com/ - James Hoffman's personal blog
http://coffeegeek.com/ - not a blog, but looks big
!!Log
8/7/10
* 11:45 - 13:15 getting email addresses for Jeremy, Lee, James @ Pitch 42 and Luke from Prufrock/Pavillion
* 17:45 - 18:10 sending out emails
7/7/10
* 12:30 - 14:00 getting email addresses from Cam (Flat White) and Matt (Milk Bar)
6/7/10
* 14:55 - 15:35 collecting email addresses, sending out personal emails; 15:50 - 16:50 sending out emails
5/7/10
* 12:40 - collecting email addresses, writing personal email idea for Caro to look at
Wednesday, 30/6/10
* 15:45 - 16:20; 16:40 - 17:00 - writing up BB distribution, thinking about what to do next
Friday, 18/6/10
* 10:25 - 16:15, BB distribution
** Pitch 42 - Jeremy, Lee (Sarah gf) - liked, thought about art, performance poetry, suggested a community board was an idea they'd had recently, where you could post for things like a barista-friendly plumber
** Climpson & Sons - Lucy, David
** Nude Espresso - Sascha, Richard
** Penny University - Tobias, Tim (heard about it, not seen it), James (not seen it on the day)
** Taylor St - Regia, Blaise (Sarah works there, not seen it on the day; Nick heard about it, not seen it)
** Grace - Phil, likes it
** Scootercaffé - Lucy, Steph
** Espresso Room - Candice, Norbert (Ben runs it, not seen it on the day)
** Flat White - Kam
** Milk Bar - Matt
** Monmouth - Scott (AJ, Anita run it, not seen it) - ideas include Monmouth beer/wine; they are opening a market at their roastery; Scott said I should email AJ & Anita
Thursday 17/6/10
* 14:30 - 21:05 BB distribution and Penny University opening
** Dose, Helen (wife of James, who runs it), Estelle, Pila; Helen said to look at CultureLabel as it's similar but not barista-focussed (this at Penny drinks)
** Pavillion Café - Rob/Brett - into the idea, suggested BB coffee cups as promotion, art from their friend who does the Darcel character
** Taste of Bitter Love - forgot name of person I spoke to, Flik runs it
** Prufrock - Luke, Gwilym (likes the list)
** Tina We Salute You - Stuart (likes Newington Greengrocers, good cherries), liked the idea
Wednesday 16/6/10
* 13:15 - 16:00; 16:50 - 17:30 BB brief finishing, printing and assembling; distributing
** Flat Cap Coffee Co - Fabio, Yasuko said Estelle from Dose would be into it; Fabio said he had an idea for a product, that he'd email me about it
** Lantana - Sheila, Sean - Sheila liked the idea, thought about shoes as an example of something not connected to coffee
** Kaffeine - forgot man's name, Emma (Emma said to look on Barista Exchange)
** Sacred - forgot lady's name
15/6/2010
* Josh and I - 17:30 - 20:35 - BB brief design and copy
* Josh - 00:15 - 1:20 branding image
Works at Nitobi. Met when he visited Osmosoft in 2009.
Quoted for the DRAMA project.
Likes:
sorry, just got back from vacation --- projects I like working on?
anything tough or considered impossible
PhoneGap, Nitobi.
iPhone/iPad apps.
There's [[Build your own TwitPic (prototype)]], which outlines the need and the approach, and [[How to build your own TwitPic]], which is a step-by-step guide.
!Problem
I can send camera-phone photos to a random blogspot blog, but I can't tweet them easily.
!Idea
Use notify.me to see that a new photo has been uploaded and kick off a workflow to tweet about it.
!Progress
The flow:
#Notify.me to monitor change in RSS feed and email me (DONE)
#Gmail filter to see the email and forward it to a tarpipe workflow (DONE)
#Tarpipe sends the email body off to [[Tarpipe / Yahoo! Pipes proxy]] (DONE)
#Proxy sends the email body off to Yahoo! Pipes to re-format the body ready to be tweeted (DONE)
#Yahoo! Pipes extracts the url of the photo and the title and text of the post and wraps it all into a string like "[DIYtwitpic.com] The seagulls - http://c.notify.me/qw6Gt6"; the URL is stored separately and not in the body string (''having problems'')
#Proxy receives response and sends back to Tarpipe (DONE)
#Tarpipe tweets the body and the shortened URL using my account
!Problems
I've found that testing is hard, for two main reasons:
#Tarpipe doesn't always seem to act on new mail - have asked about it [[here|http://getsatisfaction.com/tarpipe/topics/sending_an_email_to_a_workflow_not_working]]
#Getting at what's happening in the interaction between Tarpipe and Smart and Yahoo! Pipes is tricky
#The email body sent to Yahoo! Pipes as the item description doesn't match the regex {{{ .*(thing).* }}}, unlike the title; it also gets output as different HTML to what went in. I wonder if these problems are related
** It might also be something to do with the '.' not matching a newline
** I could try Yahoo! Pipes out on pulling from a static resource that contains the text of an email and see how it manages performing regex on that
#I need to answer the question of how to figure out exactly what data is transmitted between Tarpipe, Smart, Yahoo! Pipes and back again; and whether the HTTP headers (e.g. Content-Encoding) have any effect on this
** I have some idea that logging to a server I have direct access to might help, rather than relying on postbin, which naturally has to interpret the data to display as part of a HTML page, would be helpful
** Also, I'm trying out testing Yahoo! Pipes by feeding the userinputs in through the query string (so I should be able to recreate what is POSTed)
!Debugging
[[Notes on debugging with Tarpipe]]
I've found that Tarpipe escapes several things when it sends an email body in the REST connector e.g. this:
{{{
<br><br><div class=3D"gmail_quote">---------- Forwarded message ----------<br>From: <b class=3D"gmail_sendername">notify.me</b> <span dir=3D"ltr"><no_reply4@notify.me></span><br>Date: Thu, Jul 23, 2009 at 9:37 PM<br>Subject: notify.me [My Mobile Blog] - Hackspace @ The Hub<br>
To: <a href=3D"mailto:jnthnlstr@googlemail.com">jnthnlstr@googlemail.com</a><br><br><br>
<div>
<div><a href=3D"http://c.notify.me/M5seAw" target=3D"_blank">Hackspace @ The Hub</a></div>
<div><div><br><span>Nice</span><br></div><div></div><div style=3D"padding: 10px; background-color: rgb(42, 53, 64); margin-top: 25px; color: rgb(255, 255, 255);"> <span style=3D"font-size: 24px;">notify.me</span><span style=3D"font-size: 14px; padding-left: 10px;">always connected...</span></div>
<div style=3D"padding-top: 5px;"><a href=3D"http://www.notify.me/user/source/list" target=3D"_blank">Manage</a> Notification Settings</div>
</div>
</div></div><br><br clear=3D"all"><br>-- <br>t: @jayfresh<br>b: <a href=3D"http://www.jaybyjayfresh.com">http://www.jaybyjayfresh.com</a><br>
}}}
gets turned into this:
{{{
"description": "<br><br><div class=\"gmail_quote\">---------- Forwarded message ----------<br>From: <b class=\"gmail_sendername\">notify.me<\/b> <span dir=\"ltr\"><no_reply4@notify.me><\/span><br>Date: Thu, Jul 23, 2009 at 9:37 PM<br>Subject: notify.me [My Mobile Blog] - Hackspace @ The Hub<br>\nTo: <a href=\"mailto:jnthnlstr@googlemail.com\">jnthnlstr@googlemail.com<\/a><br><br><br>\n<div>\n<div><a href=\"http:\/\/c.notify.me\/M5seAw\" target=\"_blank\">Hackspace @ The Hub<\/a><\/div>\n<div><div><br><span>Nice<\/span><br><\/div><div><\/div><div style=\"padding: 10px; background-color: rgb(42, 53, 64); margin-top: 25px; color: rgb(255, 255, 255);\"> <span style=\"font-size: 24px;\">notify.me<\/span><span style=\"font-size: 14px; padding-left: 10px;\">always connected...<\/span><\/div>\n\n<div style=\"padding-top: 5px;\"><a href=\"http:\/\/www.notify.me\/user\/source\/list\" target=\"_blank\">Manage<\/a> Notification Settings<\/div>\n<\/div>\n<\/div><\/div><br><br clear=\"all\"><br>-- <br>t: @jayfresh<br>b: <a href=\"http:\/\/www.jaybyjayfresh.com\">http:\/\/www.jaybyjayfresh.com<\/a><br>\n\n"
}}}
You may notice that the '/' characters are escaped - I don't know why - and the line-breaks are changed to '\n'. Quotes are escaped, but that is expected as the string is wrapped in quotes.
Now then, a couple of questions:
# What does a Yahoo! Pipe do with this string?
# What's the format Yahoo! Pipes expects?
** Looking at the links 'Get as JSON', 'Get as RSS', it seems that {{{ \n }}} is encoded as {{{ \n }}}. This might be a problem since 'http://notify.me' is encoded by the REST connector as 'http:\/\/notify.me'
** I've found that if a directly put the long, escaped, string above into the pipe (through the 'run this pipe' screen), then the regex doesn't work, even if I change the {{{ \/\/ }}}' to {{{ // }}} in the target URL; if I use the non-escaped version, then it works. I wonder what the problem is...
*** It's very difficult to figure out the problem because pipes seems to cache, or something, because the results are not consistent...
**** I found some Pipes forum posts that suggest that yahoo caches pipe results for about 30 minutes (it might be handy to measure this)
**** I've [[posted|http://discuss.pipes.yahoo.com/Message_Boards_for_Pipes/threadview?m=tm&bn=pip-DeveloperHelp&tid=9717&mid=9717&tof=1&frt=2]] asking about this
** Ah, I've realised that it's meant to do this, because you need to escape forward-slashes in JSON
Moving on, I note that when this string is turned into a JSON object, the {{{ \n }}} characters seem to disappear in the strings when I log them to postbin. Continuing the example, my postbin log says that this is being sent to Yahoo! Pipes:
{{{
<br><br><div class="gmail_quote">---------- Forwarded message ----------<br>From: <b class="gmail_sendername">notify.me</b> <span dir="ltr"><no_reply4@notify.me></span><br>Date: Thu, Jul 23, 2009 at 9:37 PM<br>Subject: notify.me [My Mobile Blog] - Hackspace @ The Hub<br> To: <a href="mailto:jnthnlstr@googlemail.com">jnthnlstr@googlemail.com</a><br><br><br> <div> <div><a href="http://c.notify.me/M5seAw" target="_blank">Hackspace @ The Hub</a></div> <div><div><br><span>Nice</span><br></div><div></div><div style="padding: 10px; background-color: rgb(42, 53, 64); margin-top: 25px; color: rgb(255, 255, 255);"> <span style="font-size: 24px;">notify.me</span><span style="font-size: 14px; padding-left: 10px;">always connected...</span></div> <div style="padding-top: 5px;"><a href="http://www.notify.me/user/source/list" target="_blank">Manage</a> Notification Settings</div> </div> </div></div><br><br clear="all"><br>-- <br>t: @jayfresh<br>b: <a href="http://www.jaybyjayfresh.com">http://www.jaybyjayfresh.com</a><br>
}}}
although my postbin log says that this version of that text coming back from pipes start like this:
{{{
"<br><br><div class=\"gmail_quote\">---------- Forwarded message ----------<br>From: <b class=\"gmail_sendername\">notify.me<\/b> <span dir=\"ltr\"><\/span><br>Date: Thu, Jul 23, 2009 at 9:37 PM<br>Subject: notify.me [My Mobile Blog] - Hackspace @ The Hub<br>\nTo: <a rel=\"nofollow\" target=\"_blank\" href=\"mailto:jnthnlstr@googlemail.com\">jnthnlstr@googlemail.com<\/a><br><br><br>\n<div>\nbad:\/\/c.notify.me\/M5seAw\n<div><div><br><span>Nice<\/span><br><\/div><div><\/div><div style=\"padding:10px
...
}}}
there are line-break characters! Clearly, this is a limitation of my postbin logger.
#Is the line-break character in an input string to Yahoo! Pipes causing a problem? Something seems to be causing the problem where my regex {{{ .* }}} doesn't match all the text before my target URL (unlike in the title, which works fine)
** I'm doing to change the proxy to remove line-breaks and see if that makes things work, then at least I'll know if that's the problem
** I've done this and done some logging and it looks like the line-breaks were causing the problem
** I've found some forum posts suggesting that adding {{{ (?s) }}} to the beginning of the regex should enable "single-line mode" where the dot will match the newlines - looks like this works
*** Trying to iron this out using a simple pipe that just does a regex match trying to extract a string
*** Just though - ''I could make a string extractor sub-pipe'', which would be useful as it would remove the need to know regex...
!! Testing first working version
#With the temporary newline fix above, the system finally managed the round-tip, posting to twitter. The only problem was that when I tried it with a photograph uploaded from my phone, it posted to twitter in an endless loop, until something went wrong
** This looped 220 times for the first photo I sent, before, I think, 404'ing when tarpipe sent to Yahoo! Pipes; fortunately, it did not post 220 tweets, as I think twitter's rate limit must have kicked in when tarpipe was trying to post tweets
** This looped 20 times for the second photo I sent
#The problem was that my GMail filter that forwarded to tarpipe was matching the string 'notify.me' in the subject line (rather than the from address), which all of the logging emails contained, as I'd put the link in the subject - ''fixed''
I read recently about it being good to make your own tools (Bruce Mau). As a maker of things, doing precisely that is very tempting and can lead to the problem of "making a tool to make a tool", which tends to hold up projects.
In this lab book, I've been writing about [[Agile Methodology]] as a preferred choice of project management style. I've used it to store all the information about chunky projects such as [[Avox Wiki-data]], which have followed the Agile methodology. The essential questions I approach a project with on any new day are "what is going on now?" and "what should I be doing?", and whilst it has only been me doing most of the doings on a project, keeping the answers to those question up-to-date has been straightforward enough.
Now that I'm involved in projects that are swelling beyond involving just me, or mainly me, holes in my management technique are beginning to show. Particularly, making sure that other people are able to answer the aforementioned questions in the context of their own work is a tricky problem when I'm writing mainly about what I'm doing. I cannot expect other people to use the same technique for managing their personal work-load as I do, which means I cannot expect them to record in this document the same level of detail about what they are doing. The practical upshot of this is that my belief about what others think is the most important thing is for them to be doing, or the amount of time they have to do it, does not match what they believe.
I'm tempted at this point to build my own tool. I like the environment of a TiddlyWiki for it's model of fungible micro-content (tiddlers) and its plugin system, where the operation of practically anything is up for grabs. I also like TiddlyWeb and am motivated to be a source of demand for a hosted service a la TiddlySpot, which would provide the kind of collaborative platform suitable for building a project management tool upon.
The main reason I enjoy this environment for keeping track of projects is this: there's no enforced hierarchy or content-type for tiddlers - you don't have to say 'this is a task', 'this is a milestone' - everything is a tiddler. This is good for me because not everything I want to write about fits into some predetermined structure.
Tempted as I am to make a revolutionary new thingummy, I know that I have never had any real experience using an existing project management tool (which I secretly believe to be a good thing). So instead of rushing off to build one of my own, I thought it would be prudent to try one that somebody else has gone to the pain of building. I came across FogBugz yesterday, which seems like it will do the job - it's the product of a well-thought of software team, it's modern and imporantly, has a 45-day free trial. I'm going to use it for phase 2 of [[Avox wiki-data]], which ought to last just over a month - my site is at https://wiki-data.fogbugz.com
See http://spreadsheets.google.com/ccc?key=0AgQJ7FGUGIp_dDlES01VZWJDMnpTeWRWZ3RUMzJGQUE&hl=en
!!Useful info
http://www.independent.co.uk/money/mortgages/how-to-climb-your-way-on-to-the-property-ladder-2028600.html - news about private sales et al.
http://www.direct.gov.uk/en/HomeAndCommunity/BuyingAndSellingYourHome/index.htm - info about conveyancing
List of online estate agents (as opposed to private sales companies) - http://www.theadvisory.co.uk/online-estate-agents.php
U-rooms.com - evidence of selling services online to renters (http://www.propertypathways.co.uk/2010/03/lastminute-com-man-enters-lettings-portal-maket/)
"More innovation needed in home buying and selling market, OFT finds" - http://www.oft.gov.uk/news-and-updates/press/2010/18-10
"Recent OFT guidelines on what constitutes estate agency work mean that private sales sites can't do anything except advertise your property." - http://www.which.co.uk/advice/selling-property-online/private-sales/index.jsp - this means no For Sale boards, no negotiations, etc.
"Our consumer survey found that sellers who had chosen to use a traditional estate agent did so mainly because they valued their experience, knowledge and capability; because it was easier than alternatives; or because they thought it would attract more buyers." - OFT report 1186
"about 40 per cent [of estate agents] reported that over half of their sales were the result of leads generated by property portals" - OFT 1186
Global listings of interest now, with services to help you do it - http://www1.propertyportalwatch.com/2010/07/listglobally-providing-additional-revenue-opportunities-for-portals/
"Thirty five per cent of sellers reported that they had experienced a 'serious problem' while selling their home" - onlt 20% of these are due to the estate agent - OFT 1186, chart 5.14
For estate agent dissatisfaction, "For sellers, the top reasons were: keeping the seller informed of progress; chasing progress after exchange of contracts; and working in the seller's best interests.198 For buyers, the top reasons were: keeping the buyer informed of progress; advice on what offers to make; being honest in their dealings with you; and accuracy/ completeness of information about properties and area." - OFT 1186
A significant proportion of buyers and sellers are referred to services such as surveys, mortgages, and a significant proportion of those take up these services - it seems like online direct referrals would be attractive to estate agents, especially when fees from referrals can contribute up to 20% of profits - OFT 1186, chapter 6
Another side to this is that you could offer services not picked by estate agents e.g. offer price-comparison - this feature might be attractive to property developers (i.e. not estate agents).
Funnily enough, "Referral fees and commissions paid in the UK would constitute a criminal offence in the USA." - OFT 1186
In terms of improving the sales process, the second biggest category (13-14%) was to "improve communication during the process", something an online process could do very well - OFT 1186, Chapter 7
The average time from offer to exchange is 9 weeks - OFT 1186
"Fifty-six per cent of sellers and 76 per cent of buyers in our survey with delayed transactions said that they had experienced 'general stress/worry' as a result. A lack of control over what other people need to do contributes to the stress." - the transparency offered by an online process seems like it could help here - OFT 1186
"We welcome the Law Society's initiatives to develop completion-ready packs, and work on standardising documentation for conveyancing. These initiatives should go some way to streamline and speed up the conveyancing process." - OFT 1186
E-Homebuying Forum - http://www.e-homebuyingforum.com/ - "exists to provide a platform for companies who wish to modernise the home buying process". They have a blueprint for the future of homebuying [[here|http://www.e-homebuyingforum.com/documents/E-HomebuyingForumBlueprint-June2010_000.pdf]]
"We note that some of the major property portals have recently introduced clearer sign posting to properties which are labelled, for example, 'chain free' and set out the benefits of these properties to buyers." OFT 1186
"The internet offers a clear opportunity to improve coordination of transactions, particularly where there is a chain, through the better sharing of information between all parties. The Land Registry provisionally considered an e-conveyancing system, a prototype of which was launched in 2007. Called Chain Matrix, it allowed buyers, sellers and the professionals involved to keep track of the progress of their chain on the internet." OFT 1186
The Land Registry offers e-conveyancing, e-signatures on mortgages, electronic funds transfer...
Seller journey: http://docs.google.com/leaf?id=0BwQJ7FGUGIp_YzY5ZGFmMWEtYzhjMy00NDQ1LWE0NjItZmEyODRiYmU0OThj&hl=en - OFT1186
Buyer jounrey: http://docs.google.com/leaf?id=0BwQJ7FGUGIp_YTAxMzE3YjItYzc5Zi00YTdiLWJiMzktYzU5YmQzNjRhYmRk&hl=en - OFT1186
Also Darren Boucher.
The problem: want to do more with bank account than bank currently provide. Why don't banks open API's?
Aden is trying to persuade the bank to free up financial data. Pretty scary for banks. Who's liable if you give a 3rd-party your Internet Banking details? Mint and Wasabi are not regulated.
Near-Field Communication - destined for phones. Youtube: nokia nfc. If you were able to transfer cash to your phone, that would be a good way to get around the problem of timeliness. Aden thinks the death of cash will be brought about by developing countries - everyone has mobiles. HSBC doesn't have a big presence in Africa, but they do in India.
API's would open up internal innovation in banks as well as external innovation.
OAuth is a great model for opening access to data.
Trust models: PayPal not so trusted; people trust banks.
Problems with being a 3rd-party running a charge-card style thing:
* again, problems with immediacy of payments
* receipts
Transferring cash between devices is tricky.
Chained payments: you can pay two people in a sequence (PayPalX lets you do that now). E.g. paying for a second-hand car with part of it earmarked to pay off a loan from a finance company.
HSBC -> HSBC transfers are almost instantaneous, 10 - 11 seconds. For other banks, there's delays of 3-4 hours. The volumes of transfer affects the delay - end of the month is slow.
ukpayments.org.uk has a sort-code checker.
Credit Cards are fast because they take it on trust that you're going to be able to pay. That's true in the UK, not in the US and Japan. Debit cards might check, not so sure.
The point: API's are good, but banks lack trust for them. Could with a post asking for banks to provide API's.
35mins
Circa £20k. They're CA$200 / hour.
Open-sourcing this project would half the price of dev. Nitobi would promote the hell out of it if it were open-source: case-study, featuring the app. O'Reilly's Tools of Change, London Book Fair.
BookRiff (travel use-case) - Douglas & McKintyre, largest publisher in Canada - they're seeking advice at the moment. It's a Nitobi site. O'Reilly are the only group who've done tablet stuff with Nitobi.
If I'm going to collaborate on this, that would make it cheaper.
Nitobi have done in-app purchases a few times already.
They use Harvest and Co-op. They keep a Basecamp. They use GitHub. Automated build. Unit testing (some level of, bug-driven).
From now:
Brian talking to guys about the idea of an open-source framework for e-book reading.
Brian out until Tuesday, gone from the 10th - he'll introduce me to André the CEO.
Has friends doing news on travel technology
Want to put together resource about consolidating information about tech companies
Like CrunchBase.com
Edd normally works on reservation systems
TiddlyWeb struck Edd as something he could use
No experience building something in Python
They have developers, but they don't have experience with TWeb
Looking for advice, so got in touch with people at Osmosoft
Avox project seems very similar to what they want
Edd is a CSS nut
Going to see if they can get help on the group
10:15-10:30am, 16th October 2009
uploading via mobile phone network, each meter has a SIM
plans to commercialise? it's possible. in talks with one supplier
built themselves
cost control is a company with an internet-ready monitor
on the team robotics expert, mobile phone computer science expert
oPower (US) - look at the site - offer software to utilities to analyse usage data (10 utilities signed up), pioneering social comparison, r. cialdini part of the company (ruth rennie speaking to them), they produce software that produce bills, not internet interface
iMeasure - people put bills in themselves - does social comparison
A report on oPower site saying 2.2% reduction attributed to social comparison
The boomerang effect - mentioned by schulz (part of cialdini team) - use smiley faces to reward people who are doing well, it stops them reverting to the norm
Talking to ruth's technical people would be good
meeting in a month or two would be good
de hems research - eu funding - using meters, using internet
what nobody can do is combine data, as all the electrical companies are different
-- done! -- Junk mail cartoon:
Conceived in Miami, designed in San Francisco, manufactured in Malmo, binned in London
-- done! -- Nobody puts Zucky in the corner:
Facebook is like walking into a room with everyone you know in it and Zuckerberg sat masturbating in the corner (check notebook for actual wordage) (misquoting Blaine Cook)
<html><iframe src="http://spreadsheets.google.com/embeddedform?key=tuomBonxEQVWwBD5drYPlhw" width="500" height="460" frameborder="0" marginheight="0" marginwidth="0">Loading...</iframe></html>
A bit like Microsoft Access in a browser.
You can create public pages that look however you want, but it uses AJAX to load content.
The CecilyPlugin is installed in this TiddlyWiki. Cecily comes from Jeremy Ruston of Osmosoft and can be found at http://www.osmosoft.com/cecily.
To install Cecily in your TiddlyWiki, it is necessary to import these tiddlers from the above URL: CecilyPlugin, OverlayMenu, PageTemplate and any of the tiddlers tagged with CecilyMap. ViewTemplate is optional.
Some ideas about how to improve Cecily are in the [[Ideas to improve this]] tiddler.
In this installation, the following tiddlers have been created:
| Tiddler | to remove |
| CecilyPlugin | delete |
| MyMap | delete |
| BlankMap | delete |
| PageTemplate | replace with PageTemplateBackup |
| ViewTemplate | delete |
| OverlayMenu | delete |
/***
|''Name:''|CecilyPlugin|
|''Description:''|A zooming user interface for TiddlyWiki|
|''Author:''|Jeremy Ruston (jeremy (at) osmosoft (dot) com)|
|''Source:''|http://svn.tiddlywiki.org/Trunk/contributors/JeremyRuston/plugins/CecilyPlugin.js|
|''CodeRepository:''|http://svn.tiddlywiki.org/Trunk/contributors/JeremyRuston/plugins/CecilyPlugin.js|
|''Version:''|0.0.9|
|''Status:''|Under Development|
|''Date:''|July 20, 2008|
|''Comments:''|Please make comments at http://groups.google.co.uk/group/TiddlyWikiDev|
|''License:''|BSD|
|''~CoreVersion:''|2.4.0|
***/
//{{{
// Ensure that the plugin is only installed once.
if(!version.extensions.CecilyPlugin) {
version.extensions.CecilyPlugin = {installed:true};
//-----------------------------------------------------------------------------------
// Geometry classes
//-----------------------------------------------------------------------------------
function interpolateLinear(t,a,b) {
return a + (b - a) * t;
}
function interpolateQuad(t,a,b,c) {
return Math.pow(1 - t,2) * a + 2 * t * (1 -t) * b + t * t * c;
}
// Point class {x:,y:}
function Point(x,y) {
if(x instanceof Point) {
this.x = x.x;
this.y = x.y;
} else {
this.x = x;
this.y = y;
}
}
// Rectangle class {x:,y:,w:,h:} (w and h are both set to zero for empty rectangles)
function Rect(x,y,w,h) {
if(x instanceof Rect) {
this.x = x.x;
this.y = x.y;
this.w = x.w;
this.h = x.h;
} else {
this.x = x ? x : 0;
this.y = y ? y : 0;
this.w = w ? w : 0;
this.h = h ? h : 0;
}
}
// Determines if this rectangle is empty
Rect.prototype.isEmpty = function() {
return !this.w || !this.h;
}
// Returns the smallest rectangle that contains both this and the source rectangles
Rect.prototype.union = function(src) {
if(this.isEmpty())
return new Rect(src);
if(src.isEmpty())
return new Rect(this);
var r = new Rect(Math.min(this.x,src.x),Math.min(this.y,src.y));
r.w = Math.max(this.x+this.w-r.x,src.x+src.w-r.x);
r.h = Math.max(this.y+this.h-r.y,src.y+src.h-r.y);
return r;
}
// Determines if the source rectangle is completely contained within this rectangle
Rect.prototype.contains = function(src) {
return (src.x > this.x) && ((this.x+this.w) > (src.x+src.w))
&& (src.y > this.y) && ((this.y+this.h) > (src.y+src.h));
}
// Interpolates between this (t=0) and the source retangle (t=1)
Rect.prototype.interpolateLinear = function(t,src) {
return new Rect(interpolateLinear(t,this.x,src.x), interpolateLinear(t,this.y,src.y),
interpolateLinear(t,this.w,src.w), interpolateLinear(t,this.h,src.h));
}
// Interpolates between this (t=0) and the source rectangle (t=1) and a passing rectangle (t=0.5)
Rect.prototype.interpolateQuad = function(t,src,passing) {
return new Rect(interpolateQuad(t,this.x,passing.x,src.x), interpolateQuad(t,this.y,passing.y,src.y),
interpolateQuad(t,this.w,passing.w,src.w), interpolateQuad(t,this.h,passing.h,src.h));
}
// Scales a rectangle around it's centre
Rect.prototype.scale = function(scale) {
var w = this.w * scale;
var h = this.h * scale;
return new Rect(this.x - (w-this.w)/2,this.y - (h-this.h)/2,w,h);
}
// Returns the midpoint of a rectangle
Rect.prototype.midPoint = function() {
return new Point(this.x + this.w/2, this.y + this.h/2);
}
//-----------------------------------------------------------------------------------
// Generic helper functions
//-----------------------------------------------------------------------------------
// Given a point in the coordinates of a target element, compute the coordinates relative to a specified base element
function normalisePoint(base,target,pt) {
var e = target;
var parent = target.offsetParent;
var r = new Point(pt.x,pt.y);
while(e !== base && parent) {
r.x += parent.offsetLeft;
r.y += parent.offsetTop;
e = parent;
parent = e.offsetParent;
}
if(e == base)
return r;
else
return null;
}
// Checks which of an array of classes are applied to a given element. Returns an array of the classes that are found
function hasAnyClass(e,classNames)
{
var classes = e.className ? e.className.split(" ") : [];
var results = [];
for(var t=0; t<classNames.length; t++) {
if(classes.indexOf(classNames[t]) != -1) {
results.push(classNames[t]);
}
}
return results;
}
//-----------------------------------------------------------------------------------
// Slider control
//-----------------------------------------------------------------------------------
// The slider control is constructed with a sliderInfo object that can contain the following keys:
// place: DOM node to which the slider control is appended as a new child
// min: Minimum value (integer)
// max: Maximum value (integer)
// getterTransform: function to convert internal slider values when reading them
// setterTransform: function to convert to internal slider value when setting them
// onChange: function(value) called when the slider moves
function SliderControl(sliderInfo) {
merge(this,sliderInfo);
if(!this.getterTransform)
this.getterTransform = function(x) {return x;};
if(!this.setterTransform)
this.setterTransform = function(x) {return x;};
this.slider = createTiddlyElement(this.place,"input");
this.slider.type = "range";
this.slider.min = this.min;
this.slider.max = this.max;
this.slider.style["-webkit-appearance"] = "slider-horizontal";
var me = this;
var handler = function (ev) {
me.onChange(me.getterTransform(parseInt(me.slider.value,10)));
};
this.slider.oninput = handler;
this.slider.onchange = handler;
}
SliderControl.prototype.set = function(value) {
var n = this.setterTransform(value).toString();
if(this.slider.value != n)
this.slider.value = n;
};
//-----------------------------------------------------------------------------------
// cecilyTransform mechanism
//-----------------------------------------------------------------------------------
// Set up an element to be transformed
function cecilyTransform(element)
{
addClass(element,"cecilyTransform");
element.cecilyTransform = this;
this.element = element;
this.originalWidth = element.offsetWidth;
this.bounds = new Rect(0,0,this.originalWidth,element.offsetHeight);
this.rotate = 0;
this.enlarge = 1;
}
// Applies any of these transformations over the top of prevailing ones
// transforms.bounds = Rect() of bounds of element
// transforms.rotate = numeric radian rotation applied to element around centre
// transforms.enlarge = numeric scale factor applied after sizing
cecilyTransform.prototype.transform = function(transforms) {
if(transforms.bounds !== undefined)
this.bounds = new Rect(transforms.bounds);
if(transforms.rotate !== undefined)
this.rotate = transforms.rotate;
if(transforms.enlarge !== undefined)
this.enlarge = transforms.enlarge;
var s = this.bounds.w / this.originalWidth;
this.element.style[Cecily.cssTransform] =
"translate(-50%,-50%) " +
"scale(" + s + "," + s + ") " +
"translate(50%,50%) " +
"translate(" + this.bounds.x / s + "px," + this.bounds.y / s + "px) " +
"rotate(" + this.rotate + "rad) " +
"scale(" + this.enlarge + ")";
};
// Updates the bounds to account for text flow
cecilyTransform.prototype.getFlowedBounds = function() {
this.bounds.h = this.element.offsetHeight * (this.bounds.w / this.element.offsetWidth);
return new Rect(this.bounds);
};
//-----------------------------------------------------------------------------------
// Zoom macro
//-----------------------------------------------------------------------------------
config.macros.cecilyZoom = {};
config.macros.cecilyZoom.handler = function(place,macroName,params,wikifier,paramString,tiddler) {
var zoomElem = createTiddlyElement(place,"span",null,"cecilyLabel cecilyZoom","zoom ");
var me = this;
zoomElem.sliderControl = new SliderControl({
place: place,
min: 0,
max: 100,
getterTransform: function(slider) {
return Math.pow(Math.E,(slider/100)*12-6);
},
setterTransform: function(value) {
var n = ((Math.log(value)+6)/12)*100;
n = Math.min(100,Math.max(0,Math.floor(n + 0.5)));
return n;
},
onChange: function(value) {
if(cecily) {
var w = cecily.frame.offsetWidth;
var h = cecily.frame.offsetHeight;
var cx = cecily.view.x + cecily.view.w/2;
var cy = cecily.view.y + cecily.view.h/2;
var newView = new Rect(0,0,w / value,h / value);
newView.x = cx - newView.w/2;
newView.y = cy - newView.h/2;
cecily.setView(newView);
}
}
});
}
config.macros.cecilyZoom.propagate = function(scale) {
var zoomers = document.getElementsByClassName("cecilyZoom");
for(var t = 0; t < zoomers.length; t++) {
zoomers[t].sliderControl.set(scale);
}
}
//-----------------------------------------------------------------------------------
// Zoom All macro
//-----------------------------------------------------------------------------------
config.macros.cecilyZoomAll = {};
config.macros.cecilyZoomAll.handler = function(place,macroName,params,wikifier,paramString,tiddler) {
createTiddlyButton(place,"zoom everything","Zoom out to see everything",function(ev) {
if(cecily)
cecily.scrollToAllTiddlers();
});
}
//-----------------------------------------------------------------------------------
// Switch background macro
//-----------------------------------------------------------------------------------
config.macros.cecilyBackground = {
};
config.macros.cecilyBackground.handler = function(place,macroName,params,wikifier,paramString,tiddler) {
if(cecily) {
createTiddlyElement(place,"span",null,"cecilyLabel","background ");
var onchange = function(ev) {
var sel = this.options[this.selectedIndex].value;
if(sel != cecily.background) {
cecily.setBackground(sel);
}
};
var options = [];
for(var t in Cecily.backgrounds) {
options.push({name: t, caption: Cecily.backgrounds[t].title});
}
var d = createTiddlyDropDown(place,onchange,options,cecily.background);
addClass(d,"cecilyBackground");
}
};
config.macros.cecilyBackground.propagate = function(background) {
var backgrounders = document.getElementsByClassName("cecilyBackground");
for(var k=0; k<backgrounders.length; k++) {
var b = backgrounders[k];
for(var s=0; s<b.options.length; s++) {
if(b.options[s].value === background && b.selectedIndex !== s)
b.selectedIndex = s;
}
}
};
//-----------------------------------------------------------------------------------
// Switch map macro
//-----------------------------------------------------------------------------------
config.macros.cecilyMap = {
};
config.macros.cecilyMap.handler = function(place,macroName,params,wikifier,paramString,tiddler) {
if(cecily) {
createTiddlyElement(place,"span",null,"cecilyLabel","map ");
var onchange = function(ev) {
var sel = this.options[this.selectedIndex].value;
if(sel != cecily.mapTitle) {
cecily.setMap(sel);
}
cecily.scrollToAllTiddlers();
};
var options = [];
var mapTiddlers = store.getTaggedTiddlers("cecilyMap")
for(var t=0; t<mapTiddlers.length; t++) {
options.push({name: mapTiddlers[t].title, caption: mapTiddlers[t].title});
}
var d = createTiddlyDropDown(place,onchange,options,cecily.mapTitle);
addClass(d,"cecilyMap");
}
};
config.macros.cecilyMap.propagate = function(map) {
var mappers = document.getElementsByClassName("cecilyMap");
for(var k=0; k<mappers.length; k++) {
var m = mappers[k];
for(var s=0; s<m.options.length; s++) {
if(m.options[s].value === map && m.selectedIndex !== s)
m.selectedIndex = s;
}
}
};
//-----------------------------------------------------------------------------------
// Cecily main class
//-----------------------------------------------------------------------------------
function Cecily()
{
this.background = config.options.txtCecilyBackground ? config.options.txtCecilyBackground : "plain";
this.mapTitle = config.options.txtCecilyMap ? config.options.txtCecilyMap : "MyMap";
this.drag = null;
this.map = null;
}
Cecily.prototype.createDisplay = function() {
this.overlayMenu = document.getElementById("overlayMenu");
this.addEventHandler(this.overlayMenu,"mouseout",this.onMouseOutOverlay,false);
this.loadMap(this.mapTitle);
this.container = document.getElementById(story.containerId());
this.frame = this.container.parentNode;
addClass(this.frame,"cecily");
this.canvas = createTiddlyElement(null,"canvas",null,"cecilyCanvas");
this.frame.insertBefore(this.canvas,this.frame.firstChild);
this.setViewSize();
this.setView(new Rect(0,0,25000,12000));
this.initScroller();
var me = this;
this.addEventHandler(window,"resize",this.onWindowResize,false);
this.addEventHandler(window,"mousewheel",this.onMouseWheel,true);
this.addEventHandler(document,"click",this.onMouseClickBubble,false);
this.addEventHandler(document,"dblclick",this.onMouseDoubleClickBubble,false);
this.addEventHandler(document,"mousedown",this.onMouseDownCapture,true);
this.addEventHandler(document,"mousemove",this.onMouseMoveCapture,true);
this.addEventHandler(document,"mouseup",this.onMouseUpCapture,true);
this.defaultTiddler = null;
window.setTimeout(function() {me.scrollToTiddler(me.defaultTiddler);},10);
}
Cecily.prototype.setViewSize = function() {
var h = findWindowHeight();
this.frame.style.height = h + "px";
this.canvas.width = this.frame.offsetWidth;
this.canvas.height = this.frame.offsetHeight;
}
Cecily.prototype.addEventHandler = function(element,type,handler,capture) {
var me = this;
element.addEventListener(type,function (ev) {
if(ev.offsetX === undefined)
ev.offsetX = ev.clientX;
if(ev.offsetY === undefined)
ev.offsetY = ev.clientY;
if(ev.toElement === undefined)
ev.toElement = ev.relatedTarget;
return handler.call(me,ev);
},capture);
}
Cecily.prototype.onWindowResize = function(ev) {
this.setViewSize();
this.drawBackground();
return false;
}
Cecily.prototype.onMouseWheel = function(ev) {
var newView = new Rect(this.view);
newView.x -= (ev.wheelDeltaX/120) * (this.view.w/16);
newView.y -= (ev.wheelDeltaY/120) * (this.view.w/16);
this.setView(newView);
return false;
};
Cecily.prototype.onMouseClickBubble = function(ev) {
var tiddler = story.findContainingTiddler(ev.target);
if(tiddler && this.drag === null && hasAnyClass(ev.target,["tiddlyLink","toolbar","title","tagged"]).length == 0) {
// The next bit is equivalent to tiddler.parentNode.insertBefore(tiddler,null); but avoids moving
// the element that was clicked on
while(tiddler.nextSibling) {
tiddler.parentNode.insertBefore(tiddler.nextSibling,tiddler);
}
this.scrollToTiddler(tiddler);
}
return true;
};
Cecily.prototype.onMouseDoubleClickBubble = function(ev) {
this.showOverlayMenu(new Point(ev.offsetX,ev.offsetY));
};
Cecily.prototype.onMouseDownCapture = function(ev) {
for(var d=0; d<Cecily.draggerList.length; d++) {
var dragger = Cecily.draggers[Cecily.draggerList[d]];
if(dragger.isDrag(this,ev.target,ev)) {
this.drag = {dragger: dragger};
dragger.dragDown(this,ev.target,ev);
break;
}
}
if(this.drag !== null) {
ev.stopPropagation();
ev.preventDefault();
return false;
}
};
Cecily.prototype.onMouseMoveCapture = function(ev) {
if(this.drag) {
this.drag.dragger.dragMove(this,ev.target,ev);
ev.stopPropagation();
ev.preventDefault();
return false;
}
};
Cecily.prototype.onMouseUpCapture = function(ev) {
if(this.drag) {
this.drag.dragger.dragUp(this,ev.target,ev);
this.drag = null;
ev.stopPropagation();
ev.preventDefault();
return false;
}
};
Cecily.draggers = {};
Cecily.draggerList = ["tiddlerDragger","tiddlerResizer","backgroundDragger"];
Cecily.draggers.tiddlerDragger = {
isDrag: function(cecily,target,ev) {
return hasClass(target,"toolbar") || hasClass(target,"title");
},
dragDown: function(cecily,target,ev) {
var tiddler = story.findContainingTiddler(target);
tiddler.parentNode.insertBefore(tiddler,null);
cecily.drag.tiddler = tiddler;
cecily.drag.tiddlerTitle = tiddler.getAttribute("tiddler");
cecily.drag.lastPoint = normalisePoint(cecily.frame,target,new Point(ev.offsetX,ev.offsetY));
addClass(tiddler,"drag");
},
dragMove: function(cecily,target,ev) {
var dragThis = normalisePoint(cecily.frame,target,new Point(ev.offsetX,ev.offsetY));
if(dragThis) {
var s = cecily.frame.offsetWidth/cecily.view.w;
console.log(cecily.drag.tiddler.cecilyTransform.bounds.x);
var pos = new Rect(cecily.drag.tiddler.cecilyTransform.bounds.x + (dragThis.x - cecily.drag.lastPoint.x) / s,
cecily.drag.tiddler.cecilyTransform.bounds.y + (dragThis.y - cecily.drag.lastPoint.y) / s,
cecily.drag.tiddler.cecilyTransform.bounds.w, cecily.drag.tiddler.cecilyTransform.bounds.h);
cecily.drag.tiddler.cecilyTransform.transform({bounds: pos});
cecily.drag.lastPoint = dragThis;
}
},
dragUp: function(cecily,target,ev) {
removeClass(cecily.drag.tiddler,"drag");
cecily.updateTiddlerPosition(cecily.drag.tiddlerTitle,cecily.drag.tiddler);
}
};
Cecily.draggers.tiddlerResizer = {
isDrag: function(cecily,target,ev) {
return findRelated(target,"tagged","className","parentNode") !== null;
},
dragDown: function(cecily,target,ev) {
var tiddler = story.findContainingTiddler(target);
tiddler.parentNode.insertBefore(tiddler,null);
cecily.drag.tiddler = tiddler;
cecily.drag.tiddlerTitle = tiddler.getAttribute("tiddler");
cecily.drag.startPoint = normalisePoint(cecily.frame,target,new Point(ev.offsetX,ev.offsetY));
cecily.drag.startWidth = tiddler.cecilyTransform.bounds.w;
addClass(tiddler,"drag");
},
dragMove: function(cecily,target,ev) {
var s = cecily.frame.offsetWidth/cecily.view.w;
var dragThis = normalisePoint(cecily.frame,target,new Point(ev.offsetX,ev.offsetY));
if(dragThis) {
var pos = new Rect(cecily.drag.tiddler.cecilyTransform.bounds);
pos.w = cecily.drag.startWidth + (dragThis.x - cecily.drag.startPoint.x) / s;
if(pos.w < 0.01)
pos.w = 0.01;
cecily.drag.tiddler.cecilyTransform.transform({bounds: pos});
}
},
dragUp: function(cecily,target,ev) {
removeClass(cecily.drag.tiddler,"drag");
cecily.updateTiddlerPosition(cecily.drag.tiddlerTitle,cecily.drag.tiddler);
}
};
Cecily.draggers.backgroundDragger = {
isDrag: function(cecily,target,ev) {
return target === cecily.canvas;
},
dragDown: function(cecily,target,ev) {
cecily.drag.lastPoint = {x: ev.offsetX, y: ev.offsetY};
},
dragMove: function(cecily,target,ev) {
var s = cecily.frame.offsetWidth/cecily.view.w;
var newView = new Rect(cecily.view);
newView.x -= (ev.offsetX - cecily.drag.lastPoint.x)/s;
newView.y -= (ev.offsetY - cecily.drag.lastPoint.y)/s;
cecily.drag.lastPoint = {x: ev.offsetX, y: ev.offsetY};
cecily.setView(newView);
},
dragUp: function(cecily,target,ev) {
}
};
Cecily.prototype.showOverlayMenu = function(pos)
{
this.overlayMenu.style.display = "block";
var overlayPos = new Rect(pos.x - this.overlayMenu.offsetWidth/2,pos.y - this.overlayMenu.offsetHeight/2,
this.overlayMenu.offsetWidth,this.overlayMenu.offsetHeight);
var w = this.frame.offsetWidth;
var h = this.frame.offsetHeight;
if(overlayPos.w > w || overlayPos.h > h) {
overlayPos = overlayPos.scale(Math.min(w/overlayPos.w,h/overlayPos.h));
}
if(overlayPos.x < 0)
overlayPos.x = 0;
if(overlayPos.y < 0)
overlayPos.y = 0;
if(overlayPos.x + overlayPos.w > w)
overlayPos.x = w - overlayPos.w;
if(overlayPos.y + overlayPos.h > h)
overlayPos.y = h - overlayPos.h;
var scale = overlayPos.h / this.overlayMenu.offsetHeight;
this.overlayMenu.style[Cecily.cssTransform] = "scale(" + scale + "," + scale + ")";
this.overlayMenu.style.left = overlayPos.x + "px";
this.overlayMenu.style.top = overlayPos.y + "px";
this.overlayMenu.style.opacity = "0.9";
};
Cecily.prototype.onMouseOutOverlay = function(ev)
{
if(findRelated(ev.toElement,"overlayMenu","id","parentNode") == null) {
this.overlayMenu.style.opacity = "0.0";
this.overlayMenu.style.display = "none";
}
};
// Display a given tiddler with a given template. If the tiddler is already displayed but with a different
// template, it is switched to the specified template. If the tiddler does not exist, and if server hosting
// custom fields were provided, then an attempt is made to retrieve the tiddler from the server
// srcElement - reference to element from which this one is being opened -or-
// special positions "top", "bottom"
// tiddler - tiddler or title of tiddler to display
// template - the name of the tiddler containing the template -or-
// one of the constants DEFAULT_VIEW_TEMPLATE and DEFAULT_EDIT_TEMPLATE -or-
// null or undefined to indicate the current template if there is one, DEFAULT_VIEW_TEMPLATE if not
// animate - whether to perform animations
// customFields - an optional list of name:"value" pairs to be assigned as tiddler fields (for edit templates)
// toggle - if true, causes the tiddler to be closed if it is already opened
Cecily.prototype.displayTiddler = function(superFunction,args) {
var tiddler = args[1];
var srcElement = args[0];
args[0] = "bottom"; // srcElement to disable animation and scrolling
var title = (tiddler instanceof Tiddler) ? tiddler.title : tiddler;
var tiddlerElemBefore = story.getTiddler(title);
superFunction.apply(story,args);
var tiddlerElem = story.getTiddler(title);
if(!tiddlerElem)
return;
var pos = this.getTiddlerPosition(title,srcElement);
var transform = new cecilyTransform(tiddlerElem);
transform.transform({bounds: pos});
this.updateTiddlerPosition(title,tiddlerElem);
if(!startingUp) {
if(tiddlerElem.nextSibling) { // Move tiddler to the bottom of the Z-order if it's not already there
tiddlerElem.parentNode.insertBefore(tiddlerElem,null);
}
this.scrollToTiddler(title);
}
this.defaultTiddler = tiddlerElem;
};
// Load the current map from a named tiddler
Cecily.prototype.loadMap = function(title) {
this.map = {};
var mapText = store.getTiddlerText(title,"");
var positionRE = /^(\S+)\s(-?[0-9\.E]+)\s(-?[0-9\.E]+)\s(-?[0-9\.E]+)\s(-?[0-9\.E]+)$/mg;
do {
var match = positionRE.exec(mapText);
if(match) {
var title = decodeURIComponent(match[1]);
this.map[title] = {
x: parseFloat(match[2]),
y: parseFloat(match[3]),
w: parseFloat(match[4]),
h: parseFloat(match[5])
};
}
} while(match);
}
// Save the current map into a named tiddler
Cecily.prototype.saveMap = function(title) {
var mapTiddler = store.getTiddler(title);
if((mapTiddler == null) || (mapTiddler.isTagged("cecilyMap"))) {
var text = [];
for(var t in this.map) {
var m = this.map[t];
text.push(encodeURIComponent(t) + " " + Math.floor(m.x) + " " + Math.floor(m.y) + " " + Math.floor(m.w) + " " + Math.floor(m.h));
}
text.sort();
store.saveTiddler(title,title,text.join("\n"),"Cecily");
autoSaveChanges(null,[mapTiddler]);
}
}
// Gets the Rect() position of a named tiddler
Cecily.prototype.getTiddlerPosition = function(title,srcElement) {
var p = this.map[title];
if(p)
return new Rect(p.x,p.y,p.w,p.h);
else {
this.nextPos = this.nextPos ? this.nextPos + 250 : 250;
return new Rect(this.nextPos,500,225,250);
}
}
// Updates the position of a named tiddler into the current map
Cecily.prototype.updateTiddlerPosition = function(title,tiddlerElem) {
this.map[title] = tiddlerElem.cecilyTransform.getFlowedBounds();
this.saveMap(this.mapTitle);
}
// Switch to a new map
Cecily.prototype.setMap = function(title)
{
this.mapTitle = title;
config.options.txtCecilyMap = title;
saveOptionCookie("txtCecilyMap");
this.loadMap(title);
var me = this;
story.forEachTiddler(function(tiddler,elem) {
var pos = me.getTiddlerPosition(tiddler);
elem.cecilyTransform.transform({bounds: pos});
});
this.drawBackground();
config.macros.cecilyMap.propagate(title);
}
// Moves the viewport to accommodate the specified rectangle
Cecily.prototype.setView = function(newView) {
var w = this.frame.offsetWidth;
var h = this.frame.offsetHeight;
var centre = newView.midPoint();
this.view = new Rect(newView);
if((w/h) > (newView.w/newView.h)) {
this.view.w = newView.h * (w/h);
} else {
this.view.h = newView.w * (h/w);
}
this.view.x = centre.x - this.view.w/2;
this.view.y = centre.y - this.view.h/2;
var s = w/this.view.w;
var transform = "scale(" + s + ") translate(" + -this.view.x + "px," + -this.view.y + "px)";
this.container.style[Cecily.cssTransform] = transform;
config.macros.cecilyZoom.propagate(s);
this.drawBackground();
};
Cecily.prototype.startHightlight = function(elem) {
var me = this;
var animationStart = new Date();
var animationDuration = 3 * 1000;
var highlight = {};
var highlightElem = findRelated(elem.firstChild,"viewer","className","nextSibling");
highlight.tick = function() {
if(!highlightElem.parentNode)
return false;
var now = new Date();
var t = (now - animationStart) / animationDuration;
if(t < 1) {
var p = (Math.sin(t*Math.PI*4 + Math.PI/2)+1)/2;
highlightElem.style.backgroundColor = (new RGB("#ffff88")).mix(new RGB("#ffffff"),(p+1)/2).toString();
return true;
} else {
highlightElem.style.backgroundColor = "";
return false;
}
}
if(highlightElem)
anim.startAnimating(highlight);
};
Cecily.prototype.scrollToAllTiddlers = function() {
var currRect = null;
story.forEachTiddler(function (title,tiddlerElem) {
var tiddlerRect = new Rect(tiddlerElem.cecilyTransform.getFlowedBounds());
if(!currRect)
currRect = tiddlerRect;
else
currRect = tiddlerRect.union(currRect);
});
if(currRect)
this.startScroller([currRect.scale(1.2)]);
};
// Highlight a particular tiddler and scroll it into view
// tiddler - title of tiddler or reference to tiddlers DOM element
Cecily.prototype.scrollToTiddler = function(tiddler) {
var tiddlerElem = typeof tiddler == "string" ? story.getTiddler(tiddler) : tiddler;
if(tiddlerElem) {
this.startHightlight(tiddlerElem);
var targetRect = new Rect(tiddlerElem.cecilyTransform.getFlowedBounds());
if(this.view.contains(targetRect)) {
this.startScroller([targetRect.scale(1.2)]);
} else {
var passingRect = this.view.union(targetRect);
this.startScroller([passingRect.scale(1.1),targetRect.scale(1.2)]);
}
}
}
Cecily.prototype.initScroller = function() {
var me = this;
this.scroller = {
scrolling: false
};
var s = this.scroller;
me.scroller.tick = function() {
var now = new Date();
var t = (now - s.animationStart) / s.animationDuration;
if(t > 1)
t = 1;
switch(s.rectList.length) {
case 2:
me.setView(s.rectList[0].interpolateLinear(t,s.rectList[1]));
break;
case 3:
me.setView(s.rectList[0].interpolateQuad(t,s.rectList[2],s.rectList[1]));
break;
}
if(t == 1) {
s.scrolling = false;
return false;
} else
return true;
};
};
Cecily.prototype.startScroller = function(rectList,duration) { // One or more rectangles to scroll to in turn
var s = this.scroller;
s.rectList = [this.view];
for(var r = 0; r < Math.min(rectList.length,2); r++)
s.rectList.push(rectList[r]);
s.animationStart = new Date();
s.animationDuration = duration ? duration : 0.75 * 1000;
s.currRect = 0;
if(!s.scrolling) {
s.scrolling = true;
anim.startAnimating(s);
}
};
Cecily.prototype.setBackground = function(background) {
cecily.background = background;
config.options.txtCecilyBackground = background;
saveOptionCookie("txtCecilyBackground");
cecily.drawBackground();
config.macros.cecilyBackground.propagate(background);
};
Cecily.prototype.drawBackground = function() {
var b = Cecily.backgrounds[this.background];
if(b) {
b.drawBackground(this.canvas,this.view);
} else {
var ctx = this.canvas.getContext('2d');
ctx.fillStyle = "#cccccc";
ctx.fillRect(0, 0, this.canvas.width, this.canvas.height);
}
};
//-----------------------------------------------------------------------------------
// Background plumbing and generators
//-----------------------------------------------------------------------------------
Cecily.backgrounds = {};
Cecily.backgrounds.plain = {
title: "Plain",
description: "Plain",
drawBackground: function(canvas,view) {
var w = canvas.width;
var h = canvas.height;
var ctx = canvas.getContext('2d');
ctx.fillStyle = "#aaaacc";
ctx.fillRect(0, 0, w, h);
}
};
Cecily.backgrounds.fractal = {
title: "Fractal",
description: "Fractal cracks",
drawBackground: function(canvas,view) {
var w = canvas.width;
var h = canvas.height;
var scale = w/view.w;
var ctx = canvas.getContext('2d');
ctx.fillStyle = "#cc8888";
ctx.fillRect(0, 0, w, h);
var Turtle = function Turtle(x,y,direction) {
this.x = x ? x : 0;
this.y = y ? y : 0;
this.direction = direction ? direction : 0;
};
Turtle.prototype.line = function(d) {
this.x += Math.sin(this.direction) * d;
this.y -= Math.cos(this.direction) * d;
};
Turtle.prototype.turn = function(a) {
this.direction += a;
};
// Gosper curve as a series of angles to turn (in degrees anti clockwise, for humans)
var fractalPath = [0,300,240,60,120,0,60]; // [0,-60,60,-240,240];
// Work out the overall angle and length of the curve
var turtle = new Turtle(0,0,0);
for(var t=0; t<fractalPath.length; t++) {
turtle.turn(fractalPath[t] / 180 * Math.PI);
turtle.line(1);
}
var fractalAngle = Math.atan2(turtle.y,turtle.x);
var fractalLength = Math.sqrt(Math.pow(turtle.x,2)+Math.pow(turtle.y,2));
// Recursive function to draw a generation of the curve
var drawLeg = function drawLeg(p1,p2,depth) {
// Work out the angle and length required
var legLength = Math.sqrt(Math.pow(p2.x-p1.x,2)+Math.pow(p2.y-p1.y,2));
var legAngle = Math.atan2(p2.y-p1.y,p2.x-p1.x);
// Initialise the turtle
var legScale = legLength / fractalLength;
var turtle = new Turtle(p1.x,p1.y,legAngle);
turtle.turn(-fractalAngle);
// Step through the curve
for(var t=0; t<fractalPath.length; t++) {
var prevX = turtle.x;
var prevY = turtle.y;
turtle.turn(fractalPath[t] / 180 * Math.PI);
turtle.line(legScale);
if(depth > 0)
drawLeg(new Point(prevX,prevY),new Point(turtle.x,turtle.y),depth - 1);
ctx.lineTo(turtle.x,turtle.y);
}
}
var drawCircle = function(x,y,r) {
var radgrad = ctx.createRadialGradient(x,y,r,x-r/3,y-r/3,1);
radgrad.addColorStop(0, '#8888cc');
radgrad.addColorStop(0.9, '#f0f0ff');
radgrad.addColorStop(1, '#ffffff');
ctx.fillStyle = radgrad;
ctx.beginPath();
ctx.arc(x,y,r,0,2*Math.PI,0);
ctx.fill();
}
var scale = w/view.w;
// Get the position of the canvas on the plane
var px = view.x + view.w/2 - (w/2) / scale;
var py = view.y + view.h/2 - (h/2) / scale;
var pw = w / scale;
var ph = h / scale;
// Map coordinates
var p1 = new Point(-430,11);
var p2 = new Point(1530,674);
var x = 100;
var y = 100;
var r = 500;
// To 0..1,0..1 for viewport
p1.x = (p1.x - px)/pw;
p1.y = (p1.y - py)/ph;
p2.x = (p2.x - px)/pw;
p2.y = (p2.y - py)/ph;
x = (x - px)/pw;
y = (y - py)/ph;
r = r / pw;
// To x,y for canvas
x = x * w;
y = y * h;
r = r * w;
p1.x = p1.x * w;
p1.y = p1.y * h;
p2.x = p2.x * w;
p2.y = p2.y * h;
// Draw the circle
drawCircle(x,y,r);
// Draw the curve
ctx.strokeStyle = "#0ff";
ctx.lineWidth = 1;
ctx.beginPath();
ctx.moveTo(p1.x,p1.y);
drawLeg(p1,p2,3);
ctx.stroke();
// Draw the curve
ctx.strokeStyle = "#F00";
ctx.lineWidth = 1;
ctx.beginPath();
ctx.moveTo(p1.x,p1.y);
drawLeg(p1,p2,2);
ctx.stroke();
// Draw the curve
ctx.strokeStyle = "#Ff0";
ctx.lineWidth = 1;
ctx.beginPath();
ctx.moveTo(p1.x,p1.y);
drawLeg(p1,p2,1);
ctx.stroke();
// Draw the curve
ctx.strokeStyle = "#F0f";
ctx.lineWidth = 1;
ctx.beginPath();
ctx.moveTo(p1.x,p1.y);
drawLeg(p1,p2,0);
ctx.stroke();
}
};
Cecily.backgrounds.experimental = {
title: "Experimental",
description: "Experimental scratchpad",
drawBackground: function(canvas,view) {
var w = canvas.width;
var h = canvas.height;
var ctx = canvas.getContext('2d');
ctx.fillStyle = "#cccccc";
ctx.fillRect(0, 0, w, h);
var drawCircle = function(x,y,r) {
var radgrad = ctx.createRadialGradient(x,y,r,x-r/3,y-r/3,1);
radgrad.addColorStop(0, '#8888cc');
radgrad.addColorStop(0.9, '#f0f0ff');
radgrad.addColorStop(1, '#ffffff');
ctx.fillStyle = radgrad;
ctx.beginPath();
ctx.arc(x,y,r,0,2*Math.PI,0);
ctx.fill();
}
var scale = w/view.w;
var px = view.x + view.w/2 - (w/2) / scale;
var py = view.y + view.h/2 - (h/2) / scale;
var pw = w / scale;
var ph = h / scale;
// Map coordinates
var x = 100;
var y = 100;
var r = 500;
// To 0..1,0..1 for viewport
x = (x - px)/pw;
y = (y - py)/ph;
r = r / pw;
// To x,y for canvas
x = x * w;
y = y * h;
r = r * w;
drawCircle(x,y,r);
ctx.drawWindow(window, 0, 0, 100, 200, "rgb(0,0,0)");
}
};
Cecily.backgrounds.honeycomb = {
title: "Honeycomb",
description: "Honeycomb balls",
drawBackground: function(canvas,view) {
var w = canvas.width;
var h = canvas.height;
var scale = w/view.w;
var t = ((Math.log(scale)+6)/12);
t = Math.max(t,0);
t = Math.min(t,1);
var ctx = canvas.getContext('2d');
ctx.fillStyle = "#cccc88";
ctx.fillRect(0, 0, w, h);
var drawCircle = function(x,y,r,c) {
ctx.fillStyle = c ? c : '#bbbb66';
ctx.beginPath();
ctx.arc(x,y,r,0,2*Math.PI,0);
ctx.fill();
}
var modulo = function(num,denom) {
return num-Math.floor(num/denom)*denom;
}
var gapX = 2000 * scale;
var yscale = Math.sin(Math.PI/3)*2;
var gapY = gapX * yscale;
var radius = 600 * scale;
if(gapX < 15) {
gapX = 15;
gapY = 15;
}
if(radius < 7) {
radius = 7;
}
for(var y = -modulo(view.y * scale,gapY) - gapY; y < h + gapY; y += gapY) {
for(var x = -modulo(view.x * scale,gapX) - gapX; x < w + gapX; x += gapX) {
drawCircle(x,y,radius);
drawCircle(x + gapX/2,y + gapY/2,radius);
/*
drawCircle(x,y,radius/2,"#555577");
drawCircle(x + gapX/4,y + gapY/4,radius/2,"#555577");
drawCircle(x + gapX/2,y,radius/2,"#555577");
drawCircle(x + gapX/4,y - gapY/4,radius/2,"#555577");
drawCircle(x - gapX/4,y + gapY/4,radius/2,"#555577");
drawCircle(x - gapX/2,y,radius/2,"#555577");
drawCircle(x - gapX/4,y - gapY/4,radius/2,"#555577");
drawCircle(x + gapX/2,y + gapY/2,radius/2,"#555577");
drawCircle(x + gapX,y + gapY/2,radius/2,"#555577");
*/
}
}
}
};
//-----------------------------------------------------------------------------------
// Utilities for class substitution
//-----------------------------------------------------------------------------------
function overrideMethod(instance,method,override)
{
var oldFunction = instance[method];
instance[method] = function () {return override(oldFunction,arguments);};
}
//-----------------------------------------------------------------------------------
// Initialisation code (executed during loading of plugin)
//-----------------------------------------------------------------------------------
function runCecily()
{
setStylesheet(store.getRecursiveTiddlerText(tiddler.title + "##StyleSheet"),"cecily");
window.cecily = new Cecily();
overrideMethod(story,"displayTiddler",function(superFunction,arguments) {cecily.displayTiddler(superFunction,arguments);});
store.addNotification("PageTemplate",function () {cecily.createDisplay();});
}
Cecily.cssTransform = null;
if(document.body.style['-webkit-transform'] !== undefined)
Cecily.cssTransform = '-webkit-transform';
if(document.body.style['MozTransform'] !== undefined)
Cecily.cssTransform = 'MozTransform';
if(Cecily.cssTransform) {
runCecily();
} else {
alert("ProjectCecily currently only works on Safari 3.1, Firefox 3.1 and Google Chrome. Use the WebKit nightly build from http://webkit.org/ for the best experience");
}
} // if(!version.extensions.CecilyPlugin)
/***
!StyleSheet
body {
font-family: helvetica,arial;
}
#displayArea.cecily {
float: none;
margin: 0em 0em 0em 0em;
position: relative;
background-color: #ffff88;
overflow: hidden;
}
div#messageArea {
-webkit-transition: opacity 0.3s ease-in-out;
-webkit-border-radius: 4px;
-moz-border-radius: 4px;
border: 1px solid #222;
background-color: [[ColorPalette::SecondaryLight]];
background-image: -webkit-gradient(linear, left top, left bottom, from([[ColorPalette::SecondaryPale]]), to([[ColorPalette::SecondaryDark]]), color-stop(0.1,[[ColorPalette::SecondaryLight]]), color-stop(0.6,[[ColorPalette::SecondaryMid]]));
opacity: 0.8;
}
div#messageArea:hover {
opacity: 1.0;
}
div#messageArea .button {
padding: 0 0.25em 0 0.25em;
text-decoration: none;
-webkit-transition: opacity 0.3s ease-in-out;
opacity: 0;
-webkit-border-radius: 3px;
-moz-border-radius: 3px;
background-color: #aaa;
background: -webkit-gradient(linear, left top, left bottom, from([[ColorPalette::PrimaryLight]]), to([[ColorPalette::PrimaryDark]]), color-stop(0.5,[[ColorPalette::PrimaryMid]]));
color: [[ColorPalette::TertiaryPale]];
}
div#messageArea:hover .button {
opacity: 1;
}
div#messageArea:hover .button:active {
background-color: [[ColorPalette::Foreground]];
color: [[ColorPalette::Background]];
}
#overlayMenu {
-webkit-box-shadow: 2px 2px 13px #000;
-moz-box-shadow: 2px 2px 13px #000;
-webkit-transition: opacity 0.2s ease-in-out;
z-index: 100;
position: absolute;
padding: 0.1em 0.1em 0.1em 0.1em;
font-size: 0.8em;
-webkit-border-radius: 4px;
-moz-border-radius: 4px;
border: 1px solid #666;
background-color: #bbb;
background-image: -webkit-gradient(linear, left top, left bottom, from(#999), to(#ddd), color-stop(0.3,#bbb));
opacity: 0;
display: none;
}
#overlayMenu table.twtable {
border: none;
}
#overlayMenu .twtable th{
border: none;
}
#overlayMenu .twtable td {
border: none;
}
#overlayMenu .twtable tr {
border: none;
border-bottom: 1px solid #ccc;
}
#overlayMenu a {
-webkit-transition: color 0.3s ease-in-out;
text-decoration: none;
font-weight: bold;
font-style: normal;
color: #000;
background-color: #999;
border: none;
margin: 0 0.25em 0 0.25em;
padding: 3px 3px 3px 3px;
-webkit-border-radius: 3px;
-moz-border-radius: 3px;
}
#overlayMenu a:hover {
text-decoration: none;
font-weight: bold;
font-style: normal;
color: #000;
background-color: #ff0;
border: none;
}
#overlayMenu .overlayCommand {
font-size: 2em;
color: #fff;
text-shadow: #000 2px 2px 3px;
}
div#backstageArea {
position: absolute;
}
.cecilyCanvas {
position: absolute;
left: 0px;
top: 0px;
background-color: #eee;
}
#tiddlerDisplay {
position: relative;
-webkit-transform-origin: 0% 0%;
-moz-transform-origin: 0% 0%;
}
.cecily .tiddler {
position: absolute;
top: 0px;
left: 0px;
width: 360px;
padding: 0;
background-color: #fff;
overflow: hidden;
border: 1px solid black;
}
.cecily .tiddler.drag {
-webkit-box-shadow: 2px 2px 13px #000;
-moz-box-shadow: 2px 2px 13px #000;
}
.cecily .tiddler .heading {
background-color: #bbb;
background-image: -webkit-gradient(linear, left top, left bottom,
from(#fff), color-stop(0.5,#bbb), color-stop(0.51,#aaa), to(#999));
}
.cecily .tiddler .toolbar {
cursor: all-scroll;
padding: 4pt 2pt 4pt 4pt;
color: #aaa;
}
.cecily .tiddler.selected .toolbar {
color: #fff;
}
.cecily .tiddler .toolbar a {
-webkit-transition: opacity 0.3s ease-in-out;
opacity: 0;
margin: 0 0.25em 0 0.25em;
border: none;
-webkit-border-radius: 3px;
-moz-border-radius: 3px;
}
.cecily .tiddler.selected .toolbar a {
opacity: 1;
background-color: #aaa;
background: -webkit-gradient(linear, left top, left bottom, from(#888), to(#ccc), color-stop(0.5,#aaa), color-stop(0.7,#bbb));
color: #fff;
}
.cecily .tiddler.selected .toolbar a:hover {
background-color: #c80;
background-image: -webkit-gradient(linear, left top, left bottom, from(#c80), to(#fc1), color-stop(0.5,#c80));
color: #000;
}
.cecily .tiddler.selected .toolbar a:active {
background-color: [[ColorPalette::Foreground]];
background-image: none;
color: [[ColorPalette::Background]];
}
.cecily .tiddler .title {
cursor: all-scroll;
padding: 2pt 8pt 2pt 8pt;
color: #000;
background-color: transparent;
text-shadow: #fff 1px 1px 2px;
}
.cecily .tiddler .subtitle {
padding: 2pt 8pt 4pt 8pt;
color: #444;
font-size: 0.6em;
}
.cecily .tiddler .viewer {
padding: 4pt 8pt 4pt 8pt;
background-color: #fff;
}
.cecily .tiddler .tagging, .cecily .tiddler .tagged {
float: none;
border: none;
padding: 2pt 8pt 2pt 8pt;
background-image: -webkit-gradient(linear, left bottom, left top, from(#888), to(#ccc), color-stop(0.5,#ccc), color-stop(0.95,#fff));
margin: auto;
}
.cecily .tiddler .tagged {
cursor: nwse-resize;
}
.cecily .tiddler.selected .tagging, .cecily .tiddler.selected .tagged {
background-color: auto;
border: auto;
}
.cecilyButton {
-webkit-appearance: push-button;
}
!(end of StyleSheet)
***/
Runs eCommerce at [[Jeroboams]]
Description: Adobe AIR desktop widget as a chat client, notifying someone when they have new chat
Rate: up for revenue-split-style reward; keeping log at standard rate as credit
!Research
This is still relevant - [[Adobe AIR for Web Standards peeps]]
!Log
Design meeting on 24th September - 2hrs 30mins
[[1st October ChatLoop]] - 4hrs 35mins
[[8th October ChatLoop]] - 1hr
[[31st October ChatLoop]] - 1hr 5mins
[[1st November ChatLoop]] - 1hr 50mins
[[3rd November ChatLoop]] - 1hr
[[4th November ChatLoop]] - 4hrs 10mins
[[17th November ChatLoop]]
/***
|Name|CheckboxPlugin|
|Source|http://www.TiddlyTools.com/#CheckboxPlugin|
|Documentation|http://www.TiddlyTools.com/#CheckboxPluginInfo|
|Version|2.4.0|
|Author|Eric Shulman|
|License|http://www.TiddlyTools.com/#LegalStatements|
|~CoreVersion|2.1|
|Type|plugin|
|Description|Add checkboxes to your tiddler content|
This plugin extends the TiddlyWiki syntax to allow definition of checkboxes that can be embedded directly in tiddler content. Checkbox states are preserved by:
* by setting/removing tags on specified tiddlers,
* or, by setting custom field values on specified tiddlers,
* or, by saving to a locally-stored cookie ID,
* or, automatically modifying the tiddler content (deprecated)
When an ID is assigned to the checkbox, it enables direct programmatic access to the checkbox DOM element, as well as creating an entry in TiddlyWiki's config.options[ID] internal data. In addition to tracking the checkbox state, you can also specify custom javascript for programmatic initialization and onClick event handling for any checkbox, so you can provide specialized side-effects in response to state changes.
!!!!!Documentation
>see [[CheckboxPluginInfo]]
!!!!!Revisions
<<<
2008.01.08 [*.*.*] plugin size reduction: documentation moved to [[CheckboxPluginInfo]]
2008.01.05 [2.4.0] set global "window.place" to current checkbox element when processing checkbox clicks. This allows init/beforeClick/afterClick handlers to reference RELATIVE elements, including using "story.findContainingTiddler(place)". Also, wrap handlers in "function()" so "return" can be used within handler code.
|please see [[CheckboxPluginInfo]] for additional revision details|
2005.12.07 [0.9.0] initial BETA release
<<<
!!!!!Code
***/
//{{{
version.extensions.CheckboxPlugin = {major: 2, minor: 4, revision:0 , date: new Date(2008,1,5)};
//}}}
//{{{
config.checkbox = { refresh: { tagged:true, tagging:true, container:true } };
config.formatters.push( {
name: "checkbox",
match: "\\[[xX_ ][\\]\\=\\(\\{]",
lookahead: "\\[([xX_ ])(=[^\\s\\(\\]{]+)?(\\([^\\)]*\\))?({[^}]*})?({[^}]*})?({[^}]*})?\\]",
handler: function(w) {
var lookaheadRegExp = new RegExp(this.lookahead,"mg");
lookaheadRegExp.lastIndex = w.matchStart;
var lookaheadMatch = lookaheadRegExp.exec(w.source)
if(lookaheadMatch && lookaheadMatch.index == w.matchStart) {
// get params
var checked=(lookaheadMatch[1].toUpperCase()=="X");
var id=lookaheadMatch[2];
var target=lookaheadMatch[3];
if (target) target=target.substr(1,target.length-2).trim(); // trim off parentheses
var fn_init=lookaheadMatch[4];
var fn_clickBefore=lookaheadMatch[5];
var fn_clickAfter=lookaheadMatch[6];
var tid=story.findContainingTiddler(w.output); if (tid) tid=tid.getAttribute("tiddler");
var srctid=w.tiddler?w.tiddler.title:null;
config.macros.checkbox.create(w.output,tid,srctid,w.matchStart+1,checked,id,target,config.checkbox.refresh,fn_init,fn_clickBefore,fn_clickAfter);
w.nextMatch = lookaheadMatch.index + lookaheadMatch[0].length;
}
}
} );
config.macros.checkbox = {
handler: function(place,macroName,params,wikifier,paramString,tiddler) {
if(!(tiddler instanceof Tiddler)) { // if no tiddler passed in try to find one
var here=story.findContainingTiddler(place);
if (here) tiddler=store.getTiddler(here.getAttribute("tiddler"))
}
var srcpos=0; // "inline X" not applicable to macro syntax
var target=params.shift(); if (!target) target="";
var defaultState=params[0]=="checked"; if (defaultState) params.shift();
var id=params.shift(); if (id && !id.length) id=null;
var fn_init=params.shift(); if (fn_init && !fn_init.length) fn_init=null;
var fn_clickBefore=params.shift();
if (fn_clickBefore && !fn_clickBefore.length) fn_clickBefore=null;
var fn_clickAfter=params.shift();
if (fn_clickAfter && !fn_clickAfter.length) fn_clickAfter=null;
var refresh={ tagged:true, tagging:true, container:false };
this.create(place,tiddler.title,tiddler.title,0,defaultState,id,target,refresh,fn_init,fn_clickBefore,fn_clickAfter);
},
create: function(place,tid,srctid,srcpos,defaultState,id,target,refresh,fn_init,fn_clickBefore,fn_clickAfter) {
// create checkbox element
var c = document.createElement("input");
c.setAttribute("type","checkbox");
c.onclick=this.onClickCheckbox;
c.srctid=srctid; // remember source tiddler
c.srcpos=srcpos; // remember location of "X"
c.container=tid; // containing tiddler (may be null if not in a tiddler)
c.tiddler=tid; // default target tiddler
c.refresh = {};
c.refresh.container = refresh.container;
c.refresh.tagged = refresh.tagged;
c.refresh.tagging = refresh.tagging;
place.appendChild(c);
// set default state
c.checked=defaultState;
// track state in config.options.ID
if (id) {
c.id=id.substr(1); // trim off leading "="
if (config.options[c.id]!=undefined)
c.checked=config.options[c.id];
else
config.options[c.id]=c.checked;
}
// track state in (tiddlername|tagname) or (fieldname@tiddlername)
if (target) {
var pos=target.indexOf("@");
if (pos!=-1) {
c.field=pos?target.substr(0,pos):"checked"; // get fieldname (or use default "checked")
c.tiddler=target.substr(pos+1); // get specified tiddler name (if any)
if (!c.tiddler || !c.tiddler.length) c.tiddler=tid; // if tiddler not specified, default == container
if (store.getValue(c.tiddler,c.field)!=undefined)
c.checked=(store.getValue(c.tiddler,c.field)=="true"); // set checkbox from saved state
} else {
var pos=target.indexOf("|"); if (pos==-1) var pos=target.indexOf(":");
c.tag=target;
if (pos==0) c.tag=target.substr(1); // trim leading "|" or ":"
if (pos>0) { c.tiddler=target.substr(0,pos); c.tag=target.substr(pos+1); }
if (!c.tag.length) c.tag="checked";
var t=store.getTiddler(c.tiddler);
if (t && t.tags)
c.checked=t.isTagged(c.tag); // set checkbox from saved state
}
}
// trim off surrounding { and } delimiters from init/click handlers
if (fn_init) c.fn_init="(function(){"+fn_init.trim().substr(1,fn_init.length-2)+"})()";
if (fn_clickBefore) c.fn_clickBefore="(function(){"+fn_clickBefore.trim().substr(1,fn_clickBefore.length-2)+"})()";
if (fn_clickAfter) c.fn_clickAfter="(function(){"+fn_clickAfter.trim().substr(1,fn_clickAfter.length-2)+"})()";
c.init=true; c.onclick(); c.init=false; // compute initial state and save in tiddler/config/cookie
},
onClickCheckbox: function(event) {
window.place=this;
if (this.init && this.fn_init) // custom function hook to set initial state (run only once)
{ try { eval(this.fn_init); } catch(e) { displayMessage("Checkbox init error: "+e.toString()); } }
if (!this.init && this.fn_clickBefore) // custom function hook to override changes in checkbox state
{ try { eval(this.fn_clickBefore) } catch(e) { displayMessage("Checkbox onClickBefore error: "+e.toString()); } }
if (this.id)
// save state in config AND cookie (only when ID starts with 'chk')
{ config.options[this.id]=this.checked; if (this.id.substr(0,3)=="chk") saveOptionCookie(this.id); }
if (this.srctid && this.srcpos>0 && (!this.id || this.id.substr(0,3)!="chk") && !this.tag && !this.field) {
// save state in tiddler content only if not using cookie, tag or field tracking
var t=store.getTiddler(this.srctid); // put X in original source tiddler (if any)
if (t && this.checked!=(t.text.substr(this.srcpos,1).toUpperCase()=="X")) { // if changed
t.set(null,t.text.substr(0,this.srcpos)+(this.checked?"X":"_")+t.text.substr(this.srcpos+1),null,null,t.tags);
if (!story.isDirty(t.title)) story.refreshTiddler(t.title,null,true);
store.setDirty(true);
}
}
if (this.field) {
if (this.checked && !store.tiddlerExists(this.tiddler))
store.saveTiddler(this.tiddler,this.tiddler,"",config.options.txtUserName,new Date());
// set the field value in the target tiddler
store.setValue(this.tiddler,this.field,this.checked?"true":"false");
// DEBUG: displayMessage(this.field+"@"+this.tiddler+" is "+this.checked);
}
if (this.tag) {
if (this.checked && !store.tiddlerExists(this.tiddler))
store.saveTiddler(this.tiddler,this.tiddler,"",config.options.txtUserName,new Date());
var t=store.getTiddler(this.tiddler);
if (t) {
var tagged=(t.tags && t.tags.indexOf(this.tag)!=-1);
if (this.checked && !tagged) { t.tags.push(this.tag); store.setDirty(true); }
if (!this.checked && tagged) { t.tags.splice(t.tags.indexOf(this.tag),1); store.setDirty(true); }
}
// if tag state has been changed, update display of corresponding tiddlers (unless they are in edit mode...)
if (this.checked!=tagged) {
if (this.refresh.tagged) {
if (!story.isDirty(this.tiddler)) // the TAGGED tiddler in view mode
story.refreshTiddler(this.tiddler,null,true);
else // the TAGGED tiddler in edit mode (with tags field)
config.macros.checkbox.refreshEditorTagField(this.tiddler,this.tag,this.checked);
}
if (this.refresh.tagging)
if (!story.isDirty(this.tag)) story.refreshTiddler(this.tag,null,true); // the TAGGING tiddler
}
}
if (!this.init && this.fn_clickAfter) // custom function hook to react to changes in checkbox state
{ try { eval(this.fn_clickAfter) } catch(e) { displayMessage("Checkbox onClickAfter error: "+e.toString()); } }
// refresh containing tiddler (but not during initial rendering, or we get an infinite loop!) (and not when editing container)
if (!this.init && this.refresh.container && this.container!=this.tiddler)
if (!story.isDirty(this.container)) story.refreshTiddler(this.container,null,true); // the tiddler CONTAINING the checkbox
return true;
},
refreshEditorTagField: function(title,tag,set) {
var tagfield=story.getTiddlerField(title,"tags");
if (!tagfield||tagfield.getAttribute("edit")!="tags") return; // if no tags field in editor (i.e., custom template)
var tags=tagfield.value.readBracketedList();
if (tags.contains(tag)==set) return; // if no change needed
if (set) tags.push(tag); // add tag
else tags.splice(tags.indexOf(tag),1); // remove tag
for (var t=0;t<tags.length;t++) tags[t]=String.encodeTiddlyLink(tags[t]);
tagfield.value=tags.join(" "); // reassemble tag string (with brackets as needed)
return;
}
}
//}}}
https://cheddargetter.com
Projects involving large amounts of client-side dev (HTML, CSS, JavaScript) include:
[[Streams]]
PixelPatternMaster
[[Geekmap]]
[[ILGA]]
[[Anna Freud Centre]]
[[Contributively]]
[[Flock O Tweets]]
[[TiddlyTweets]]
11:45 am, Sunday
!Setback
Broke phone, so got a replacement HTC Magic - this doesn't have the Post to Blogger option for photos, so can't use the same application I have blogged about before (http://jaybyjayfresh.com/2009/07/30/how-to-build-a-diy-twitpic-without-any-coding-skillz/http://jaybyjayfresh.com/2009/07/30/how-to-build-a-diy-twitpic-without-any-coding-skillz/)
!Creating
I do have the option to post pictures to Picasa, so I've tried using that. [[Notify.me|http://notify.me]] doesn't seem to like my public photo [[rss feed|http://picasaweb.google.com/data/feed/base/user/jnthnlstr/albumid/5377947565433318449?alt=rss&kind=photo&hl=en_US]] on Picasa... I've moaned about this on the Notify.me [[Get Satisfaction page|http://getsatisfaction.com/notifyme/topics/picasa_feeds_are_they_valid_sources_for_notify_me]].
I'm going to try putting this feed through Feedburner to see if that helps. The feed is at http://feeds.feedburner.com/bcb4test - testing: posted photo at 18:50; photo was on feedburner feed by 18:58.
Can't continue to set this up until I get Notify.me sending notification email. Nothing showing by 19:53.
Other option is to try Notifixio.us... No matter whether I supply the feedburner or the picasa feed address, Notifixio.us resolves it to: http://picasaweb.google.com/jnthnlstr/MobileBlog. I thought this required a log-in to view, but when logged-out I then seemed to be able to access it! Testing: posted photo at 20:11, email arrived at 20:21 (notified about photo posted at 19:56 as well); posted photo at 20:54, email arrived at 20:55. Delay range: 1-25mins. This fits in the length of a talk...
An example of a notification email is:
{{{
Hello dear Notifixist ;-)
There is something new for you on Mobile blog:
100robots
http://notifixio.us/alerts/3625312
- Hide quoted text -
If you want to delete this subscription, please click on the following link: http://notifixio.us/channels/3850/sources/11313-picasaweb-google-mobile/subscription?perform=destroy.
—
The Notifixious team
http://blog.notifixio.us
http://twitter.com/notifixious
Important: The emails we send are sometimes treated as SPAM. To prevent this, even if this one was not in your SPAM inbox, please send an email to both info@notifixio.us and admin@notifixio.us as most of email clients will “learn” from this and stop considering our emails as junk. Thanks a lot!
}}}
How are we going to get at the information? First thing, send it to tarpipe by using a Google mail filter - match on emails "from:info@notifixio.us" with subject "Mobile blog". But where are we going to send the mail? We need to create the tarpipe workflow and save it to get the email address to send to (tarpipe uses Open ID to login, so have to login to blog first). Simply choosing "Mail drop box" and hitting "save workflow" will generate an email address. I have set up tarpipe to post to a test twitter account called "jayfresh_test" (at least for testing, if not for the live tutorial).
When creating the filter, I choose to forward to the email address tarpipe created and to apply a label of "mobile blog" just so I get alerted to the fact that things are going on.
The workflow is made from an EmailDecoder connected to a REST module, the results of which are connected to a TwitterUpdater. A TextInput module is connected to the serviceUrl input of the REST module, for the address of a Yahoo! Pipe we're going to use to get the relevant information out of a Yahoo! Pipe. The address we're going to use is for a proxy which gets the data in the right format before sending on to Yahoo! Pipes and handles the response. This is at: http://tarpipe-yahoopipes-proxy.smart.joyent.com/ and takes the query string {{{?pipeId=xxxxx}}} where you replace the "x's" with the ID of the Yahoo! Pipe you're using to extract the information. We'd better create the Pipe.
Creating the Yahoo! Pipe can be a case of looking at an example and changing bits that you need. For some reason, I can't type any letter past 'o' in the alphabet into the title of the user input module, so I'm going to have to modify the proxy to send 'deciion' instead of 'description'...
For testing, I'm using a feature of the proxy to log to PostBin.
These are virtual machine hosting platforms, with easy config:
http://www.rightscale.com/ - uses Amazon AWS
http://www.gogrid.com/
Seems that with DropBox combined with source control, you could have a neat version control setup.
http://beanstalkapp.com - Git/SVN hosting with simplicity of use in mind. Lets you preview past revisions of HTML pages in the browser. Deploy via S/FTP directly. Free - $200/month
http://github.com
http://codesion.com
http://playnice.ly - has badges!
!The problem
Want a guide to good coffee shops in London; edited by Nick and me. Don't want it to be a hassle to set up and run.
!The approach
Had a look at a lot of options for easy-to-create websites and think SquareSpace might be the way to go. Currently trying it out. Here's some notes about what's weird or good:
* what's all this nonsense about not being able to remove links from a nav bar without deleting the underlying page?
* why have sections as containers for pages rather than the other way round?
* why's it so hard to create a list of pages that don't make them open in another window?
* why can't I create layouts for my pages that I can re-use so they look the same?
* why's the primary nav fixed in my sidebar section even when it's placed at the top of the page?
* why do maps have to be in their own pages and not as widgets on my sidebar?
Also related to project management tools.
http://www.cubetree.com
http://huddle.com
http://basecamp.com
http://lighthouseapp.com - good for tracking developments
http://tiddlyspace.com
http://www.bantamlive.com - social CRM. Apparently.
http://manymoon.com - integrates nicely with Google Apps, including GMail so you can create tasks from email.
http://signalfire.com/ - group chat; free
http://teambox.com/
Background: #fff
Foreground: #000
PrimaryPale: #ccc
PrimaryLight: #18f
PrimaryMid: #000
PrimaryDark: #014
SecondaryPale: #ffc
SecondaryLight: #fe8
SecondaryMid: #db4
SecondaryDark: #000
TertiaryPale: #eee
TertiaryLight: #ccc
TertiaryMid: #999
TertiaryDark: #666
Error: #f88
These let you annotate or otherwise mark up images and/or text with comments.
Ideally, I wanted something that would make the comments social objects.
| Name | Free? | Tried? | Comments allow replies? | In-place comments? | any URL? | Notes |
| Diigo | yes and paid | yes | yes | yes | yes | |
| SharedCopy | yes | yes | yes | by highlighting text | yes | makes copies of pages |
| ScribLink | yes | y | ? | yes | no | whiteboarding; couldn't get upload work (Safari) |
| Skrbl | yes | y | ? | yes | no | whiteboarding; not sure it works |
| VoiceThread | y and paid | y | ? | y | y if image | Annotate streams; allows voice and video annotations alongside animated doodling |
| Finetuna | y | y | ? | y | y if image | |
| Flickr | y and paid | y | n | y | n | |
Do we get into this? What is it? What should we ask people about this?
Etherpad: http://ietherpad.com/hSTRlIAW4T
Very thin conclusions:
* commercial leasing has many "things" you're supposed to pay for or register, or "do"
* opportunities to save time and costs by automating/onlining these things/processes
We don't know:
* the "normal" process for leasing commercial property, from either letter or renter point of view
* who would be interested in our stuff
WireIt's objects, from either Layer.getWiring() or WiringEditor.getValue(), include the positions of the modules. The stream wiring does't have this concept yet.
!Structure of the Streams object describing the modules and connections
A 'wiring' has this structure:
{{{
{nodes: {}, edges: []}
}}}
A node has this structure (if it has a terminal property it is a source or destination):
{{{
{ "name": { type: "" | terminal: "" [, configuration: {}] } }
}}}
An edge has this structure:
{{{
{to: { node: "" [, channel: ""] }, from: { node: "" [, channel: ""] }}
}}}
Something to help with the time tracking, which takes quite a long time to put into the spreadsheet and analyse... so really, this is more about making a data processing web service...
This is a tool to track relative contributions people make to a project. It is an open project, written in HTML, CSS, JavaScript (and server-side JavaScript for optional data storage).
App - http://contributively.appjet.net
Feedback - http://contributively.uservoice.com (source, tickets and wiki linked from there)
!Search Engine Optimisation
#http://ccatf.org.uk has not been mapped to http://www.ccatf.org.uk so resolves to an EasySpace holding page
#Page titles lose the CCATF label except on homepage
#A 'description' meta-tag would be a useful addition to the pages, as they appear in search engine results and can be used to improve rankings
!Copy
!!Errors
!!!Apprentices
#"in formation" -> "information"
!!!Biographies
#No link to Geoff Lister's
#Max Hamps' is empty
!!Suggestions
!!!About
#Mission statement: why no commas? I can't breath.
!!!Employers
#"27% of small companies" - link to source of statistic
#I think the case for apprentices could be made more concrete and more convincingly on this page - it is really your main message
!!!News
#News is stale - two articles for 21st April, nothing since - does this reflect the state of CCATF?
#Links to press coverage of the topics of your articles adds credibility, as CCATF is not a primary source
#A 'back' button would be useful on article pages, as you don't have a way to get back to the index underneath the article, where the eye ends up.
!!!Contact Us
#A Google map illustrating where to find CCATF would be a nice touch
#mailto links can include a subject and body which pre-fill those fields in the email, which is helpful for discovering which came from the website
!!!Partners/sponsors
#Is there something special about the National Federation of Builders? They get a whole page of the carousel to themselves
!!!Links
You name-drop a lot, so links to the organisations, people and initiatives named would be helpful to put the CCATF in context
!Performance
!!Noticed
#Why do the photos take so long to load, particularly the small headshots in the biographies?
!!As determined by YSlow
!!!Significant effects
#Many separate JavaScript and CSS files, where they could be combined into one
#No compression on the server
!!!Less significant effects
#JavaScript not minified
!Appearance
#Justified text isn't that nice to read
#Serif fonts are better for smaller body text, with sans-serif for headers
#It appears the height of the main body is limited, as it doesn't change. Flexibility here would give you scope for more text in the main area, which would help flesh out the content of some of the pages (Ian Billyard's profile shows you can do this)
#Variation within the main body text would be pleasing to the eye; sections and headers are the first thing I'd add
!Accessibility
#Site content not at top of HTML document
#No NOSCRIPT for partners
!Compliance to standards
#Homepage [[almost valid XHTML|http://validator.w3.org/check?uri=http%3A%2F%2Fwww.ccatf.org.uk&charset=%28detect+automatically%29&doctype=Inline&group=0]] (which it defines itself as). Errors are mainly caused by using capital letters for tags and attributes. Read about [[Why should I care?|http://validator.w3.org/docs/why.html]]
Testing cross-browser, cross-OS, without installing software. VNC a big plus.
| Name | Free? | Tried it? | OS's | Screenshoting | VNC | notes |
| CrossBrowserTesting | plus paid | no | Windows, Mac, Ubuntu | yes | yes | |
| Spoon sandbox | yes | no | Windows, Mac | no | yes | only works on Windows |
| BrowserCamp | no | no | mac | no | yes | |
| LitmusApp | plus paid | no | window, mac | yes | no | |
| [[NetRenderer|http://ipinfo.info/netrenderer/]] | only free | no | windows | yes | no | |
| BrowserShots | only free | no | linux, windows, mac, BSD | yes | no | apparently very fast |
| Adobe BrowserLab | only free | no | unsure | yes | no | |
Olivier from Electric Blue put me in touch with Dom Pascal, who is Art Director at DRAMA. They want an iPad app to complement their iPhone app (http://itunes.apple.com/WebObjects/MZStore.woa/wa/viewSoftware?id=334668760&mt=8).
I spoke to [[Tom Taylor]] (tomtaylor.co.uk) after James Stewart introduced us at TCO. He can't do it. Have been in touch with [[Sam Butterfield]], who has directed me to Do Tank. I spoke to [[Jean-Phillipe Altier]] there.
Also asked Nitobi for a quote via [[Brian LeRoux]].
!!Log
[[21st May 2010 DRAMA]] - 2 hrs
[[Call with Brian LeRoux about DRAMA magazine 02/06/10]] - 35 mins
[[4th June 2010 DRAMA]] - 40 mins
Hours: 14hrs 45mins
Rate: £200/8-hour day or £25/hour
Invoice #: [[0001|Invoice 0001]]
!Log
[[26th June - Dad's IT problems]] - 9hrs 20mins
[[27th June - Dad's IT problems]] - 3hrs 50mins
[[28th June - Dad's IT problems]] - 1hr 35mins
I've started to tag HostedService tiddlers with RespondsTo and SendsTo. This lets me say that e.g. a service responds to changes in an RSS feed, and sends new output to XMPP. This should allow me to write a little macro, kinda like the RelatedTagsPlugin, that shows what a HostedService tiddler can connect to. You could use the NestedPopups style to show what the next tiddler in the chain could connect to and so on.
This is getting interesting, as recently Tarpipe joined Pipes and Dapper in the drag 'n' drop data processing world, except that they are focussed on event-triggered workflows such as updating Twitter in response to an email.
| Name | Input | Output | Free |
| [[Yahoo! Pipes]] | REST | RSS, POST, email, SMS (US), JSON | Yes |
| [[Tarpipe]] | Email, POST | POST to selected services | Yes |
| [[Dapper]] | REST | RSS, XML, (some alert thing... add more detail) | Yes |
| [[BT Rules]] | RSS feed changes, "envelopes", Google Finance stock changes | SMTP, Facebook status, Google Translate | Yes |
This is different from [[Storage]], since this is about structured data.
Current need is for [[Flock O Tweets]].
Some to check out:
http://www.programmableweb.com/api/qrimp
http://www.programmableweb.com/api/amazon-simpledb
http://www.programmableweb.com/api/dabbledb
http://www.programmableweb.com/apis/directory/1?apicat=Database
http://www.programmableweb.com/api/nextdb
http://www.computerworld.com/action/article.do?command=viewArticleBasic&articleId=9081958&pageNumber=5
http://blog.programmableweb.com/2008/12/05/mashups-get-a-hosted-database-with-nextdbnet/
http://www.nextdb.net/
http://creator.zoho.com/
http://dabbledb.com/
3/3/10
With [[Alan Blackwell]] as host.
Meeting with sculptor Bruce Gren..., musician Richard ..., PhD student [[Luke Church]]. Bruce is interested in making sculptures that are in some way evolutions of one natural form into another.
Student Design competition exhibition. Notes about lightning talks in [[Student Design Lightning talks]]
"Network and computational code". So reads the material description, on a little white plaque, of a digital masterpiece embedded in the wall. This is Decode, an exhibition running until April at the V&A, one that celebrates technology's influence on art. This is where thick black reeds, with LEDs topping them, flicker and flick against you as enter the smallish hall, densely packed with deep tech. It is a bit like a computer fair from the 80's, in that the organisers have put marvels on display to attract thousands.
[[This Workbook]]
[[What's on]]
Doing an experiment.
Started out being about creating web pages from scratch, now about cloning and tweaking. The spikes that need to happen now are:
* load a foreign page into an iFrame, read the HTML back out, check it is the same
* load a foreign page into and iFrame, change the HTML, read the HTML back out, check changes are there
* edit in-iFrame HTML using Firebug and read HTML back out, check changes are there
Also, see [[DesignView stories]].
24th May: Grid: I want to be able to use the principal of snapping to a grid for any CSS property, so I can load a stylesheet to use with the document. I want the snapping to effect a change in an element's class or id, removing the rule from element.style, so I have a way to stop the spread of lots of element.style properties.
-----
1st March: Grid: I want to be able to put a grid on top of what I'm designing, because a blank canvas is hard to use and I know grids are good for keeping things nicely laid out
-----
23rd Dec '09
Feedback: This is cool, but I would change the way updating styles works so that generic rules e.g. "div" are not created, but "element.style" is created instead. Then there is a mechanism to move style rules from an element into a less specific rule e.g. by dragging or overwriting element.style with a selector. Also, I would use pre-defined rules and HTML snippets more often.
If this was done as a Bespin plugin, perhaps I could take advantage of the undo framework and other document management tasks.
Story: I ask for a new document and I am given a large blank screen; my mouse cursor looks like a target. I see a floating box with a basic HTML document and an empty stylesheet. There is a flashing cursor in the <body>. There is a flashing cursor in the top-left of the document.
When I click on the document and drag my mouse, a box appears under my cursor. When I let go, the box springs to the top-left, glows and fades out. When I clicked, the flashing cursors disappeared and a <div> appeared in the HTML, where the cursor was flashing in the <body>.
An empty CSS rule appears in the CSS window. As I drag, the CSS rule gains an updating width and height. When I let go, the flashing cursors reappear inside the <div> I just created.
I click on the new <div> in the HTML and an edit box pops up where I clicked, containing the text "<div>". I change this to "<div id='header'>" and hit return. The HTML updates to add the ID to the <div> and an empty CSS rule appears at the bottom of the CSS view for "div", whereas the existing CSS rule change to read "#header".
While the cursors are flashing, I start to type and an edit box appears on the document where a cursor was flashing. I type and a new <p> element is created in the HTML view and a blank CSS rule appears at the top of the CSS view. I click the <p> and change it to a <h1> in the HTML view editor. The </p> change to </h1> and the document and CSS rule update accordingly.
Anytime I feel like it, I can right-click the document and choose to preview it in a new tab. I can also elect to export the document as a zip. If I choose to save any changes, the document is seralized to a folder structure.
Snapshot visuals and CSS/HTML (image yes, code how?)
Use RGBa to layer on top of single colour backgrounds
Add a grid (desktop tools or JS bookmarklets (source?))
Add different page states to wireframes using something like Polypage (http://github.com/andykent/polypage/)
Start from a page of semantic content
Designing layout in a browser is hard!
It would be great if we both knew what we were supposed to be doing (this is partly helped by using things like Etherpad to allocate chunks)
It would be great if we didn't have a static build folder and a CMS-integrated folder, since they get out of sync
It would be great if we didn't have to maintain and merge copies of a project's files (DropBox helps keep us in sync, including symlinking folders in other places to the DropBox e.g. wptheme folders - a problem with this is that sometimes you overwrite each other's changes because DropBox doesn't do so well at merging)
It would be great if we could both see the output from a local server running on someone's machine (something like localtunnel.com promises to solve this, but we haven't been able to get it to work satisfactorily)
It would be great if we didn't have copies of the same CSS & JS files in the static build and the CMS-backed build - perhaps use symlinks here?
LShift project, formerly known as FastDiff
!Morning session (from 10:00 to 12:00; 12:15 to 14:00; 14:15 to 15:15)
Helping Tager help Bell Pottinger with a pitch for London Olympics PR. After a brief from Barbara and Ben, it looks like the things for me to do immediately are:
* how did Obama's campaign use the web well?
* re-read the brief
* look at what people are saying about the Olympics on the web
* think about how what I'm doing can be presented as part of the brief
* check out material on official site - http://london2012.com
Obama used Twitter a lot (circa 250 tweets), MySpace, Facebook, Flickr and YouTube, as well as running my.barackobama.com, a social network. It looks like the small-value donations solicited through online channels was innovative - it was certainly successful, breaking records with $150m raised in September 2008 (more than 3m donors in total, average donation $84 per person - [[source|http://news.bbc.co.uk/1/hi/7678674.stm]]). They also used "famous online fundraising lotteries that gave small donors a chance to win face time with Obama. A key lesson is that using online channels allowed the team to be more responsive.
The election team said: The key guideline was a simple message: "A Record Turnout Is Expected." That's because studies by psychologist Robert Cialdini and other group members had found that the most powerful motivator for hotel guests to reuse towels, national-park visitors to stay on marked trails and citizens to vote is the suggestion that everyone is doing it ([[source Time|http://www.time.com/time/magazine/article/0,9171,1889153,00.html]]).
Where are tweets coming from? GeoTweets, #uksnow maps, microformats in tweets or tweeter location - ''you could show the geographic distribution of people talking about the London olympics to show it's a worldwide phenomenon'' - you could also link in the news that's being published about the olympics
There's already a #olympics tag on twitter.
Topic of conversation on Twitter seem to be:
* security worries
* cost
* money brought to the economy
* what's happening on YouTube?
The official site has a [[map|http://www.london2012.com/map.php]] of what's going on now about the olympics - news, blogs, events, venues, slideshows, photos & videos - shows there's very little North of Halifax (there's some stuff in Newcastle)
* what companies are involved in the olympics?
£5bn quid up for grabs - http://www.ir35calc.co.uk/olympics_contractors_bonanza.aspx - More than 1,000 UK businesses have won contracts worth over £5 billion to help build the venues and infrastructure for the London 2012 Games. ([[source|http://www.london2012.com/news/2009/09/uk-businesses-grabbing-golden-london-2012-opportunities.php]]). I'm thinking that there could be an interest in the potential of the Olympics to rejuvenate the economy - not just in London. ''where are the companies winning the contracts coming from?''
There's a map of suppliers to the ODA here - http://www.london2012.com/get-involved/business-network/oda-suppliers/map.php
The source for this list is: http://data.map.london2012.com/suppliers.php
There's a load of new infrastructure being built in East London
There seem to be a number of out-reach programmes (sourced from [[progress report|http://www.london2012.com/making-it-happen/progress-report/index.php]]):
# 1,500 UK businesses have won around £5 billion worth of Games-related contracts.
# 15,000 schools are part of the Get Set education programme.
# More than 2.5 million children and young people in five countries have been reached by the International Inspiration programme, encouraging them to be active. The programme is expanding to 11 countries.
** The programme aims to reach 12 million children in 20 countries by 2012
# 310 outstanding non-commercial projects and events have been recognised with the London 2012 Inspire mark. Sport, culture, education, sustainability, volunteering and business opportunities all feature.
# There are currently 21 Live Sites around the UK - big screens and event spaces in urban centres offering live information, video, news and community events.
# 25 domestic commerical partners have signed up to the London 2012 programme, working to help deliver the Games and inspire the UK. The partnerships have generated more than £600 million.
Geographic foci for people to enjoy the games in public - Henman Hill - is this the sort of thing that could happen online? Digital hubs? There are [[20 live sites|http://www.london2012.com/get-involved/live-sites/index.php]] around the country for people to watch the games on (done with the BBC)
Volunteers - 70k registered volunteers - how can they be used in online campaigns?
Random ideas for things that you'd have in the olympics if you could - a social site with people voting up their favourite ideas
Tickets - there are 7.7m tickets available, but obviously they're all happening in London. What do you do if you can't get to London? ''Is there such a thing as an online ticket?'' Official tickets will get you free travel - could online tickets get you free something-else?
After walking about a bit, I'm thinking - what are the questions that these exercises in digital could answer?
Where's the money? ''Why should I care?''
Even if you're not interested in the games itself, you might care about something that's happening BECAUSE OF the Olympics. I'm thinking International Inspiration programme, or the Open Weekend across the country (The three-day celebration featured 800 events held by around 500 organisations across the UK; [[source|http://www.london2012.com/get-involved/open-weekend/index.php]]).
Are people aware there are things around them? "Almost half (44 per cent) of board-level executives surveyed said they are unaware activities will be happening in their area." ([[source|http://www.webwire.com/ViewPressRel.asp?aId=110622]]). An online thing where you put your post-code in and you see a calendar of what's coming up in your area, or what businesses are participating in the games, or how you can pro-actively get more things happening in your area (since there seem to be people available all over the country to help run stuff, and is there money available?).
This gets me on to two things: digital as a way to aggregate all sorts of information about the geographic effect on the country; digital as a way to bring people together under the banner of the Olympics.
Sport on a high level - the presence of national heroes has an affect on the participation in that sport (think Chris Hoy and [[this|http://www.insidethegames.biz/index.php?option=com_content&view=article&id=8754:finch-predicts-london-2012-will-help-basketabll-explode-in-britain&catid=91:basketball-news&Itemid=125]] about basketball; also see [[Heroes|http://getset.london2012.com/en/heroes]]). Does digital have a role to play here?
What about other things going on where the UK is on a world stage? 2011 is the World Skills competition in London and Sport Accord 2011, the world's largest sports congress (set to bring in £2.4million and the 52-nation World Skills Competition in 2011 to promote vocational training, expected to generate £29 million). The Commonwealth Games is in Glasgow in 2014.
There's the legacy of the Olympics to think about - how is digital relevant here? The aims of the Olympic Legacy Action Plan are:
1. To make the UK a world-leading sporting nation
- focus on grass-roots or elite? could there be support for grass-roots sport? there is general low awareness of existing programmes (such as the PE and Sport Strategy for Young People; the Home Country Sports Councils’ community sport strategies; and UK Sport’s World Class Performance Programme. Future programmes include: A new ‘Fit for the Future’ incentive scheme pilot for young adults; a £75 million targeted social marketing programme to promote healthy living).
2. To transform the heart of East London
3. To inspire a generation of young people
4. To make the Olympic Park a blueprint for sustainable living
5. To demonstrate the UK is a creative, inclusive and welcoming place to live in, visit and for business.
The Cultural Olympiad "puts culture at the heart of the 2012 Games - encouraging participation and celebrating the cultures that make up the UK" (london2012.com).
So the nation's health is a primary concern for the Olympics committee. There must be a load of online/mobile/social things you could do to promote health living and an increase in the amount of sport you do? Social apps to compare you to your friends; mobile apps to help you get motivated to do regular exercise; mobile services to help community groups working with groups of people who continue to get value from meetings when at home.
The full legacy plan is at: http://www.culture.gov.uk/images/publications/2012LegacyActionPlan.pdf
A research document called [[The Olympic Legacy - Qualitative research into public attitudes|http://www.culture.gov.uk/images/publications/stimulus_final.pdf]] found in October 2007: "an almost complete lack of awareness of any plans or initiatives which might give confidence for the future"
I emailed a few people to ask what's going on and what will happen post-Olympics about raising the profile of sport - North East (www.onenortheast.co.uk/london2012), Scotland (http://www.scotland.gov.uk/Topics/ArtsCultureSport/Sport/MajorEvents/2012/ScottishSteeringGroup).
Nick's comment about Olympics on the net:
- most people in the world will use BBC.
- BBC will be expected to provide television coverage of every event, all the latest results, interviews etc all streamable over the web.
- I would work on a joint net PR campaign between BBC & 2012
me: hmm
yes...
Nick: 2012 site only stuff - maps for people coming to events, real-time twitter feed, flickr photos feed, videos of reactions of people to victories/losses across the globe, videos of impact of money spent to benefit Britain, 2012 merchandise online shop (partnerships with local providers overseas)
Sent at 1:45 PM on Tuesday
Nick: opportunities for demos from all major sponsors
Sorry, will leave you to it. Ta ra!
me: thanks muchly!
laters!
any more ideas, please share
Nick: oh ok - can you say who the PR is for? and it's purpose?
me: I dunno
haven't read the brief
Nick: tricky assignment!
hahaha
me: or the draft pitch
I guess it's the Olympic committee's PR
but I should find out more
my guess would be to raise awareness and profile of what's going on around the country and make people happier about the olympics
yes yes, how about tie-ins with celebs across the country. e.g. eddie izzard doing a gig solely about the olympics in Manchester
and tie-in with a special Olympic sports relief?
Old sportsmen vs celebs. Jonathan Ross vs Sally Gunnel at the hurdles
Surprise events - a repeat of the Jimmy Page playing immigrant song all around london
What about bowler hats given out to every athlete
English accent lessons for the american athletes
ho-hum
Sent at 1:52 PM on Tuesday
Nick: What about the first ever simultaneous fireworks show. Where exactly the same spectacular fireworks display happens at the same time at several major cities in the UK and across the world (New York, Sydney, Cape Town)
me: I like ambition
Nick is typing…
athletics lessons in trafalgar square by old sporting greats
A world record for the longest relay
around the UK
The olympics means something different to everybody - just capturing that and exposing it would be interesting.
Web as a broadcast device - people using the web to watch, re-watch, see niche stuff.
!Afternoon session (from 15:40 to 16:45; 16:55 to 17:45; 17:50 to 18:30)
Having presented my thoughts, I've decided that things fall into three areas - the propaganda of the event before it happens, the coverage during the event and the legacy.
According to the brief, we're mainly answering the question "What makes the Olympics special?". The minor themes are: "a games within reach of everyone", "once in a lifetime opportunity to be a part of something big", "pride" and "promoting events - volunteering, ticketing, torch relay, etc."
My thoughts about these themes are that we want to look at how people all over the country can be a part of the Olympics even if they're not in London nor going to travel there. What digital analogues can there be of real-world events and how does the digital proximity of the entire world affect the way people will want to interact with each other?
!!! Propaganda
From the evidence of the vox-pops and the conversations found on the Internet, thoughts about the Olympics are mainly about security concerns, the cost of putting on the Olympics and how much of a boost to the UK economy the Olympics will bring. There is an entrenched feeling that the Olympics is a Southern concern.
People are not taking enough notice of the programmes put in place and upcoming by the Olympic Committee, to promote culture across the country and encourage wider participation in sport and better health.
1. Around £5bn is being spent all over the country with suppliers for the Olympics. An infographic displaying the dispersion of suppliers around the country would show that this economic boost is spread throughout the country. An effort has been made on London2012.com to show the spread of suppliers, but it is not effective in making its point, nor is it prominent. An ideal infographic would show the influx of supplier income relative to local population, or some other appropriate measure of relative economic boost.
2. Efforts should be made to increase the feeling that it is already possible for everyone to get involved in something related to the Olympics. The existing school-age programme of "Get Set" ought to be familiar to parents of small children, but "Inspire International" and the national cultural programmes could garner more voluntary support. The programmes to encourage greater participation in sport can get off to an early start with the adoption of online and mobile services helping busy people find the time to exercise. An example would be a local council who adopted a social fitness programme encouraging people to eat well and exercise, rating them amongst their peers and providing helpful reminders, suggestions and information. It would be essential that this is delivered over digital channels due to the inconvenience of organising large numbers of people.
3. The Olympic torch relay is a powerful landmark in the period approaching the games. The torch relay for London 2012 is planned to be much smaller than the international relay before Beijing 2008, which sets a promise of an Olympics smaller in scale and ambition than those in Beijing. As both a side-step to this problem and a means of engaging the nation and the world, a Digital Torch Relay would take place. The concept is one of an inclusive race to pass the "torch" between 1m participants. This would take the form of an online game where people can upload their photo into an ever-growing chain. The game would be compelling and encourage people to "pass on the flame" to others in different parts of the country, or parts of the world.
!!! Coverage
The crucial problem during the games is that people who are not in a position to get to an event in London will feel like the Olympics are "someone else's games". This feeling is likely to be stronger in the North of the country.
Because the Internet and mobiles are very good at removing distance between people, these digital channels have a lot of potential to reduce this negative feeling and help people get involved and appreciate the significance of the event.
The BBC will be expected to provide blanket coverage of the Olympics and they are likely to have their own strategies for using digital channels. Since the Olympic Committee are already working with the BBC to install Live Sites, it makes sense to develop the digital coverage strategy alongside the BBC.
1. People use the Internet to create niche content of all varieties, from pictures, comments, videos to amateur journalism. The Olympics is an event around which a massive amount of individual content will be created. The Olympics will be a massive event for the country and for the world, and if people are given an opportunity to participate in a meaningful way, it is likely that they will. This opportunity will be presented in the form of a massive online aggregation and archive of all material that is created about the Olympics over its duration. This will differ from simple automatic aggregators by allowing people to submit their own sources to the archive and create links between different pieces of content. The end result will be finalised shortly after the Olympics closes, creating a valuable online resource that thousands of people will have contributed to.
2. Digital supplements to real-world artifacts will allow people all over the country to get involved in the events surrounding the Olympics. More than 7m tickets are due to be sold to allow people to attend the games - if 70m virtual tickets were available to online events, for example in Second Life, what kind of engagement would that create and from which corners of the planet? BBC Live Sites can be supplemented with digital services such as location-specific SMS reminders to go and watch particular events on the big screen. At the Wimbledon tournament, "Henman Hill" was a popular spot for people without event tickets to gather and watch the tennis in a crowd. What is the online equivalent of Henman Hill as applied to the Olympics?
3. Entrance to the Olympics is reserved for the sporting elite. In the spirit of inclusive participation, a series of social games would be launched, which combine both digital channels and the real world: an online game where people suggest the event they would like to see in the Olympics (tiddlywinks, ballroom dancing) and rate other people's contributions, creating a leaderboard of the country's favourite activities; an alternative-reality game taking players through a nationwide hunt for the perpetrators of industrial espionage and government corruption.
!!! Legacy
The Olympics' legacy is the least well-understood element of the Olympics at the moment. In fact, there is a well-researched plan for making the most of the world's investment in the 2012 Olympics. The principal aim of interest to the average person in the UK is the encouragement of greater participation in sport. Millions of pounds have been allocated to help improve the health of the nation, particularly young people.
The Olympics will provide more value to people once they are over than during their course. This is known as the "Because Effect" - many things are made possible because of the Olympics. The challenge is to make the most of the potential. Digital channels can be used to disseminate information to a large number of people in a way that is relevant to their context in an ongoing manner. Because a long-term relationship can be set up with people, this will create a connection to the UK's wider context as the stage for world events: the World Skill Olympics is in London in 2011; the Commonwealth Games is in Glasgow in 2014. The momentum created by the Olympics will benefit engagement with these other events.
One of the underlying goals of the legacy plan is to change the culture of the country, towards better health, and increased levels of volunteering and social activity. As with any behavioural change, ensuring that someone sees their peers behaving in the desired way hugely increases the likelihood that person will change. The Barack Obama election campaign used this to great effect by broadcasting the inclusive message "a record turnout is expected". The effect of people interacting in social, online environments is that they observe their peers on a regular basis - these environments will play a critical role in determining the success or failure of the legacy's goals.
1. Helping people get started with changing behaviours is all about understanding their context, which is initially an innately human task. Beyond an initial consultation, an accessible digital resource would provide help tailored to an individual, taking into account what is available in their local area. This self-service channel is the only sustainable method of delivering necessary information.
2. Due to the strength of peer comparison in behavioural change, social applications on popular online services, such as Facebook, will provide people the necessary stimulus to form new habits. A similar logic is being deployed by energy companies in the US and Europe, to expose energy use compared to others and thus encourage "green" behaviour. The Olympic Committee would partner with respected, healthy-living brands such as Nike and Fitness First to launch these applications.
If ScraperWiki supported JavaScript, maybe I could use that.
The idea for ongoing testing is that there is a website which tests the API when you visit it; either you set a cron job to test it, you build a visit into the use of the app, or you just rely on people testing it out.
The next things for getting this underway are:
- speak to ScraperWiki about SSJS
- get the HSBC prototype to perform a bank transfer
- figure out how to read off recent transactions from HSBC
Very handy online storage service, where you can install a desktop client that mounts a folder on your machine and keeps it sync'ed with the online version. You can publish pages in a public folder - useful for publishing static websites; you can create shared folders too. Files are versioned, to the point where you can undelete.
If you're working in a shared folder, the conflict resolution is not quite as prohibitive/informative as it should be - in my opinion, it's too easy to overwrite someone's changes and not know about it, or have your changes overwritten when you were working on the document.
PhoneGap developer, responded to search on behalf of [[Sheraz Arif]].
iPhone/iPad
A project to use TiddlyWiki and my ScreenScraping library to put a more friendly interface onto a corporate expenses system.
The Economist [[special report on digital nomadism|http://www.economist.com/specialreports/displayStory.cfm?story_id=E1_TDJVDPJQ]] from April 2008 is relevant. I'm reading it again in March 2010 and following up links and reading comments.
wiki.electronomad.com/org - ongoing research notes.
blog.electronomad.com/org - I don't know yet
www.electronomad.com - agency?
www.electronomad.org - mainly coffee guide (wifi/power/good coffee), then co-working and people and collaboration techniques - articles, research?
On spaces/people - the important thing is that they support communities; intranets at oases
On the .com, put the [[Weekly reviews]] as blog posts. Also put a list of projects, with people lists and links to trackers/other relevance. On .org, put a café guide and a set of helpful articles, starting with tax guide for dummies.
------
The plan is to create Electronomad.com with these sections: projects and people. Projects is about open projects, with the quote "it should be as easy for someone new to get involved as it is for someone already a part of the project", or something pithier of the same thrust. This will be the section for writings about running projects, as well as examples of open projects done by Electronomad, with links to their trackers. It also functions as a project portfolio for Electronomad.
The people section is about personal productivity and about acknowledging what you want. It could also be a listing of the people that have worked on Electronomad projects. I don't know whether this spreads into an area I wanted to use .org for, or even feel entirely comfortable doing at all i.e. listing people.
There's also the blog section, but potentially I wrap the whole thing up as a blog and make the project/people pages category index pages e.g. in a Wordpress theme. Presumably, that requires an index page with the recent posts and selected links.
.org is going to start out as a café guide, with a mobile version that shows you the nearest places/oases. It should definitely allow people to offer suggestions.
The first steps with .com are to get the portfolio bit in place - stating what Electronomad is, what it does, who I am and how to get in touch. Then put out bits about existing projects with links to project trackers; and put links to people who have worked on the projects (I'm thinking that as recognition, it's a good thing to mention people).
.org: "for a lifestyle practised by individuals and small groups in the web industry"; everything you need to emulate .com
.com: "the water camel knows the person not the company". Or "an open digital agency".
----
[[ElectroNomad - some research notes]]
----
The point: to show how to run a "public" project. This means a project where it is as easy as someone new to contribute to it as it is for someone already involved.
What do I need? Examples! Two examples done the best they can be done. They would illustrate: how you could do in-public project development efficiently, what benefit you get from doing that; for learning purposes, these would both have to be in public for real.
-----
A lifestyle magazine for the freelance generation. I edit the coffee section.
http://mailchimp.com - have used this, seems good, very popular
http://www.feelbreeze.com/
''[Update 17th April]'' Excitement! Have a Yahoo! Pipe up that takes a POST from s2w and converts it into an RSS item before POSTing as JSON to an example app at http://postin.appjet.net, which just records and displays the POST: http://pipes.yahoo.com/pipes/pipe.info?_id=ZsJuNEcr3hGw69BO3nBDOQ
-----
''Yay! Patrick Quinn-Graham has put a version up at http://www.s2w.m.ac.nz'' - only open to emails on that domain at the moment, trying to persuade him to open it up.
''this is posted, in a slightly different form, at http://jaybyjayfresh.com/2009/03/26/email-to-http-gimme-kthxbai/''
I really want a service that POSTs the body text of an email to a specified URL. Why? It's holding me up in: [[FOIRequestsUK]], [[TiddlySMS]], [[dm2me]] and possibly others.
It's a question of data momentum: many web sites and services send their updates to people via email, probably because it's judged to be private and may be easier to do than creating RSS feeds for everything; plus, it makes sense for one-shot notifications, where you don't have another way to get in touch with a person without them logging in.
Information can be carried in these emails that's useful to other services. As an example, take Twitter - direct messages are sent as emails to the account owner. But what if the account owner is a bot? If they want to respond to direct messages, the normal method would be polling using the [[Twitter API|http://apiwiki.twitter.com/]]. This is pretty inefficient, and you are limited to the number of times an hour you can do this. Responding to emails seems a much better use of resources.
What could you do with the text of an email? Usually, the text is not going to be in a state that another web service could directly interpret, so it will need to be passed through an intermediate service for some text manipulation. Fortunately, Yahoo! Pipes works fairly well as a text manipulation tool, accepts POSTs as input and can POST to another service as output.
A while ago, I found [[smtp2web|http://smtp2web.com]], which appears to do what I want but, unfortunately, requires you to verify that you own the target, which for the ways I want to use it, I don’t. (Attempts to get in touch with the authors haven’t raised any response, although [[the source|http://code.google.com/p/smtp2web/]] is on Google Code.) Another promising lead is [[mailbucket.org|http://mailbucket.org]], although this currently only converts emails to RSS. If something like [[Notify.me|http://notify.me]] POSTed to a URL, we could hook those two services together.
If someone wants to make something I could use, I'd appreciate that.
Blogged at: http://jaybyjayfresh.com/2010/07/11/employee-effect-on-the-worlds-biggest-companies/
-----
I was wondering on Saturday how you could reasonably compare a large company with a loose band of freelancers, where the word "employee" doesn't operate. I dug out of Wikipedia [[some|http://en.wikipedia.org/wiki/List_of_companies_by_employees]] [[statistics|http://en.wikipedia.org/wiki/List_of_companies_by_revenue]] on the world's biggest companies, figuring that a reasonable comparison would be the amount of money made per employee.
The data is likely to be a bit rough and I used data from different years indifferently, but I was only aiming for a rough idea so I think the conclusions are valid.
Here are the headlines, the data is in a Google Spreadsheet you can play with [[here|http://spreadsheets.google.com/ccc?key=0AgQJ7FGUGIp_dEo1Tk9LQVhvMVFzYnJSR0JZTnFDbWc&hl=en]].
!!!Employees of the world's wealthiest companies pull in an incredible amount of cash
The world's most financially successful companies boast a significant amount of revenue per employee - an average of $1.17m, with Fannie Mae and Freddie Mac (US mortgage megaliths) standing out at front with an amazing $8.7m per employee between them (this dates from 2007, pre-global financial meltdown and government takeover, but still...). Even the lowest earnings per employee I looked at was around $114k, significantly above the [[US average salary|http://en.wikipedia.org/wiki/Personal_income_in_the_United_States]] of $32k.
This has made clear to me how much of an economic heavyweight successful companies can be. It is pretty amazing to see that for every single person Fannie Mae, Exxon Mobil, Legal & General or Goldman Sachs employs, millions of dollars are being pumped into the US economy.
!!!Big doesn't mean rich
Interestingly, the world's most people-heavy companies were not showing such impressive financial results - whilst the average revenue per employee was up near $200k, the highest didn't even break $1m and the lowest was just under $12k (although this is Indian Railways who employ 1.6m people - I couldn't find the average Indian salary but it is likely that $12k is several times bigger).
The big difference between the earning potential of the world's wealthiest and biggest companies could come down to the difference in industry - those with the most staff tended to be in manufacturing, infrastructure and retail, with the richest companies circulating around the world's oil and gas, healthcare and finance.
!!!Most companies don't grow past 500k heads
I wondered whether there was any pattern of companies increasing their revenue per employee up to a certain size, and then becoming more inefficient per head as they continued to grow, so I made a graph for the world's 38 largest companies:
[img[http://spreadsheets1.google.com/oimg?key=0AgQJ7FGUGIp_dEo1Tk9LQVhvMVFzYnJSR0JZTnFDbWc&oid=4&zx=qjewh8-l0zyf5]]
What this shows must be taken with a pinch of salt given that country of origin is not shown. However, we can see that passing half a million employees is very hard; it is also very hard to do that whilst maintaining a high revenue per head.
There appears to be a weak correlation between increasing a company size between 250k and 500k employees and decreasing revenue per head. It would be interesting to expand the data set to include smaller companies.
[img[http://spreadsheets1.google.com/oimg?key=0AgQJ7FGUGIp_dEo1Tk9LQVhvMVFzYnJSR0JZTnFDbWc&oid=3&zx=17jms6-ldw32m]]
http://env-js.appspot.com/
Purely from a financial point of view, what makes sense?
Purchasing Power Parity ratios - something like this - http://www.economist.com/node/16646178 - you divide the cost in dollars with the local cost given in dollars to get a cost of living comparison.
Average salary for web developer ratios ('cos then you can get better people) - here's an international comparison of computer programmer salaries - http://www.worldsalaries.org/computerprogrammer.shtml - already applies PPP ratios; doesn't include India; this suggests Lithunia and then China as places where the money goes furthest.
But what about quality? This - http://pisa2006.acer.edu.au/ - assesses student quality after secondary education.
You could use this - http://www.topuniversities.com/university-rankings/world-university-rankings/2010/results - which gives scores to top universities, although that probably doesn't include a university in Lithunia. What I really want is a score for the higher education system in each country.
There is data here about higher education - http://www.oecd.org/document/52/0,3343,en_2649_39263238_45897844_1_1_1_1,00.html. Chapter A, indicator A1, gives stats about what proportion of a country's adult population achieved what level of educaton, but for OECD only (so no China or India)
Handling the decision-making in a flow of data.
http://alert-grid.com/ - set up logical checks for incoming HTTP signals; send out SMS, email or telephone notifications if rules are met or broken
It's good to keep track of events.
Glenn has introduced the possibility that the website behaves differently if you want to add another tenant to the same property. Reasonable. So I'm going to check that out and see if it means the robot has to change.
Yes! There's a new way in to adding new tenants to a property. It looks like the way to add a new tenant is just to GET the new tenant page i.e.:
{{{
/default.cfm?refpage=remoteref_applicant&action=add&propertyID=DC2043E9-CF22-D50D-1AFD50E27175AE65
}}}
So - as long as we know from the API call whether we want to create a new property or a new tenant, we can do the right thing by omitting the new property step as necessary. This begs the question - what happens if you try to create a new property with identical information to an existing one... it turns out it works, but the property is a separate property with the same address.
Another way to add multiple tenants would be to add them all at once, but this isn't appropriate for SS, since the tenants add their guarantor information individually. So, we need a way to retain the property ID for each property - I suspect it would be best if this was scraped off the remoteref page.
This means I need a new step - step 1b - where we GET {{{/default.cfm?refpage=remoteref}}} and check whether there is a property that matches the one we're submitting. However, I don't actually need to do this yet, as the referencing process does still work with multiple instances of the same property...
''In the event that we change to use multiple tenants to the same property, we need to change the rent on tenancy to be total rent, and each person's the fractional amount''
Problem: after getting all the dependencies up on to Heroku, the HTML parser still seems not to be working. I think this might be because of the old version of node running on Heroku. I'm posting on the Heroku Node.js group to see if anyone has successfully made jsdom work on 0.1.92 node.js; I've also written to nodejitsu asking for an account (as I suspect they're more up to date with the node releases).
---------
They changed the website! See [[here|FCC Paragon robot - old]] for the previous manifestation.
Doing the comparison of the HTTP headers from two identical journeys:
Global observations:
1. Cookies CFID & CFTOKEN are consistent throughout the journey.
2. The CFGLOBALS cookie is updated (with timestamps) at each step.
3. The differences are always just in the cookies, except in the last step.
4. I am assuming I do not need to make GET requests on intermediate pages.
5. When POSTing, Content-Type header is "application/x-www-form-urlencoded" and Host header is "admin.fccparagon.com".
6. All the named form fields also have id's that match the names.
Step 1 - login:
POST http://admin.fccparagon.com/default.cfm
{{{
loginAgentNo=xxxxx
&loginAgentPassword=xxxxx
&+=
&referer=%2Fdefault.cfm%3F
}}}
Response is a 302 to /default.cfm?
The CFID and CFTOKEN cookies are set in the Set-cookie header in the response.
Step 2 - submit property information:
POST http://admin.fccparagon.com/referencing/default.cfm?refpage=remoteref_submit&tdlp=false
{{{
proceed=V
&landlord_title=
&landlord_firstname=
&landlord_lastname=
&landlord_postcode=
&landlord_address_id=
&landlord_flat=
&landlord_building_no=
&landlord_building_name=
&landlord_street=
&landlord_town=
&landlord_county=
&landlord_country=44
&landlord_phone=
&landlord_email=
&requestedwarranty=false
&requestedwarranty_plus=false
&requestedsection21=false
&postcode=e1+7eb
&address_id=
&flat=449+petticoat+square
&building_no=
&building_name=
&street=middlesex+street
&town=London
&county=
&country=44
&property_type=DETACHED
&bedrooms=4
&rent=550
&period=12
&tenancy_start=01%2F12%2F2010
&no_of_tenants=4
&names_of_tenants=jonathan%2C+phillip%2C+boris
&GuidelinesRead=true
&continue=
}}}
Response is a 200.
The BODY of the returned page contains a META refresh tag, with the location of the tenant info page, which includes the unique ID of the property.
Step 3 - submit tenant information:
POST http://admin.fccparagon.com/referencing/default.cfm?refpage=remoteref_applicant&action=add&propertyID=0E5DEE51-FD2C-D7BE-B3871755C73135B0
{{{
proceed=V
&requestedwarranty=false
&requestedwarranty_plus=false
&requestedsection21=false
&requestedcollect_and_pay=false
&smart_deposit=false
&tdlp=false
&customer_contact=Glenn+Engelsman
&title=Mr
&first_name=Jonathan
&middle_name=
&last_name=Lister
&company_name=
&email=jonathan%40electronomad.com
&passcode=
&share_rent=0.2
&reference_type=full_guarantor
&reference_service=48
&application_agent_notes=test.+do+not+process.
&heard_about_1hour=test
&transmit=true
&agent_identification=true
&continue=
}}}
The response is a 200.
!!Writing the robot
I want an online API that I can send data that comes from a form on the SweetSpot site. Really, this needs 1 endpoint, which a form would POST to, and the response would contain whatever the response would be from the website.
Step 1 - simply POST the login credentials.
Save all the returned cookies.
Step 2a - GET http://admin.fccparagon.com/referencing/default.cfm?refpage=remoteref_submit
Check the returned page for the elements with ID's equal to the named form fields required for step 2b.
Update CFGLOBAL cookie.
Step 2b - POST the property credentials.
Check the returned page the meta refresh tag needed to get the URL for step 3.
Update the CFGLOBAL cookie.
The hard-coded elements are:
{{{
proceed=V
&landlord_title=
&landlord_firstname=
&landlord_lastname=
&landlord_postcode=
&landlord_address_id=
&landlord_flat=
&landlord_building_no=
&landlord_building_name=
&landlord_street=
&landlord_town=
&landlord_county=
&landlord_country=44
&landlord_phone=
&landlord_email=
&requestedwarranty=false
&requestedwarranty_plus=false
&requestedsection21=false
&address_id=
&county=
&country=44
&period=12
&names_of_tenants=
&GuidelinesRead=true
&continue=
}}}
The elements I need the data for are:
{{{
&postcode=e1+7eb
&flat=449+petticoat+square
&building_no=x
&building_name=x
&street=middlesex+street
&town=London
&property_type=DETACHED
&bedrooms=4
&rent=550
&tenancy_start=01%2F12%2F2010
&no_of_tenants=4
}}}
property_type can be: DETACHED, FLAT, SEMI-DETACHED or TERRACED.
technically, only postcode and town are required.
Extract the location of the next page from the META refresh tag on the page.
Example (note the lack of quotes around the url parameter):
{{{
<meta http-equiv="Refresh" content="1; url=default.cfm?refpage=remoteref_applicant&action=add&propertyID=xxxxx">
}}}
Step 3a - GET http://admin.fccparagon.com/referencing/default.cfm?refpage=remoteref_applicant&action=add&propertyID=xxxxx
Check for the presence of all the expected elements needed for step 3b.
Update CFGLOBAL cookie.
Step 3b - POST the tenant credentials.
Check response matches something sensible out of this, which is the unique part of the page:
{{{
<b>Thank you</b><br />
<br />
The Applicant 'Mr Jon Lister' has been added to the property.<br />
<br />
The passcode issued for this application is '<b>List145666</b>'<br />
<br />
An Email has been sent to 'jonathan@electronomad.com' to complete the application online.<br />
<br />
<a href="default.cfm?refpage=remoteref_applicant&action=add&propertyID=0FD6FC4C-AFD1-5E18-6CFBC1D9C3DED6AD">Click Here</a> to add another application
or <a href="default.cfm?refpage=remoteref">Click Here</a> to finish.
}}}
Hard-coded:
{{{
proceed=V
&requestedwarranty=false
&requestedwarranty_plus=false
&requestedsection21=false
&requestedcollect_and_pay=false
&smart_deposit=false
&tdlp=false
&customer_contact=Glenn+Engelsman
&middle_name=
&company_name=
&passcode=
&share_rent=1
&reference_type=full_guarantor
&reference_service=48
&application_agent_notes=test.+do+not+process.
&heard_about_1hour=test
&transmit=true
&agent_identification=true
&continue=
}}}
I am assuming that application_agent_notes and heard_about_1hour can just be set to a standard value. I don't think they can be blank, as the client-side validation requires you to fill them in.
Data I need are:
{{{
&title=Mr
&first_name=Jonathan
&last_name=Lister
&email=jonathan%40electronomad.com
}}}
I am looking into writing this on Node.js. This means I need to check that Node.js deals well with HTTP headers.
!!What I need data for
In total, I need:
About the property:
{{{
&postcode=e1+7eb
&flat=449+petticoat+square
&building_no=x
&building_name=x
&street=middlesex+street
&town=London
&property_type=DETACHED
&bedrooms=4
&rent=550
&tenancy_start=01%2F12%2F2010
&no_of_tenants=4
}}}
postcode, flat, building_no, building_name, street, town, bedrooms & rent can be drawn from the property information. Only postcode and town need to be submitted...
tenancy_start can be drawn from the end date of the current tenancy, which we don't currently store! The tenancy date has to be one day less than two months away, otherwise the website redirects you back to the page with an error.
About the guarantor:
{{{
&title=Mr
&first_name=Jonathan
&last_name=Lister
&email=jonathan%40electronomad.com
}}}
These will all be provided by the student.
Steps:
0. GET http://admin.fccparagon.com/
Response includes:
Set-Cookie: CFID=xxxxxx
Set-Cookie: CFTOKEN=xxxxxxxx
Set-Cookie: CFGLOBALS
1. login: at http://admin.fccparagon.com/
POST: loginAgentNo=xxx&loginAgentPassword=xxxx&+=; CFGLOBALS cookie set on response
2. fill in landlord, property and tenancy details at: http://admin.fccparagon.com/referencing/default.cfm?refpage=remoteref_submit
{{{
POST /referencing/default.cfm?refpage=remoteref_submit:
proceed=V
&landlord_name=
&landlord_postcode=
&landlord_address_id=
&landlord_street1=
&landlord_street2=
&landlord_town=
&landlord_county=
&landlord_country=44
&landlord_phone=
&landlord_email=
&requestedwarranty=false
&requestedwarranty_plus=false
&requestedsection21=false
&postcode=E1+7EB
&address_id=7830933.00
&flat=&building_no=449
&building_name=
&street=Petticoat+Square
&town=London
&county=
&country=44
&property_type=DETACHED
&bedrooms=4
&rent=500
&period=12
&tenancy_start=01%2F12%2F2010
&continue=
}}}
Q: where did the address_id come from? A: turns out this is from the PostcodeAnywhere plugin that is doing the postcode lookup, so it can't be important.
Potential problem - putting a tenancy start date too far in the future causes an error. This is identifiable by a redirection back to the page with the parameter '&tdlp=false' in the URL. On Monday 18th October, at 18:27, the latest date I could get through was: 17th December 2010 - so up to one day less than two months away.
The response:
Set-Cookie: CFGLOBALS
3. An intermediate page loads up, which contains:
{{{
<meta http-equiv="Refresh" content="1; url=default.cfm?refpage=remoteref_applicant&action=add&propertyID=xxxxxxxxxxXXXXXxxxXXxx">
}}}
When you GET this, the response contains:
Set-Cookie: CFGLOBALS
4. POST applicant and reference details:
I haven't submitted the last screen, but I can see that the action is
and the POSTed form fields are:
{{{
proceed: hidden, V
requestedwarranty: hidden, false
requestedwarranty_plus: hidden, false
requestedsection21: hidden, false
smart_deposit: hidden, false
tdlp: hidden, false
customer_contact: text, Glenn Engelsman
title: text, Mr
first_name: text, GuarantorFirst
middle_name: text, ""
last_name: text, GuarantorSurname
company_name: text, ""
email: text, gtest@example.com
passcode: text, ""
share_rent: text, 1/5
reference_type: select-one, full_guarantor
declarationofterms_signed: checkbox, false
reference_service: select-one, 48
application_agent_notes: textarea,
heard_about_1hour: textarea, ""
transmit: radio, true
agent_identification: checkbox, true
continue: submit, ""
}}}
I don't know what happens in the response for this.
This is an idea to use http://whatdotheyknow.com (a MySociety project that exposes Freedom of Information requests) and pipe the email notifications about successful requests into Twitter.
After playing with tarpipe for TiddlySMS, I thought it would be easy to use it for this project. The flow would be: email comes in to Tarpipe and is pushed onto Twitter.
!What to tweet
Speaking to @emmaboon has led us to conclude that sending these fields would be good. We only have 140 characters to play with:
title
council name
name of the organisation that the FOI was submitted to (might be the same as the previous)
geographical area
shortened URL
!Technical obstacles
Although a simple idea, in practice this has meant that I have had to go through this process:
#set up a new Goole Mail account for foirequestsuk@googlemail.com ''because'' Twitter wouldn't let me use my email address for a new account
#set up a new twitter account for [[foirequestsuk|http://twitter.com/foirequestsuk]] that emails foirequestsuk@googlemail.com ''because'' I wanted a separate account for the bot
#set up a new tarpipe account for foirequestsuk ''because'' tarpipe only allows one twitter account per account, so I couldn't add @foirequestsuk to my jayfresh account
#sign up with http://www.whatdotheyknow.com using my foirequestsuk email address (not the tarpipe workflow email address) ''because'' you have to confirm your email address by clicking on a link in the email they send you
#I've discovered that I can't now change the email address associated with the theyworkforyou account
#however, I need whatdotheyknow to mail the tarpipe workflow, so I need to be able to get at the email sent to tarpipe
#unfortunately, tarpipe doesn't let me route email to myself yet, as I can't specify an email address in the "To" field of the Mailer module
This means that I am at the point where I need tarpipe to make a change, or I need whatdotheyknow to alter my registered email address once I've made the confirmation. I've made a post on the tarpipe getsatisfaction list, and have emailed whatdotheyknow asking them to change my email address to the tarpipe email address - dads26jags@tarpipe.net
Idea! Forward from googlemail if sent from whatdotheyknow... and then handle that
Had a meeting on Monday 8th June 2009 to talk about Shaun's idea - he is a fashion photographer; he has two colleagues who also take footage; they think they could make more money by selling licenses to an easy-to-browse collection of their footage where the related items were pulled together e.g. a catwalk shot, a close up of the accessories from backstage and the video.
Also attending: Steve Ganly, who set up the meeting; Ivan Kocienski works with Steve; Matthew Byrne and Per Kvalvaag from germinatr.com. Held at Black's. Nice food, not expensive.
A project for LShift to design the interface for a tool that uncovers differences between datasets stored in different systems.
Attn tracked with [[fastdiff]]
<<attentionTracker timeline fastdiff>>
Ken Price = KP
Brett Hodge = BH
[[wiki-data phase 1 consolidated feedback call]] = CALL
Jonathan Lister = JRL
!!Bugs
* on IE:
**the "hide/show columns" box appears behind the table when you mouse over (KP) - READY FOR REVIEW
**the “hide/show columns” function is frozen. (KP) - READY FOR REVIEW
**the column headers do not align with columns. (KP, BH) - READY FOR REVIEW
**cannot add filters (using “+” sign or by clicking on “add filter” (KP, BH) - READY FOR REVIEW
** Home function (mouse over Wikidata) - cursor does not change to indicate functionality is operable - READY FOR REVIEW
*** ''needs looking at again'' - original fix stopped the link from working
**** have put the anchor around the entire header
**** think I've fixed it
* the two-letter country code filter is unexpected (KP)
** auto-complete country/state pick-list instead (KP)
** Company Details Screen - replace 3 letter ISO with full country description (CALL)
** need a list of code->country mappings - can probably get mapping off web given they are ISO codes
* in the “Partners” document, please change the Software Partners section to: (KP)
**Software/Development partners
***Business entity information is a fundamental requirement in almost any software application. Avox will provide test files, documentation and support to software and development firms as well as individual developers that would like to populate their systems with entity data for product development and demonstrations. Typically contracts for usage of the content within developed products will be signed directly between Avox and the end user. Alternative arrangements are possible.
***(Note to JL – does this look OK to you?)
*** I suggested an alternative, which Ken accepted - need to put it in
* URL not wiki-data.com (JRL)
** Avox need to change this
* email sending not in place from challenge, request and suggest pages (BH)
** Email server is now setup, needs the code to use it
* Show that there are more results than 50 on the server (JRL) - DONE
* Change last two sample searches to: (KP) - DONE
**Company Name: Energy, Operational State: Texas
**Company Name: Commodities, Operational Country: Singapore
* country of registration not yet a filter criterion (KP) - DONE
*sorting no longer appears to work on the search results columns. It is possible to drag and drop the columns. (KP) - FIXED
** this is confusing, as I seemed to have fixed this in [[18th September Avox Wiki-data]]
*** ah, the event handling was a bit messed up
*still need registered country on there (KP) - FIXED
*on the form for suggesting a new record, look under the Captcha form and it says “Submit challenge”. I suspect that inadvertently got carried over from that form. It should read “Submit request”. (KP) - FIXED
*remove “registered country” from the request for more data form as it will be included in the public data footprint. (KP) - FIXED
*Operational postcode does not appear on Customer Information screen (i.e. one which displays Google maps) - FIXED
*in the “Partners” document, under the paragraph of the last section “Consulting partners”, please add another paragraph as follows: (KP)
**“For all partnership enquries, please email us at partnerenquiry@wiki-data.com.” - READY FOR REVIEW
* slow, when compared to speed of existing version of wiki-data.com (BH) - READY FOR REVIEW
** Changed server setup to run from apache and mod_wsgi - much faster
* google maps error statement appears (at least for me!) when the the core base data /map appears after a search (BH) - NOT A BUG
** will go away when on wiki-data.com
!!Enhancements
* Search by AVID not required (CALL, BH)
* Challenge AVID not required (CALL)
* Company Details screen - add ability to 'Challenge' and 'Request More Information' (CALL)
* Choice box for RMI should appear on right side of screen (with base info and map) to encourage clients (CALL)
* General Search - reduce filtering options (CALL)
* Enable ability to sort by industry (should we be giving this info away free??) (CALL)
* Company Details screen - formatting needs refining (CALL)
* Terms and conditions - precedent (KP)
** find examples of developer terms on other sites
*on the form for suggesting a new record, I’d like to have a free text field at the very bottom labeled “Anything else we should know”. (KP)
*on the form for requesting more data on an entity, I’d like to have a free text field at the very bottom labeled “Anything else you would like to know”. (KP)
* The performance is pretty good for single company name searches but still quite slow when generic terms are put into the search criteria (e.g. Corp takes about 10 seconds). We should continue to work on that. (KP)
*Automating the upgrade of the database
** Get this problem worked out with Chris and Greg (KP)
* When you drill into a company details (so the base details screen with the google map), I think at this point you should also have the ability to 'challenge' and in particular 'request more information', rather than back track out to the main listing screen. Also, I think the info choice box for requesting further information should be on the RHS of the same screen with the base info and map, rather than a separate screen. The reason being is that it makes the decision to request more easier, and this is where we will be earning the revenue. We want them to push that button! (BH)
* 'request further information'. The choices listed in the drop down box should be grouped. That is, one check box for Registered address, NAICS (code and description), PX and Ticker, Date of rego/dissolved; IP name and AVID etc. This is one area where we can obviously expand. Eg Hierarchy content and also reflection on the Issue-Issuer linkage work....but I guess thats a later release. (BH)
** client should not have to specify what information they require. The Avox response will be all available information in the standard template (CALL)
* With the 'request more info' drill down box, for IP and UP, I think we should include country of registration - not just name and AVID. (BH)
* For general 'search criteria', at all levels - would people actually ever search by AVID???. Does the inclusion of this just clutter the search screen and is it necessary?. (BH)
* General Search: much of the information you can filter by in the drop down box is unnecessary (eg, Operational address street name). Really this should be slimed down to the basic key's - state, country, (BH)
* Also, I think we should include SECTOR information with the free general data footprint (SIC code?). And allow people to sort by this as well.? what do you think?. sector code is key basic data, eg for credit risk, a big consumer/user of this. (BH)
* With Challenge, Id like to see an 'Avox-Check' checkbox, which indicates that the client may understand that there been a change to an entity, but they may not have time to give us the correct info, may not know what it is etc, and this checkbox acts a revalidation trigger by avox. (BH)
* Show the total number of available search results (KP)
* Support 'any field' as an option when filtering (JRL)
!!Design and aesthetics
* I assume the headers will be changed to reflect Avox/DB logo/colours etc?. I dont like the dictionary bit on the top right corner as well. (BH)
* One other suggestion for the footer that contains all the links such as "Suggest a Record". The contrast is too low imo. I think the font needs to be black or the bar needs to be darker. It is quite difficult to see. Perhaps we could use the DBAG blue as per our logo (which I happen to like anyway). Anyone have a different view? (KP)
* Im interested on how the feedback loop will work for contributors who challenge and requesting further information. Also, we need to very soon think about the payment options/prices for requesting further information. Our response to this and timing of the 'payed'release needs to be very clear by the time of your launch at FIMA. (BH)
* Encouraging feedback: I'd like to enhance the 'challenge' process by providing credits to the contributor. I think this is the best way to encourage 'challenges' which are essential to our business model of creating a collaborative feedback model of triggers to reval the data. The credit can be used for other company detail searches. say 1 credit for alerting us to a change via the 'avox-check' checkbox, and say 2 for actually changing details??. Down the track perhaps there should also be some sort of public acknowledgement process. (BH)
* Smart search. Is there such a thing as an 'intelligent' search engine?. That is, one that can be modified by avox on an ongoing basis?. So, a 'CS' search will also show credit-suisse, SCB will show standard chartered bank listings (etc)?. (BH)
* we need to change the 'who are you' bit when you challenge and request info. (grammar etc). Some general questions here: can a 'registered user' avoid the admin overhead of doing this?. Also whats with the security text boxes?? (BH)
* Something buzzing in my mind is that not all our data willbe correct of course. We wont pick up all changes, and there will be some out there who will use this againt us. At some point we should discuss an ''accuracy tag''. This may be subtle - like a colour code on a the key listing screen which shows that an entity's details has changed in the last 90 days...the added advantage here is that it also encourages people to buy the new details. (BH)
TiddlyWiki.prototype.filterTiddlers = function(filter)
{
var results = [];
if(filter) {
var tiddler;
var re = /([^\s\[\]]+)|(?:\[([ \w]+)\[([^\]]+)\]\])|(?:\[\[([^\]]+)\]\])/mg;
var match = re.exec(filter);
while(match) {
if(match[1] || match[4]) {
var title = match[1] || match[4];
tiddler = this.fetchTiddler(title);
if(tiddler) {
results.pushUnique(tiddler);
} else if(this.isShadowTiddler(title)) {
tiddler = new Tiddler();
tiddler.set(title,this.getTiddlerText(title));
results.pushUnique(tiddler);
} else {
results.pushUnique(new Tiddler(title));
}
} else if(match[2]) {
switch(match[2]) {
case "title":
store.forEachTiddler(function(title,t) {
if(title.indexOf(match[3])!==-1) {
results.push(t);
}
});
break;
case "tag":
var matched = this.getTaggedTiddlers(match[3]);
for(var m = 0; m < matched.length; m++)
results.pushUnique(matched[m]);
break;
case "sort":
results = this.sortTiddlers(results,match[3]);
break;
}
}
match = re.exec(filter);
}
}
return results;
};
Also, it's probably a good idea to revise the left-hand menu of this workbook to make it more portfolio-like. In fact, I can imagine portfolio-ising my stuff to be pretty important thing for me right now, perhaps that indicates what my [[new top Want|Review week ending 21st June 2009]] is...
-------
Started w/c 4th May 2009.
As part of going freelance, I think it's important to make sure my portfolio is looking as good as it can be. I have a handful of cool but half-done projects that would benefit from some love. Examples: TiddlyTweets, EasyExpenses, [[dm2me]], PixelPatternMaster, RelevantTickets, [[BonnieParsons.com]], CoffeeSnob, [[Contributively]].
This will also help with making a standard launch process for web apps (e.g. start a Twitter account, put up Google Analytics) - which I could write about - and hopefully with providing demand for a hosted version of TiddlyWeb.
Then there's blogging... a set of manifesto-like articles might be useful... probably to back up the "[[credo|http://jaybyjayfresh.com/credo]]" page.
Fabio ([[@faenrique|http://twitter.com/faenrique]]) and Robert strike out on their own.
Previously [[Blessed Blend]].
Nick Webb (@nickwebb) made Flock O' Tweets (http://flockotweets.com) using Yahoo! Pipes. Nice. He wanted to provide a daily digest of tweets and it turns out this is hard to do in Pipes, as it involves combing many feed items into one, which is difficult.
As an experiment with AppJet, I made an app (http://itemcombiner.appjet.net) to take a feed and combine all the item titles into one item's description. Currently, that item's title is "Digest".
Nick has taken the pipe and made another, really complex pipe (!), that does exactly what he wants! Nice. It's up at: http://pipes.yahoo.com/pipes/pipe.info?_id=7a89129e964a367adb8a0e50d8e158e1
http://www.icebrrg.com/ - take payments, have your forms hosted - $9-$199/month
http://fortumo.com/
Provide some pre-built services like:
SMS-chatbox - put a chat colum on your blog/webpage
SMS-info - send a keyword, get some info
SMS-pay - send a SMS to get access to a protected file/area of site
!The problem
I can't see the http://moveyourframe.com class calendar in my Google Calendar, which would be nice!
!The approach
#Use dapper to get a RSS feed of each day's classes
#Use a Smart app to provide an iCal feed of the week's classes
!8th August Progress
#Showed Pip and Jo @ Frame - they liked it. Said I'd improve some bits, so...
#Ran into a problem where the Smart server is set to UTC, so it wasn't able to figure out that the class times are currently BST (unlike the local version running in BST)
** ''have manually corrected, must remember to correct again or make it automated by 22ND OCTOBER''
#Getting the durations right
** Is the Dapper feed chopping off the description? Each class has a duration on it
*** modified the Dapp to produce the class duration as a property in the item
** modified the Dapp to produce the full summary as well as a separate description
#Found that saving a string to a Resource with a '£' in it breaks Smart
** Reported here: http://discuss.joyent.com/viewtopic.php?pid=203495
#Found that line-breaks by themselves in iCal fields is not valid...
** replacing with '\\n'
#Google Calendar doesn't seem to accept the feed anymore - 'we could not parse the calendar at the URL requested'
** took the line-breaks out to see if it helped - it didn't
** there's a suggestion it might be due to caching - ''going to wait and see if it works tomorrow''
!5th August Progress
*I tried to use Dapper to provide an iCal feed, but I couldn't combine data fields into iCal fields, and the raw data fields aren't suitable; I've settled for a RSS feed
** I have these fields:
*** Class
*** Time
*** Class leader (''not for each class'')
*** Cost (''not for each class'')
*** Description
** I'd like to provide a link to the 'book now' page, but I've noticed you get to it by POSTing to a form; this is different to just grabbing a link, so it's something to look into later
*I found an example [[iCal spec|http://en.wikipedia.org/wiki/ICalendar]] on Wikipedia:
{{{
BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//hacksw/handcal//NONSGML v1.0//EN
BEGIN:VEVENT
DTSTART:19970714T170000Z
DTEND:19970715T035959Z
SUMMARY:Bastille Day Party
END:VEVENT
END:VCALENDAR
}}}
* I had a go getting [[Scriptlets]] to output the above, at http://www.scriptlets.org/run/15dt923 - it imports into Google Calendar
* Not sure whether to use Scriptlets or Smart to do the conversion (Scriptlets is a bit annoying because you can't edit scriptlets, so you're always going back and forth)
** Decided to build a converter with Smart
*** Two endpoints - {{{/refreshCalendar}}} grabs the current two weeks listings from Dapper and converts them to iCal; presumably, it'll save them as Resources. I expect this will be run on a cron. Second endpoint is {{{/calendar}}}, which returns the iCal and will be used as the import URL for e.g. Google Calendar
* Found two limitations:
## Rate limiting on dapper (don't know whether it's accidental or done on purpose) - this has led me to put the day variable in the query string so you can manually update rather than doing it all at once
## Operation limiting on Smart - I had to increase from 100k to 200k because the app was just dropping out with a blank page - problem discussed on forum [[here|http://discuss.joyent.com/viewtopic.php?id=25651]]
* I have a working version up at http://frameclasscalendar.smart.joyent.com/calendar
** Improvements include: getting class duration right, adding explanation text to 'index.html', automating daily updates (cron? Smart thing?), adding link to 'book now', saving calendar iCal to static file so it's really quick to load
** Another thought is that I could drop dapper and just screen-scrape myself, as it turns out the hidden fields are very simple to understand, but not available to dapper (although using dapper is cooler)
Sometimes you need to create a new email account for a bot or some such.
| Name | Input | Output | Free? | Notes |
| [[Mailinator]] | ? | ? | Yes | Totally open |
| [[Google Mail]] | SMTP | SMTP, IMAP | Yes | n/a |
Met through Josh. Graphic & design consultant. He was working with [[de Candole Robinson|de Candole]], wanted some help animating a site.
http://www.gistudios.co.uk/
http://www.geekmap.co.uk
Information here: http://wiki.geekmap.co.uk
I'm helping with design, client-side development, ruby views and project structure.
I'm blogging too. Here are my articles: http://blog.geekmap.co.uk/author/jnthnlstr/
This time:
1. superfeedr or equivalent to monitor feeds and push them to XMPP
2. imified to route XMPP to web app (I've noticed imified works now...)
3. node.js to host web app and send tweet on to cloudvox (or equivalent. Ribbit?)
4. cloudvox to SMS me
Alternatively:
1. superfeedr to monitor feeds and push them to PuSH
2. feednotifier.org sends notifications via Notify.io (this bit doesn't seem to work, which is well annoying - Julien G suggests building my own version of feednotifier.org to see if anything is being pushed ('cos it's open-source)
3. a webhook picks this up and sends the message to cloudvox (or sends a dm to me)
4. I get an SMS with the tweet, yay.
----
The alternative is still to:
1. monitor @ replies feed
2. changes get emailed to me
3. emails get forwarded to tarpipe
4. tarpipe sends email to Yahoo! Pipes
5. Pipes extracts the message and the sender
6. Tarpipe sends a dm to me via another Twitter account
7. Twitter sends me message via SMS
At 6, you could instead do this:
6. Tarpipe sends a dm to dm2me robot
7. dm2me reflects me the message
8. Twitter sends me message via SMS
That does rather mean that you have to build dm2me...
1. dm2me receives tweet
2. Twitter sends email
3. email is forwarded to Tarpipe
4. Tarpipe sends to Y! Pipes
5. Pipes extracts message and sender
6. Tarpipe sends dm to sender with message
Want: to run jQuery on the server so I can be using the same code on the server as on the client.
A few potentials:
* Env.js + jQuery - need to run the test suite for Env.js / jQuery
** I have tried putting this into a Smart app, it loads without errors, but the window.location setting doesn't seem to load any pages
** ok, some advice from Chris T - http://groups.google.com/group/envjs/browse_thread/thread/f42f48a515852fcc
*** can't get env.rhino.js to work, I think because Smart is running Spidermonkey... so I'm looking for the Spidermonkey port
** Realisation: need Env.js (or maybe jsdom) to work before the other libraries will pass their tests, since they need a document model to work.
* [[jsdom|http://github.com/tmpvar/jsdom]] - need to run the test suite; implemented using [[CommonJS|http://commonjs.org/]] API
** attempting to work this into a Smart app that uses the new API - asking how to use the new features locally [[here|http://discuss.joyent.com/viewtopic.php?pid=212990#p212990]]
* [[XUI|http://github.com/cluster/xui]]
** test suite core.html doesn't pass all tests
** need to put inside a Smart app and see if I can figure out how to run the test suite core.html
* a different approach - [[Underscore|http://documentcloud.github.com/underscore/]] gives you the utility functions I was really after when I started looking at how to support jQuery on the server
People:
Ryan Dahl (creator of [[Node.js]]) works for Joyent
Brock Whitten (@sintaxi) works at Nitobi on PhoneGab and XUI
Chris Thatcher - [[Env.js]] (community mainainer)
Scott McWhirter (@konobi) - full-time on Smart Platform for Joyent
Steven Parkes - Env.js (Ruby port)
This is a development of a way to operate a collaborative and commercial project. It's similar to the idea of an 'open business'.
!Go Public principle:
#Make it as easy for someone new to make a contribution as it is for you
And more...
*Publish everything, including financials
*Publish information about what we're doing and what we plan to do next (see motivation below - posting about individuals too)
*Open source code (not necessarily everything, but whatever could be useful and is generic)
*Let anyone contribute that wants to
*Create stats and as much information as possible about problems and make these public
''These are some ideas; newer near the top (generally):''
!Credit notes idea
You issue credit to people when you do work for them, pricing stuff as you would if you were selling it. This gives them an idea of what it would have cost to get your investment had they just paid you, which means they can (a) buy you out if they want to; (b) put a limit on payments through profit/revenue-sharing; (c) have help valuing your contribution when thinking about profit/revenue-sharing.
This idea provokes some other thoughts - you could enhance the quantified value of your contribution by adding interest (or similar) to reflect the risk of delaying payment; people will disagree on what is a fair valuation and a fair enhancement; providing a cost that you know is not immediately going to need to be paid encourages price inflation and seems open to gaming.
!Open contributions and reward (also see [[motivation, influence and rewards in open groups]]):
Made easier if we publish what we need - code, user stories, plans...
Kitty - shared profits up to the point where it becomes unmanageable.
Not sure yet how you quantify someone's contribution. Some ideas: let the team assess size; use a points system where a) people can allocate points as you go along as much as you want, or b) you have a set number of points you can use up; maybe another points idea based on the thing where you send an email only if you have enough credits and people can opt to give you your credit back if they like/don't hate your email; ''see new idea below''
Other people thinking about this:
E-Text Editor: http://e-texteditor.com/blog/2009/opencompany (trust metrics proposed to figure out reward)
!!Contribution measurement
You partner people up into pairs who work together closely as you progress through an iteration. Apart from two people, each person is a member of two pairs in such a way that, if you drew the network of connections out on paper, it would not have any closed loops (see below). At the end of an iteration, you have both people in a pair work out, together, their relative contributions to that iteration. From these, you can figure out the relative contribution between any two people.
To understand why it is important not to create closed loops when pairing people up, imagine 3 people: A, B, C. Take the pairs as A and B; B and C; C and A. Once the first two pairs have worked out their relative contributions, the relative contribution between C and A is fixed mathematically, so it is a conflict for them to agree between themselves what their relative contribution is. This reflects the inherent subjectivity in the system, which you can combat by partnering people with those who are most likely to be able to judge their relative contributions.
For an example, take 4 people working on an iteration: A, B, C and D. A and B partner; B and C partner; C and D partner. At the end of the iteration, the pairs decide their relative contributions as shown in the table below.
| Pair | Relative contribution |
| A, B | 50%, 50% |
| B, C | 25%, 75% |
| C, D | 90%, 10% |
You can now calculate the relative contribution between A and C, A and D or B and D.
[[Contributively]] is a tool designed to help with this.
!Motivation:
People are, I imagine, motivated to work on something by: expectation of money or expectation that people will think they're really cool.
Here's a thought - if your livelihood depends on something, you'll be motivated to do it.
Just realised that if your team can't be bothered to do something, better to find someone who is than do a bad job of it.
Create a sense of accountability - talk openly about what people have done and are intending to do next (on your blog, for instance).
http://www.google.com/analytics
See [[Google Tasks integration into TeamTasks]]
Had a look at Google Tasks via the new mobile version at:
http://mail.google.com/tasks/android
You can do a string grab from "_init(){_setup(" to ")" to get an evaluateable string that gives you all the information about the tasks, including:
{{{
e: your email address
ik: some string like "fff9cd0f42" - looks like a hex colour, but with the wrong number of chars
t: info about tasks
latest_sync_point: number
lists: lists array
1 entry with properties: acl, child_entity, id, name
tasks: tasks array
each has properties: archived, completed, creation_date, deleted, id, list_id, name
v: 8-digit number (likely to be client_version - see below)
}}}
! Method to discover nature of API
!! Log and diff HTTP headers and requests
# Log the HTTP headers sent when you repeat the same action (ticking or unticking the same task) and when you repeat similar actions (ticking or unticking different tasks). I use [[Live HTTP Headers|https://addons.mozilla.org/en-US/firefox/addon/3829]] in Firefox for this.
# Diff the headers from the same action to find out which are the most volatile data fields. I use opendiff that comes with the Mac OS X Developer Tools, but these two are free and cross-platform: [[Meld|http://meld.sourceforge.net/]], [[DiffMerge|http://www.sourcegear.com/diffmerge/index.html]].
# It can help to add linebreaks between fields (using a search and replace & -> \n) and unencode characters so the diff is clearer
## In this case, I found "action_id" went from 1 to 3; "latest_sync_point" changed.
## The cookie in the request header stays the same.
# Diff the headers from ticking different tasks.
## "action_id" went from 1 to 5; "id" dropped by 1; "latest_sync_point" changed.
# Diff the headers from unticking the same task twice.
## "action_id" went from 8 to 12; "latest_sync_point" changed.
# Diff the headers from unticking different tasks.
## "action_id" went from 8 to 6; "id" dropped by 1; "latest_sync_point" changed.
# Diff ticking and unticking the same task:
## "action_id" went from 1 to 8; "entity_delta, completed" went from "true" to "false"; "latest_sync_point" changed.
# Diff ticking one task and unticking another (at this point, we expect "action_id", "entity_delta, completed", "id" and "latest_sync_point" to change.
## "action_id" went from 3 to 6; "id" dropped by 1; "entity_delta, completed" went from "true" to "false"; "latest_sync_point" changed.
The data in this step (with example values) are:
{{{
http://mail.google.com/tasks/r/i
POST
r={
"action_list":[{
"action_type":"update",
"action_id":"1",
"id":"11079462339530044910:0:221",
"entity_delta":{
"completed":true,
"entity_type":"TASK"
}
}],
"client_version":12374053,
"current_list_id":"11079462339530044910:0:0",
"latest_sync_point":1237852950987000
}
}}}
The conclusions we can make at this point are:
# id probably comes from "t.tasks" in the _setup data
# latest_sync_point looks like the kind of thing that would be returned in the response
# action_id is confusing; looks like a count; ticking uses odd numbers, unticking uses even; not as simple as that though
!! Log and diff HTTP response
The response is always (with example data):
{{{
{
"latest_sync_point":1233922300347000,
"response_time":1233922300,
"results":[],
"tasks":[],
"groups":[],
"lists":[]
}
}}}
Conclusion:
# lastest_sync_point is indeed coming from the response
!! Further experiment to figure out what determines action_id
I repeated the first step, this time making sure I kept track of the order in which I did things. The conclusion:
# "action_id" is a count of your actions, incrementing by 1 each time
! Experimenting with sending requests
Now that I have an idea of what to send to tick and untick tasks, I'm going to check whether I can send a request from a local file or not.
Annoyingly, I'm getting a 401 Unauthorized back from Google. The differences in the HTTP Headers I'm sending are:
# X-Requested-With: TiddlyWiki 2.4.3 (vs. not there when sending from Google)
# Referrer: (empty) (vs. http://mail.google.com/tasks/android)
# AT: (missing) (vs. 1)
# Content-Type: application/x-www-form-urlencoded; charset=UTF-8 (vs. "utf-8")
After advice from @mahemoff, I tried using {{{curl}}} from the command line to replicate the headers and see what was the crucial header. It turns out the you have to send {{{AT: 1}}} for the request to be successful. ''Sorted''. Moving on...
! Listing tasks
Each task in the tasks array captured above has the following properties (with example data):
{{{
tasks
archived: false
completed: true
completed_date: 1237813085316
creation_date: ""
deleted: false
id: "11079462339530044910:0:225"
list_id:
0: "11079462339530044910:0:0"
name: "I'm a task!"
}}}
! Adding tasks
Add some stuff here about what to POST to add a task.
! Plan
Now, I can put up a demo of the thing working and blog this. Can mention ScreenScraper lib too. Prob should whack into a standalone jQuery-based app.
This does also bode well for TiddlyCity, which is going to rely on being able to POST to Google, for mimicing clicking "add to my maps" when you've overlaid a KML file. Probably.
{{{
// CustomTracker as a namespace for tracking related functions
var CustomTracker = {
// Specify your account number here!
_uacct: "UA-8392022-1",
// store a reference to the original displayTiddler function
displayTiddler: story.displayTiddler,
// initialise the Google tracker
pageTracker: window._gat ? _gat._getTracker("UA-8392022-1") : null
};
CustomTracker.track = function(path) {
if (document.location.protocol == "http:" || document.location.protocol == "https:") {
this.pageTracker._trackPageview(path);
}
};
CustomTracker.trackAndDisplayTiddler = function(srcElement, t) {
// cope whether a tiddler or a string is passed
var title = t.title || t;
// log with the tracker
CustomTracker.track('/' + title);
// call the original displayTiddler function
CustomTracker.displayTiddler.apply(this,arguments);
};
// replace the default displayTiddler function with a tracking version
story.displayTiddler = CustomTracker.trackAndDisplayTiddler;
// Call once for the initial page load
CustomTracker.track();
}}}
I'm doing Phil a website.
http://gracestpauls.com
Ray - Creative Zones - 07702 370404 (hosting)
Josh doing the design.
Put it up on 21st April 2010.
/***
|''Name:''|HTMLParserPlugin|
|''Description:''|parse a HTML string into a DOM|
|''Author:''|PaulDowney (psd (at) osmosoft (dot) com)|
|''Source:''|http://whatfettle.com/2008/07/HTMLParserPlugin/|
|''CodeRepository:''|http://svn.tiddlywiki.org/Trunk/contributors/PaulDowney/plugins/HTMLParserPlugin/|
|''Version:''|0.2|
|''License:''|[[BSD License|http://www.opensource.org/licenses/bsd-license.php]]|
|''Comments:''|Please make comments at http://groups.google.co.uk/group/TiddlyWikiDev|
|''~CoreVersion:''|2.4|
!!Documentation
Provides a JavaScript ~HTMLParser class intended to be used by other plugins to parse HTML files into a DOM object.
Much of the code and idea for this plugin originates from Jon Lister's experimentations with [[iframes|http://en.wikipedia.org/wiki/IFrame]].
There is a simple [[standalone test|simple.html]] for this library, which ideally should be rewritten using [[QUnit|http://docs.jquery.com/QUnit]].
!!Code
***/
//{{{
if(!version.extensions.HTMLParserPlugin) {
version.extensions.HTMLParserPlugin = {installed:true};
HTMLParser = {};
HTMLParser.removeScripts = function (str) {
if(str){
str = str.replace(/<script(.*?)>.*?<\/script>/ig,"");
str = str.replace(/(onload|onunload)(=.)/ig,"$1$2\/\/");
}
return str;
};
HTMLParser.iframeDocument = function (iframe)
{
if(iframe.contentDocument) {
return iframe.contentDocument;
}
if(iframe.contentWindow) {
return iframe.contentWindow.document; // IE5.5 and IE6
}
return iframe.document;
};
HTMLParser.parseText = function (text,handler,context)
{
// create hidden iframe to hold HTML
var iframe = document.createElement("iframe");
var id = "parseHTML-iframe";
iframe.setAttribute("id", id);
iframe.setAttribute("name", id);
iframe.setAttribute("type", "content");
iframe.style.display = "none";
document.body.appendChild(iframe);
// callback fired when iframe HTML has been parsed
var onload = function (event) {
var iframe = arguments.callee.iframe;
// poll for load complete
if(iframe.readyState){
if(iframe.readyState!=4 && iframe.readyState!="complete"){
window.setTimeout(arguments.callee,10);
return false;
}
}
// call user handler
var handler = arguments.callee.handler;
var context = arguments.callee.context;
if(handler){
handler(HTMLParser.iframeDocument(iframe),context);
}
// delete iframe asynchronously
// - inline caused spinlock in FF3.1
var collect = function() {
var iframe = arguments.callee.iframe;
iframe.parentNode.removeChild(iframe);
};
collect.iframe = iframe;
window.setTimeout(collect,10);
return false;
};
// call user supplied callback with context when HTML loaded
onload.iframe = iframe;
onload.handler = handler;
onload.context = context;
// write HTML text into the iframe
var doc = HTMLParser.iframeDocument(iframe);
text = HTMLParser.removeScripts(text);
doc.open();
doc.writeln(text);
doc.close();
// IE6 and Opera don't support onload event for iframe ..
window.setTimeout(onload,10);
return;
};
}
//}}}
See [[Review week ending 14th June 2009]] - I improved my estimate of non-habitual time spent to 31 hours.
-------
I've analysed a week's worth of information and the results are below. I think after another few weeks of this, I'll have a good idea of what I spend my time on and I can properly adjust my estimates of my habits.
<html><img src="http://spreadsheets.google.com/pub?key=rKfpRrNqJyy1CFwaq7_e0xg&oid=2&output=image" /></html>
--------
w/c 1st June - I'm measuring what I'm doing this week. I suspect I'll find I spend a lot of time doing random stuff...
This is meant for habits that I have, not habits I want. You'd list a desired habit as one of the goals for a [[want|Wants]]. I think I'll end up using this for figuring out how much of my week I already have used up by habitual things.
Don't put things here that are projects, put things here that are outside the scope of those.
Don't forget to include travel-time.
| Habit | Hours a week | Sticking to it? | Notes |
| [[Hanging out with friends]] | 20 | Yes, but should measure for accuracy | Includes pubs, eating out, general socialising |
| [[Spending time with Bonnie]] | 20 | Yes, but should measure for accuracy | |
| [[Admin]] | 2 | Yes | Includes review of the week and planning the next week's [[Top Wants, Goals and Projects]] |
| [[Gym training]] | 6 | Yes | |
| [[Bonnie's dance class]] | 3 | Yes | |
| [[Cartoons]] | 1 | Yes | |
| [[Improving cooking]] | 3 | Yes | |
| [[Reading the Economist]] | 5 | Yes | |
| [[Using Facebook more to keep in touch with people]] | 1 | No | |
| [[Reading more]] | 5 | No | |
| [[Sleeping]] | 56 | No | |
| [[Training for Great North Run]] | 6 | No | |
| [[Climbing]] | 4 | Yes | |
There are 168 hours in a week. The above adds up to 132 hours, leaving 36 hours a week for other things. That's a bit of a surprise!
<html><img src="http://spreadsheets.google.com/pub?key=rKfpRrNqJyy1CFwaq7_e0xg&oid=4&output=image" /></html>
It's easy to forget to make space for quality time with the people who are closest to you.
It's easy to forget to make space for quality time with the people who are closest to you.
Met Jim Mellor Thursday 11th June 2009, because Dan organised it. Jim's girlfriend Anna is Dan's colleague at BBC Switch.
http://www.hogarthww.com/hogarthCortex.html
We found out a lot about how the digital media world works and how old-fashioned some of the technology used is - expensive tapes are shipped by courier to houses where $250k machines process their contents. Jim's company are using Mac's and digital transfer to undercut the traditional houses and win business.
Because Jim's clients are advertising companies, they are interested in any creative digital ideas that improve the adverts they produce. Jim is interested in us creating iPhone application prototypes to help them win business.
''Jim is waiting to see examples of our work and will arrange a meeting with partner, Mark Keller''
You don't want to run a SMTP server. Send email using HTTP.
http://elasticemail.com - $0.001 per email, pay-as-you-go
http://emailyak.com/ - receive as well as send
http://sendgrid.com - 200/day free
I'm trying to maintain a delicious tag for these, and I might think of a better name than "hosted services".
These are services that you can plug together to do certain things in a system.
My first tutorial about this subject is on my blog:
http://jaybyjayfresh.com/2009/07/30/how-to-build-a-diy-twitpic-without-any-coding-skillz/
Things to check out:
http://lessprojects.com/
http://www.liquidplanner.com/ - looks interesting, because it combines project management with wiki-like pages and social chatter
http://www.sohoos.com - free suite of business management tools, and some premium tools such as credit-card processing
http://www.timetableapp.com - project management
Also see: [[A survey of hosted application building tools]]
These links take you to lists of the various types of service I've come across: <<tagging "HostedServices">>
----
Nick: Ok, so most of the stuff I've added below doesn't belong here.
!Graphics
http://www.atebits.com/scribbles/ - not a web app (Mac), but for people with a web-design mindset (i.e. not PhotoShop!)
http://paintbrush.sourceforge.net/ - also a desktop app (Mac)
!Book keeping
| Name | Any good? |
| BlinkSale | Not tried |
| QuickBooks | Not tried |
!Task Management
| Name | Input | Output | Free? |
| Remember The Milk | ? | ? | Yes, with paid (add more detail) |
| Google Tasks | REST | ? | Yes |
!Automated Time Auditing:
| Name | Any good? |
| Rescue Time | Yes-ish |
| Time Snapper | Not tried |
| http://keeptempo.com | Not tried |
!Diagram
| Name | Any good? | Free? | Notes |
| http://my.lovelycharts.com/ | Yes | Yes, plus paid | Doesn't publish to a URL, which is annoying. Otherwise, basic, but easy to use |
| http://www.dabbleboard.com/draw | Yes | ? | |
| http://www.websequencediagrams.com/ | Yes | Yes | Does lovely flow diagrams from a text description |
Also:
http://www.drawanywhere.com/ - looks a bit crap
!Collab Writing
| Name |
| Etherpad |
!TextMate for Windows
http://www.e-texteditor.com/
!To Take A Look At
Ning, Rollbar, Longjump, Intuit, Coghead, Dipity, Zoho, Zamzar, Wufoo, CollabTrack, I'm Cooked,
Tag for [[Hosted services]]
<<SimileTimeline HostedServicesTimelineSpec>>
|timelineHeight:|500|
|bubbleWidth:|350|
|bubbleHeight:|200|
|band0.width:|80%|
|band0.intervalUnit:|MONTH|
|band0.intervalPixels:|100|
|band0.date:|Jan 1 2010|
|band0.eventSourceType:|tiddlerFields|
|band0.eventSourceParams:|HostedService|
|band1.width:|5%|
|band1.intervalUnit:|WEEK|
|band1.intervalPixels:|100*7/31|
|band2.width:|15%|
|band2.intervalUnit:|MONTH|
|band2.intervalPixels:|25|
|band2.showEventText:|false|
|band2.eventSourceType:|tiddlerFields|
|band2.eventSourceParams:|HostedService|
Really simple, 1-click install. Not a techy thing.
http://www.webfaction.com - from $9.50 / month (down to $5.50 if you buy 5 years)
This, seemingly innocuous, question is an excuse to bang on about a few tools of the modern web designer.
The process:
* realise car-parks must be affected by traffic being lower
* find stat about reduction of 21% or 70k cars a day
* wonder how many car-parks NCP have and how many are inside the London congestion charging zone
* find the car-park locator on NCP website
* discover URL of data feed - ''Firebug Net tab as snooper''; see reference to total count; hack params to get full feed
* convert feed into data structure of KML placemarks - ''Firefox as a dev environment''
* find KMZ of London congestion charging zone and convert into KML
* extract all co-ordinates of congestion charge zone from KML file - ''regex''
* figure out bounding box of congestion charge and trim car-parks list to those inside this box
* generate car-parks KML
* combine two KML files
* display on Google Maps (screen shot [[here|http://dl.dropbox.com/u/331606/carparks/carparksandcongestionzone.jpg]]) - ''dropbox as web hosting''
* visually inspect to not count those outside congestion charge zone - ''manual can be ok''
Verdict:
* There are 46 of NCP's 712 car-parks affected by the congestion charge, which is 6%. This doesn't tell me what an effect this will have had on NCP's income, since I don't know what percentage of cars were just driving through the congestion charge zone before it came into effect. Central London car-parks are significantly more expensive than car-parks in the centre of other cities nationwide e.g. around £11 for two hours versus around £5. I also don't know how what percentage of cars are parking in NCP car-parks vs. office car-parks or other car-parks.
Blogged at: http://jaybyjayfresh.com/2009/07/30/how-to-build-a-diy-twitpic-without-any-coding-skillz/
!Accounts you need
#A blog you can upload photos to from your phone (e.g. [[Blogger|http://blogger.com]] account on my Sony Ericsson K800i)
#[[Notify.me|http://notify.me]] (or other email notification service)
#[[Gmail|http://mail.google.com]] (or other email account with mail filters and auto-forwarding)
#[[Tarpipe|http://tarpipe.com]]
#[[Yahoo! Pipes|http://pipes.yahoo.com]]
#[[Twitter|http://twitter.com]]
!Coding skillz you need
#None
!What is ~TwitPic?
In a nutshell, [[TwitPic|http://twitpic.com]] lets you upload pictures and automatically tweet a link to them. You send photos by emailing them to ~TwitPic.
!Why would I want to build my own ~TwitPic?
For me, it comes down to having an existing place that I post pictures to from my phone. In my case, I have a Sony Ericsson K800i, which offers me the option to 'Blog this' when I take a photo. If I choose to, the photo is uploaded to a Blogger blog. This is neat, but I'd really like to be able send out tweet with a link to new photos, as I generally take photos of things I'd like to show to people.
Another reason to want your own auto-tweeting photo-blog is that you are not able to use ~TwitPic because you can't send email with photo attachments from your phone.
!How to build your own ~TwitPic (without any coding skillz)
I'm going to run through how to make a complex system without writing any code. This involves mashing together existing services to get the desired cause and effect you're looking for. The first thing to understand is the flow of data through the services we're going to use. After that, I'll step through how to set up each bit.
!! The flow
The flow looks like this: you take a photograph on your phone and choose to send it to your mobile blog (this is the point when you lose control); the blog system updates the RSS feed for your blog; Notify.me (or another notification service) notices the change and sends an email message to you with the body and title of the RSS item; Gmail (or another mail service) filters the incoming email and forwards it to a Tarpipe workflow; Tarpipe posts the email to a Yahoo! Pipe; Yahoo! Pipes extracts the URL of the new blog post and formats the text of the tweet, before returning the information to Tarpipe; Tarpipe tweets on your Twitter account.
Tabulated, this looks like this:
| ''this thing'' | ''does this'' | ''to this'' |
| Person | sends photo | to Blog |
| Blog | updates | RSS feed |
| Notify.me | reads change from RSS feed and emails | Gmail |
| Gmail | forwards email | to Tarpipe |
| Tarpipe | posts email to | Yahoo! Pipes |
| Yahoo! Pipe | extracts blog post URL and sends back | to Tarpipe |
| Tarpipe | posts a tweet | to Twitter |
!! Step 1 - sending a photo to your blog
In my case, the option to 'blog this' was already available on my phone's camera menu, which posts a photo to a Blogger blog. It is not unusual for blog services to allow you to send new messages as email, so you could email photos to your blog as attachments.
!! Step 2 - updating the blog RSS feed
If your blogging service doesn't already do this, it's time to get a new blogging service...
!! Step 3 - getting an email about the change
Both [[Notify.me|http://notify.me]] and [[Notifixio.us|http://notifixio.us]] can email you if an RSS feed changes. They can take up to an hour to respond to changes, so this is the bottleneck in the flow. On the other hand, it is very simple to set one of these services up. There are other notification services, but they tend to require the writing of some code to work with them.
Assuming you're using Notify.me, the subject of the email will be the title of the blog post prepended by "notify.me ", and the body of the email will be the body of the blog post appended by a load of garbage advertising Notify.me.
!! Step 4 - responding to the incoming email
I expect that your email client allows you to set up filters to respond to incoming emails automatically. If it doesn't, as above, it is time to get a new email client... Assuming you are using Notify.me as the notification service, you can filter out the incoming mail by creating a filter that selects email with a 'FROM' email address including '@notify.me'. Of these, you can get to the emails about your photo blog by selecting those whose subject includes your blog title. Set up the mail filter to forward to the email address you'll get for the Tarpipe workflow we're about to create.
[img[http://img.skitch.com/20090730-jqwchtihgggtnttdxmdymwmrg4.jpg]] [img[http://img.skitch.com/20090730-gx4cb1e4d6scw2njxn9im9ru12.jpg]]
!! Step 5 - the Tarpipe workflow
Tarpipe is a service for setting up workflows that respond to emails or API calls. Generally, the output will be an update to Twitter, Facebook or some other service that receives messages. Because a Tarpipe workflow can be kicked-off using email, and because you can post the email to any web service, it has opened the door to creating zero-code systems that are able to do interesting things. In short, hurray for Tarpipe.
We want to create a Tarpipe workflow that receives an email, posts it to Yahoo! Pipes, takes the response and tweets it. This is a simple chain of the "~MailDecoder", "REST" (with a "~TextInput" connector to supply the URL to post to) and "~TwitterUpdater" connectors. When you save the workflow for the first time, you'll get the email address to use in the email filter from the previous step.
At the moment, Tarpipe has a shortcoming whereby the REST connector, which is what is used to send messages to any web service of your choosing, encodes the information in a structure that is very difficult to use directly with Yahoo! Pipes, despite Pipes' obvious utility providing the string manipulation capabilities that Tarpipe lacks. You can use an intermediate service, such as [[this proxy|http://tarpipe-yahoopipes-proxy.smart.joyent.com/]], to munge the data into a more suitable format. The proxy takes a parameter in the URL to specify the ID of the Yahoo! Pipe to use. To get that ID, we'll need to create a Yahoo! Pipe.
!!Step 6 - getting at the blog post URL with a Yahoo! Pipe
Pipes has been around for a while now, and I believe it is mainly used to grab remote RSS feeds and other data, perform some operations such as selecting items from a feed, sorting them, turning them into Japanese and sending on the resulting feed. However, you can also use Pipes as a text manipulation machine, posting in some strings of text rather than grabbing them from somewhere else.
We're going to create a Pipe to take the large and messy email body, delicately extract the URL of the blog post with our photo on, and append that to the subject of the email. We'll also remove the string "Fwd: notify.me " from the subject of the email.
You can get access to any of a list of parameters, posted as text to a Pipe, by adding a "Text Input" module from the "User inputs" list, one for each named parameter being sent, with the 'name' property of each module set to the name of the parameter. Using the proxy linked to in the previous step, the subject of the email will be posted to the Pipe as 'title' and the body will be posted as 'description'. Set up your text input modules as shown below (only the 'name' property matters).
[img[http://img.skitch.com/20090730-f8xstghq8uiqc2c26y9cs7ku7.jpg]]
Now we have access to the strings, we have to extract the relevant strings from them. String extraction can be achieved directly by using the 'string regex' module, which stands for 'string regular expression'. "Regex", or "Regular expressions" is a language for describing patterns, such as "four letters, followed by a space, followed by a letter or a number".
The string regex module doesn't actually extract strings, it replaces any matches of a pattern with another string. However, we can make this a string extractor by replacing a pattern of "anything, followed by the pattern we want, followed by anything" with "the pattern we want".
In regex, that looks like this:
{{{
replace: (?s).*(<the pattern we want>).*
with: $1
}}}
An explanation of the above runs like this: -
{{{ (?s) }}} means "treat the string as a single-line" - without it, the pattern matching would stop at a line-break;
{{{ .* }}} means "match zero-or-more of any character";
{{{ (<the pattern we want>) }}} puts <the pattern we want> into a "group", to be used in...;
{{{ $1 }}} means "the first group from the pattern"
The title is the simpler case, where the pattern we want to extract is {{{[My Mobile Blog] - <the rest of the title>}}}, from a string that looks like "Fwd: notify.me [My Mobile Blog] - super new photo". To get what we want, connect a string regex module to the text input module named 'title' and put the following in the 'replace' and 'with' properties:
{{{
replace: (?s).*Fwd: notify\.me (\[My Mobile Blog\] - .*)
with: $1
}}}
The places where there is a {{{\}}} preceding another character are examples of 'escaping' characters that would otherwise have special meaning in regex. For example, {{{[}}} would normally mean "start a set of characters to choose from", so {{{\[}}} just means "match {{{[}}}".
[img[http://img.skitch.com/20090730-ks9kmifp3wyuet4df1gjcsgh3w.jpg]]
The more complicated extraction is getting the URL from the email body. Here we want to get a URL of the form {{{http://c.notify.me/F6d3HG}}} out of a much longer string of text. This URL points to an address on Notify.me's ~URL-shortening service, that will direct you to the blog post's much longer URL. Connect a new string regex module to the text input module named 'description' and put the following in the 'replace' and 'with' properties:
{{{
replace: (?s).*(http:\/\/c\.notify\.me/\w{6}).*
with: $1
}}}
The {{{\w{6}}}} pattern means "6 word-characters". A "word-character" is something you would expect to find in a word, rather than punctuation or a space. We don't need to worry too much about what is and what isn't a word-character, as we only expect to find letters, numbers and "_" in the 6 characters making up the end of the URL, which are //definitely// word-characters.
[img[http://img.skitch.com/20090730-px7qwjkwtxm8y6xurkb48xegeb.jpg]]
(If this has piqued your curiosity, there is a lot of information on regular expressions at http://regular-expressions.info.)
We're nearly done. Now we have two strings that we want to join together as our tweet. To do this, use the 'string builder' module and assemble them as "<title>" plus " - " plus "<URL>". Before outputting this string, we're going to create a new 'item', which is the unit of output from a Pipe - use the "item builder" module, setting the 'title' of the new item as the tweet string.
[img[http://img.skitch.com/20090730-ky3p267jsrmwmt6mnwuy4br5gn.jpg]]
With that all done, we can save the Pipe. By clicking on the 'run this pipe' link, you can find the ID of the pipe in the URL of the page you're taken to, named "_id". To use this ID in the Tarpipe workflow, in the text input module which is connected to the REST connector, add the ID to the URL as a query-string parameter named "pipeId": {{{ http://<proxy>/?pipeId=... }}}. (Note the {{{/}}} between the domain of the proxy and the {{{?}}}.)
[img[http://img.skitch.com/20090730-fjqmh4k3xt4tca94hfewn7ne38.jpg]] [img[http://img.skitch.com/20090730-j24hq1wcnffiautj4kmq1rbgtn.jpg]]
!!Step 7 - tweeting from Tarpipe
If you have set up your pipe as above, the text to use for the tweet will be available from the 'title' terminal on the right-hand side of the REST connector. Wire this to the 'title' terminal of a ~TwitterUpdater connector.
You need to set up your Twitter account in the 'accounts' tab so Tarpipe can tweet with it. At the moment, you need to give out your Twitter username and password, which is the dreaded anti-pattern, and since Twitter now supports ~OAuth, it makes sense for Tarpipe to use that to get the appropriate level of access to your account. For the purposes of this example, you could set up a new twitter account to link to Tarpipe.
[img[http://img.skitch.com/20090730-eac1firjgq375kkstbmqykkmen.jpg]]
Save the workflow, and you're done! If everything is configured correctly, sending a photo to your mobile blog will cause a tweet to appear on your timeline, linking back to your new blog post.
[img[http://img.skitch.com/20090730-cme3ujnyim5ysruyweq1chr7sp.jpg]] [img[http://img.skitch.com/20090730-di7wt22xp122yfdsn37xf5c5yk.jpg]]
!Testing the bugger
With a total of 5 remote systems (6 with the Tarpipe / Yahoo! Pipes proxy) between you and the intended tweet, there's a lot of scope for things to go wrong. The weakest parts of the chain are going to be those you set up yourself, particularly the regex-heavy Yahoo! Pipe, however errors can happen at any point in the system, and problems caused by delays, caching or rate-limits can manifest themselves in various ways.
It's a good idea to set up test accounts for any services you are hooking into that you use on a day-to-day basis, especially if they publish information to your friends or the public. If you don't, as I didn't for my Twitter account, you could end up with something like this mess:
[img[http://img.skitch.com/20090730-br1k2sqj6enpqietag86mps7sn.jpg]] [img[http://img.skitch.com/20090730-puigp3h34i8me41rh53eaum6x5.jpg]]
The trick to understanding what is going on in these remote systems is to log activity whenever you get the chance. One useful service for this is [[PostBin|http://postbin.org]], which lets you create a logger that records anything you post to it.
In the flow above, opportunities to post to this log are:
# Adding another REST connector to post what is received by the Tarpipe workflow
# Adding a 'web service' module to post what is received by the Yahoo! Pipe
# Adding a 'web service' module to post what comes out of the string extraction steps
# Adding another REST connector to post what is returned to Tarpipe from the Yahoo! Pipe
When using the Tarpipe / Yahoo! Pipes proxy, adding {{{&postBinId=<id>}}} to the URL in the text input which connects to the REST connector will cause the proxy to log at these four points:
# What comes into the proxy
# What is sends Yahoo! Pipes
# What comes back from Yahoo! Pipes
# What it sends back to Tarpipe
Here's an example of the ~PostBin logs you get from the proxy:
[img[http://img.skitch.com/20090730-jq5fgqxa2frcwqn98gjr94raip.jpg]]
As well as logging, some of the services mentioned provide help with the testing and debugging process:
* Gmail puts mail it forwards due to filters into your 'sent items'; the exact text sent can be found by selecting 'show original' from the 'reply' drop-down in an email
* Tarpipe provides an 'activity' tab that shows what came in and went out (note that sometimes you have to click on 'all' to see workflow activity, particularly when workflows only contain REST connectors)
* Yahoo! Pipes has an in-built debugger that allows you to examine the effect of your modules on the text you put into the 'debug' property of a user input
* [[RegexPal|http://regexpal.com]] lets you test out a regex on some text (such as the body text of your email)
Despite all these tools, debugging a chain of systems you have no control over can be a painful process. Be particularly careful when using Yahoo! Pipes, as it is not uncommon for results of a Pipe to be cached for 30 minutes. I have experienced this happening even if you change the input data. Lastly, note that should an error actually occur in your flow, it's likely you're not going to be told about it...
Good luck!
This is to be taken very broadly to mean that it's not all about me - there are lots of people in the world who are doing things worth your attention and it feels good to strike a balance between appreciating those things and putting out your own works. You can interpret this as anything from spending more time with people (after all, they are the creation of two other people) to seeing a movie.
Because it feels like quite a big change in the way I want to spend my time, the first steps are to sort things out in a very general sense so that I CAN strike the balance I want to strike.
This includes making the administrative overhead of running a company as minimal as possible. I think it also helps with my portfolio, because automating business stuff is something I want to be able to do for other people.
Started September '09; ended February '10
I don't think I've made much progress with this, as my work with Yellowcar to date has been consultancy stuff - paid for at the time, billed by time, with no stake in what is produced. I still think that the smart thing to do would be to get involved in projects where I get a stake in what's produced, and I want to produce products now more than ever.
-----
!!How things will look
I'm slightly obsessed by automating things, which extends to automating revenues. Since reading "The 4-Hour Work Week" by Tim Ferriss, I've wanted to experiment with a fully-automated revenue stream for a single person, but my serious take on automated revenues is the pleasure of growing a company and delegation of a self-contained area to someone new. I have found that selling real products is amenable to automation because it is well understood. Product development still sits centrally, as it is a part of overall strategy. It's satisfying to know that I could take myself out of the process and things would continue to run fine until people stopped buying our products.
!!How things look now
This feels mainly like a product-development problem to me - what am I going to put in my company's portfolio that I can sell? It might be that this revenue comes from my stake in other people's projects - this would fit well with what I'm saying I want in [[I want to invest in startup businesses or art projects]]. This also feels like it would be quite a big thing to achieve if it was about bits of my own company - you'd need a lot of diversity to run each product successfully.
An idea: if I provided common services that people needed, maybe that would be a way of securing a stake in other companies that have the inspiration for new things. I wonder if you could do this without making crappy services, making sure that people can give their customers an experience that's unique to them. It seems that what's common to both paths is that I'd need to know how to tap things like customer service and distribution. I always thought I could just whack in a bit of Internet here, but I've never tested that. Might be a good idea to start by making a distributable product myself (cartoons? t-shirts?).
So, there's definitely a number of ways of achieving this, from stakes in other businesses to selling my own products. What would be the most fun?
Started September '09. Ended January '10.
I think I've got to the point where I've realised that doing expenses is something I can do in batch at the end of the month if I keep receipts together - this doesn't feel overwhelming now. I also record any online expenses in my spreadsheet. I'm quite happy with my draft accounts that get created with Google Spreadsheets, although they are not reconciled with the accounts Jayne prepares, which include a proper treatment of tax. That's ok though, I'm happy for that to be the case as long as the accounts don't diverge too wildly. I've realised that the idea of selling this approach to other people isn't realistic at the moment, but it is something that is useful and interesting; but it's not a product.
My decision about not employing people definitely keeps things simpler.
-----
!!How things will look
I'm finding that I try to ignore the "paper" side of my business, justifying this by thinking that I'll concentrate on revenue-generation and then everything will be ok. This, of course, is not true - I need to file expenses and costs so I don't file incorrect VAT returns or get taxed more than I need to. I am slightly obsessed with having everything automated and simple; I let Jayne handle things because I'm paying her £50 a month to run things. I do have snazzy automated reporting and processes when they are genuinely useful, because I sell these services to other people.
!!How things look now
There are some things that I need to do to make sure things are running smoothly, such as "doing expenses" regularly (whatever that means), figuring out what home-office expenditure I could run through the company and figuring out whether I should run my medical insurance through the company. I think there ought to be copious information on the web about these things. I also need to make sure that everything is going to come out smoothly if I just pay Chris Dent for the work he does i.e. do I need to do anything special/specific to have a sub-contractor? I would like to sort out my standard contract (which I asked Matthew to help with) - I'm due a pub-meeting about this.
DONE! My first day of working for myself was Friday 19th June 2009. I was in Brussels on a 3-day workshop for the ILGA project, so BT got a free day out of me. ;)
This is made up of some very basic needs, such as some sort of income, and some rather less specific ones, like developing a reputation in a particular area that leads on to future paid work. The phrasing of this allows me to focus on the impediments to becoming self-employed.
Important things in this:
* business plan for being self-employed
* contract work from other companies
* things to make and sell
* understanding financial situation at present
* reputation
* portfolio
Started September '09; ended February '10.
I definitely don't plan food shopping. I do, however, cook a bit more regularly, both for myself and for other people. I still eat out a lot. I'd like a bit more of the at-home dinner party thing, with a variety of people, but in general I'm not so worried about food expenditure or not cooking enough.
-----
!!How things will look
I love eating good food. I find eating out all the time a bit expensive and often less pleasurable than a group of people eating at home. I also like to eat well when I'm by myself and get good value out of what I spend on food. One of the great things about cooking regularly is the leftovers that provide for subsequent meals. I don't find it difficult to experiment with new recipes and I enjoy figuring out in advance what I'm going to make in a week and how things from one recipe can be re-used in another.
!!How things look now
I tried recently to get more cooking going. I even read a bit of "Jamie's Dinners". It's an improvement. My flatmates do a lot of cooking and we have people over quite a lot. I could start to arrange these things, that would be fun. I think that a reason why I don't do as much cooking as I'd like is that I tend to leave it quite late to go down to the shops to buy food - it makes it difficult to motivate myself if I'm already hungry by the time I think to go shopping.
Started September 2009; ended February '10
I've come half of the way to doing this. I am practising fair-reward and openness model in all my projects, in that I don't employ people, I keep project information visible and I'm not the most highly paid in all of them. I've also been talking to people about wanting to work collaboratively with different groups of people on different projects, rather than sticking to a particular group (which would be like a company without the legal bit).
What I haven't done is make any sort of formal model out of this, nor have I worked with more than half a dozen people. Maybe both of things are not so bad. I think this is still important to me, but might have become the principle of the importance of the individual, rather than an aspiration to make a formal model.
-----
!!How things will look
I have tried out several structures to projects that keep them open in the sense that anyone can get involved and fair in the sense that the more impact someone has, the more they are rewarded. It is meritocratic without falling prey to entrenchment. I am very pleased by the fact that, although I am a part of several of these projects, I am not the most highly rewarded person in almost all of them. It is also very pleasing that there are a few projects using this model which I didn't start.
An example is ElectroNomad, where I run the coffee section and others run other sections and administer the project.
!!How things look now
I think this is not just a case of doing it and hoping that it catches on - writing about it and making an online resource about it would not be bad idea. I could change the way I run projects to make them obviously open - for example, I don't have a company website advertising projects, nor do I actively say in public that anyone can get involved in my projects. I probably should. The approach I have taken with Chris Dent is to pay him by the hour, which is pretty standard. This type of payment obviously has to live alongside speculative involvement in projects, so it's not bad to have someone in that position straight from the off. I need to come up with some ideas about how to go from where I am now - one main client, some specualtive work, chunky ideas - to there being revenue-generating projects with people working to this model on them.
I could try to enforce this model on the projects I do, as a way of forcing myself to behave in a certain way.
Started September '09; ended February '10.
What I've managed is to nearly finish the [[Grace coffee]] website, and plan to have a coffee section in [[Electronomad.org]]. This is not quite the revolution I was hoping for. Reading through what I wrote below, I realise I'd forgotten about the aspiration to help coffee-sellers sell interesting things to a global audience online (even a national audience, or a local audience would be a success).
-----
!!How things will look
I love coffee. I'm surprised I'm not drinking it now. I also love small, creative businesses because I dislike chains and normalising products towards mediocrity. I've been lucky enough to be in the right place at the right time to get coffee-makers, who are not the most webby crowd, experiencing huge benefits from using the web to connect themselves to a much wider audience. They have become rivals in popularity to established coffee companies, encouraging others to start making high-quality coffee and raising the accepted standard of coffee in this country. They reached this position because I helped create a duality: their customers experience the buzz of hyper-locality, whilst it is easy for the coffee-makers to sell unique products to anyone in the world (such as the roasts they choose, the hampers they curate, the limited-edition accessories they source).
!!How things look now
The coffee crowd in London seems to be internally well-connected, which could mean they'd be hard to get the collective trust of, but it might also mean that once one person buys the idea, others will follow. I think that [[Flat Cap Coffee Co]] is the company to start with, although I'd be happy to work with others such as [[Grace Coffee]] that I already have a relationship with. On the subject of [[Grace Coffee]], I've said I'll do their website, which could be an incentive to set up the coffee section of ElectroNomad, where everyone who meets the grade has a page. It would be nice to make each page look different, but with something constant.
Aiming to raise the standard of coffee across the country gives me a target that is ambitious enough to get people to take me seriously. Perhaps. At least it ought to make the job of choosing marketing campaigns kinda obvious.
I think that something that might stop me making progress with this is that I don't have any examples of selling things online yet (I imagine I'd be providing this service for coffee-makers). Perhaps what I've been writing in [[I want my business to be generating a few thousand a month in revenue that doesn't require any upkeep]] about selling something myself will give me a way to get this up and running - of course, vice versa could be true too (and given that this is more specific, it could be easier to start selling coffee products - how about Fabio's?).
The portfolio could be this site; that would be interesting...
As described in the notes to [[Finishing things off]] in [[Review week ending 28th June 2009]], I've made a list of things I could do that would be good portfolio pieces. I think the development of the portfolio is going to be a development of those alongside a different presentation of the content here.
As I keep saying to people, I really left BT to help small creative businesses grow through judicious use of technology. I've made more of a start with bigger clients, although they've generally been open-source projects, which is all very much in the right spirit.
Started September '09; ended January '10.
I think I've generally got better at thinking about other people. I've also got better at saying that I don't have time to do favours, rather than agreeing to and then dragging my feet.
-----
!!How things will look
I seem to forget people's birthdays a little more often than I'm happy with! I won't be forgetting people's birthdays or even being unprepared and rushing presents.
I find that I can help people out with computer-related problems quite often and although I say I will, sometimes I just don't get around to it. I won't be neglecting these favours, because people's gratitude seems a lot more than the effort I have to put in to sort out their problems. I think of these things as fun.
!!How things look now
I had an idea that keeping a calendar of people's birthday's would be a useful thing to do - it would be easy to see what's coming up. And then there's actually clearing some space to think about good presents and stuff. It's more than just birthdays too, it wouldn't hurt to remember to ring people more often (Grandma, people I don't see often).
At a web level, this seems to mean that helping people out with little things for free shouldn't interfere with the serious business of taking over the world... Specifically, I ought to finish sorting out [[Dad's IT problems]] and tweak the [[TiddlyManual]] more often for Dickon.
Started September '09; ended '10
I think I've reduced the amount of money I spend a bit, although I haven't set up regular savings. I'm definitely spending less money on holidays and booze, although I don't think I spend less on food. I still haven't analysed and written-up the second part of the [[Baselining my life]] research, which is daft.
-----
!!How things will look
I don't save a lot, but I do save to create a cushion against irregular cash-flow, which could be because I'm splurging or because it's an unexpected expense. I found that a big chunk of the money I save became available when I realised how much money I spent on things and could make my spending more efficient.
!!How things look now
When I was thinking about going freelance, I said I wasn't doing it to take a pay-cut. Bearing in mind that I wasn't already saving, to now say that I'll achieve savings by reducing my lifestyle cost seems a bit of a backwards step. However, there is nothing wrong with having a more efficient lifestyle, which seems like something I could achieve now I have done all the [[Baselining my life]] research - when I've finished the second part, I'll know not only what I'm spending on everything, but what is the most expensive activity per hour.
If I don't take money from my lifestyle to fund my savings, I will need to raise my salary. It just occurs to me now I'm thinking about my business, that I can reduce my lifestyle cost by treating certain things (such as travel) as a business expense - this allows me to pay less tax, which is one way of getting more lifestyle for less revenue.
Started September '09; ended February '10
On the surface, I haven't really done much of this: I don't have a major revenue-sharing project with a startup (I definitely neglected the opportunity with [[Paul Dyson]]), nor am I identifiably investing in an art project. What am I doing, looking at things differently, is helping [[Grace coffee]], doing semi pro-bono at the [[Anna Freud Centre]] and helping a new product get off the ground in the [[wiki-data]] project. What those three projects share is that they are all paying me in one way or another. Perhaps there's a truth in this - some sort of up-front payment helps motivation.
I still think it's important to offer skills instead of money (as donations) and allow for credit note-style reward.
Perhaps this want was too nebulous to be made important enough to satisfy.
-----
!!How things will look
I have enough money in my business to use some of it to invest in other projects, whether this means investing in my own time or someone else's. I like to patronise the creation of beautiful things so these can happen, although I'm not into throwing money away unnecessarily.
!!How things look now
I think buying art is one way of patronising art projects, but it's not very interesting. On the other hand, participating in something like [[WHO]] might be a cool way to use what I know alongside what appeals artistically. It's a personal investment of time. In terms of investing in startups, there is something deliberately more mercenary about that - I do want to be rewarded. I think this fits into my attempts to figure out a business model where I can allow small, creative businesses to get top-class help without big up-front cost. The "credit note model" I talk about in [[Go Public]] is my current favourite method for doing this. I think it might help create a business model that you can use in the open projects model I'm talking about in [[I want to create an effective model for running fair-reward open projects]].
Projects I'm investing my time in at the moment, without being paid, include [[Paul Dyson]], [[Paul Bacchus]] (expecting him to be in touch during October), [[WHO]], [[Notion Twitter/SMS]], [[Flat Cap Coffee Co]], [[Grace Coffee]]. I expect to try and agree revenue/profit-share with most of these, rather than charge fees.
Used first as a Want on 12th April; previously a theme: [[Make people happy]]
!!How things will look
I like automating things. I like the idea that there is no split between "work" and "life", there is just "stuff that you do". One way to make this real is to streamline things to give yourself more usable time, which can involve automating things. Another way is to free up money, which can also come from automating things.
I have been a part of a number of projects to make things people use to give themselves more time. Two examples are making web-developers and web-designers a slicker team and making it easy for the population to save money on their electricity usage.
!!How things look now
I think it would be cool to have a major effect on a large number of people, and increasing productivity is something I am quite keen on, whether it's of money or time. It feels like quite a big thing to want.
I imagine that creating a popular product with a large market is one way of affecting a large number of people. I'd also like to work with institutions people feel beholden to, such as councils, the government, utility providers, banks. Projects I can see with this size of appeal are [[fee-free micropayments]] and [[web-enabled electricity monitors]].
I can imagine that a big thing that would prevent me from pushing on with projects of this size is that they could take a long time to start paying and require a lot of investment (e.g. many people's time). I'm quite averse to taking funding unless you really need it (if you're not an infrastructure company, I don't think you need it), so a way round this might be to figure out how to generate revenues immediately; another might be to use funding from other projects, akin to [[I want to invest in startup businesses or art projects]].
I have decided to worry first about setting up a stable income that covers what I need, and build the speculative stuff later.
Started September '09. Ended January '10.
I've made no inroads into this. I guess it just isn't that important to me right now. I'm still awake to an opportunity to get back into music, but I haven't done anything to seek out an opportunity. I didn't play at Dad's party, which was ok.
-----
!!How things will look
I haven't played the guitar for a long time. I used to be on the verge of being really good. It always frustrated me that I didn't know songs off the top of my head to be able to pick up a guitar and bang some out - this was a little different when I was busking. I find playing very relaxing and it stimulates my creative side. I enjoy music and feeling like I'm creating new music. Playing guitar is the corner-stone of my musical identity at the moment, although I look forward to getting more electro and experimenting with digital music and new instruments.
!!How things look now
I'm totally out of the habit of playing the guitar (or making music of any variety). I really need to think of a way to get this back in my life, presumably starting slowly. It might be cool to find some sort of group(s) to go along to where people are playing the kind of music I want to play - I remember the ukelele-jam I accidently went to in Soho with Dan and James, there might be things like that for spanish. I'm sure groups for jazz and singer/songwriter stuff would be easy to find. I need to be in shape by Dad's party on 12th Dec.
Started as [[Habitualize making time to spend with Bonnie]].
Most of the information is held on a Google Site at: sites.google.com/site/ilgawebsite/
Using Agile development techniques to make a new website for http://ILGA.org.
Jon Robson doing lots of work with mapping geographic data using JavaScript - http://jonrobson.me.uk/#GeoTiddlyWiki
''The text below has been blogged, in a modified form, at http://jaybyjayfresh.com/2009/06/13/using-tiddlyweb-as-a-content-management-system-for-ilga-org/''
! Using TiddlyWeb as a content management system
The ILGA project has taken our use of TiddlyWeb in unexplored directions. The essential problem that we're using this system to solve is that we have a set of people around the world who wish to contribute content to a website, the which is stored and displayed to the visitor when they access the site. It is, simply, a CMS problem as solved a hundred times over by projects, public and private, around the world.
Chris Dent [[did not want|http://cdent.tumblr.com/post/51774343/twoter-tiddlyweb-based-web-clippings]] to create a framework, but with TiddlyWeb this is what he has done, and with ILGA, we have been determined to push the limits of the system and expose new patterns of use and holes for further development.
The following describes how we've used TiddlyWeb to solve the problems of content-editing and publishing. The natural progression has been to work with a TiddlyWeb plugin, cheekily called "jinx", when the existing toolset didn't seem to be adequate. As you'll see, revisions of our methods have used more and more of TiddlyWeb's innards as we've gone along, showing the power of the system in its native form.
!! Accepting content from contributors
!!! Where and how to publish
TiddlyWeb uses the concepts of "''bags''", which are spheres of permissions; and "''recipes''", which collect bags and pour out the tiddlers through filters. Tiddlers themselves do not have permissions assigned to them, which in a sense, helps you with your decision about where to let people put content. Each contributor gets their own bag, and anything that they contribute ends up in this bag.
In a finally enlightening cycle of development, we discovered the connection between a recipe and the bags it references to be implemented in a useful and elegant way. In short, when you create a new tiddler and save it back to TiddlyWeb, it will end up in whichever is the first bag you have permission to PUT to, counting up from the bottom of the recipe. This means that if your own bag is the last line in the recipe, and new tiddlers will be assigned to that bag. A neat syntactical trick in creating the recipe can make it generic for any contributor:
{{{
recipe
/bags/system/tiddlers
/bags/common/tiddlers
/bags/{{ user }}/tiddlers
}}}
This simple construction saved large chunks of code that had been written as TiddlyWeb plugins, before this mechanism was properly understood.
!!! The TiddlyWiki as a content manager
Although the pages that we wanted to show to site visitors had to behave like ordinary web pages, there is no such limitation on pages that the relatively small number of contributors use to edit and publish content, as they are effectively private. This allowed us to take advantage of the full power of TiddlyWiki as an editor and manager of the content in someone's bag.
!!! Draft vs. publish
One feature we added to the TiddlyWiki early on was a button to publish a piece of content. Initially, this involved a lot of custom code to handle PUTing an article into a different location that where it was saved, until the understanding of the bag and recipe concept outlined above reduced this feature to a simple flag on the tiddler, which indicated "published" status. This could be queried later when deciding whether to display a tiddler or not.
!! Displaying content to visitors
Now that we have content safely tucked up in bags, the question is how to get it out again for visitors to the site; and outside of the shackles of a standard TiddlyWiki, which is not suitable for our ordinary web pages for reasons of accessibility, page size and search engine optimization. This has so far been the area most in need of attention and experimentation, as TiddlyWeb does not make content presentation as obvious as content saving.
!!! Getting stuff out - recipes, filters and tiddler selection
The flip-side of the content-storing bags mentioned above are the recipes, which deliver a filtered set of tiddlers that come from any number of bags. We've set the content up with each person's tiddlers stored in their own bag, so we target these bags in our recipes. There are two downsides to this approach:
# You can't bake the title of a tiddler you're looking for into a recipe, unless you have a single recipe for e.g. each news article. The upshot of this is that our recipes deliver a large number of tiddlers, which then have to be filtered further in some way, either in the template or in the jinx plugin
# A further consequence is that the recipes get longer as you add more contributors, and have to be maintained. It would be easier to be able to point to all contributors' bags in a recipe.
As just mentioned, further selection of tiddlers is required after a recipe has delivered a set of tiddlers. This is where the jinx plugin has been developed (and repeatedly re-worked!) the most. Because we can examine the URL requested in the plugin, there is an interdependency between the URL's structure and the filtering applied to a set of tiddlers returned by a recipe. The URL structure itself is examined further down.
The scenarios we have for wanting to get content out of TiddlyWeb, the corresponding recipes and the tiddler selection we've set up in the jinx plugin are:
* Pages with one news article (a tiddler tagged 'article')
{{{
recipe
/bags/JON/tiddlers?filter=[tag[article]]
/bags/BOB/tiddlers?filter=[tag[article]]
/bags/PETER/tiddlers?filter=[tag[article]]
selection
get_tiddler_from_recipe(tiddler_name,recipe_name)
}}}
* Pages with many news articles
{{{
recipe (same as before)
/bags/JON/tiddlers?filter=[tag[article]]
/bags/BOB/tiddlers?filter=[tag[article]]
/bags/PETER/tiddlers?filter=[tag[article]]
selection (no further filtering)
get_tiddlers_from_recipe(recipe_name)
}}}
* Pages with country profile information (tiddlers tagged 'countryprofile' from a single contributor), news articles for that country, a tiddler defining themes for a map and a tiddler for the country itself
{{{
recipe
/bags/countries/tiddlers?filter=JAPAN
/bags/BOB/tiddlers?filter=[tag[countryprofile]]
/bags/JON/tiddlers?filter=[tag[article]location[JAPAN]]
/bags/PETER/tiddlers?filter=[tag[article]location[JAPAN]]
/bags/BOB/tiddlers?filter=[tag[article]location[JAPAN]]
/bags/ILGA/tiddlers?filter=[tag[maptheme]]
selection
get_tiddlers_from_recipe(recipe_name,named_tiddlers_array)
}}}
The final example, although still developing, illustrates how difficult it can be to get the tiddlers you need in a way that you can tell them apart. We added the second argument to the {{{get_tiddlers_from_recipe}}} function so that we could pass in tiddlers as named variables to the next step, HTML templating, and refer to them directly rather than iterating through a huge loop of tiddlers each time we wanted to find one.
!!! Formatting stuff - templates
Right from the word go, we've wanted to "template" tiddler content through common HTML templates, inserting bits and pieces of tiddlers into the relevant places in a mainly static page. The approach we've developed so far involves using the jinx plugin to map a particular URL path, say {{{/articles/MyArticle}}}, to a function that collects a set of tiddlers as described above (in this case probably "articles") and pushes them through a templating process to generate the HTML pages.
A technique we've found ourselves using quite frequently is iterating through the delivered set of tiddlers to find the one we want. For example, on country page, we want to be able to isolate the tiddlers which are news articles. At the moment, we can do that like this:
{{{
{% for tiddler in tiddlers %}
{% for tag in tiddler.tags %}
{% if tag == 'article' %}
<h2>{{tiddler.title}}</h2>
{% endif %}
{% endfor %}
{% endfor %}
}}}
This structure is less than ideal and it would be much easier to be able to access the groups of tiddlers directly, which is why the {{{named_tiddlers_array}}} parameter was created for {{{get_tiddlers_from_recipe}}}. However, this doesn't feel very solid yet.
!!! Accessing stuff - URL's
The URL used to access a tiddler can be accessed and acted on by the jinx plugin, so we have used it to determine what is presented and how, through two methods: first, by mapping various URL structures to functions inside the jinx plugin; second, by using the sections of a URL's path as variables in these functions, determining things like which template is used, or which tiddler is retrieved.
The mappings we are using are listed below. TiddlyWeb differentiates between {{{/resource}}} and {{{/resource/}}}, so there are double entries for most mappings:
{{{
config['selector'].add('/index.html', GET=get_index)
config['selector'].add('/myactivism', GET=get_myactivism)
config['selector'].add('/myactivism/', GET=get_myactivism)
config['selector'].add('/allies', GET=get_allies)
config['selector'].add('/allies/', GET=get_allies)
config['selector'].add('/article/{name:segment}', GET=get_article)
config['selector'].add('/countries/{country:segment}', GET=get_country_section)
config['selector'].add('/countries/{country:segment}/', GET=get_country_section)
config['selector'].add('/countries/{country:segment}/{section:segment}/', GET=get_country_section)
config['selector'].add('/countries/{country:segment}/{section:segment}', GET=get_country_section)
}}}
It seems that, in the pursuit of genericism, {{{GET /path/to/resource}}} should be understood as {{{GET /path/to/recipe/tiddler}}} - accessing a recipe, pushing the results through a template (perhaps with the same name as the recipe), and selecting the named tiddler.
Lots of server problems at the moment.
Having looked at the introduction videos, it looks cool - quite an understandable app-building experience, and it integrates nicely with XML-based external services. It appears to be only for building apps you use yourself. They'll host for $100/year or you can download it and run it yourself for $200.
Check out the [[Practical Projects]] list, which is a snapshot of what I thought I had around me during June/July 2009. It has a lot of ideas. A couple of sections are included below.
!Candidates for being active
<<tiddler [[Practical Projects##Candidates for being active]]>>
!Ideas
<<tiddler [[Practical Projects##Ideas]]>>
!Improving Cecily
#Add command to open all the tiddlers in a map - actually, what I want is a command to save all open tiddlers as DefaultTiddlers - I've written [[saveDefaultTiddlersPlugin]] to add a button to the OverlayMenu that saves the current view as the default
#Might be nice if the map defined relative distances from other tiddlers, so if you open two tiddlers they'll be close to one another i.e. visible - at the moment, the positions are absolute
#Being able to set a map as the default tiddlers would be good
#Being able to flick back and forth between Cecily and normal GUI would be useful / could be useful / could help with transitioning (initial thought)
#Tiny tiddlers shouldn't respond to clicks on buttons, as you can't see what they are!
!Bugs in Cecily
#In edit mode, cursor doesn't always move to where the character input is going to be (Safari 3.1.2)
#Edit mode scroll bar appears to be an image rather than a working scroll bar
#When the page loads, everything is off to the left...
| Name | Free? | tried? |
| Photoshop.com | y | y |
| Aviary | y | n |
Also:
http://www.thepiclab.com/ - online image editor
See [[project trackers]].
It would be handy to be able to link an email to a tiddler so I can keep email's text attached to relevant tiddlers
It would be great to be able to easily see how much time we've spent on a project vs. how much we predicted
I've been using TiddlyHoster recently to track projects such as [[Jeroboams]] and [[SweetSoft]]. Some thing could be done to make this easier:
It would be great if Wants could be viewed as a batch and assigned to phases, without having to type them out in the phase tiddlers, so it's not a pain to track wants in phases and make a sensible chronology
I would like to be able to comment on any piece of text that someone else has written, in place, so I can make it easy to consume my comments
I would like to be able to "clock in" and "clock off" a project easily. I don't want to have to figure out which of many log tiddlers I should be using. I don't want it to be a hassle to split logs into the phases that go with billing and different bits of a project.
I'd like to be able to clock a group of people in and off, so they don't have to do it.
It would be nice if more than one person could edit a TiddlyHoster tracker - see [[TiddlyWeb Bag Policy Editor]]
It would be nice if a link to a tiddler showed you what that tiddler was tagged with (keeping to minimal space use - perhaps showing first couple, and then more if you hover over a '...')
It would be nice if you could see which bag a tiddler is from, and elect to move to it to another - it's annoying having to swap between recipes to avoid creating tiddlers in the comments bag
It would be nice if there was a way to get the benefits of version control and releases without having to make non-programmers use version control
!!Strengths
Having the links to non-existant tiddlers update to show they exist when you create the new tiddler - very handy for creating a bunch of links to non-existant tiddlers and seeing your progress as you go through to create them
"jump" on the toolbar is super-useful for when you have many tiddlers open at once
Also see [[Improving project trackers]]
It would be great if I could copy wants across from one fortnight to the next without copying and pasting so it takes me less time to set up the next fortnight's plan
It would be handy if my plan totaled up the hours by itself, so I didn't have to spend time doing it
It would be great if I could see the Story down the right-hand side, so I know what's open where.
It would be great if I could "pop-out" a tiddler, particularly one I'm editing, so I can edit it whilst scrolling the backdrop of the Story around
It would be nice if I could see time tracking from every different tracker, aggregated to show me what I've been doing.
It would be nice if creating a new log entry for a project didn't involve adding a link to the bottom of the project tiddler.
It would be nice if tracking time wasn't a manual thing - although it's important to be able to edit the amount to account for times when you aren't on the project but haven't written down a break
It would be nice if billed time was added up automatically
It would be nice if things I labelled up like "problem:..." or "bug:..." could be pulled out and listed
Search = match any word
If a tiddler is linked to, it should be prominently displayed somewhere in the tiddler header area. That way, you would be prompted to look for reverse connections, rather than always going forwards.
It would be nice if you could see how much of a TiddlyWiki you've read - see [[readCountPlugin]]
Speaking with [[Alan Blackwell]] at Cam, he runs the End-user programming research group at the CL. 3rd March is the presentations of a student design competition for an EUP tool.
Speaking with [[Katarina Zajacova]] at the University of Surrey about their sociology/psychology student placement scheme.
!!Constraints
* Cannot be an employee, as I don't have employees
* Pay needs to be kept at a level where it does not endanger the solvency of Yellowcar Ltd
Looked into using JSCoverage to show me how much of dependentInputs I was covering by my test code. Didn't get it going in default mode, because it assumes that you're testing a file directly and I want to test a library which a test file exercises.
"Inverted mode" comes with an example using JSUnit, so this could be better suited - see [[the manual|http://siliconforks.com/jscoverage/manual.html]] for more info.
Details: [[Dad's IT problems]].
Date: 6th June 2009
Total: 424.00
Online at: http://docs.google.com/View?id=dr44nfc_68d8ssv4zc
VAT invoice online at: http://docs.google.com/View?id=dr44nfc_73gm3ndzhm
Details: [[Polling app for Nokia music blog]]
Date: 6th July 2009
Total: 273.13
Online at: http://docs.google.com/View?id=dr44nfc_69vrhtp5v8
VAT invoice online at: http://docs.google.com/View?id=dr44nfc_72h69978gw
Details: [[Streams interface for LShift]]
Date: 6th July 2009
Total: £2764.17
Online at: http://docs.google.com/View?id=dr44nfc_706jkdvrg6
VAT invoice issued separately. Online at: http://docs.google.com/View?id=dr44nfc_71g7h8bgfz
Details: [[Avox Wiki-data]]
Date: 10th August 2009
Total: £707.25
Online at: http://docs.google.com/View?id=dr44nfc_74cfs96dfq
Details: [[Anna Freud Centre]]
Date: 25th September 2009
Total: £394.88
Online at: http://docs.google.com/View?id=dr44nfc_81csm9wrdk
Details: [[Avox Wiki-data]]
Date: 10th September 2009
Total: £3200.31
Online at: http://docs.google.com/View?id=dr44nfc_82wvkc2zm9
Details: [[Anna Freud Centre]]
Date: 5th November 2009
Total: £371.31
Online at: http://docs.google.com/View?id=dr44nfc_87x37k3bgz
Details: [[Avox Wiki-data phase 1]]
Date: 10th October 2009
Total: £3707.17
Online at: http://docs.google.com/View?id=dr44nfc_86fq6wjbgn
Details: [[Avox Wiki-data phase 1]]
Date: 10th November 2009
Total: £7449.70
Online at: http://docs.google.com/View?id=dr44nfc_90cqj35sc9
Details: [[Avox Wiki-data phase 2]]
Date: 10th December 2009
Total: £3529.59
Online at: http://docs.google.com/Doc?docid=0AQQJ7FGUGIp_ZHI0NG5mY185M2Q2M3NwamY1&hl=en
Details: [[BPSF]] and [[Digital Olympics]]
Date: 15th January 2010
Total: £1605.84
Online at: http://docs.google.com/View?id=dr44nfc_104vw7xqj3z
Details: [[Avox Wiki-data phase 2]]
Date: 15th January 2010
Total: £2483.51
Online at: http://docs.google.com/View?id=dr44nfc_105ddcxnggn
Details: [[Anna Freud Centre]]
Date: 17th February 2010
Total: £578.10
Online at: http://docs.google.com/View?id=dr44nfc_117dn5z4gd5
Details: [[wiki-data]]
Date: 17th February 2010
Total: £5726.81
Online at: http://docs.google.com/View?id=dr44nfc_118cz85sfdb
Details: [[BPSF]]
Date: 12th March 2010
Total: £7050.00
Online at: https://docs.google.com/Doc?docid=0AQQJ7FGUGIp_ZHI0NG5mY18xMjRnOG1rNXhoag&hl=en
Details: [[wiki-data phase 3]]
Date: 22nd March 2010
Total: £3161.49
Online at: http://docs.google.com/Doc?docid=0AQQJ7FGUGIp_ZHI0NG5mY18xMjVoaHpjN2hmZA&hl=en
Details: [[SweetSoft research]]
Date: 1st April 2010
Total: £687.38
Online at: http://docs.google.com/View?id=dr44nfc_127gg8rr4cd
Details: [[Rewired State Home Office Hack Day]]
Date: 9th April 2010
Total: £235.00
Online at: http://docs.google.com/View?id=dr44nfc_137c3r767d9
Details: [[Jeroboams]]
Date: 9th April 2010
Total: £1690.24
Online at: http://docs.google.com/View?id=dr44nfc_138g37msbgt
Details: [[SweetSpot]]
Date: 27th April 2010
Total: £1840.84
Online at: http://docs.google.com/View?id=dr44nfc_145hhtsgcwp
Details: [[Jeroboams]] or http://hoster.peermore.com/recipes/jeroboams/tiddlers.wiki#%5B%5BPhase%202%20priorities%20redux%5D%5D
Date: 18th May 2010
Total: £1206.78
Online at: http://docs.google.com/View?id=dr44nfc_149g48jtzfj
Details: [[wiki-data phase 3]]
Date: 11th June 2010
Total: £2264.23
Online at: http://docs.google.com/View?id=dr44nfc_218cnpr4rgx
Details: [[SweetSpot]]
Date: 18th May 2010
Total: £2021.00
Online at: http://docs.google.com/View?id=dr44nfc_148gvxfvcgk
Details: [[SweetSpot]] or http://hoster.peermore.com/recipes/sweetsoft-editor/tiddlers.wiki#%5B%5BPhase%204%5D%5D
Date: 14th June 2010
Total: £1903.50
Online at: http://docs.google.com/View?id=dr44nfc_219cs89dbhk
Details: [[de Candole]]
Date: 6th July 2010
Total: £188.00
Online at: http://docs.google.com/View?id=dr44nfc_224fkm5q4cn
Details: [[Scheduler 4]]
Date: 28th July 2010
Total: £409.49
Online at: http://docs.google.com/View?id=dr44nfc_227hr9d5jds
Details: [[SweetSpot]] or http://hoster.peermore.com/recipes/sweetsoft-editor/tiddlers.wiki#%5B%5BPhase%205%5D%5D
Date: 11th August 2010
Total: £411.25
Online at: http://docs.google.com/View?id=dr44nfc_231grffpjcj
Details: [[csa]]
Date: 11th August 2010
Total: £587.50
Online at: http://docs.google.com/View?id=dr44nfc_232hhw97rc3
Details: [[Alison Coward]]
Date: 12th August 2010
Total: £47.00
Online at: http://docs.google.com/View?id=dr44nfc_233fxx6sdg7
Details: [[SweetSpot]] or http://hoster.peermore.com/recipes/sweetsoft-editor/tiddlers.wiki#%5B%5BPhase%206%5D%5D
Date: 20th August 2010
Total: £1578.41
Online at: http://docs.google.com/View?id=dr44nfc_235s86vxrc6
<<<
Hi Jayne!
Dad's spoken to me about you handling my invoices and doing my books. I think this is great and I'm happy to pay the rate my Dad's suggested.
As you might know, I like to experiment with new ways of doing things in the hopes that they can help other people, so in that spirit I have set up a tracker for my invoices, which I'd like you to use when you receive cheques. It's a very simple form on the web which I use to log when I have sent out invoices and which you can use to log when they are paid. In the background, this updates a spreadsheet which generates a report for me about which invoices are outstanding and how long they have been outstanding for.
The address for the form is:
http://tinyurl.com/yellowcarltdinvoicetracker
Submitting through that form updates this webpage:
http://tinyurl.com/yellowcarltdinvoicesstatus
If there should be more options for the invoice status than "sent" and "paid", let me know and I'll update the form. Also, if you get any other ideas for things I could do similar to this, let me know.
I have sent out three invoices, numbered 0001, 0002 and 0003, so hopefully there will be some cheques arriving soon!
Thanks!
J.
<<<
http://invoicemachine.com/
See http://withjandj.com
We're developing some products for use in automating the process of selling and renting houses. We don't have an overall product name yet. This came out of [[J&J's retreat]].
There's a Google Shared folder for process descriptions.
Also:
[[Commercial property]]
Saturday 17th - Monday 19th July 2010
Pad for Smarthomes/offices research: http://ietherpad.com/dIK1tG6vUJ
Pad for Home help/services/conveniences research: http://ietherpad.com/NTx1ZRbCaM
Improved estate agent experience: http://ietherpad.com/tXYvGJvIoM
Online estate agents: http://ietherpad.com/VUPnHxfeye
Here's what we concluded:
We're focussing on software for online estate agents. We're also targetting property developers who we can encourage to do their own agenting. We want the product to come out of the project with SweetSpot, which will an example of applying it to the rental market, where students are the customers.
We've made http://property.withjandj.com to show where we're going with this.
We also put a holding page up at http://withjandj.com
A project by [[Joshua Bradley]] to make a CSS framework that maintains vertical rhythm of type, and allows different rhythms to co-exist on one page. This is something that happens in print typography and it looks good. I'm helping figure out some maths.
!Review
!!Bad
* Could have done with better editing
** Repeats itself practically verbatim within two or three lines
* I get the feeling Part 1 is mainly useless
** Part 1 I count less than 30 of actual content in 78 pages
** How much is covered in the next two parts? It would be better if Part 1 was a good, short overview
!!Good
* DOM profiling tool included - DOM Monster
* It does look nice - graphics/typorgraphy
* Coverage of subjects unfamiliar to front-end developers
** Part 2 - Importance of caching and subleties
!! Lessons
...some from chapters 1-3
Sprockets - looks good for deploying JS - concatenates, adds version numbers, generates docs...
Have got an account with Heroku's Node.js beta, looking into getting jQuery running on there - or TiddlyWiki...
-----
''Improved and blogged at: jaybyjayfresh.com/2010/05/04/javascript-in-the-cloud/''
The idea of running JavaScript on the server has been around for a while now (think [[Jaxer|http://ejohn.org/blog/server-side-javascript-with-jaxer/]] back in 2008), but it recently got a big boost with the featuring of [[Node.js]] at [[JSConf|http://jsconf.eu/2009/video_nodejs_by_ryan_dahl.html]] in November 2009. Node.js found immediate fame by demonstrating blinding performance as a web server, and by building on the hotly hyped V8 JavaScript engine Google bundled with Chrome.
I have a big interest in server-side JavaScript ([[SSJS]]) because I generally code in the browser and would enjoy being able to make things happen on a server without having to resort to another language. The ideal would be to take client-side code and run it unchanged on a server. The last step is pain-free application hosting - call it "[[Platform as a service|http://en.wikipedia.org/wiki/Platform_as_a_service]]" for want of a less boring name.
Over a year ago, Joyent [[bought|http://www.joyent.com/joyeurblog/2009/01/14/joyent-acquires-reasonably-smart/]] Reasonably Smart, a cloud provider of SSJS, and subsequently transmuted it into [[Smart Platform|http://smart.joyent.com/]], which runs on their distributed infrastructure. This followed the shining example of Heroku and Google App Engine, in that application deployment became a matter of pushing some new code to a Git repository online. For me, this was fantastically attractive, as I avoid touching servers as much as realistically possible. They came out with Smart Platform shortly before [[the demise of AppJet's free service|http://jaybyjayfresh.com/2009/06/02/appjet-make-boo-boo-they-closed/]], which I had been happily trying for a while, as it did much the same thing.
Nearly a year on, and Smart Platform hasn't really taken off, if [[Google Trends|http://www.google.com/trends?q=%22smart+platform%22&ctab=0&geo=all&date=all]] is anything to go by. And my ambitions to run my entire web estate on identical server-side and client-side code have rather fallen by the way-side. I am happy to see that Smart Platform now has a permanent full-time coder ([[@konobi|http://twitter.com/konobi]]), who is making Smart Platform play nicely with the [[CommonJS spec|http://commonjs.org/]] for server-side JS, but I don't yet know when Joyent are going to drop a new release, or where they're going with it.
Enter Node.js. The interest in Node.js has [[stayed high|http://www.google.com/trends?q=node.js&ctab=0&geo=all&date=all]] since its November launch, with developers building an [[assortment of frameworks and tools|http://wiki.github.com/ry/node/]] on top of it or with it, in effect legitimising it as a serious development environment for software running on a server.
Of significant interest to me is the [[jsdom|http://github.com/tmpvar/jsdom]] project, which brings the browser environment to Node.js in a similar way to how [[Env.js|http://env-js.appspot.com/]] brought it to Rhino ([[and Ruby|http://github.com/smparkes/env-js]]). Env.js has supported jQuery [[since the get-go|http://ejohn.org/blog/bringing-the-browser-to-the-server/]], and there are [[rumblings|http://stackoverflow.com/questions/1801160/can-i-use-jquery-with-node-js]] of jQuery support in Node.js via jsdom. Both these situations inspire joy as, if they pan out successfully, I'll be able to use one methodology for writing and testing JavaScript applications, whether they run in the browser or on the server. Unfortunately, I've not seen Env.js working on any hosted platform (although Smart Platform [[may get it soon|http://groups.google.com/group/envjs/browse_thread/thread/f42f48a515852fcc]]).
The number of cloud providers for Node.js has increased from 0 to 2 very recently: Heroku has [[announced|http://blog.heroku.com/archives/2010/4/28/node_js_support_experimental/]] a very experimental Node.js stack, and ElusiveHippo is in development, releasing accounts slowly.
Now then, if Coda starts [[supporting Git|http://getsatisfaction.com/panic/topics/are_you_using_coda_and_git]], or I start doing all my code editing in Bespin, I think with a bit of fairy-dust I'm going to have a pretty slick JavaScript-only setup fairly soon...
Works at Do Tank Studios. Asked him for a quote about [[DRAMA]] project. iPhone/iPad apps.
As well as iOs app, we offer a wide range of services using many different technologies:
- Flash interactives online and offline (web, kiosks, installations, prototyping)
- Facebook app
- Websites design and build
- Video Encoding
- Video/Audio Streaming
What we enjoy the most is interactive music project and music reactive installation, that's our background.
Be nice to work together at some point in the future.
@jaav
Responded to Twitter request for PhoneGap developer for [[Sheraz Arif]] project.
iPhone/iPad
A wine seller. Josh and I pitched to do their En Primeur site in February. We also pitched some longer-term strategic ideas. Our contact is [[Charli Hambly]]
Tracking this on http://hoster.peermore.com/recipes/jeroboams/tiddlers.wiki
See [[Invoice 0019]] and [[Invoice 0021]]
Joshua is a graphic designer, web designer and Wordpress customiser. He has also done Flash. We have worked on much together.
Met on Wednesday 23rd September, from 14:30 - 18:30.
Talked about Julie's Internet ideas and how she could meet technical people. Talked about my [[fee-free micropayments]] idea - Julie pointed out it could be useful for people keeping business expenses (reminds me of the conversation I had at BarcampBrighton about this with Michael (?) from the online accountancy firm).
Also talked about [[web-enabled electricity monitors]]. Julie had the great idea to talk to house-builders about electricity-efficiency; there would be other people interested too e.g. councils.
http://www.jumpchart.com/
What is quite interesting about this is that you can export HTML/CSS of the content structure. This suggests that you can have people editing the content using this and then you have another process that converts it to be shown through the design you want. Also, it exports to Wordpress format in a similar way.
Professional placement tutor @ Surrey University.
Holistic marketeer. Met at the Cube, summer 2010. Helping get new website and self-presentation sorted out.
See project proposal here: https://docs.google.com/document/edit?id=1lJ7FPZqKjJTQt5BlnBPTX5ppBXYswiJJt7Q3TTXfKJA&hl=en&authkey=CPHooI8G
Project tracker will be linked to from here: [[kat]]
Worked with them on the [[Streams]] project in July 2009.
Helping out with interface design for [[Scheduler 4]], May 2010.
Done as a favour.
http://laurenparsons.com
Single-page site, hosted on a dropbox public folder with the domain masking used to conceal the URL (i.e. a frameset)
{{{
curl http://foo.lighthouseapp.com/projects.xml?_token=YOUR_API_KEY
}}}
tickets:
{{{
GET /projects/{project_id}/tickets.xml
}}}
A simple code runner that outputs the result of a server-side evaluation of the code you type.
Found that Smart Platform doesn't escape '+' characters in POSTed parameters properly - although request.content is correct, request.body has the '+' characters replaced by ' '. Posted about on the Smart forum: http://discuss.joyent.com/viewtopic.php?pid=212906
Code in a gist at: http://gist.github.com/383485
Screenshot: http://img.skitch.com/20100429-pt7nuf9a6gy53ebnn9r47q5dh2.jpg
Posted about on Smart forum: http://discuss.joyent.com/viewtopic.php?pid=212905
Other thing to note: .js files can't have '-' characters in their names. Posted about on the Smart forum: http://discuss.joyent.com/viewtopic.php?pid=212907
/***
|''Name:''|LoadRemoteFileThroughProxy (previous LoadRemoteFileHijack)|
|''Description:''|When the TiddlyWiki file is located on the web (view over http) the content of [[SiteProxy]] tiddler is added in front of the file url. If [[SiteProxy]] does not exist "/proxy/" is added. |
|''Version:''|1.1.0|
|''Date:''|mar 17, 2007|
|''Source:''|http://tiddlywiki.bidix.info/#LoadRemoteFileHijack|
|''Author:''|BidiX (BidiX (at) bidix (dot) info)|
|''License:''|[[BSD open source license|http://tiddlywiki.bidix.info/#%5B%5BBSD%20open%20source%20license%5D%5D ]]|
|''~CoreVersion:''|2.2.0|
***/
//{{{
version.extensions.LoadRemoteFileThroughProxy = {
major: 1, minor: 1, revision: 0,
date: new Date("mar 17, 2007"),
source: "http://tiddlywiki.bidix.info/#LoadRemoteFileThroughProxy"};
if (!window.bidix) window.bidix = {}; // bidix namespace
if (!bidix.core) bidix.core = {};
bidix.core.loadRemoteFile = loadRemoteFile;
loadRemoteFile = function(url,callback,params)
{
if ((document.location.toString().substr(0,4) == "http") && (url.substr(0,4) == "http")){
url = store.getTiddlerText("SiteProxy", "/proxy/") + url;
}
return bidix.core.loadRemoteFile(url,callback,params);
}
//}}}
Met w/c 15th Feb, they're a company making TV and radio adverts.
Met [[Tim Last]].
Interested in an eLearning project they have coming up.
Preeti said I ought to look at these "video tour" videos - "introduction" and something about workflows/triggers.
-------
longjump demo, 11th August 2010, 17:20-18:10
with Preeti and Pankaj Malviya.
showing demo of portal-site
you can automate the creation of accounts via API
"sites" look pretty customisable…
logged-in sections are also visually customisable
the back-office section:
for administrative purposes
objects are the "things in an app" e.g. properties
Objects have fields
Views show you lists of the objects' instances and their fields
Pages are the front-end pages, created as JSP's
Sites are collections of Pages, with a choice about whether they require authentication or not
Data Policies = triggers (can be action-based or calendar-based)
You can write Java to create custom actions e.g. integrate with external systems
Classes are Java classes - classes can be executed via the API (they have a REST API)
Workflows can be created with a flow diagram editor - data policies can be executed when you reach a certain state
you can create unauthenticated URL's that exec classes and do stuff.
you can package applications for selling to other customers
we'd register as a managed-service provider
- you get different tenancies/environments (testing, production)
- you also get a tenant management system to see which of your customers is using what
you can re-brand the backend (url, CSS, banner)
there is a pricing model where you can hide the longjump url
sidebar is customisable
tabs are jQuery, so they are customisable
you *could* write your own container if you wanted (HP did this using GWT)
Look at wiki for examples.
Preeti is continuing my trial and upgrading to an MSP account, which will have the Sites API.
Went to the Maemo Summit 2009 with Josh and Harry from Tager. We studied the community and interviewed various people. The idea was to gather some ideas and form some opinions about how the community can be present in and have an effect on the evolution of Maemo as a brand.
''Blogged with links and a few changes at: http://jaybyjayfresh.com/2009/11/12/maemo-summit-2009-amsterdam-a-bit-of-an-open-source-eye-opener/''
------
Last month, Tager Communications (a customer) took me out to the Nokia-sponsored Maemo Summit in Copenhagen to help figure out whether Maemo could play a part in the national message Nokia's marketing division puts out about its new N9xx series.
The most striking thing about being a visitor at the summit was how large the community was - at least 300 people turned up. It was later made doubly striking when I found out that almost all of those people don't get paid to work on or with Maemo - in fact, the N900 pre-production loan we were treated to was only available to people not on Nokia's payroll.
This was my first experience of a cohesive open source community outside of the TiddlyWiki community - it gave me much more faith in the ability of large numbers of people to work together to produce something without a centrally enforced power structure coercing them to do so. Everyone we spoke to had a very different reason for participating in the project, often because their participation gave them experience or tools that helped them sell services to other companies. Others contributed simply because they found it interesting (or earnt them Wuffie).
The whole community seemed to be attached to Nokia through a single employee - Quim Gill - and, almost surprisingly, there was 'nuff luv felt for the man. It really seems as if Nokia have done a good job "stewarding" the Maemo community, filling such gaps as needed filling (graphic design, workshop hosting) and ultimately, being rewarded with a piece of software that represents an entirely different understanding of what it means to be a smart phone OS.
And that is killer with Maemo - Nokia's N900 is obviously a competitor to the iPhone, but the experience of using it is nothing short of using a tiny computer. The "app" metaphor is nowhere near as prominent as on the iPhone or on Google Android - applications can also surface in deep integrations with other system software. As an example, the N900 ships with Skype, although you'd never know it - there is no app icon to click on. To make a Skype call you first add your account as a "VOIP and IM" account and then choose "Skype call" instead of "Cellular call" from the dialer. (Cellular? I'm sure there will be a UK localisation by the time the device is released in Blighty.)
Another departure from the standards set by both the iPhone and Android is the acceptance of web technologies as the building blocks for Maemo applications. Launching at some point in November, Nokia Web Runtime will allow packages, suspiciously similar to W3C widgets, to be installed on the phone. HTML and CSS control an application's appearance and JavaScript controls the behaviour and taps into the native abilities of the device, such as the camera and accelerometer. The fact that these applications can also make it into Nokia's Ovi store will mean that the huge financial appeal of Apple's App Store is extended to thousands times more people than the Objective-C massive.
The N900 isn't going to convert many Mac fan-boys (self included), but it's a damn fine start at a different approach. Keep an eye out for the N910.
''THINGS''
[[This Workbook]]
[[What's on]]
[[Reviews|Weekly Reviews]]
''THEMES''
[[Motive]]
[[Automation]]
[[Open Business]]
[[Go Public]]
[[Productively]]
[[Zero-code]]
[[Agile Methodology]]
[[Improving this lab book]]
[[edit this menu|MainMenu]]
[[Cecily]]
As of 31/8/10, I'm not creating tiddlers for invoices anymore. I've never had reason to use one of the invoice tiddlers, and the only reason I can see them being useful - financial transparency - is attained by having my electronomad Google Docs folder published. There is also the question of what to link to on the invoice, but for now I'm using the project's tiddler.
The process is now:
1. Clone an invoice in Google Docs; 2. change the relevant details; 4. download as a PDF; 5. email invoice PDF to customer; 6. add invoice to yellowcar ltd folder; 7. submit record into invoice tracker - 2010-2011: http://tinyurl.com/39y4ef4
-----
This process is a bit complicated...
1. Clone an invoice in Google Docs; 2. change the relevant details; 3. publish it; 4. download as a PDF; 5. create a new invoice tiddler with the correct details; 6. email invoice PDF to customer; 7. add Google Doc to electronomad folder; submit record into invoice tracker
The invoice tiddlers are here for transparency purposes (and so they can be linked to from project tiddlers) - maybe I'd be better just linking to the docs in Google Docs?
Submit a new invoice:
2010-2011: http://tinyurl.com/39y4ef4
2009-2010: http://tinyurl.com/yellowcarltdinvoicetracker2
Existing invoices:
<<list filter '[title[Invoice 0]][sort[-title]]'>>
Tuesday 14th April - cooked mackerel with Josh
This theme probably covers doing freelance jobs well as well as coming up with good new things for people; might also include something like making time to make Bonnie feel special.
Used a TiddlyWeb to track a project rather than a mix of TiddlyWiki and Google Spreadsheets.
CommentsPlugin let me allow Charli to add comments as answers to questions.
A snippetsPlugin scans for delimited snippets e.g. questions delimited by the bold marker, {{{''}}}, and lists them.
A readLogPlugin looks for time entries in natural language e.g. 15:00 - 17:00 or 3hrs 10mins, and produces a summary of how much time has been invested.
One thing that's come up is that having all the times logged in a tiddler is cool and easy, but it doesn't immediately give the graphs and sense of progress that you get from Google Spreadsheets.
markshahid.co.uk
Flash, design, getting into web design and Wordpress
<script type="text/javascript">
var gaJsHost = (("https:" == document.location.protocol) ? "https://ssl." : "http://www.");
document.write(unescape("%3Cscript src='" + gaJsHost + "google-analytics.com/ga.js' type='text/javascript'%3E%3C/script%3E"));
</script>
<!--{{{-->
<meta name="viewport" content="width=device-width" />
<link rel='alternate' type='application/rss+xml' title='RSS' href='index.xml' />
<!--}}}-->
Aim for zero-code, max func:
[[Build your own TwitPic]]
[[dm2me]]
[[Twitter @replies]]
[[whoscelebratingtoday.com]]
Book by Ricardo Semler about SemCo
!Constraints
* Powerbook G4 has
|Name |URL |What does it do? |Free? |
|Thumalizr |http://www.thumbalizr.com/ |Creates thumbnails of images |Yes, with paid options |
Also:
http://www.flowtown.com - gets information about a person, from social sites, from their email address
http://diffchecker.com - online diffing between text files and typed-in text
http://www.namechecklist.com/ - check domain, social media and traditional media for your prospective name
http://100pulse.com/ - server uptime monitoring, 5min/15mins = paid/free
http://whenisgood.net/ - like Doodle
http://doodle.com - meeting planner
http://www.loop11.com/ - usability-testing - basically uses framed-journeys asking people to complete tasks on a site and check a box when they have; 1 project free, $350 a project thereafter. Essentially like SMM's Trailz (have emailed him about this)
http://www.webservius.com - set up a developer API program
http://www.kindlingapp.com - idea discussion and rating tool. Gives you kudos (or money) for participation.
http://www.postful.com/ - allows you to create print materials via API calls or email
http://www.clickhooks.com/ - link shortener that lets you attach a webhook to a link-click
http://www.typetester.org/ - lets you compare different fonts and create safe font stacks
http://www.timeapi.org/ - RESTful time querying based on natural language; also supports JSONP
You want to send jobs to your people in the field. Customers want transparent access to job statuses.
http://www.connect2field.com - hosted application and has a REST API
http://gomockingbird.com/
# Take power from centralised few and distribute to the dispersed many
# The recession is a good time for small, independent, creative producers
# Take yourself out of the process
# Duplication - don't do it
# psd: a) we, decided, policies, rules b) you, invited, help, participate
# Things are very often "big" or "small"
# [[Work for perks]]
Also, see "Credo" - http://jaybyjayfresh.com/Credo
I want to get to a set of Amit's designs, so that I can use them as the basis for a build cycle. I navigate to them because I know where they are stored.
I want to update the Avox team what has been going on with the development, and I know that they can understand how to get the information that they want from the various places I (and others) put it.
I want the change history of Amit's designs to be obvious so people can see where we've come from. I can point to a group of old designs and say "they were version 1", "these are version 2", and so on.
Chris finds it easy to understand the priorities for the server-side work even though I am writing about it in various places such as on email threads, in project plans, in bug comments, et al.
I have a lot of confidence that it is not just me keeping things up-to-date, and that important changes do not happen without ripples moving through the system and the news surfacing to anyone who cares about it.
I know that only me and Chris are using Subversion - this does not mean that Josh or Amit's designs and builds don't make it into the repositories.
I know that when I make a change, the test suite is run.
I know that deploying isn't a case of copying the repositories onto the live server and restarting the web server.
When Josh or Amit create a design, feedback on that design appears as annotations, not as emails, which reduces duplication of feedback. The feedback can be responded to in situ, which helps to evolve a conversation about a design.
When Ken or anyone else finds something is wrong with something that's been deployed, they know exactly where to go to report it. They are kept up-to-date as the problem moves to resolution. They are also aware if someone has reported something similar or the same and can opt to watch that other thing if they want, feeling equal to the original reporter. The stack of problems is clear to Ken or Adam when they report a problem, so they know whether to expect an immediate response or not.
When I want to send a message to a group of people asking them to do a certain thing, such as work together to automate updates to wiki-data, it is easier for me to do this outside of email because I know it will ease the pain of linking this thing into everything else that's being tracked.
If Michael hasn't been paying attention for a while, he can get up to speed about what's important without having to ask.
When Brian or Adam had ideas for a colour-contrast tool or a test-automation tool, they could drop this information in where it would be picked up by anyone looking at colours or tests.
When it comes to tracking time, I don't have to add the totals up myself, I just say how long things took. I get my own personal total.
When it comes to estimating how long things are going to take, I say how much I think things will take relative to other things, and as I complete the things, the projected time to complete the other things updates taking into account my ability to estimate accurately. I can see the effect of completing things on the total estimated time and things are scaled up or down depending on how long I take in total.
As a contributor, I can just write stuff, you know, it doesn't have to be a task, a design, a comment, et al. I link to other stuff and tag for my own convenience.
Anna%20Freud%20Centre 75 1501 767 332
AnotherMap 3250 500 225 358
BlankMap 3500 500 225 77
Cecily 2888 513 225 312
CecilyPlugin 500 500 225 7641
Databases 1703 608 225 266
DefaultTiddlers 250 500 225 129
FOIRequestsUK 642 294 492 1197
Flock%20O%20Tweets 116 1504 981 913
Google%20Tasks%20integration%20into%20TeamTasks 500 500 225 354
Hosted%20services 1940 414 575 2131
ILGA 111 971 475 316
Ideas%20to%20improve%20this 250 500 225 374
MainMenu -356 504 461 457
MyMa 1000 500 225 171
MyMap 3000 500 225 326
New%20Tiddler 582 2229 225 171
OverlayMenu 1250 500 225 193
PageTemplate 250 500 225 221
Projects -351 964 415 494
SideBarOptions -350 1486 225 101
SideBarTabs 750 500 225 603
TiddlySMS 1369 302 426 2887
ViewTemplate 4500 500 225 70
Website%20creators 1527 1255 392 389
Where%20I've%20been 250 500 225 139
eCommerce%20services 1446 883 480 405
saveDefaultTiddlersPlugin 14000 500 225 286
See [[NCP Alternative Use]]
Have been looking into putting shipping containers or perspex boxes on the top of NCP car-parks for use as office, studio, residential, etc. Doing this with Ben. [[Ben Heath]] has been the main contact at NCP.
Chat with [[Andy Kythreotis]] - Ben L and I met him on 25th March. He's interested in putting structures on the roofs of car-parks. He's going to get together a list of suitable car-parks for us and we'll go have a look at them. He's also said that he thinks planning permission will be the major hurdle. He mentioned something about circa £7k because a cost to change use (maybe that was a landlord fee). He also said that NCP are generally looking to sub-let the space. He mentioned storage as a use they're realistically considering. Plus, they've let space outside the Brewer St. NCP to a routemaster vegan bus.
Google Doc with research: https://docs.google.com/Doc?docid=0AQQJ7FGUGIp_ZHI0NG5mY185OWZoZDdrNHNm&hl=en
you
me
josh
alex
sandeep
tom
dan
mike mcnare - schoolfriend
shane - h's sister's boyfriend
paolo
stephen i
? martin b
? ben nickolls
? jon robson
Feb...
venice? ask H about friend's flat. foodie? too expensive? It's carnevale, which makes it expensive.
don't want to go to Prague, not Berlin (Paolo did Berlin).
get in touch with Dan - he's half Italian. N is going to send me D's email. Speak to Dan about Italy. Daniel.Ferro@foe.co.uk, daniel.ferro@rocketmail.com
Budapest - ask Orsi
Ljubljana?
Salzburg?
What's the weather like in Feb in Southern Italy?
Dubrovnik, Croatia?
http://nodejs.org
Untested hosted version: http://elusivehippo.com
Using [[PostBin]] to receive and log POSTs is useful. You can fork workflows to include this debugging step.
Turns out debugging with the console is quite helpful, as you can see the response from POSTs (not very hosted, but hmm...) e.g.
{{{
curl -d 'pipeId=lAUEoB1R3hGTqt6hggSecQ&daon": "I wonder if this will work<br clear=\"all\"><br>-- <br>t: @jayfresh<br>b: <a href=\"http:\/\/www.jaybyjayfresh.com\">http:\/\/www.jaybyjayfresh.com<\/a><br>\n\n", "link": null, "title": "another test" } ] }' http://f7e3dbf.smart.joyent.com
}}}
''Blogged at http://tinyurl.com/mhwxgz''
It's not as simple as hitting an edit button yet, but it's still worth having a play. The use of [[Sammy|http://smart.joyent.com/docs/library/com/joyent/Sammy.html]] to handle and dispatch incoming requests, along with a single "bootstrap.js" file that controls all the SSJS, means that it ought to be fairly easy to port over single-page AppJet apps to Smart Platform.
Here's some notes I've gathered whilst porting over an application that acts as a gateway between tarpipe's REST connector and a Yahoo! Pipe, allowing you to send data to a Yahoo! Pipe for processing.
* Request object - in AppJet, you get the POSTed variables and the query-string parameters both in {{{request.params}}}. In Smart, the query-string parameters are in {{{request.query}}} with the raw string in {{{request.queryString}}}; POSTed variables are in {{{request.body}}} with the raw datastring in {{{request.content}}}.
* Dispatching - in AppJet, you have the option to define different functions for handling different types of request e.g. GETs, POSTs and requests to different resources e.g. {{{/this}}}, {{{/that}}}. Smart uses [[Sammy|http://code.quirkey.com/sammy/]], which gives you a similar mechanism to choose which functions respond to different incoming requests, although you also have the option to use a single function {{{main(request) {...}}}} to handle all responses. A concrete example is:
{{{
AppJet:
function get_resource() { ... }
Smart:
GET("/resource", function() { });
}}}
* Writing a response - AppJet provides a default chrome that wraps an application, which you can remove by setting {{{page.setMode('plain')}}}; then you use functions like {{{print(response)}}} to send the respons. Smart does it slightly differently, where whatever you return from a function is sent as the response. It doesn't seem possible to send HTTP header information.
* Making HTTP requests - AppJet provides functions like {{{wpost}}} and {{{wget}}} to make HTTP calls. Smart provides the {{{system.http.request}}} function to do the same sort of thing. The difference in the returned response is that AppJet gave you a raw string of the response body, whereas Smart returns an object containing {{{headers}}} and {{{content}}} properties.
Doing the Git bit for the deployment was pretty easy, and now my app is up at http://f7e3dbf.smart.joyent.com. Try it out with:
{{{
curl -d 'pipeId=lAUEoB1R3hGTqt6hggSecQ&data={"items":[{"title":"A title","description":"Any description","link":"http:\/\/example.com"}]}' http://f7e3dbf.smart.joyent.com
}}}
This is related to data momentum. For a service that doesn't implement web hooks or keep the momentum going in some other way, services that monitor change and notify other apps are useful. These make up a part of the web's nervous system.
| Name | Input | Output | Support for protected feeds? | Free |
| [[gnip]] | REST, ATOM, XMPP | REST | ? | no, min $100/month |
| [[notifixio.us]] | RSS, ATOM, XML feed | Email, SMS, IM | No | yes |
| [[notify.me]] | RSS, ATOM, XML feed | Email, SMS, IM | HTTP Auth | yes |
Also:
Amazon Simple Notification Service - messaging hub. Publishes over HTTP, Email or Amazon SQS (which just does queuing)
http://kwwika.com/ - push notifications, including Web Sockets
http://pusherapp.com - push Web Socket notifications
http://femtoo.com - track changes in any webpage
http://notifo.com/ - mobile notifications service via a native app
http://www.feednotifier.org/ - uses notify.io to provide real-time feed updates for any PubSubHubbub-enabled feed
http://ping.fm - can POST to any URL
http://tarpipe.com - can POST to any URL
!Afternoon session (from 17:00-19:30)
!!Notes from yesterday's meeting
inbound SMS cases:
# stop
# someone has texted AFTER dm'ing 'JOIN' to @notion
# someone has texted BEFORE dm'ing 'JOIN' to @notion (which means we don't know if they are following or not)
Need to make:
#example web form
#interface with gateway to respond to form POST and send out a message asking people to sign up (Olly still figuring out if there's a way to handle this in the system so I don't have to hold the text of the SMS)
#handle POSTs to /stop, /add
#set up service and send host details to Olly
#provide example of how to send out to private list
!!The bits of the robot
#Respond to 'JOIN' dm from twitter, from someone who hasn't texted in
** This will cause a POST to '/join'
** This needs to store their twitter ID and their phone number, so the link can be made
#Respond to a SMS from someone who hasn't dm'd 'JOIN' to @notion yet
** This will cause a POST to '/join'
** The number needs to be saved so that it can be looked up when they do dm @notion
#Auto-follower
#Handle submits of web form and POST to gateway to trigger sign-up SMS
#Handle interaction with Notion staff to send out messages
#Collect responses to messages
** if you ran parallel competitions, how would you separate the messages out?
** if you had 'normal' dm's coming in as well, how would you separate the messages out?
** would it be ok to ask people to prefix answers with a keyword?
!!Progress
#Got hold of http://notion.smart.joyent.com
#Added some functions to handle incoming text messages and incoming 'JOIN' tweets, with (hopefully) the right logic to activate people at the right time
#Added handling for STOP requests
!!Barriers
#http://notion.smart.joyent.com (and local version) returns error about no bootstrap file, which there is... posted about this on the forum: http://discuss.joyent.com/viewtopic.php?pid=202747
!Morning session (from 11am - 12pm)
#Trying to get app to deploy and run
** Managed to get it running locally - you have to point the Smart.app at the folder above the 127.0.0.1
** Don't know why I'm still getting the bootstrap problem - might try adding a bootstrap.js file at the top-level and see if that helps, which would show that the wrong level of the folder hierarchy is being committed
#Inputs to the robot
** Hopefully, I have a better idea now of where the inputs can come from:
*** Twitter actions e.g. follow, @reply, dm
**** the dm can signify a desire to JOIN and supply phone number (this would need to trigger a SMS from Dragon)
*** POST from a web form
**** this can signify a desire to JOIN and supply phone number (although probably not follow, unless provide username and password for twitter account; this would need to trigger a SMS from Dragon)
*** POST from Dragon
**** this can signify signing up, whether or not they are following @notion, or a desire to STOP
!Afternoon session (from 12:30pm - 14:15)
#Testing locally - how can I test this?
** I can mock up flows and check they work as expected. Let's start with the best case:
*** Signup
#### person follows @notion
#### @notion follows them back
#### person decides to sign up and dm's 'JOIN 07xxxxxxxxx' to @notion
#### watcher lets robot know at /tweet
***** probably POSTs Yahoo! Pipes data={...} format
#### robot saves person's twitter id and phone number and replies to person asking them to watch their phone
#### robot let's Dragon know
***** Dragon expects an XML document, I think
#### Dragon adds person to NON-SUBS list and sends SMS to person asking them to reply with 'TWEET'
#### Person replies with 'TWEET'
#### Dragon adds person to SUBS list and lets robot know at /join
***** POSTs key-pair values string as described [[here|https://dragon.brainstorm.co.uk/MEnable/Client/Extra/bspostevent-1_0_0.dtd]]
#### Robot looks person's phone number up and adds to ACTIVATED list
#### Robot sends welcome dm to person
*** Quiz
#### Notion sends out dm to all on ACTIVATED list
#### Person gets dm
#### Person responds
#### Robot logs person's answer
** Also doing the case where the person sends the 'TWEET' SMS before they dm 'JOIN':
*** Signup
#### person sends SMS to Dragon with 'TWEET' in it
#### Dragon adds the person to SUBS list and sends SMS to person welcoming them and asking them to dm 'JOIN' and their phone number to @notion; Dragon lets robot know at /join
#### Robot adds person's phone number but doesn't activate them
#### person follows @notion
#### @notion follows them back
#### person dm's 'JOIN 07xxxxxxxxx' to @notion
#### watcher lets robot know at /tweet
#### robot sees that person's phone number is already set up, adds their twitter id and adds them to the ACTIVATED list
#### robot sends welcome dm to person
** NOTE: we can ''make this simpler'' by having the person text as so: TWEET twitterId, then we don't need them to make the dm to @notion after they've signed up
#Figured out how to avoid the bootstrap.js deploy problem (from yesterday)
** Upgraded Git from v1.6.0 to v1.6.3.3
!!Questions
#How do I put error logging in if communication with Dragon goes wrong?
!Afternoon session (from 13:00 to 14:15)
#Going to try to respond to some messages from Dragon, as part of trying to make the ideal journey described in [[Notion 23rd July]]
#Writing tests (in qunit) for the robot
** added two new endpoints on the robot - /add_test_account and /remove_test_accounts
!Afternoon session (from 19:20 to 19:35)
#Continuing to write the test for the two journeys described in the afternoon session of [[Notion 23rd July]]
!Afternoon session (from 21:10 - 22:10)
#Continuing to write the test for the two journeys described in the afternoon session of [[Notion 23rd July]]
** Found that searching a Smart Resource before you've added any throws an exception
** It doesn't look like creating a new resource with a parameter sets the id to that parameter - I think the docs suggested it did; instead, creates a random id; ''should verify this''
!Morning session (from 11:30 to 13:50)
#Continuing to get the tests working...
** Found that if you don't return anything it produces a 'not found' error - a 404
** Found that the Resource searching has incomplete documentation e.g. doesn't say that search objects enforce type and that searching by default properties doesn't work; still don't know how to return all Resources of a type; posted forum question [[here|http://discuss.joyent.com/viewtopic.php?id=25688]]
** Found that boolean values of a Resource are sometimes stored numerically - posted [[here|http://discuss.joyent.com/viewtopic.php?pid=203294#p203294]]
** Have got a test working!
** Have the tests for '/sms' module working
!Afternoon session (from 14:20 to 17:00)
#Going to test the '/tweet' module - DONE
** Wondered how you can send emails from Smart - posted on the forum [[here|http://discuss.joyent.com/viewtopic.php?id=25633]]
#Now am wondering:
## Do we know yet whether me sending a POST to the Dragon gateway puts a person into the NON-SUBS list (which the Interaction Tree for TWITTER_MAIN assumes)
## Have done the unit tests, how do we test the system?
## When sending to the Dragon gateway, what password do I use and how do I format the mobile numbers (do I need e.g. 'o2.uk-' on the front)?
** Have emailed Olly about all this, waiting for his response
Customer: Notion
Rate: revenue share (35% / 30% for two months each, then 25%)
Description: Allowing online channels (Twitter as example) for running competitions that are more interesting and engaging than premium SMS competitions; continuing to use SMS for billing, so requires a connection between the web and premium SMS.
!Before the log
Three meetings to ascertain nature of project, set up the SMS service and agree revenue share
!Log
[[Notion 22nd July]]
[[Notion 23rd July]]
[[Notion 27th July]]
[[Notion 29th July]]
[[Notion 2nd August]]
[[Notion 3rd August]]
Temporary conclusion, one year in: the market is the least efficient part of the company. I have not earnt a penny through revenue- or profit-sharing projects (which I intended to), nor have I become shareholder in any company other than my own (which I intended to be). This is the constraint to financial growth.
The solution to the market constraint is to raise the awareness of the product and make it attractive enough. So, what is the product? In my case, it is "using the Web to improve the way someone interacts with a company as a customer or an employee". Or thereabouts. Actually, that is the end result. The bulk of the product is a group of people taking on the burden of figuring out how to satisfy a business owner's needs, and then doing it. All within an environment of co-operative venturing, for both the group and the client. This means being a part of a project's success and being fairly rewarded for your impact.
What raises awareness? Presence in other people's minds defines awareness, so getting in front of them through meeting, advertising, publishing or teaching raises awareness. What makes the product attractive enough? Catering to the specific needs of a person, which can mean being sensitive to their context and often helping them realise what they want - these things are attractive, so it is important that a person feels like I would provide that.
So, how to raise the profile of my product in such as a way as to be attractive to the sort of people who would want to operate in an environment of co-operative venturing...
Presence: The co-opererative aspect is partly about promoting the individual and their talents, their niche in some cases. The network of people should come across as a group of individuals with very obvious strengths that complement each other. The network creates a single object for someone to direct their attention to, which potentially makes it more attractive than any individual. This network has no presence at the moment, except when I talk about it. I think is worth exploring what a permanent presence for this network could look like, and persuade the people I consider inside the network to make a commitment to being a part of it. Ideas include: an online gallery of projects by the network, with links to their personal presences; encouraging the individuals to curate a presence demonstrating their niche (or interests and talents); any common presence to be co-owned and co-maintained - this for a sense of belonging and privilege, not in anticipation of equal efforts towards upkeep.
Attractiveness: The need for attractiveness may be served by making a virtue out of the reasonably interesting aspects of the product. The first of these is the desire to become a part of a project's future success by offsetting consultancy fees now in exchange for a stake in the returns. I think this is a recession-friendly concept, and flies in the face of conventional startup funding schemes where money is invested - here, time and effort is invested. There is also the pay-off model for growing an equal-stakes project. It is hoped these financial models will be practical alternatives to conventional, hierarchical and asymmetric, growth models.
The second interesting aspect is the mutual nature of the network. There is no hierarchy and no fixed source for projects. The network grows through the process of participation in projects, as it is important for efficient working relationships to develop between members. The third interesting aspect is partly a consequence of the second - the quality of people is very high since the network grows by members choosing to work with people who they think will improve a project's outcome.
Aside from providing an interesting product, I think it is attractive if a product claims to satisfy what you are looking for and if those claims appear credible. Credibility is a field well-studied by advertisers, and I think books like "Purple Cow" and most behavioural economics books help to illuminate how human decision-making works. I feel less confident with the question of how to make a product appear to satisfy what someone is looking for. I believe targeting a niche would be a successful strategy, although I don't feel I know of a niche of people's needs and wants that matches both the product I offer and the way I want to produce it. It is entirely possible that the operational and structural nature of the group matter little to future customers, which would invalidate much of what I've written above. In which case, it is still possible that I am effectively "selling" the product of membership of the group to other future members.
Creating our own products: All of the above is written from the point of view of acting as a supplier to someone else who has had the idea for a product. I very much want to create products from within the group, and to that end have thought a lot about sensible ways to divvy up profits (hence the pay-off model mentioned above). Creating your own products would probably involve offsetting a greater amount of payment than projects where the group acts as a supplier.
To Conclude: It seems that the creation of a group presence and the targeting of people who our product would be attractive to is a path to growth. It is unclear to me whether any or all of co-operative venturing is or needs to be interesting to either customers, or whether it is primarily a carrot for members of the group. If it is the case that co-operative venturing is not of interest to customers, that still leaves high quality, data-driven, results-based business-transformation as a product. Targeting customers would then come down to those who had interesting projects, which paid sufficiently well. I hope that securing a stake in the future success of projects in return for better value now is attractive to customers, as it is very attractive to me.
A place to put structured data in the cloud. Different from static file storage and different from hosted development. Online databases tend to give you some cool tools to produce interesting output that results from processing the stored data.
See [[A survey of hosted application building tools]]
| Name | Input | Output | Free? |
| [[Dabble DB]] | Manual, REST | n/a | Yes, with paid (add more detail) |
| [[nextDB]] | ? | ? | ? |
| [[Zoho Creator]] | ? | ? | ? |
Nick: Databases I might want include products, customers, suppliers, staff, ...
These are limited to services that let you write JavaScript and save it in the sky.
Also see: [[A survey of hosted application building tools]]
| Name | Free? | Notes |
| Smart Platform | Yes, at the moment (July 2009) | Came out around July 2009. Server-side JS you can deploy using 'git push'. Not in-browser editing yet, but worth keeping an eye on. |
| limebits.com | yes | came out in Jan 2008. online development, sharing of code and hosting service. |
| AppJet | yes | closed in July 2009! |
| [[JGate]] | yes | clone of AppJet |
| http://ecco.sourceforge.net/ | n/a (not out yet) | |
| https://bespin.mozilla.com/ | Yes | |
| http://www.codeide.com/ | Yes | Can't understand what this is going to do |
| http://tide4javascript.com/ | Yes | Not sure what this is going to do |
| PostBin | yes | logs any requests so you can debug ajax and webhooks |
| RegexPal | yes | helps visualize applying regex to text |
Heroku is worth a mention even though it is not for JavaScript, it is for ruby (and now others!)
Google AppEngine is for Python.
UtilityMill is for Python.
Also:
http://scriptlets.org - simple online JavaScript, Python & PHP hosting and execution
http://istylr.com/ - CSS/HTML editor, uses CSS templates (not seen details)
http://www.codeita.com/ - online code editor, SVG editor, MySQL and PHP - I have a demo account now
http://www.qrimp.com/ - online database exploration and visualisation; also provides notifications when entries change according to logic-based criteria
http://fontstruct.fontshop.com/ - create fonts
http://geticeberg.com - $100/year, hosted workflow-based app creation - have emailed to ask for specific demo
http://app2you.com - no idea really, have applied for beta account
http://longjump.com - no idea how it works, have written to ask for advice
http://www.wolfframeworks.com - not sure how it works, have written to ask for demo
http://www.notify.io/ - an online service you can use an a notifications hub (supports PubSub and direct notifications, plus you can receive notifications in various formats)
http://www.timeapi.org/ - send natural language, get back ISO dates
http://www.mailhooks.com/ - gives you an @mailhooks.com email address that you set up to POST to a URL
http://www.jabberhooks.com/ - like mailhooks, but for Jabber/XMPP
http://www.getachievements.com - hosted achievements to unlock on your website
http://jsbeautifier.org/ - makes JS easier to read
http://www.twilio.com/ - voice/SMS-enable apps via API
http://www.imified.com - lets you turn a dynamic webpage into an IM bot
http://spinn3r.com/ - blogosphere indexer (with API and webhooks)
http://wufoo.com - hosted forms and notifications
http://cquery.com - API to query CSS-selected content of any page
http://localtunnel.com - put a local server on the web - couldn't get it to work in MAMP
http://jsbin.com/ - in-browser JS editing, with simple HTML wrapper and common JS libraries
http://del-fuegos.no.de/ - Nodify, hosted node.js app editor
http://inflatable-chum.no.de - hosted JS dev, not sure how you run these things
http://wrathjs.com/ - "Wrath is a hosted API mocking and testing service"
http://edit.shiftcreate.com/ - "ShiftEdit is a browser-based IDE for developing PHP, Ruby, HTML, CSS and JavaScript with built-in (S)FTP"
https://builder.mozillalabs.com/ - Firefox add-on builder, uses Bespin/SkyWriter and needs JetPack
http://ethercodes.com/ - EtherPad back-end, SkyWriter front-end
!!My tools
[[Make an invoice]]
[[See invoice summary]]
!!Running an Open Business
Trying to be as open as possible...
# all invoices published
# all cashflow published
# salaries and expenses published
# projects log published here
# projects open for anyone to get involved
<fill-in more as I think of them>
!!Examples of Open Businesses
*NixxonMcInnes (haven't checked them out yet)
*Gore
*eText-editor
*Semco
...needs some links to stuff about them
I have a GitHub repository at http://github.com/jayfresh
!Initial contributor to:
TiddlyTweets: http://svn.tiddlywiki.org/Trunk/verticals/TiddlyTweets/
[[Contributively]]: http://github.com/jayfresh/contributively/tree/master
Several others at: http://github.com/jayfresh
!Major contributor to:
I've been contributing to the TiddlyWiki project since 2007 - my [[TiddlyWiki]] contributors' directory is at: http://trac.tiddlywiki.org/browser/Trunk/contributors/JonathanLister
[[wiki-data]]: http://github.com/jayfresh/wiki-data
[[Geekmap]]: http://github.com/danieljohnmorris/geekmap/tree/master
!Minor contributor to:
[[Joyent Smart Platform]] - written [[tutorials|http://jaybyjayfresh.com/?s=joyent+smart+platform]]
[[WireIt]] JavaScript library - written [[tutorials|http://jaybyjayfresh.com/?s=wireit]] and influenced development of WiringEditor features e.g. [[this|http://groups.google.com/group/wireit/browse_thread/thread/29b070dfb0495d29]] and [[this GGroups thread|http://groups.google.com/group/wireit/browse_thread/thread/ba477699bd397195]]; bug reporting: tickets [[22|http://neyric.lighthouseapp.com/projects/25048/tickets/22]] and [[23|http://neyric.lighthouseapp.com/projects/25048/tickets/23]]
[[OpenLayers SVN layer]]: http://trac.openlayers.org/ticket/1808
UserVoice widget tweak for proper function for local files: http://uservoice.zendesk.com/requests/270
jQuery news ticker fix for using with dynamically created lists: http://plugins.jquery.com/node/6990
OAuth library for JavaScript bug fix to handle null parameters: http://code.google.com/p/oauth/issues/detail?id=24
[[jsunity]] - bug fix http://code.google.com/p/jsunity/issues/detail?id=6
!The problem
It's a pain to figure out cross-browser layouts in CSS.
!The idea
An online gallery where you can find and use modular chunks of HTML and CSS, such as 2-column layouts or tabs.
PhilHawksworth suggests there may be some examples of this already.
Needs some examples of the Layer working with real SVN data before they will accept. Perhaps this problem is coming from the Layer only supporting one of the two types of path.
!The problem
Hard to make any project Open Source at the flick of a switch.
Also, the benefits of transparency in the Open Source software world are clear; we'd like to make networks of people, whether or not they are developers, benefit from similar transparency.
!The idea
Make all software projects run on a Open Source-ready infrastructure. Not sure about the transparency part yet.
Working with Gary Casey.
[Another update on same day as all the stuff below] I went to my Staction with the intention of writing the 7 goals in as projects, to have time contributed to. I changed my mind when it came down to it, and I think I'm going to try calling "projects" as things you want to achieve, or maybe create. Then you'll add time to those and they will each contribute to one or more goals, which will happen outside of Staction.
The two I added today are: 'training for the Great North Run' and 'productively'.
------
[Update] After writing the stuff below, I did an exercise where I took my list of 7 goals that I'd made on Monday (13th) and ordered them by priority (using what seemed to be a manual bubble sort). I also identified which ones helped with others and attached tags to them; I used: "creation", "personal", "reputation", "achievement", "impression", "asset" - not so much to categorise in a concrete way, but to see if any patterns emerged from explicitly labelling the impressions I got as I read them.
The conclusion I reached from the prioritisation is that I can now observe how I spend my time on these various goals and see whether the time I'm putting in lines up to the priority order I've specified. It'll probably help with planning time-use too, when I come to do that. It doesn't feel like I ''need'' bubbles yet.
I don't have any conclusions from the tagging, except that the "reputational-type" tags and the "achievement-type" tags are well mixed across the priorities.
-----
Premise: Personal and team organisation can be treated as similar things and that they can be treated scientifically
I try to develop a methodology that allows me to remain confident that:
# I am investing my time in the right things
# I know what the "right things" are
I like the principle of not duplicating information and connecting information from where it enters a system to where it leaves a system. I've found that using a non-hierarchical task list (Google Tasks specfically) has proven very useful as a place to jot down new to-do's; and the lack of hierarchy has meant that it feels important to keep the task list short and not spend too long organising it.
After using Google Tasks for a while, I've found that the size has crept up and that there are number of to-do's that won't be completed for a while and yet take up space. It is beginning to look a bit unmanageable. This has partly happened because the use of this task list has allowed me to expand the range of things I'm doing without losing control. It's now become important to me to know which of these things I should be concentrating my time on and how much, relative to the other projects, I should be spending on each one.
My thoughts about a general system to help me go like this: the unsorted task list remains the way to get to-do's into the system and display the top to-do's that I should be dealing with. These tasks are the information that feeds the more complex bits of the system. There should exist something that shows you how important each project/initiative/whatever is relative to the others, and whether other projects contribute or depend on them. I'm imagining something like a set of bubbles of various sizes, conncected together like a tree, although this latter idea might have to be adjusted as I discover that the relationships between projects are more complex than I originally imagined. Overlaid on top of that visualization, which is in essence a demonstration of relative importances, you would have some indication of how much you were achieving on each project, relative to the others - the idea being that this measure should line up with what you've stated the relative importances to be.
Something independent of the visualisation just described is a statement of what you really want to achieve. I'm imagining that when you try to write down your projects, you end up writing down two types of things: stuff you want and stuff you do. I would like to separate these somehow to show how stuff you do contributes to stuff you want. I am veering towards thinking that stuff you want tends to be things that other people think of you for, whereas stuff you do is individual things that other people can touch that you've made.
This is still a bit confused, but it's become obvious that I could do a couple of things to help clarify the ideas: try and draw some dependency trees and some bubbles; start to measure relative time put into things to see if I that is a useful metric for measuring your patterns of work (I generally don't like measuring time put in, as opposed to achievements, but this is focussing on the relative, not absolute time). I don't quite know how I'm going to do this yet. Perhaps a [[tiddler|Relative time spent, day by day]].
|{{overlayCommand{go}}} |[[home|ProjectCecily]][[a-z|SideBarTabs]]<<search>> |
|{{overlayCommand{zoom}}} |<<cecilyZoomAll>><<cecilyZoom>> |
|{{overlayCommand{map}}} |<<cecilyMap>><<saveDefaultTiddlers>><<closeAll>> |
|{{overlayCommand{share}}} |<<permaview>> |
|{{overlayCommand{create}}} |<<saveChanges>><<newTiddler>><<newJournal "DD MMM YYYY" "journal">> |
|{{overlayCommand{tweak}}} |<<cecilyBackground>>[[options|OptionsPanel]] |
Problem: sometimes you don't have cash on you to pay for small things that you can't pay for with card, but everyone always has an oyster card
Idea: can you use your oyster card to pay for things?
!Questions
#Can it use the existing oyster system?
** ''I don't know'' but I have been looking into a parallel system
#What makes it difficult?
** It is hard to figure out how to take money off people in a convenient way, where it is economically feasible for small-value transactions
#Can you use an Oyster card as an ID?
** An Oyster card has an RFID which you can read (Islington Hub's door entry-system works using Oyster cards - @mrchrisadams set it up)
multi-tenancy: each system needs to be able to cope with multiple accounts, which serve multiple people. E.g. booking system - manages list of admin accounts, which are linked with Google Accounts. Each account has its own public presentation where booked events are saved to the account's linked Google Calendar. If the people booking events were going to have accounts too, they'd need to be linked to the admin account.
Wordpress multisite?
portability?
GAE, Force.com
data
access to data
input of data
events when data is input - occurrence of
logic of handling events
external pages
login pages
protected pages
dynamic pages
our customers' admin accounts
our customers' bills
our customers' customers' accounts
our customers' customers' bills
3rd-party systems taking part in any bit of logic
the development experience
See [[A survey of hosted application building tools]]
------
The following blogged at: http://jaybyjayfresh.com/2010/08/24/paas-a-mid-2010-survey/
I've said in the past that I am a hands-off type of guy when it comes to servers. To avoid dealing with servers in any traditional sense, I have been keeping an eager eye on how things are going with server-side JavaScript, and the push-to-deploy type of application development (Heroku, Smart Platform). But there's something relatively new in the offing, which claims to do away with code entirely - the Platform-as-a-Service (PaaS) company. You might have heard of some - LongJump, Force.com, Zoho Creator, QuickBooks (there's a longer list at the bottom of the post).
In general, people who label themselves with "PaaS" want to do two things: reduce your dependency on coders, and give you a shortcut to setting up business online. Reasonably appealing, you might think? I've been giving the PaaS sector a bit of a once-over in the last couple of weeks, and this is an attempt to piece together half-formed conclusions about what PaaS is and whether it is useful to me.
!!Rapid recommendation
If you don't have time to read the rest of this article, then here's my advice:
* if you are at the non-technical end of the scale, with no access to designers and developers, and you don't care about the way your apps will look, PaaS will help you open up your company information for visualising and editing; changes can be set up to trigger other changes and notifications, and you can schedule reports
* if you are a professional web designer and developer, you are likely to become frustrated at the unresponsive tools used to create PaaS apps, and the barriers to designing the experience of the people using your app
!!A motivating problem
This survey was prompted by the familiar need for a pair of hands to help build a system that neither myself nor my partner felt capable of building well. If I give you an outline of what the system roughly looks like, it ought to set my opinions of PaaS in context.
Essentially, we want to make a website handling information about people and the properties they live in. There are public areas for browsing and private areas for people with accounts. Plenty of design has gone into the appearance and function. There are some web services in the mix - PayPal, a booking system I wrote in server-side JavaScript, and EchoSign. Interactions with the website and notifications from foreign systems need to have effects, manipulating the people and property information.
I think that what I have just described sounds like a very generic web application. Some bits require more know-how than we possess to build: how to write and manage workflows, which link events and notifications to effects; what our data stores should look like and where they should be relative to the website and its contents; how to get our system to respond to notifications from foreign systems; and how to serve the product to multiple customers and charge for it.
I don't recall what prompted me to look at PaaS, but the PaaS companies I first looked at made a big deal about how you can easily model company data structures and create workflows between them. This sounded great. So I went about having a long look at what was out there and whether it would be cheaper and easier to build my app on top of that.
!!What does PaaS claim to do?
In //general//, PaaS companies want you to model your business and its processes as objects on their platform, with various properties hanging off the objects (think Salesforce.com and its tabs). You create forms that open up the system to let people add and amend data, and you hook workflows on to triggers such as the submission of a form. The example that everyone seems to use is an expenses approval system, with Bob, his manager, his manager's manager and Lynne from accounts, ploughing through the process of dealing with Bob's $5000 business trip claim.
Once you've configured a PaaS app to model whatever it is you want to model, you can publish the app to the world, often as something that other people can buy and customise for themselves and their own businesses.
Variations around this theme include companies devoted to: creating complex workflows; providing sets of micro-services; pain-free hosting for code.
!!General characteristics of PaaS
Without giving what would probably be a boring account of all the companies I looked at, there are several axes along which PaaS seems to range which it is worth looking at. I've picked out a couple of example companies that fall along each axis.
!!!Building a business
Several PaaS companies highlight your ability to use their platform to run a business. That can mean several things: you use the app you make to help run your business; you use the app as a part of your own customers' experience; you run a business selling the app to other companies to use with their customers. Some companies cater for all these scenarios, others specialise in one or two.
If you're building an application to use with all your customers, it makes sense to make it a multi-tenanted system - that is, the different customers all log-in to the same app, but see whatever is appropriate to them. If you are building an app that other businesses will sell to their customers, you'll want something a level deeper - multi-tenancy within multi-tenancy (erm, Inception?).
Apps for your own use: Zoho Creator, TrackVia, Iceberg
Build your shop: Caspio Bridge, Wolf Frameworks
Shop-within-a-shop: WorkXPress, RollBase, Longjump
!!!Technical accessibility
If you're a no-hoper when it comes to HTML and you've never heard of PHP, there are PaaS products for you. They will help you model your business and its processes and do helpful things like sending you emails when your tasks are late.
Equally, some products require someone versed in the vagaries of Microsoft systems integration to get anything out of them. However, if you have the requisite knowledge, you could be integrating with your Active Directory or performing complex database queries to help the system make decisions.
Anyone could do it: TrackVia, WorkXPress
You've just come off a web dev course: Zoho Creator, BlankSlate, Iceberg
You'll need a seriously hardcore dude: Skelta, LongJump
!!!Access to the bare metal
If you've ever used Salesforce, you'll know that almost every Salesforce app looks the same. This is not an adequate level of control over the experience. Ideally, a PaaS product should give you - the app developer - enough power to control your customers' and their customers' experience (hopefully whilst providing tools to make this easier than coding from scratch). Some companies have a stab at this (even if they don't make it very easy), others lock the experience down.
Complete control: BlankSlate, LongJump
Not quite like putty: Zoho Creator, Rollbase, WorkXPress
You do it OUR way!: Skelta, Iceberg
!!!Presentation: AJAX vs. templates
This is not PaaS' strong point. It seems very few PaaS companies expect there to be a presentation layer beyond Salesforce.com-style tabs and tables, which is a shame.
You can pretty much take it for granted that online products provide API access to read & write your data. PaaS products are online products and that assumption seems to hold good with most of them. This of course means that you can create experiences all of your own by writing AJAZy mashups. This isn't ideal in practice, as it leaves the non-JavaScript browser without an experience at all, not to mention that AJAZ-heavy apps are unlikely to function appropriately on a mobile; SEO and accessibility are concerns as well. Unfortunately, some PaaS companies actively promote this method.
You could employ an app developer to use the PaaS API's as a substitute data store, but this approach does seem a little odd given that a stated aim is to take the developers out of the process, but anyway…
The PaaS companies who have their heads screwed on correctly, manage your presentation layer using templates, interpreted on the server. However, you are often straitjacketed into using a particular layout. It would be a valid PaaS model to offer you an easy way to hook in a presentation layer of your choice, and since an app's appeal very often benefits from decent visual presentation, this model could look very attractive.
AJAZ-only is the way: Wolf Frameworks, BlankSlate, Caspio Bridge
Templated goodness: LongJump, Zoho Creator
!!But does any of this suit?
My overall impression from looking at around twenty-five PaaS companies, and deeper at a dozen, is that everyone wants you to think their way, and that even though webby people are generally supportive of open data, there is something very "lock-in" about the way you put these applications together. This includes the feeling that you have to get into their developers' heads to understand how you should construct an application; that your application is really just tweaking something that's been set up in advance, both functionally and visually; that no-one wants to play nicely with any other tool-providers out there.
Then there's the question of whether the objects/properties/process model is as straightforward a mapping of the real world as is claimed. Even if an entire business can be abstracted to these bare elements, the difference between two businesses is more than skin-deep, and so this abstraction feels hollow. I am troubled by the observation that I struggled to find any examples of PaaS offerings backing good public websites and services. Only BlankSlate had anything attractive and functional to show, and they had integrated with existing websites (BlankSlate are entirely focused on providing APIs).
The main advantage to the craft of web development and web design is that you are free to imagine any types of interaction and see those brought to life. You design and create both public and private areas, and hand-code the logic that links the two. Normally, this involves a creative and complex response to a client's problem - a design process.
I think that the experience of use, which is the end result of a design process, is the most important facet of an online system. In my opinion, PaaS companies are neglecting this, and they appear to imagine their customers want to build applications for robots. Even if a PaaS app is only destined to be used inside a business (which it would appear they are in the vast majority of cases), that is no excuse for a bad experience. Haven't big-company employees been complaining about the state of their corporate IT for decades?
Web development by web developers is something approached with a toolbox. What makes a tool? Something that is self-contained and has its part in the bigger job - a favourite framework, or an image editor, for example. The tools work together to turn stuff into useful stuff. PaaS is supposed to be bringing the experience of creating application development to the people who are coming up with the needs for them - "business" people. Most PaaS products do not look like a toolbox, but a single, infinitely complex tool. And when an app is built, it's likely the people using it will be doing so through the tool used to build it i.e. itself.
Something's gone wrong here.
!!So what do I want?
Here's my shopping list for PaaS:
I want the typical PaaS setup to resemble a toolbox, full of useful things that you use to create experiences. The output is not just the tool, reconfigured.
I don't want to fight with my tools - they should be tuned to the context they will be used in and they should respond to my touch (laggy interfaces are a massive turn-off).
The best (web) tools are those you can extend in a modular way with plugins. They play nicely with other tools, exporting and importing files in standard or commonly-used formats. People get passionate about them, because they let you exercise your craft.
Tools should talk JSON (or, if necessary, XML). While we're on the J-subject, JavaScript is spoken by many, and JavaScript on the server got hot a while ago - if you want to empower less-technical people, start supporting it.
Everything you create should have a URL and behave as a RESTful resource. Then people can do cool things with their resources you didn't expect.
Notifications over HTTP is the underpinning of workflow. The thinking behind Webhooks is very appealing - systems talking to little blobs of code on the web. This keeps things nice and open.
Open source used as a learning process: almost everything a person makes is not worth hiding and definitely worth sharing. In an environment where people are working with similar systems, open source will make people make better things (we don't have to be talking code - process, procedure, example are all good).
Finally, don't try to do everything. Not everyone needs to write their own process editor or form maker, nor do they need to invent their own cloud or firewall.
!!The companies
This does not include everyone who thinks they are doing PaaS (Google App Engine and Heroku are not on my list). My investigation was focused on developing applications with less technical skills.
!!!Generally convincing
TrackVia
BlankSlate
Zoho Creator
Rollbase
Iceberg
SnapLogic
Skelta
Wolf Frameworks
LongJump
WorkXpress
!!!Generally unconvincing
Caspio Bridge
wyaworks
QuickBase
Ragic
WaveMaker
BungeeConnect
ProcessMaker
MicrosoftDynamics
AppPad
Force.com
Google Scripts
DabbleDB
Tarpipe
Boomi
Informatica Cloud
Hubspan
IBM Cast Iron
ElasticApps
<!--{{{-->
<div class='header'>
<div class='headerShadow'>
<span class='siteTitle' refresh='content' tiddler='SiteTitle'></span>
<span class='siteSubtitle' refresh='content' tiddler='SiteSubtitle'></span>
</div>
<div class='headerForeground'>
<span class='siteTitle' refresh='content' tiddler='SiteTitle'></span>
<span class='siteSubtitle' refresh='content' tiddler='SiteSubtitle'></span>
</div>
</div>
<div id='mainMenu' refresh='content' tiddler='MainMenu'></div>
<div id='sidebar'>
<div id='sidebarOptions' refresh='content' tiddler='SideBarOptions'></div>
<div id='sidebarTabs' refresh='content' force='true' tiddler='SideBarTabs'></div>
</div>
<div id='displayArea'>
<div id='messageArea'></div>
<div id='tiddlerDisplay'></div>
</div>
<!--}}}-->
<!--{{{-->
<div class='header'>
<div class='headerShadow'>
<span class='siteTitle' refresh='content' tiddler='SiteTitle'></span>
<span class='siteSubtitle' refresh='content' tiddler='SiteSubtitle'></span>
</div>
<div class='headerForeground'>
<span class='siteTitle' refresh='content' tiddler='SiteTitle'></span>
<span class='siteSubtitle' refresh='content' tiddler='SiteSubtitle'></span>
</div>
</div>
<div id='mainMenu' refresh='content' tiddler='MainMenu'></div>
<div id='sidebar'>
<div id='sidebarOptions' refresh='content' tiddler='SideBarOptions'></div>
<div id='sidebarTabs' refresh='content' force='true' tiddler='SideBarTabs'></div>
</div>
<div id='displayArea'>
<div id='messageArea'></div>
<div id='tiddlerDisplay'></div>
</div>
<!--}}}-->
<!--{{{-->
<div id='displayArea'>
<div id='overlayMenu' refresh='content' tiddler='OverlayMenu'></div>
<div id='messageArea'></div>
<div id='tiddlerDisplay'></div>
</div>
<!--}}}-->
/***
|''Name:''|PasswordOptionPlugin|
|''Description:''|Extends TiddlyWiki options with non encrypted password option.|
|''Version:''|1.0.2|
|''Date:''|Apr 19, 2007|
|''Source:''|http://tiddlywiki.bidix.info/#PasswordOptionPlugin|
|''Author:''|BidiX (BidiX (at) bidix (dot) info)|
|''License:''|[[BSD open source license|http://tiddlywiki.bidix.info/#%5B%5BBSD%20open%20source%20license%5D%5D ]]|
|''~CoreVersion:''|2.2.0 (Beta 5)|
***/
//{{{
version.extensions.PasswordOptionPlugin = {
major: 1, minor: 0, revision: 2,
date: new Date("Apr 19, 2007"),
source: 'http://tiddlywiki.bidix.info/#PasswordOptionPlugin',
author: 'BidiX (BidiX (at) bidix (dot) info',
license: '[[BSD open source license|http://tiddlywiki.bidix.info/#%5B%5BBSD%20open%20source%20license%5D%5D]]',
coreVersion: '2.2.0 (Beta 5)'
};
config.macros.option.passwordCheckboxLabel = "Save this password on this computer";
config.macros.option.passwordInputType = "password"; // password | text
setStylesheet(".pasOptionInput {width: 11em;}\n","passwordInputTypeStyle");
merge(config.macros.option.types, {
'pas': {
elementType: "input",
valueField: "value",
eventName: "onkeyup",
className: "pasOptionInput",
typeValue: config.macros.option.passwordInputType,
create: function(place,type,opt,className,desc) {
// password field
config.macros.option.genericCreate(place,'pas',opt,className,desc);
// checkbox linked with this password "save this password on this computer"
config.macros.option.genericCreate(place,'chk','chk'+opt,className,desc);
// text savePasswordCheckboxLabel
place.appendChild(document.createTextNode(config.macros.option.passwordCheckboxLabel));
},
onChange: config.macros.option.genericOnChange
}
});
merge(config.optionHandlers['chk'], {
get: function(name) {
// is there an option linked with this chk ?
var opt = name.substr(3);
if (config.options[opt])
saveOptionCookie(opt);
return config.options[name] ? "true" : "false";
}
});
merge(config.optionHandlers, {
'pas': {
get: function(name) {
if (config.options["chk"+name]) {
return encodeCookie(config.options[name].toString());
} else {
return "";
}
},
set: function(name,value) {config.options[name] = decodeCookie(value);}
}
});
// need to reload options to load passwordOptions
loadOptionsCookie();
/*
if (!config.options['pasPassword'])
config.options['pasPassword'] = '';
merge(config.optionsDesc,{
pasPassword: "Test password"
});
*/
//}}}
Science-based use of TiddlyWiki.
Now [[ChatLoop]].
Paul and Stuart Berwick want help making an Adobe AIR widget for a Salesforce app.
Research - [[Adobe AIR for Web Standards peeps]]
http://www.evolus.vn/Pencil/
That's the combo of this site, the blog and anything else that's there to display what I'm all about.
Thinking of updating the credo on the blog. What I stand for now could be:
#Your website is your API (scraping)
#Anyone can build (wiring)
#Tech makes money (small creative business)
#Better coffee
!The problem
Hard to make repeating patterns for the backgrounds of websites. Want to "create repeating pixel-art patterns interactively".
!The approach
In-page editor and visualisation for creating pixel-level patterns. Uses canvas to render pattern, but DOM elements for editor. Makes use of "save as" capability of canvas.
Basics pretty much done. Published here so far: http://jnthnlstr.googlepages.com/index.html
!Challenges
# What's the IE "save as" solution (as well as render)?
!Possible/certain things to do
# Support click-and-drag to toggle multiple pixels - ''requested by @nickwebb and @jermolene''
# Support different sizes, not just 8px square
# Put source on github
# Blog about it - ''done'' - http://jaybyjayfresh.com/2009/04/01/pixelpatternmaster-create-repeating-pixel-art-patterns-interactively/
# Allow more than black in the color pallette - ''done''
# Add some explanatory copy
# Tweet about it
# Set up a UserVoice for ideas and feedback
Client: Ben @ Tager
Description: Ben uploads a track to the music blog. People who visit the post have a multiple-choice quiz to fill in, with answers (a), (b) or (c). Their results are stored. The results are aggregated onto a sliding scale that shows a score for the track, using the algorithm "the difference between C and A". This means that any B's count for 0, whereas A's push up the score and C's reduce it.
Hours: 9hrs 30mins
Rate: £200 / 8-hour day or £25 / hour
Invoice: [[Invoice 0002]]
!!Spikes:
# How's the form quiz going to be presented?
** The quiz and the data can live on the Internet even though the blog is on an Intranet
** The quiz and graph can be presented in an embedded iframe or div
** A Wordpress plugin could be used since the blog is running on Wordpress
# Where's the data going to be stored?
** Google Spreadsheets?
** TiddlyWeb?
# How is the sliding scale graph going to be constructed?
** There is a Google-o-meter that looks up to the job e.g. {{{http://chart.apis.google.com/chart?chs=225x125&cht=gom&chd=t:70&chl=Hello}}} generates <html><img src="http://chart.apis.google.com/chart?chs=225x125&cht=gom&chd=t:70&chl=Hello"/></html>
# What data needs to be stored?
** Track name, person's name, their answers to the six questions
# Would this be suitable for use with a Google Spreadsheet?
** I think it would... you'd end up with a bunch of rows for each track, and you could
# How do you get the data out?
** Found that you can use Google's Visualization API to query the data. e.g. {{{var url = "http://spreadsheets.google.com/tq?key=rq0QidU27Du-3niWD-btFqQ&pub=1&single=true&gid=1&output=csv&tq=SELECT%20E%2CF%2CH%20WHERE%20A%20%3D%20"+trackId;}}}
!! How this is an interesting architecture for a web application
Most interesting thing is figuring out how to use Google Spreadsheets to store and query the data. We also need to get a proxy set up. Entirely written client-side, making use of existing Google services to store/query/display data.
Getting data out of a Google Spreadsheet:
{{{
var url = "http://spreadsheets.google.com/tq?key=rq0QidU27Du-3niWD-btFqQ&pub=1&single=true&gid=1&output=csv&tq=SELECT%20E%2CF%2CH%20WHERE%20A%20%3D%20"+trackId;
}}}
The app is created to work embedded in a Wordpress blog, inside an iframe, where the query string of the 'src' attribute of the iframe is checked for the value that becomes the primary key in the Google Spreadsheet.
This is manually updated, so not perhaps the most up-to-date illustration of what I want to label as a 'portfolio piece'.
[[BPSF]]
[[wiki-data]]
[[How to build your own TwitPic]] - a guide to creating a complex system without writing code
[[Streams interface for LShift]] - prototyping of single-page Ajax application including adaption of WireIt (an open-source, JavaScript, interface library)
[[Twitter @replies]] - uses Yahoo! Pipes and Notify.me to send you emails of Twitter @replies
[[Geekmap]] - put on hold, but good example of agile, startup, interface design and build
[[whoscelebratingtoday.com]] - example using DabbleDB to present information about public holidays around the world
[[ILGA]] - agile, complex systems design and build
[[EasyExpenses]] - complex screen-scraping
[[Polling app for Nokia music blog]] - building a system without server-side coding (and minimal client-side)
What defines a "project" is something that you and others can do - stuff you push out. They engender the effects desired in [[Wants]]. Projects are different to [[Habits]], which generally recur each week.
Also see [[Portfolio pieces]].
-----------------
This is effectively ''a snapshot at June/July 2009'', as since then I have found that the [[Top Wants, Goals and Projects]] and the [[Weekly reviews]] gives a more accurate picture of what I'm actually doing.
!Active
[[Finishing things off]]
[[ILGA]]
[[Anna Freud Centre]]
[[Make cooking once a week a habit]]
[[Starting yoga]]
!Candidates for being active
[[iPhone translation app]] - decide on an app (or apps), make it, release it under yellowcar; 3 people, hours a week; longtime, 100 hours; money
[[PixelPatternMaster]] - couple of features, wrap us as a tool, figure out how it can save in IE; 10 hours; coolness
[[TiddlyCity]] - re-address use-case to find a solid use-case, rebuild, set up on web for multi-user hosted, find magazine to use it; 30 hours; coolness, money
[[Notion Twitter/SMS]] - get agreement on paying me, build; 10 hours; money
[[Google Tasks API]] - write lib, tests and docs; 10 hours; coolness, utility
[[TextTasks]] - figure out how to do it, build, write about it; 15 hours; coolness, utility
[[ScreenScraping HSBC website]] - make work as function calls, investigate legalities (lawyer help?), make API, make work as embeddable payment method, find someone to use it, do more than HSBC; 15 hours; coolness/utility
[[Oyster Card payments]] - make work with [[ScreenScraping HSBC website]], find people who'd use it; longtime, 30 hours; money, coolness, REVOLUTION
[[Productively]] - write more about methods, start to automate, make hosted, get to the stage where I understand the separate bits and can write about them as a system; 25 hours; utility
[[Blessed Blend]] - set up Twitter orders, test the feasibility of web site ideas like ordering equipment and training; 10 hours; coolness, money
[[CoffeeSnob]] - figure out what I really want to do with this idea - is it a website or a movement? how can I make a difference to coffee in London?, make something and show it to people, talk to people; 15 hours; coolness, REVOLUTION
[[Paul Bacchus]] - figure out some awesome prototype use-cases, build; 15 hours; money
[[Personal site]] - portfolio stuff, what I stand for; 10 hours; coolness, money
[[Build your own TwitPic]] - get it working, write about it; 5 hours; coolness
[[TweetCheck]] - figure out how to make it work, make it, make it work multi-user hosted, write about it; 50 hours; money, coolness, utility
[[TiddlyInvoicing]] - figure out how to make it work on IE, do it; 5 hours; coolness, utility
[[dm2me (Twitter bot)]] - make it, use it in a few things (such as TextTasks), make it multi-user; 10 hours; coolness
[[TiddlyTweets]] - finish remaking for search use-case, consistently brand, write about it; 10 hours; coolness, utility
[[jsAutomatic]] - make an example (such as [[Google Tasks API]] or [[ScreenScraping HSBC website]], release on github (DONE), write about it; 5 hours; coolness, utility
!Not active
[[Training for Great North Run]] - should be active!
[[Using Facebook more to keep in touch with people]] - should be active
[[Contributively]] - on hold as a tool until mechanism better decided upon
[[Geekmap]] - put on hold
!Ideas
[[TiddlySMS]]
[[WikiCSS]] - wiki-access to your CSS files
[[Twargame]]
[[Improving salsa dancing]]
[[Getting good at the guitar again]]
[[Reading more fiction]]
[[Open TShrrts store]]
[[DOM Object Mayhem]]
[[WebDev tool for teams]]
[[Bansko Chalet Soltir Website help]]
[[Twitter distance learning tool for UnaMesa]]
[[RelevantTickets]] - on hold until Songkick release RSS feeds
[[OpenCSS]]
[[FOIRequestsUK]]
[[Richard Mountain's photo shop]]
[[Data momentum visualisation from this TiddlyWiki]]
[[Making switching between Cecily and TiddlyWiki in a similar style to the ThemeTogglerPlugin]]
[[Adding DefaultTiddlers to a theme and making those display when you switch theme (maybe control that by a parameter)]]
!Done
[[Polling app for Nokia music blog]]
[[Attitude Audit Tool]] - did the prototype
[[BonnieParsons.com]] - done v1
[[Analyse current spending]]
[[Habitualize making time to spend with Bonnie]]
[[Habitualize Bonnie's dance class]] - made it a habit, once a week; now actually a Pilates class
[[Habitualize drawing cartoons]]
[[Habitualize making time to hang out with friends]]
[[Twitter @replies]] - uses Yahoo! Pipes and Notify.me to send you emails of Twitter @replies
[[Flock O Tweets]] - wrote a web service (in AppJet) to combine RSS items for use with Yahoo! Pipes
[[Habitualize gym training]] - made it a habit, 2 or 3 times a week
[[Email to HTTP]] - infrastructure to POST emails to a URL - should write about it; sort of have
[[whoscelebratingtoday.com]] - example using DabbleDB to present information about public holidays around the world
[[ToggleThemePlugin]] - TiddlyWiki plugin that provides a button to switch between two themes
[[Nokia
http://productplanner.com
There's a growing feeling that I'll try to make something useful out of my system of personal productivity and group productivity. I heard about www.asana.com - looks very interesting.
I've been extracting some stories from real projects - [[My stories of the wiki-data and BPSF projects]].
--------
This Workbook is my experiment to devise a tool to complement a system (also in devision) that works for me to document and plan my thinkings and doings.
Top priorities are tracked week-by-week in [[Top Wants, Goals and Projects]].
I'm also writing weekly reviews about how I'm getting on: [[Weekly Reviews]]. I think these provide the best way of tracking how I'm getting on with things.
I refreshed my [[Wants]] at the end of September, to give me longer-term focal points for each week's plan - [[Wants refresh 28th September 2009]]
Before the weekly reviews, I wrote a series of tiddlers describing a developing idea about how to organise yourself so you are able to focus on the things most important to you (and recognise what they are).
[[Productively, part 3]]
[[Productively, part 2]]
[[Productively, part 1]]
[[Organisation]]
The process of organising yourself (and others). I've come to realise that I'm the sort of person who always wants to be doing something new and improving things, and pays less attention to things that are already there. I think it is important to achieve a balance between appreciating what others have gone to pains to create and creating your own things.
I wrote quite a bit yesterday (14th April) in [[Organisation]] about how I've been using Google Tasks to list out what I want to do and how that has allowed me to take on more stuff.
Today, I've been trying to better understand the difference between what you [[want|Wants]] and what you [[do|Practical Projects]]. I've consolidated my prioritised goals (now [[Wants]]) list and assigned each [[project|Practical Projects]] to each want.
I think the process I've been using to sort out what I do went a bit like this:
# Written down wants
# Written down projects
# Prioritised wants
# Mapped projects to wants
# Realised that some wants as written are actually projects, and consolidated wants list by making them projects
After this last step, I've gone from 8 wants down to 5. Although I have billions of projects. I think I should figure out which projects depend on other projects, or can be helped along by other projects. I should probably also check that if I only did "want 1" projects, I wouldn't be missing out on other projects I really want to do. Perhaps I could figure out my gut feeling about each want - like how much of my life I want to devote to each one. There should probably also be something about prioritising the projects, as there are too many to tackle them all at once.
This is an ongoing series of tiddlers describing how I'm figuring out my priorities and what I should be devoting my time to. The previous tiddler in the series is [[Productively, part 1]].
!Progress
Having reduced my [[Wants]] list from 8 to 5 and attached projects to each, this still didn't give me a clear sense of what I should be doing with my time. Thinking about it some more, I came to the conclusion that my first want, which was phrased as "[[I want to actively appreciate what I've got in life and what others have created]]", was indeed my highest priority at the moment, but was really a statement about sorting out my priorities... Looking at the other 4 wants, it occurred to me that they were all really statements arising from a desire to become an independent of my employer. This suggested that my second-priority want should be written as "I want to be able to go freelance". So, now I'm down to just 2 wants, which seems like a number I could cope with focussing on.
I had this idea for how to add something concrete to these wants, from which I've created [[Top Wants, Goals and Projects]]: at the start of each week, I list the top 3 goals that would move me towards achieving each want and I see whether the goals are small things that can just be DONE or whether they imply some sort of project. If the latter is true, then I see what other projects I have in the bucket that could contribute to the goal, perhaps by changing them a bit. I'll have to see how this works out... This should become a habit; I'm thinking about listing my [[Habits]] so that they can be different to [[Projects]] and not take up space there - however, when I come to figure out how much time to give things, I'll have to take habits into account.
I wrote down my current [[Habits]] and found, to my surprise, that I have already accounted for 147 hours in a week, only leaving 21 for other things! I'm quite looking forward to refining this list as I get a better understanding of how much time I spend on things.
Other things:
# decided not to do the project-dependency thing. I had a go but it seems like things aren't so clear-cut that you can put them in hierarchies; plus I've learnt to be wary of hierarchies. Instead, I've picked just the top project that could help with each goal and written how big I think they are, roughly - it's a start to sizing
# feeling like choosing top 3 goals for a want is a bit arbitrary, but it will allow me to experiment with sizing of things and figuring out how much time you have and what you want to fill it up with
# on the subject of sizing, projects are going to have different time demands over time - for example, early stage research may take a lot of time, whereas keeping something ticking over (presumably as a habit) might not
Previous tiddler in this series: [[Productively, part 2]]
!Progress
I'm happy now that I have a set of top priority things that is small enough to focus on. This is all well and good, but leaves two holes to plug: firstly, what's happened to all the things that I'm effectively dropping for the time-being, along with the other people involved in them; secondly, how do I decide how much time to put into each item or measure what I do put in?
For the first problem - essentially how to let people down gently - I think the thing to do is not shy away from the fact that I need to let people know what I'm focussing on and that involves figuring out who they are. So, I should list the projects that involve other people and then tell them what's going on.
For the second problem, I've already done a rough estimate of how long each top priority thing would take and have an idea of how many hours I have available (20-ish!) after taking habits out. Looking down the [[Top Wants, Goals and Projects]], I have allocated 14 hours to the top priority set, which leaves about 6 for the second set. I'd like to cover all 3 in the set, which is a bit of a conflict...
see [[Practical Projects]]
attn tracked with [[pjj]]
"Keeping your work yours". http://protected.cc
Renamed to ProtectedCC
!!Log
There were two meetings at Lina's flat and one at the Cube.
Two hours with Sherene on 29th Nov, discussing content for the closed beta
[[7th December ProtectedCC]] - 5hrs 40mins
[[8th December ProtectedCC]] - 1hr 45mins
[[25th January 2010 ProtectedCC]]
Out there
#~TVBomb - [[Black Kids + Foals @ Koko gig review at the iTunes Festival, 7th July 2008|http://www.tvbomb.co.uk/music/black-kids-foals-koko-7th-july-08.html]]
Blogs
# [[JayByJayFresh.com|http://www.jaybyjayfresh.com]] - writing about early-stage technology, open source and drawing cartoons
# [[Geekmap Blog|http://blog.geekmap.co.uk]] - writing about open business
Planning
#Piece on zero-packing for Nihilista
LShift pitch. Attn tracked with [[radio1]].
<<attentionTracker timeline radio1>>
A service to find people really interesting places to stay. Introduced to Emma through my Dad in June 2010.
http://rareretreats.com
Looked into [[Using Google Checkout or PayPal to send invoices]]
Includes [[Reading more fiction]]
Type the text for 'Reading the Economist'
See [[Habitual vs. Non-habitual time spent]] and anything referencing it.
------
Was going to do this as a tiddler, but decided for a while to try doing it using Staction and then pulling an RSS feed from there:
http://jnthnlstr.staction.com/feed/1805/0/02a48d73f5fafb2165ea78af947905538ea01227/
!The problem
@nickwebb and @philhawksworth set up twittertickets.com a while back to send twitter updates whenever seetickets.co.uk updates for a city. This feed has too much traffic to be relevant (and doesn't have enough bands).
!The approach
Want to use last.fm to figure out what you'd like given what you're into and then filter the new tickets to give you personally interesting suggestions of gigs.
Have thought about doing a paid SMS service. Since then, found this press release about ticketmaster.co.uk releasing SMS updates: http://www.prnewswire.co.uk/cgi/news/release?id=30125
I wanna send a list of band names, have them tokenized, go get similar artists, combine the lists, make it unique, use that list to filter the feed of new tickets. If any get through this filter, I want to know about them.
!Progress
I had a Dapp that scrapes TicketMaster.co.uk to get at the most recent hot tickets. Of course, it's not that simple, as Dapp can't differentiate between tickets that are on sale and those that are upcoming. And it kept breaking. I persuaded Yahoo! Pipes to do some screen-scraping and ended up with this: http://pipes.yahoo.com/jayfresh/ticketmasterhottickets
Sadly, this just isn't good enough for two reasons - there are no dates attached to the gigs, and this is only hot tickets.
This is a potential source: http://www.londonlivemusicguide.com/ - have written to them to ask advice about finding a list of new gigs. Also written to http://www.songkick.com/.
Getting similar bands to a band you like through last.fm can be gleaned from pages like this: http://www.last.fm/music/Calexico/+similar
Annoyingly, listings seem to come in date order of the gig, rather than showing the most recently available tickets. Seetickets.com's feeds.seetickets.com was down when I checked...
All the ticket scanning websites must be able to present some sort of feed of new tickets, because that's what their incoming data is...
I've been very remiss in not keeping up the weekly reviews for the last three weeks. I've been focussing mainly on the [[Avox Wiki-data]] project, which has reached the end of the timetabled Phase 1, and we are tying up some loose ends, before launching before the end of September (we hope).
It's been interesting to see what happened to how I felt about my work when I didn't have the routine of the weekly plan and weekly review. I definitely felt like I'd like to have it back as a framework to measure myself by. I have felt more anxious about whether I've been doing the right sorts of things, which has been aggravated if not caused by not having a target for the week to aim for. It seems a bit bureaucratic to have these targets and metrics, but really the space I think they are filling is some sort of peer review of your work; or rather, they act as a virtual, informal self-review, which I think is healthier, as you are not reacting to someone else's aspirations for you, you are shaping your own.
This week, I hope to work efficiently through the last parts of wiki-data Phase 1, as well as getting some work done on [[Ripplz]] and [[Visualising finances for Tager]]. I have a paid day booked with the [[Anna Freud Centre]] on Wednesday.
-----
To 31 hours - see [[Habits]]
These are somewhat hard to reconcile now, since this review is over a 3-week period. I've added in as Bonus Points the extra things I've done.
![[I want to make people happy]]
#Crack over half the user stories for the [[Avox Wiki-data]] project - 20 hours - DONE
#[[Dad's IT problems]] - finish off the Owners' Direct advert - 1 hour - NOT DONE
#[[Migrating from Macbook Pro to Powerbook G4]] - 6 hours - DONE
![[I want to develop my portfolio into something that is strong enough to get work from]]
#[[ScreenScraping HSBC website]] - make it work for business banking - 4 hours - NOT DONE
** Changed purpose of day with Tager slightly, see [[Visualising finances for Tager]]
!Bonus points
#[[Avox Wiki-data]] - finished most of the Phase 1 user stories
#[[Avox Wiki-data]] - presented the concept at BarcampBrighton4 and sought feedback
** The overwhelming response was "please open up all the data fields"
#[[ScreenScraping HSBC website]] - presented live demo at BarcampBrighton4
** Build log at [[Cloning TwitPic @ BarcampBrighton4]]
#[[Baselining my life]] - posted part 1, about money
#[[Events]] - went to BarcampBrighton4, saw lots of good talks, met some good people, loved the Prescription art gallery, slept on the floor
This fortnight was a bit of a collection of different things, and I ended up doing a lot of unexpected things (see bonus points below). The main event was Chris' wedding, which was great and made me realise that I ought to see Chris more often as I have scarcely met his wife. The other big event was the first birthday of yellowcar ltd, which made me rather reflective about what I ought to be doing with the whole thing, now I've proved myself I can make money running a web agency.
This caused me to write [[One Year In: The Market As A Constraint And What To Do About It]], the broad conclusion of which was that I should be selecting clients rather than responding to random requests for work, and that I should do something about making something to sell. The means for achieving the credibility to do the first is to get some attractive web presence up, where attractive applies to the people I want to work with and the clients I wish to have. For the second point, I think just getting my finger out and trying to make something is an approrpriate path.
In this "making" vein, I've been looking at reinventing the way time is tracked, so that the vocabulary is changed to be about attention. This is being written about in [[An attention tracking system]].
I think in the next fortnight, I want to get a good idea of how I'm going to respond to these ideas about being proactive with what I'm doing. I don't want to continue in the mold of randomly contracting, so I hope to have something more to say about that later.
-----
Fortnight beginning 28th June 2010, up to 62 hours (see [[Habits]]). Seeded from [[Wants]].
* [[enjoy life, good things, food, music, people, friends]]
** [[SF trip]] - book flights - 2 hours - DONE (1 hr)
** Book flights to Pisa for August - 1 hour - DONE (1hr)
** Pick up usher kit for Chris' wedding - 1 hour - DONE (1hr)
** Be usher at Chris' wedding weekend - 10 hours - DONE (10 hours)
** See Chris' improv show in Oxford on 10th July - 7 hours - DONE (7 hours)
** [[BonnieParsons.com]] - get on proper hosting, fix the small bugs - 1 hour - DONE (1 hr 5mins)
** [[SuperFreakonomics]] - read - 5 hours - DONE (5 hrs)
** RareRetreats - send Emma information about Google Checkout and Paypal, for use charging custom amounts - 1 hour - DONE (30mins)
** [[wiki-data]] - Wednesday night celebration event after they won their recent award - 2 hours - NOT DONE
*** turned out to be on Thursday and I was in Birmingham that evening
** [[DRAMA]] - let the guys who quoted for the project know we haven't got it - 15 mins - DONE (10 mins)
** [[Anna Freud Centre]] - help Dickon get his standalone version of the AIM working - 1 hr - DONE (25mins)
* [[electronomads, promote lifestyle, individual importance, products, projects, creation]]
** [[Alison Coward]] - Speak on (second) Wednesday about collaboration and how an open agency could grow big - 1 hour - DONE (1 hour)
*** concluded that we should co-write some online statement about open agencies, as a way to have something to point to, that has more than one person signed up to it
* [[products, cashflow, stakes, consumers, creation, make people powerful]]
** [[Boutique Barista]] - send a group email around about what we do next (Caro offered to help) - 3 hours - HALF-DONE (3 hours)
*** decided to send individual email; had to collect four people's email addresses, ended up with a few more; Caro helped make the email good
** [[wiki-data]] - check in with Adam attend to banner change ticket and any others - 1 hour - DONE (45 mins)
** [[de Candole]] - meet [[Gary Inwood]]; make static transition page, flashy intro and help Josh get quote together - 2 hours - DONE (2hrs 55mins)
* [[system, web projects, streamline, software, tools, different people]]
** SweetSpot - talk to [[Chris Dent]] and [[Ket Lai]] about doing the SweetSpot backend - 2 hours - DONE (2hrs)
*** spoke to [[Chris Dent]], [[Ket Lai]], [[Ben Griffiths]] and Tom Ward from [[freerange]] - all generally busy until mid-August or September. Spoke to Nick about this, we decided that the project doesn't need to get done until mid-end October. I'm still aiming to have a quote ready by the end of this month
** SweetSpot - get the project tracker's wants section up-to-date - 1 hour - NOT DONE
*** didn't make the time
!Bonus points
* [[One Year In: The Market As A Constraint And What To Do About It]]
* [[An attention tracking system]] - studying the problem and design, (about) 6hrs 10mins
* [[Sheraz Arif]] - 1hr 5mins
* [[Using Google Checkout or PayPal to send invoices]]
* [[Employee effect on the world's biggest companies]]
The reason this is a fortnightly review is that I went on holiday for a week from the 7th July to the 14th, so I only had 1 day in the second week for non-holiday things and didn't get around to doing a weekly review before I went away. This review will concentrate on the week before I went away.
This week, I failed to complete quite a lot of what I had set myself. I am not entirely sure why, but I think it was probably because I spent a lot of time going to see shows and the like.
!Top Wants, Goals and Projects for fortnight beginning 29th June 2009
To 31 hours - see [[Habits]]
!![[I want to not have to worry about paying the rent]]
#[[Make people happy]] - impress the LShift guys with the [[prototype build|Streams interface for LShift]] and project estimation this week, so they take me on for the next two 3-week development cycles - 21 hours - DONE
** Also wrote [[a blog post|http://jaybyjayfresh.com/2009/07/06/adapting-wireit-to-work-with-independent-data-stores/]] about using WireIt, which went down well with the WireIt guys and certainly [[provoked|http://groups.google.com/group/wireit/msg/996e87b33cf8aeeb]] a change to the WireIt trunk where SMD and YUI-RPC has been replaced with adaptors
** Another post to the WireIt group [[prompted|http://groups.google.com/group/wireit/msg/f91192f4fcfe4bde]] them to include the Tarpipe-style containers in the trunk
#[[Setting up freelance contracts]] - figure out how long in the future the funding is likely to be coming from Dickon, by the end of the week - 1 hour - NOT DONE
#[[Setting up freelance contracts]] - get in touch with Paul Bacchus and agree with him which the best 3 use cases for the prototype would be, by the end of the week - 2 hours - NOT DONE
!![[I want to develop my portfolio into something that is strong enough to get work from]]
#[[Finishing things off]] - order the list I made in [[Practical Projects]] and do something significant to the top priority, by the end of the weekend - 5 hours - NOT DONE
** I was expecting to work on the [[DIYTwitpic]] idea, but I was blocked by jgate.de being non-responsive, meaning my method for making Twitter bots didn't work. I ended up writing [[a blog post|http://jaybyjayfresh.com/2009/07/06/joyent-smart-platform-a-replacement-for-appjet/]] about the Joyent Smart Platform, which was received well by the Joyent guys
#[[Productively]] - tie-up the loose ends with the time tracking and cash tracking, by the end of the week - 2 hour - DONE (but outside this time period)
!!Bonus points
#Invoices - sorted out an invoice template and sent out invoices 0001, 0002 and 0003.
#Spent Saturday with the family - took Joshua for his haircut at Electric Blue, went to the Imperial War Museum and babysat on Saturday night
#Saw a lot of live music with Bonnie - Cabaret on Thursday night, Blur on Friday night and Wireless on Sunday
#Went on holiday!
!!Thinking about [[Productively]]
I am considering adding events that I know about in advance to a week's plan, so I can lower the available hours.
This fortnight ended up mainly being about SweetSpot, as we prepared a big pack of mockups for presentation to the SweetSpot board. It all went down very well, and Andy decided to pay us for the speculative design, which we're very happy about. It looks like we will be formally signing up with PropMan soon, so I imagine that will take up a chunk in the next fortnight, along with getting the pricing/effort sorted for the core online experience.
The rest of this fortnight has been taken up with some smaller items and thinking about what's coming up next. I'm really trying to get some momentum going with the [[Boutique Barista]] (BB) project, but it's been tricky to fit it in. Apart from that project, I am in a position where I don't have anything big lined up in the near future, with the exception of SweetSpot. Unless SweetSpot is going to earn enough to focus solely on that, which I don't think it is, I have the opportunity to think about where to shine the searchlight, as it were. Is BB going to get me anywhere? Is this the right time to get started with [[Electronomad]] again? And having got very close to the end of my first year, what are the right things to be thinking about now?
I'm spending half the next fortnight in France, which will be lovely I hope. I'm reading a very interesting book called [[The Goal]], which is all about how America's factories changed their manufacturing processes after the Japanese started to beat them on both price and quality. It's firstly about the idea that companies are there solely to make money, so your measurements of company success should be serve that. Then it's about re-engineering processes using the idea that the constraints on your system limit your total capacity, so you should figure out what these are and improve them, but not expect your system as a whole to move faster than the slowest bit. Constraints can be anything from a slow machine in a factory to a lack of market demand. I've been thinking about how this applies to my company. I think my main constraint is Josh, because I want him to do everything design-y, and that's not practical.
-----
From Monday 1st June, up to 62 hours (see [[Habits]]). Seeded from [[Wants]].
*[[enjoy life, good things, food, music, people, friends]]
** Holiday in Spain and a day in the Sun (18 hours) - DONE (18 hours)
** [[BPSF]] - get IE7 bug fixed (1 hour) - HALF DONE (1 hour)
*** Catherine is still working on it; I made an improvement, which reduces the frequency of the problem
** [[wiki-data]] - tie-up the loose ends of the phase, which I think were mainly finished in my absence (1 hour) - HALF DONE (40 minutes)
*** there are some outstanding tickets
*[[system, web projects, streamline, software, tools, different people]]
** SweetSoft - quote for the core online experience (6 hours) - HALF DONE (15hrs 10mins)
*** this expanded a bit... we've done lots of mockups, which we think are necessary before we can get a quote from a supplier to build the back-end
** [[Scheduler 4]] - interface design process and costing (4 hours) - DONE (3hrs 35mins)
*** the project is now waiting for some more detail from LShift before it can be approved
*[[electronomads, promote lifestyle, individual importance, products, projects, creation]]
**[[DRAMA]] - get the quote sorted (5 hours) - DONE (3hrs 15mins)
*** got quotes off DoTank and Nitobi; presented to Dom; have been undercut, so we're not getting the job
**RewireLondon - attend and speak about something interesting (4 hours) - DONE (4hrs)
*** didn't run a session, but had many interesting conversations
*[[products, cashflow, stakes, consumers, creation, make people powerful]]
** [[Rewired State Home Office Hack Day]] - follow-up meeting on Thursday 3rd (3 hours) - DONE (3 hours)
** [[Rewired State]] - police data conversation, courtesy of Kabi (2 hours) - NOT DONE
*** rescheduled until I get from France, so last week of June
** [[Boutique Barista]] - get the brief designed nicely and laid out (2 hours) - NOT DONE
*** didn't make the time
** [[Boutique Barista]] - circulate the idea and the brief to a handful of baristas (4 hours) - NOT DONE
*** didn't make the time
** [[Jeroboams]] - get the shop list feature done (2 hours) - NOT DONE
*** didn't make the time
!!Bonus points
[[The Goal]] - started and read about half of this
I ended up not making a new plan for the second week mainly because it seemed obvious what I was supposed to do: [[wiki-data]] needed to be finished in time for Ken's presentation to Citigroup on the Thursday (which it was) and BPSF needed pushing along. I had a day at the [[Anna Freud Centre]] on the Friday booked in too.
Josh made a good mockup of the [[Grace coffee]] website at the end of the first week, so we're waiting for feedback from Phil before we build it. This means I'm now going to turn my website-building attention to BonnieParsons.com and [[Violet Hill]].
I am still trying to meet more people who I can work on projects with, and develop my ideas about how I am going to contribute to giving a name to recognising that we all have similar needs and should work together. That sentence is meant to apply to the web industry. I've set up a couple of meetings this week to help with that - LondonCreative and WeAreSuburb.
Also, I'm not sure my [[Wants]] list is totally valid anymore - I should review how I've done with them and see if I want to set some new priorities.
-----
Week beginning 1st February 2010, to 31 hours (see [[Habits]]). Seeded from [[Wants]].
* [[I want to invest in startup businesses or art projects]] - 4 months (January)
** [[wiki-data]] - complete the phase 3 tickets (20 hours)
** [[BonnieParsons.com]] - get started on the new site (2 hrs)
** [[ProtectedCC]] - start content refresh (2 hrs)
** [[BPSF]] - spend a day on the new cycle (5 hrs)
!Events
Meet [[Richard Marr]] with Jeremy on Thursday night to discuss freelance work
Lunch with [[Alex O'Byrne]] on Tuesday to discuss freelance work
RCA interim show
R&R housewarming on Saturday
!Bonus points
DesignView - experiment
[[Grace coffee]] site with Josh
[[Using DropBox as a collaborative task tracker]]
Day at [[Anna Freud Centre]] - 8 hours
I'm writing this on the Sunday at the end of the fortnight, as I didn't get around during the second week to doing the review and plan. In a way, things were pretty clear for the second week, as it was all about launching [[wiki-data]] on the Tuesday and doing the follow-up on the Thursday. I took the Wednesday day-time, Friday and the weekend off as holiday.
The launch went fine - we presented to the circa 200 audience at FIMA and everyone that gave feedback seemed to be giving supportive feedback. I don't think there was much feedback that was suggestive of ways to build on what we've done, so I guess we have to keep exploring what people will find useful. The [[client workshop|12th November wiki-data workshop]] was telling in that the client was only excited by one aspect of the proposed "my wiki-data" site and also excited about the prospect of her IT team working directly with the API.
I'm celebrating going out with Bonnie for a year this (second) weekend, so taking holiday from Friday until Tuesday. I'm hoping that the down-time will also help me get back into writing, which I did do a bit of during the period after the wiki-data launch.
I helped out on some [[Anna Freud Centre]] problems - feels like that project is on the cusp of getting somewhere big.
Other interesting things: Osmosoft (FND, Mahemoff, JDLR and Ben) are all thinking about client/server templating and page publishing, so I've been talking about what had been achieved with TiddlyTemplating. A core idea is to replace the Jinja2 templating framework people have been using on TiddlyWeb with wikification - looks to me like TT on the server. This would be good as then there can be little or no difference between client and server templates; and they'd be stored in tiddlers, which would be good for sharing.
Went to see Avenue Q on the 14th - absolutely amazing.
I also re-branded (sort of) - I haven't been identifying with 'yellowcar', so I'm leaving that as the holding company name and am going to develop the identity of 'electronomad' - I bought electronomad.com and a bunch of other domains.
-----
For week from 2nd November - 8th November 2009
No more than 31 hours (see [[Habits]])
* [[I want to invest in startup businesses or art projects]] - 4 months (January)
** [[Avox Wiki-data phase 1]] - make a mashup - 6 hours - DONE (about 7hrs 30mins)
*** going to Newbury to spend the day with Chris on Friday
** [[Avox Wiki-data phase 1]] - deal with any bugs - 6 hours - NOT DONE
*** Did the API documentation instead and deferred bugs until the next week
** Spend the day with Prem on Monday - 6 hours - DONE (6 hours)
** Get the [[ChatLoop]] widget working - 6 hours - HALF-DONE (5 hours 10 mins)
*** does enough to show, but needs a bit more work to finish off a user story
* [[I want my business to be so efficiently run that it takes me no time to administer]] - 2 months (November)
** [[Automating company workflows]] - get an automated accounts sheet going so I can see what the cashflow looks like - 2 hours - DONE (2 hours)
*** See [[yellowcar draft accounts|http://spreadsheets.google.com/pub?key=t7O4rMq-GCnfGkDwkVxZgDQ&output=html]]
* [[I want to have a surplus of cash of a few hundred pounds a month in my personal account]] - 6 months (March)
** Start the regular £2k dividends - 1 hour - DONE
* [[I want my business to be generating a few thousand a month in revenue that doesn't require any upkeep]] - 6 months (March)
** Discuss credit-note model at [[ChatLoop]] meeting on Thursday - 1 hour - NOT DONE
!Events
* Celebrate with Nick on Tuesday (brunch and party) - DONE
* Bonfire night celebrations - Battersea Park followed by East End art - NOT DONE (went to Clapham Common instead)
!Bonus points (including second week)
Blog post - [[Maemo Summit 2009, Copenhagen - a bit of an open-source eye-opener]]
[[Why booking time works for me]] (draft blog post)
This fortnight, from a web point of view, has been mostly about getting the SweetSoft bookings system working, which it does now. I've learnt a great deal about server-side JavaScript ([[SSJS]]), and the system is running on Smart platform. I have a reusable component for talking to Google Calendar from SSJS, which is cool as Google haven't published a client-library for server-side JavaScript.
I'm planning to get the SweetSoft bookings system launched on Monday, and we have a meeting with PropMan on Monday morning to see if we can work out some of the difficult-looking areas of integrating their product with an online experience. We've postponed the decision on supplier until Friday to give CFP a chance to respond, since they seem a lot cheaper than PropMan, but have a solid feature set.
I've had a few potential job appear, that need some time spending on them in this next fortnight; I also want to make good on my intention to speak to baristas and see what they think of [[Boutique Barista]]. I'm beginning to think that my trip to America in September could be used a way of exploring the café culture in Silicon Valley, meeting the café owners and coffee-lovers and seeing if I could do an American arm of Boutique Barista too. Whilst also finding out what I should expect from a café that supports Electronomads, assuming Silicon Valley has the best examples.
On the subject of Electronomad, I think it's not a bad time to collect the information about the cafés I'm targetting, given the list of criteria I have in my head: coffee, wi-fi, power, protocols allowed, opening days...
-----
From Monday 3rd May, up to 62 hours (see [[Habits]]). Seeded from [[Wants]].
*[[enjoy life, good things, food, music, people, friends]]
**Bank holiday - 4hrs 30mins - DONE (4hrs 30mins)
**[[wiki-data]] - Respond to Adam's request for a quote - 1 hour - DONE (1 hr)
**[[Violet Hill]] - Speak to Daniel and put Amit and Daniel in touch - 30 mins - NOT DONE (30 mins)
*** have confirmed to both that Amit is going to do the job; need to send email out
**[[BPSF]] - fix the bugs that are mine to fix - 5 hours - NOT DONE (1 hr)
*** had a look at the IE7 problems, found it is a pain - thinking about outsourcing (as an outsourcing experiment as much as anything)
**[[Grace]] - chase up Ray to get Grace's webmail on mail.gracestpauls.com - NOT DONE
*** Phil also asked me to change the Twitter address to @gracecreedlane and change email address to grace@gracestpauls.com
**[[NCP]] - chase up Andy again to get his shortlist and event contact for Ben - DONE
*** still no response, thinking of giving this up as a bad bet. Could probably find the events contact through a different source.
*[[products, cashflow, stakes, consumers, creation, make people powerful]]
**Complete the server-side component for SweetSoft - 25 hours - DONE (50 hours)
*** took a *little* longer than anticipated; happy with it, wrote lots of tests
**Create the supplier recommendation for SweetSoft - 2 hours - NOT DONE
*** postponed until next Friday
*[[electronomads, promote lifestyle, individual importance, products, projects, creation]]
**[[Boutique Barista]] - speak to five coffee shop owners about which products they could put in the store - 6 hours - NOT DONE
*** tried to speak to two, but they were out; will make more of an effort
*[[system, web projects, streamline, software, tools, different people]]
**[[SSJS]] - keep tabs on the Env.js/Smart discussion and see if jsdom will run on Smart Platform - 3 hours - HALF-DONE
*** run into a dead-end with Env.js until someone makes the Perl connections; haven't tried out jsdom yet. Ben G pointed out that a goal for DOM-on-the-server could be getting a TiddlyWiki rendered.
!!Bonus points
*[[JavaScript in the cloud]]
*[[SSJS]]
*[[Boutique Barista]]
This fortnight was my first back after America, and I didn't make my plan until the end of the first week. As it happened, the second week saw the launch of the What On Earth site, and some brainstorming by J&J about the nature of J&J, which was cool. The rest of the second week was a bit of a hodge-podge of miscellany - not the most productive week, and marred by an ongoing feeling of lethargy, which might have something to do with jetlag.
Aspiring to a more productive fortnight this time, I'm writing this on Monday morning of the first week. The main things to focus on at the moment are SweetSpot Phase VIII and Kathryn McMann's site build. We have the What On Earth party on Wednesday, which Chris is throwing to celebrate a 8ft x 24ft wallbook being erected by City Hall - pretty cool. A piece was supposed to go in Saturday's Daily Mail, but was pulled at the last minute, which is a shame not least from our point of view as it would have carried the website address.
------
Fortnight beginning 4th October 2010. Up to 62 hours (see [[Habits]]). Seeded from [[Wants]].
* [[enjoy life, good things, food, music, people, friends]]
** re-establish contact with my family - HALF-DONE
*** still haven't spoken to Grandma!
** presents for Joshua and Grandma - NOT DONE
*** plan is to get them this week
** read [[Decoding Reality]] - 6 hours - DONE
*** not recommended - too wooly
* [[system, web projects, streamline, software, tools, different people]]
** AttnTracker - improve so you can tell the total for a project between two events - 3hrs - HALF-DONE (2 hrs)
*** you can type "bill <project>" and that creates a point in time that can be used as the upper bound for a timeline and summary; the next thing is to make the lower limit of these periods the previous bill-point
* [[electronomads, promote lifestyle, individual importance, products, projects, creation]]
** [[Boutique Barista]] - make a plan to get this done - 2hrs - DONE (30 mins)
*** have put the project on hold as it has become a burden
* [[products, cashflow, stakes, consumers, creation, make people powerful]]
** FastDiff - improvements - 16hrs - DONE (20hrs)
** SweetSpot - phase VIII progress - 16hrs - DONE (10 hrs)
** [[Kathryn McMann]] - get site build started - 2 hours - DONE (2 hrs)
** [[What On Earth]] - help Josh with the build - 12 hours - DONE (15 hrs)
Total: 57 hours
Billable: 46 hours
I'm writing this a week into the following fortnight. This is mainly because what I've wanted to do over the past week has seemed very clear and because I've not made the time by myself.
This fortnight was dominated by the [[SweetSoft research]], which ended up taking more than five times longer than I expected it would. I also had half of the fortnight off for a holiday with Bonnie's Dad, which was very relaxing.
The first week of the following fortnight is going to be all about completing projects and being able to think about what's coming next: [[Jeroboams]], [[BPSF]] and [[Grace]] all ought to be launched by the end of the week, or early the following week. SweetSoft is looking like it will start to require regular commitment, which is cool, as it fits the bill for an Electronomad.com project - helping a startup with our skills, with an opportunity to attach our reward to the success of the project.
-----------
From Monday 5th April, 62 hours (see [[Habits]]). Seeded from [[Wants]].
* [[enjoy life, good things, food, music, people, friends]]
** Monday 5th April = bank holiday - 4hrs 30mins
** Holiday, 12th - 16th April inclusive - 22hrs
** get BPSF cross-browser testing and last tweaks finished - 5 hours - HALF-DONE (1 hr 25mins)
*** IE6 problems outstanding, along with a couple of bugs and dealing with a set choice of first video
** complete [[Jeroboams]] features - 5 hours - DONE (9hrs 10mins)
*** also worked through extras and fixes sent by Charlie
** get Latitude tickets - 1hr - NOT DONE
** pay Jayne - 30mins - DONE
** get Grace build finished - 2 hours - NOT DONE
*** put off for another week
** design layout of content for [[Daniel's|Violet Hill Studio]] site - 2 hours - NOT DONE
*** tried to outsource to [[Mark Shahid]], but didn't as he doesn't have enough time
** tweaks to [[BonnieParsons.com]], including incorporating Richard's feedback - 2 hours - DONE (5hrs 30mins)
* [[electronomads, promote lifestyle, individual importance, products, projects, creation]]
** visit at least one NCP car-park on Andy's list - 3 hours - NOT DONE
*** Andy has not been in touch
* [[products, cashflow, stakes, consumers, creation, make people powerful]]
** organise SweetSoft meetings, write and send questions in advance for the suppliers - 1 hour - DONE (1hr)
** create proposal, including business model, for SweetSpot, which includes finishing any needed research - 3 hours - DONE (16hrs 25mins)
*** this took a *lot* longer than I was expecting!
This fortnight has been 3/4 spent in the US: San Francisco and the coast down to LA. So far the trip has been an interesting exercise in getting to know a new city and new people. I've discovered that going out by myself to a (dance) club can be fun, and that new acquaintances don't just drop into your lap.
This trip is partly an experiment in electronomadism. I think I have been reasonably effective, even though the people I'm working with have been eight hours ahead - in fact, being alone most of the time makes for a more efficient day. San Fran has some great cafés with good coffee, food, wi-fi and power, so there hasn't been a noticeable problem with environment. The one downside is that some of the projects feel like they have come a bit "unstuck", although Josh has suggested that this could be overcome by having regular, short, Skype calls to overcome barriers and make sure you are not spending a long time getting over a hurdle that would otherwise be broken down in a few hours of being together. LShift have indicated similar feelings.
The practicalities of electronomadism have meant wander around with a rucksack, which is not always the most desirable-sized bag. I think a smaller bag, that can still be carried across both shoulders when desired, would be better than a travellers' 20-litre rucksack.
Being in an entirely new context, meeting and talking to people who are interested in Node.JS and Webhooks, has led me to work up a demonstration of a ReverseHTTP server running on Node.JS, based on Tony Garnock-Jones' project that used Rabbit. What I'd like to do with this is show how you can develop a JS app in the browser and then push it, unchanged, to a server to run there.
The next fortnight looks pretty busy. Geographically, I'm in LA for the first half and SF for the second. There is quite a lot to get done between now and two weeks' hence, not least because we've picked up or are picking up quite a few new projects, which are adding to the ongoing projects. Headline items include [[What On Earth Books]]. SweetSpot phase 8, FastDiff and an LShift project for the BBC.
----------
Fortnight starting Monday 6th September 2010. Up to 62 hours (see [[Habits]]). Seeded from [[Wants]].
* [[enjoy life, good things, food, music, people, friends]]
** [[SF trip]] - online ESTA (30mins) - DONE (30mins)
** [[SF trip]] - book hotel for first few days (1 hr) - DONE (1hr)
** [[SF trip]] - travel (18 hours) - DONE (26hrs)
** [[SF trip]] - meet lots of interesting people - HALF DONE
*** it is ongoing!
** Experience some SF goodness! - DONE
*** yeah!
* [[system, web projects, streamline, software, tools, different people]]
** AttentionTracker - improve again (2hrs) - DONE (90mins)
*** added sparklines that show when an attention event has notes attached and in what quantity
* [[electronomads, promote lifestyle, individual importance, products, projects, creation]]
** [[Boutique Barista]] - plan for launch (1 hr) - NOT DONE
*** didn't make the time for this
** [[Boutique Barista]] - get basic site up, with the eCommerce bits working (5 hrs) - NOT DONE
*** didn't make the time for this
* [[products, cashflow, stakes, consumers, creation, make people powerful]]
** J&J - plan for getting client, do with Josh (1 hr) - NOT DONE
*** didn't make time. Josh & I haven't talked that much while I've been away.
** SS7 - complete this phase (15 hrs) - DONE (15hrs?)
*** hard to know how much was spent on this - need a way to get AttnTracker to show total time in-between two events
** [[wiki-data]] - call with Adam (1hr) - DONE (1hr)
** [[wiki-data]] - quote and estimate for next phase (2 hrs) - DONE (3hrs)
** LShift - meeting about their new project (2 hrs) - DONE (3hrs)
** LShift - interface design for the DiffEngine project (8 hrs) - HALF DONE (6 hrs)
*** started a design document and made sketches of various visuals
!!Bonus points
ReverseHTTP server using Node.JS & Socket.IO - http://github.com/jayfresh/Node.JS-reverseHTTP-experiment (3hrs 30mins)
This fortnight felt like a good length of time to be setting a plan for. It was a relatively successful fortnight, with almost all the things planned done, although my estimates for the big things were way under. Perhaps that reflects a certain uncertainty about what you're going to do in the second week.
I'm thinking a lot at the moment what I'm going to do for ElectroNomad and how I'm going to go about creating products. I have a good idea of what products I want to make, whereas I can't say the same for the ElectroNomad project. I've got a general idea of what .org will have on it, and how I can build it from a cafe guide into something substantial. What I'm still not clear on is how .org makes sense in the context of me, .com and whether my online identity becomes electronomad, and what I do with jayfresh and jaybyjayfresh.com. I know I want something online about me specifically, which talks about what I'm into, and about the projects I've done, and about electronomad.org. Perhaps the reason this is hard to think about is because the need for it is less clear than for .org. Over the next fortnight, I want to become more clear about this.
I expect the next fortnight to see the conclusion to [[Jeroboams]] and [[BPSF]], and I'm not sure whether there will be anything from [[wiki-data]] coming up, so it's an opportunity to plan my products' development, plan electronomad.org/.com, and do some software experiments. By the end of the fortnight, I'd like to be clear about where the money's coming from next and how these new projects are going to grow.
-----
Monday 8th March 2010. Two weeks. To 62 hours (see [[Habits]]). Seeded from [[Wants]].
*[[electronomads, promote lifestyle, individual importance, products, projects, creation]]
** [[NCP Alternative Use]] - speak to [[Andy Kythreotis]] about nature of proposals (pegs and holes) - 1 hour - DONE (30 mins)
** [[NCP Alternative Use]] - vist Frank's Cafe in Peckham to get a feel of the place - 3 hours - not done
*** it is no longer open!
** [[Industrial placements]] - speak to [[Andy Blackwell]] about summer school students to do a [[fee-free micropayments]] research project into measuring time to transfer money between banks - 1 hour - DONE (30 mins)
*** he's finding out how I advertise a placement
*** also in conversation with Andy's colleague Alastair ...
*[[products, cashflow, stakes, consumers, creation, make people powerful]]
** [[Jeroboams]] - figure out how the wines are going to be stored in Wordpress and how the bulk upload is going to work - 10 hours - DONE (22hrs 25mins)
*** also sorted the mailing list system
** [[wiki-data]] - complete the work for the next milestone - 16 hours - DONE (22hrs 5mins)
* [[enjoy life, good things, food, music, people, friends]]
** finish [[Grace coffee]] site - 5 hours - NOT DONE
*** tried to give this to [[Mark Shahid]], in progress
** launch [[BonnieParsons.com]] v2 - 5 hours - DONE (not sure, around 5 hours, likely more)
** let Daniel from [[Violet Hill]] know I'll be working on his site - 0.5 hours - NOT DONE
** help [[Alex Vickery]] with bulk upload for firstpilotjob.com - 2 hours - done (3 hours)
!Bonus points
* Wrote this in an email asking me to describe my company in 20 words or less: Recognise and support a lifestyle based on the web project, the individual and the group, rather than company and hierarchy.
* [[Rewired State Home Office Hack Day]] (inc. postcode to London borough mapper)
!Thinking about [[Productively]]
I'm not sure I need to list events in my plan. They're all in my calendar after all. Maybe if they directly relate to a part of the plan.
Something Josh and I reflected on was that in the [[Jeroboams]] project, we've spent a long time on design, which is what we said we would and then wittled down in the quote because it sounded like a lot to the client. We've come to the conclusion that design is something that happens each cycle, not least because it is one of the few things the client can have a constant input on. Next time we write a quote, we'll take this into account, spreading design across all the cycles - an inevitable consequence of rethinking, altered priorities and the back-and-forth that happens whilst everything else is going on. We shouldn't aim to pin a client down to a design decided in the early stages of the project - much like everything else, change is ok - it is just that with design, we should account for its changes right from the start.
This fortnight has been dominated for me by the PaaS research I was doing, along with the related SweetSpot phase 6. Also put a lot of effort into the Attention Tracker, which is benefiting from continuous use by Josh and me.
I feel like I've neglected my Boutique Barista project this fortnight, and I haven't got much planned for the San Fran trip yet.
This next fortnight, I'm spending a week in Tuscany, although I'm taking my laptop to try a bit of sunny remote working. The main things over the next two weeks are SweetSpot Phase 7, and pursuing the leads we're putting dogs in: Kat, BSPF, AwimAway.
I'd also really like to continue to improve the AttentionTracker. I want to figure out with Josh what an appropriate system for exposing to each other what we want to do over a period of time that involves the other person. This is part of figuring out how a team can be efficient once the people involved are, but isn't about enforcing one's own system on the other members, nor is it about task lists.
----
Fortnight starting Monday 9th August 2010. Up to 62 hours (see [[Habits]]). Seeded from [[Wants]].
*[[enjoy life, good things, food, music, people, friends]]
** [[Elements of Typographic Style]] - read - 6hrs - DONE (10 hrs)
** [[Maverick]] - type up notes into blog post - 1 hour - NOT DONE
*** not made the time
** [[SF trip]] - plan what to do, including checking out suggestions by [[John Merrells]], [[Andrew Back]] and [[Chris Sugden]] - 4hrs - NOT DONE
*** didn't make the time
** [[CSA]] - create site - 9 hrs - DONE (15hrs)
*[[system, web projects, streamline, software, tools, different people]]
**[[readCountPlugin]] - write blog post about "page numbers for HTML" or "the curse of hypertext" - 1hour - NOT DONE
*** didn't make the time
*[[electronomads, promote lifestyle, individual importance, products, projects, creation]]
** [[Boutique Barista]] - make contact with the artists who would be contributing and firm up the contribution details - 5hrs - NOT DONE
*** haven't made the time
*** meet with Steve and the artist he's recommending
*** arrange meeting with Rob
*** reply to James
*** talk to Mihail
*** talk to Oscar
** [[Boutique Barista]] - design essential pages with Josh and start build - 5hrs - NOT DONE
*** was going to do on second Sunday, didn't'; rescheduled for Monday
*[[products, cashflow, stakes, consumers, creation, make people powerful]]
**[[Kathryn McMann]] - get prices together - 2hrs - DONE (1hr)
** [[SweetSpot]] - phase 6 - 10 hrs - DONE
** [[Alison Coward]] - invoice Alison - 30mins - DONE (15mins)
**[[J&J]] - basic business plan - get first-hand feedback from 3 agents/developers about where the pain in the processes is - 5hrs - NOT DONE
*** Glenn gave good feedback
**[[J&J]] - basic business plan - get the commercial sales process written down - 2hrs - NOT DONE
*** didn't speak to anyone about it
**[[J&J]] - basic business plan - get the commercial assignment process written down - 2hrs - NOT DONE
*** didn't speak to anyone about it
!!Bonus points
* [[PaaS: A mid-2010 survey]] - 15 hrs
** Did a lot of research into the various PaaS providers out there, saw lots of demos, wrote up the report
* [[Developing with another person]]
!!Thinking about [[Productively]]
We experimented with Pivotal Tracker for SweetSpot Phase 6. It didn't seem to bring us much. In fact, as we are using the AttentionTracker regularly, it felt like using two systems to do similar things. There wasn't a perceptible improvement over using simple lists of things to do. I suspect this is because we used Pivotal Tracker to track tasks rather than stories (we had the stories in there, but they didn't get actively tracked). This highlights that we are not developing stories - we are developing according to lists of the things we need to complete to satisfy stories.
This fortnight, I've been on holiday in Sicily for a week! It's been very relaxing and has involved little more than sitting by a pool reading, with the occasional break to wander into town and eat pizza. I did 3 months reading in a week - "A History of Misogyny", "Hangover Square" and "Bad Science". I recommend all three.
The week before I went on holiday, I sorted out the details of what I'll be doing for Avox and created an estimate of this phase of the project. I have a fortnight to get it done and I would like to do it early so I leave space for dConstruct and BarcampBrighton at the end of that fortnight. I intend to talk to Chris when he is back from holiday to see if I can subcontract some of the server-side work (search mainly).
While I was away, I figured that I have a new want: [[I want to be well-known for making it easy for small business people to help themselves]] - this involves raising my profile and getting some examples down (to begin with). I'm thinking that there are three tiers to this: creating API's for things that don't have them; making it easier to make useful mashups; producing the things that connect systems with API's to mashups. I'm active in all three, but I think I should explore further with a view to creating examples of competency in all areas. I don't think I've written this before, but I came up with a phrase to describe how I want people to think of the company I run: "If you're doing something that people love, you deserve a helping hand - that's us".
As a [[Productively]] observation, I've seen that the effect of seeing how good I am at estimating the time taken for each task has led to two things: I make longer (and truer) estimates; I have less things in the plan for the week. I think these are both good things.
-----------
To 31 - 8 = 23 hours - see [[Habits]] (I'm away on holiday Saturday and Sunday)
![[I want to not have to worry about paying the rent]]
#[[Avox Wiki-data]] - make sure the time spent on Monday afternoon and at Tuesday's workshop leads to the work for the next phase of development on the project - 12 hours - DONE (took the day and a half, which is as expected)
#[[Avox Wiki-data]] - see what I can do on the project before the end of the week - 8 hours - NOT DONE (spent 6 hours)
** Creating detailed estimate and plan of V1 work - DONE
** Put the first changes into place to satisfy a user story - NOT DONE
![[I want to develop my portfolio into something that is strong enough to get work from]]
#[[Ripplz]] - get the metric worked out for choosing which picture is best for matching a wave-height value - 2 hours - DONE (2 hours)
** Going to use monochrome images and use the linear hex value 00-FF
![[I want to make people happy]]
#[[Dad's IT problems]] - finish off the Owners' Direct advert - 1 hour - NOT DONE
** Made a start, but the site was down when I came to change the copy
!Bonus points
#[[Auto-generation of invoices]] - Google Spreadsheets-based system for tracking and building invoices - 5hrs 45m
#Booked dConstruct ticket
#Blog post about [[ScreenScrape: logging into HSBC website]] at http://jaybyjayfresh.com/2009/08/14/how-to-log-into-hsbc-online-banking-from-the-command-line/ - 2 hours
#[[Migrating from Macbook Pro to Powerbook G4]] - started - 1 hour
I missed a week! This was mainly spent on [[BPSF]] and [[wiki-data]]. During the first week, we launched the new design of wiki-data, which I am very pleased about. We've arranged a Chinese to celebrate (preceded by a gallery opening with free wine). I spent a day with Tager that week, working on a special project for Bell Pottinger. Tager had been asked to produce a short film to help with a pitch for the Olympics PR, and I produced a digital proposal to go alongside it - how you'd use technology to help people feel like the Olympics was uniquely important for them.
I had a call with Avox at the end of the fortnight, agreeing the next phase of wiki-data - we are separating wiki-data from My Avox Data (MAD), to emphasise the difference between the public-domain information and the Avox product.
This next week, Josh and I are finishing off the second phase of the BPSF development, and we have a review meeting planned for Thursday. I am planning to make inroads into the wiki-data work.
I'm thinking about how to sub-contract more work from projects. I spoke to Jeremy on Friday and he suggested I get some youth in and train them up. This is an appealing idea - I wonder if the government grants for apprenticeships apply to apprentice freelancers... Jeremy also said he'd introduce me to Richard Moss, who is a web designer. I've got back in touch with Alex from WeMakeWebsites to see if he has any time. I've been asking people to let me know if they know any freelance web designers. We'll see how this goes.
At last, Ben and I got some work done looking into putting office space on the roof of car-parks. We have a Google Doc going with [[research notes|http://docs.google.com/View?id=dr44nfc_99fhd7k4sf]].
-----
Week beginning 11th January, up to 31 hours (see [[Habits]]). Seeded from [[Wants]].
* [[I want to invest in startup businesses or art projects]] - 4 months (January)
** [[wiki-data]] - get wiki-data launched (20 hrs)
** [[BPSF]] - move second phase of BPSF project along, with Josh on Thursday and Friday (10 hrs)
!Events
Dinner at Nick and Hannah's Thursday night
Dinner at PJ's Saturday night
Climbing Tuesday
Go-test.it meeting in Osmosoft Friday afternoon, followed by beer (3 hrs)
!Bonus points
* [[Digital Olympics]] - 7hrs 20mins ([[Invoice 11]])
* [[Testing local TiddlyWeb applications in virtual machines]]
* [[How is NCP affected by the London congestion charge?]]
I've been on holiday quite a lot this fortnight, which is why this is review is for two weeks. The big thing that happened is that I gave my resignation, in the form of a [[blog post|http://jaybyjayfresh.com/2009/05/20/this-is-not-an/]]. Also, I spent useful time with Saq getting some advice about going it alone, which has led me to consider getting a stable freelance income to be the top priority, with the speculative work coming after that. Saq also helped by pimping me out to a few people, including Loic, who is talking to me about a freelance JavaScript job.
!Top Wants and Goals for fortnight commencing 11th May 2009
!! I want to be able to go freelance
# [[Setting up freelance contracts]]
** Speak to Saq about UnaMesa work possibilities - 2 hours - DONE
** Find out what's the plan with LShift - 0.5 hour
*** Speaking to Mike B w/c 25th May when we're both back from holiday
** Pin down time for meeting with Tager's accountant - 0.5 hour - NOT DONE
# [[Finishing things off]]
** [[ILGA]] - 6 hours - DONE
** Establish list of projects that I could "finish off" and write down what needs to be done on them - 1 hour - NOT DONE
** [[Anna Freud Centre]] - 2 hours - DONE
# [[iPhone translation app]]
** Set up automated testing and come to a conclusion about what the realistic constraints are on what we can scan successfully (consider post-processing) - 6 hours - NOT DONE
Other things going on: discussion with Chris Sugden about WebDev tools and open business and reward; meeting with Drama Magazine on Tuesday about possible iphone/web/translation opporuntities (NOT DONE); meeting with Notion Magazine about hooking up their mobile services with the web (DONE; meeting with editor scheduled for Wednesday 27th to talk about revenue-sharing)
This fortnight was rather dominated by [[J&J's retreat]], which happened over the middle weekend. We met up on the Friday before to do a little braingashing on how we could solidly be doing four billable hours a day. We first concluded that we should be making a product for a niche, and then we concluded that we should focus on the niche of "expensive houses". Over the weekend, this was developed into the idea that we create software to help people sell or rent property online.
We ended up with some basic ideas for products and described them on http://property.withjandj.com. On the following Friday, we spoke with Nick about the plan, and he seems to support the effort. We're not sure what form we'd like that support to take yet.
The second week was quite good for [[Boutique Barista]] - I heard back from a few more baristas and on the Saturday, spoke to Oscar and Mihail about them getting involved. Fabio's come up with a Brazilian sweet made in tiny amounts in a village near Sao Paolo (I think).
In other news, I did a few random other things, most satisfyingly finishing two great books and making a TiddlyWiki plugin that shows you how much of a hyperlinked document you've consumed (went down well on the TiddlyWiki group).
-----
Fortnight starting Monday 12th July 2010. Up to 62 hours (see [[Habits]]). Seeded from [[Wants]].
*[[enjoy life, good things, food, music, people, friends]]
** SF trip - plan what to do, including checking out suggestions by [[John Merrells]], [[Andrew Back]] and [[Chris Sugden]] - 4hrs - NOT DONE
*** didn't make the time
** SweetSpot - get PropMan problems worked out - 1 hour - HALF-DONE
*** have emailed PropMan with questions
** [[Thinking with type]] - read (5hrs) - DONE (5hrs)
*[[system, web projects, streamline, software, tools, different people]]
** [[Maverick]] - read, type up notes into blog post - 10hrs - HALF-DONE (9hrs)
*** read it, haven't written up notes yet
** Build basic version of [[An attention tracking system]] - 5hrs - DONE (5hrs)
*** see [[Attention tracker prototype]]
** SweetSpot - get the project tracker's wants section up-to-date - 1 hour - NOT DONE
*** didn't make the time
*[[electronomads, promote lifestyle, individual importance, products, projects, creation]]
** [[Sheraz Arif]] - help Sheraz get a iPhone/Android developer for his prayer app - 1hr - DONE (30mins)
*** found [[Dustin Henderson]] & [[Jef Waumans]]; Sheraz found someone to do the job on a profit-share basis, so I'm not taking this job
** [[Alison Coward]] - help Alison get the right team together for her charity project - 1hr - DONE (1hr)
*** need to invoice Alison - NOT DONE
** attend Simplicity Day @ The Cube, 12th July, by Minimoko (3hrs) - DONE (3hrs)
*[[products, cashflow, stakes, consumers, creation, make people powerful]]
**[[What I want]] - Having decided now's the time to sell something, sort out what I'm selling, with Josh (8 hrs) - DONE (12hrs)
*** see [[J&J's retreat]]
**[[What I want]] - Figure out a plan getting a product off the ground (12hrs) - DONE (16hrs)
**SweetSoft - get a quote together by end of first week (4hrs) - NOT DONE
*** speak to CDent and others about the mechanics of a pluggable system for online estate agent software
*** not got the systems architecture sorted out; considering the option of searching for appropriate 3rd-party systems to use
** [[J&J]] - get a good idea of how we're going to develop the products (4hrs) - HALF-DONE
*** get the facts and figures comparison with [[COTEA|COTEA facts]] (from 2007), to show how market has changed
*** speak to Pex
*** speak to Michael Collins about what it's like in the US (where there is more online apparently)
*** talk to Nick about supporting the product developments
*** get the product architecture sorted out
**** asked Chris, Jeremy, Alexis and Blaine for some advice
*** now, at the point where we need proper process descriptions so we can ask for help identifying "where's the pain?"
!Bonus points
[[readCountPlugin]] - 3 hours
The first week of this fortnight ended with a concerted effort to get [[Boutique Barista]] off the ground. Josh and I finished the brief and, over three days, I took it round 20 independent coffee shops. I'm surprised it took this long (although I wasn't putting in full days until Friday), but I feel it was worth it. The plan was to wait until I got back from France to see the response and send out a group email about what the next stage should be. I have to decide what that next stage should indeed be...
France was excellent. I had my first Michelin-starred experience (as far as I'm aware), which was superb. All very minimal, but done to bring out flavours and combinations rather than to keep you hungry, which we certainly weren't by the end. I didn't think about anything very much apart from lunch, swimming and reading, until the last few days, when I thought about these things: [[Eddy feedback loops and parallelism in the design process]], [[How to profit-share equally with a growing team using one-off payments to keep things fair]], and the idea that one should treat one's partners like black-box companies, rather than manage through the network, which probably amounts to little more than a recognition that delegation is good (although seems to say something about incentives, but I'm not clear on that yet).
This next fortnight, I want to make sure that I check-in with the baristas and see what they're thinking. I need to be prepared to kill the idea if there is no enthusiasm for it. I also want to get speaking to people who could do the server-side for the SweetSpot online experience, and do whatever needs doing to get PropMan on its way to being installed.
Plus there's Chris' wedding on the first weekend!
I ought to start putting some plans together for America. [[John Merrells]] has been very helpful suggesting people to speak to or things to look at, so I should follow some of those leads and get in touch with people. It might help to book my flights first...
-----
From Monday 14th June, up to 62 hours (see [[Habits]]). Seeded from [[Wants]].
* [[enjoy life, good things, food, music, people, friends]]
** Take a break in France (31 hours) - DONE (31 hours)
** [[BonnieParsons.com]] - put on proper hosting so Google sees it; remove dead vids and replace with new ones (1 hour) - NOT DONE
*** didn't make the time
** RareRetreats - speak to Emma about her business and what we can do (30 mins) - DONE (30 mins)
*** Emma is at an early stage; I gave her some advice and look forward to hearing from her again soon
** [[DRAMA]] - let the guys who quoted for the project know we haven't got it (15 mins) - NOT DONE
*** was waiting to hear back from Dom about who had got the job, will let the guys know anyway soon
** SweetSpot - write the PropMan-choice executive summary for Nick (2 hours) - DONE (2 hours)
** [[The Goal]] - finish the book (6 hours) - DONE (6 hours)
* [[products, cashflow, stakes, consumers, creation, make people powerful]]
** [[Boutique Barista]] - get the brief looking good (4 hours) - DONE (3 hours)
** [[Boutique Barista]] - circulate the brief to a half-dozen baristas (3 hours) - DONE (12 hours)
*** circulated to 20 baristas; will wait until back from France to see what the response is
* [[system, web projects, streamline, software, tools, different people]]
** [[wiki-data]] - finish tickets and launch new version (2 hours) - HALF-DONE (3 hours)
*** tickets done, asked Chris to make the new version live while I am away
* [[electronomads, promote lifestyle, individual importance, products, projects, creation]]
** [[Alison Coward]] - speak to Alison about what an open agency would look like if it grew to be a £100 million company (1 hour) - NOT DONE
*** didn't make the time
This fortnight focussed mainly on the [[SweetSoft]] and [[Jeroboams]] projects, with [[SweetSoft]] feeling on track and [[Jeroboams]] given the appropriate amount of support so their big launch of wine email alerts is working smoothly.
In the second week, I really got back into exploring server-side JavaScript ([[SSJS]]), as I want to use [[Smart Platform]] for [[SweetSoft]]. I found out about [[Env.js]] and [[Node.js]] (and [[jsdom]]), and got in touch with the folks at Joyent and on the relevant groups. The conclusions are: that I'd like to see Joyent create a service using Node.js analogous to Smart Platform; I'd like to get a browser environment running on Smart Platform - Env.js might work if someone writes the appropriate platform support, and I'm not clued up enough about jsdom yet to know if that's suitable.
This next fortnight (apart from the bank holiday!), my top priority has to be finishing this phase of SweetSoft; after that, I owe [[BSPF]] some work to finish it off. Adam from Avox has got back in touch asking for a quote for some work, which I will respond to. I'd also like to speak to some more coffee shop owners to find out if they are interested in [[Boutique Barista]].
--------
From Monday 19th April, 62 hours (see [[Habits]]). Seeded from [[Wants]].
* [[enjoy life, good things, food, music, people, friends]]
** Enjoy the sunshine, which is rather fantastic at the moment - 5 hours - DONE (2 hours)
*** sat in the park
** Get [[Grace]] launched - 3 hours - DONE (3 hours)
** Write a brief for [[Violet Hill]] and see if Amit can do it - 2 hours - DONE (1 hour)
*** need to call Daniel and let him know about Amit, then introduce the two of them
** Finish off the [[BPSF]] changes and fixes - 3 hours - NOT DONE
*** there are still a small number of things to do
* [[system, web projects, streamline, software, tools, different people]]
** make some tweaks to improve the way TiddlyWeb-based project trackers work - 4 hours - DONE (4 hours)
*** see [[TiddlyWeb Bag Policy Editor]]
* [[electronomads, promote lifestyle, individual importance, products, projects, creation]]
** chase up [[NCP]] to see where they're at making our shortlist of car-parks to investigate - 1 hour - DONE (30 mins)
*** Left a voicemail for Andy, no reply yet
* [[products, cashflow, stakes, consumers, creation, make people powerful]]
** Help Charlie to get [[Jeroboams]] launched and finish the changes and fixes - 6 hours - DONE (5hrs 50mins)
** Collect the required information for and create the supplier recommendation for SweetSoft - 2 hours - HALF-DONE (1 hour)
*** still waiting for a reply from CFP
** Design the simplest student booking system for [[SweetSoft]] and get half-way through building it - 25 hours - DONE (15 hrs)
*** Have done all the technical investigation needed to build a server-side component that speaks to GCal; also, confirmed we can do everything we want to with GCal
*** Josh has made the property web pages appropriate for displaying property information and plugging into the server-side
** Agree a reward model for [[SweetSoft]] - 1 hour - DONE
*** for the time being, just work on fixed fee
!! Thinking about [[Productively]]
Without really thinking about it, I've started to use Google Tasks again - I'm adding things that I want to make myself do that day, and they're only really meant to be on the list for a few hours. They complement the list above, because they represent a certain set of micro-things that need to not be forgotten. This contrasts with how I used Google Tasks previously, when I didn't have this [[Wants]] thing going.
!!Bonus points
[[Getting the DOM on Smart Platform]] - 3 hrs
[[Live JavaScript editor for Smart Platform]] - 2 hrs
This fortnight was much about SweetSpot, and about being on holiday. These both went nicely.
I've managed to get [[Boutique Barista]] going a bit, having written a brief and tried to speak to Fabio. The plan is to speak to more baristas and get a better idea of what products they could source, and start circulating the brief once it's looking nice.
The quote collection for [[DRAMA]] is going reasonably smoothly; the [[Scheduler 4]] project appears to be producing potential for work later on in Summer. It looks like SweetSpot is going to be the main income source over Summer, which should leave enough time to get [[Boutique Barista]] going if I don't mess about.
This next fortnight, I really want to bite a chunk off BB, get a concrete plan together for DRAMA and get a proposal costed for building SweetSpot's core online experience.
I'm planning to go to RewireLondon too, which is a day conference for creatives. This should be fun and interesting.
------
From Monday 17th May, up to 62 hours (see [[Habits]]). Seeded from [[Wants]].
*[[enjoy life, good things, food, music, people, friends]]
** trip to Spain, Wed to Sunday of second week (22 hours) - DONE (22 hours)
** Josh's birthday party (3 hours) - DONE (3 hours)
** museums day with Bon (4 hours) - DONE (4 hours)
** [[SweetSpot]] - launch bookings app and tweak form design to improve flow (4 hours) - HALF-DONE (55 mins)
*** little bit of form flow to tweak before it's complete...
** [[Violet Hill]] - introduce Amit and Daniel to get Amit started (2 hours) - DONE (2 hours)
** [[wiki-data]] - get tickets for next phase sorted (2 hours) - DONE (2hrs 15mins)
** [[LShift]] - plan for BBC project (1 hour) - DONE (1 hour)
** [[LShift]] - interface design work (5 hours) - DONE (2hrs 5mins)
*[[system, web projects, streamline, software, tools, different people]]
** [[J-Base]] - help Josh get maths sorted (3 hours) - NOT DONE
*** Not sure Josh is continuing with this after finding something very similar on his birthday...
*[[electronomads, promote lifestyle, individual importance, products, projects, creation]]
** [[DRAMA]] - get a plan sorted for doing their iPad app (4 hours) - HALF-DONE (2 hours)
*** have a quote from DoTank Studios; in the process of getting a quote from Nitobi
** [[BPSF]] - outsource IE7 bug fix (2 hours) - DONE
*** [[Catherine Milton]] doing this
*** not finished yet
** [[Boutique Barista]] - get a one-page description written and designed (3 hours) - HALF-DONE (1 hour)
*** written, not designed - asked Richard White to quote for it; also, Josh said he wanted to do it
** [[Boutique Barista]] - introduce idea to Fabio and one of Gwilym (2 hours) - NOT DONE
*[[products, cashflow, stakes, consumers, creation, make people powerful]]
** SweetSpot - design the in-browser experience for all the processes in the core system; figure out what back-end is required to support this and describe the links to the property management system - diagrams are good (8 hours) - DONE (6 hours)
*** pretty much sorted, need to draw up the pencil drawings into mockups
** [[Grace]] - change Twitter address to @gracecreedlane or get @gracestpauls password reset; make email address grace@gracestpauls.com (1 hour) - HALF-DONE (1 hour)
*** asked BenJam where the password reset email will go to
*** grace@gracestpauls.com is set up and mail@ is forwarding to it
*** there is a catch-all email forwarding set up to grace@
*** appears to be no progress getting password reset
** [[Jeroboams]] - shop list feature (2 hours) - NOT DONE
*** can wait until after Spain according to Charlie
This fortnight was my last fortnight in the US, and I spent the first week driving down to LA with Bon (Big Sur = awesome) and then in LA, and the second week back in San Francisco. LA is a very different place to SF - much bigger and harder to enjoy, mainly because you see a lot of it from behind a steering wheel. Venice Beach is cool - "Shoreditch on the sea" (as said by an Australian in Jack Spade). The Intelligentsia café is in Venice too, which was pretty much the only good café I went to in LA.
I had planned to go down to Silicon Valley in the second week, but I ended up hanging around the city (mainly in Hayes Valley and the enjoyable Mercury Café), and working on FastDiff.
A feeling I'm left with after this trip is that the electronomad concept is pretty fun and that getting serious about making a resource for people who are thinking about it or doing it would be a really good thing. It would be cool to have an international network of stop-offs for electronomads, but starting with a London directory can't hurt. I think I've always thought that [[Boutique Barista]] is a prerequisite to doing any electronomad stuff, but it doesn't have to be. Again, it's a question of time.
I'm writing this on Friday of the first week of the new fortnight, and this week has been about sorting the Phase VIII proposal for SweetSpot (approved) and ploughing through the [[What On Earth]] build, the bulk of which Josh and I did on Wednesday. Thursday and Friday are mainly given over to a batch of FastDiff improvements.
Next week, I'd like to start on SweetSpot Phase VIII, and get [[Kathryn McMann]]'s site advanced. I have a deal to swap Jaco a website for a J&J jingle - he wants to be able to put his tracks online for people to play and license.
I am feeling like I could do with a separate me to look after launching [[Boutique Barista]]. I'm not quite sure what to do about this, as time repeatedly gets rather short. I am considering outsourcing.
-----
Fortnight beginning 20th September 2010. Up to 62 hours (see [[Habits]]). Seeded from [[Wants]].
* [[enjoy life, good things, food, music, people, friends]]
** [[SF trip]] - Enjoy LA - done!
*** Venice beach is the place...
* [[system, web projects, streamline, software, tools, different people]]
** AttnTracker - improve so you can tell the total for a project between two events - 3hrs - NOT DONE
*** didn't make the time
* [[electronomads, promote lifestyle, individual importance, products, projects, creation]]
** BB - visit Intelligentsia (2 hrs) - DONE
** [[SF trip]] - meet more peeps (8 hrs) - DONE
* [[products, cashflow, stakes, consumers, creation, make people powerful]]
** FastDiff - interface design (32 hours) - DONE
** LShift - BBC pitch contribution (3 hrs) - DONE
** SweetSpot - phase 8 plan (2 hrs) - HALF DONE
*** did the plan, didn't do the time estimates
** SweetSpot - phase 8 (10 hrs) - NOT DONE
*** didn't finish the plan, so didn't start the phase
** [[What On Earth Books]] - build a page (3 hrs) - NOT DONE
*** didn't make the time
Total: 63hrs
Billable: 50hrs
This fortnight was mainly about SweetSpot phase 7, which has generally been a very enjoyable affair. I've somewhat sidelined Boutique Barista, which is unfortunate, but I'm trying to get that back on track by asking people to help.
I've also had fun in Italy and discovered that remote working from truly remote locations does work if there's web.
Next fortnight is somewhat uncertain right now, as I'm not sure what San Fran will hold, but it will certainly involve completing SS7 and starting SS8.
---------
Fortnight starting Monday 23rd August 2010. Up to 62 hours (see [[Habits]]). Seeded from [[Wants]].
*[[enjoy life, good things, food, music, people, friends]]
** Spend a week in Tuscany - 31 hours - DONE (31 hours)
*** Read [[It's Not Luck]] - DONE (5 hrs)
*** Did plenty of SS7 and [[wiki-data]] here
** LoungeLover booking for Tues or Wed night - 30mins - DONE
*** postponed until October
** See Sargeant and the Sea @ Royal Academy - 2 hrs - NOT DONE
*** might be able to fit it in Tuesday day time
** [[Maverick]] - type up notes into blog post - 1 hour - NOT DONE
*** didn't make the time
** Return [[Maverick]] to RSA - 30mins - NOT DONE
*** didn't make the time; need to do this before going to San Fran...
** [[SF trip]] - plan what to do, including checking out suggestions by [[John Merrells]], [[Andrew Back]] and [[Chris Sugden]] - 4hrs - NOT DONE
*** didn't make the time
*[[system, web projects, streamline, software, tools, different people]]
** [[J&J]] - Publish [[PaaS: A mid-2010 survey]] - 2 hours - DONE (3 hrs)
**[[readCountPlugin]] - write blog post about "page numbers for HTML" or "the curse of hypertext" - 1hour - NOT DONE
*** have decided that this is specific to TiddlyWiki, so I'm leaving this at the post on the TiddlyWiki group
*[[electronomads, promote lifestyle, individual importance, products, projects, creation]]
** [[Boutique Barista]] - make contact with the artists who would be contributing and firm up the contribution details - 5hrs - HALF DONE
*** meet with Steve and the artist he's recommending - NOT DONE
**** call Wednesday after 2pm; ''calling me back around 15:45''
**** didn't call back
*** arrange meeting with Rob - NOT DONE
**** ''call Thursday between 7-9am''
**** didn't call back
*** reply to James - HALF-DONE
**** done, asked about whether he wants to promote Matt Stuart (http://www.mattstuart.com/), a photographer
**** he's into this, but haven't sorted any specific plan
*** talk to Mihail - NOT DONE
*** talk to Oskar - DONE
**** have emailed
**** he's up for it - http://www.oskarjohanson.blogspot.com - listed as Taylor St barista
**** need to sort specifics
*** stories/photos off Fabio - HALF-DONE
**** have emailed
**** he's planning to send me something as soon as he can
*** chat with Cathy about logistics - NOT DONE
*** chat with Caroline about how she can help - HALF-DONe
**** spoke in Italy; no specific plan
** [[Boutique Barista]] - design essential pages with Josh and start build - 5hrs - HALF-DONE (2 hrs)
*** the design has started, but it's tricky before having the content
*[[products, cashflow, stakes, consumers, creation, make people powerful]]
** [[SweetSpot phase 7|ss7]] - plan and get started - 10 hrs - DONE (not sure how many hours)
** Improve AttentionTracker so it is easier to use - 2hrs - DONE (3hrs)
*** created [[parseToDoPlugin]] to interpret to-do's in tiddler prose
** [[BPSF]] - meet (hopefully Wed) - 2hrs - NOT DONE
*** was postponed
** AwimAway - mockups - 2hrs - DONE (2hrs)
**[[J&J]] - basic business plan - get first-hand feedback from 3 agents/developers about where the pain in the processes is - 5hrs - NOT DONE
*** no new input
*** incorporating Glenn's feedback
*** Emma meet to talk about commercial property
** Respond to [[Kat|Kathryn McMann]] about her website - 30mins - DONE (30 mins)
!!Deferring or do if poss
**[[J&J]] - basic business plan - get the commercial sales process written down - 2hrs
**[[J&J]] - basic business plan - get the commercial assignment process written down - 2hrs
!!Bonus points
TiddlyAIR - 3hrs
I decided half way through this fortnight that I was going to try doing fortnightly plans for a while, as that gives time for things to develop. Maybe that'll work out. Having reached the end of a fortnight and looked back, it feels a long time has passed, so I'm not sure I could reasonably plan two weeks in advance. Nevertheless, I'll give it a go in the name of science...
At the beginning of the last fortnight, I refreshed my [[Wants]], which I'm very happy about, as it has removed a constraint that was getting out of date. I've spent a lot of time talking to people, visiting, seeing things, all in the name of exploring how to get interns, make connections to other web groups, etc. I didn't write a plan at the beginning of the fortnight, and now that I'm looking back at my paltry list of "things", I can see the value in having a set of things to achieve written down to compare myself against.
In the next two weeks, the big things are that I'm hoping the [[BPSF]] site will be launched, and the [[Jeroboams]] project will be mostly completed. I'd like to get the [[Grace coffee]] site done, as well as [[BonnieParsons.com]], to leave space to do the [[Violet Hill]] site next. I'll also be pursuing the [[NCP Alternative Use]] research.
-----
Monday 22nd February '10
Unstructured list - got too involved refreshing Wants to think about the plan clearly.
* Refresh my [[Wants]].
* BPSF content meetings
* Electronomad.com WP theme
** looking into Posterous as hosted alternative
** thinking I could publish the weekly reviews there
** could also write stuff that's not generic for the .org
* Starting [[Jeroboams]] project (mainly design)
* [[Day at Cambridge Computer Lab]]
* More [[wiki-data]] work to support sales mission
* Showing Phil [[Grace coffee]] design
* Working on [[BonnieParsons.com]]
!Thinking about [[Productively]]
During the Wants refresh, I read back over what I'd written in the [[Productively]] series. I'd written this in April last year - 10 months ago. What stood out most was that I had way more on my plate back then - "billions of projects" - whereas now I feel like I have a manageable set of things going on at any one time. Thinking a bit more about that, it's clear that I still have a number of things ticking over at the back of my mind, but I know they're not to be worried about until I decide they're important and focus on them.
After I'd written down all the things I wanted, I had a look at them and realised that some of them were tangible things I could do and some of them were descriptions of a type of thing I want to do. Roughly a third of the things (out of 24) were tangible. I was wondering about how to write some new Wants that would reflect what I wanted, while balancing the tangible with the as-yet-unknown-realisation. I've ended up writing the Wants as short lists of words that give context, rather than sentences. The text of those tiddlers describes the kinds of things I'm thinking about.
I've re-used the "how things will be/how things are now" style in writing the text of Wants. I like this as the gap is revealed to you as you write about it.
I am going to experiment with updating [[What I want]] every fortnight, not every week. I think this might give me the option of including things that take slightly longer to do.
!Bonus points
[[Day at Cambridge Computer Lab]]
[[Making my own tools for Jeroboams]]
This fortnight was mainly about getting the [[J&J]] business plan sorted. I think we got about half way through this. My investigations into architecture for the system became a big exploration of Platform as a Service (PaaS) companies - see [[A survey of hosted application building tools]] - I'd quite like to write this up as a blog post. We also pinned down most of the processes used to buy/sell or rent/lease residential or commercial property. We don't have much of an "in" to first-hand accounts of where the pain is in the processes.
[[Boutique Barista]] took a bit of a back seat in this fortnight, although the conversations I've had with people lead me to think that I have enough interest to launch - I do now need to pin the guys down who are contributing. I have asked Josh for an evening's design time too, which I'm hoping to get in the next fortnight. It would be good to have the site structure worked out and the build started.
I've started to make plans for the [[SF trip]] too, and I want to do much more of that in the next fortnight. I think [[Jeff Lindsey]] would be a great guy to meet, as WebHooks has really developed since I looked last year.
-----
Fortnight starting Monday 26th July 2010. Up to 62 hours (see [[Habits]]). Seeded from [[Wants]].
*[[enjoy life, good things, food, music, people, friends]]
** [[Elements of Typographic Style]] - read - 8hrs - HALF-DONE
** [[Maverick]] - type up notes into blog post - 1 hour - NOT DONE
*** didn't make the time
** [[SF trip]] - plan what to do, including checking out suggestions by [[John Merrells]], [[Andrew Back]] and [[Chris Sugden]] - 4hrs - HALF-DONE
*** made a start, needs much more
** See Enron - 3 hours - DONE (3hrs)
*[[system, web projects, streamline, software, tools, different people]]
**[[readCountPlugin]] - condense interface - 1 hour - DONE (1hr)
**[[readCountPlugin]] - write blog post about "page numbers for HTML" or "the curse of hypertext" - 1hour - NOT DONE
*** did post on the TW group, but didn't make time for the blog post
**[[Attention tracker prototype]] - get prototype to the point where I can use it - 4hrs - DONE (3hrs)
*** analysis needs to show time on projects
*** support time modification
*[[electronomads, promote lifestyle, individual importance, products, projects, creation]]
** [[Boutique Barista]] - keep plugging away with the baristas till I have enough confirmed products. Five would do - 3hrs - HALF-DONE
*** speak to Oscar and Mihail
**** done, but needs more conversation
*** meet with Steve and the artist he's recommending
**** not yet
*** arrange meeting with Rob
**** not yet
*** reply to James and Nick
**** saw Nick, not replied to James yet
*[[products, cashflow, stakes, consumers, creation, make people powerful]]
** [[Alison Coward]] - invoice Alison - 30mins - NOT DONE
*** didn't make the time
** [[Scheduler 4]] - invoice LShift - 30mins - DONE (30mins)
**[[J&J]] - basic business plan - 30 hrs - HALF-DONE
***get processes described - 10hrs
**** mostly done, needs commercial buying and sub-letting/assignments
***find pain points - 10hrs
**** not had much conversation, but have sent out some emails
***feasibility of architecture - 10hrs
**** spent a really long time in this area
**** been looking at using 3rd-party systems to handle logic, see [[Automated Billing]]
**** also see [[A survey of hosted application building tools]] - details of online demos with PaaS suppliers
**** had a meeting with James Stewart to talk over the idea of the distributed architecture
** SweetSpot - get the project tracker's wants section up-to-date - 1 hour - HALF-DONE
** SweetSpot - get the PropMan problems worked out - 2hrs - DONE (1hr)
!!Thoughts on [[Productively]]
I found that Paste Interactive have released a service called [[Paprika|getpaprika.com]], which is effectively a notes & to-do list app. The thing that interests me is that the to-do's use wiki-text and they have some special syntax: start a line with '-' to create a to-do; type in a '#' to create a tag; type a date in one of a set of formats to make a date.
You can then see all your to-do's, tags and dates across all your notes and projects.
This reminded me of the embedded questions I've been using in project trackers, by pulling out anything that is ''bold'' into a summary list. I think this embedded syntax that you summarise later is another nice way of adding function to microcontent, without having to make an up-front choice about what type of content you're creating.
!!Bonus points
[[Kathryn McMann]] - meet about website
Monday 22nd March 2010. Two weeks. To 62 hours (see [[Habits]]). Seeded from [[Wants]].
*[[electronomads, promote lifestyle, individual importance, products, projects, creation]]
**[[NCP Alternative Use]] - meet Andy Kythreotis and talk about nature of proposals - 2 hours - DONE (2 hours)
** ElectroNomad - create a plan for ElectroNomad.org - 2 hours - DONE (3 hrs)
** ElectroNomad - draw some sketches for what EN.org is going to look like and how it will work - 5 hours - NOT DONE
*[[products, cashflow, stakes, consumers, creation, make people powerful]]
** [[fee-free micropayments]] - design the free/cheap peer-to-peer mobile app - 5 hours - HALF-DONE (1 hour)
*** did think about cloning the Natwest app as an example, but not sure I'm totally interested; wrote down app design in [[mobile bank transfer app]]
** [[fee-free micropayments]] - plan the public resource for documenting online banking API's - how will it look? how will people update the API's? how will people test the API's? will they test themselves? - 2 hours - DONE (2 hrs)
*[[enjoy life, good things, food, music, people, friends]]
** launch [[Grace coffee]] - 1 hour - NOT DONE
*** didn't make time to fix the problems remaining
** [[wiki-data]] - tweaks - 5 hours - DONE (10hrs 35mins)
** launch [[Jeroboams]] site - 15 hours - DONE (20hrs)
*** there are still some incomplete (which will need to be completed before Jeroboams want to start adding prices) features
** tweaks to [[BonnieParsons.com]] (Google Analytics, graphics) - 5 hours - NOT DONE
** design Daniel's site [[Violet Hill]] - 2 hours - NOT DONE
** get tickets for Latitude - 1 hour - NOT DONE
** sort White Heat + Sleigh Bells gig - 1 hour - DONE (1hr)
** [[wiki-data]] - bill Avox up to 10th March - 1 hour - DONE (1hr)
** [[BPSF]] - sort out IE6 compatibility - 2 hours - HALF-DONE (2hrs 5mins)
*** we tackled all the IE problems we could find; still some problems
*[[system, web projects, streamline, software, tools, different people]]
** continue to think about how DesignView can help with the practical challenges of me designing web pages - write a story, based on my/Josh's experience, of changing the design of a site - 2 hours - HALF-DONE
*** thought and wrote about it some more, didn't write the story
!!Thinking about [[Productively]]
A system to track a project only needs to be good enough to get you to the end of the project in control of the project e.g. if you get so far on a system and then change systems for a "last push", that is ok. I think.
!!Bonus points
[[Rewired State Culture hack-day]] - 10hrs
[[SweetSoft research]] - 9hrs 30mins
Meeting with [[Richard Marr]] - 2 hours
Visited [[The Troubadour]] and [[The Pavillion Café (Victoria Park)]]
Had Easter bank holiday entirely off!
As often with holidays, I think about how things are going and whether I know what I really want to be progressing towards. The Christmas holidays are special because they have New Year in them, which encourages me more than any other time to think about whether I'm happy with what I'm doing. Surprisingly, this year, it didn't even cross my mind to make New Year's resolutions, which I assume is because I'm always reviewing what I want to be doing via [[Productively]] anyway. Nevertheless, I did ponder some of the more abstract thoughts I've been having about how to achieve the things I want, until they became a bit more concrete.
The main thing that has floating around is how I can increase my capacity for doing work in a way that doesn't require employing people. This is the demand driving the idea of a collection of freelancers with mutually beneficial skills. It is in this collection that I intend to have the "ElectroNomad" name mean something. I think that I should be more definite with the freelancers, who I do know and that I would want to work with, about my plans to have us work on things.
A related subject is that of open projects, which I also want Electronomad to mean something in. I came to the conclusion a while ago that openness doesn't have to mean on public display, but I do think that I need to be public with a couple of my own projects in order to make the point about how it is possible in reality, rather than pumping out theory. It seems that the [[Anna Freud Centre]] project might be ideal for this, since it is connected to the NHS, which is a public body anyway (I should check this with Dickon). I'd like my website - electronomad.com - to have links to all the central project spaces for projects I'm working on.
This leads me on to another subject, that of collaboration tools. I feel like I'm in a position now where I could usefully turn what I do on a single TiddlyWiki (such as this one) into a tool that is hosted, multi-user and includes some shortcuts for getting things done quickly. I ought to work on this with Chris Dent and someone else who is not a TW-savvy person, but know the pains of distributed teams (Josh? Amit?).
I seem to have read a lot recently about how people in the rich world sort of have enough material goods and services, in general. They are looking for other things to give them satisfaction, such as doing meaningful work and being creative, or doing charitable things. I have come to the conclusion that the aim of being in the kind of collection that, I hope, Electronomad becomes associated with, is to make it possible to do the things you want to do, whatever they are. This could become possible because of the presence of more people with your interests at heart, who help you achieve things; because you end up doing more interesting paid work; because you're an individual not someone's employee.
I have been thinking about [[wiki-data]] and where that could and should be going next. I think there three areas I would want attention to be directed. I don't mean to present these in any particular priority. Firstly, working with a client / clients to create a case-study of an app built for the client's internal use on top of the wiki-data API. Secondly, improving the wiki-data coverage and comprehensiveness. I think this could be done by letting anyone contribute records and information, with the Avox-certified data something that lives somewhere else under a different product name (Ken, Adam and I have been discussing the pro's and con's of separating wiki-data and My Avox Data (MAD)). Thirdly, creating a case-study of an app built on wiki-data for public use (perhaps something done with Talis and RDF?).
I found yet another time with I could have really done with my yet-to-be-written bank transfer app. I think this project needs to take off, but it shouldn't divert my attention until wiki-data and BPSF are finished, or my involvement in them is reduced.
This period started on 18th November. The first fortnight was well planned, the second week-and-a-half less so.
The main achievement of the period was SweetSpot phase VIII, which took around 50% longer to do than we thought, even with conservative estimates. This could be because there was a lot of new stuff in the phase, including working with external systems. But it's definitely pushed us towards ways of saving time and making sure we're testing things. I'm looking forward to getting automated testing going - we've discovered Sauce Labs (and met the founder), which looks really helpful.
The J&J Fridays are going very well. We have updated the website, written two articles and designed business cards (which we've been getting quotes in for). So that's all very exciting. The writing is the most exciting part.
This is the first period where I'm ending it in the middle of a week. It's because I spent four days in Venice, on a holiday (no laptop!), and feel that this is an appropriate break. We are in-between SweetSpot phases, and there is some other stuff to be getting on with immediately - BPSF improvements, What On Earth improvements, Kat's site and the Malta Live brief (which we're collaborating with Ben from Digital Brand Creative on - also exciting).
----
From 1st - 10th November:
BonnieParsons.com - NOT DONE
Kat - HALF-DONE
People meetings - HALF-DONE
SweetSpot - DONE
WOE - NOT DONE
------
Fortnight beginning 18th October 2010. Up to 62 hours (see [[Habits]]). Seeded from [[Wants]].
* [[enjoy life, good things, food, music, people, friends]]
** Leeds trip - DONE
** present buying for G & J - DONE
** read Whoops! - 5 hrs - DONE (5hrs)
* [[products, cashflow, stakes, consumers, creation, make people powerful]]
** SweetSpot Phase VIII progress - 20hrs - DONE (30 hrs)
** [[Kathryn McMann]] - build the site - 10 hrs - NOT DONE
*** didn't make the time
* [[system, web projects, streamline, software, tools, different people]]
** AttentionTracker - make bill-points work as lower bounds for a period as well as upper bounds - 2hrs - NOT DONE
*** didn't make the time
** Set up intro meeting with Ben from Cube - 1hr - DONE (1hr)
** Set up meet with Paul Goodenough - NOT DONE
*** didn't make the time
** Set up coffee with Johanna Kollman - NOT DONE
*** didn't make the time
** Lunch with Sarb - NOT DONE
*** didn't make the time
* [[electronomads, promote lifestyle, individual importance, products, projects, creation]]
** End of year admin - tax, accounts - 1hr - DONE (1 hr)
** J&J - start the manifesto - 3hrs - DONE (3hrs)
** J&J - get WithJ&J.com set up as a WordPress Network - 3hrs - NOT DONE
*** didn't make the time to finish this off
Total estimate: 44hrs
Billable estimate: 30hrs
!Bonus points
* J&J - business cards
I gave myself a pretty slim list of things to do this week, mainly because I didn't start work until the Wednesday. The main priority was sorting out the wiki-data site, which was due to launch at the end of the week. We missed the deadline, which was a shame since an advertising campaign had been booked in a trade magazine from Monday 11th January.
I didn't do any BPSF work, as I was concentrating on wiki-data.
Extending the holiday over Bonnie's birthday was really fun and relaxing, definitely the right way to start the new year...
The meeting at Tager with Geoff Smith, Bell Pott's COO, went well it seemed. I was providing the "digital" angle, as per.
This next week, it's all about finishing the wiki-data site and putting work into the BPSF website.
-----
Week beginning 4th January, up to 31 hours (see [[Habits]]). Seeded from [[Wants]].
* [[I want to invest in startup businesses or art projects]] - 4 months (January)
** [[wiki-data]] - finish the re-design work with Amit and Josh (12 hrs) - HALF-DONE (26hrs 30mins)
** [[BPSF]] - help get the ball rolling for the second iteration, with Josh on Thursday (2 hrs) - NOT DONE
!!Events
Celebrating Bonnie's birthday on Monday and Tuesday, at Crazy Bear Beaconsfield then All Star Lanes for bowling/diner party. (2 days)
Tager meeting with Geoff Smith on Thursday morning (2 hrs)
This week, I did a load on the two major projects I'd allocated myself: ILGA and [[iPhone translation app]], which I'm happy about. I also managed to make the time to do most of the smaller projects. Overall, I think this has been the most successful week in terms of matching up what I wrote down as wanting to do and what I did. I managed some extra things too, which were fairly chunky (see "bonus points" at the bottom).
I've realised I have 5 weeks left until I'm going freelance. This means that my priorities now are going to be [[Finishing things off]], [[iPhone translation app]] and [[Setting up freelance contracts]].
I figured out how to do "OR" filters for gmail.
!Top Wants and Goals for week commencing 4th May 2009
!!1. [[I want to be able to go freelance]]
Top goals and projects (to 24 hours worth - see [[Habits]]):
# [[iPhone translation app]]
** Get to the point where I understand how to optimise photographs for scanning and whether tesseract training makes any difference - 9 hours
*** MOSTLY DONE - didn't do training, but learnt a lot more about [[optimizing tesseract]]
# [[Setting up freelance contracts]]
** Speak to Stephen Barris - 1 hour
*** DEFERRED - Spoke to Jeremy about aspirational departure timescale and agreed that we can talk to Stephen later on as the need for an outside contractor becomes more apparent
** Follow up with LShift - 1 hour
*** MOSTLY DONE - sent email, but no response yet
** Follow up wtih Tager - 1 hour
*** NOT DONE - no further concrete plan
# [[Finishing things off]] - new project, read tiddler for more detail
** [[ILGA]] - 10 hours
*** DONE
** [[Anna Freud Centre]] - 2 hours
*** DONE
!! Bonus points
* Made [[BonnieParsons.com]] over the weekend in preparation for Bonnie's Monday meeting with MoveYourFrame studio in Shoreditch
* Long blog post about the role of hacking in making new products and Yahoo! Open Hack London - http://jaybyjayfresh.com/2009/05/11/the-will-to-build/
This week I had a bit of a surprise visit to Amsterdam to go to the Maemo Summit 2009, which rather meant that I didn't spend much time doing anything else from Thursday night to Monday morning. I got a few hours in though. I think it's been a worthwhile thing to do though, as I know a lot more about Maemo and the amazing maemo.org community site (karma, social apps review process). And I have a N900 for six months.
This next week, I think the most important thing is to get the wiki-data.com site launched as soon as possible. This is because I really want to start advertising the developers' workshop I think is going to happen on the 6th/7th November. I plan to start off with a blog post about wiki-data.com, its aims and a request for anyone interested to come along to the November workshop.
I've set up http://wiki-data.fogbugz.com to use as the project tracker now that it is, in my opinion, too big to track in this document. I'm experimenting with FogBugz as an alternative to this document's hyperlinked micro-content style of writing.
-----
*[[OpenElectric]] - get site set up, hardware hackers spoken to, meeting with Charm lead arranged, figure out how to do technology feasibility study of intercepting signals from devices - HALF-DONE
** site is up, meeting with Ruth Rennie (Charm lead) arranged
*[[Avox Wiki-data]] - design, performance, launch, workshop - HALF-DONE
** performance much improved, not launched, still some tweaks necessary; workshop venue investigations started - £1500 a night for Guj's gallery, seems expensive!
*ChatLoop - widget version 1 for Thursday afternoon - HALF-DONE
**did some tests and a little bit of code
*OwnersDirect - finish advert - NOT DONE
*Developer/Bell Pottinger drinks Wednesday night - DONE
** Cefn Hoyle and Michael Mahemoff came to meet Sirwan and Erin from Bell Pottinger Sans Frontieres
*BT Open Source conference on Monday - DONE
*Pay people - DONE
*Get Glyn @ The Bell to try out the online beer ordering idea - HALF-DONE
** Arranged to go and speak to him Wednesday afternoon
* [[I want to do the little things that make other people happy that don't seem big or important to me]] - 2 months (November)
* [[I want my business to be so efficiently run that it takes me no time to administer]] - 2 months (November)
* [[I want to remember a dozen or so guitar tracks - spanish, jazz, songs - and be able to play them]] - 3 months (December)
* [[I want to create an effective model for running fair-reward open projects]] - 4 months (December)
* [[I want to invest in startup businesses or art projects]] - 4 months (January)
* [[I want to be convinced that I'm a fluent, good cook who brings regular pleasure to myself and others through food]] - 5 months (February)
* [[I want my business to be generating a few thousand a month in revenue that doesn't require any upkeep]] - 6 months (March)
* [[I want to have a surplus of cash of a few hundred pounds a month in my personal account]] - 6 months (March)
* [[I want to make people waste less time]] - 6 months (March)
* [[I want to create something that makes high-quality coffee-makers more prominent than current dominant coffee shops]] - 6 months (March)
!! Bonus points
[[Building your own tools (and the dry subject of project management)]]
[[Maemo]] - attended the Maemo Summit 2009
This week, I had intended to work on three main things and instead devoted the time for [[wiki-data]] to [[BPSF]], which means the BPSF project is coming along great, but I left Amit to work on wiki-data, which I didn't want to do. I will have to prioritise wiki-data next week to make sure we get the redesigns done by Christmas.
Tuesday was a bit of a wash-out, with a massive hangover from Colin's birthday making it hard to do much.
I went up to Leeds on Saturday morning for my Dad's 65th party, which was fun.
---------------
Week beginning 7th December 2009 - to 31 hours (see [[Habits]])
Pulling top-level from [[Wants]]
*[[I want to invest in startup businesses or art projects]] - 4 months (January)
** [[wiki-data]] - sort out a support agreement for updating wiki-data data and fixing bugs, both urgent and not - 2hrs - HALF DONE (30 mins)
*** starts with a call at 10am on Monday with Paul
**** passed the action onto Paul
*** speak to Chris to see if he'd be interested in taking on any support obligations
**** didn't do this
** [[wiki-data]] - sort out identified bugs - 4hrs - HALF DONE
*** IE problem with logged-in ajax_search
**** not done
*** caching
**** accidentally fixed through Chris' changes, need to re-examine uses of no-cache vs. etag
** [[wiki-data]] - make progress with re-design build so I feel confident we'll hit the two-week timescale - 6 hrs - NOT DONE
** [[BPSF]] - spike all the whizzy JavaScript features - 5hrs - DONE (14hrs 10mins)
*** did more than just spike JS features
*** we got about 16% of the way through (according to the fantastic [[estimator|http://spreadsheets.google.com/ccc?key=0AgQJ7FGUGIp_dHlYX3JUUGdOQlhSbHBZZU1nZTFmNUE&hl=en]])
** [[ProtectedCC]] - get the closed beta site up in time for Tuesday night - 6hrs - HALF DONE (7hrs 25mins)
*** nearly managed it, didn't put it up because forms didn't POST to anywhere - it's on http://beta.protected.cc
*** Marcella was helping with logo design
* [[I want my business to be so efficiently run that it takes me no time to administer]] - 2 months (November)
** [[yellowcar]] - Do my November expenses - 1hr - NOT DONE
!!Events
Launch48 follow-up on Tuesday night
Dad's 65th on Saturday night
This week I tried two new methods for [[Productively]]: writing goals instead of tasks in the [[Top Wants, Goals and Projects]] sections and maintaining a "New stuff" list.
I found the goal-setting quite fun and it certainly helped to focus my mind on the reason for doing the tasks in hand. In this week's example, I had set a goal to get a contract by the end of the week and had made a note about the highest priority tasks, as I saw it at the time, to achieve that goal. Whilst I failed to achieve the goal, most of the work I did this week contributed to getting contracts of //some// variety. I'm going to continue to experiment with this.
Tracking new stuff was good too - I think this format is much preferable to maintaining the gigantic [[Practical Projects]] list, as this adds time context to the things I'm working on. I might change [[Practical Projects]] to just show current stuff, with links to completed things; or I might replace it with an editorialised list of projects I want to show.
I did the first analysis of my time spent this week, for 1st-7th June. I was hoping this would be useful for making my estimates of time spent on [[Habits]] more accurate, but that has been made tricky because I've included more than one category in habits e.g. travel is included in many; also, I've split measured categories when naming habits e.g. sport is measured as a whole. I can go through the data again and adjust for these factors, but I didn't do that this week.
What I have been able to get a handle on is how much //total// time I've spent on habits - this pie chart shows how much time I have left after you take the habits out, which I could compare to my estimate of 38 hours worth - it turns out I have 31 hours - less than I thought:
<<slider chkTimePie [[Habitual vs. Non-habitual time spent]] "pie">>
Last point - having written the [[Top Wants, Goals and Projects]] list for next week, it seems that my hierarchy of things I'm writing about is something like: I have Wants, describing gut feelings e.g. [[I want to be able to go freelance]]; there are Themes under each Want, describing broad areas of things you could do to get what you Want e.g. [[Setting up freelance contracts]]; I have Goals that are tied to Themes e.g. "sorting out one definite contract by the end of the week"; I have the most important (as it seems as the time) tasks I want to do to achieve those goals e.g. "Talk to Matthias at LShift about a three-day-a-week contract", which I estimate the time for. I think "Projects" might be another name for "Themes", although I like Themes as it suggests something less concretely defined.
!Top goals and projects for week commencing 8th June 2009:
!![[I want to be able to go freelance]]
# [[Setting up freelance contracts]] - sorting out one definite contract by the end of the week - FAIL
** Talk to Matthias at LShift about a three-day-a-week contract - 2 hours - MOSTLY DONE
*** Mike has suggested I do the User Interface for feeds hub as well as being a customer with different needs to the BBC; now we're figuring out how much time I will give to this and how I would be paid
# [[Setting up freelance contracts]] - do some work with one group that will make them more likely to give me a contract in the future - SUCCESS (I think the work I did on the [[Polling app for Nokia music blog]] counts for this goal)
** Fix Dickon's pressing [[Anna Freud Centre]] project needs - 3 hours - MOSTLY DONE
*** I got back in touch and caught up but didn't create anything new
# [[Finishing things off]] - figure out what would be totally awesome to have on the portfolio - FAIL
** List the things that would be good to finish off and order them by priority (to include blog posts and projects) - 1 hour - NOT DONE
# [[Finishing things off]] - make sure I do what I can to make sure we do what we need to do for the ILGA project before we go to Brussels next week - SUCCESS
** ILGA - Tackle the items in ScrumWorks - 5 hours - DONE
# [[Transition to self-employed]] - get to the point where I can write about baselining my life - SUCCESS (I know how I spend my cash and time)
** Analyse the last week's diary to find out how I spend my time and compare it to my predicted [[Habits]] - 2 hours - DONE
!!Bonus points
Congratulations card for Andrew & Peter
[[Polling app for Nokia music blog]]
[[Set up a business bank account]]
!!New stuff
[[Flash content updater for www.laurarussoweddings.com]], [[Secret Seed Society]] - pitch, [[Fashion Site for Shaun Cunningham]], [[thelondonpaper]], [[webkit patch for Loic]], [[scientific research project using TiddlyWeb]], started talking to [[LimeBits]], met Jim Mellor from [[Hogarth/Cortex]]
!!Continuing to think about [[Productively]]
On Tuesday, had what feels like quite a good idea - in [[Top Wants, Goals and Projects]], write down actual goals under the Wants, rather than just tasks, or as I have done this week, a mix of goals and tasks. Then figure it out for yourself how to achieve the goals. It doesn't matter if you don't hit the goal, it is better to have written down the intent than not to have done, as the writing will put you in to a frame of mind where you are more likely to try.
Example goals I might try for: sell one t-shirt by the end of the week, get one contract by the end of the week; get a job through using this site as a portfolio site by the end of the week...
This week, two things seemed to dominate - trying to get wiki-data.com (see [[Avox Wiki-data]]) launched and the [[Launch48|Launch48 October 2009]] event, that took up most of my waking hours between Friday night and Sunday night.
For wiki-data.com, things got very close: the main barrier to launching is that the database is not future-proofed, as in we're planning to change its structure for Phase 2 to allow public and private fields to be served from the same database. It will also be a lot simpler in design and a new TiddlyWeb store will be written that speak to it. Getting this sorted is the highest priority for this next week. From the client-side point of view, the unsolved IE problems, the state drop-downs and getting the email sending working are the only problems left.
Other things that need to happen with [[Avox Wiki-data]] are the organising of the [[wiki-data workshop]] and the estimates of the [[phase 2 user stories|Avox Wiki-data phase 2 user stories]]. I think a sensible plan for organising the workshop starts with finding and talking to people who are interested. The phase 2 estimates can wait, I think, until Phase 1 is launched.
Launch48 went really well. It was very interesting and I met some great people. The idea - [[Protected.CC]] - turned into a simple, self-contained product, lots of people were interested in it and we are planning to take it forward. The follow-up event is December 8th.
On the subject of web design: I had a meeting with Phil from Grace and BenJam on Friday night, when we decided that a simple flyer-style front-page, advertising the location, the 100% "we are the best" guarantee and the weekly menu would be enough. Nick later pointed out that simply advertising the presence of the Michelin-starred chef would bring in the crowds. I also decided that I should arrange to go into [[Violet Hill Studio]] to see Daniel and get some content for his personal site.
This next week, I think completing this sentence is really important - "Wiki-data.com is cool because..." - this is to do with speaking to people who I think would be interested in company information, creating ideas for using the data and seeing if they can get involved with the [[wiki-data workshop]].
There is also ChatLoop - Nick helped me make some images showing how things would look for their meeting with SalesForce on Thursday, which went down well, but now I need to find some time to make the simple widget. I don't want the momentum to disappear on this project, as I think it is a great trial of a model for investing time and skills in a startup.
------
(No more than 31 hours - see [[Habits]])
Hours recorded here: 34 hours
Listed is the most important thing to advance each want. Maybe more than one. They're not all vital to this week; I'll indicate this somehow.
* [[I want to do the little things that make other people happy that don't seem big or important to me]] - 2 months (November)
** Finish OwnersDirect advert - 2 hours - NOT DONE
*** I didn't make the time
* [[I want my business to be so efficiently run that it takes me no time to administer]] - 2 months (November)
** Set up the Google Docs version of my accounts that Jayne sent; formulate an idea of what can be automated and to what ends (Jayne sent me a list of the things she does) - 3 hours - NOT DONE
*** I didn't make the time
* [[I want to remember a dozen or so guitar tracks - spanish, jazz, songs - and be able to play them]] - 3 months (December)
** Find a Spanish jam session and go along - 5 hours - NOT DONE
*** I didn't make the time to look
* [[I want to create an effective model for running fair-reward open projects]] - 4 months (December)
** The OpenElectric project is an example; make sure we explain on the site how people can get involved and what it means; this will culminate in a blog post on the subject at some point - 3 hours - DONE DIFFERENTLY (1 hour)
*** [[Call with Ruth Rennie about the Charm Project]] - this gave me some leads to follow; I should put some information on the wiki
** [[Avox Wiki-data]] is an example too, and I should populate http://wiki-data.fogbugz.com with all the stuff for phase 2 as well as the improvements we want to make to phase 1 - 2 hours - DONE DIFFERENTLY (2 hours)
*** I decided to use http://wiki-data.lighthouseapp.com instead of FogBugz, which wouldn't let me publish projects
* [[I want to invest in startup businesses or art projects]] - 4 months (January)
** [[Avox Wiki-data]] is effectively a startup and I need to make sure I get the developer community interested and make a success out of the November workshop
*** Find and book a venue - 2 hours - HALF-DONE
**** I spoke to Guj, Sarah and Sophie at Calvert 22 - they quoted around £1000 for the hire of the downstairs from 5:30pm Sunday 8th Nov - Monday 9th (2 hours)
*** Write an interesting blog post about wiki-data and advertising the workshop - 2 hours - NOT DONE
**** Doesn't make sense to do this until the site is launched and I can just speak to people
*** Find individuals to speak to about wiki-data and speak to them with a view to getting more participation in the wiki-data workshop - 3 hours - DONE (3 hours)
**** I now have a few leads - see [[wiki-data workshop]] for the information; met Dan on Thursday night as part of this
** [[ChatLoop]] desktop widget version 1 - 5 hours - DONE DIFFERENTLY (1 hour)
*** Did some graphics with Nick on Thursday so they'd have something to show
** [[ChatLoop]] is an example of the 'credit-note' business model I'm experimenting with; after we've achieved something, I'll write about the model - 3 hours - NOT DONE
*** Haven't done the thing yet
* [[I want to be convinced that I'm a fluent, good cook who brings regular pleasure to myself and others through food]] - 5 months (February)
** Organise to have some people over for dinner; do some cooking - NOT DONE
*** Didn't make the time
* [[I want my business to be generating a few thousand a month in revenue that doesn't require any upkeep]] - 6 months (March)
** [[ChatLoop]] is a potential source of income (see above)
** the pre-order online idea I'm trying to get Glyn @ The Bell to try is another - 10 hours - NOT DONE
*** Intended to go see Glyn to work out what we're doing, did other things instead
* [[I want to have a surplus of cash of a few hundred pounds a month in my personal account]] - 6 months (March)
** I can be more definitive with moving some expenditure into my business and thus saving the tax
***phone - 1 hour - NOT DONE
***travel - 1 hour - NOT DONE
***medical insurance - 2 hours - NOT DONE
***pension - 1 hour - DONE
**** I figured out that my pension is locked up in Standard Life until I retire because I didn't pay into it
* [[I want to make people waste less time]] - 6 months (March)
** Developing [[Productively]] is a good thing for this; I should figure out and write about how to use the wants-based planning properly and link to all my explanations of ways to be productive in [[Productively]] - 3 hours - NOT DONE
*** Although see this week's "Thinking about Productively"
* [[I want to create something that makes high-quality coffee-makers more prominent than current dominant coffee shops]] - 6 months (March)
** This seems less important right now
*** I told a number of people about de Koffie Salon in Amsterdam, which seems to have the ideal setup for the ElectroNomad
!Bonus points
#[[Protected.CC]] - at the [[Launch48|http://www.launch48.com]] event - 24 hours
!Thinking about Productively
Using the format above to write about what I'm going to do and what I've done is a different experience to the task-list shaped approach I was taking before. This feels a bit more wide-angle and it's interesting how projects are appearing under more than one want. I think it is worth continuing to try this narrative-based approach, as I can see it being more useful to read over in the future (not that I've done much reading over previous reviews, except as I'm writing the next plan).
This week, I don't feel like I committed much time to tying-up the paid-work options available to me, instead thinking about portfolio-expanding examples of things. Unfortunately, I didn't finish one of those either, although I have set myself up for an exciting time next week with the [[Oyster Card payments]] projects; also, put out another blog post about Smart, which was picked up again by Joyent staff and re-tweeted. There could be something new in the works with Joyent, waiting for details from @bbogens.
This coming week, I need to get firmer with myself about finding out what my income-earning potential is over the next four weeks on projects like [[IMP]], [[Streams]], [[Quelloja]]... I also should make good of my promise (to myself) to build the Twitter bot for the [[Notion Twitter/SMS]] project. I also plan to make more progress on the TiddlyWebConfigurator I started with Fred on Friday.
There is also the small matter of [[ILGA]], which launches next week... I'd like to help in the last stages, probably with the small (graphical?) fixes that will make it look more impressive. I need to make sure I have flights sorted (which I think Jeremy is organising).
I could book my dConstruct ticket too. It might not be a bad idea to figure out what other events I'd like to go to this year; book my Sicily flights; book my Bulgaria (New Year) flights; sort out PubSub for a night in the next two weeks.
-----
To 31 hours, see [[Habits]]
Holiday Monday and Tuesday - 9 hours ((31 / 7) * 2 rounded up) - DONE
Leaving 22 hours for...
![[I want to not have to worry about paying the rent]]
#[[Setting up freelance contracts]] - figure out how long it will be until I'll be working for LShift again - 1 hour - NOT DONE
#[[Setting up freelance contracts]] - figure out how long in the future the funding is likely to be coming from Dickon, by the end of the week - 1 hour - NOT DONE
#[[Setting up freelance contracts]] - get in touch with Paul Bacchus and agree with him which the best 3 use cases for the prototype would be, by the end of the week - 2 hours - DONE
** Reviewed the grant application and prototype description and responded; awaiting response
![[I want to develop my portfolio into something that is strong enough to get work from]]
#[[Finishing things off]] - arrange a hack-day with Chris Adams on the [[Oyster Card payments]] idea - 1 hour - DONE
#[[Finishing things off]] - spend a day helping out on the [[ILGA]] project - 8 hours - DONE
#[[Finishing things off]] - spend an afternoon hacking together a visual editor for creating TiddlyWeb config JSON - 6 hours - DONE
![[I want a balance between appreciating what other have created and creating things myself (aka sort your life out)]]
#[[Reading more]] - finish off "The Soul of a New Machine" - 3 hours - DONE (within next week)
!Bonus points
#[[Invoice Tracker]] - uses Google Spreadsheets and keeps a record of my outstanding invoices
#[[Notes on migrating from AppJet to Smart Platform]] - migrated my Yahoo! Pipes / Tarpipe proxy to Smart Platform from AppJet - and blog post at http://tinyurl.com/mhwxgz
#Made the [[Cash Spent form]] and [[Time Spent form]]
#Fixed up the [[Tarpipe / Yahoo! Pipes proxy]]
I'm writing this on Thursday of the following week, which means my ability to retrospect is a little limited... This last week, I spent quite a long time working on wiki-data to make sure it was launched on Friday, which did happen. In truth, most of the work had been done by Thursday. I caught "Endgame" at the Duchess Theatre on Wednesday night, which was a bit disappointing compared to Complicite's other productions I've seen (Shun Kin and the one about the mathematicians).
On Saturday, we had a [[Protected.CC]] meeting, which was quite productive - coming up with the idea for a site to gather tales of woe about copyright theft, and agreeing that a closed beta could start now (I said I'd send out a plan of what the closed beta should consist of).
The Halloween party at Ruth and Richard's at the weekend was rather good fun.
This next week, I'm planning my focus will be on getting apps built on wiki-data and on getting the ChatLoop widget to a working state.
----------
To 31 hours (see [[Habits]])
* [[I want to invest in startup businesses or art projects]] - 4 months (January)
** [[Avox Wiki-data phase 1]] - launch this please - 15 hours - DONE (24hrs 10mins)
** [[Avox Wiki-data phase 1]] - get a solid idea of what can be achieved in terms of building apps on the wiki-data platform before FIMA (10th Nov) - 6 hours - HALF-DONE (2hrs)
** [[ChatLoop]] - finish the basic widget - 5 hours - NOT DONE
** Spend the day with Prem on Thursday - 6 hours - NOT DONE
*** postponed to Monday
!Bonus points
[[Call with Edd McArdle]]
[[Instrumenting JavaScript with JSCoverage]]
* [[I want to do the little things that make other people happy that don't seem big or important to me]] - 2 months (November)
* [[I want my business to be so efficiently run that it takes me no time to administer]] - 2 months (November)
* [[I want to remember a dozen or so guitar tracks - spanish, jazz, songs - and be able to play them]] - 3 months (December)
* [[I want to create an effective model for running fair-reward open projects]] - 4 months (December)
* [[I want to be convinced that I'm a fluent, good cook who brings regular pleasure to myself and others through food]] - 5 months (February)
* [[I want my business to be generating a few thousand a month in revenue that doesn't require any upkeep]] - 6 months (March)
* [[I want to have a surplus of cash of a few hundred pounds a month in my personal account]] - 6 months (March)
* [[I want to create something that makes high-quality coffee-makers more prominent than current dominant coffee shops]] - 6 months (March)
This was quite a good week for the [[BPSF]] project - we got the project development off to a good start, completing the first iteration pretty much as we described it. The [[wiki-data]] project suffered slightly for the time I put into BPSF plus Amit having less time in Columbia than he was expecting. We asked for and received an extension for the re-design until the end of the first week in January.
I got my pre-Christmas chores done in time and went on holiday for two and a bit weeks.
-----
Week beginning 14th December 2009 - to 31 hours (see [[Habits]])
Pulling top-level from [[Wants]]
*[[I want to invest in startup businesses or art projects]] - 4 months (January)
** [[wiki-data]] - help get the build of the redesigns as close to done as possible - 8hrs - HALF-DONE (4 hrs)
*** Got Josh involved, pretty much completed the common elements of a page and the results table
** [[BPSF]] - make sure we finish what we have said we will for the first phase, by Friday - 8hrs - DONE (11hrs)
* [[I want my business to be so efficiently run that it takes me no time to administer]] - 2 months (November)
** [[yellowcar]] - Do my November expenses - 1hr - DONE (1 hr)
*[[I want to do the little things that make other people happy that don't seem big or important to me]] - 2 months (November)
** Finish Christmas shopping - 2hrs - DONE (4 hrs)
** Get a haircut - 2hrs - DONE (2 hrs)
!!Events
Lunch with Alex V, Wednesday
Josh & Marc Xmas do, Wednesday night
Tager / BP meet, Thursday morning
Redmonk / Current Cost / PubSub Xmas party, Thursday night
Petticoat Xmas party, Thursday night
Xmas dinner in Stanmore with Bon and family, Friday night
I did the Great North Run this week - that was great. I managed 1:51:31, which beats my previous personal best of 1:59:57 by a good margin. Will be aiming for 1:42 or something next year...
By the time I got on the train to go up to Newcastle on Friday, I'd sent out an Avox email saying we'd pretty much done Phase 1, apart from a few small gaps. I didn't have to work on it over the weekend. I think this is a good success.
Last week, I decided to abandon the sticking to the 31 hours for allotting what I was going to do that week (probably because things had built up, but also because I'd wanted to devote so much time to the Avox project). I estimated 47 hours worth of stuff; I completed an estimated 57 hours of stuff. I don't really know whether this means my capacity estimate is way too low, but it does suggest a further experiment with ignoring the 31 hours cap and allotting what I think I could do. This precise measurement thing is complicated by the fact that I don't track exactly how long I spend on things (since I'm not measuring that in principle) - I'm trying to get an alignment between what I say I can do in a week and what I end up being able to do. Having said that, I now remember that I'm trying to track how accurate my predictions are, so perhaps I'll make more of an effort to track time spent on things.
In the spirit of the above, I've gone back and measured more carefully how long I spent on things (thankfully, I record time spent on chunky stuff) and come up with a total of 43 hours 35 mins. The main thing to note is that I estimated 31 hours for the Avox and did what I did in 19 hours (although some bits are still missing that will take a few hours).
This week, I'm spending a day in Brighton with Prem to help him with some JavaScript stuff and meet some people who work at his co-working space; I'm hoping to spend an afternoon (or morning) with Tager to get them sorted with their finance visualisation thing; I'm going on a date with Bonnie and her cousin (Bonnie's term, not mine) on Saturday evening; Josh, Ben and I are booked in for Saturday day to do some experimental WHO web pages.
-----
This week, I'm going to ignore the 31 hour limit and see what happens.
To 31 hours - see [[Habits]]
![[I want to make people happy]]
#[[Avox Wiki-data]] - finish Phase 1 work described in [[this estimate|Avox Wiki-data: estimate of development gaps to V1 launch]] - 31 hours - DONE (19 hours)
** A few things left, but the phase has been drawn to an end satisfactorily
#[[Ripplz]] - make a mapping of photos to a wave-height (even if only grayscale/monochrome) - 3 hours - NOT DONE
** Had a go with Josh looking up rgb->hsl conversion for about 30 mins when drunk
#[[Baselining my life]] - post part 2, time - 3 hours - NOT DONE
![[I want to develop the small business side of my business]]
#[[Paul Dyson]] - meet on Thursday to hear about new internet venture - 2 hours - DONE (2 hours)
** Have scheduled another meeting next week to introduce me to their system and design a desktop widget
** Need to gen up on how/whether Adobe Air can run a web standards app and make desktop notifications
#[[Violet Hill Studio]] - first meeting on Monday about making their website - 1 hour - DONE (1 hour)
** Waiting for Daniel to send me their list of stuff they want on the site; ought to email him to ask for it
#[[Anna Freud Centre]] - sort out their client note printing, saving problems and TiddlyWeb bag setup; Wednesday - 7 hours - DONE (5hrs 35mins)
** I got their saving problems sorted (I hope) and set them up for use by multiple people with different bags to save into
!Bonus points
#[[A short jog with Eddie Izzard|http://jaybyjayfresh.com/2009/09/16/a-short-jog-with-eddie-izzard/]] - blog post showing videos from Tuesday's 10-mile run supporting Eddie Izzard - 4 hours
#[[WHO]] - Nice long meetup, drinks and grilling Clive - 3 hours
#[[Great North Run]] - took up Saturday and Sunday - 9 hours (31/7 *2)
This week was a bit more of a chilled week, with a visit to the V&A on Monday and some meetings with digital agencies to talk about helping each other out. The BPSF project got to the point where my input is pretty much done with, the focus is now on getting the content together. I picked up some of the backlog this week, getting [[Dad's IT problems]] (the remaining ones) sorted as much as I could and starting the new [[BonnieParsons.com]].
I thought more about where interns could come from, what they'd do, how I'd support them, which makes me think more generally about how to expand. I've been pursuing the meet-people approach and telling them about the Electronomad model (although I don't use that term often) of working together while remaining independent.
This next week, I'm hoping to be working on the [[Jeroboams]] project, which Josh and I have been quoting for. I will also take the time to review my [[Wants]].
-----
Week beginning 15th February, seeded from [[Wants]]. To 31 hours - see [[Habits]].
!![[I want to create an effective model for running fair-reward open projects]] - 4 months (December)
* Meeting with LondonCreative, Monday 3:30pm, to discuss possible collaborations over advertising/video - 2 hours - DONE
* Meeting with WeAreSuburb (date undecided), to discuss same - 2 hours - DONE
!![[I want to invest in startup businesses or art projects]] - 4 months (January)
* [[BPSF]] - get site finished in time for final review meeting (date undecided) - 8 hours - DONE (6hrs 35mins)
* [[BonnieParsons.com]] - get site built in static - 5 hours - HALF-DONE (2 hrs 30mins)
!Events
Decode @ V&A with Bonnie, Monday
!Bonus points
* [[greenwoodservices.co.uk]]
This week I went freelance! thus achieving [[my first Want|I want to be able to go freelance]] since starting [[Productively]]. So that's cool. It was a good week for doing what I'd planned too. I don't know whether that's due to the fact that there was ''one main thing I focussed on'' or just that I was, in a sense, a captive audience - much of my planned stuff was made up of the trip to Brussels to work on the ILGA project. Perhaps focussing on one main thing is something to play with.
For this coming week, I think it's all about securing some work with LShift and planning what the speculative things I'm going to do are. I think this latter thing starts with figuring out what are the best things to do that are covered by [[Finishing things off]]. I'll also check in with Dickon to firm up plans for working on the [[Anna Freud Centre]] project - I spoke to him at the weekend (at Jeremy's party) and he seemed keen to start soon.
Also, I'm working for my Dad for three days this week to help him with his IT problems.
I need a new Want! Looking back over [[Wants]], I can see that what used to be my biggest wants aren't really anymore. I think I will have to have a think and come up with a new set this week; this will be good for [[Productively]], as I've been working with the same system since April - I'll be able to see how I'd create the foundations now.
!! Top goals and projects for week beginning 15th June 2009 (to 31 hours worth - see Habits):
!!! [[I want to be able to go freelance]] - WOOHOO!
# [[Setting up freelance contracts]] - secure a contract with LShift by the end of the week - DONE (on following Monday)
** Write a response to MikeB's recent email asking for a more detailed proposal - 2 hours - DONE
*** Organised a meeting for Monday morning at 10am
**** Agreed on 5 days work over 2 weeks to lead the development of an interface prototype
# [[Finishing things off]] - get ILGA website working with the Global Directory - DONE
** Three days work in Brussels to achieve this - 24 hours
# [[Setting up freelance contracts]] - complete the [[Polling app for Nokia music blog]] project by Tuesday
** Address Ben's list of improvements, agree what to make and make it - 4 hours - DONE
# [[Setting up freelance contracts]] - get Dickon able to publish client notes from IMP
** Add client note publishing to the [[Anna Freud Centre]] project - 1 hour
*** Had mail exchange with Dickon and made prioritised plan of things to do
!! New stuff
[[Streams interface for LShift]]
[[Context switching iPhone app]] - an idea
This week was pretty well mapped out for me, in terms of what I do on which days - Monday, holiday; Tuesday, Prem; Wednesday, Amit; Thursday, Tager; Friday, Full Frontal. It seemed like quite an effective way to get things organised, although it did mean I didn't put much contiguous time onto the wiki-data problem I'd set myself. As I've agreed to the deadline of the coming Friday, I'll have to be very careful not to assign myself much else for that week.
Having said that, I have agreed to help Josh with the [[BPSF]] project, so I'll mainly limit myself to those two things.
I'd also like to do a bit of Christmas shopping this week...
-----
Week beginning 16th November 2009. Capped at 31 hours (see [[Habits]]).
* [[I want to invest in startup businesses or art projects]] - 4 months (January)
** [[wiki-data]] - Figure out with Chris and Greg the path we take for supporting client access to wiki-data records (4 hours) - NOT DONE
** [[wiki-data]] - Get started on the logins for full-field access and interface to support that (4 hours) - DONE
*** "getting started" involved trying to get my machine working with the MySQL setup - a big fail. Hoping this will be remedied next week when I install the 10.4 SDK from the Snow Leopard DVD (which Tony has)
** [[wiki-data]] - Get started, with Amit, on cutting his designs into pages (4 hours) - DONE (3hrs 15mins)
*** We turned the results page into a semi-styled HTML page
** [[ChatLoop]] - Prem is coming to visit on Tuesday to help me with the ChatLoop widget (6 hours) - DONE (6 hrs)
*** We created a login step where people can add their details instead of them being hard-coded to mine
* [[I want to create an effective model for running fair-reward open projects]] - 4 months (December)
** Finish [[Why booking time works for me]] and post (1 hour) - HALF-DONE (1 hour)
*** Feels almost finished. Renamed to [[Why time booking isn't just for grown-ups]].
!Bonus points
#Helping Tager with the pitch for [[BPSF]] website
** meeting on Monday - 2 hours
** half-day Thursday afternoon
** 3 hours on Saturday
!Events
Holiday on Monday (6 hours)
Full Frontal Conference on Friday (6 hours)
------
* [[I want to do the little things that make other people happy that don't seem big or important to me]] - 2 months (November)
* [[I want my business to be so efficiently run that it takes me no time to administer]] - 2 months (November)
* [[I want to remember a dozen or so guitar tracks - spanish, jazz, songs - and be able to play them]] - 3 months (December)
* [[I want to be convinced that I'm a fluent, good cook who brings regular pleasure to myself and others through food]] - 5 months (February)
* [[I want my business to be generating a few thousand a month in revenue that doesn't require any upkeep]] - 6 months (March)
* [[I want to have a surplus of cash of a few hundred pounds a month in my personal account]] - 6 months (March)
* [[I want to create something that makes high-quality coffee-makers more prominent than current dominant coffee shops]] - 6 months (March)
Last week, I completely forgot to account for any development time on [[Avox Wiki-data]] and then I spent 37hrs 50mins working on it, which pretty much precluded working on anything else...
However, I did spend the day on Friday working at the [[Anna Freud Centre]], as I'd promised Dickon last week I would.
This week, I'm hoping to finish off the first phase of that work and get the site launched. I intend to spend as much of the rest of the time as possible getting people to get involved in using the platform. There's only two weeks left until the date I'd intended to hold the workshop on.
I'd like to spend some time working on the [[ChatLoop]] widget and suggested to Paul and Stuart that Tuesday and Wednesday I'd be able to grab a few hours.
I'm planning to do a day in Brighton with Prem on Thursday and Thursday evening with Josh working on [[Ripplz]]. I'm off to the theatre to see Complicite in Endgame on Wednesday, which is very exciting, there's climbing on Tuesday and hopefully a Noisettes gig tonight (Monday).
----
To 31 hours (see [[Habits]])
* [[I want to invest in startup businesses or art projects]] - 4 months (January)
** [[Avox Wiki-data]] - it's all about workshop and v1 launch; less worried about a venue right now, as I'm unclear about what the workshop is going to look like
*** Get a good idea of who all the important-to-speak-to people are for this and speak to them - 5 hours - HALF-DONE
**** hit a dead-end with @iand as he's away for a week; sent a message to @libbymiller, waiting for response; Andrew Back is going to introduce me to a couple of his friends who are quite into data
*** Get some initial ideas for how to use the data - 3 hours - NOT DONE
** [[ChatLoop]] - widget v1 - 5 hours - NOT DONE
** [[Protected.CC]]
*** help collate information and set up collaborative working environment - 2 hours - NOT DONE
*** organise architectural review for next week - 1 hour - NOT DONE
* [[I want to be convinced that I'm a fluent, good cook who brings regular pleasure to myself and others through food]] - 5 months (February)
** Cook for Bonnie - 5 hours - NOT DONE
*** we went to Pret instead
* [[I want my business to be generating a few thousand a month in revenue that doesn't require any upkeep]] - 6 months (March)
** Agree a form of recompense for [[ChatLoop]] - 1 hour - NOT DONE
* [[I want to have a surplus of cash of a few hundred pounds a month in my personal account]] - 6 months (March)
** Sort out travel card - 1 hour - NOT DONE
* [[I want to create an effective model for running fair-reward open projects]] - 4 months (December)
** OpenElectric - it's all about figuring out what the market is like now, in partnership with Dan and Julie; I should get the info from [[Call with Ruth Rennie about the Charm Project]] onto the wiki and make sure there is some investigation going on into the competitors (2 hours) - NOT DONE
*** Andrew Back gave me some ideas about using radio receivers to look for signals on unlicensed spectra using known protocols; he also suggested that device manufacturers might be interested in a community-led effort to web-ise the usage data and that I should get in touch
* [[I want to remember a dozen or so guitar tracks - spanish, jazz, songs - and be able to play them]] - 3 months (December)
** Find Spanish jam session
* [[I want to do the little things that make other people happy that don't seem big or important to me]] - 2 months (November)
** OwnersDirect advert
* [[I want my business to be so efficiently run that it takes me no time to administer]] - 2 months (November)
** Google Spreadsheet-up my accounts
* [[I want to create an effective model for running fair-reward open projects]] - 4 months (December)
** Think I'll leave this one this week, but keep an eye out for opportunities in [[Protected.CC]]
* [[I want to create something that makes high-quality coffee-makers more prominent than current dominant coffee shops]] - 6 months (March)
** leave this one for this week
!Bonus points
[[Avox Wiki-data phase 1]] - 37hrs 50mins
[[Anna Freud Centre]] - 5hrs 15mins
[[Call with Aden Davies]] - 50mins
[[Improving this lab book]]
This was the first week of using this system. I found the highlighting of top priorities very helpful for making me feel like the amount of things I had to do was manageable. When I met Dan on Thursday, we talked about my reassessment of my priorities and the effect this had on my contribution to Geekmap, in that it doesn't fit with the need to be finding projects that I can take some payment from.
In terms of the structure of this process, I'm not sure having 3 goals with only 1 top project is the right structure. This week I'm going to limit to 3 projects, spread across whatever number of goals.
Top Wants and Goals for week commencing 20th April 2009
----------
!1. [[I want a balance between appreciating what other have created and creating things myself (aka sort your life out)]]
TOP 3 GOALS:
# To make using Facebook to stay in touch with more people a habit so I have more opportunity for appreciating a diverse range of things
** Top project: [[Using Facebook more to keep in touch with people]] - 4 hours a week
*** FAIL: I think I managed maybe 30 mins at one point. I'm not sure 4 hours is really necessary, maybe more like 2 hours.
# To get a better handle on my finances so I can understand the financial boundaries of enjoying a diverse range of things
** Top project: [[Analyse current spending]] - 5 hours
*** FAIL: I think I really need to do this.
# To make cooking for people once a week a habit so I can learn more recipes and make dining out less of a habit
** Top project: [[Make cooking once a week a habit]] - 5 hours a week
*** FAIL: annoyingly, didn't manage to make the time.
!2. [[I want to be able to go freelance]]
TOP 3 GOALS:
# Find a suitable product that could provide me with an income that has the potential to be automated
** Top project: [[iPhone translation app]] - 100 hours
*** SUCCESS: not 100 hours... ;) Dan is now involved, and is doing plenty such as putting tesseract on the iPhone simulator; we've made progress with defining the interface, finding a good use scenario, trying out OCR libraries and talking to owners of hosted services
# See whether [[ILGA]] and [[Anna Freud Centre]] can be taken on as contracts
** A task: speak to the people connected to the projects - 4 hours
*** HALF SUCCESS: spoke to Dickon about becoming self-employed and had a good reaction. Didn't broach the subject with Stephen. Also spoke to Alexis (now at LShift), who suggested to Matthias that I had an interview; and to Saq, who suggested we speak about what we could do at some point in the future (we're going to Istanbul together on the 8th May, which seems convenient.
# See what level of income I can get from Theme
** A task: figure out and size the areas where ongoing (or one-off) contributions could be paid for - 3 hours
*** SUCCESS: Had a conversation with Josh where he suggested I increase my involvement with Tager; I'm already involved with the [[Attitude Audit Tool]] and providing some mobile apps advice. Should clarify further.
This week I seem to be getting worse at completing the things I say I'll complete. I think this has something to do with the lack of directly paid work, although I am surprised I didn't feel more motivated to do more on the [[Notion Twitter/SMS]] robot.
The biggest thing diverting my attention this week was getting [[Build your own TwitPic]] working. I think this is really significant. As it happens, I intend to use most of it (the method for watching Twitter) in the [[Notion Twitter/SMS]] robot, and it has been a massive part of what I've wanted to do for a while. I think I've shown (to my satisfaction) that I can make a non-trivial application without writing code.
---------
To 31 hours, see [[Habits]]
![[I want to not have to worry about paying the rent]]
#[[Notion Twitter/SMS]] - build a prototypical bot by the end of the week - 6 hours - NOT DONE
** Made a start, not finished
#[[Setting up freelance contracts]] - figure out how long it will be until I'll be working for LShift again - 1 hour - NOT DONE
** Didn't speak to them, although will be seeing them on Friday at PubSub event; I've booked us in at the Water Poet on Friday from 3pm
#[[Setting up freelance contracts]] - figure out how long in the future the funding is likely to be coming from Dickon, by the end of the week - 1 hour - NOT DONE
#[[Setting up freelance contracts]] - get in touch with Paul Bacchus and agree with him what I'd be developing for free, by the end of the week - 1 hour - NOT DONE
** Rather than confirm anything specific, I have had one exchange where we discussed the timescale of the project - he is expecting to start funding it in October; I am not clear how much he wants to be done by then
![[I want to develop my portfolio into something that is strong enough to get work from]]
#[[Oyster Card payments]] - figure out how to read Oyster-cards at the hack-day with Chris Adams - 3 hours - NOT DONE
** Chris couldn't make it, which didn't help; I met @jonty, who has offered to provide a RFID reader for £10
#[[ScreenScraping HSBC website]] - get a prototype working before the hack-day on Thursday - 6 hours - NOT DONE
** Made a start, not finished
#[[Finishing things off]] - spend some time helping out debugging IE problems on the [[ILGA]] project - 4 hours - NOT DONE
** I did help out a bit, but not as much as I'd intended to
#[[Finishing things off]] - spend an afternoon developing the visual editor for configuring TiddlyWeb - 3 hours - DONE
![[I want a balance between appreciating what other have created and creating things myself (aka sort your life out)]]
#[[Create a nice living environment]] - strip the new desk and install in room - 4 hours - DONE
#[[Remembering birthdays]] - go to Sandeep's birthday party on Thursday night - 3 hours (in habit [[Hanging out with friends]]) - DONE
#[[Sorting holidays in advance]] - book flights to Sicily - 30mins - DONE
#[[Sorting holidays in advance]] - book flights to New Year @ Bulgaria - 30mins - DONE
!Thinking about [[Productively]]
I'm still thinking about the structure of the weekly plan. It's sometimes useful to think of the items under the Wants as generic Themes, as I've mentioned before, such as [[Setting up freelance contracts]] and sometimes not (generally when it's hard to think of a theme to tie a project to). I think I'm going to experiment with being a bit looser about whether I put themes or projects down as the items - I've noticed with the weekly review that the general introspection about whether I'm doing the right thing is getting written down there (and that was part of the reason to limit myself to pre-selected themes (which, in any case, I was breaking by creating new themes when I didn't have a suitable one)). I guess that the plan should make it easy to get an overview of what I'm doing and why.
!Bonus points
# [[Build your own TwitPic]] - got it working! This is good because it's the model for the eyes of a Twitter bot
** thinking about making a video of how to make a DIYTwitPic
# Wrote [[ScrumptiousPlugin]] to add commenting to permaviews of this workbook
** @mahemoff appreciated this and tweeted about it
#I realised [[yoga|Starting yoga]] is now a habit, which is something I have wanted for a while
!New stuff
Started [[Ripplz]] with Josh
** much planning and figuring out the basics; keeping notes in this workbook prompted Josh to read lots of the workbook and comment about it, which I was pleased about
This week, I estimated 35 hours of stuff; I recorded 40 hours. A big chunk (10 hours) of this was a contiguous block of relaxing time on Sunday, which perhaps shouldn't be included as it is a fairly habitual thing. Taking this into account, I can see that I am likely to spend around 30 hours a week doing "new stuff". Anyway, based on my notes during the week, I think I'm going to call uncapping my plan's time limit a failure; this time, I'll experiment with only putting the most important stuff down and trying to stay way below the limit. This is to see if it helps with the creativity-stifling I am worried is going on.
I'm also going to do a big refresh of my [[Wants]] this next week, since I suspect that I've become short-sighted in my weekly plans, rather than a more desirable long-term want-focussed thing.
-------
Ignoring 31 hours limit this week.
![[I want to not have to worry about paying the rent]]
#[[Avox Wiki-data]] - send out invoice up to 10th September - 1 hour - DONE (30 minutes)
![[I want to make people happy]]
#Spend a day with Prem at his co-lo spot in Hove, on Tuesday - 7 hours - DONE (6 hours)
![[I want to develop the small business side of my business]]
#[[Visualising finances for Tager]] - spend Wednesday morning sorting their finance visualisations to make them useful - 3 hours - NOT DONE
** Postponed to next week
#[[Violet Hill Studio]] - have a look round the studio with BenJam to see if we need to hire photo equipment - 2 hours - NOT DONE
** Reducing to "organise to do this" - DONE (outside the week) - 30 mins
#[[Grace coffee]] - have a photo-shoot with BenJam to get more photos for the Grace website - 2 hours - NOT DONE
** Reducing to "organise to do this" - NOT DONE
#Talk to people building Arduino electricity monitors to see if any of them are thinking about web-broadcast possibilties - 1 hour - NOT DONE
** Have been chatting to people, haven't structured this yet
![[I want to develop my portfolio into something that is strong enough to get work from]]
#[[WHO]] - spend Saturday day with Josh & Ben to get 3 experimental webpages together - 5 hours
#[[Baselining my life]] - part 2 - 3 hours - DONE (7 hours)
#[[ScreenScraping HSBC website]] - speak to Dickon about his web contact with a view to presenting my HSBC app to her - 1 hour - NOT DONE
#[[ScreenScraping HSBC website]] - talk to HSBC Innovations contact about idea for mobile bank transfer app / OAuth in banks - 1 hour - NOT DONE
![[I want to spend time with Bonnie]]
#Date with Bonnie and Kimberley on Saturday night - 4 hours - DONE (4 hours)
** Wasn't a date with Kimberley, went to a party instead and then had tea with Dan
#Spend Sunday evening and Monday morning @ Yom Kippur in Solihull - 10 hours - DONE (9 hours)
** Changed plans not to go to Solihull, had a picnic in the park and a Thai in Maida Vale instead
!Bonus points
#Started [[JavaScript Performance Rocks]] review - 2 hours
#Meeting [[Julie Walker]] on Wednesday afternoon - 4 hours
#Sent out [[Invoice 0005]] for [[Anna Freud Centre]] - 0.5 hours
#Met [[Paul Dyson]] and Stuart again to help plan their desktop client - 2hrs 30mins
#[[Great Espresso Hunt]] - Friday afternoon with Fabio and Nick - 4 hours
!Thinking about Productively
I've concluded that putting loads of stuff in my weekly plan stresses me out, lowers my creativity and makes me feel bad about not achieving it all. I'm going to experiment with the other side of the spectrum, where the 31 hour limit is the MAX I can put down, but I should only put down the most important things and see what happens with the rest of my time.
Also, I think that the Q&A-style exploration, such as is used in [[Adobe AIR for Web Standards peeps]] is a good way of learning. Credit to Chris Dent for suggesting this.
This week I had no primary Want because I hadn't figured out what I wanted now that I am freelance. That's ok I guess, as it shows that once you've achieved one thing, what you want next isn't necessarily what you wanted before you achieved that thing.
After thinking over the week and into the next week, I think that the things I really want now are:
[[I want to not have to worry about paying the rent]]
[[I want to develop my portfolio into something that is strong enough to get work from]]
I managed to do all the things that I set myself to do this week, which is pretty satisfying! Looking back over the goals, I think I can be even more concrete about the goals and should set goals that are harder to complete, if I'm at the stage where I'm managing them all.
!Top Wants, Goals and Projects for week beginning 22nd June 2009 (to 31 hours - see [[Habits]])
!!I'm not sure what this is yet, but I know the themes I think
# [[Make people happy]] - make an initial prototype for the [[Streams interface for LShift]] on Monday and Wednesday - 14 hours - DONE
# [[Make people happy]] - sort out [[Dad's IT problems]] - 10 hours - DONE
# [[Finishing things off]] - figure out which are the best things to be doing - 1 hour - DONE
** re-worked [[Practical Projects]] to include a section about what I could be doing next and gave each item a list of things that I'd do to finish it (or progress it quite a lot), approximately how long it would take (in 5-hour multiples) and what the motivation is (coolness, utility, money or REVOLUTION). Whilst I'm not sure I have decided what I'll do next, I think that looking through this list and sleeping on it will produce a reasonably sensible gut feeling
# [[Setting up freelance contracts]] - firm up a number of days and a scope with Dickon before the end of the week - 1 hour - DONE
** Fixed his edit/save problem, as it was the top priority (see [[Anna Freud Centre]] where I've started a log)
!![[I want a balance between appreciating what other have created and creating things myself (aka sort your life out)]]
# [[Spending time with friends]] - make space for Kiki's funeral - 5 hours - DONE
!!New stuff
[[Blessed Blend]] - Fabio and Robert's coffee company. I thought Josh and I could do something for the Edinburgh festival; using Twitter to take orders. Saw Fabio and Robert on Wednesday night - they're interested in us setting up some new revenue streams for them on the web, such as selling coffee grinders/roasters, barista training; and us doing online marketing. I also talked a little about wanting a cause for better coffee in London.
[[TiddlyInvoicing]] - idea for TiddlyTemplating-based HTML invoice creator - print to PDF and send...
[[ScreenScraping HSBC website]] - making online payments programatically
[[Build your own TwitPic]] - auto-tweeting my mobile blog uploads
[[TweetCheck]] - when sending by SMS, make sure you meant to send the tweet
!!Bonus points
#Met [[Paul Bacchus]] and found out about his project. Sadly, I've signed a confidentiality agreement, so I can't talk about it more (yet).
#I wrote [[Why I love Osmosoft and why leaving is the best thing I can do for BT|http://jaybyjayfresh.com/2009/06/28/why-i-love-osmosoft-and-why-leaving-is-the-best-thing-i-can-do-for-bt/]]
!!Notes on [[Productively]]
Tweaked [[Productively]] (that's this thing) - stopped listing all projects in the 'What's on' tiddler, showing links to this tiddler and to [[Practical Projects]]. Really, I'd like to remove the manual list of projects and compile it programmatically using tags.
I've found the "New stuff" section really good for keeping track of the new things I think about or hear about. It's much better than maintaining a single list e.g. [[Practical Projects]].
This week, I managed to do everything I set myself to do - I think that's the first time that has happened in some time. It was also a time when I didn't max out my allocated estimation time - only estimated 29 hours, not 31. My estimates for the things I did do were all pretty accurate as well. So, I think, a good week.
I'm feeling that it's time that two things happened: I came up with concrete plan of action for this whole "electronomad lifestyle" thing, which has grown to encompass in my mind the joy of open projects and the cafe directory project. There is the nagging future point where I have a warehouse space with all varieties of people coming to it to work on projects together. I am beginning to suspect that the physical sense of space is the great unnecessary and a red herring - that I should concentrate on the interactions between people on an ongoing - i.e. virtual - level.
The second thing is the acceptance of the need to focus efforts on a smaller number of projects (once again) - it is certainly ok to invest time and skills as an alternative to investing money or extracting bill payments, but that does not mean that you should do it with wild abandon nor with any less care than would be applied to seeking out and working on paid-for projects (where the urgency and focus is created by the immediacy of the cash-reward).
-----
Week beginning 23rd November 2009. Capped at 31 hours (see [[Habits]]).
* [[I want to invest in startup businesses or art projects]] - 4 months (January)
** [[wiki-data]] - get my laptop working with the MySQL setup - 2 hours - DONE (1 hour)
** [[wiki-data]] - get a working version of the logged-in experience - 18 hours - DONE (15hrs 50mins)
** [[BPSF]] - help Josh finish proposal of information flows and visual ideas (Tuesday morning, Friday morning) - 8 hours - DONE (6 hrs)
* [[I want to create an effective model for running fair-reward open projects]] - 4 months (December)
** Finish [[Why booking time works for me]] and post (1 hour) - DONE (2hrs 30mins)
!!Events
Seeing Dad on Wednesday night for long-term electronomad planning and ale drinking @ The Wenlock
E Rejuvenation session, Friday morning
!! Bonus points
[[LaurenParsons.com]]
!! Thinking about Productively
This week, I started using LighthouseKeeper to deal with Lighthouse tickets. It's definitely quicker to do things in, as you're not waiting for pages to load so much.
(writing on the following Monday)
I had another review of project management-type apps today. I looked at Huddle, back at Staction, and at a few others. None really made me think "aha, perfect". I think that Staction has a really refreshing approach, in that it sells itself as a people-management app. They've taken the Twitter idea of micro-messaging, but boosted it with a 'meta' layer which understands what you post and can group it around projects and people, and create reports of what's going on. I like it because it gets information out of the inbox but without the over-engineering I feel is common to many project management apps.
My main criticism as I'm looking through web apps that are supposed to make you and your team more productive is that they all try to do everything at once, and I think that makes them optimized for trivial projects. This suspicion is reinforced by the screencasts that inevitably introduce the application and tend to feature Joe and Jane Bloggs sending one another a document for review or something. Yes, this does happen in real-life, but there is so much other //stuff// going on, which we tend to drive through our inboxes because the fact that emails are so unencumbered by //context// means that we can use them for anything. That's the main problem with trying to shoe-horn all your data into being a "task", or a "discussion" or a "status update" - it pulls you apart in the head, when what you really need is for these things to become unneccessary - to remove endless rounds of document creation and approval, throw away task-lists, and get together with people with the sole aim of moving your damn project forward. Preferably recording things as you go so you can all benefit from hindsight.
My perfect tool would give me a combination of personal productivity tools and an element of "sharedness" with my groups so it's easy to work on the same information. The personal tools would include: an easy way to record information about a project, where it's up to me to classify after I've created it, should I want to; a way to mark down and tot up how much time I'm spending on things. Group tools would include: ways for me to write on stuff that others have made; ways to make people aware of something I've made.
There's also the problem of one tool trying to do too much - you want one tool for each separate and important thing you're doing: drawing, wireframing, web conferencing, etc. There is a tension with this, because it's also important that people can comment on all these things. Perhaps you also abstract your commenting/discussion tool so that you are commenting/discussing about URL's, which represent everything else (an email in Gmail has a URL).
This week, I mainly went to Copenhagen to see the ILGA project launch to activists. It went down very well. Unfortunately for me, it was a quite expensive exercise (flights were £282, I think) that didn't lead to the indications of future contract work I was hoping for.
On a more positive note, the tutorial I wrote for [[How to build your own TwitPic]] went down very well - it's on my blog [[here|http://jaybyjayfresh.com/2009/07/30/how-to-build-a-diy-twitpic-without-any-coding-skillz/]]. I think I'm going to have to make something of this.
I think for the next week, I need to sort out some administration-type things, like making sure my Google Spreadsheets are working well for Jayne (the lady who does my books) and sending out VAT invoices now I have my VAT number (973325801).
--------
To 31 hours, see [[Habits]]
![[I want to not have to worry about paying the rent]]
#[[Setting up freelance contracts]] - get Stephen Barris to employ me on the ILGA project - 9 hours - NOT DONE
** Went to Copenhagen and saw the launch, chatted to Stephen, but no indication that he'd contract me in the future
#[[Notion Twitter/SMS]] - make the robot work so we can make a round-trip of the 2 best-case scenarios (as written down in [[Notion 23rd July]] afternoon session) - 3 hours - NOT DONE
** Wrote tests but haven't finished the coding yet
![[I want to develop my portfolio into something that is strong enough to get work from]]
#[[Build your own TwitPic]] - finish the tutorial and post to blog - 2 hours - DONE
#[[Build your own TwitPic]] - record a tutorial video and post to blog - 4 hours - NOT DONE
** I think that doing this, as well as making a 'string extractor' module for Yahoo! Pipes would be great things
#[[Ripplz]] - get a simple example working, with non-circular ripples - 4 hours - NOT DONE
** Got some simple stuff done, like downloading images and turning them into single pixel thumbnails
** Josh is figuring out what metric we should use to test match of an image to a height of a ripple
![[I want a balance between appreciating what other have created and creating things myself (aka sort your life out)]]
#[[Celebrate Peter's 40th birthday]] - spend Saturday and Sunday having a celebration in Dublin - 9 hours - DONE
!New stuff
#[[I want to be able to read for more than 30 minutes without falling asleep]] - I found that I'd written this down in June as something I wanted...
#[[Frame Studio class calendar]] - an idea I came up with over the weekend to provide an iCal feed of the Frame timetable
!Thinking about Productively
Building on last week's comments about the use of the items underneath a Want, I think I'll make sure each item's title links to a tiddler, then I will have a record of the atoms which I have around me (as I can always look at the links from a weekly review plus the missing tiddlers (for when I don't create the tiddler for an item) to get a list)
This week has been a mish-mash of meetings, events and working on several projects. There are quite a lot of bonus points, which is stuff I'm happy with, but I'm not sure I focussed on the things I wanted to do!
I spent a lot of time this week thinking both about where new freelancers are going to come from and about the possibilities of interns/placements/work experience. I spoke to Peter about the possibility of taking a sociology student on for 22 weeks, as an industrial placement. They might find something interesting in the [[fee-free micropayments]] research. I also asked Josh and Harry if I could borrow Flora to help with the NCP research, which is [[coming along|http://docs.google.com/Doc?docid=0AQQJ7FGUGIp_ZHI0NG5mY185OWZoZDdrNHNm&hl=en]] nicely.
I've thought and written quite a bit about how to make [[Productively]] simpler - the project tracking side of things had grown to the point where it seems silly. That's all below.
I did quite a bit of work on Sunday night for the [[Anna Freud Centre]] - that's long overdue!
This next week, I'm going to concentrate on getting [[wiki-data phase 3]] sorted. I'm also going to help Bonnie with her website. I will chase Josh again about the Grace site.
It occurs to me I am due a [[Wants]] refresh, since some have timed out...
-----
Week beginning 25th January, to 31 hours - see [[Habits]]. Seeded from [[Wants]].
* I want to invest in startup businesses or art projects - 4 months (January)
** [[wiki-data]] - get through half of the phase 3 tickets (15 hrs) - NOT DONE (3hrs 5mins)
** [[BPSF]] - get phase 2 complete before Thursday (10 hrs) - DONE (8hrs 55mins)
** [[BPSF]] - review meeting on Thursday morning (2 hrs) - DONE (2 hrs)
** [[ProtectedCC]] - beta UI session with Tony on Monday (2 hrs) - DONE (3hrs 15mins)
** [[Violet Hill Studio]] - planning meeting on Wednesday (2 hrs) - DONE (1 hr)
!!Events
wiki-data Chinese on Wednesday
Climbing on Tuesday
Kukutana - Friday morning
Maryce's birthday party on Saturday
!!Bonus points
[[DesignView stories]]
[[Lighthouse API]]
[[Web designers]] blog post
!!Thinking about [[Productively]]
One of the difficulties with the unit and time tracking Josh and I have done for [[BSPF]] is that we're effectively entering progress twice - once in the chronology section, which tracks how long we took to do units and which days we worked, and is used to calculate all the metrics; and the second time on the estimation sheets, where we've broken down the pages into units. I'd like a method where I only have to enter progress once, but where I can see which bits are done and which are not.
----
I'm going slightly crazy with the system I'm running for wiki-data phase 3 - Google Docs spreadsheet to track the overall of what needs doing and what's done; simultaneously running a ticket-tracking system on lighthouse; github posting to lighthouse; also time-tracking and writing about activity in this workbook. I really need to cut down on the number of places I'm recording information!
----
I've been looking into time-tracking applications. I don't want to use these, as I like making notes at the same time as tracking time. Plus, I don't want to link time to particular tickets. Maybe I do... I think it is good to track that a block of time achieved an amount of units, but I am not so keen on getting more granular than that - otherwise, you might start to believe in your own estimates. The point is to make measurement support estimation, so if you get too precise about measurement, you might be encouraged to estimate too precisely.
----
The ideal workflow: have my list of things, give them sizings (units), prioritise them. Something would add up all the sizings. When I do work, I'm writing down what I'm doing. Something would track how long I spend on the project. I'd be able to look at how long I'd spent doing how many units, which would predict how long the project will take. I could also see how the prediction changes over time, which would show how good I was at estimating ticket size.
At the moment, the time-tracking is manual. The ticket sizing is manual. The checking-off of units as I complete tickets is manual. The calculation is manual. If Lighthouse had ticket sizing, I would have to make a join somehow between time-tracking and unit-tracking (Lighthouse are not going to get into time-tracking). You kind of want something that reads off the units for a ticket as you mark it complete in the project notes... so you could be resolving tickets from the project log...
----
Realised late Sunday night that the thing I really want to track is total units done and total time. This means that I can one system tracking units (Lighthouse) and one system tracking time (workbook) and I don't need to cross-post the information about what happened on a particular day. I don't know if I'd be able to get Google spreadsheets pulling the unit info from Lighthouse... probably not... although I *could* make a web service to do it, since GDocs can speak csv, xml, etc.
This was a pretty good week. I have my [[first job|jPoker translation plugin]], which is pretty exciting! The LShift meeting, followed by the PubSub social was great - I spent some time with Mike, Alex and Tony from LShift, as well as with Alexis, and we discussed many things over Black Sheep and London Pride.
3 weeks until leaving date now, think the next week needs to be about securing the contracts I have as potentials and not messing up the first job I've got.
! Top Wants and Goals for week commencing 25th May 2009
!! I want to be able to go freelance
# [[Setting up freelance contracts]]
** Nail down the details of job with Loic - 1 hour - DONE
** Agree revenue split and job scope in meeting with Notion's editor (Bill) and technical guy (Olly) - 3 hours - MOSTLY DONE
*** Revenue split not yet agreed on
** Meet with MikeB from LShift to discuss BBC Feeds Hub project opportunity - 3 hours - DONE
*** Figured out that I would be useful as a "quasi-customer" describing how I'd use the system. I could also contribute to design and build of the interface as well as influencing the architecture with my use cases.
# [[Transition to self-employed]]
** Meet/speak with/email Tager's accountant to ask questions about self-employment structure and tax - 2 hours - DONE (on the following Monday)
** Create business plan for small creative business helping idea - 2 hours - MOSTLY DONE
*** Did some work on this with Nick that showed that drip-feed incomes from businesses with circa £100k turnover isn't going to provide much income. I think the important thing is to help the businesses increases their market by A LOT (as this is the variable that is uncapped - profit share is capped at 100%, for example)...
# [[Finishing things off]]
** [[ILGA]] - 5 hours - DONE
** [[Anna Freud Centre]] - 2 hours - NOT DONE
! Bonus points
# Business cards - designed and ordered from Moo
** front: http://www.flickr.com/photos/jayfresh/3567292494/ back: http://www.flickr.com/photos/jayfresh/3566545859/
This week, I did some work on things that were not covered by the top goals section below, which suggests that I need to make more systematic use of the individual [[Wants]] referred to.
Again, I failed to complete the projects or tasks that I set myself as the highest priorities. This could be because I've not correctly recognised the highest priority tasks, or because I'm being stubborn and not responding to what I've said myself to be most important. I'm thinking particularly of the task of uploading videos from Bulgaria to Facebook - easy, but I didn't do it. I guess I just don't see it as that important. Next week, I'm going to write [[Top Wants, Goals and Projects]] from scratch, rather than copying and pasting from the previous week, to give myself the opportunity to check my priorities haven't changed.
A new thing I did this week was clear out my Google Tasks list and start a new one called 4/5/09. I don't know if I'll do this every week, but it seemed sensible to start afresh so I can see how I can combine the use of that tool with this. At the moment, it is too overloaded to be helpful.
Finally, for the week ahead, I've swapped the priority of the top 2 priorities (as seen on [[Wants]]) to reflect that I'm happy with the progress to sorting out my habits and that going freelance is taking up more of my thoughts.
!! Later thoughts
# The estimating of time in the [[Top Wants, Goals and Projects]] section is turning out useful, even thought I don't generally agree with measuring time put in. I think it's turning out useful because it at least gives you an idea of how big you think something is and how big that thing is relative to the other things you care about.
# I'm not doing a good job of tracking projects and tasks that relate to goals, other than the ones that I put in the [[Top Wants, Goals and Projects]] section. I am doing a better job of tracking the [[iPhone translation app]], so I think I'll do some more thinking about what you ought to surface from the large amount of copy that gets generated when you're tracking a project. My anxiety comes from the question of "what do I do when I finish all the things in the top priority list?", which is currently a bit irrelevant since I've not managed to do that in the two weeks I've been working like this.
# This tool should not be thought of as running my life, it is simply something there to help me remember what's important... this is a developing identity... ;)
# I've started to use Google mail filters to highlight when I have unread mail from people. I haven't yet figured out how to do compound filters (e.g. from bob OR simon). I'm getting good, non-overlapping coverage, since it turns out that the main contacts for a project tend not to be doing other projects with me (this is with me only having set up for 3 projects). I'm sure some email slip through the cracks, so I'm not expecting to rely on it, more hoping that it will be an extra alert for unread and relevant email.
!!Top Wants and Goals for week commencing 27th April 2009
!1. [[I want a balance between appreciating what other have created and creating things myself (aka sort your life out)]]
TOP 3 GOALS AND PROJECTS:
# Sort out my financial situation
** Sort insurance claim - 1 hour - DONE
** [[Analyse current spending]] - 3 hours - DONE
# Habitualise using Facebook to keep in touch with people (and upload photos/videos they're waiting for)
** Upload videos from Bulgaria - 1 hour - NOT DONE
!2. [[I want to be able to go freelance]]
TOP 3 GOALS AND PROJECTS:
# Build, market and sell the [[iPhone translation app]]
** Find out limitations of OCR libraries and figure out how to stretch these by training - 10 hours - PARTIALLY DONE
** Establish whether translation libraries can be put on the iPhone - 3 hours - NOT DONE
# Clarify situation with Tager (nee Theme nee Sick as a Dog)
** Sort out Barbara's iPhone - 2 hours - DONE
This week, I did some massive thinking. It was draining. It had been about six months since I looked at what I really want and wrote it down, and it felt like I'd been focussing quite a lot on short-term or small-picture stuff. So I did a bit of introspection and wrote it all down in [[Wants refresh 28th September 2009]]. In that tiddler, I wrote about the process I was going through, as I felt like I'd done things like this enough times to start to describe how to do it effectively, or at least usefully.
I only really got maybe two-thirds through the process, but it certainly gave me a much better sense of the "big picture" and I had the busiest week I've had in a long time. Although I haven't written this down yet, I've decided to stop the practice of writing the [[Top Wants, Goals and Projects]] tiddler from scratch each week; instead, I'm going to keep my list of wants in a tiddler called [[What I want]] and use them as headlines to write the most important things under. I'll try to use the tiddler for each want to keep track of how that is progressing. I've realised that prose is a much more powerful tool than bullet-points for writing down what I think about things.
As a consequence of the above decision taking some time to crystallize, I didn't write a very structured plan for the week, as you can see below.
-----
submit expenses - DONE
organise violet hill shoot - DONE
organise grace shoot
organise violet hill design
set up open project
plan yellowcar website to list open projects going on and explain terms of engagement - "projects open - anyone welcome"
speak to HSBC man about potential for [[fee-free micropayments]]
speak to Dickon about [[fee-free micropayments]] contact
read up on Julie Walker's emails - DONE
plan for sorting out Avox's gaps to launch on Friday - DONE
organise going to Twickenham on Friday and promoting social media breakfast (Julie needs better Twitter address first) - DONE
JavaScript Performance Rocks review
Speaking to people about energy monitors - all the making peeps, @kevglobal, Julie's eletronics-producing friend, etc...
Play more guitar
Go to shops earlier for dinner food
This week, I seemed to spend a lot more time on [[BPSF]] than I'd predicted (actually 6hrs 45mins vs 2 hours), but I think it was worth while. If nothing goes wrong, it looks like we'll be doing the job for the next six weeks. This is good from my point of view as I am trying to have more than one major client - eggs in one basket and that.
On the wiki-data front, I didn't manage to do as much bug fixing as I'd thought I would. I guess I was more concerned with getting the new designs sorted with Amit, which we made good progress on. I also failed to have any "what's next" chat with Ken. I think this is made slightly less urgent by the fact we've said we'll be spending the next two weeks on building the site re-design (phase 1 i.e. what's agreed already, with some amendments being developed in parallel to be built next - phase 2).
I found it hard to motivate myself to work on [[ProtectedCC]], not managing to do anything on the project. I am still in a position where I don't really know why the technology is useful in the real world, but this doesn't take away from the fact that I've agreed to work on the copy (and design) for the closed beta site. Sherene and I made a good start last week, so I will try to finish it off on Monday or Tuesday - it is the Launch48 follow-up event on Tuesday and it would be great to have something in place for then...
This next week, I'm hoping to get cracking with the [[BPSF]] build, do some solid work on the [[wiki-data]] build (Amit's just arrived in Columbia, will probably need some time to adjust) and sort this [[ProtectedCC]] thing.
There are a few things on the horizon which will want some attention in the near-term: where's my Grace site? What's going on with [[Daniel's site|Violet Hill Studio]]? What's next for the [[Anna Freud Centre]] project (Chris has been reviewing how we've done things and is interested)?
------
Week beginning 30th November. Capped at 31 hours (see [[Habits]]).
*[[I want to invest in startup businesses or art projects]] - 4 months (January)
** [[wiki-data]] - sort out any problems with MADv1 (caching, display of results) - 5 hours - HALF DONE (2hrs 5mins)
*** There is still a problem in IE if you login and do an ajax_search
*** There is still a problem with caching
**** Need to continue the discussion with Chris
** [[wiki-data]] - figure out with Ken what the next three months looks like for wiki-data (apps, data, features, clients, feedback...) - 2 hours - NOT DONE
*** thought about it, didn't write anything
*** arranged call to discuss support on Monday
** [[wiki-data]] - keep helping with the design-to-build stuff with Amit - 5 hours - DONE (7hrs 10mins)
**[[BPSF]] - attend meeting on Tuesday morning to help iron out difficulties with responding to brief - 2 hours - DONE (6hrs 45mins)
**[[ProtectedCC]] - improve copy for closed beta site - 1 hour - NOT DONE
**[[ProtectedCC]] - logo design - 1 hour - NOT DONE
*[[I want to create an effective model for running fair-reward open projects]] - 4 months (December)
**ElectroNomad - figure out what's coming up... - 3 hours - HALF DONE
*** Got a better idea of what I want to be doing
!!Events
* [[SiteLoop]] meeting on Thursday afternoon with Josh, to discuss future involvement - DONE (1hr)
!!What others are doing
* Josh is putting some time into the [[Grace coffee]] site
** supports [[I want to do the little things that make other people happy that don't seem big or important to me]]
!!Thinking about [[Productively]]
On the subject of free-form text entry, this review of [[TaskPaper|http://mac.appstorm.net/reviews/productivity-review/taskpaper-simple-productivity/#more-4594]] shows it to have some interesting parsing of your text to add meaning such as projects or tasks. I'm interested in auto-parsing free-form text because I think free-form text is an awesome way to store information, I just wish there was an easier way to extract important "bits" from it (such as bugs or important memo's) - see [[Improving this lab book]]
On the subject of everything having a URL, this review of [[Droplr|http://mac.appstorm.net/how-to/utilities-how-to/a-preview-of-sharing-files-with-droplr/]] shows how Droplr gives you a hot-spot you drag things on to have them uploaded to the web and given a URL, even a URL on your own domain. I think that everything having a URL means you can do anything you want with those things, including overlaying multiple different organisational structures, commenting (see Scrumptious)...
!! Bonus points
[[My stories of the wiki-data and BPSF projects]] - related to [[Productively]] discussion above.
[[SiteLoop]] - conf call on Friday to see innards of SF and SL (30 mins)
This week, I had my first disappointment in the run up to being a freelancer - I had my quote for [[jPoker translation plugin]] rejected. This is possibly a good thing, as it's made me realise I need to figure out where the money is coming from, rather than worrying more about administrative things like setting up a limited company, or speculative work. It also helps that the rejected project was only about a day and a half's work, not three months.
This week, I managed to add to the ILGA development for the first time in a while, rather than just dealing with project administration. I failed to do any work on the [[Setting up freelance contracts]] area.
I did quite a lot of work on things I hadn't planned to do, which suggests I either should have planned to do work on them, or I have been less disciplined this week. Thinking about it, I think that the [[Setting up freelance contracts]] section contained tasks that were difficult because they involved potentially awkward conversations with people about money, whereas the things I did spend time on were devoid of that.
Thinking about how this productivity system is working out: I wonder whether I should phrase my goals more like goals than section titles, as they are now. //Update:// I've just done next week's [[Top Wants, Goals and Projects]] and I put more specific goals //next to// the section titles, and allowed more than one goal per section. This seems cool, will see how it goes.
I managed to record all my time this week, so I'm looking forward to doing the analysis to see what I've spent my time on. After this week's [[Tarpipe/Yahoo! Pipes integration]] success, I think I could automate the processing of the data I am collecting on how I spend my time (done by [[context switching]]).
!Top goals and projects for week commencing 1st June 2009:
!![[I want to be able to go freelance]]
#[[jPoker translation plugin]]
** Scope the job and submit time and cost estimate to Loic via rentacoder - 1 hour - DONE (took 2hrs 30mins)
#[[Setting up freelance contracts]]
** Agree on when I can work on the Feeds Hub project and for how much - 2 hours - NOT DONE
** Work out plan for work on [[Notion Twitter/SMS]] - 2 hours - NOT DONE
** Agree revenue share for [[Notion Twitter/SMS]] with Bill and Olly - 2 hours - NOT DONE
#[[Finishing things off]]
** List the things that would be good to finish off and order them by priority (to include blog posts and projects) - 1 hour - NOT DONE
** [[Anna Freud Centre]] - 2 hours - NOT DONE
** [[ILGA]] - sort out suggested tags for articles - 5 hours - DONE
#[[Transition to self-employed]]
** Update spending data with cash breakdown I've been doing - 1 hour - DONE
** Create a business model for me, using my spending data and the tax models I found out about at Monday's meeting with Kirit (the accountant) - goal is to update the amount I need to earn - 2 hours - HALF DONE
*** I have created a more accurate spending model; I haven't modeled tax yet
!![[I want a balance between appreciating what other have created and creating things myself (aka sort your life out)]]
# [[Habits]]
** Improve estimate of time spent on habits by measuring what I do this week - 1 hour - DONE
!! Bonus points
[[Tarpipe/Yahoo! Pipes integration]], [[motivation, influence and rewards in open groups]] research
This week was dominated by the idea I had in Dublin to 'knock together' an application to show Frame's class timetable in iCal format. It ended up taking twice as long as I'd thought, which ate into other things. This is the first week when I've compared how long I spent on things with how long I said I would spend on them - you think I would have done that before now!
I think it was a good week really, as I managed to get the demo of [[Screenscraping HSBC website]] done, showed it at Hackspace and popped a video on my blog. I'm feeling fairly motivated to get into Phonegap and see if I can make the same demo work in the context of an iPod app.
The day's pro-bono I did this week on the [[Anna Freud Centre]] project led to a booking for a day's paid work, which was a big surprise, as I thought their funding was still in the pipeline.
Next week, I need to make sure the [[Avox Wiki-data]] workshop goes well and focus on that project. It would be nice to find some time to give to [[Ripplz]], as well as put together the string extraction Yahoo! Pipe, to make [[How to build your own TwitPic]] a bit easier (and make it easier for other applications to be created). I think the Phonegap investigation will be left for a week. It would be nice to get figure out why the [[Frame Class Calendar]] doesn't work so well in Google Calendar...
-------------
To 31 hours, see [[Habits]]
![[I want to not have to worry about paying the rent]]
#[[Notion Twitter/SMS]] - make the bot work and integrate with Notion's systems - 8 hours - NOT DONE (actually took 5 hours)
** Robot works (passes tests), need to get Notion to help out to integrate last bits
** Sent Notion email, didn't hear back within the week
#[[Setting up freelance contracts]] - learn what's going on with the Avox project in preparation for workshop next week - 3 hours - DONE (actually took 1 hour)
** Downloaded source code, discussed project at Osmosoft
#[[Make people happy]] - speak to Dickon about organising a day of pro-bono - 1 hour - DONE (actually spent 7 hours on it)
** Did the day on Friday and they booked me in for a day of paid work on September 4th
#[[Setting up freelance contracts]] - find out from Mike B when LShift are going to be needing me again - 1 hours - NOT DONE
** Have emailed Mike, waiting for response
![[I want it to be no hassle to run a company]]
#[[Automating company workflows]] - make sure Jayne is happy with the way invoices are treated - 2 hours - DONE (took 2 hours)
** Have updated tracker and form
#[[Automating company workflows]] - make sure Jayne is happy with the way expenses are treated - 3 hours - NOT DONE
![[I want to develop my portfolio into something that is strong enough to get work from]]
#[[Frame Studio class calendar]] - get the calendar iCal feed working - 6 hours - DONE (actually spent 12 hours on it)
#[[Frame Studio class calendar]] - show it to Frame and suggest it's worth 30 days' yoga - 2 hours - NOT DONE
** Showed it to Pip and Jo, they said they would send it to their IT guy to put on the site
** haven't suggested it's worth yoga classes
#[[ScreenScraping HSBC website]] - get something working in time for hackspace on Thursday - 5 hours - DONE (actually spent 5 1/2 hours on it; then spent 4 hours at Hackspace)
** Showed an app at Hackspace that gets your bank account information
** Recorded a video and posted to blog at: jaybyjayfresh.com/2009/08/06/online-banking-with-javascript-first-working-thing/
![[Events]]
#Dinner at Richard and Ruth's on Friday night - DONE
#Hackspace on Thursday night - DONE
!Bonus points
#[[AjaxReq]] - JS lib for making local cross-domain HTTP requests and using a proxy when made remote
#[[jsAutomatic]] - JS lib for automating chains of interaction with a web site
#Sent out VAT invoices for [[Invoice 0001]], [[Invoice 0002]] and [[Invoice 0003]]
!New stuff
[[Bryan Bogensberger idea]] - intro to idea, need for Smart Platform dev - more to be heard towards the end August
An unconference for creative types. First one held in RichMix on Tuesday 8th June 2010. Organised by [[Alison Coward]] and Sinead.
Was interesting to see that it's not just me that thinks small, collaborating groups can be an alternative to the agency model.
See:
[[Rewired State Home Office Hack Day]]
[[Rewired State Culture hack-day]]
Saturday 27th March 2010 @ The Guardian offices, KX, 9am - 7pm
Worked on TAPCulture. On team with @1jh, @lawrencejob, @danielmorris and @abscond. Presented.
Helping Rob.
See [[Invoice 0018]].
!Postcode to London Borough lookup
Found this mapping: http://www.museumoflondon.org.uk/postcodes/places/boroughs.html
* Not a 1:1 mapping though
* Would need to know the area you're in, which you could get from the police API nearest-police-station, although this doesn't guarantee to return a police station in the same borough; it doesn't always show borough either
Also looking at the geocode crime area API: http://policeapi.rkh.co.uk/geocode-crime-area/
* Nope, this returns "metropolitan" often for all areas
Looking at Google Local Search, e.g. http://ajax.googleapis.com/ajax/services/search/local?v=1.0&q=e1%207eb,london
* This returns Lat/Lon, but not area
Found this mapping of postcodes to boroughs: http://www.milesfaster.co.uk/london-postcodes-list.htm - as an idea, take first part of postcode, do a lookup on nearest police station and see if the address contains any of the listed boroughs. If it doesn't, try a Google Local search for a pub and work through that.
* I've found that no postcode appears in more than 3 boroughs. Let's test this with those postcodes: nw10, se19, w1, wc1, sw1
[[This|http://www.museumoflondon.org.uk/postcodes/places/boroughs.html]] also gives all the local area names in a borough's postcode zones e.g. Aldgate, Broadgate. That could be useful if the data we can get from addresses is only at that level.
Crime area for a post code can be found using this API: http://policeapi.rkh.co.uk/api/geocode-crime-area?key=rewiredcrime&q=SW5%200QT - unfortunately, that can give "metropolitan" as the area, rather than the borough.
Leon at RKH (build CrimeMappers and the police API) helped me with this idea:
* Get crime areas list from: http://policeapi.rkh.co.uk/api/geocode-crime-area?key=rewiredcrime&q=POSTCODE
* Get the number for the second area in the list
* Get the borough out of the name of the crime-area using this: http://policeapi.rkh.co.uk/api/crime-area?key=rewiredcrime&force=metropolitan&area=AREACODE
This worked, here's the guts: http://pastebin.com/KywnppJM
Code published here: http://github.com/jayfresh/PostcodeToBorough
!List of police consultative groups
http://www.google.co.uk/search?q=police+consultative+groups
Met Thursday 4th February 2010, at Osmo and then beers with Jeremy.
Richard used to work with Jeremy at Onboard.info (I think), and he currently works at a fashion site. He is an all-round web developer/designer, but prefers client-side. He is going freelance shortly and interested in working on projects together.
Met at the Cube, works as a graphic designer, does a lot of layout of corporate documents, such as annual reports. Helped with [[BonnieParsons.com]].
!The problem
Photo-montage used to create other images is nice, but it's not animated.
!The idea
Josh and I thought we'd put some animation into a photo-montage image by changing which photos are displayed. The animation we thought we'd like to do is a water-ripple effect
!The ideal
You choose a tag, which sets which photos to use. The animation would be a circular ripple flowing out from wherever you touched, with the photos as small as necessary to make the effect look good.
! Breakdown of parts
!! Images
#Something to pull lists of tagged images from Flickr
**they have an API
*** I have a non-commercial key now
*** example tag search: {{{ http://api.flickr.com/services/rest/?method=flickr.photos.search&api_key=xxxxxxxxxxx&tags=dogs&format=json }}}
**** this gets you a JSON document with a list of photos - you can get the thumbnail of each photo by combining the properties like this: {{{ farm<farm>.static.flickr.com/<server>/<id>_<secret>_t.jpg }}}
**** Now I have a JS app that downloads 100 thumbnails from Flickr and saves them to a directory
#Something to analyse what the average colour of a photograph is
** ImageMagick seems to be able to do this, see [[here|http://www.imagemagick.org/Usage/compare/#metric_cmatrix]] and [[here|http://www.imagemagick.org/pipermail/magick-users/2009-May/022559.html]]
*** This command gets an average value for an image: {{{ convert <imageFileName> -colors 1 -crop 1x1+0+0 -depth 8 -format '%[pixel:s]' info:- }}}
** The above gets a RGB value, which isn't directly comparable to a single metric as it's 3D. My thought for finding the nearest value to a RGB value is to minimize the sum-square-differences of each dimension of the value
*** This doesn't actually get me a single value to use for the height of the wave. I need to figure out how things like saturation are related to RGB
# A way to get the photos through the image analyser
** How about a TiddlyWeb plugin, to talk to ImageMagick in Python? (We've done similar for [[ILGA]])
** There's an interesting [[post|http://mail.python.org/pipermail/python-list/2006-January/535256.html]] on the python list with this use of Python Image Library to get the average colour:
<<<
{{{
> > Shrink it to one pixel, and get that pixel's value. ;-)
> >
> > Seriously, that ought to do it. Bear in mind that you
> > need to use the right sampling mode (IIRC, you want
> > ANTIALIAS).
This is answering the OP's question which is how to get
the average color for a whole image:
color = img.resize( (1,1), Image.ANTIALIAS).getpixel((0,0))
You just shrink the image down. The ANTIALIAS filter
averages every pixel in the image (this occurs in the PIL
library, so it's probably pretty fast), and then you fish
the single remaining pixel out into tuple format.
}}}
<<<
Would use the above in something like:
{{{
from PIL import Image
import glob, os
def averageColour(filename):
for infile in glob.glob(filename):
img = Image.open(infile)
color = img.resize( (1,1), Image.ANTIALIAS).getpixel((0,0))
return color
}}}
Using this I've created a set of single-pixel files and saved their RGB values. ''I'd like to have their HSL values instead, but I don't know how to get that yet.''
# A way to model the ripples
** ...these would be of the type described as [[gravity waves|http://en.wikipedia.org/wiki/Gravity_wave]]
#A way to display the height of the wave
** There would be a relationship between the colour of the displayed photograph and the desired height of the wave (perhaps we should be using image saturation rather than colour... hmm...)
!! Animation
#Algorithm for ripples
#Figuring out how to draw a circle out of square div's of not-tiny size
!Simplifications
#Starting with a square ripple - in order to focus on getting the colouring of a ripple right, it makes a sort of sense to avoid the circular problem first
#Starting with a non-continuous ripple - instead of modelling an entire wave, perhaps it would be easier to start with rings of high intensity that just move outward. After we've got that working, we could bring in the wave simulation
! Other people doing similar things
Email from Josh:
<<<
http://krazydad.com/colrpickr/ -flash based unfortunately
http://labs.ideeinc.com/multicolr - also does multiple colours! - they're selling software that does this
http://color.slightlyblue.com/ - rougher but does the same thing
So possibly imagemagick is the open route.. here's some stuff:
http://www.codeproject.com/KB/WPF/ImageMagicImageColorSpace.aspx -- a related thing? Certainly has to judge colours. Has source.
http://www.imagemagick.org/Usage/compare/#metric_cmatrix -- ref from imagemagic... hmm
<<<
!Possible Problems
#Flickr licensing - one should respect an image's license
Appears to use server-side JavaScript for things like validation.
Integration links seem to be GET-only, which makes it tricky to know how to authenticate...
Plan is to spend September 2010 in America, with 2 weeks in and around San Fran and 2 weeks in LA.
Flights booked: 9th Sep-2nd Oct
People:
Jeff Lindsay, webhooks.org, blogrium.org - Alexis has intro'd me
Alex Collins' friend James
Stuart King
Ryan Dahl (alexis rec) - AR has intro'd me
Heroku guys (alexis) - AR has intro'd me
- Blake
Oleg - around until Friday
Rapportive's Martin Kleppmann (Alexis rec) - AR has intro'd me
Adrian Cole (and David from RedHat)
Julien G (superfeedr)
At node event, met: Ryan, Phil Freo (quizlets), Saikat (mockingbird), Guillermo (Socket.IO), James (ex-Zynga, StarStream?)
Web Places:
HackerDojo (MountainView) / NoiseBridge (SF, Andrew, say hi to Mike (Chinese) from Andrew)
PARC, Mountain View (they host public events, Andrew)
Sub-Mission, Mission - gigs (andrew)
List of co-working spaces - http://coworking.pbworks.com/SanFranciscoCoworking (oleg)
PariSoMa (Adrian)
Eating:
Golden Boy Pizza, North Beach (andrew)
Papalote, 24th St Valencia (via http://www.burritoeater.com/, which came via Alexis)
Minako Sushi (blaine)
Other places:
Museum of Tech!
Kabuki Springs (Mon, Thurs, Sat men) - http://www.kabukisprings.com/
TenderNob - Polk/California - cool (Adrian)
Exploratorium, Tactile Dome (monkeybanjo, adrianhon)
Avila Beach - stop for lunch on Sunday (Bill), harbour area off Hwy 1.
Elephant seals - by Ragged Point, past Plastic Creek; usually marked for the seals.
Events:
gigs - http://www.foopee.com
geek events - http://laughingsquid.com/calendar/
Coffee Shops:
Philz, 24th (Blaine)
Blue Bottle (alexis; Blaine says Mint Plaza or Hayes Valley (315 Linden St), Ferry Plaza)
Four Barrel (Blaine, 375 Valencia at 14th)
SightGlass (blaine, SOMA)
Bank of America, russian cafe nearby (ann witbrock)
Intelligentsia, LA (fabio)
Something about Irish coffee (fabio)
Coffee Couture/Culture - geisha coffee (fabio)
Epicenter (Julien, 764 Harrison St, near 4th; try French Press; good for weekend working)
Coffee Bar (Julien, 1890 Bryant St, between 17th & Mariposa)
Ritual (blaine, Julien; no plugs anymore; 1026 Valencia, )
Haus (saw it; 3086 24th St)
Mission cafés...
Shopping:
Birthday present for Andrew - done
B'day present for Peter (a CD he says)
Andrew's misc shopping list - done
B'day for Caro - done
Nutty Sees Candies medium box, for Nick - done
Intelligentsia espresso for Fabio, 1kg
Hotels: http://www.tripadvisor.com/HACSearch?geo=60713&q=San+Francisco,+California,+USA&inDay=27&inMonth=08/2010&checkIn=8/27/2010&outDay=12&outMonth=09/2010&checkOut=9/12/2010&adults=1#02,1282861582124,bc_airports:S,bc_convention_centers:S,nameContains:S,l1price:S0|150,cb:Azfc_3
| Name | Free? |
| [[Zong]] | 5% on top of carrier cut |
| [[Fortumo]] | 2-3% on top of carrier cut |
See:
[[JavaScript in the cloud]] - blog post
[[Getting the DOM on Smart Platform]]
[[Live JavaScript editor for Smart Platform]]
[[Testing server-side JavaScript]]
James Governor introduced us. I asked him about the [[DRAMA]] iPad app, he referred me to Do Tank studios in Clerkenwell. Digital agency.
Mike Rowlands got in touch to ask me about this. Paul Jones is leading on the design and development.
!!Log
[[20th May 2010 Scheduler 4]] - 2hrs 5mins
[[4th June 2010 Scheduler 4]] - 3hrs 35mins
!Production User Stories
Interface estimates are in relative units. These are converted to times by considering a middle-sized, well-understood item and estimating time.
Ideal Total units: 106
Hours / unit: 12/11
Ideal hours: 115.6 = 14.5 days
Top estimate = 14.5 + 50% = 21.7 days, rounded to 22 days
!!Navigation
ID NAV-1
Title Select Programme
Content As a production user,
I want to be able to select a programme scheduled on my network,
So that I can navigate to the programme I want to work with.
Priority Must Have
Interface - construction of homepage calendar view of programmes, with navigation to different weeks; click to open Schedule Editor. Programmes list comes from communication with server programmes endpoint.
Interface estimate - 25
ID NAV-2
Title Mark Favourites
Content As a production user,
I want to be able to mark and save programmes as favourites,
So that I can easily navigate to programmes I work on frequently.
Priority Should Have
Interface - provide a button to favourite a programme series; programme list must include information about whether a programme is part of a series and what that series is
Interface estimate - 3
ID NAV-3
Title Select Favourite
Content As a production user,
I want to be able to select from favourited programmes,
So that I can navigate to favourite programmes quickly.
Priority Should Have
Interface - there would be some sort of favourites list, which means the programme list must include information about whether a programme is someone's favourite
Interface estimate - 3
ID NAV-4
Title Hide non-favourites
Content As a production user,
I want to be able to hide shows that I haven't marked as favourites,
So that I can make it easier to see my favourite shows.
Priority Should Have
Interface - a button would switch non-favourites on and off, meaning the programmes list must include information about whether a programme is someone's favourite
Interface estimate - 3
!!Input Feeds
ID IN-1, IN-2, IN3
Title Import Twitter Feed Entries
Content As a production user,
I want to be able to import items from Twitter / WIN / RSS feeds into the labels for a programme I'm editing,
So that I don't have to copy the content manually.
Priority Must Have
Interface - this would mean that the Carousel Editor would read a list of UGC items from the the ugc endpoint on the server and display them in a list; the UGC items could be dragged on to the carousel queue
Interface estimate - 11
Hours estimate - from 1-2 days, average 12 hours
ID IN-4 / IN-5
Title Use live references to PIPs / VCS Metadata in Labels
Content As a production user,
I want to be able to use live references to PIPs metadata in labels that I publish,
So that if programme metadata is updated I won't end up with out-of-date text.
Priority Must Have
Interface - the interface must be getting up-to-date information about the uneditable carousels on a regular basis; this could be done by polling, or through checking for updates every time a new edit is submitted (if the response is always a list of all carousels)
Interface estimate - 10
ID IN-6
Title Import PIPs schedule
Content As a production user,
I want data from PIPs to be imported automatically to provide the programme schedule,
So that scheduled data for a programme is available to edit.
Priority Must Have
Interface - the interface must not overwrite any carousels that you've made a change to if the underlying auto-populated data changes; the interface must pull in updates for carousels defined elsewhere, either by polling or by receiving changes when updates are sent to the server
Interface estimate - 5
!!Scheduling
ID SCHED-1
Title Enter Draft Schedules
Content As a production user,
I want to be able to enter draft templates against a PID that isn't yet in the schedule,
So that I can pre-prepare for special programmes far in advance.
Priority Must Have
Interface - ''is this an interface problem?''
Interface estimate - ??
ID SCHED-2
Title Edit labels populated from template
Content As a production user,
I want to be able to edit the labels that has been prepopulated from a template,
So that I can maintain editorial control over all content that is published.
Priority Must Have
Interface - satisfied by IN-6
ID SCHED-3 / SCHED-4
Title Create/update DAB/RDS label
Content As a production user,
I want to be able to create and update DAB / RDS length free-text labels in a schedule I'm editing,
So that I can send out information at any point, and schedule it in advance.
Priority Must Have
Interface - the creation of DAB and RDS labels would be possible after clicking on a carousel, through the Carousel Editor. This limits the length of DAB labels to 128 characters and RDS to 64 characters. The text is synced between the two boxes unless you deliberately unlink them.
Interface estimate - 8
ID SCHED-5
Title Schedule label rotation or time length
Content As a production user,
I want to be able to schedule the number of times a label rotates or the start and end times that it is to be published,
So that I can keep content interesting for my consumers.
Priority Must Have
Interface - the start and end times for a carousel are set by moving and resizing the carousel on the Schedule Editor's calendar view; updates are sent to the server's carousels endpoint
Interface estimate - 8
ID SCHED-6
Title Cycle through active labels
Content As a production user,
I want be able to have multiple labels active at any point, and have the end systems cycle through them,
So that I can keep content interesting for my consumers.
Priority Must Have
Interface - the Schedule Editor allows for multiple streams to be displayed at once, and submits new carousels and changes to carousels, to the server, when you make them
Interface estimate - 3
ID SCHED-7
Title Relative priorities of active labels
Content As a production user,
I want to be able to set the relative priorities of active labels,
So that I can make more important information appear more frequently.
Priority Must Have
Interface - this is achieved by setting the volume of each stream - the relative volumes dictate how often a carousel will appear in a stream; updating a stream's volume triggers an update to the server's volumes endpoint
Interface estimate - 9
ID SCHED-8
Title Override labels
Content As a production user,
I want to be able to completely override labels from some sources temporarily,
So that I can hide or augment feed information when desired.
Priority Must Have
Interface - you can cause a stream to have it's volume set to 11, which disables all other streams temporarily (visual cues could include setting the other streams to grey and colouring the overriding slider differently; the volumes of all other streams would be effectively set to zero, which is what would be sent in the update to the volumes endpoint; when overriding is switched off, there is an update sent to the volumes endpoint to put the other streams back to where they were
Interface estimate - 5
ID SCHED-9
Title Schedule labels without programme
Content As a production user,
I want to be able to schedule labels without a programme being active,
So that on channels that don't broadcast all the time (such as Sports Extra) I can publish trailers.
Priority Must Have
Interface - creating new carousels in the calendar, at any time, is how you achieve this; creating a new carousel triggers a submission to the carousels endpoint once there are some labels in there. Adding a new carousel triggers the Carousel Editor to pop up.
Interface estimate - 3
ID SCHED-10
Title Schedule programmes without PIPs metadata
Content As a production user,
I want to be able to schedule programmes immediately without PIPs metadata,
So that on sporadic channels such as Sports Extra I can immediately send out scores when games start.
Priority Must Have
Interface - see SCHED-9
ID SCHED-13
Title Upload images
Content As a production user,
I want to be able to upload images,
So that they can be selected for scheduling.
Priority Must Have
Interface - ''is uploading images a problem for this interface?''
ID SCHED-14
Title Schedule images
Content As a production user,
I want to schedule an image to be published,
So that I can publish images alongside labels.
Priority Must Have
Interface - available images would appear by the UGC section of the carousel editor; they would be sourced from the same endpoint as the UGC content
Interface estimate - 3
!!Publishing
ID PUB-1
Title Publish labels to DAB
Content As a production user,
I want labels that I've assembled to be sent to the DAB distribution system,
So that DAB users can see content I've produced.
Priority Must Have
Interface - as soon as a change is saved in the Carousel Editor, a submission is made to the carousels endpoint; the response is the new carousel object and should there have been an error in server-side validation, an error code will be returned, which would tell the person using the interface there was an error and not update the interface, leaving the Carousel Editor open
Interface estimate - 4
ID PUB-2
Title Publish labels to RDS
Content As a production user,
I want labels that I've assembled to be sent to the RDS distribution system,
So that RDS users can see content I've produced.
Priority Must Have
Interface - see PUB-1
ID PUB-3
Title No Label Truncation
Content As a production user,
I want labels never to be truncated,
So that inadvertent phrases and words are not produced.
Priority Must Have
Interface - the Carousel Editor will always make sure that the submitted content does not exceed the allowable 64/128 character limit for RDS/DAB, respectively
Interface estimate - 3
Below here, unsure of whether these stories are in scope:
-----
!!Labels
ID LBL-1
Title Override network messages
Content As a interactive user,
I want to be able to override messages for the entire network,
So that I can send out network-wide messages.
Priority Must Have
ID LBL-2
Title Promotional messages
Content As a interactive user,
I want to be able to set promotional messages that apply across programmes,
So that I can promote other content on a network.
Priority Must Have
!!Archiving
ID ARCHV-1
Title Archive labels
Content As a interactive user,
I want all labels that have been sent out to be archived,
So that on demand platforms can retrieve labels alongside content streams.
Priority Must Have
!Administrator Stories
!!Access Control
ID AUTH-2
Title Manage Users that can login
Content As a administrator,
I want to be able to manage the users that have access to the system,
So that unauthorized users can be prevented from accessing the system.
Priority Must Have
ID AUTH-3
Title Manage user network access
Content As a administrator,
I want to be able to configure the networks that a user has access to,
So that users do not inadvertently modify data that they don't need to access.
Priority Must Have
!!Labels
ID LBL-3
Title Override all labels
Content As a administrator,
I want to be able to override labels for all networks,
So that I can broadcast critical messages.
Priority Must Have
!!Monitoring
ID MON-1
Title Failure Alerting
Content As a administrator,
I want to be alerted if any part of the system fails,
So that the system does not enter a non-functional state with anybody knowing.
Priority Must Have
!System Configuration User Stories
!!Extensibility
ID EXT-1
Title Add/Configure input feed
Content As a system configuration user,
I want to be able to add and configure input feeds,
So that additional data sources can be made available within the system.
Priority Must Have
ID EXT-3
Title Add/Configure publishing platform
Content As a system configuration user,
I want to be able to add and configure publishing platforms,
So that additional outputs can be generated from the system.
Priority Must Have
ID EXT-3
Title Remove input feed
Content As a system configuration user,
I want to be able to remove input feeds,
So that unused or un-necessary input feeds don't need to be kept running.
Priority Must Have
ID EXT-4
Title Remove publishing platform
Content As a system configuration user,
I want to be able to remove publishing platforms,
So that unused or un-necessary publishing platforms don't need to be kept running.
Priority
You might want to give access to your availability without showing your calendar.
http://tungle.me
Blogged in slightly altered form at: http://jaybyjayfresh.com/2009/08/14/how-to-log-into-hsbc-online-banking-from-the-command-line/
!Getting to the login page
Visiting {{{ http://www.hsbc.co.uk/1/2/ }}} sets two cookies:
* HSBC_COOKIEMI
* piba
The button for getting to the Internet Banking login has a href like this:
{{{
http://www.hsbc.co.uk/1/2/HSBCINTEGRATION/CAM10;jsessionid=0000JS99dpn5pfsbqa9G-jdUL4x:11j74l29q?IDV_URL=hsbc.MyHSBC_pib
}}}
which matches the URL you get to to log in.
Alternatively, you can go straight to
{{{
http://www.hsbc.co.uk/1/2/HSBCINTEGRATION/
}}}
even without any cookies set. The response sets these cookies:
{{{
Set-Cookie: HSBC_COOKIEMI=e7f408d0-75f9-11de-8a43-000408000305;Domain=;Expires=Sun, 20-Jul-2014 13:25:06 GMT;Path=/
Set-Cookie: CAMToken=GCPyfe4/i1TFoVS3L/z4qRhzbNs=;Path=/1; Secure
Set-Cookie: piba=73472940.21248.0000; path=/
}}}
!Submitting your Internet Banking number
This POSTs to:
{{{
https://www.hsbc.co.uk/1/2/;jsessionid=0000EzVqNO88EsWt4lhQhIBMHVE:12c58sh8k?idv_cmd=idv.CustomerMigration
}}}
Note the {{{jsessionid=...}}} on the end of the URL, which is different for each session; the POSTed content:
{{{
userid=IBxxxxxxxxxx&StartMigration=
}}}
The response is a 302 to:
{{{
https://www.hsbc.co.uk/1/2/!ut/p/kcxml/04_Sj9SPykssy0xPLMnMz0vM0Y_QjzKLN4o38nYFSZnFm8cbm-pHmsWbxjt7QkQM4h3hAkH6BbmhEeWOiooAtvLMKw!!;jsessionid=0000EzVqNO88EsWt4lhQhIBMHVE:12c58sh8k;jsessionid=0000EzVqNO88EsWt4lhQhIBMHVE:12c58sh8k
}}}
and sets this cookie:
{{{
Set-Cookie: CAMToken=ItaH8ksgEuKY6vv6YC5nRJSTiEg=;Path=/1; Secure
}}}
The bits of your PIN that the system wants are contained in this structure in the content:
{{{
<div class="extPibRow hsbcRow">
<div class="logonPageAlignment">
<p>
<strong>The</strong>
<span class="hsbcTextHighlight"> <strong>FIRST</strong></span>
<strong>and</strong>
<span class="hsbcTextHighlight"> <strong>NEXT TO LAST</strong></span>
<strong>and</strong>
<span class="hsbcTextHighlight"> <strong>LAST</strong></span>
}}}
!Submitting login details
This POSTs to:
{{{
https://www.hsbc.co.uk/1/2/;jsessionid=0000PPUs1fHtg7XOIHMqjvIsoHW:11j74lld0;jsessionid=0000PPUs1fHtg7XOIHMqjvIsoHW:11j74lld0?idv_cmd=idv.Authentication
}}}
with content:
{{{
userid=IBxxxxxxxxxx&memorableAnswer=xxxxxx&password=xxx
}}}
The response is a 302 with this location:
{{{
https://www.hsbc.co.uk/1/2/!ut/p/kcxml/04_Sj9SPykssy0xPLMnMz0vM0Y_QjzKLN4w38nYFSZnFm8cbm-pHoggZxDuiiZjEm7khhIL0C3JDQyPKHRUBimrSIQ!!;jsessionid=0000PPUs1fHtg7XOIHMqjvIsoHW:11j74lld0;jsessionid=0000PPUs1fHtg7XOIHMqjvIsoHW:11j74lld0
}}}
and sets these cookies:
{{{
Set-Cookie: CAMToken=1HOPXe7zOn2qQ/dp28qb1yeozS0=;Path=/1; Secure
}}}
!Continuing with login
An additional step happens due to the JavaScript in the page. There is a GET to:
{{{
https://www.hsbc.co.uk/1/2/personal/internet-banking;jsessionid=0000PPUs1fHtg7XOIHMqjvIsoHW:11j74lld0;jsessionid=0000PPUs1fHtg7XOIHMqjvIsoHW:11j74lld0?BlitzToken=blitz
}}}
The response is a 200.
!What you can get once you're logged in - example - accounts' balance
The account summary information on the left-hand column is in a structure like this:
{{{
<div id="jsAccountDetails" class="hsbcDivletBoxContent">
<div class="hsbcDivletBoxTitle" id="jsHideAccounts" style="display: none;">
<div class="hsbcDivletBoxRow">
<a title="Hide my balances summary" onkeypress="this.onclick" onclick="hideAccounts(); return false;" href="#" class="hsbcDivletBoxRowText">
<span class="hsbcDivletBoxRowImage">
<img border="0" title="Hide my balances summary" src="/1/themes/html/hsbc_ukpersonal_ib/icon_balances.gif"/>
</span>
<span>Hide balances</span>
</a>
</div>
<div class="hsbcSeparator"/></div>
<div class="hsbcDivletBoxRow hsbcDivletBoxRowNoLink">
<span class="hsbcDivletBoxRowText">
<strong>GRAD PLUS</strong>
</span>
</div>
<br/>
<div class="hsbcDivletBoxRow hsbcDivletBoxRowNoLink">
<span class="hsbcDivletBoxRowText">LISTER JR</span>
</div>
<br/>
<div class="hsbcDivletBoxRow hsbcDivletBoxRowNoLink">
<span class="hsbcDivletBoxRowText"> 40-27-16 91466410 </span>
</div>
<br/>
<div class="hsbcDivletBoxRow hsbcDivletBoxRowNoLink">
<span class="hsbcDivletBoxRowText">
<strong> £ xx.xx C</strong>
</span>
</div>
<br/>
<div class="hsbcSeparator"></div>
<br/>
</div>
</div>
}}}
/************************
* ScreenScraper plugin *
*************************
Description:
The ScreenScraper object provides methods which allow you to describe a set of interactions or 'steps'
with a remote system that achieves a particular goal, e.g. adding an item to a database. It assumes
that each interaction is made up of an HTTP request to the remote system, which returns HTML containing
a form, that itself contains the INPUT elements used to construct the next request to the system. It is
possible to provide custom inputs at this point, useful if for instance, the system expects human input.
The last part in the interaction is to create a data string suitable for use by the next interaction.
Creating a new ScreenScraper:
var ss = new ScreenScraper(endHandler);
where:
endHandler is a function to call when the step is done; endHandler is called with the ScreenScraper object as a parameter
Adding a generic step to the ScreenScraper:
ss.addStep(name,{
url:"http://www.example.com",
method:"POST",
form:"DefaultForm",
userInputs:{
i_1:"some",
i_2:"test",
i_3:"custom",
i_4:"data"
},
handler:myHandlerFunc,
errorMessage:"Error getting http://www.example.com",
nextStep:"TestStep2"
});
where:
name is a string used to identify this step
url is the url for the remote system used in this step
method is the HTTP verb used in this step
form is the name or id of the form to look for in the response from the host (name used in preference)
customInputs is an object of extra/replacement key-value pairs to include in data sent to the host
handler is a function that gives you access to the key-value pairs, the responseText, any HTML document created from that and the ScreenScraper object before moving onto the next step
errorMessage is the message to put in the error thrown if the HTTP request fails
nextStep is the 'name' of the step to use next
Starting off a series of steps:
ss.go(firstStep);
where:
firstStep is the value of the 'name' key of the step to start from
Often will want to use some data in the first request, which can come from the current document. If a step does not have a url parameter, the current document will be searched for the form instead. If you provide a userInputs parameter, these will be used even if there is no form parameter. An as example, a pair of steps, starting by pulling data from a form in the document:
ss.addStep('a first step', {
form:'myForm',
nextStep:'login'
});
ss.addStep('login', {
url:'http://www.mydomain.com/login',
method:'POST'
});
ss.go('firstStep');
This example illustrates the use of custom inputs in the first step:
ss.addStep('another first step', {
userInputs: {
name:'ANOther',
password:'password'
},
nextStep('login')
});
ss.addStep('login', {
url:'http://www.mydomain.com/login',
method:'POST'
});
Using a custom handler function in a step:
ss.addStep('myCustomStep', {
form:'DefaultForm',
userInputs:{
test1:'testValue'
},
handler:function(keyValuePairs,responseText) {
global_var_step_length = responseText.length;
}
});
handler is called with the keyValuePairs as the first argument, the responseText as the second argument, the doc as the third argument and the ScreenScraper object as the fourth argument
Error handling:
ss.addStep('myErrorStep', {
url:'http://www.doesnotexist.om',
errorMessage:'Error in myErrorStep'
});
If an exception occurs during the HTTP request for a step, errorMessage will be passed to the ScreenScraper error function along with the exception.
At the moment, all other errors are handled by throwing exceptions with internal information; this will probably change to include step-specific information and be routed through the ScreenScraper error function.
Branching:
function firstBit() {
var ss = new ScreenScraper(secondBit);
ss.addStep('a first step', {
url:'http://www.example.com/step1',
handler:function(keyValuePairs,responseText,doc,scraper) {
var skipAStep;
if(responseText.indexOf('Skip this step')!==-1) {
skipAStep = true;
} else {
skipAStep = false;
}
scraper.addEndVars({
skipAStep:skipAStep,
keyValuePairs:keyValuePairs
});
}
});
}
function secondBit(params) {
var skipAStep = params.skipAStep;
var keyValuePairs = params.keyValuePairs;
var ss = new ScreenScraper();
ss.addStep('handleBranch', {
userInputs:keyValuePairs,
url:skipAStep ? http://www.example.com/step3 : http://www.example.com/step2
});
}
Branching is not currently very easy, but you can do it. By branching, the general case I am referring to is changing the logical
progression through a series of steps based on the response to a request. The example above illustrates one method of branching, using
the ScreenScraper object's addEndVars method to set some variables to be passed through to the second stage, which is called as the
endHandler to the first stage. The first step of the second stage deals uses the passed variables to decided, in this case, which url
to call - it could just as easily make a decision about which nextStep to set.
What we really want to be able to do for branching is something like this:
var ss = new ScreenScraper();
ss.addStep('a first step', {
url:'http://www.example.com/step1',
handler:function() {
if(responseText.indexOf('Skip this step')!==-1) {
stepVars.skipAStep = true;
} else {
stepVars.skipAStep = false;
}
},
nextStep:'my branching step'
});
ss.addStep('my branching step', {
url:stepVars.skipAStep ? http://www.example.com/step3 : http://www.example.com/step2
});
This is not doable with the current framework, since the variable stepVars.skipAStep used when adding 'my branching step' would be evaluated at the moment of adding the step, not when the step is called, which is the desirable behaviour. A way around this could be to use a function for the second argument of the addStep method, although I have not worked through this idea yet. (22/10/08)
*/
function ScreenScraper(endHandler,errorHandler) {
if(endHandler && typeof endHandler !== "function") {
var e = new Error();
e.message = "Error in ScreenScraper constructor - first argument should be a function or undefined";
throw e;
} else {
this.endHandler = endHandler;
}
if(errorHandler && typeof errorHandler !== "function") {
var e = new Error();
e.message = "Error in ScreenScraper constructor - second argument should be a function or undefined";
throw e;
} else {
this.errorHandler = errorHandler;
}
this.steps = {};
this.endVars = {};
this.context = {};
this.setContext = function(params) {
this.context = {};
if(params.url) {
this.context.url = params.url;
}
if(params.method) {
this.context.method = params.method;
}
if(params.headers) {
this.context.headers = params.headers;
}
if(params.form) {
this.context.form = params.form;
}
if(params.userInputs) {
this.context.userInputs = params.userInputs;
}
if(params.handler) {
this.context.handler = params.handler;
}
if(params.errorMessage) {
this.context.errorMessage = params.errorMessage;
}
if(params.nextStep) {
this.context.nextStep = params.nextStep;
}
};
// note: should think about whether this is what I really want to do - thinking about chucking this.data or putting it second
this.end = function() {
this.endHandler ? this.endHandler(this.data,this.endVars) : this.genericEnd(this.data,this.endVars);
};
this.error = function(ex,errorMessage) {
this.errorHandler ? this.errorHandler(ex,errorMessage) : this.genericError(ex,errorMessage);
};
this.genericEnd = function(data,endVars) {
console.log('genericEnd',data,endVars);
};
this.genericError = function(ex,errorMessage) {
console.log('genericError',ex,errorMessage);
};
}
ScreenScraper.prototype.go = function(firstStep) {
if(!firstStep || typeof firstStep !== 'string') {
var e = new Error();
e.message = 'Error in ScreenScraper.prototype.go - the first argument should be a string';
throw e;
}
this.context.nextStep = firstStep;
this.nextStep();
};
ScreenScraper.prototype.stop = function() {
this.context.nextStep = null;
this.endHandler = function() {};
};
ScreenScraper.prototype.addStep = function(name,obj) {
if(!name || typeof name !== "string") {
var e = new Error();
e.message = "Error in ScreenScraper.prototype.addStep - first argument should be a string";
throw e;
}
if(!ScreenScraper.is_object(obj)) {
var e = new Error();
e.message = "Error in ScreenScraper.prototype.addStep - second argument should be an object";
throw e;
}
this.steps[name] = obj;
};
ScreenScraper.prototype.branchStep = function(name) {
var e;
if(!name || typeof name !== "string") {
e = new Error();
e.message = "Error in ScreenScraper.prototype.branchStep - first argument should be a string";
throw e;
}
if(!this.steps[name]) {
e = new Error();
e.message = "Error in ScreenScraper.prototype.branchStep - there is no step called "+name;
throw e;
} else {
this.context.nextStep = name;
}
};
ScreenScraper.prototype.nextStep = function() {
var nextStep = this.context.nextStep;
if(!nextStep) {
this.end();
} else {
step = this.steps[nextStep];
if(!step) {
var e = new Error();
e.message = 'Error in ScreenScraper.prototype.nextStep - there is no step called '+nextStep;
throw e;
} else {
this.setContext(step);
this.makeRequest();
}
}
};
ScreenScraper.prototype.makeRequest = function() {
var context = this.context;
if(context.url) {
if(!context.method) {
context.method = "GET";
}
var that = this;
var callback = function(status,params,responseText,url,xhr) {
console.log(xhr,url);
console.log(xhr.getAllResponseHeaders());
that.extractData(responseText);
};
try {
httpReq(
context.method,
context.method==="POST" ? context.url : context.url+"?"+this.data,
callback,
null,
context.headers ? context.headers : null,
context.method==="POST" ? this.data : null,
null,null,null,true
);
} catch(ex) {
this.error(ex,context.errorMessage ? context.errorMessage : "");
}
} else {
this.extractData();
}
};
ScreenScraper.prototype.extractData = function(source) {
var context = this.context;
var formId = context.form;
var keyValuePairs = {};
var that = this;
var getPairs = function(doc) {
if(formId) {
var form;
if(typeof formId === "number") {
form = doc.forms[formId];
} else {
form = doc[formId];
}
if(!form) {
form = doc.getElementById(formId);
}
if(form) {
var elem;
for(var i=0;i<form.length;i++) {
elem = form[i];
keyValuePairs[elem.name] = elem.value;
}
} else {
var e = new Error();
e.message = 'Error in ScreenScraper.prototype.extractData - specified form not found: '+formId;
throw e;
}
}
if(context.userInputs) {
keyValuePairs = ScreenScraper.replaceValues(keyValuePairs,context.userInputs);
}
if(context.handler) {
context.handler(keyValuePairs,source,doc,that);
}
that.data = ScreenScraper.serialize(keyValuePairs,true);
that.nextStep();
};
if(source) {
HTMLParser.parseText(source,function(doc) {
getPairs(doc);
});
} else {
getPairs(document);
}
};
ScreenScraper.prototype.addEndVars = function(obj) {
if(!ScreenScraper.is_object(obj)) {
var e = new Error();
e.message = "Error in ScreenScraper.prototype.addEndVars";
throw e;
}
merge(this.endVars,obj);
};
// crockford
ScreenScraper.is_array = function(value) {
return value &&
typeof value === 'object' &&
typeof value.length === 'number' &&
typeof value.splice === 'function' &&
!(value.propertyIsEnumerable('length'));
};
ScreenScraper.is_object = function(value) {
return value &&
typeof value === 'object' &&
!ScreenScraper.is_array(value);
};
ScreenScraper.replaceValues = function(keyValuePairs,userInputs) {
if(!ScreenScraper.is_object(keyValuePairs)) {
var e = new Error();
e.message = "Error in ScreenScraper.replaceValues - first argument should be an object";
throw e;
}
if(!ScreenScraper.is_object(userInputs) && typeof userInputs !== 'undefined') {
var e = new Error();
e.message = "Error in ScreenScraper.replaceValues - second argument should be an object or undefined";
throw e;
}
if(!window.merge || typeof window.merge !== "function") {
window.merge = function(dst,src,preserveExisting) {
for(var i in src) {
if(!preserveExisting || dst[i] === undefined) {
dst[i] = src[i];
}
}
return dst;
};
}
return window.merge(keyValuePairs,userInputs);
};
ScreenScraper.serialize = function(obj, encode) {
if(!obj || typeof obj !== 'object' || ScreenScraper.is_array(obj)) {
var e = new Error();
e.message = "Error in ScreenScraper.serialize - first argument should be an object";
throw e;
}
if(encode !== undefined && typeof encode !== 'boolean') {
var e = new Error();
e.message = "Error in ScreenScraper.serialize - second argument should be a boolean or undefined";
throw e;
}
var out = '';
for(var a in obj) {
if(obj.hasOwnProperty(a)) {
if(encode) {
out+= '&' + encodeURIComponent(a) +"="+ encodeURIComponent(obj[a]);
} else {
out+= '&' + a +"="+ obj[a];
}
}
}
return out.substr(1);
};
Idea: can one make an online bank transfer programmatically?
Context: fee-free micropayments
Got the interest of HSBC Innovations - see [[Call with Aden Davies]]; also, Crunch accounting interested for automation of sucking in transaction details
!What do you need to do to make an online transfer? Case study HSBC online banking.
[[ScreenScrape: logging into HSBC website]]
[[Writing a HSBC ScreenScraper]]
#Enter Internet Banking ID, move to next stage
#Enter date of birth and whichever 3 digits are asked for from security number, move to next stage
#Select current account
#Select "make a payment"
#Select "friends, family & others" to make a new payment
#Enter payment details, submit
#Confirm
#Done
!What problems exist with doing this programatically?
#You need to understand which digits of the security code are being asked for
** It might be a case of reading text; it might be a case of reading images
#You need to have access to the security code (and other login credentials) - not everyone would be happy giving this out
** You could break the process up to request and accept input from the person making the transaction
!How would you present this?
#Embeddable payment on a website
#Library
!How would you use this?
#See [[Oyster card payments]]
#As a mobile app to let you make payments; merchants could sign up to get an ID that is quicker to input than their bank details
!Some links
Had a chat with Chris Adams, he told me about these things:
#[[Bankjob|http://wiki.github.com/rhubarb/bankjob]] - ruby tool to get documents out of online banking sites (presumably has good description of procedure to get in to different services)
#[[Opencoin|http://opencoin.org/front-page]] - "open-source electronic money"
#[[The Nag|http://thenag.net]] - Chris' recent project
#[[HSBC and Pret|http://www.google.com/search?hl=en&client=safari&rls=en-us&q=hsbc+pret+a+manger+pin&aq=f&oq=&aqi=]] getting together to make PINless small-value transactions
#[[Visa Wave|http://www.google.com/search?client=safari&rls=en-us&q=visa+wave+increased+sales&ie=UTF-8&oe=UTF-8]] showing increased sales
#[[Worn Again|http://www.wornagain.co.uk/]] - where Chris has been working recently
#[[Anti-apathy|http://antiapathy.org/?page_id=2]] - the company sponsoring The Nag
#[[BBC Glow|http://www.bbc.co.uk/glow/docs/1.5/api/glow.widgets.editor.shtml]] - recently released and with an in-built, nice-n-simple rich-text editor
JavaScript library for automating chains of interaction with a website.
//{{{
config.macros.scrumptious = {};
config.macros.scrumptious.handler = function(place,macroName,params) {
var href = "javascript:(function()%20{var%20script%20=%20document.createElement(%22script%22);script.type%20=%20%22text/javascript%22;script.src%20=%20%22http://scrumptious.tv/comments/static/js/injection.js%22;document.body.appendChild(script);var%20loadTimer%20=%20setInterval(function()%20{if%20(window.openScrumptiousComments)%20{window.openScrumptiousComments();clearTimeout(loadTimer);}},%20100);})();"
var text = params[0] || 'comment';
var btn = createTiddlyButton(place, text, 'open scrumptious sidebar');
btn.setAttribute('href',href);
};
//}}}
Summary: http://tinyurl.com/yellowcarltdinvoicesstatus
<<list filter '[title[Invoice 0]][sort[-title]]'>>
It's probably a good idea to get some freelance contracts sorted out before going self-employed.
A few possibilties:
* [[Notion Magazine SMS services]]
* Freelance JavaScript work
* Tager
* LShift
* [[Anna Freud Centre]]
* ILGA
* UnaMesa
Met through the Cube, Fri 9th July 2010.
Sheraz - industrial designer - making an iPhone/Android app to help muslims figure out their prayer times. Looking for a developer, as his partner is really busy.
!Log
9/7/2010
* 11:15 - 12:20 meeting to look at the project; suggested more market research to establish profit-share risk and that I look for a quote - Sheraz will send me a good brief for me to use
<<search>><<scrumptious 'comment on this view'>><<closeAll>><<permaview>><<newTiddler>><<newJournal "DD MMM YYYY" "journal">><<saveChanges>><<tiddler TspotSidebar>><<slider chkSliderOptionsPanel OptionsPanel "options »" "Change TiddlyWiki advanced options">>
<<tabs txtMainTab "Timeline" "Timeline" TabTimeline "All" "All tiddlers" TabAll "Tags" "All tags" TabTags "More" "More lists" TabMore "Attn" "AttentionTracker tiddlers" AttnTab>>
/***
|''Name:''|SimileTimelineBundlePlugin|
|''Description:''|[[Simile Timelines|http://simile.mit.edu/SimileTimeline/]] |
|''Author:''|Martin Budden (mjbudden [at] gmail [dot] com)|
|''Source:''|http://www.martinswiki.com/#SimileTimelineBundlePlugin |
|''CodeRepository:''|http://svn.tiddlywiki.org/Trunk/contributors/MartinBudden/plugins/SimileTimelineBundlePlugin.js |
|''Version:''|0.1.0|
|''Date:''|Mar 4, 2007|
|''Comments:''|Please make comments at http://groups.google.co.uk/group/TiddlyWikiDev |
|''License:''|BSD-style license from MIT|
|''~CoreVersion:''|2.2|
***/
/*{{{*/
// Ensure that the SimileTimelineBundlePlugin is only installed once.
if(!version.extensions.SimileTimelineBundlePlugin) {
version.extensions.SimileTimelineBundlePlugin = {installed:true};
if(version.major < 2 || (version.major == 2 && version.minor < 2))
{alertAndThrow('SimileTimelineBundlePlugin requires TiddlyWiki 2.2 or newer.');}
// Following code is from Simile (Semantic Interoperability of Metadata and Information in unLike Environments).
// SIMILE is a joint project conducted by the MIT Libraries and MIT CSAIL.
// Code is released under a BSD-style license.
// See:
// http://simile.mit.edu/repository/timeline/trunk/src/webapp/api/bundle.js
var Timeline = new Object();
Timeline.Platform = new Object();
Timeline.Platform.serverLocale = "en";
Timeline.Platform.clientLocale = "en";
/* timeline.js */
Timeline.strings={};Timeline.create=function(elmt,bandInfos,orientation,unit){return new Timeline._Impl(elmt,bandInfos,orientation,unit);};Timeline.HORIZONTAL=0;Timeline.VERTICAL=1;Timeline._defaultTheme=null;Timeline.createBandInfo=function(params){var theme=("theme"in params)?params.theme:Timeline.getDefaultTheme();var eventSource=("eventSource"in params)?params.eventSource:null;var ether=new Timeline.LinearEther({centersOn:("date"in params)?params.date:new Date(),interval:Timeline.DateTime.gregorianUnitLengths[params.intervalUnit],pixelsPerInterval:params.intervalPixels});var etherPainter=new Timeline.GregorianEtherPainter({unit:params.intervalUnit,multiple:("multiple"in params)?params.multiple:1,theme:theme,align:("align"in params)?params.align:undefined});var layout=new Timeline.StaticTrackBasedLayout({eventSource:eventSource,ether:ether,showText:("showEventText"in params)?params.showEventText:true,theme:theme});var eventPainterParams={showText:("showEventText"in params)?params.showEventText:true,layout:layout,theme:theme};if("trackHeight"in params){eventPainterParams.trackHeight=params.trackHeight;}
if("trackGap"in params){eventPainterParams.trackGap=params.trackGap;}
var eventPainter=new Timeline.DurationEventPainter(eventPainterParams);return{width:params.width,eventSource:eventSource,timeZone:("timeZone"in params)?params.timeZone:0,ether:ether,etherPainter:etherPainter,eventPainter:eventPainter};};Timeline.createHotZoneBandInfo=function(params){var theme=("theme"in params)?params.theme:Timeline.getDefaultTheme();var eventSource=("eventSource"in params)?params.eventSource:null;var ether=new Timeline.HotZoneEther({centersOn:("date"in params)?params.date:new Date(),interval:Timeline.DateTime.gregorianUnitLengths[params.intervalUnit],pixelsPerInterval:params.intervalPixels,zones:params.zones});var etherPainter=new Timeline.HotZoneGregorianEtherPainter({unit:params.intervalUnit,zones:params.zones,theme:theme,align:("align"in params)?params.align:undefined});var layout=new Timeline.StaticTrackBasedLayout({eventSource:eventSource,ether:ether,theme:theme});var eventPainterParams={showText:("showEventText"in params)?params.showEventText:true,layout:layout,theme:theme};if("trackHeight"in params){eventPainterParams.trackHeight=params.trackHeight;}
if("trackGap"in params){eventPainterParams.trackGap=params.trackGap;}
var eventPainter=new Timeline.DurationEventPainter(eventPainterParams);return{width:params.width,eventSource:eventSource,timeZone:("timeZone"in params)?params.timeZone:0,ether:ether,etherPainter:etherPainter,eventPainter:eventPainter};};Timeline.getDefaultTheme=function(){if(Timeline._defaultTheme==null){Timeline._defaultTheme=Timeline.ClassicTheme.create(Timeline.Platform.getDefaultLocale());}
return Timeline._defaultTheme;};Timeline.setDefaultTheme=function(theme){Timeline._defaultTheme=theme;};Timeline.loadXML=function(url,f){var fError=function(statusText,status,xmlhttp){alert("Failed to load data xml from "+url+"\n"+statusText);};var fDone=function(xmlhttp){var xml=xmlhttp.responseXML;if(!xml.documentElement&&xmlhttp.responseStream){xml.load(xmlhttp.responseStream);}
f(xml,url);};Timeline.XmlHttp.get(url,fError,fDone);};Timeline.loadJSON=function(url,f){var fError=function(statusText,status,xmlhttp){alert("Failed to load json data from "+url+"\n"+statusText);};var fDone=function(xmlhttp){f(eval('('+xmlhttp.responseText+')'),url);};Timeline.XmlHttp.get(url,fError,fDone);};Timeline._Impl=function(elmt,bandInfos,orientation,unit){this._containerDiv=elmt;this._bandInfos=bandInfos;this._orientation=orientation==null?Timeline.HORIZONTAL:orientation;this._unit=(unit!=null)?unit:Timeline.NativeDateUnit;this._initialize();};Timeline._Impl.prototype.dispose=function(){for(var i=0;i<this._bands.length;i++){this._bands[i].dispose();}
this._bands=null;this._bandInfos=null;this._containerDiv.innerHTML="";};Timeline._Impl.prototype.getBandCount=function(){return this._bands.length;};Timeline._Impl.prototype.getBand=function(index){return this._bands[index];};Timeline._Impl.prototype.layout=function(){this._distributeWidths();};Timeline._Impl.prototype.paint=function(){for(var i=0;i<this._bands.length;i++){this._bands[i].paint();}};Timeline._Impl.prototype.getDocument=function(){return this._containerDiv.ownerDocument;};Timeline._Impl.prototype.addDiv=function(div){this._containerDiv.appendChild(div);};Timeline._Impl.prototype.removeDiv=function(div){this._containerDiv.removeChild(div);};Timeline._Impl.prototype.isHorizontal=function(){return this._orientation==Timeline.HORIZONTAL;};Timeline._Impl.prototype.isVertical=function(){return this._orientation==Timeline.VERTICAL;};Timeline._Impl.prototype.getPixelLength=function(){return this._orientation==Timeline.HORIZONTAL?this._containerDiv.offsetWidth:this._containerDiv.offsetHeight;};Timeline._Impl.prototype.getPixelWidth=function(){return this._orientation==Timeline.VERTICAL?this._containerDiv.offsetWidth:this._containerDiv.offsetHeight;};Timeline._Impl.prototype.getUnit=function(){return this._unit;};Timeline._Impl.prototype.loadXML=function(url,f){var tl=this;var fError=function(statusText,status,xmlhttp){alert("Failed to load data xml from "+url+"\n"+statusText);tl.hideLoadingMessage();};var fDone=function(xmlhttp){try{var xml=xmlhttp.responseXML;if(!xml.documentElement&&xmlhttp.responseStream){xml.load(xmlhttp.responseStream);}
f(xml,url);}finally{tl.hideLoadingMessage();}};this.showLoadingMessage();window.setTimeout(function(){Timeline.XmlHttp.get(url,fError,fDone);},0);};Timeline._Impl.prototype.loadJSON=function(url,f){var tl=this;var fError=function(statusText,status,xmlhttp){alert("Failed to load json data from "+url+"\n"+statusText);tl.hideLoadingMessage();};var fDone=function(xmlhttp){try{f(eval('('+xmlhttp.responseText+')'),url);}finally{tl.hideLoadingMessage();}};this.showLoadingMessage();window.setTimeout(function(){Timeline.XmlHttp.get(url,fError,fDone);},0);};Timeline._Impl.prototype._initialize=function(){var containerDiv=this._containerDiv;var doc=containerDiv.ownerDocument;containerDiv.className=containerDiv.className.split(" ").concat("timeline-container").join(" ");while(containerDiv.firstChild){containerDiv.removeChild(containerDiv.firstChild);}
var elmtCopyright=Timeline.Graphics.createTranslucentImage(doc,Timeline.urlPrefix+(this.isHorizontal()?"images/copyright-vertical.png":"images/copyright.png"));elmtCopyright.className="timeline-copyright";elmtCopyright.title="Timeline (c) SIMILE - http://simile.mit.edu/timeline/";Timeline.DOM.registerEvent(elmtCopyright,"click",function(){window.location="http://simile.mit.edu/timeline/";});containerDiv.appendChild(elmtCopyright);this._bands=[];for(var i=0;i<this._bandInfos.length;i++){var band=new Timeline._Band(this,this._bandInfos[i],i);this._bands.push(band);}
this._distributeWidths();for(var i=0;i<this._bandInfos.length;i++){var bandInfo=this._bandInfos[i];if("syncWith"in bandInfo){this._bands[i].setSyncWithBand(this._bands[bandInfo.syncWith],("highlight"in bandInfo)?bandInfo.highlight:false);}}
var message=Timeline.Graphics.createMessageBubble(doc);message.containerDiv.className="timeline-message-container";containerDiv.appendChild(message.containerDiv);message.contentDiv.className="timeline-message";message.contentDiv.innerHTML="<img src='"+Timeline.urlPrefix+"images/progress-running.gif' /> Loading...";this.showLoadingMessage=function(){message.containerDiv.style.display="block";};this.hideLoadingMessage=function(){message.containerDiv.style.display="none";};};Timeline._Impl.prototype._distributeWidths=function(){var length=this.getPixelLength();var width=this.getPixelWidth();var cumulativeWidth=0;for(var i=0;i<this._bands.length;i++){var band=this._bands[i];var bandInfos=this._bandInfos[i];var widthString=bandInfos.width;var x=widthString.indexOf("%");if(x>0){var percent=parseInt(widthString.substr(0,x));var bandWidth=percent*width/100;}else{var bandWidth=parseInt(widthString);}
band.setBandShiftAndWidth(cumulativeWidth,bandWidth);band.setViewLength(length);cumulativeWidth+=bandWidth;}};Timeline._Band=function(timeline,bandInfo,index){this._timeline=timeline;this._bandInfo=bandInfo;this._index=index;this._locale=("locale"in bandInfo)?bandInfo.locale:Timeline.Platform.getDefaultLocale();this._timeZone=("timeZone"in bandInfo)?bandInfo.timeZone:0;this._labeller=("labeller"in bandInfo)?bandInfo.labeller:timeline.getUnit().createLabeller(this._locale,this._timeZone);this._dragging=false;this._changing=false;this._originalScrollSpeed=5;this._scrollSpeed=this._originalScrollSpeed;this._onScrollListeners=[];var b=this;this._syncWithBand=null;this._syncWithBandHandler=function(band){b._onHighlightBandScroll();};this._selectorListener=function(band){b._onHighlightBandScroll();};var inputDiv=this._timeline.getDocument().createElement("div");inputDiv.className="timeline-band-input";this._timeline.addDiv(inputDiv);this._keyboardInput=document.createElement("input");this._keyboardInput.type="text";inputDiv.appendChild(this._keyboardInput);Timeline.DOM.registerEventWithObject(this._keyboardInput,"keydown",this,this._onKeyDown);Timeline.DOM.registerEventWithObject(this._keyboardInput,"keyup",this,this._onKeyUp);this._div=this._timeline.getDocument().createElement("div");this._div.className="timeline-band";this._timeline.addDiv(this._div);Timeline.DOM.registerEventWithObject(this._div,"mousedown",this,this._onMouseDown);Timeline.DOM.registerEventWithObject(this._div,"mousemove",this,this._onMouseMove);Timeline.DOM.registerEventWithObject(this._div,"mouseup",this,this._onMouseUp);Timeline.DOM.registerEventWithObject(this._div,"mouseout",this,this._onMouseOut);Timeline.DOM.registerEventWithObject(this._div,"dblclick",this,this._onDblClick);this._innerDiv=this._timeline.getDocument().createElement("div");this._innerDiv.className="timeline-band-inner";this._div.appendChild(this._innerDiv);this._ether=bandInfo.ether;bandInfo.ether.initialize(timeline);this._etherPainter=bandInfo.etherPainter;bandInfo.etherPainter.initialize(this,timeline);this._eventSource=bandInfo.eventSource;if(this._eventSource){this._eventListener={onAddMany:function(){b._onAddMany();},onClear:function(){b._onClear();}}
this._eventSource.addListener(this._eventListener);}
this._eventPainter=bandInfo.eventPainter;bandInfo.eventPainter.initialize(this,timeline);this._decorators=("decorators"in bandInfo)?bandInfo.decorators:[];for(var i=0;i<this._decorators.length;i++){this._decorators[i].initialize(this,timeline);}
this._bubble=null;};Timeline._Band.SCROLL_MULTIPLES=5;Timeline._Band.prototype.dispose=function(){this.closeBubble();if(this._eventSource){this._eventSource.removeListener(this._eventListener);this._eventListener=null;this._eventSource=null;}
this._timeline=null;this._bandInfo=null;this._labeller=null;this._ether=null;this._etherPainter=null;this._eventPainter=null;this._decorators=null;this._onScrollListeners=null;this._syncWithBandHandler=null;this._selectorListener=null;this._div=null;this._innerDiv=null;this._keyboardInput=null;this._bubble=null;};Timeline._Band.prototype.addOnScrollListener=function(listener){this._onScrollListeners.push(listener);};Timeline._Band.prototype.removeOnScrollListener=function(listener){for(var i=0;i<this._onScrollListeners.length;i++){if(this._onScrollListeners[i]==listener){this._onScrollListeners.splice(i,1);break;}}};Timeline._Band.prototype.setSyncWithBand=function(band,highlight){if(this._syncWithBand){this._syncWithBand.removeOnScrollListener(this._syncWithBandHandler);}
this._syncWithBand=band;this._syncWithBand.addOnScrollListener(this._syncWithBandHandler);this._highlight=highlight;this._positionHighlight();};Timeline._Band.prototype.getLocale=function(){return this._locale;};Timeline._Band.prototype.getTimeZone=function(){return this._timeZone;};Timeline._Band.prototype.getLabeller=function(){return this._labeller;};Timeline._Band.prototype.getIndex=function(){return this._index;};Timeline._Band.prototype.getEther=function(){return this._ether;};Timeline._Band.prototype.getEtherPainter=function(){return this._etherPainter;};Timeline._Band.prototype.getEventSource=function(){return this._eventSource;};Timeline._Band.prototype.getEventPainter=function(){return this._eventPainter;};Timeline._Band.prototype.layout=function(){this.paint();};Timeline._Band.prototype.paint=function(){this._etherPainter.paint();this._paintDecorators();this._paintEvents();};Timeline._Band.prototype.softLayout=function(){this.softPaint();};Timeline._Band.prototype.softPaint=function(){this._etherPainter.softPaint();this._softPaintDecorators();this._softPaintEvents();};Timeline._Band.prototype.setBandShiftAndWidth=function(shift,width){var inputDiv=this._keyboardInput.parentNode;var middle=shift+Math.floor(width/2);if(this._timeline.isHorizontal()){this._div.style.top=shift+"px";this._div.style.height=width+"px";inputDiv.style.top=middle+"px";inputDiv.style.left="-1em";}else{this._div.style.left=shift+"px";this._div.style.width=width+"px";inputDiv.style.left=middle+"px";inputDiv.style.top="-1em";}};Timeline._Band.prototype.getViewWidth=function(){if(this._timeline.isHorizontal()){return this._div.offsetHeight;}else{return this._div.offsetWidth;}};Timeline._Band.prototype.setViewLength=function(length){this._viewLength=length;this._recenterDiv();this._onChanging();};Timeline._Band.prototype.getViewLength=function(){return this._viewLength;};Timeline._Band.prototype.getTotalViewLength=function(){return Timeline._Band.SCROLL_MULTIPLES*this._viewLength;};Timeline._Band.prototype.getViewOffset=function(){return this._viewOffset;};Timeline._Band.prototype.getMinDate=function(){return this._ether.pixelOffsetToDate(this._viewOffset);};Timeline._Band.prototype.getMaxDate=function(){return this._ether.pixelOffsetToDate(this._viewOffset+Timeline._Band.SCROLL_MULTIPLES*this._viewLength);};Timeline._Band.prototype.getMinVisibleDate=function(){return this._ether.pixelOffsetToDate(0);};Timeline._Band.prototype.getMaxVisibleDate=function(){return this._ether.pixelOffsetToDate(this._viewLength);};Timeline._Band.prototype.getCenterVisibleDate=function(){return this._ether.pixelOffsetToDate(this._viewLength/2);};Timeline._Band.prototype.setMinVisibleDate=function(date){if(!this._changing){this._moveEther(Math.round(-this._ether.dateToPixelOffset(date)));}};Timeline._Band.prototype.setMaxVisibleDate=function(date){if(!this._changing){this._moveEther(Math.round(this._viewLength-this._ether.dateToPixelOffset(date)));}};Timeline._Band.prototype.setCenterVisibleDate=function(date){if(!this._changing){this._moveEther(Math.round(this._viewLength/2-this._ether.dateToPixelOffset(date)));}};Timeline._Band.prototype.dateToPixelOffset=function(date){return this._ether.dateToPixelOffset(date)-this._viewOffset;};Timeline._Band.prototype.pixelOffsetToDate=function(pixels){return this._ether.pixelOffsetToDate(pixels+this._viewOffset);};Timeline._Band.prototype.createLayerDiv=function(zIndex){var div=this._timeline.getDocument().createElement("div");div.className="timeline-band-layer";div.style.zIndex=zIndex;this._innerDiv.appendChild(div);var innerDiv=this._timeline.getDocument().createElement("div");innerDiv.className="timeline-band-layer-inner";if(Timeline.Platform.browser.isIE){innerDiv.style.cursor="move";}else{innerDiv.style.cursor="-moz-grab";}
div.appendChild(innerDiv);return innerDiv;};Timeline._Band.prototype.removeLayerDiv=function(div){this._innerDiv.removeChild(div.parentNode);};Timeline._Band.prototype.closeBubble=function(){if(this._bubble!=null){this._bubble.close();this._bubble=null;}};Timeline._Band.prototype.openBubbleForPoint=function(pageX,pageY,width,height){this.closeBubble();this._bubble=Timeline.Graphics.createBubbleForPoint(this._timeline.getDocument(),pageX,pageY,width,height);return this._bubble.content;};Timeline._Band.prototype.scrollToCenter=function(date){var pixelOffset=this._ether.dateToPixelOffset(date);if(pixelOffset<-this._viewLength/2){this.setCenterVisibleDate(this.pixelOffsetToDate(pixelOffset+this._viewLength));}else if(pixelOffset>3*this._viewLength/2){this.setCenterVisibleDate(this.pixelOffsetToDate(pixelOffset-this._viewLength));}
this._autoScroll(Math.round(this._viewLength/2-this._ether.dateToPixelOffset(date)));};Timeline._Band.prototype._onMouseDown=function(innerFrame,evt,target){this.closeBubble();this._dragging=true;this._dragX=evt.clientX;this._dragY=evt.clientY;};Timeline._Band.prototype._onMouseMove=function(innerFrame,evt,target){if(this._dragging){var diffX=evt.clientX-this._dragX;var diffY=evt.clientY-this._dragY;this._dragX=evt.clientX;this._dragY=evt.clientY;this._moveEther(this._timeline.isHorizontal()?diffX:diffY);this._positionHighlight();}};Timeline._Band.prototype._onMouseUp=function(innerFrame,evt,target){this._dragging=false;this._keyboardInput.focus();};Timeline._Band.prototype._onMouseOut=function(innerFrame,evt,target){var coords=Timeline.DOM.getEventRelativeCoordinates(evt,innerFrame);coords.x+=this._viewOffset;if(coords.x<0||coords.x>innerFrame.offsetWidth||coords.y<0||coords.y>innerFrame.offsetHeight){this._dragging=false;}};Timeline._Band.prototype._onDblClick=function(innerFrame,evt,target){var coords=Timeline.DOM.getEventRelativeCoordinates(evt,innerFrame);var distance=coords.x-(this._viewLength/2-this._viewOffset);this._autoScroll(-distance);};Timeline._Band.prototype._onKeyDown=function(keyboardInput,evt,target){if(!this._dragging){switch(evt.keyCode){case 27:break;case 37:case 38:this._scrollSpeed=Math.min(50,Math.abs(this._scrollSpeed*1.05));this._moveEther(this._scrollSpeed);break;case 39:case 40:this._scrollSpeed=-Math.min(50,Math.abs(this._scrollSpeed*1.05));this._moveEther(this._scrollSpeed);break;default:return true;}
this.closeBubble();Timeline.DOM.cancelEvent(evt);return false;}
return true;};Timeline._Band.prototype._onKeyUp=function(keyboardInput,evt,target){if(!this._dragging){this._scrollSpeed=this._originalScrollSpeed;switch(evt.keyCode){case 35:this.setCenterVisibleDate(this._eventSource.getLatestDate());break;case 36:this.setCenterVisibleDate(this._eventSource.getEarliestDate());break;case 33:this._autoScroll(this._timeline.getPixelLength());break;case 34:this._autoScroll(-this._timeline.getPixelLength());break;default:return true;}
this.closeBubble();Timeline.DOM.cancelEvent(evt);return false;}
return true;};Timeline._Band.prototype._autoScroll=function(distance){var b=this;var a=Timeline.Graphics.createAnimation(function(abs,diff){b._moveEther(diff);},0,distance,1000);a.run();};Timeline._Band.prototype._moveEther=function(shift){this.closeBubble();this._viewOffset+=shift;this._ether.shiftPixels(-shift);if(this._timeline.isHorizontal()){this._div.style.left=this._viewOffset+"px";}else{this._div.style.top=this._viewOffset+"px";}
if(this._viewOffset>-this._viewLength*0.5||this._viewOffset<-this._viewLength*(Timeline._Band.SCROLL_MULTIPLES-1.5)){this._recenterDiv();}else{this.softLayout();}
this._onChanging();}
Timeline._Band.prototype._onChanging=function(){this._changing=true;this._fireOnScroll();this._setSyncWithBandDate();this._changing=false;};Timeline._Band.prototype._fireOnScroll=function(){for(var i=0;i<this._onScrollListeners.length;i++){this._onScrollListeners[i](this);}};Timeline._Band.prototype._setSyncWithBandDate=function(){if(this._syncWithBand){var centerDate=this._ether.pixelOffsetToDate(this.getViewLength()/2);this._syncWithBand.setCenterVisibleDate(centerDate);}};Timeline._Band.prototype._onHighlightBandScroll=function(){if(this._syncWithBand){var centerDate=this._syncWithBand.getCenterVisibleDate();var centerPixelOffset=this._ether.dateToPixelOffset(centerDate);this._moveEther(Math.round(this._viewLength/2-centerPixelOffset));if(this._highlight){this._etherPainter.setHighlight(this._syncWithBand.getMinVisibleDate(),this._syncWithBand.getMaxVisibleDate());}}};Timeline._Band.prototype._onAddMany=function(){this._paintEvents();};Timeline._Band.prototype._onClear=function(){this._paintEvents();};Timeline._Band.prototype._positionHighlight=function(){if(this._syncWithBand){var startDate=this._syncWithBand.getMinVisibleDate();var endDate=this._syncWithBand.getMaxVisibleDate();if(this._highlight){this._etherPainter.setHighlight(startDate,endDate);}}};Timeline._Band.prototype._recenterDiv=function(){this._viewOffset=-this._viewLength*(Timeline._Band.SCROLL_MULTIPLES-1)/2;if(this._timeline.isHorizontal()){this._div.style.left=this._viewOffset+"px";this._div.style.width=(Timeline._Band.SCROLL_MULTIPLES*this._viewLength)+"px";}else{this._div.style.top=this._viewOffset+"px";this._div.style.height=(Timeline._Band.SCROLL_MULTIPLES*this._viewLength)+"px";}
this.layout();};Timeline._Band.prototype._paintEvents=function(){this._eventPainter.paint();};Timeline._Band.prototype._softPaintEvents=function(){this._eventPainter.softPaint();};Timeline._Band.prototype._paintDecorators=function(){for(var i=0;i<this._decorators.length;i++){this._decorators[i].paint();}};Timeline._Band.prototype._softPaintDecorators=function(){for(var i=0;i<this._decorators.length;i++){this._decorators[i].softPaint();}};
/* platform.js */
Timeline.Platform.os={isMac:false,isWin:false,isWin32:false,isUnix:false};Timeline.Platform.browser={isIE:false,isNetscape:false,isMozilla:false,isFirefox:false,isOpera:false,isSafari:false,majorVersion:0,minorVersion:0};(function(){var an=navigator.appName.toLowerCase();var ua=navigator.userAgent.toLowerCase();Timeline.Platform.os.isMac=(ua.indexOf('mac')!=-1);Timeline.Platform.os.isWin=(ua.indexOf('win')!=-1);Timeline.Platform.os.isWin32=Timeline.Platform.isWin&&(ua.indexOf('95')!=-1||ua.indexOf('98')!=-1||ua.indexOf('nt')!=-1||ua.indexOf('win32')!=-1||ua.indexOf('32bit')!=-1);Timeline.Platform.os.isUnix=(ua.indexOf('x11')!=-1);Timeline.Platform.browser.isIE=(an.indexOf("microsoft")!=-1);Timeline.Platform.browser.isNetscape=(an.indexOf("netscape")!=-1);Timeline.Platform.browser.isMozilla=(ua.indexOf("mozilla")!=-1);Timeline.Platform.browser.isFirefox=(ua.indexOf("firefox")!=-1);Timeline.Platform.browser.isOpera=(an.indexOf("opera")!=-1);var parseVersionString=function(s){var a=s.split(".");Timeline.Platform.browser.majorVersion=parseInt(a[0]);Timeline.Platform.browser.minorVersion=parseInt(a[1]);};var indexOf=function(s,sub,start){var i=s.indexOf(sub,start);return i>=0?i:s.length;};if(Timeline.Platform.browser.isMozilla){var offset=ua.indexOf("mozilla/");if(offset>=0){parseVersionString(ua.substring(offset+8,indexOf(ua," ",offset)));}}
if(Timeline.Platform.browser.isIE){var offset=ua.indexOf("msie ");if(offset>=0){parseVersionString(ua.substring(offset+5,indexOf(ua,";",offset)));}}
if(Timeline.Platform.browser.isNetscape){var offset=ua.indexOf("rv:");if(offset>=0){parseVersionString(ua.substring(offset+3,indexOf(ua,")",offset)));}}
if(Timeline.Platform.browser.isFirefox){var offset=ua.indexOf("firefox/");if(offset>=0){parseVersionString(ua.substring(offset+8,indexOf(ua," ",offset)));}}})();Timeline.Platform.getDefaultLocale=function(){return Timeline.Platform.clientLocale;};
/* data-structure.js */
Timeline.SortedArray=function(compare,initialArray){this._a=(initialArray instanceof Array)?initialArray:[];this._compare=compare;};Timeline.SortedArray.prototype.add=function(elmt){var sa=this;var index=this.find(function(elmt2){return sa._compare(elmt2,elmt);});if(index<this._a.length){this._a.splice(index,0,elmt);}else{this._a.push(elmt);}};Timeline.SortedArray.prototype.remove=function(elmt){var sa=this;var index=this.find(function(elmt2){return sa._compare(elmt2,elmt);});while(index<this._a.length&&this._compare(this._a[index],elmt)==0){if(this._a[index]==elmt){this._a.splice(index,1);return true;}else{index++;}}
return false;};Timeline.SortedArray.prototype.removeAll=function(){this._a=[];};Timeline.SortedArray.prototype.elementAt=function(index){return this._a[index];};Timeline.SortedArray.prototype.length=function(){return this._a.length;};Timeline.SortedArray.prototype.find=function(compare){var a=0;var b=this._a.length;while(a<b){var mid=Math.floor((a+b)/2);var c=compare(this._a[mid]);if(mid==a){return c<0?a+1:a;}else if(c<0){a=mid;}else{b=mid;}}
return a;};Timeline.SortedArray.prototype.getFirst=function(){return(this._a.length>0)?this._a[0]:null;};Timeline.SortedArray.prototype.getLast=function(){return(this._a.length>0)?this._a[this._a.length-1]:null;};Timeline.EventIndex=function(unit){var eventIndex=this;this._unit=(unit!=null)?unit:Timeline.NativeDateUnit;this._events=new Timeline.SortedArray(function(event1,event2){return eventIndex._unit.compare(event1.getStart(),event2.getStart());});this._indexed=true;};Timeline.EventIndex.prototype.getUnit=function(){return this._unit;};Timeline.EventIndex.prototype.add=function(evt){this._events.add(evt);this._indexed=false;};Timeline.EventIndex.prototype.removeAll=function(){this._events.removeAll();this._indexed=false;};Timeline.EventIndex.prototype.getCount=function(){return this._events.length();};Timeline.EventIndex.prototype.getIterator=function(startDate,endDate){if(!this._indexed){this._index();}
return new Timeline.EventIndex._Iterator(this._events,startDate,endDate,this._unit);};Timeline.EventIndex.prototype.getAllIterator=function(){return new Timeline.EventIndex._AllIterator(this._events);};Timeline.EventIndex.prototype.getEarliestDate=function(){var evt=this._events.getFirst();return(evt==null)?null:evt.getStart();};Timeline.EventIndex.prototype.getLatestDate=function(){var evt=this._events.getLast();if(evt==null){return null;}
if(!this._indexed){this._index();}
var index=evt._earliestOverlapIndex;var date=this._events.elementAt(index).getEnd();for(var i=index+1;i<this._events.length();i++){date=this._unit.later(date,this._events.elementAt(i).getEnd());}
return date;};Timeline.EventIndex.prototype._index=function(){var l=this._events.length();for(var i=0;i<l;i++){var evt=this._events.elementAt(i);evt._earliestOverlapIndex=i;}
var toIndex=1;for(var i=0;i<l;i++){var evt=this._events.elementAt(i);var end=evt.getEnd();toIndex=Math.max(toIndex,i+1);while(toIndex<l){var evt2=this._events.elementAt(toIndex);var start2=evt2.getStart();if(this._unit.compare(start2,end)<0){evt2._earliestOverlapIndex=i;toIndex++;}else{break;}}}
this._indexed=true;};Timeline.EventIndex._Iterator=function(events,startDate,endDate,unit){this._events=events;this._startDate=startDate;this._endDate=endDate;this._unit=unit;this._currentIndex=events.find(function(evt){return unit.compare(evt.getStart(),startDate);});if(this._currentIndex-1>=0){this._currentIndex=this._events.elementAt(this._currentIndex-1)._earliestOverlapIndex;}
this._currentIndex--;this._maxIndex=events.find(function(evt){return unit.compare(evt.getStart(),endDate);});this._hasNext=false;this._next=null;this._findNext();};Timeline.EventIndex._Iterator.prototype={hasNext:function(){return this._hasNext;},next:function(){if(this._hasNext){var next=this._next;this._findNext();return next;}else{return null;}},_findNext:function(){var unit=this._unit;while((++this._currentIndex)<this._maxIndex){var evt=this._events.elementAt(this._currentIndex);if(unit.compare(evt.getStart(),this._endDate)<0&&unit.compare(evt.getEnd(),this._startDate)>0){this._next=evt;this._hasNext=true;return;}}
this._next=null;this._hasNext=false;}};Timeline.EventIndex._AllIterator=function(events){this._events=events;this._index=0;};Timeline.EventIndex._AllIterator.prototype={hasNext:function(){return this._index<this._events.length();},next:function(){return this._index<this._events.length()?this._events.elementAt(this._index++):null;}};
/* date-time.js */
Timeline.DateTime=new Object();Timeline.DateTime.MILLISECOND=0;Timeline.DateTime.SECOND=1;Timeline.DateTime.MINUTE=2;Timeline.DateTime.HOUR=3;Timeline.DateTime.DAY=4;Timeline.DateTime.WEEK=5;Timeline.DateTime.MONTH=6;Timeline.DateTime.YEAR=7;Timeline.DateTime.DECADE=8;Timeline.DateTime.CENTURY=9;Timeline.DateTime.MILLENNIUM=10;Timeline.DateTime.EPOCH=-1;Timeline.DateTime.ERA=-2;Timeline.DateTime.gregorianUnitLengths=[];(function(){var d=Timeline.DateTime;var a=d.gregorianUnitLengths;a[d.MILLISECOND]=1;a[d.SECOND]=1000;a[d.MINUTE]=a[d.SECOND]*60;a[d.HOUR]=a[d.MINUTE]*60;a[d.DAY]=a[d.HOUR]*24;a[d.WEEK]=a[d.DAY]*7;a[d.MONTH]=a[d.DAY]*31;a[d.YEAR]=a[d.DAY]*365;a[d.DECADE]=a[d.YEAR]*10;a[d.CENTURY]=a[d.YEAR]*100;a[d.MILLENNIUM]=a[d.YEAR]*1000;})();Timeline.DateTime.parseGregorianDateTime=function(o){if(o==null){return null;}else if(o instanceof Date){return o;}
var s=o.toString();if(s.length>0&&s.length<8){var space=s.indexOf(" ");if(space>0){var year=parseInt(s.substr(0,space));var suffix=s.substr(space+1);if(suffix.toLowerCase()=="bc"){year=1-year;}}else{var year=parseInt(s);}
var d=new Date(0);d.setUTCFullYear(year);return d;}
try{return new Date(Date.parse(s));}catch(e){return null;}};Timeline.DateTime._iso8601DateRegExp="^(-?)([0-9]{4})("+["(-?([0-9]{2})(-?([0-9]{2}))?)","(-?([0-9]{3}))","(-?W([0-9]{2})(-?([1-7]))?)"].join("|")+")?$";Timeline.DateTime.setIso8601Date=function(dateObject,string){var regexp=Timeline.DateTime._iso8601DateRegExp;var d=string.match(new RegExp(regexp));if(!d){throw new Error("Invalid date string: "+string);}
var sign=(d[1]=="-")?-1:1;var year=sign*d[2];var month=d[5];var date=d[7];var dayofyear=d[9];var week=d[11];var dayofweek=(d[13])?d[13]:1;dateObject.setUTCFullYear(year);if(dayofyear){dateObject.setUTCMonth(0);dateObject.setUTCDate(Number(dayofyear));}else if(week){dateObject.setUTCMonth(0);dateObject.setUTCDate(1);var gd=dateObject.getUTCDay();var day=(gd)?gd:7;var offset=Number(dayofweek)+(7*Number(week));if(day<=4){dateObject.setUTCDate(offset+1-day);}else{dateObject.setUTCDate(offset+8-day);}}else{if(month){dateObject.setUTCDate(1);dateObject.setUTCMonth(month-1);}
if(date){dateObject.setUTCDate(date);}}
return dateObject;};Timeline.DateTime.setIso8601Time=function(dateObject,string){var timezone="Z|(([-+])([0-9]{2})(:?([0-9]{2}))?)$";var d=string.match(new RegExp(timezone));var offset=0;if(d){if(d[0]!='Z'){offset=(Number(d[3])*60)+Number(d[5]);offset*=((d[2]=='-')?1:-1);}
string=string.substr(0,string.length-d[0].length);}
var regexp="^([0-9]{2})(:?([0-9]{2})(:?([0-9]{2})(\.([0-9]+))?)?)?$";var d=string.match(new RegExp(regexp));if(!d){dojo.debug("invalid time string: "+string);return false;}
var hours=d[1];var mins=Number((d[3])?d[3]:0);var secs=(d[5])?d[5]:0;var ms=d[7]?(Number("0."+d[7])*1000):0;dateObject.setUTCHours(hours);dateObject.setUTCMinutes(mins);dateObject.setUTCSeconds(secs);dateObject.setUTCMilliseconds(ms);return dateObject;};Timeline.DateTime.setIso8601=function(dateObject,string){var comps=(string.indexOf("T")==-1)?string.split(" "):string.split("T");Timeline.DateTime.setIso8601Date(dateObject,comps[0]);if(comps.length==2){Timeline.DateTime.setIso8601Time(dateObject,comps[1]);}
return dateObject;};Timeline.DateTime.parseIso8601DateTime=function(string){try{return Timeline.DateTime.setIso8601(new Date(0),string);}catch(e){return null;}};Timeline.DateTime.roundDownToInterval=function(date,intervalUnit,timeZone,multiple,firstDayOfWeek){var timeShift=timeZone*Timeline.DateTime.gregorianUnitLengths[Timeline.DateTime.HOUR];var date2=new Date(date.getTime()+timeShift);var clearInDay=function(d){d.setUTCMilliseconds(0);d.setUTCSeconds(0);d.setUTCMinutes(0);d.setUTCHours(0);};var clearInYear=function(d){clearInDay(d);d.setUTCDate(1);d.setUTCMonth(0);};switch(intervalUnit){case Timeline.DateTime.MILLISECOND:var x=date2.getUTCMilliseconds();date2.setUTCMilliseconds(x-(x%multiple));break;case Timeline.DateTime.SECOND:date2.setUTCMilliseconds(0);var x=date2.getUTCSeconds();date2.setUTCSeconds(x-(x%multiple));break;case Timeline.DateTime.MINUTE:date2.setUTCMilliseconds(0);date2.setUTCSeconds(0);var x=date2.getUTCMinutes();date2.setTime(date2.getTime()-
(x%multiple)*Timeline.DateTime.gregorianUnitLengths[Timeline.DateTime.MINUTE]);break;case Timeline.DateTime.HOUR:date2.setUTCMilliseconds(0);date2.setUTCSeconds(0);date2.setUTCMinutes(0);var x=date2.getUTCHours();date2.setUTCHours(x-(x%multiple));break;case Timeline.DateTime.DAY:clearInDay(date2);break;case Timeline.DateTime.WEEK:clearInDay(date2);var d=(date2.getUTCDay()+7-firstDayOfWeek)%7;date2.setTime(date2.getTime()-
d*Timeline.DateTime.gregorianUnitLengths[Timeline.DateTime.DAY]);break;case Timeline.DateTime.MONTH:clearInDay(date2);date2.setUTCDate(1);var x=date2.getUTCMonth();date2.setUTCMonth(x-(x%multiple));break;case Timeline.DateTime.YEAR:clearInYear(date2);var x=date2.getUTCFullYear();date2.setUTCFullYear(x-(x%multiple));break;case Timeline.DateTime.DECADE:clearInYear(date2);date2.setUTCFullYear(Math.floor(date2.getUTCFullYear()/10)*10);break;case Timeline.DateTime.CENTURY:clearInYear(date2);date2.setUTCFullYear(Math.floor(date2.getUTCFullYear()/100)*100);break;case Timeline.DateTime.MILLENNIUM:clearInYear(date2);date2.setUTCFullYear(Math.floor(date2.getUTCFullYear()/1000)*1000);break;}
date.setTime(date2.getTime()-timeShift);};Timeline.DateTime.roundUpToInterval=function(date,intervalUnit,timeZone,multiple,firstDayOfWeek){var originalTime=date.getTime();Timeline.DateTime.roundDownToInterval(date,intervalUnit,timeZone,multiple,firstDayOfWeek);if(date.getTime()<originalTime){date.setTime(date.getTime()+
Timeline.DateTime.gregorianUnitLengths[intervalUnit]*multiple);}};Timeline.DateTime.incrementByInterval=function(date,intervalUnit){switch(intervalUnit){case Timeline.DateTime.MILLISECOND:date.setTime(date.getTime()+1)
break;case Timeline.DateTime.SECOND:date.setTime(date.getTime()+1000);break;case Timeline.DateTime.MINUTE:date.setTime(date.getTime()+
Timeline.DateTime.gregorianUnitLengths[Timeline.DateTime.MINUTE]);break;case Timeline.DateTime.HOUR:date.setTime(date.getTime()+
Timeline.DateTime.gregorianUnitLengths[Timeline.DateTime.HOUR]);break;case Timeline.DateTime.DAY:date.setUTCDate(date.getUTCDate()+1);break;case Timeline.DateTime.WEEK:date.setUTCDate(date.getUTCDate()+7);break;case Timeline.DateTime.MONTH:date.setUTCMonth(date.getUTCMonth()+1);break;case Timeline.DateTime.YEAR:date.setUTCFullYear(date.getUTCFullYear()+1);break;case Timeline.DateTime.DECADE:date.setUTCFullYear(date.getUTCFullYear()+10);break;case Timeline.DateTime.CENTURY:date.setUTCFullYear(date.getUTCFullYear()+100);break;case Timeline.DateTime.MILLENNIUM:date.setUTCFullYear(date.getUTCFullYear()+1000);break;}};Timeline.DateTime.removeTimeZoneOffset=function(date,timeZone){return new Date(date.getTime()+
timeZone*Timeline.DateTime.gregorianUnitLengths[Timeline.DateTime.HOUR]);};
/* debug.js */
Timeline.Debug=new Object();Timeline.Debug.log=function(msg){};Timeline.Debug.exception=function(e){alert("Caught exception: "+(Timeline.Platform.isIE?e.message:e));};
/* dom.js */
Timeline.DOM=new Object();Timeline.DOM.registerEventWithObject=function(elmt,eventName,obj,handler){Timeline.DOM.registerEvent(elmt,eventName,function(elmt2,evt,target){return handler.call(obj,elmt2,evt,target);});};Timeline.DOM.registerEvent=function(elmt,eventName,handler){var handler2=function(evt){evt=(evt)?evt:((event)?event:null);if(evt){var target=(evt.target)?evt.target:((evt.srcElement)?evt.srcElement:null);if(target){target=(target.nodeType==1||target.nodeType==9)?target:target.parentNode;}
return handler(elmt,evt,target);}
return true;}
if(Timeline.Platform.browser.isIE){elmt.attachEvent("on"+eventName,handler2);}else{elmt.addEventListener(eventName,handler2,false);}};Timeline.DOM.getPageCoordinates=function(elmt){var left=0;var top=0;if(elmt.nodeType!=1){elmt=elmt.parentNode;}
while(elmt!=null){left+=elmt.offsetLeft;top+=elmt.offsetTop;elmt=elmt.offsetParent;}
return{left:left,top:top};};Timeline.DOM.getEventRelativeCoordinates=function(evt,elmt){if(Timeline.Platform.browser.isIE){return{x:evt.offsetX,y:evt.offsetY};}else{var coords=Timeline.DOM.getPageCoordinates(elmt);return{x:evt.pageX-coords.left,y:evt.pageY-coords.top};}};Timeline.DOM.cancelEvent=function(evt){evt.returnValue=false;evt.cancelBubble=true;if("preventDefault"in evt){evt.preventDefault();}};
/* graphics.js */
Timeline.Graphics=new Object();Timeline.Graphics.pngIsTranslucent=(!Timeline.Platform.browser.isIE)||(Timeline.Platform.browser.majorVersion>6);Timeline.Graphics.createTranslucentImage=function(doc,url,verticalAlign){var elmt;if(Timeline.Graphics.pngIsTranslucent){elmt=doc.createElement("img");elmt.setAttribute("src",url);}else{elmt=doc.createElement("img");elmt.style.display="inline";elmt.style.width="1px";elmt.style.height="1px";elmt.style.filter="progid:DXImageTransform.Microsoft.AlphaImageLoader(src='"+url+"', sizingMethod='image')";}
elmt.style.verticalAlign=(verticalAlign!=null)?verticalAlign:"middle";return elmt;};Timeline.Graphics.setOpacity=function(elmt,opacity){if(Timeline.Platform.browser.isIE){elmt.style.filter="progid:DXImageTransform.Microsoft.Alpha(Style=0,Opacity="+opacity+")";}else{var o=(opacity/100).toString();elmt.style.opacity=o;elmt.style.MozOpacity=o;}};Timeline.Graphics._bubbleMargins={top:33,bottom:42,left:33,right:40}
Timeline.Graphics._arrowOffsets={top:0,bottom:9,left:1,right:8}
Timeline.Graphics._bubblePadding=15;Timeline.Graphics._bubblePointOffset=6;Timeline.Graphics._halfArrowWidth=18;Timeline.Graphics.createBubbleForPoint=function(doc,pageX,pageY,contentWidth,contentHeight){var bubble={_closed:false,_doc:doc,close:function(){if(!this._closed){this._doc.body.removeChild(this._div);this._doc=null;this._div=null;this._content=null;this._closed=true;}}};var docWidth=doc.body.offsetWidth;var docHeight=doc.body.offsetHeight;var margins=Timeline.Graphics._bubbleMargins;var bubbleWidth=margins.left+contentWidth+margins.right;var bubbleHeight=margins.top+contentHeight+margins.bottom;var pngIsTranslucent=Timeline.Graphics.pngIsTranslucent;var urlPrefix=Timeline.urlPrefix;var setImg=function(elmt,url,width,height){elmt.style.position="absolute";elmt.style.width=width+"px";elmt.style.height=height+"px";if(pngIsTranslucent){elmt.style.background="url("+url+")";}else{elmt.style.filter="progid:DXImageTransform.Microsoft.AlphaImageLoader(src='"+url+"', sizingMethod='crop')";}}
var div=doc.createElement("div");div.style.width=bubbleWidth+"px";div.style.height=bubbleHeight+"px";div.style.position="absolute";div.style.zIndex=1000;bubble._div=div;var divInner=doc.createElement("div");divInner.style.width="100%";divInner.style.height="100%";divInner.style.position="relative";div.appendChild(divInner);var createImg=function(url,left,top,width,height){var divImg=doc.createElement("div");divImg.style.left=left+"px";divImg.style.top=top+"px";setImg(divImg,url,width,height);divInner.appendChild(divImg);}
createImg(urlPrefix+"images/bubble-top-left.png",0,0,margins.left,margins.top);createImg(urlPrefix+"images/bubble-top.png",margins.left,0,contentWidth,margins.top);createImg(urlPrefix+"images/bubble-top-right.png",margins.left+contentWidth,0,margins.right,margins.top);createImg(urlPrefix+"images/bubble-left.png",0,margins.top,margins.left,contentHeight);createImg(urlPrefix+"images/bubble-right.png",margins.left+contentWidth,margins.top,margins.right,contentHeight);createImg(urlPrefix+"images/bubble-bottom-left.png",0,margins.top+contentHeight,margins.left,margins.bottom);createImg(urlPrefix+"images/bubble-bottom.png",margins.left,margins.top+contentHeight,contentWidth,margins.bottom);createImg(urlPrefix+"images/bubble-bottom-right.png",margins.left+contentWidth,margins.top+contentHeight,margins.right,margins.bottom);var divClose=doc.createElement("div");divClose.style.left=(bubbleWidth-margins.right+Timeline.Graphics._bubblePadding-16-2)+"px";divClose.style.top=(margins.top-Timeline.Graphics._bubblePadding+1)+"px";divClose.style.cursor="pointer";setImg(divClose,urlPrefix+"images/close-button.png",16,16);Timeline.DOM.registerEventWithObject(divClose,"click",bubble,bubble.close);divInner.appendChild(divClose);var divContent=doc.createElement("div");divContent.style.position="absolute";divContent.style.left=margins.left+"px";divContent.style.top=margins.top+"px";divContent.style.width=contentWidth+"px";divContent.style.height=contentHeight+"px";divContent.style.overflow="auto";divContent.style.background="white";divInner.appendChild(divContent);bubble.content=divContent;(function(){if(pageX-Timeline.Graphics._halfArrowWidth-Timeline.Graphics._bubblePadding>0&&pageX+Timeline.Graphics._halfArrowWidth+Timeline.Graphics._bubblePadding<docWidth){var left=pageX-Math.round(contentWidth/2)-margins.left;left=pageX<(docWidth/2)?Math.max(left,-(margins.left-Timeline.Graphics._bubblePadding)):Math.min(left,docWidth+(margins.right-Timeline.Graphics._bubblePadding)-bubbleWidth);if(pageY-Timeline.Graphics._bubblePointOffset-bubbleHeight>0){var divImg=doc.createElement("div");divImg.style.left=(pageX-Timeline.Graphics._halfArrowWidth-left)+"px";divImg.style.top=(margins.top+contentHeight)+"px";setImg(divImg,urlPrefix+"images/bubble-bottom-arrow.png",37,margins.bottom);divInner.appendChild(divImg);div.style.left=left+"px";div.style.top=(pageY-Timeline.Graphics._bubblePointOffset-bubbleHeight+
Timeline.Graphics._arrowOffsets.bottom)+"px";return;}else if(pageY+Timeline.Graphics._bubblePointOffset+bubbleHeight<docHeight){var divImg=doc.createElement("div");divImg.style.left=(pageX-Timeline.Graphics._halfArrowWidth-left)+"px";divImg.style.top="0px";setImg(divImg,urlPrefix+"images/bubble-top-arrow.png",37,margins.top);divInner.appendChild(divImg);div.style.left=left+"px";div.style.top=(pageY+Timeline.Graphics._bubblePointOffset-
Timeline.Graphics._arrowOffsets.top)+"px";return;}}
var top=pageY-Math.round(contentHeight/2)-margins.top;top=pageY<(docHeight/2)?Math.max(top,-(margins.top-Timeline.Graphics._bubblePadding)):Math.min(top,docHeight+(margins.bottom-Timeline.Graphics._bubblePadding)-bubbleHeight);if(pageX-Timeline.Graphics._bubblePointOffset-bubbleWidth>0){var divImg=doc.createElement("div");divImg.style.left=(margins.left+contentWidth)+"px";divImg.style.top=(pageY-Timeline.Graphics._halfArrowWidth-top)+"px";setImg(divImg,urlPrefix+"images/bubble-right-arrow.png",margins.right,37);divInner.appendChild(divImg);div.style.left=(pageX-Timeline.Graphics._bubblePointOffset-bubbleWidth+
Timeline.Graphics._arrowOffsets.right)+"px";div.style.top=top+"px";}else{var divImg=doc.createElement("div");divImg.style.left="0px";divImg.style.top=(pageY-Timeline.Graphics._halfArrowWidth-top)+"px";setImg(divImg,urlPrefix+"images/bubble-left-arrow.png",margins.left,37);divInner.appendChild(divImg);div.style.left=(pageX+Timeline.Graphics._bubblePointOffset-
Timeline.Graphics._arrowOffsets.left)+"px";div.style.top=top+"px";}})();doc.body.appendChild(div);return bubble;};Timeline.Graphics.createMessageBubble=function(doc){var containerDiv=doc.createElement("div");if(Timeline.Graphics.pngIsTranslucent){var topDiv=doc.createElement("div");topDiv.style.height="33px";topDiv.style.background="url("+Timeline.urlPrefix+"images/message-top-left.png) top left no-repeat";topDiv.style.paddingLeft="44px";containerDiv.appendChild(topDiv);var topRightDiv=doc.createElement("div");topRightDiv.style.height="33px";topRightDiv.style.background="url("+Timeline.urlPrefix+"images/message-top-right.png) top right no-repeat";topDiv.appendChild(topRightDiv);var middleDiv=doc.createElement("div");middleDiv.style.background="url("+Timeline.urlPrefix+"images/message-left.png) top left repeat-y";middleDiv.style.paddingLeft="44px";containerDiv.appendChild(middleDiv);var middleRightDiv=doc.createElement("div");middleRightDiv.style.background="url("+Timeline.urlPrefix+"images/message-right.png) top right repeat-y";middleRightDiv.style.paddingRight="44px";middleDiv.appendChild(middleRightDiv);var contentDiv=doc.createElement("div");middleRightDiv.appendChild(contentDiv);var bottomDiv=doc.createElement("div");bottomDiv.style.height="55px";bottomDiv.style.background="url("+Timeline.urlPrefix+"images/message-bottom-left.png) bottom left no-repeat";bottomDiv.style.paddingLeft="44px";containerDiv.appendChild(bottomDiv);var bottomRightDiv=doc.createElement("div");bottomRightDiv.style.height="55px";bottomRightDiv.style.background="url("+Timeline.urlPrefix+"images/message-bottom-right.png) bottom right no-repeat";bottomDiv.appendChild(bottomRightDiv);}else{containerDiv.style.border="2px solid #7777AA";containerDiv.style.padding="20px";containerDiv.style.background="white";Timeline.Graphics.setOpacity(containerDiv,90);var contentDiv=doc.createElement("div");containerDiv.appendChild(contentDiv);}
return{containerDiv:containerDiv,contentDiv:contentDiv};};Timeline.Graphics.createAnimation=function(f,from,to,duration){return new Timeline.Graphics._Animation(f,from,to,duration);};Timeline.Graphics._Animation=function(f,from,to,duration){this.f=f;this.from=from;this.to=to;this.current=from;this.duration=duration;this.start=new Date().getTime();this.timePassed=0;};Timeline.Graphics._Animation.prototype.run=function(){var a=this;window.setTimeout(function(){a.step();},100);};Timeline.Graphics._Animation.prototype.step=function(){this.timePassed+=100;var timePassedFraction=this.timePassed/this.duration;var parameterFraction=-Math.cos(timePassedFraction*Math.PI)/2+0.5;var current=parameterFraction*(this.to-this.from)+this.from;try{this.f(current,current-this.current);}catch(e){}
this.current=current;if(this.timePassed<this.duration){this.run();}};
/* html.js */
Timeline.HTML=new Object();Timeline.HTML._e2uHash={};(function(){e2uHash=Timeline.HTML._e2uHash;e2uHash['nbsp']='\u00A0[space]';e2uHash['iexcl']='\u00A1';e2uHash['cent']='\u00A2';e2uHash['pound']='\u00A3';e2uHash['curren']='\u00A4';e2uHash['yen']='\u00A5';e2uHash['brvbar']='\u00A6';e2uHash['sect']='\u00A7';e2uHash['uml']='\u00A8';e2uHash['copy']='\u00A9';e2uHash['ordf']='\u00AA';e2uHash['laquo']='\u00AB';e2uHash['not']='\u00AC';e2uHash['shy']='\u00AD';e2uHash['reg']='\u00AE';e2uHash['macr']='\u00AF';e2uHash['deg']='\u00B0';e2uHash['plusmn']='\u00B1';e2uHash['sup2']='\u00B2';e2uHash['sup3']='\u00B3';e2uHash['acute']='\u00B4';e2uHash['micro']='\u00B5';e2uHash['para']='\u00B6';e2uHash['middot']='\u00B7';e2uHash['cedil']='\u00B8';e2uHash['sup1']='\u00B9';e2uHash['ordm']='\u00BA';e2uHash['raquo']='\u00BB';e2uHash['frac14']='\u00BC';e2uHash['frac12']='\u00BD';e2uHash['frac34']='\u00BE';e2uHash['iquest']='\u00BF';e2uHash['Agrave']='\u00C0';e2uHash['Aacute']='\u00C1';e2uHash['Acirc']='\u00C2';e2uHash['Atilde']='\u00C3';e2uHash['Auml']='\u00C4';e2uHash['Aring']='\u00C5';e2uHash['AElig']='\u00C6';e2uHash['Ccedil']='\u00C7';e2uHash['Egrave']='\u00C8';e2uHash['Eacute']='\u00C9';e2uHash['Ecirc']='\u00CA';e2uHash['Euml']='\u00CB';e2uHash['Igrave']='\u00CC';e2uHash['Iacute']='\u00CD';e2uHash['Icirc']='\u00CE';e2uHash['Iuml']='\u00CF';e2uHash['ETH']='\u00D0';e2uHash['Ntilde']='\u00D1';e2uHash['Ograve']='\u00D2';e2uHash['Oacute']='\u00D3';e2uHash['Ocirc']='\u00D4';e2uHash['Otilde']='\u00D5';e2uHash['Ouml']='\u00D6';e2uHash['times']='\u00D7';e2uHash['Oslash']='\u00D8';e2uHash['Ugrave']='\u00D9';e2uHash['Uacute']='\u00DA';e2uHash['Ucirc']='\u00DB';e2uHash['Uuml']='\u00DC';e2uHash['Yacute']='\u00DD';e2uHash['THORN']='\u00DE';e2uHash['szlig']='\u00DF';e2uHash['agrave']='\u00E0';e2uHash['aacute']='\u00E1';e2uHash['acirc']='\u00E2';e2uHash['atilde']='\u00E3';e2uHash['auml']='\u00E4';e2uHash['aring']='\u00E5';e2uHash['aelig']='\u00E6';e2uHash['ccedil']='\u00E7';e2uHash['egrave']='\u00E8';e2uHash['eacute']='\u00E9';e2uHash['ecirc']='\u00EA';e2uHash['euml']='\u00EB';e2uHash['igrave']='\u00EC';e2uHash['iacute']='\u00ED';e2uHash['icirc']='\u00EE';e2uHash['iuml']='\u00EF';e2uHash['eth']='\u00F0';e2uHash['ntilde']='\u00F1';e2uHash['ograve']='\u00F2';e2uHash['oacute']='\u00F3';e2uHash['ocirc']='\u00F4';e2uHash['otilde']='\u00F5';e2uHash['ouml']='\u00F6';e2uHash['divide']='\u00F7';e2uHash['oslash']='\u00F8';e2uHash['ugrave']='\u00F9';e2uHash['uacute']='\u00FA';e2uHash['ucirc']='\u00FB';e2uHash['uuml']='\u00FC';e2uHash['yacute']='\u00FD';e2uHash['thorn']='\u00FE';e2uHash['yuml']='\u00FF';e2uHash['quot']='\u0022';e2uHash['amp']='\u0026';e2uHash['lt']='\u003C';e2uHash['gt']='\u003E';e2uHash['OElig']='';e2uHash['oelig']='\u0153';e2uHash['Scaron']='\u0160';e2uHash['scaron']='\u0161';e2uHash['Yuml']='\u0178';e2uHash['circ']='\u02C6';e2uHash['tilde']='\u02DC';e2uHash['ensp']='\u2002';e2uHash['emsp']='\u2003';e2uHash['thinsp']='\u2009';e2uHash['zwnj']='\u200C';e2uHash['zwj']='\u200D';e2uHash['lrm']='\u200E';e2uHash['rlm']='\u200F';e2uHash['ndash']='\u2013';e2uHash['mdash']='\u2014';e2uHash['lsquo']='\u2018';e2uHash['rsquo']='\u2019';e2uHash['sbquo']='\u201A';e2uHash['ldquo']='\u201C';e2uHash['rdquo']='\u201D';e2uHash['bdquo']='\u201E';e2uHash['dagger']='\u2020';e2uHash['Dagger']='\u2021';e2uHash['permil']='\u2030';e2uHash['lsaquo']='\u2039';e2uHash['rsaquo']='\u203A';e2uHash['euro']='\u20AC';e2uHash['fnof']='\u0192';e2uHash['Alpha']='\u0391';e2uHash['Beta']='\u0392';e2uHash['Gamma']='\u0393';e2uHash['Delta']='\u0394';e2uHash['Epsilon']='\u0395';e2uHash['Zeta']='\u0396';e2uHash['Eta']='\u0397';e2uHash['Theta']='\u0398';e2uHash['Iota']='\u0399';e2uHash['Kappa']='\u039A';e2uHash['Lambda']='\u039B';e2uHash['Mu']='\u039C';e2uHash['Nu']='\u039D';e2uHash['Xi']='\u039E';e2uHash['Omicron']='\u039F';e2uHash['Pi']='\u03A0';e2uHash['Rho']='\u03A1';e2uHash['Sigma']='\u03A3';e2uHash['Tau']='\u03A4';e2uHash['Upsilon']='\u03A5';e2uHash['Phi']='\u03A6';e2uHash['Chi']='\u03A7';e2uHash['Psi']='\u03A8';e2uHash['Omega']='\u03A9';e2uHash['alpha']='\u03B1';e2uHash['beta']='\u03B2';e2uHash['gamma']='\u03B3';e2uHash['delta']='\u03B4';e2uHash['epsilon']='\u03B5';e2uHash['zeta']='\u03B6';e2uHash['eta']='\u03B7';e2uHash['theta']='\u03B8';e2uHash['iota']='\u03B9';e2uHash['kappa']='\u03BA';e2uHash['lambda']='\u03BB';e2uHash['mu']='\u03BC';e2uHash['nu']='\u03BD';e2uHash['xi']='\u03BE';e2uHash['omicron']='\u03BF';e2uHash['pi']='\u03C0';e2uHash['rho']='\u03C1';e2uHash['sigmaf']='\u03C2';e2uHash['sigma']='\u03C3';e2uHash['tau']='\u03C4';e2uHash['upsilon']='\u03C5';e2uHash['phi']='\u03C6';e2uHash['chi']='\u03C7';e2uHash['psi']='\u03C8';e2uHash['omega']='\u03C9';e2uHash['thetasym']='\u03D1';e2uHash['upsih']='\u03D2';e2uHash['piv']='\u03D6';e2uHash['bull']='\u2022';e2uHash['hellip']='\u2026';e2uHash['prime']='\u2032';e2uHash['Prime']='\u2033';e2uHash['oline']='\u203E';e2uHash['frasl']='\u2044';e2uHash['weierp']='\u2118';e2uHash['image']='\u2111';e2uHash['real']='\u211C';e2uHash['trade']='\u2122';e2uHash['alefsym']='\u2135';e2uHash['larr']='\u2190';e2uHash['uarr']='\u2191';e2uHash['rarr']='\u2192';e2uHash['darr']='\u2193';e2uHash['harr']='\u2194';e2uHash['crarr']='\u21B5';e2uHash['lArr']='\u21D0';e2uHash['uArr']='\u21D1';e2uHash['rArr']='\u21D2';e2uHash['dArr']='\u21D3';e2uHash['hArr']='\u21D4';e2uHash['forall']='\u2200';e2uHash['part']='\u2202';e2uHash['exist']='\u2203';e2uHash['empty']='\u2205';e2uHash['nabla']='\u2207';e2uHash['isin']='\u2208';e2uHash['notin']='\u2209';e2uHash['ni']='\u220B';e2uHash['prod']='\u220F';e2uHash['sum']='\u2211';e2uHash['minus']='\u2212';e2uHash['lowast']='\u2217';e2uHash['radic']='\u221A';e2uHash['prop']='\u221D';e2uHash['infin']='\u221E';e2uHash['ang']='\u2220';e2uHash['and']='\u2227';e2uHash['or']='\u2228';e2uHash['cap']='\u2229';e2uHash['cup']='\u222A';e2uHash['int']='\u222B';e2uHash['there4']='\u2234';e2uHash['sim']='\u223C';e2uHash['cong']='\u2245';e2uHash['asymp']='\u2248';e2uHash['ne']='\u2260';e2uHash['equiv']='\u2261';e2uHash['le']='\u2264';e2uHash['ge']='\u2265';e2uHash['sub']='\u2282';e2uHash['sup']='\u2283';e2uHash['nsub']='\u2284';e2uHash['sube']='\u2286';e2uHash['supe']='\u2287';e2uHash['oplus']='\u2295';e2uHash['otimes']='\u2297';e2uHash['perp']='\u22A5';e2uHash['sdot']='\u22C5';e2uHash['lceil']='\u2308';e2uHash['rceil']='\u2309';e2uHash['lfloor']='\u230A';e2uHash['rfloor']='\u230B';e2uHash['lang']='\u2329';e2uHash['rang']='\u232A';e2uHash['loz']='\u25CA';e2uHash['spades']='\u2660';e2uHash['clubs']='\u2663';e2uHash['hearts']='\u2665';e2uHash['diams']='\u2666';})();Timeline.HTML.deEntify=function(s){e2uHash=Timeline.HTML._e2uHash;var re=/&(\w+?);/;while(re.test(s)){var m=s.match(re);s=s.replace(re,e2uHash[m[1]]);}
return s;};
/* xmlhttp.js */
Timeline.XmlHttp=new Object();Timeline.XmlHttp._onReadyStateChange=function(xmlhttp,fError,fDone){switch(xmlhttp.readyState){case 4:try{if(xmlhttp.status==0||xmlhttp.status==200){if(fDone){fDone(xmlhttp);}}else{if(fError){fError(xmlhttp.statusText,xmlhttp.status,xmlhttp);}}}catch(e){Timeline.Debug.exception(e);}
break;}};Timeline.XmlHttp._createRequest=function(){if(Timeline.Platform.browser.isIE){var programIDs=["Msxml2.XMLHTTP","Microsoft.XMLHTTP","Msxml2.XMLHTTP.4.0"];for(var i=0;i<programIDs.length;i++){try{var programID=programIDs[i];var f=function(){return new ActiveXObject(programID);};var o=f();Timeline.XmlHttp._createRequest=f;return o;}catch(e){}}
throw new Error("Failed to create an XMLHttpRequest object");}else{try{var f=function(){return new XMLHttpRequest();};var o=f();Timeline.XmlHttp._createRequest=f;return o;}catch(e){throw new Error("Failed to create an XMLHttpRequest object");}}};Timeline.XmlHttp.get=function(url,fError,fDone){var xmlhttp=Timeline.XmlHttp._createRequest();xmlhttp.open("GET",url,true);xmlhttp.onreadystatechange=function(){Timeline.XmlHttp._onReadyStateChange(xmlhttp,fError,fDone);};xmlhttp.send(null);};Timeline.XmlHttp.post=function(url,body,fError,fDone){var xmlhttp=Timeline.XmlHttp._createRequest();xmlhttp.open("POST",url,true);xmlhttp.onreadystatechange=function(){Timeline.XmlHttp._onReadyStateChange(xmlhttp,fError,fDone);};xmlhttp.send(body);};Timeline.XmlHttp._forceXML=function(xmlhttp){try{xmlhttp.overrideMimeType("text/xml");}catch(e){xmlhttp.setrequestheader("Content-Type","text/xml");}};
/* decorators.js */
Timeline.SpanHighlightDecorator=function(params){this._unit=("unit"in params)?params.unit:Timeline.NativeDateUnit;this._startDate=(typeof params.startDate=="string")?this._unit.parseFromObject(params.startDate):params.startDate;this._endDate=(typeof params.endDate=="string")?this._unit.parseFromObject(params.endDate):params.endDate;this._startLabel=params.startLabel;this._endLabel=params.endLabel;this._color=params.color;this._opacity=("opacity"in params)?params.opacity:100;};Timeline.SpanHighlightDecorator.prototype.initialize=function(band,timeline){this._band=band;this._timeline=timeline;this._layerDiv=null;};Timeline.SpanHighlightDecorator.prototype.paint=function(){if(this._layerDiv!=null){this._band.removeLayerDiv(this._layerDiv);}
this._layerDiv=this._band.createLayerDiv(10);this._layerDiv.setAttribute("name","span-highlight-decorator");this._layerDiv.style.display="none";var minDate=this._band.getMinDate();var maxDate=this._band.getMaxDate();if(this._unit.compare(this._startDate,maxDate)<0&&this._unit.compare(this._endDate,minDate)>0){minDate=this._unit.later(minDate,this._startDate);maxDate=this._unit.earlier(maxDate,this._endDate);var minPixel=this._band.dateToPixelOffset(minDate);var maxPixel=this._band.dateToPixelOffset(maxDate);var doc=this._timeline.getDocument();var createTable=function(){var table=doc.createElement("table");table.insertRow(0).insertCell(0);return table;};var div=doc.createElement("div");div.style.position="absolute";div.style.overflow="hidden";div.style.background=this._color;if(this._opacity<100){Timeline.Graphics.setOpacity(div,this._opacity);}
this._layerDiv.appendChild(div);var tableStartLabel=createTable();tableStartLabel.style.position="absolute";tableStartLabel.style.overflow="hidden";tableStartLabel.style.fontSize="300%";tableStartLabel.style.fontWeight="bold";tableStartLabel.style.color=this._color;tableStartLabel.rows[0].cells[0].innerHTML=this._startLabel;this._layerDiv.appendChild(tableStartLabel);var tableEndLabel=createTable();tableEndLabel.style.position="absolute";tableEndLabel.style.overflow="hidden";tableEndLabel.style.fontSize="300%";tableEndLabel.style.fontWeight="bold";tableEndLabel.style.color=this._color;tableEndLabel.rows[0].cells[0].innerHTML=this._endLabel;this._layerDiv.appendChild(tableEndLabel);if(this._timeline.isHorizontal()){div.style.left=minPixel+"px";div.style.width=(maxPixel-minPixel)+"px";div.style.top="0px";div.style.height="100%";tableStartLabel.style.right=(this._band.getTotalViewLength()-minPixel)+"px";tableStartLabel.style.width=(this._startLabel.length)+"em";tableStartLabel.style.top="0px";tableStartLabel.style.height="100%";tableStartLabel.style.textAlign="right";tableEndLabel.style.left=maxPixel+"px";tableEndLabel.style.width=(this._endLabel.length)+"em";tableEndLabel.style.top="0px";tableEndLabel.style.height="100%";}else{div.style.top=minPixel+"px";div.style.height=(maxPixel-minPixel)+"px";div.style.left="0px";div.style.width="100%";tableStartLabel.style.bottom=minPixel+"px";tableStartLabel.style.height="1.5px";tableStartLabel.style.left="0px";tableStartLabel.style.width="100%";tableEndLabel.style.top=maxPixel+"px";tableEndLabel.style.height="1.5px";tableEndLabel.style.left="0px";tableEndLabel.style.width="100%";}}
this._layerDiv.style.display="block";};Timeline.SpanHighlightDecorator.prototype.softPaint=function(){};Timeline.PointHighlightDecorator=function(params){this._unit=("unit"in params)?params.unit:Timeline.NativeDateUnit;this._date=(typeof params.date=="string")?this._unit.parseFromObject(params.date):params.date;this._width=("width"in params)?params.width:10;this._color=params.color;this._opacity=("opacity"in params)?params.opacity:100;};Timeline.PointHighlightDecorator.prototype.initialize=function(band,timeline){this._band=band;this._timeline=timeline;this._layerDiv=null;};Timeline.PointHighlightDecorator.prototype.paint=function(){if(this._layerDiv!=null){this._band.removeLayerDiv(this._layerDiv);}
this._layerDiv=this._band.createLayerDiv(10);this._layerDiv.setAttribute("name","span-highlight-decorator");this._layerDiv.style.display="none";var minDate=this._band.getMinDate();var maxDate=this._band.getMaxDate();if(this._unit.compare(this._date,maxDate)<0&&this._unit.compare(this._date,minDate)>0){var pixel=this._band.dateToPixelOffset(this._date);var minPixel=pixel-Math.round(this._width/2);var doc=this._timeline.getDocument();var div=doc.createElement("div");div.style.position="absolute";div.style.overflow="hidden";div.style.background=this._color;if(this._opacity<100){Timeline.Graphics.setOpacity(div,this._opacity);}
this._layerDiv.appendChild(div);if(this._timeline.isHorizontal()){div.style.left=minPixel+"px";div.style.width=this._width+"px";div.style.top="0px";div.style.height="100%";}else{div.style.top=minPixel+"px";div.style.height=this._width+"px";div.style.left="0px";div.style.width="100%";}}
this._layerDiv.style.display="block";};Timeline.PointHighlightDecorator.prototype.softPaint=function(){};
/* ether-painters.js */
Timeline.GregorianEtherPainter=function(params){this._params=params;this._theme=params.theme;this._unit=params.unit;this._multiple=("multiple"in params)?params.multiple:1;};Timeline.GregorianEtherPainter.prototype.initialize=function(band,timeline){this._band=band;this._timeline=timeline;this._backgroundLayer=band.createLayerDiv(0);this._backgroundLayer.setAttribute("name","ether-background");this._backgroundLayer.style.background=this._theme.ether.backgroundColors[band.getIndex()];this._markerLayer=null;this._lineLayer=null;var align=("align"in this._params&&this._params.align!=undefined)?this._params.align:this._theme.ether.interval.marker[timeline.isHorizontal()?"hAlign":"vAlign"];var showLine=("showLine"in this._params)?this._params.showLine:this._theme.ether.interval.line.show;this._intervalMarkerLayout=new Timeline.EtherIntervalMarkerLayout(this._timeline,this._band,this._theme,align,showLine);this._highlight=new Timeline.EtherHighlight(this._timeline,this._band,this._theme,this._backgroundLayer);}
Timeline.GregorianEtherPainter.prototype.setHighlight=function(startDate,endDate){this._highlight.position(startDate,endDate);}
Timeline.GregorianEtherPainter.prototype.paint=function(){if(this._markerLayer){this._band.removeLayerDiv(this._markerLayer);}
this._markerLayer=this._band.createLayerDiv(100);this._markerLayer.setAttribute("name","ether-markers");this._markerLayer.style.display="none";if(this._lineLayer){this._band.removeLayerDiv(this._lineLayer);}
this._lineLayer=this._band.createLayerDiv(1);this._lineLayer.setAttribute("name","ether-lines");this._lineLayer.style.display="none";var minDate=this._band.getMinDate();var maxDate=this._band.getMaxDate();var timeZone=this._band.getTimeZone();var labeller=this._band.getLabeller();Timeline.DateTime.roundDownToInterval(minDate,this._unit,timeZone,1,this._theme.firstDayOfWeek);var p=this;var incrementDate=function(date){for(var i=0;i<p._multiple;i++){Timeline.DateTime.incrementByInterval(date,p._unit);}};while(minDate.getTime()<maxDate.getTime()){this._intervalMarkerLayout.createIntervalMarker(minDate,labeller,this._unit,this._markerLayer,this._lineLayer);incrementDate(minDate);}
this._markerLayer.style.display="block";this._lineLayer.style.display="block";};Timeline.GregorianEtherPainter.prototype.softPaint=function(){};Timeline.HotZoneGregorianEtherPainter=function(params){this._params=params;this._theme=params.theme;this._zones=[{startTime:Number.NEGATIVE_INFINITY,endTime:Number.POSITIVE_INFINITY,unit:params.unit,multiple:1}];for(var i=0;i<params.zones.length;i++){var zone=params.zones[i];var zoneStart=Timeline.DateTime.parseGregorianDateTime(zone.start).getTime();var zoneEnd=Timeline.DateTime.parseGregorianDateTime(zone.end).getTime();for(var j=0;j<this._zones.length&&zoneEnd>zoneStart;j++){var zone2=this._zones[j];if(zoneStart<zone2.endTime){if(zoneStart>zone2.startTime){this._zones.splice(j,0,{startTime:zone2.startTime,endTime:zoneStart,unit:zone2.unit,multiple:zone2.multiple});j++;zone2.startTime=zoneStart;}
if(zoneEnd<zone2.endTime){this._zones.splice(j,0,{startTime:zoneStart,endTime:zoneEnd,unit:zone.unit,multiple:(zone.multiple)?zone.multiple:1});j++;zone2.startTime=zoneEnd;zoneStart=zoneEnd;}else{zone2.multiple=zone.multiple;zone2.unit=zone.unit;zoneStart=zone2.endTime;}}}}};Timeline.HotZoneGregorianEtherPainter.prototype.initialize=function(band,timeline){this._band=band;this._timeline=timeline;this._backgroundLayer=band.createLayerDiv(0);this._backgroundLayer.setAttribute("name","ether-background");this._backgroundLayer.style.background=this._theme.ether.backgroundColors[band.getIndex()];this._markerLayer=null;this._lineLayer=null;var align=("align"in this._params&&this._params.align!=undefined)?this._params.align:this._theme.ether.interval.marker[timeline.isHorizontal()?"hAlign":"vAlign"];var showLine=("showLine"in this._params)?this._params.showLine:this._theme.ether.interval.line.show;this._intervalMarkerLayout=new Timeline.EtherIntervalMarkerLayout(this._timeline,this._band,this._theme,align,showLine);this._highlight=new Timeline.EtherHighlight(this._timeline,this._band,this._theme,this._backgroundLayer);}
Timeline.HotZoneGregorianEtherPainter.prototype.setHighlight=function(startDate,endDate){this._highlight.position(startDate,endDate);}
Timeline.HotZoneGregorianEtherPainter.prototype.paint=function(){if(this._markerLayer){this._band.removeLayerDiv(this._markerLayer);}
this._markerLayer=this._band.createLayerDiv(100);this._markerLayer.setAttribute("name","ether-markers");this._markerLayer.style.display="none";if(this._lineLayer){this._band.removeLayerDiv(this._lineLayer);}
this._lineLayer=this._band.createLayerDiv(1);this._lineLayer.setAttribute("name","ether-lines");this._lineLayer.style.display="none";var minDate=this._band.getMinDate();var maxDate=this._band.getMaxDate();var timeZone=this._band.getTimeZone();var labeller=this._band.getLabeller();var p=this;var incrementDate=function(date,zone){for(var i=0;i<zone.multiple;i++){Timeline.DateTime.incrementByInterval(date,zone.unit);}};var zStart=0;while(zStart<this._zones.length){if(minDate.getTime()<this._zones[zStart].endTime){break;}
zStart++;}
var zEnd=this._zones.length-1;while(zEnd>=0){if(maxDate.getTime()>this._zones[zEnd].startTime){break;}
zEnd--;}
for(var z=zStart;z<=zEnd;z++){var zone=this._zones[z];var minDate2=new Date(Math.max(minDate.getTime(),zone.startTime));var maxDate2=new Date(Math.min(maxDate.getTime(),zone.endTime));Timeline.DateTime.roundDownToInterval(minDate2,zone.unit,timeZone,zone.multiple,this._theme.firstDayOfWeek);Timeline.DateTime.roundUpToInterval(maxDate2,zone.unit,timeZone,zone.multiple,this._theme.firstDayOfWeek);while(minDate2.getTime()<maxDate2.getTime()){this._intervalMarkerLayout.createIntervalMarker(minDate2,labeller,zone.unit,this._markerLayer,this._lineLayer);incrementDate(minDate2,zone);}}
this._markerLayer.style.display="block";this._lineLayer.style.display="block";};Timeline.HotZoneGregorianEtherPainter.prototype.softPaint=function(){};Timeline.YearCountEtherPainter=function(params){this._params=params;this._theme=params.theme;this._startDate=Timeline.DateTime.parseGregorianDateTime(params.startDate);this._multiple=("multiple"in params)?params.multiple:1;};Timeline.YearCountEtherPainter.prototype.initialize=function(band,timeline){this._band=band;this._timeline=timeline;this._backgroundLayer=band.createLayerDiv(0);this._backgroundLayer.setAttribute("name","ether-background");this._backgroundLayer.style.background=this._theme.ether.backgroundColors[band.getIndex()];this._markerLayer=null;this._lineLayer=null;var align=("align"in this._params)?this._params.align:this._theme.ether.interval.marker[timeline.isHorizontal()?"hAlign":"vAlign"];var showLine=("showLine"in this._params)?this._params.showLine:this._theme.ether.interval.line.show;this._intervalMarkerLayout=new Timeline.EtherIntervalMarkerLayout(this._timeline,this._band,this._theme,align,showLine);this._highlight=new Timeline.EtherHighlight(this._timeline,this._band,this._theme,this._backgroundLayer);};Timeline.YearCountEtherPainter.prototype.setHighlight=function(startDate,endDate){this._highlight.position(startDate,endDate);};Timeline.YearCountEtherPainter.prototype.paint=function(){if(this._markerLayer){this._band.removeLayerDiv(this._markerLayer);}
this._markerLayer=this._band.createLayerDiv(100);this._markerLayer.setAttribute("name","ether-markers");this._markerLayer.style.display="none";if(this._lineLayer){this._band.removeLayerDiv(this._lineLayer);}
this._lineLayer=this._band.createLayerDiv(1);this._lineLayer.setAttribute("name","ether-lines");this._lineLayer.style.display="none";var minDate=new Date(this._startDate.getTime());var maxDate=this._band.getMaxDate();var yearDiff=this._band.getMinDate().getUTCFullYear()-this._startDate.getUTCFullYear();minDate.setUTCFullYear(this._band.getMinDate().getUTCFullYear()-yearDiff%this._multiple);var p=this;var incrementDate=function(date){for(var i=0;i<p._multiple;i++){Timeline.DateTime.incrementByInterval(date,Timeline.DateTime.YEAR);}};var labeller={labelInterval:function(date,intervalUnit){var diff=date.getUTCFullYear()-p._startDate.getUTCFullYear();return{text:diff,emphasized:diff==0};}};while(minDate.getTime()<maxDate.getTime()){this._intervalMarkerLayout.createIntervalMarker(minDate,labeller,Timeline.DateTime.YEAR,this._markerLayer,this._lineLayer);incrementDate(minDate);}
this._markerLayer.style.display="block";this._lineLayer.style.display="block";};Timeline.YearCountEtherPainter.prototype.softPaint=function(){};Timeline.QuarterlyEtherPainter=function(params){this._params=params;this._theme=params.theme;this._startDate=Timeline.DateTime.parseGregorianDateTime(params.startDate);};Timeline.QuarterlyEtherPainter.prototype.initialize=function(band,timeline){this._band=band;this._timeline=timeline;this._backgroundLayer=band.createLayerDiv(0);this._backgroundLayer.setAttribute("name","ether-background");this._backgroundLayer.style.background=this._theme.ether.backgroundColors[band.getIndex()];this._markerLayer=null;this._lineLayer=null;var align=("align"in this._params)?this._params.align:this._theme.ether.interval.marker[timeline.isHorizontal()?"hAlign":"vAlign"];var showLine=("showLine"in this._params)?this._params.showLine:this._theme.ether.interval.line.show;this._intervalMarkerLayout=new Timeline.EtherIntervalMarkerLayout(this._timeline,this._band,this._theme,align,showLine);this._highlight=new Timeline.EtherHighlight(this._timeline,this._band,this._theme,this._backgroundLayer);};Timeline.QuarterlyEtherPainter.prototype.setHighlight=function(startDate,endDate){this._highlight.position(startDate,endDate);};Timeline.QuarterlyEtherPainter.prototype.paint=function(){if(this._markerLayer){this._band.removeLayerDiv(this._markerLayer);}
this._markerLayer=this._band.createLayerDiv(100);this._markerLayer.setAttribute("name","ether-markers");this._markerLayer.style.display="none";if(this._lineLayer){this._band.removeLayerDiv(this._lineLayer);}
this._lineLayer=this._band.createLayerDiv(1);this._lineLayer.setAttribute("name","ether-lines");this._lineLayer.style.display="none";var minDate=new Date(0);var maxDate=this._band.getMaxDate();minDate.setUTCFullYear(Math.max(this._startDate.getUTCFullYear(),this._band.getMinDate().getUTCFullYear()));minDate.setUTCMonth(this._startDate.getUTCMonth());var p=this;var incrementDate=function(date){date.setUTCMonth(date.getUTCMonth()+3);};var labeller={labelInterval:function(date,intervalUnit){var quarters=(4+(date.getUTCMonth()-p._startDate.getUTCMonth())/3)%4;if(quarters!=0){return{text:"Q"+(quarters+1),emphasized:false};}else{return{text:"Y"+(date.getUTCFullYear()-p._startDate.getUTCFullYear()+1),emphasized:true};}}};while(minDate.getTime()<maxDate.getTime()){this._intervalMarkerLayout.createIntervalMarker(minDate,labeller,Timeline.DateTime.YEAR,this._markerLayer,this._lineLayer);incrementDate(minDate);}
this._markerLayer.style.display="block";this._lineLayer.style.display="block";};Timeline.QuarterlyEtherPainter.prototype.softPaint=function(){};Timeline.EtherIntervalMarkerLayout=function(timeline,band,theme,align,showLine){var horizontal=timeline.isHorizontal();if(horizontal){if(align=="Top"){this.positionDiv=function(div,offset){div.style.left=offset+"px";div.style.top="0px";};}else{this.positionDiv=function(div,offset){div.style.left=offset+"px";div.style.bottom="0px";};}}else{if(align=="Left"){this.positionDiv=function(div,offset){div.style.top=offset+"px";div.style.left="0px";};}else{this.positionDiv=function(div,offset){div.style.top=offset+"px";div.style.right="0px";};}}
var markerTheme=theme.ether.interval.marker;var lineTheme=theme.ether.interval.line;var weekendTheme=theme.ether.interval.weekend;var stylePrefix=(horizontal?"h":"v")+align;var labelStyler=markerTheme[stylePrefix+"Styler"];var emphasizedLabelStyler=markerTheme[stylePrefix+"EmphasizedStyler"];var day=Timeline.DateTime.gregorianUnitLengths[Timeline.DateTime.DAY];this.createIntervalMarker=function(date,labeller,unit,markerDiv,lineDiv){var offset=Math.round(band.dateToPixelOffset(date));if(showLine&&unit!=Timeline.DateTime.WEEK){var divLine=timeline.getDocument().createElement("div");divLine.style.position="absolute";if(lineTheme.opacity<100){Timeline.Graphics.setOpacity(divLine,lineTheme.opacity);}
if(horizontal){divLine.style.borderLeft="1px solid "+lineTheme.color;divLine.style.left=offset+"px";divLine.style.width="1px";divLine.style.top="0px";divLine.style.height="100%";}else{divLine.style.borderTop="1px solid "+lineTheme.color;divLine.style.top=offset+"px";divLine.style.height="1px";divLine.style.left="0px";divLine.style.width="100%";}
lineDiv.appendChild(divLine);}
if(unit==Timeline.DateTime.WEEK){var firstDayOfWeek=theme.firstDayOfWeek;var saturday=new Date(date.getTime()+(6-firstDayOfWeek-7)*day);var monday=new Date(saturday.getTime()+2*day);var saturdayPixel=Math.round(band.dateToPixelOffset(saturday));var mondayPixel=Math.round(band.dateToPixelOffset(monday));var length=Math.max(1,mondayPixel-saturdayPixel);var divWeekend=timeline.getDocument().createElement("div");divWeekend.style.position="absolute";divWeekend.style.background=weekendTheme.color;if(weekendTheme.opacity<100){Timeline.Graphics.setOpacity(divWeekend,weekendTheme.opacity);}
if(horizontal){divWeekend.style.left=saturdayPixel+"px";divWeekend.style.width=length+"px";divWeekend.style.top="0px";divWeekend.style.height="100%";}else{divWeekend.style.top=saturdayPixel+"px";divWeekend.style.height=length+"px";divWeekend.style.left="0px";divWeekend.style.width="100%";}
lineDiv.appendChild(divWeekend);}
var label=labeller.labelInterval(date,unit);var div=timeline.getDocument().createElement("div");div.innerHTML=label.text;div.style.position="absolute";(label.emphasized?emphasizedLabelStyler:labelStyler)(div);this.positionDiv(div,offset);markerDiv.appendChild(div);return div;};};Timeline.EtherHighlight=function(timeline,band,theme,backgroundLayer){var horizontal=timeline.isHorizontal();this._highlightDiv=null;this._createHighlightDiv=function(){if(this._highlightDiv==null){this._highlightDiv=timeline.getDocument().createElement("div");this._highlightDiv.setAttribute("name","ether-highlight");this._highlightDiv.style.position="absolute";this._highlightDiv.style.background=theme.ether.highlightColor;var opacity=theme.ether.highlightOpacity;if(opacity<100){Timeline.Graphics.setOpacity(this._highlightDiv,opacity);}
backgroundLayer.appendChild(this._highlightDiv);}}
this.position=function(startDate,endDate){this._createHighlightDiv();var startPixel=Math.round(band.dateToPixelOffset(startDate));var endPixel=Math.round(band.dateToPixelOffset(endDate));var length=Math.max(endPixel-startPixel,3);if(horizontal){this._highlightDiv.style.left=startPixel+"px";this._highlightDiv.style.width=length+"px";this._highlightDiv.style.top="2px";this._highlightDiv.style.height=(band.getViewWidth()-4)+"px";}else{this._highlightDiv.style.top=startPixel+"px";this._highlightDiv.style.height=length+"px";this._highlightDiv.style.left="2px";this._highlightDiv.style.width=(band.getViewWidth()-4)+"px";}}};
/* ethers.js */
Timeline.LinearEther=function(params){this._params=params;this._interval=params.interval;this._pixelsPerInterval=params.pixelsPerInterval;};Timeline.LinearEther.prototype.initialize=function(timeline){this._timeline=timeline;this._unit=timeline.getUnit();if("startsOn"in this._params){this._start=this._unit.parseFromObject(this._params.startsOn);}else if("endsOn"in this._params){this._start=this._unit.parseFromObject(this._params.endsOn);this.shiftPixels(-this._timeline.getPixelLength());}else if("centersOn"in this._params){this._start=this._unit.parseFromObject(this._params.centersOn);this.shiftPixels(-this._timeline.getPixelLength()/2);}else{this._start=this._unit.makeDefaultValue();this.shiftPixels(-this._timeline.getPixelLength()/2);}};Timeline.LinearEther.prototype.setDate=function(date){this._start=this._unit.cloneValue(date);};Timeline.LinearEther.prototype.shiftPixels=function(pixels){var numeric=this._interval*pixels/this._pixelsPerInterval;this._start=this._unit.change(this._start,numeric);};Timeline.LinearEther.prototype.dateToPixelOffset=function(date){var numeric=this._unit.compare(date,this._start);return this._pixelsPerInterval*numeric/this._interval;};Timeline.LinearEther.prototype.pixelOffsetToDate=function(pixels){var numeric=pixels*this._interval/this._pixelsPerInterval;return this._unit.change(this._start,numeric);};Timeline.HotZoneEther=function(params){this._params=params;this._interval=params.interval;this._pixelsPerInterval=params.pixelsPerInterval;};Timeline.HotZoneEther.prototype.initialize=function(timeline){this._timeline=timeline;this._unit=timeline.getUnit();this._zones=[{startTime:Number.NEGATIVE_INFINITY,endTime:Number.POSITIVE_INFINITY,magnify:1}];var params=this._params;for(var i=0;i<params.zones.length;i++){var zone=params.zones[i];var zoneStart=this._unit.parseFromObject(zone.start);var zoneEnd=this._unit.parseFromObject(zone.end);for(var j=0;j<this._zones.length&&this._unit.compare(zoneEnd,zoneStart)>0;j++){var zone2=this._zones[j];if(this._unit.compare(zoneStart,zone2.endTime)<0){if(this._unit.compare(zoneStart,zone2.startTime)>0){this._zones.splice(j,0,{startTime:zone2.startTime,endTime:zoneStart,magnify:zone2.magnify});j++;zone2.startTime=zoneStart;}
if(this._unit.compare(zoneEnd,zone2.endTime)<0){this._zones.splice(j,0,{startTime:zoneStart,endTime:zoneEnd,magnify:zone.magnify*zone2.magnify});j++;zone2.startTime=zoneEnd;zoneStart=zoneEnd;}else{zone2.magnify*=zone.magnify;zoneStart=zone2.endTime;}}}}
if("startsOn"in this._params){this._start=this._unit.parseFromObject(this._params.startsOn);}else if("endsOn"in this._params){this._start=this._unit.parseFromObject(this._params.endsOn);this.shiftPixels(-this._timeline.getPixelLength());}else if("centersOn"in this._params){this._start=this._unit.parseFromObject(this._params.centersOn);this.shiftPixels(-this._timeline.getPixelLength()/2);}else{this._start=this._unit.makeDefaultValue();this.shiftPixels(-this._timeline.getPixelLength()/2);}};Timeline.HotZoneEther.prototype.setDate=function(date){this._start=this._unit.cloneValue(date);};Timeline.HotZoneEther.prototype.shiftPixels=function(pixels){this._start=this.pixelOffsetToDate(pixels);};Timeline.HotZoneEther.prototype.dateToPixelOffset=function(date){return this._dateDiffToPixelOffset(this._start,date);};Timeline.HotZoneEther.prototype.pixelOffsetToDate=function(pixels){return this._pixelOffsetToDate(pixels,this._start);};Timeline.HotZoneEther.prototype._dateDiffToPixelOffset=function(fromDate,toDate){var scale=this._getScale();var fromTime=fromDate;var toTime=toDate;var pixels=0;if(this._unit.compare(fromTime,toTime)<0){var z=0;while(z<this._zones.length){if(this._unit.compare(fromTime,this._zones[z].endTime)<0){break;}
z++;}
while(this._unit.compare(fromTime,toTime)<0){var zone=this._zones[z];var toTime2=this._unit.earlier(toTime,zone.endTime);pixels+=(this._unit.compare(toTime2,fromTime)/(scale/zone.magnify));fromTime=toTime2;z++;}}else{var z=this._zones.length-1;while(z>=0){if(this._unit.compare(fromTime,this._zones[z].startTime)>0){break;}
z--;}
while(this._unit.compare(fromTime,toTime)>0){var zone=this._zones[z];var toTime2=this._unit.later(toTime,zone.startTime);pixels+=(this._unit.compare(toTime2,fromTime)/(scale/zone.magnify));fromTime=toTime2;z--;}}
return pixels;};Timeline.HotZoneEther.prototype._pixelOffsetToDate=function(pixels,fromDate){var scale=this._getScale();var time=fromDate;if(pixels>0){var z=0;while(z<this._zones.length){if(this._unit.compare(time,this._zones[z].endTime)<0){break;}
z++;}
while(pixels>0){var zone=this._zones[z];var scale2=scale/zone.magnify;if(zone.endTime==Number.POSITIVE_INFINITY){time=this._unit.change(time,pixels*scale2);pixels=0;}else{var pixels2=this._unit.compare(zone.endTime,time)/scale2;if(pixels2>pixels){time=this._unit.change(time,pixels*scale2);pixels=0;}else{time=zone.endTime;pixels-=pixels2;}}
z++;}}else{var z=this._zones.length-1;while(z>=0){if(this._unit.compare(time,this._zones[z].startTime)>0){break;}
z--;}
pixels=-pixels;while(pixels>0){var zone=this._zones[z];var scale2=scale/zone.magnify;if(zone.startTime==Number.NEGATIVE_INFINITY){time=this._unit.change(time,-pixels*scale2);pixels=0;}else{var pixels2=this._unit.compare(time,zone.startTime)/scale2;if(pixels2>pixels){time=this._unit.change(time,-pixels*scale2);pixels=0;}else{time=zone.startTime;pixels-=pixels2;}}
z--;}}
return time;};Timeline.HotZoneEther.prototype._getScale=function(){return this._interval/this._pixelsPerInterval;};
/* labellers.js */
Timeline.GregorianDateLabeller=function(locale,timeZone){this._locale=locale;this._timeZone=timeZone;};Timeline.GregorianDateLabeller.monthNames=[];Timeline.GregorianDateLabeller.dayNames=[];Timeline.GregorianDateLabeller.labelIntervalFunctions=[];Timeline.GregorianDateLabeller.getMonthName=function(month,locale){return Timeline.GregorianDateLabeller.monthNames[locale][month];};Timeline.GregorianDateLabeller.prototype.labelInterval=function(date,intervalUnit){var f=Timeline.GregorianDateLabeller.labelIntervalFunctions[this._locale];if(f==null){f=Timeline.GregorianDateLabeller.prototype.defaultLabelInterval;}
return f.call(this,date,intervalUnit);};Timeline.GregorianDateLabeller.prototype.labelPrecise=function(date){return Timeline.DateTime.removeTimeZoneOffset(date,this._timeZone).toUTCString();};Timeline.GregorianDateLabeller.prototype.defaultLabelInterval=function(date,intervalUnit){var text;var emphasized=false;date=Timeline.DateTime.removeTimeZoneOffset(date,this._timeZone);switch(intervalUnit){case Timeline.DateTime.MILLISECOND:text=date.getUTCMilliseconds();break;case Timeline.DateTime.SECOND:text=date.getUTCSeconds();break;case Timeline.DateTime.MINUTE:var m=date.getUTCMinutes();if(m==0){text=date.getUTCHours()+":00";emphasized=true;}else{text=m;}
break;case Timeline.DateTime.HOUR:text=date.getUTCHours()+"hr";break;case Timeline.DateTime.DAY:text=Timeline.GregorianDateLabeller.getMonthName(date.getUTCMonth(),this._locale)+" "+date.getUTCDate();break;case Timeline.DateTime.WEEK:text=Timeline.GregorianDateLabeller.getMonthName(date.getUTCMonth(),this._locale)+" "+date.getUTCDate();break;case Timeline.DateTime.MONTH:var m=date.getUTCMonth();if(m!=0){text=Timeline.GregorianDateLabeller.getMonthName(m,this._locale);break;}
case Timeline.DateTime.YEAR:case Timeline.DateTime.DECADE:case Timeline.DateTime.CENTURY:case Timeline.DateTime.MILLENNIUM:var y=date.getUTCFullYear();if(y>0){text=date.getUTCFullYear();}else{text=(1-y)+"BC";}
emphasized=(intervalUnit==Timeline.DateTime.MONTH)||(intervalUnit==Timeline.DateTime.DECADE&&y%100==0)||(intervalUnit==Timeline.DateTime.CENTURY&&y%1000==0);break;default:text=date.toUTCString();}
return{text:text,emphasized:emphasized};}
/* layouts.js */
Timeline.StaticTrackBasedLayout=function(params){this._eventSource=params.eventSource;this._ether=params.ether;this._theme=params.theme;this._showText=("showText"in params)?params.showText:true;this._laidout=false;var layout=this;if(this._eventSource!=null){this._eventSource.addListener({onAddMany:function(){layout._laidout=false;}});}};Timeline.StaticTrackBasedLayout.prototype.initialize=function(timeline){this._timeline=timeline;};Timeline.StaticTrackBasedLayout.prototype.getTrack=function(evt){if(!this._laidout){this._tracks=[];this._layout();this._laidout=true;}
return this._tracks[evt.getID()];};Timeline.StaticTrackBasedLayout.prototype.getTrackCount=function(){if(!this._laidout){this._tracks=[];this._layout();this._laidout=true;}
return this._trackCount;};Timeline.StaticTrackBasedLayout.prototype._layout=function(){if(this._eventSource==null){return;}
var streams=[Number.NEGATIVE_INFINITY];var layout=this;var showText=this._showText;var theme=this._theme;var eventTheme=theme.event;var layoutInstant=function(evt,startPixel,endPixel,streamOffset){var finalPixel=startPixel-1;if(evt.isImprecise()){finalPixel=endPixel;}
if(showText){finalPixel=Math.max(finalPixel,startPixel+eventTheme.label.width);}
return finalPixel;};var layoutDuration=function(evt,startPixel,endPixel,streamOffset){if(evt.isImprecise()){var startDate=evt.getStart();var endDate=evt.getEnd();var startPixel2=Math.round(layout._ether.dateToPixelOffset(startDate));var endPixel2=Math.round(layout._ether.dateToPixelOffset(endDate));}else{var startPixel2=startPixel;var endPixel2=endPixel;}
var finalPixel=endPixel2;var length=Math.max(endPixel2-startPixel2,1);if(showText){if(length<eventTheme.label.width){finalPixel=endPixel2+eventTheme.label.width;}}
return finalPixel;};var layoutEvent=function(evt){var startDate=evt.getStart();var endDate=evt.getEnd();var startPixel=Math.round(layout._ether.dateToPixelOffset(startDate));var endPixel=Math.round(layout._ether.dateToPixelOffset(endDate));var streamIndex=0;for(;streamIndex<streams.length;streamIndex++){if(streams[streamIndex]<startPixel){break;}}
if(streamIndex>=streams.length){streams.push(Number.NEGATIVE_INFINITY);}
var streamOffset=(eventTheme.track.offset+
streamIndex*(eventTheme.track.height+eventTheme.track.gap))+"em";layout._tracks[evt.getID()]=streamIndex;if(evt.isInstant()){streams[streamIndex]=layoutInstant(evt,startPixel,endPixel,streamOffset);}else{streams[streamIndex]=layoutDuration(evt,startPixel,endPixel,streamOffset);}};var iterator=this._eventSource.getAllEventIterator();while(iterator.hasNext()){var evt=iterator.next();layoutEvent(evt);}
this._trackCount=streams.length;};
/* painters.js */
Timeline.DurationEventPainter=function(params){this._params=params;this._theme=params.theme;this._layout=params.layout;this._showText=params.showText;this._showLineForNoText=("showLineForNoText"in params)?params.showLineForNoText:params.theme.event.instant.showLineForNoText;this._filterMatcher=null;this._highlightMatcher=null;};Timeline.DurationEventPainter.prototype.initialize=function(band,timeline){this._band=band;this._timeline=timeline;this._layout.initialize(band,timeline);this._eventLayer=null;this._highlightLayer=null;};Timeline.DurationEventPainter.prototype.getLayout=function(){return this._layout;};Timeline.DurationEventPainter.prototype.setLayout=function(layout){this._layout=layout;};Timeline.DurationEventPainter.prototype.getFilterMatcher=function(){return this._filterMatcher;};Timeline.DurationEventPainter.prototype.setFilterMatcher=function(filterMatcher){this._filterMatcher=filterMatcher;};Timeline.DurationEventPainter.prototype.getHighlightMatcher=function(){return this._highlightMatcher;};Timeline.DurationEventPainter.prototype.setHighlightMatcher=function(highlightMatcher){this._highlightMatcher=highlightMatcher;};Timeline.DurationEventPainter.prototype.paint=function(){var eventSource=this._band.getEventSource();if(eventSource==null){return;}
if(this._highlightLayer!=null){this._band.removeLayerDiv(this._highlightLayer);}
this._highlightLayer=this._band.createLayerDiv(105);this._highlightLayer.setAttribute("name","event-highlights");this._highlightLayer.style.display="none";if(this._eventLayer!=null){this._band.removeLayerDiv(this._eventLayer);}
this._eventLayer=this._band.createLayerDiv(110);this._eventLayer.setAttribute("name","events");this._eventLayer.style.display="none";var minDate=this._band.getMinDate();var maxDate=this._band.getMaxDate();var doc=this._timeline.getDocument();var p=this;var eventLayer=this._eventLayer;var highlightLayer=this._highlightLayer;var showText=this._showText;var theme=this._params.theme;var eventTheme=theme.event;var trackOffset=eventTheme.track.offset;var trackHeight=("trackHeight"in this._params)?this._params.trackHeight:eventTheme.track.height;var trackGap=("trackGap"in this._params)?this._params.trackGap:eventTheme.track.gap;var appendIcon=function(evt,div){var icon=evt.getIcon();var img=Timeline.Graphics.createTranslucentImage(doc,icon!=null?icon:eventTheme.instant.icon);div.appendChild(img);div.style.cursor="pointer";Timeline.DOM.registerEvent(div,"mousedown",function(elmt,domEvt,target){p._onClickInstantEvent(img,domEvt,evt);});};var createHighlightDiv=function(highlightIndex,startPixel,length,highlightOffset,highlightWidth){if(highlightIndex>=0){var color=eventTheme.highlightColors[Math.min(highlightIndex,eventTheme.highlightColors.length-1)];var div=doc.createElement("div");div.style.position="absolute";div.style.overflow="hidden";div.style.left=(startPixel-3)+"px";div.style.width=(length+6)+"px";div.style.top=highlightOffset+"em";div.style.height=highlightWidth+"em";div.style.background=color;highlightLayer.appendChild(div);}};var createInstantDiv=function(evt,startPixel,endPixel,streamOffset,highlightIndex,highlightOffset,highlightWidth){if(evt.isImprecise()){var length=Math.max(endPixel-startPixel,1);var divImprecise=doc.createElement("div");divImprecise.style.position="absolute";divImprecise.style.overflow="hidden";divImprecise.style.top=streamOffset;divImprecise.style.height=trackHeight+"em";divImprecise.style.left=startPixel+"px";divImprecise.style.width=length+"px";divImprecise.style.background=eventTheme.instant.impreciseColor;if(eventTheme.instant.impreciseOpacity<100){Timeline.Graphics.setOpacity(divImprecise,eventTheme.instant.impreciseOpacity);}
eventLayer.appendChild(divImprecise);}
var div=doc.createElement("div");div.style.position="absolute";div.style.overflow="hidden";eventLayer.appendChild(div);var foreground=evt.getTextColor();var background=evt.getColor();var realign=-8;var length=16;if(showText){div.style.width=eventTheme.label.width+"px";div.style.color=foreground!=null?foreground:eventTheme.label.outsideColor;appendIcon(evt,div);div.appendChild(doc.createTextNode(evt.getText()));}else{if(p._showLineForNoText){div.style.width="1px";div.style.borderLeft="1px solid "+(background!=null?background:eventTheme.instant.lineColor);realign=0;length=1;}else{appendIcon(evt,div);}}
div.style.top=streamOffset;div.style.height=trackHeight+"em";div.style.left=(startPixel+realign)+"px";createHighlightDiv(highlightIndex,(startPixel+realign),length,highlightOffset,highlightWidth);};var createDurationDiv=function(evt,startPixel,endPixel,streamOffset,highlightIndex,highlightOffset,highlightWidth){var attachClickEvent=function(elmt){elmt.style.cursor="pointer";Timeline.DOM.registerEvent(elmt,"mousedown",function(elmt,domEvt,target){p._onClickDurationEvent(domEvt,evt,target);});};var length=Math.max(endPixel-startPixel,1);if(evt.isImprecise()){var div=doc.createElement("div");div.style.position="absolute";div.style.overflow="hidden";div.style.top=streamOffset;div.style.height=trackHeight+"em";div.style.left=startPixel+"px";div.style.width=length+"px";div.style.background=eventTheme.duration.impreciseColor;if(eventTheme.duration.impreciseOpacity<100){Timeline.Graphics.setOpacity(div,eventTheme.duration.impreciseOpacity);}
eventLayer.appendChild(div);var startDate=evt.getLatestStart();var endDate=evt.getEarliestEnd();var startPixel2=Math.round(p._band.dateToPixelOffset(startDate));var endPixel2=Math.round(p._band.dateToPixelOffset(endDate));}else{var startPixel2=startPixel;var endPixel2=endPixel;}
var foreground=evt.getTextColor();var outside=true;if(startPixel2<=endPixel2){length=Math.max(endPixel2-startPixel2,1);outside=!(length>eventTheme.label.width);div=doc.createElement("div");div.style.position="absolute";div.style.overflow="hidden";div.style.top=streamOffset;div.style.height=trackHeight+"em";div.style.left=startPixel2+"px";div.style.width=length+"px";var background=evt.getColor();div.style.background=background!=null?background:eventTheme.duration.color;if(eventTheme.duration.opacity<100){Timeline.Graphics.setOpacity(div,eventTheme.duration.opacity);}
eventLayer.appendChild(div);}else{var temp=startPixel2;startPixel2=endPixel2;endPixel2=temp;}
if(div==null){console.log(evt);}
attachClickEvent(div);if(showText){var divLabel=doc.createElement("div");divLabel.style.position="absolute";divLabel.style.top=streamOffset;divLabel.style.height=trackHeight+"em";divLabel.style.left=((length>eventTheme.label.width)?startPixel2:endPixel2)+"px";divLabel.style.width=eventTheme.label.width+"px";divLabel.style.color=foreground!=null?foreground:(outside?eventTheme.label.outsideColor:eventTheme.label.insideColor);divLabel.style.overflow="hidden";divLabel.appendChild(doc.createTextNode(evt.getText()));eventLayer.appendChild(divLabel);attachClickEvent(divLabel);}
createHighlightDiv(highlightIndex,startPixel,endPixel-startPixel,highlightOffset,highlightWidth);};var createEventDiv=function(evt,highlightIndex){var startDate=evt.getStart();var endDate=evt.getEnd();var startPixel=Math.round(p._band.dateToPixelOffset(startDate));var endPixel=Math.round(p._band.dateToPixelOffset(endDate));var streamOffset=(trackOffset+
p._layout.getTrack(evt)*(trackHeight+trackGap));if(evt.isInstant()){createInstantDiv(evt,startPixel,endPixel,streamOffset+"em",highlightIndex,streamOffset-trackGap,trackHeight+2*trackGap);}else{createDurationDiv(evt,startPixel,endPixel,streamOffset+"em",highlightIndex,streamOffset-trackGap,trackHeight+2*trackGap);}};var filterMatcher=(this._filterMatcher!=null)?this._filterMatcher:function(evt){return true;};var highlightMatcher=(this._highlightMatcher!=null)?this._highlightMatcher:function(evt){return-1;};var iterator=eventSource.getEventIterator(minDate,maxDate);while(iterator.hasNext()){var evt=iterator.next();if(filterMatcher(evt)){createEventDiv(evt,highlightMatcher(evt));}}
this._highlightLayer.style.display="block";this._eventLayer.style.display="block";};Timeline.DurationEventPainter.prototype.softPaint=function(){};Timeline.DurationEventPainter.prototype._onClickInstantEvent=function(icon,domEvt,evt){domEvt.cancelBubble=true;var c=Timeline.DOM.getPageCoordinates(icon);this._showBubble(c.left+Math.ceil(icon.offsetWidth/2),c.top+Math.ceil(icon.offsetHeight/2),evt);};Timeline.DurationEventPainter.prototype._onClickDurationEvent=function(domEvt,evt,target){domEvt.cancelBubble=true;if("pageX"in domEvt){var x=domEvt.pageX;var y=domEvt.pageY;}else{var c=Timeline.DOM.getPageCoordinates(target);var x=domEvt.offsetX+c.left;var y=domEvt.offsetY+c.top;}
this._showBubble(x,y,evt);};Timeline.DurationEventPainter.prototype._showBubble=function(x,y,evt){var div=this._band.openBubbleForPoint(x,y,this._theme.event.bubble.width,this._theme.event.bubble.height);evt.fillInfoBubble(div,this._theme,this._band.getLabeller());};
/* sources.js */
Timeline.DefaultEventSource=function(eventIndex){this._events=(eventIndex instanceof Object)?eventIndex:new Timeline.EventIndex();this._listeners=[];};Timeline.DefaultEventSource.prototype.addListener=function(listener){this._listeners.push(listener);};Timeline.DefaultEventSource.prototype.removeListener=function(listener){for(var i=0;i<this._listeners.length;i++){if(this._listeners[i]==listener){this._listeners.splice(i,1);break;}}};Timeline.DefaultEventSource.prototype.loadXML=function(xml,url){var base=this._getBaseURL(url);var wikiURL=xml.documentElement.getAttribute("wiki-url");var wikiSection=xml.documentElement.getAttribute("wiki-section");var dateTimeFormat=xml.documentElement.getAttribute("date-time-format");var parseDateTimeFunction=this._events.getUnit().getParser(dateTimeFormat);var node=xml.documentElement.firstChild;var added=false;while(node!=null){if(node.nodeType==1){var description="";if(node.firstChild!=null&&node.firstChild.nodeType==3){description=node.firstChild.nodeValue;}
var evt=new Timeline.DefaultEventSource.Event(parseDateTimeFunction(node.getAttribute("start")),parseDateTimeFunction(node.getAttribute("end")),parseDateTimeFunction(node.getAttribute("latestStart")),parseDateTimeFunction(node.getAttribute("earliestEnd")),node.getAttribute("isDuration")!="true",node.getAttribute("title"),description,this._resolveRelativeURL(node.getAttribute("image"),base),this._resolveRelativeURL(node.getAttribute("link"),base),this._resolveRelativeURL(node.getAttribute("icon"),base),node.getAttribute("color"),node.getAttribute("textColor"));evt._node=node;evt.getProperty=function(name){return this._node.getAttribute(name);};evt.setWikiInfo(wikiURL,wikiSection);this._events.add(evt);added=true;}
node=node.nextSibling;}
if(added){this._fire("onAddMany",[]);}};Timeline.DefaultEventSource.prototype.loadJSON=function(data,url){var base=this._getBaseURL(url);var added=false;if(data&&data.events){var wikiURL=("wikiURL"in data)?data.wikiURL:null;var wikiSection=("wikiSection"in data)?data.wikiSection:null;var dateTimeFormat=("dateTimeFormat"in data)?data.dateTimeFormat:null;var parseDateTimeFunction=this._events.getUnit().getParser(dateTimeFormat);for(var i=0;i<data.events.length;i++){var event=data.events[i];var evt=new Timeline.DefaultEventSource.Event(parseDateTimeFunction(event.start),parseDateTimeFunction(event.end),parseDateTimeFunction(event.latestStart),parseDateTimeFunction(event.earliestEnd),event.isDuration||false,event.title,event.description,this._resolveRelativeURL(event.image,base),this._resolveRelativeURL(event.link,base),this._resolveRelativeURL(event.icon,base),event.color,event.textColor);evt._obj=event;evt.getProperty=function(name){return this._obj[name];};evt.setWikiInfo(wikiURL,wikiSection);this._events.add(evt);added=true;}}
if(added){this._fire("onAddMany",[]);}};Timeline.DefaultEventSource.prototype.loadSPARQL=function(xml,url){var base=this._getBaseURL(url);var dateTimeFormat='iso8601';var parseDateTimeFunction=this._events.getUnit().getParser(dateTimeFormat);if(xml==null){return;}
var node=xml.documentElement.firstChild;while(node!=null&&(node.nodeType!=1||node.nodeName!='results')){node=node.nextSibling;}
var wikiURL=null;var wikiSection=null;if(node!=null){wikiURL=node.getAttribute("wiki-url");wikiSection=node.getAttribute("wiki-section");node=node.firstChild;}
var added=false;while(node!=null){if(node.nodeType==1){var bindings={};var binding=node.firstChild;while(binding!=null){if(binding.nodeType==1&&binding.firstChild!=null&&binding.firstChild.nodeType==1&&binding.firstChild.firstChild!=null&&binding.firstChild.firstChild.nodeType==3){bindings[binding.getAttribute('name')]=binding.firstChild.firstChild.nodeValue;}
binding=binding.nextSibling;}
if(bindings["start"]==null&&bindings["date"]!=null){bindings["start"]=bindings["date"];}
var evt=new Timeline.DefaultEventSource.Event(parseDateTimeFunction(bindings["start"]),parseDateTimeFunction(bindings["end"]),parseDateTimeFunction(bindings["latestStart"]),parseDateTimeFunction(bindings["earliestEnd"]),bindings["isDuration"]!="true",bindings["title"],bindings["description"],this._resolveRelativeURL(bindings["image"],base),this._resolveRelativeURL(bindings["link"],base),this._resolveRelativeURL(bindings["icon"],base),bindings["color"],bindings["textColor"]);evt._bindings=bindings;evt.getProperty=function(name){return this._bindings[name];};evt.setWikiInfo(wikiURL,wikiSection);this._events.add(evt);added=true;}
node=node.nextSibling;}
if(added){this._fire("onAddMany",[]);}};Timeline.DefaultEventSource.prototype.add=function(evt){this._events.add(evt);this._fire("onAddOne",[evt]);};Timeline.DefaultEventSource.prototype.addMany=function(events){for(var i=0;i<events.length;i++){this._events.add(events[i]);}
this._fire("onAddMany",[]);};Timeline.DefaultEventSource.prototype.clear=function(){this._events.removeAll();this._fire("onClear",[]);};Timeline.DefaultEventSource.prototype.getEventIterator=function(startDate,endDate){return this._events.getIterator(startDate,endDate);};Timeline.DefaultEventSource.prototype.getAllEventIterator=function(){return this._events.getAllIterator();};Timeline.DefaultEventSource.prototype.getCount=function(){return this._events.getCount();};Timeline.DefaultEventSource.prototype.getEarliestDate=function(){return this._events.getEarliestDate();};Timeline.DefaultEventSource.prototype.getLatestDate=function(){return this._events.getLatestDate();};Timeline.DefaultEventSource.prototype._fire=function(handlerName,args){for(var i=0;i<this._listeners.length;i++){var listener=this._listeners[i];if(handlerName in listener){try{listener[handlerName].apply(listener,args);}catch(e){Timeline.Debug.exception(e);}}}};Timeline.DefaultEventSource.prototype._getBaseURL=function(url){if(url.indexOf("://")<0){var url2=this._getBaseURL(document.location.href);if(url.substr(0,1)=="/"){url=url2.substr(0,url2.indexOf("/",url2.indexOf("://")+3))+url;}else{url=url2+url;}}
var i=url.lastIndexOf("/");if(i<0){return"";}else{return url.substr(0,i+1);}};Timeline.DefaultEventSource.prototype._resolveRelativeURL=function(url,base){if(url==null||url==""){return url;}else if(url.indexOf("://")>0){return url;}else if(url.substr(0,1)=="/"){return base.substr(0,base.indexOf("/",base.indexOf("://")+3))+url;}else{return base+url;}};Timeline.DefaultEventSource.Event=function(start,end,latestStart,earliestEnd,instant,text,description,image,link,icon,color,textColor){this._id="e"+Math.floor(Math.random()*1000000);this._instant=instant||(end==null);this._start=start;this._end=(end!=null)?end:start;this._latestStart=(latestStart!=null)?latestStart:(instant?this._end:this._start);this._earliestEnd=(earliestEnd!=null)?earliestEnd:(instant?this._start:this._end);this._text=Timeline.HTML.deEntify(text);this._description=Timeline.HTML.deEntify(description);this._image=(image!=null&&image!="")?image:null;this._link=(link!=null&&link!="")?link:null;this._icon=(icon!=null&&icon!="")?icon:null;this._color=(color!=null&&color!="")?color:null;this._textColor=(textColor!=null&&textColor!="")?textColor:null;this._wikiURL=null;this._wikiSection=null;};Timeline.DefaultEventSource.Event.prototype={getID:function(){return this._id;},isInstant:function(){return this._instant;},isImprecise:function(){return this._start!=this._latestStart||this._end!=this._earliestEnd;},getStart:function(){return this._start;},getEnd:function(){return this._end;},getLatestStart:function(){return this._latestStart;},getEarliestEnd:function(){return this._earliestEnd;},getText:function(){return this._text;},getDescription:function(){return this._description;},getImage:function(){return this._image;},getLink:function(){return this._link;},getIcon:function(){return this._icon;},getColor:function(){return this._color;},getTextColor:function(){return this._textColor;},getProperty:function(name){return null;},getWikiURL:function(){return this._wikiURL;},getWikiSection:function(){return this._wikiSection;},setWikiInfo:function(wikiURL,wikiSection){this._wikiURL=wikiURL;this._wikiSection=wikiSection;},fillDescription:function(elmt){elmt.innerHTML=this._description;},fillWikiInfo:function(elmt){if(this._wikiURL!=null&&this._wikiSection!=null){var wikiID=this.getProperty("wikiID");if(wikiID==null||wikiID.length==0){wikiID=this.getText();}
wikiID=wikiID.replace(/\s/g,"_");var url=this._wikiURL+this._wikiSection.replace(/\s/g,"_")+"/"+wikiID;var a=document.createElement("a");a.href=url;a.target="new";a.innerHTML=Timeline.strings[Timeline.Platform.clientLocale].wikiLinkLabel;elmt.appendChild(document.createTextNode("["));elmt.appendChild(a);elmt.appendChild(document.createTextNode("]"));}else{elmt.style.display="none";}},fillTime:function(elmt,labeller){if(this._instant){if(this.isImprecise()){elmt.appendChild(elmt.ownerDocument.createTextNode(labeller.labelPrecise(this._start)));elmt.appendChild(elmt.ownerDocument.createElement("br"));elmt.appendChild(elmt.ownerDocument.createTextNode(labeller.labelPrecise(this._end)));}else{elmt.appendChild(elmt.ownerDocument.createTextNode(labeller.labelPrecise(this._start)));}}else{if(this.isImprecise()){elmt.appendChild(elmt.ownerDocument.createTextNode(labeller.labelPrecise(this._start)+" ~ "+labeller.labelPrecise(this._latestStart)));elmt.appendChild(elmt.ownerDocument.createElement("br"));elmt.appendChild(elmt.ownerDocument.createTextNode(labeller.labelPrecise(this._earliestEnd)+" ~ "+labeller.labelPrecise(this._end)));}else{elmt.appendChild(elmt.ownerDocument.createTextNode(labeller.labelPrecise(this._start)));elmt.appendChild(elmt.ownerDocument.createElement("br"));elmt.appendChild(elmt.ownerDocument.createTextNode(labeller.labelPrecise(this._end)));}}},fillInfoBubble:function(elmt,theme,labeller){var doc=elmt.ownerDocument;var title=this.getText();var link=this.getLink();var image=this.getImage();if(image!=null){var img=doc.createElement("img");img.src=image;theme.event.bubble.imageStyler(img);elmt.appendChild(img);}
var divTitle=doc.createElement("div");var textTitle=doc.createTextNode(title);if(link!=null){var a=doc.createElement("a");a.href=link;a.appendChild(textTitle);divTitle.appendChild(a);}else{divTitle.appendChild(textTitle);}
theme.event.bubble.titleStyler(divTitle);elmt.appendChild(divTitle);var divBody=doc.createElement("div");this.fillDescription(divBody);theme.event.bubble.bodyStyler(divBody);elmt.appendChild(divBody);var divTime=doc.createElement("div");this.fillTime(divTime,labeller);theme.event.bubble.timeStyler(divTime);elmt.appendChild(divTime);var divWiki=doc.createElement("div");this.fillWikiInfo(divWiki);theme.event.bubble.wikiStyler(divWiki);elmt.appendChild(divWiki);}};
/* themes.js */
Timeline.ClassicTheme=new Object();Timeline.ClassicTheme.implementations=[];Timeline.ClassicTheme.create=function(locale){if(locale==null){locale=Timeline.Platform.getDefaultLocale();}
var f=Timeline.ClassicTheme.implementations[locale];if(f==null){f=Timeline.ClassicTheme._Impl;}
return new f();};Timeline.ClassicTheme._Impl=function(){this.firstDayOfWeek=0;this.ether={backgroundColors:["#EEE","#DDD","#CCC","#AAA"],highlightColor:"white",highlightOpacity:50,interval:{line:{show:true,color:"#aaa",opacity:25},weekend:{color:"#FFFFE0",opacity:30},marker:{hAlign:"Bottom",hBottomStyler:function(elmt){elmt.className="timeline-ether-marker-bottom";},hBottomEmphasizedStyler:function(elmt){elmt.className="timeline-ether-marker-bottom-emphasized";},hTopStyler:function(elmt){elmt.className="timeline-ether-marker-top";},hTopEmphasizedStyler:function(elmt){elmt.className="timeline-ether-marker-top-emphasized";},vAlign:"Right",vRightStyler:function(elmt){elmt.className="timeline-ether-marker-right";},vRightEmphasizedStyler:function(elmt){elmt.className="timeline-ether-marker-right-emphasized";},vLeftStyler:function(elmt){elmt.className="timeline-ether-marker-left";},vLeftEmphasizedStyler:function(elmt){elmt.className="timeline-ether-marker-left-emphasized";}}}};this.event={track:{offset:0.5,height:1.5,gap:0.5},instant:{icon:Timeline.urlPrefix+"images/dull-blue-circle.png",lineColor:"#58A0DC",impreciseColor:"#58A0DC",impreciseOpacity:20,showLineForNoText:true},duration:{color:"#58A0DC",opacity:100,impreciseColor:"#58A0DC",impreciseOpacity:20},label:{insideColor:"white",outsideColor:"black",width:200},highlightColors:["#FFFF00","#FFC000","#FF0000","#0000FF"],bubble:{width:250,height:125,titleStyler:function(elmt){elmt.className="timeline-event-bubble-title";},bodyStyler:function(elmt){elmt.className="timeline-event-bubble-body";},imageStyler:function(elmt){elmt.className="timeline-event-bubble-image";},wikiStyler:function(elmt){elmt.className="timeline-event-bubble-wiki";},timeStyler:function(elmt){elmt.className="timeline-event-bubble-time";}}};};
/* units.js */
Timeline.NativeDateUnit=new Object();Timeline.NativeDateUnit.createLabeller=function(locale,timeZone){return new Timeline.GregorianDateLabeller(locale,timeZone);};Timeline.NativeDateUnit.makeDefaultValue=function(){return new Date();};Timeline.NativeDateUnit.cloneValue=function(v){return new Date(v.getTime());};Timeline.NativeDateUnit.getParser=function(format){if(typeof format=="string"){format=format.toLowerCase();}
return(format=="iso8601"||format=="iso 8601")?Timeline.DateTime.parseIso8601DateTime:Timeline.DateTime.parseGregorianDateTime;};Timeline.NativeDateUnit.parseFromObject=function(o){return Timeline.DateTime.parseGregorianDateTime(o);};Timeline.NativeDateUnit.toNumber=function(v){return v.getTime();};Timeline.NativeDateUnit.fromNumber=function(n){return new Date(n);};Timeline.NativeDateUnit.compare=function(v1,v2){var n1,n2;if(typeof v1=="object"){n1=v1.getTime();}else{n1=Number(v1);}
if(typeof v2=="object"){n2=v2.getTime();}else{n2=Number(v2);}
return n1-n2;};Timeline.NativeDateUnit.earlier=function(v1,v2){return Timeline.NativeDateUnit.compare(v1,v2)<0?v1:v2;};Timeline.NativeDateUnit.later=function(v1,v2){return Timeline.NativeDateUnit.compare(v1,v2)>0?v1:v2;};Timeline.NativeDateUnit.change=function(v,n){return new Date(v.getTime()+n);};
Timeline.GregorianDateLabeller.monthNames["en"] = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
Timeline.strings["en"] = {wikiLinkLabel:"Discuss"};
} // end of 'install only once'
/*}}}*/
/***
|''Name:''|SimileTimelinePlugin|
|''Description:''|Plugin to support Simile Timelines, see http://simile.mit.edu/timeline/ |
|''Author:''|Martin Budden ( mjbudden [at] gmail [dot] com)|
|''Source:''|http://www.martinswiki.com/#SimileTimelineBundlePlugin |
|''CodeRepository:''|http://svn.tiddlywiki.org/Trunk/contributors/MartinBudden/plugins/SimileTimelinePlugin.js |
|''Version:''|0.1.8|
|''Date:''|Jul 20, 2010|
|''Comments:''|Please make comments at http://groups.google.co.uk/group/TiddlyWikiDev |
|''License:''|[[Creative Commons Attribution-ShareAlike 3.0 License|http://creativecommons.org/licenses/by-sa/3.0/]] |
|''~CoreVersion:''|2.2|
Note that to use this pluing you also need:
1) to install SimileTimelineBundlePlugin (see http://svn.tiddlywiki.org/Trunk/contributors/MartinBudden/plugins/SimileTimelineBundlePlugin.js )
2) to include [[SimileTimelineStyleBundle]] in your StyleSheet tiddler (see http://svn.tiddlywiki.org/Trunk/contributors/MartinBudden/styles/SimileTimelineStyleBundle.css.js )
!!Todo
* fix bubble height and width
* etherpainters
* allow multiple painters per timeline
* multiple event sources per band
* multiple clocks
* JSON
* XML
***/
/*{{{*/
// Ensure that the SimileTimelinePlugin is only installed once.
if(!version.extensions.SimileTimelinePlugin) {
version.extensions.SimileTimelinePlugin = {installed:true};
if(version.major < 2 || (version.major == 2 && version.minor < 2))
{alertAndThrow('SimileTimelineBundlePlugin requires TiddlyWiki 2.2 or newer.');}
config.macros.SimileTimeline = {};
config.macros.SimileTimeline.closeTiddler = Story.prototype.closeTiddler;
Story.prototype.closeTiddler = function(title,animate,slowly)
{
config.macros.SimileTimeline.closeTiddler.apply(this,arguments);
if(config.macros.SimileTimeline.tickTitle && config.macros.SimileTimeline.tickTitle==title) {
config.macros.SimileTimeline.clearTick();
}
};
// used for date string in bubble
Timeline.urlPrefix = 'http://martinswiki.com/timeline/'; //!! kludge for now
Timeline.GregorianDateLabeller.prototype.labelPrecise = function(date)
{
var dt = Timeline.DateTime.removeTimeZoneOffset(date,this._timeZone);
var template = "mmm DD, YYYY";
return dt.formatString(template);
//#return dt.toUTCString();
};
Timeline.GregorianDateLabeller.labelIntervalWeek = function(date)
{
var dt = Timeline.DateTime.removeTimeZoneOffset(date,this._timeZone);
var text = '' + dt.getWeek();
return {text:text,emphasized:false};
};
Timeline.GregorianDateLabeller.labelIntervalFunctions['en'] = function(date,intervalUnit)
{
if(intervalUnit==Timeline.DateTime.WEEK) {
var dt = Timeline.DateTime.removeTimeZoneOffset(date,this._timeZone);
var text = '' + dt.getWeek();
return {text:text,emphasized:false};
} else {
return this.defaultLabelInterval(date,intervalUnit);
}
};
Timeline.DefaultEventSource.Event.prototype.fillDescription = function(e)
{
e.innerHTML = wikifyStatic(this._description,null,new Tiddler("temp"));
};
/*
Timeline.DefaultEventSource.Event.prototype.fillInfoBubble = function(elmt,theme,labeller)
{
var doc = elmt.ownerDocument;
var image = this.getImage();
if(image) {
var img = doc.createElement("img");
img.src = image;
theme.event.bubble.imageStyler(img);
elmt.appendChild(img);
}
var divTitle = doc.createElement("div");
var title = this.getText();
var textTitle = doc.createTextNode(title);
var link = this.getLink();
if(link) {
var a = doc.createElement("a");
a.href = link;
a.appendChild(textTitle);
divTitle.appendChild(a);
} else {
divTitle.appendChild(textTitle);
}
theme.event.bubble.titleStyler(divTitle);
elmt.appendChild(divTitle);
var divBody = doc.createElement("div");
this.fillDescription(divBody);
theme.event.bubble.bodyStyler(divBody);
elmt.appendChild(divBody);
var divTime = doc.createElement("div");
this.fillTime(divTime,labeller);
theme.event.bubble.timeStyler(divTime);
elmt.appendChild(divTime);
var divWiki = doc.createElement("div");
this.fillWikiInfo(divWiki);
theme.event.bubble.wikiStyler(divWiki);
elmt.appendChild(divWiki);
};
*/
Timeline.loadTiddlerJSON = function(title,fn)
{
var tiddler = store.fetchTiddler(title);
try {
var uri = '';
var j = eval('(' + tiddler.text + ')');
fn(j,uri);
} catch(ex) {
return exceptionText(ex);
}
};
Timeline.loadTiddlers = function(data,fn)
{
fn(data);
};
Timeline.DefaultEventSource.prototype.loadTiddlers = function(data)
{
//#displayMessage('loadTiddlers:'+data.params);
var include = true;
var tag = data.params;
if(data.type && data.type=='tiddlerFields') {
if(!tag) {
tag = 'excludeLists';
include = false;
}
}
var url = data.url ? data.url : "dummy";
var base = this._getBaseURL(url);
// wikiURL and wikiSection used for the "Discuss" button.
var wikiURL = data.wikiURL;
var wikiSection = data.wikiSection;
var dateTimeFormat = null;
var parseDateTimeFunction = this._events.getUnit().getParser(dateTimeFormat);
var added = false;
var tiddlers = store.reverseLookup('tags',tag,include);
//#displayMessage('length:'+tiddlers.length);
for(var i=0; i<tiddlers.length; i++) {
//var event = config.macros.SimileTimeline.getEvent(tiddlers[i].title);
var event = tiddlers[i].getSimileTimelineEvent(data.type);
var evt = new Timeline.DefaultEventSource.Event(
parseDateTimeFunction(event.start),
parseDateTimeFunction(event.end),
parseDateTimeFunction(event.latestStart),
parseDateTimeFunction(event.earliestEnd),
event.isDuration || false,
event.title,
event.description,
this._resolveRelativeURL(event.image,base),
this._resolveRelativeURL(event.link,''),
this._resolveRelativeURL(event.icon,base),
event.color,
event.textColor);
evt._obj = event;
evt.getProperty = function(name) {
return this._obj[name];
};
evt.setWikiInfo(wikiURL,wikiSection);
this._events.add(evt);
added = true;
}
if (added) {
this._fire('onAddMany',[]);
}
};
/*
Timeline.loadXML = function(url, f) {
var fError = function(statusText, status, xmlhttp) {
alert("Failed to load data xml from " + url + "\n" + statusText);
};
var fDone = function(xmlhttp) {
var xml = xmlhttp.responseXML;
if (!xml.documentElement && xmlhttp.responseStream) {
xml.load(xmlhttp.responseStream);
}
f(xml,url);
};
SimileAjax.XmlHttp.get(url, fError, fDone);
};
*/
Timeline.loadXMLRemote = function(uri,f) {
var callback = function(status,context,responseText,uri,xhr) {
if(status) {
var xml = xhr.responseXML;
if(window.Components && window.netscape && window.netscape.security && document.location.protocol.indexOf("http") == -1)
window.netscape.security.PrivilegeManager.enablePrivilege("UniversalBrowserRead");
if (!xml.documentElement && xhr.responseStream) {
xml.load(xhr.responseStream);
}
try {
f(xml,uri);
} catch(ex) {
return exceptionText(ex);
}
} else {
alert("Failed to load data xml from " + uri + "\n" + xhr.statusText);
}
};
loadRemoteFile(uri,callback)
};
/*
Timeline.loadJSON = function(url, f) {
var fError = function(statusText, status, xmlhttp) {
alert("Failed to load json data from " + url + "\n" + statusText);
};
var fDone = function(xmlhttp) {
f(eval('(' + xmlhttp.responseText + ')'), url);
};
SimileAjax.XmlHttp.get(url, fError, fDone);
};
*/
Timeline.loadJSONRemote = function(uri,f) {
var callback = function(status,context,responseText,uri,xhr) {
if(status) {
var json = responseText;
var data = eval('(' + json + ')');
try {
f(data,uri);
} catch(ex) {
return exceptionText(ex);
}
} else {
alert("Failed to load data xml from " + uri + "\n" + xhr.statusText);
}
};
loadRemoteFile(uri,callback)
};
Timeline.loadJSONFile = function(filePath,f) {
var json = loadFile(filePath);
try {
var data = eval('(' + json + ')');
f(data, filePath);
} catch(ex) {
return exceptionText(ex);
}
};
Tiddler.prototype.getSimileTimelineEvent = function(type,eventFields)
{
//#displayMessage('getEvent:'+this.title);
var t = this.title;
var f = eventFields ? eventFields : config.macros.SimileTimeline.eventFields;
var ev = {};
if(type && type=='tiddlerFields') {
//# get the event from the tiddler's fields
ev.start = this.created;
ev.end = this.modified;
ev.isDuration = true;
ev.title = t;
ev.description = this.text ? this.text : '';
ev.link = this.fields.link;
if(!ev.link)
//ev.link = 'javascript:story.displayTiddler(null,"' + t + '")';
ev.link = store.getTiddlerText('SiteUrl',null) + '#' + encodeURIComponent(String.encodeTiddlyLink(t));
} else {
//# get the event from the slices specified by the eventFields
ev.start = store.getTiddlerSlice(t,f.start);
ev.latestStart = store.getTiddlerSlice(t,f.latestStart);
ev.end = store.getTiddlerSlice(t,f.end);
ev.earliestEnd = store.getTiddlerSlice(t,f.earliestEnd);
ev.isDuration = store.getTiddlerSlice(t,f.isDuration);
ev.title = store.getTiddlerSlice(t,f.title);
ev.description = store.getTiddlerSlice(t,f.description);
if(!ev.description)
ev.description = '';
ev.image = store.getTiddlerSlice(t,f.image);
ev.link = store.getTiddlerSlice(t,f.link);
if(!ev.link)
ev.link = 'javascript:story.displayTiddler(null,"' + t + '")';
ev.icon = store.getTiddlerSlice(t,f.icon);
ev.color = store.getTiddlerSlice(t,f.color);
ev.textColor = store.getTiddlerSlice(t,f.textColor);
}
//#displayMessage('t:'+ev.title+' s:'+ev.start+' e:'+ev.end);
return ev;
};
// to allow loading from tiddlers with differently named fields
config.macros.SimileTimeline.eventFields = {
start:'start',latestStart:'latestStart',end:'end',earliestEnd:'earliestEnd',
isDuration:'isDuration',title:'title',description:'description',image:'image',link:'link',
icon:'icon',color:'color',textColor:'textColor'
};
config.macros.SimileTimeline.handler = function(place,macroName,params,wikifier,paramString,tiddler)
{
this.clearTick();
var spec = params[0];
var eventSource = new Timeline.DefaultEventSource();
var theme = Timeline.ClassicTheme.create();
//# default bubble size is w:250, h:125
var bWidth = store.getTiddlerSlice(spec,'bubbleWidth');
if(bWidth)
theme.event.bubble.width = bWidth;
var bHeight = store.getTiddlerSlice(spec,'bubbleHeight');
if(bHeight)
theme.event.bubble.height = bHeight;
//#var defaultDate = Timeline.DateTime.parseGregorianDateTime('2000');
var defaultDate = new Date();
var eventSources = [];
var ev = {};
var bandInfos = [];
var i = 0;
var bandParams = config.macros.SimileTimeline.getBandParams(spec,'0',defaultDate);
while(bandParams) {
if(bandParams.ev.type && bandParams.ev.type != 'none') {
bandParams.bp.eventSource = eventSource;
ev = bandParams.ev;
if(bandParams.ev.type != 'timer') {
ev.source = eventSource;
}
if(eventSources.length==0)// !!!for now only deal with one eventSource
eventSources.push(ev);
}
bandParams.theme = theme;
defaultDate = bandParams.bp.date;
bi = bandParams.bp.zones ? Timeline.createHotZoneBandInfo(bandParams.bp) : Timeline.createBandInfo(bandParams.bp);
bandInfos.push(bi);
if(bandParams.ep) {
var ep = bandParams.ep;
//#displayMessage("ep:"+ep.etherPainter+" ep.sd:"+ep.startDate+" ep.m:"+ep.multiple);
try {
bandInfos[i].etherPainter = new Timeline[ep.etherPainter]({startDate:ep.startDate,multiple:ep.multiple,theme:theme});
} catch(ex) {
}
}
if(bandParams.dec) {
var dec = bandParams.dec;
//#displayMessage("dec:"+dec.decorator+" dec.sd:"+dec.startDate+" dec.ed:"+dec.endDate);
try {
bandInfos[i].decorators = [new Timeline[dec.decorator]({
startDate:dec.startDate,
endDate:dec.endDate,
startLabel:'',//dec.startLabel,
endLabel:'',//dec.endLabel,
color:dec.color,
opacity:dec.opacity,
theme:theme})];
} catch(ex) {
}
}
if(i>0) {
bandInfos[i].syncWith = 0;
bandInfos[i].highlight = bandParams.highlight;
}
i++;
bandParams = config.macros.SimileTimeline.getBandParams(spec,String(i),defaultDate);
}
var timelineElem = createTiddlyElement(place,'div',null,'simileTimeline');// simileTimeline css class
var tHeight = store.getTiddlerSlice(spec,'timelineHeight');
timelineElem.style['height'] = tHeight ? tHeight + 'px' : '150px';
var tBorder = store.getTiddlerSlice(spec,'timelineBorder');
if(tBorder)
timelineElem.style['border'] = tBorder + 'px';
config.macros.SimileTimeline.timeline = Timeline.create(timelineElem,bandInfos);
var data = {};
for(i=0;i<eventSources.length;i++) {
ev = eventSources[i];
if(ev.type=='timer') {
config.macros.SimileTimeline.tickTitle = tiddler.title;//!!! temporary kludge, only support one timer
config.macros.SimileTimeline.timerId = setTimeout('config.macros.SimileTimeline.tick()',1000);
}
data.type = ev.type;
data.params = ev.params;
if(ev.source) {
switch(data.type) {
case 'XML':
//#Timeline.loadXML("example1.xml", function(xml,url) { ev.source.loadXML(xml,url); });
Timeline.loadXMLRemote(data.params,function(xml,url) { if(ev.source) ev.source.loadXML(xml,url); });
break;
case 'tiddlerJSON':
Timeline.loadTiddlerJSON(data.params,function(data,url) { if(ev.source) ev.source.loadJSON(data,url); });
break;
case 'JSON':
Timeline.loadJSONRemote(data.params,function(data,url) { if(ev.source) ev.source.loadJSON(data,url); });
break;
default:
if(data.type||data.params) {
Timeline.loadTiddlers(data,function(data,url) { if(ev.source) ev.source.loadTiddlers(data,url); });
}
break;
}
}
}
eventSource.addMany([]);
};
config.macros.SimileTimeline.tick = function()
{
//#displayMessage("tick");
config.macros.SimileTimeline.timeline.getBand(0).setCenterVisibleDate(new Date());
if(config.macros.SimileTimeline.timerId)
config.macros.SimileTimeline.timerId = setTimeout('config.macros.SimileTimeline.tick()',1000);
};
config.macros.SimileTimeline.clearTick = function()
{
//#displayMessage("clearTick");
if(config.macros.SimileTimeline.timerId)
clearTimeout(config.macros.SimileTimeline.timerId);
config.macros.SimileTimeline.timerId = null;
};
config.macros.SimileTimeline.getBandParams = function(title,n,defaultDate)
{
var t = title;
var pfx = 'band' + String(n) + '.';
var width = store.getTiddlerSlice(t,pfx+'width');
//#displayMessage("width"+":"+width);
if(!width)
return null;
var bp = {};
bp.width = width;
var intervalUnit = store.getTiddlerSlice(t,pfx+'intervalUnit');
switch(intervalUnit) {
case 'MILLISECOND':
bp.intervalUnit = 0;
break;
case 'SECOND':
bp.intervalUnit = 1;
break;
case 'MINUTE':
bp.intervalUnit = 2;
break;
case 'HOUR':
bp.intervalUnit = 3;
break;
case 'DAY':
bp.intervalUnit = 4;
break;
case 'WEEK':
bp.intervalUnit = 5;
break;
case 'MONTH':
bp.intervalUnit = 6;
break;
case 'YEAR':
bp.intervalUnit = 7;
break;
case 'DECADE':
bp.intervalUnit = 8;
break;
case 'CENTURY':
bp.intervalUnit = 9;
break;
case 'MILLENNIUM':
bp.intervalUnit = 10;
break;
case 'EPOCH':
bp.intervalUnit = -1;
break;
case 'ERA':
bp.intervalUnit = -2;
break;
default:
bp.intervalUnit = 7;
break;
}
var intervalPixels = store.getTiddlerSlice(t,pfx+'intervalPixels');
bp.intervalPixels = eval(intervalPixels);
var date = store.getTiddlerSlice(t,pfx+'date');
bp.date = date ? Timeline.DateTime.parseGregorianDateTime(date) : defaultDate;
var showEventText = store.getTiddlerSlice(t,pfx+'showEventText');
bp.showEventText = showEventText ? eval(showEventText) : true;
var trackHeight = store.getTiddlerSlice(t,pfx+'trackHeight');
if(trackHeight)
bp.trackHeight = eval(trackHeight);
var trackGap = store.getTiddlerSlice(t,pfx+'trackGap');
if(trackGap)
bp.trackGap = eval(trackGap);
var ret = {};
var ev = {};
ev.type = store.getTiddlerSlice(t,pfx+'eventSourceType');
//#displayMessage("eventSourceType"+":"+ev.type);
ev.params = store.getTiddlerSlice(t,pfx+'eventSourceParams');
//#displayMessage("eventSourceParams"+":"+ev.params);
var etherPainter = store.getTiddlerSlice(t,pfx+'etherPainter');
if(etherPainter) {
var ep = {};
ep.etherPainter = etherPainter;
ep.startDate = store.getTiddlerSlice(t,pfx+'etherPainter.startDate');
ep.multiple = store.getTiddlerSlice(t,pfx+'etherPainter.multiple');
ret.ep = ep;
}
var decorator = store.getTiddlerSlice(t,'decorator');
if(decorator) {
var dec = {};
var pdc = pfx + 'decorator0.';
dec.decorator = decorator;
dec.startDate = store.getTiddlerSlice(t,pdc+'startDate');
dec.endDate = store.getTiddlerSlice(t,pdc+'endDate');
dec.startLabel = store.getTiddlerSlice(t,pdc+'startLabel');
dec.endLabel = store.getTiddlerSlice(t,pdc+'endLabel');
dec.color = store.getTiddlerSlice(t,pdc+'color');
dec.opacity = store.getTiddlerSlice(t,pdc+'opacity');
ret.dec = dec;
}
var highlight = store.getTiddlerSlice(t,pfx+'highlight');
ret.highlight = highlight ? eval(highlight) : false;
ret.ev = ev;
ret.bp = bp;
return ret;
};
} // end of 'install only once'
/*}}}*/
/***
|''Name''|SimpleSearchPlugin|
|''Description''|displays search results as a simple list of matching tiddlers|
|''Authors''|FND|
|''Version''|0.4.1|
|''Status''|stable|
|''Source''|http://devpad.tiddlyspot.com/#SimpleSearchPlugin|
|''CodeRepository''|http://svn.tiddlywiki.org/Trunk/contributors/FND/plugins/SimpleSearchPlugin.js|
|''License''|[[Creative Commons Attribution-ShareAlike 3.0 License|http://creativecommons.org/licenses/by-sa/3.0/]]|
|''Keywords''|search|
!Revision History
!!v0.2.0 (2008-08-18)
* initial release
!!v0.3.0 (2008-08-19)
* added Open All button (renders Classic Search option obsolete)
* sorting by relevance (title matches before content matches)
!!v0.4.0 (2008-08-26)
* added tag matching
!To Do
* tag matching optional
* animations for container creation and removal
* when clicking on search results, do not scroll to the respective tiddler (optional)
* use template for search results
!Code
***/
//{{{
if(!version.extensions.SimpleSearchPlugin) { //# ensure that the plugin is only installed once
version.extensions.SimpleSearchPlugin = { installed: true };
if(!config.extensions) { config.extensions = {}; }
config.extensions.SimpleSearchPlugin = {
heading: "Search Results",
containerId: "searchResults",
btnCloseLabel: "close",
btnCloseTooltip: "dismiss search results",
btnCloseId: "search_close",
btnOpenLabel: "Open all",
btnOpenTooltip: "open all search results",
btnOpenId: "search_open",
displayResults: function(matches, query) {
story.refreshAllTiddlers(true); // update highlighting within story tiddlers
var el = document.getElementById(this.containerId);
query = '"""' + query + '"""'; // prevent WikiLinks
if(el) {
removeChildren(el);
} else { //# fallback: use displayArea as parent
var container = document.getElementById("displayArea");
el = document.createElement("div");
el.id = this.containerId;
el = container.insertBefore(el, container.firstChild);
}
var msg = "!" + this.heading + "\n";
if(matches.length > 0) {
msg += "''" + config.macros.search.successMsg.format([matches.length.toString(), query]) + ":''\n";
this.results = [];
for(var i = 0 ; i < matches.length; i++) {
this.results.push(matches[i].title);
msg += "* [[" + matches[i].title + "]]\n";
}
} else {
msg += "''" + config.macros.search.failureMsg.format([query]) + "''"; // XXX: do not use bold here!?
}
createTiddlyButton(el, this.btnCloseLabel, this.btnCloseTooltip, config.extensions.SimpleSearchPlugin.closeResults, "button", this.btnCloseId);
wikify(msg, el);
if(matches.length > 0) { // XXX: redundant!?
createTiddlyButton(el, this.btnOpenLabel, this.btnOpenTooltip, config.extensions.SimpleSearchPlugin.openAll, "button", this.btnOpenId);
}
},
closeResults: function() {
var el = document.getElementById(config.extensions.SimpleSearchPlugin.containerId);
removeNode(el);
config.extensions.SimpleSearchPlugin.results = null;
highlightHack = null;
},
openAll: function(ev) {
story.displayTiddlers(null, config.extensions.SimpleSearchPlugin.results);
return false;
}
};
config.shadowTiddlers.StyleSheetSimpleSearch = "/*{{{*/\n" +
"#" + config.extensions.SimpleSearchPlugin.containerId + " {\n" +
"\toverflow: auto;\n" +
"\tpadding: 5px 1em 10px;\n" +
"\tbackground-color: [[ColorPalette::TertiaryPale]];\n" +
"}\n\n" +
"#" + config.extensions.SimpleSearchPlugin.containerId + " h1 {\n" +
"\tmargin-top: 0;\n" +
"\tborder: none;\n" +
"}\n\n" +
"#" + config.extensions.SimpleSearchPlugin.containerId + " ul {\n" +
"\tmargin: 0.5em;\n" +
"\tpadding-left: 1.5em;\n" +
"}\n\n" +
"#" + config.extensions.SimpleSearchPlugin.containerId + " .button {\n" +
"\tdisplay: block;\n" +
"\tborder-color: [[ColorPalette::TertiaryDark]];\n" +
"\tpadding: 5px;\n" +
"\tbackground-color: [[ColorPalette::TertiaryLight]];\n" +
"}\n\n" +
"#" + config.extensions.SimpleSearchPlugin.containerId + " .button:hover {\n" +
"\tborder-color: [[ColorPalette::SecondaryMid]];\n" +
"\tbackground-color: [[ColorPalette::SecondaryLight]];\n" +
"}\n\n" +
"#" + config.extensions.SimpleSearchPlugin.btnCloseId + " {\n" +
"\tfloat: right;\n" +
"\tmargin: -5px -1em 5px 5px;\n" +
"}\n\n" +
"#" + config.extensions.SimpleSearchPlugin.btnOpenId + " {\n" +
"\tfloat: left;\n" +
"\tmargin-top: 5px;\n" +
"}\n" +
"/*}}}*/";
store.addNotification("StyleSheetSimpleSearch", refreshStyles);
// override Story.search()
Story.prototype.search = function(text, useCaseSensitive, useRegExp) {
highlightHack = new RegExp(useRegExp ? text : text.escapeRegExp(), useCaseSensitive ? "mg" : "img");
var matches = store.search(highlightHack, null, "excludeSearch");
var q = useRegExp ? "/" : "'";
config.extensions.SimpleSearchPlugin.displayResults(matches, q + text + q);
};
// override TiddlyWiki.search() to sort by relevance
TiddlyWiki.prototype.search = function(searchRegExp, sortField, excludeTag, match) {
var candidates = this.reverseLookup("tags", excludeTag, !!match);
var primary = [];
var secondary = [];
var tertiary = [];
for(var t = 0; t < candidates.length; t++) {
if(candidates[t].title.search(searchRegExp) != -1) {
primary.push(candidates[t]);
} else if(candidates[t].tags.join(" ").search(searchRegExp) != -1) {
secondary.push(candidates[t]);
} else if(candidates[t].text.search(searchRegExp) != -1) {
tertiary.push(candidates[t]);
}
}
var results = primary.concat(secondary).concat(tertiary);
if(sortField) {
results.sort(function(a, b) {
return a[sortField] < b[sortField] ? -1 : (a[sortField] == b[sortField] ? 0 : +1);
});
}
return results;
};
} //# end of "install only once"
//}}}
Broader project encompassing ChatLoop.
3rd December - 1 hour meeting with Paul, Stu and Josh to discuss UI needs
[[4th December SiteLoop]]
Call with Ratheesh
asp.net controls
"enterprise console" used to link together different systems
going to have a demo
can provide with eval version of software for me to install
they integrate with Microsoft systems (connectors) and SAP.
Demo with Sam and Ratheesh.
you install the software on a Windows server.
forms designer, process designer
notifications over SMS, email, IM
scheduler
dB - MS-SQL2005/08, Oracle 10/11G
Accounts can come from - Active Directory, LDAP…
Can integrate with any WSDL web service
You can delegate your workflows to other people - "alternate actors"
Queues - you can have your people pick items queues e.g. for support; you can automate these by algorithm such as round robin, least-busy-employee, IT-manager-gets-it
Workflows - you can hook into any Skelta event or SharePoint/BizTalk event (and some others)
- Templates for email notifications can be pulled from Asp.net forms, InfoPath forms, et al.
- you can write custom C# expressions (assemblies, DLL's) for calculating things like task timeout before escalation
- workflows made using a Visio-like editor
forms: made using a drag 'n' drop editor; you can link them to the start of workflows (or in the middle)
couple of days training make it easy to design workflows
a workflow's progress is recorded and you can look at it while it is in progress or after it has finished - this is pretty cool…
all Skelta's visual interface files are aspx files you can edit
SnapLogic call, 6th August 2010, 18:30 - 19:30
Call with Oliver (he wanted to make roommateadmin.com - a rentshare!)
They have "pipelines" in their vocabulary
The Salesforce "snap" (connection) is two-way
Snaps have a RESTful API so you can work directly with the other system the snap talks to
You can create pipelines over the REST API.
You can create pipelines that receive data parameters and then do stuff with them (including triggering other pipelines)
Canonical use SnapLogic to run their webstore - you get a download URI after buying something; they also use the SnapLogic REST API to query Salesforce
Any connectors you write could be resold in the store
Snaps are written in Python and Java
SnapLogic is definitely for creating the integrations, not the graphical display
As an example, the web app would pass the logged-in person's details along to SnapLogic's pipelines, which would do stuff with them
Xpeerient use SnapLogic… they use scheduled pipelines to look in their database for new invoices, and then put that in QuickBooks.
Snaps give you RESTful APIs to systems that don't have them
/***
|''Name''|SparklinePlugin|
|''Description''|provides support for [[sparklines|http://www.edwardtufte.com/bboard/q-and-a-fetch-msg?msg_id=0001OR&topic_id=1]]|
|''Version''|1.0.0|
|''Status''|stable|
|''Source''|http://www.tiddlywiki.com/coreplugins.html#SparklinePlugin|
|''~CodeRepository:''|http://svn.tiddlywiki.org/Trunk/association/plugins/SparklinePlugin/SparklinePlugin.js |
|''License''|[[BSD open source license]]|
|''~CoreVersion''|2.3.0|
|''Feedback''|[[TiddlyWiki community|http://groups.google.com/group/TiddlyWiki]] |
|''Keywords''|visualization|
!Usage
{{{
<<sparkline numbers>>
}}}
The macro accepts space-separated numeric values as parameter.
!!Examples
Activity on http://www.tiddlywiki.com during the month of April 2005:
{{{<<sparkline 163 218 231 236 232 266 176 249 289 1041 1835 2285 3098 2101 1755 3283 3353 3335 2898 2224 1404 1354 1825 1839 2142 1942 1784 1145 979 1328 1611>>}}}
<<sparkline 163 218 231 236 232 266 176 249 289 1041 1835 2285 3098 2101 1755 3283 3353 3335 2898 2224 1404 1354 1825 1839 2142 1942 1784 1145 979 1328 1611>>
!Code
***/
//{{{
if(!version.extensions.SparklinePlugin) {
version.extensions.SparklinePlugin = {installed:true};
//--
//-- Sparklines
//--
config.macros.sparkline = {};
config.macros.sparkline.handler = function(place,macroName,params)
{
var data = [];
var min = 0;
var max = 0;
var v;
for(var t=0; t<params.length; t++) {
v = parseInt(params[t]);
if(v < min)
min = v;
if(v > max)
max = v;
data.push(v);
}
if(data.length < 1)
return;
var box = createTiddlyElement(place,"span",null,"sparkline",String.fromCharCode(160));
box.title = data.join(",");
var w = box.offsetWidth;
var h = box.offsetHeight;
box.style.paddingRight = (data.length * 2 - w) + "px";
box.style.position = "relative";
for(var d=0; d<data.length; d++) {
var tick = document.createElement("img");
tick.border = 0;
tick.className = "sparktick";
tick.style.position = "absolute";
tick.src = "data:image/gif,GIF89a%01%00%01%00%91%FF%00%FF%FF%FF%00%00%00%C0%C0%C0%00%00%00!%F9%04%01%00%00%02%00%2C%00%00%00%00%01%00%01%00%40%02%02T%01%00%3B";
tick.style.left = d*2 + "px";
tick.style.width = "2px";
v = Math.floor(((data[d] - min)/(max-min)) * h);
tick.style.top = (h-v) + "px";
tick.style.height = v + "px";
box.appendChild(tick);
}
};
}
//}}}
StackOverflow.com - Q&A site for programmers. Uses peer-reviewed reputation.
No barrier to use:
> Reputation is completely optional; normal use of Stack Overflow — asking and answering questions — does not require any reputation whatsoever.
Open:
> At the high end of this reputation spectrum there is little difference between users with high reputation and moderators
Paying for help:
> If, after two days, you still don't have an answer you like, you can offer a bounty. Slice off a bit of your own hard-earned reputation -- anywhere from 50 to 500 -- and attach it to the question as a bounty. We'll even throw in 50 reputation to sweeten the deal. The bountied question will appear with a special icon in all question lists, and it will also be visible on the home page Featured tab.
Responsibilities and power accrue with reputation:
http://stackoverflow.com/questions/130654/how-does-reputation-work-on-stackoverflow
As a user gains reputation, he or she will gain the following abilities and responsibilities:
+15 to upvote
+15 to mark offensive
+15 to post images
+50 to add comments to a post
+50 to delete your own comments
+100 to downvote
+250 to open/close your own questions
+500 to retag others' questions
+750 to edit community 'wiki editable' posts
+750 to delete comments on a question if you are the question owner (source)
+2000 to edit others' questions and answers
+2000 to view offensive counts
+2000 your website url on your user page is not nofollowed (source)
+3000 to vote for opening/closing any question
+5000 to delete comments from a post you own (source)
+10000 to delete closed questions and access moderation tools (source)
These need to preserve the file structure of what you upload and give you a clean URL. http://www.example.com/?file=_594jghjc is BAD; http://www.example.com/me/file.html is GOOD.
| Name | Input | Output | Free? | Notes |
| [[Google Pages]] | POST | GET | yes, but closed to new signups | Not technically a storage service, but provides one directory where you can drop files and access them through a clean URL |
| [[Dropbox]] | ? | ? | ? | ? |
| Putplace | ? | ? | No; €40 for 20Gb for 12 months | #jdrumgoole @jayfresh try our product http://putplace.com I promise you a clean URL. Use the promotion code "joe" for a 90 day free trial; desktop client |
I have some lurking in Twitter to look at:
#jungledisk - desktop client, don't know if it gives you a clean URL yet
Nick: I use Dropbox and I love it. I've also been trying Mozy (?) out. Seems to back things up nice and automatically but I haven't tried restoring any files from it yet.
See [[Streams interface for LShift]]
Time: 5 days work between Monday 22nd June - Friday 3rd July.
Rate: £492 / 8-hour day or £61.50 / hr
Hours: 39hours 5mins
Brief (attempting to remember): need for an interface prototype for Streams (née FeedsHub) to give the BBC something to react to. There are three three-week timeboxes left in the project; this 5 days is to the end of the first timebox. Further budget being sought at Beeb to expand development of project and a vision for the interface is a useful thing at this stage.
Invoice: [[Invoice 0003]]
Day logs:
Week 1:
[[23rd June 2009 - Streams]] - 7 hours
[[24th June 2009 - Streams]] - 6hrs 45mins
Week 2:
[[30th June 2009 - Streams]] - 8hrs 25mins
[[1st July 2009 - Streams]] - 6hrs 55mins
[[3rd July 2009 - Streams]] - 5hrs 35mins
[[5th July 2009 - Streams]] - 3hrs 25mins
[[6th July 2009 - Streams]] - 1hr
Things the management API needs to be able to support:
#Creation of streams
* PUT to /feeds_hub/stream_name
** Should return 201 if stream is created
** Should return 409 if stream_name is already there and revision number PUT does not match
#Modification of stream status
* PUT to /feeds_hub/stream_name_status
** This should trigger the orchestrator to alter the status of the stream
** Shoud return 201 as above
** Should return 409 as above
#List available feeds
* GET to /feeds_hub/listStreams
** This should return a list of documents with type 'feed'
#List available modules
* GET to /feeds_hub/listModules
** This should return a list of plugins and terminals
This is mainly the good points and the descriptions
#Bravo - critical care system
** form editor for data entry about patients
** Android app design for form entry used by doctors in the field - they're proud of this
** they talked to doctors about what they needed
#Charlie - robotic chess
** priority: familiar chess experience
** it works
# Delta - tweetguv - http://tweetguv.co.uk
** analyse politicians tweets and see if they tow party line, or are aligned with the opposition
** produce graphics e.g. triangle of allegiance
** good range of features to filter information
** good graphics
# Foxtrot - pedestrian autopilot
** uses inertial motion sensors to work out where you move and help you navigate
** the accelerometers they used with the XMOS (which uses a bluetooth modem to speak to the Android phone) were not sensitive enough
** it "sort of works" - the bits are connected, but the sensitivity is not good - "after just a few minutes, the system might think it was on the moon"
# Golf - real guitar hero
** decided to use a real guitar to teach you how to play guitar (as well as do cool digital effects)
** used a mBed (like the Arduino, but uses C++)
# Hotel - hyper-resolution camera (is online as "SnapStitcher")
** stitches together several photos and provides zoomable interface to look at the image
** uses "scale invariant feature transform" - supposedly very useful for image matching
** anyone can use website
# India - iProfessor
** filter email into similarity categories automatically - similarity NOT topic
** uses an algorithm based on creating a network where where the nodes are emails and the edges are similarity scores
*** get the minimum spanning edge (or something) and weight length of edge by score (smaller the more similar) - categories emerge
# Juliet - Teach your cat to twitter (http://www.srcf.cam.ac.uk/juliet - I think, http://twitter.com/zedcat, zedcat on Facebook)
** image analysis and motion detection causing tweets to emerge
** managed through an iPhone app - you can define hotspots and see activity summary
# Kilo - Spot the Medicine - instant feedback from chemical structures
** analyses chemical structure shapes and sends you information about them
** Android app, uses camera
** OCR done on a server, uses OSRA (Optical Structure Recognition Application)
# Mike - Integrating phones with the web
** Facebook app for Metaswitch Networks VoIP product
/*{{{*/
.headerShadow {
padding:0px;
color:#eee;
}
#mainMenu {
font-size: 1.1em;
border-right: 1px solid;
}
#displayArea {
}
h1 {
font-size: 2.35em;
}
/*}}}*/
/***
From:
// http://simile.mit.edu/repository/timeline/trunk/src/webapp/api/bundle.css
***/
/*{{{*/
.timeline-ether-marker-bottom {
width: 5em;
height: 1.5em;
border-left: 1px solid #aaa;
padding-left: 2px;
color: #aaa;
}
.timeline-ether-marker-bottom-emphasized {
width: 5em;
height: 2em;
border-left: 1px solid #aaa;
padding-left: 2px;
color: black;
}
.timeline-ether-marker-top {
width: 5em;
height: 1.5em;
border-left: 1px solid #aaa;
padding-left: 2px;
color: #aaa;
}
.timeline-ether-marker-top-emphasized {
width: 5em;
height: 2em;
border-left: 1px solid #aaa;
padding-left: 2px;
color: black;
}
.timeline-ether-marker-right {
width: 5em;
height: 1.5em;
border-top: 1px solid #aaa;
padding-top: 2px;
color: #aaa;
}
.timeline-ether-marker-right-emphasized {
width: 7em;
height: 1.5em;
border-top: 1px solid #aaa;
padding-top: 2px;
color: black;
}
.timeline-ether-marker-left {
width: 5em;
height: 1.5em;
border-top: 1px solid #aaa;
padding-top: 2px;
color: #aaa;
}
.timeline-ether-marker-left-emphasized {
width: 7em;
height: 1.5em;
border-top: 1px solid #aaa;
padding-top: 2px;
color: black;
}
.timeline-duration-event {
position: absolute;
overflow: hidden;
border: 1px solid blue;
}
.timeline-instant-event2 {
position: absolute;
overflow: hidden;
border-left: 1px solid blue;
padding-left: 2px;
}
.timeline-instant-event {
position: absolute;
overflow: hidden;
}
.timeline-event-bubble-title {
font-weight: bold;
border-bottom: 1px solid #888;
margin-bottom: 0.5em;
}
.timeline-event-bubble-body {
}
.timeline-event-bubble-wiki {
margin: 0.5em;
text-align: right;
color: #A0A040;
}
.timeline-event-bubble-wiki a {
color: #A0A040;
}
.timeline-event-bubble-time {
color: #aaa;
}
.timeline-event-bubble-image {
float: right;
padding-left: 5px;
padding-bottom: 5px;
}.timeline-container {
position: relative;
overflow: hidden;
}
.timeline-copyright {
position: absolute;
bottom: 0px;
left: 0px;
z-index: 1000;
cursor: pointer;
}
.timeline-message-container {
position: absolute;
top: 30%;
left: 35%;
right: 35%;
z-index: 1000;
display: none;
}
.timeline-message {
font-size: 120%;
font-weight: bold;
text-align: center;
}
.timeline-message img {
vertical-align: middle;
}
.timeline-band {
position: absolute;
background: #eee;
z-index: 10;
}
.timeline-band-inner {
position: relative;
width: 100%;
height: 100%;
}
.timeline-band-input {
position: absolute;
width: 1em;
height: 1em;
overflow: hidden;
z-index: 0;
}
.timeline-band-input input{
width: 0;
}
.timeline-band-layer {
position: absolute;
width: 100%;
height: 100%;
}
.timeline-band-layer-inner {
position: relative;
width: 100%;
height: 100%;
}
/*}}}*/
See [[SweetSoft research]]
See http://hoster.peermore.com/recipes/sweetsoft/tiddlers.wiki
SweetSpot is Nick Webb's student property company.
See [[SweetSoft]].
Just a note - Prem from DD.com called with some info about what they can do for us.
direct submitter - £5k, with 20% annual
bureau solution (hosted and managed) - £795 setup, £0.35 per transaction + £3.50 "file charge", which is for a month's batch
Prem calling back in mid-Sept if he hasn't heard from me.
/***
|Name|TaggedTemplateTweak|
|Source|http://www.TiddlyTools.com/#TaggedTemplateTweak|
|Documentation|http://www.TiddlyTools.com/#TaggedTemplateTweakInfo|
|Version|1.6.1|
|Author|Eric Shulman|
|License|http://www.TiddlyTools.com/#LegalStatements|
|~CoreVersion|2.1|
|Type|plugin|
|Description|use alternative ViewTemplate/EditTemplate for specific tiddlers|
This plugin extends the core function, story.chooseTemplateForTiddler(), so that any given tiddler can be viewed and/or edited using alternatives to the standard tiddler templates.
!!!!!Documentation
>see [[TaggedTemplateTweakInfo]]
!!!!!Revisions
<<<
2009.09.02 [1.6.1] apply field-based template (if any) *before* tag-based template
| please see [[TaggedTemplateTweakInfo]] for previous revision details |
2007.06.11 [1.0.0] initial release
<<<
!!!!!Code
***/
//{{{
version.extensions.TaggedTemplateTweak= {major: 1, minor: 6, revision: 1, date: new Date(2009,9,2)};
if (!config.options.txtTemplateTweakFieldname)
config.options.txtTemplateTweakFieldname='template';
Story.prototype.taggedTemplate_chooseTemplateForTiddler = Story.prototype.chooseTemplateForTiddler
Story.prototype.chooseTemplateForTiddler = function(title,template)
{
// get core template and split into theme and template name
var coreTemplate=this.taggedTemplate_chooseTemplateForTiddler.apply(this,arguments);
var theme=""; var template=coreTemplate;
var parts=template.split(config.textPrimitives.sectionSeparator);
if (parts[1]) { theme=parts[0]; template=parts[1]; }
else theme=config.options.txtTheme||""; // if theme is not specified
theme+=config.textPrimitives.sectionSeparator;
// look for template using title as prefix
if (!store.getTaggedTiddlers(title).length) { // if tiddler is not a tag
if (store.getTiddlerText(theme+title+template))
{ return theme+title+template; } // theme##TitleTemplate
if (store.getTiddlerText(title+template))
{ return title+template; } // TitleTemplate
}
// look for templates using custom field value as prefix
var v=store.getValue(title,config.options.txtTemplateTweakFieldname);
if (store.getTiddlerText(theme+v+template))
{ return theme+v+template; } // theme##valueTemplate
if (store.getTiddlerText(v+template))
{ return v+template; } // valueTemplate
// look for template using tags as prefix
var tiddler=store.getTiddler(title);
if (!tiddler) return coreTemplate; // tiddler doesn't exist... use core result
for (i=0; i<tiddler.tags.length; i++) {
var t=tiddler.tags[i]+template; // add tag prefix to template
var c=t.substr(0,1).toUpperCase()+t.substr(1); // capitalized for WikiWord title
if (store.getTiddlerText(theme+t)) { return theme+t; } // theme##tagTemplate
if (store.getTiddlerText(theme+c)) { return theme+c; } // theme##TagTemplate
if (store.getTiddlerText(t)) { return t; } // tagTemplate
if (store.getTiddlerText(c)) { return c; } // TagTemplate
}
// no match... use core result
return coreTemplate;
}
//}}}
Currently running as my first [[Joyent Smart Platform]] app on http://f7e3dbf.smart.joyent.com/
Update:
See [[Tarpipe / Yahoo! Pipes proxy]]
Update:
It's working!
http://skitch.com/jayfresh/buymc/google-mail-inbox-1211-jnthnlstr-googlemail.com
----------
[[Tarpipe]] now have a [[REST module|http://www.pipetree.com/qmacro/blog/2009/05/tarpipe-rest-connector-in-5-minutes/]] where you can make POSTs to any URL. The module behaves in a similar way to Yahoo! Pipes' 'web service' module, presumably so it can take advantage of any services a Yahoo! Pipe is using.
The obvious thing for me to do here is to use the REST module to POST to a Yahoo! Pipe, process data and accept it back into the workflow. The obstruction I'm finding is that the data format is not helpfully set up for Yahoo! Pipes to consume it, not is Pipes' JSON output format digestible by Tarpipe' REST module. I'm having a Get Satisfaction [[discussion with Bruno|http://getsatisfaction.com/tarpipe/topics/sending_to_yahoo_pipes]] from Tarpipe about this.
Best thing to do at this point, I think, is to make a proxy that accepts POSTs from Tarpipe, converts them to a form-encoded string, POSTs to Yahoo! Pipes, accepts the response, extracts the items object and sends that back to Tarpipe. I'm thinking of doing this with [[JGate]].
I'm having some problems debugging the flow of email->tarpipe->JGate Yahoo! Pipes proxy->Yahoo! Pipes->JGate->tarpipe->email - it's particularly tricky to see whether the round-trip from JGate to Yahoo! Pipes is working... I don't get any notice of the JGate app failing at this point.
I'd like to say I always write tests before I write any JavaScript code. Those times when I do write tests, I use the qunit framework, which is used by jQuery, so pretty well-tested itself.
I've recently been putting together an Adobe AIR application using HTML, CSS and JavaScript (aka Web Standards), which is very empowering and everything, but turned out to be a little tricky to write tests for. All the interesting stuff I wanted to do through AIR's {{{window.runtime}}} object, such as opening native windows and messing with dock icons, is missing from the browser if you're trying to test the JavaScript components in isolation. The ideal would be able to run your qunit tests within the AIR runtime itself.
Fortunately, it's straightforward to create a test app that wraps the qunit test runner in the AIR runtime and exercises your code. Here's how I got going:
!!Setting up the file structure
The folder structure I am using for the AIR app is like this:
{{{
/myApp
myApp.html
myApp-app.xml
/js
myApp.js
/css
styles.css
}}}
The {{{myApp-app.xml}}} file is the "application descriptor" that AIR uses to create the {{{.air}}} package when you build the app.
I added two folders at the same level as {{{/myApp}}}, to contain the test framework and test code; the complete structure looks like:
{{{
/myApp
myApp.html
myApp-app.xml
/js
myApp.js
AIRAliases.js
jquery-1.3.2.min.js
/css
styles.css
/myAppTests
runner.html
tests.js
myAppTests-app.xml
/qunit
testrunner.js
testsuite.css
}}}
!!Configuring the test app
The application descriptor for myAppTests is not very different from the descriptor for myApp; here they are:
{{{
myApp-app.xml:
<?xml version="1.0" encoding="UTF-8"?>
<application xmlns="http://ns.adobe.com/air/application/1.5">
<id>examples.html.myApp</id>
<version>0.1</version>
<filename>myApp</filename>
<initialWindow>
<content>myApp.html</content>
<visible>true</visible>
<width>400</width>
<height>800</height>
</initialWindow>
</application>
myAppTests-app.xml:
<?xml version="1.0" encoding="UTF-8"?>
<application xmlns="http://ns.adobe.com/air/application/1.5">
<id>examples.html.myAppTests</id>
<version>0.1</version>
<filename>myAppTests</filename>
<initialWindow>
<content>myAppTests/runner.html</content>
<visible>true</visible>
<width>400</width>
<height>800</height>
</initialWindow>
</application>
}}}
The main structural difference is that {{{myApp-app.xml}}} has an {{{initialWindow.content}}} property set to {{{myApp.html}}}, whereas {{{myAppTests-app.xml}}} refers to the test runner from a directory above: {{{myAppTests/runner.html}}}. This is needed because the test app needs to have its working directory set to the top-level of the file structure, otherwise {{{runner.html}}} won't be able to get at all the files it needs in other directories (more below).
!!Putting the correct files in runner.html
Here's a {{{runner.html}}} that works with the folder structure described above:
{{{
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Test Suite</title>
<link rel="stylesheet" type="text/css" href="../qunit/testsuite.css">
<script src="../myApp/js/AIRAliases.js" type="text/javascript"></script>
<script src="../myApp/js/jquery-1.3.2.min.js" type="text/javascript"></script>
<script src="../qunit/testrunner.js" type="text/javascript"></script>
<script src="../myApp/js/myApp.js"></script>
<script src="tests.js" type="text/javascript"></script>
</head>
<body>
<h2 id="banner"></h2>
<h2 id="userAgent"></h2>
<ol id="tests"></ol>
<div id="main"></div>
</body>
</html>
}}}
!!Running the test suite in debug mode
The easiest way to run the test suite is to use the built-in Adobe Debugger, which lets you test your app without having to build it into an AIR package:
{{{
adl myAppTests/myAppTests-app.xml .
}}}
Don't forget the '.' character on the end - this tells the debugger to run with the top-level directory as the working directory - without it, the default is the same directory as XML file.
Blogged at http://jaybyjayfresh.com/2010/01/15/testing-local-tiddlyweb-applications-in-virtual-machines/
-----
This is particular to VirtualBox, but I imagine the principles are the same for VMWare, Parallels and all those other virtual doohikies.
I've been developing a TiddlyWeb application on a local server on my Mac. This is fine for when I want to test in Firefox, Safari, Chrome, Webkit and others, but not so hot when I want to test in IE6/7/8. Virtualisation makes the general problem a lot easier, and I run VirtualBox for that purpose.
Unfortunately, accessing a locally running web server, rather than plain ol' Internet, gave me some problems, so I thought I'd write down how I managed it.
Generally, to start up a TiddlyWeb instance, I've been using:
{{{
twanager server localhost 3000
}}}
I can point a browser to {{{http://localhost:3000/}}} and I'm away. If I want to run on port 80, I can use:
{{{
sudo twanager server localhost 80}}}
then I can point a browser to {{{http://localhost/}}}
The key thing to getting my virtual machines talking to my local server is to run TiddlyWeb on an IP address, rather than on "localhost", which I had been doing. The IP address to run the application on is the IP address of your machine on the network. So how do you get this?
Given I'm using a mac, the non-confusing way is to open up System Preferences, hit the "Network" icon and read off the IP address in the "Status" section.
[img[http://img.skitch.com/20100115-rp22r1fa83u6rkprnwgtxip6if.jpg]]
The more confusing way is to open up a Terminal, type in {{{ifconfig}}} and read off the IP address:
[img[http://img.skitch.com/20100115-njs7kemddshgj92fa8cdq4qyws.jpg]]
With your IP address in hand, you can now use {{{twanager}}} to start up TiddlyWeb on an address accessible to your virtual machines:
{{{
twanager server 192.168.1.71 3000
}}}
Then you'll be in IE testing hell in no time!
The main problem with writing server-side tests is that most testing frameworks for JavaScript assume the presence of a browser.
[[jsUnity|http://jsunity.com/]] doesn't.
[[mjsunit|http://github.com/tmpvar/mjsunit.runner]] for [[Node.js]] doesn't.
A bookmarklet; a Firefox plugin; a Google Mail plugin even???
See [[here|Google Tasks integration into TeamTasks]] for write-up of Tasks API
A book. By Eliyahu M. Goldratt.
Great coffee and cheap. Not sure about power availability.
Down by Earl's Court. French café, good coffee, wifi and power.
Great book about typography by Ellen Lupton.
Hi! This is not a portfolio website, it's a living document where I write down everything that ought to be written down - plans, notes of experiments, logs of what I do.
I try to make it readable, but it won't always make sense... ;) Better look [[on my blog|http://jaybyjayfresh.com]] for more considered stuff.
See http://github.com/jayfresh/TiddlyAIR
! The problem
Out and about, want to:
# Remember cool places
# Know about places my friend's like
! The scenario
!! Saving places
You're out at a bar. You really like it. You want to remember it. You take out your phone and send a SMS that goes something like this: "The decor here is totally retro and the mojitos are amazing. L:La Revolution, Wardour St"
!! Finding places
You're out on Wardour Street in Soho. You want to know if there is anywhere round here your friends like. You take out your iPhone and open up TiddlyCity. The app locates you and shows you on a map the places around you that have been reviewed by your friends. If you touch a marker, their review shows up. You can even see a view of the street to help you find the place.
! The approach
I've had ideas about how to do this. I'm going for a decentralised app, where you have one person who acts as a host for the project and controls updates to the map from an app running on their computer.
!! Saving places and getting them onto a map
When you send a SMS, you send it to Twitter, which puts the text of your SMS on to the web. The "L:" syntax is becoming more frequently used as a syntax to say "location" and services have sprung up that take advantage of this information e.g. [[TwitterVision|http://twittervision.com/]].
Having the information stored in Twitter means that an app running on your hard-drive can search through a feed of a set of your friends' tweets for tweets with location information. Nick Webb created [[FlockOTweets]] to help you create a RSS feed of a group of your friends' tweets. TiddlyCity could reuse the Yahoo! Pipe behind this to create a feed that the app uses to find relevant tweets. (You could run this app as a hosted thing, although running locally means you don't need a proxy nor an account-management system.)
Once you've found relevant tweets, if there is no web service available to automatically geocode these tweets, you can use Google local business search to convert e.g. "Wetherspoons, Liverpool Street" into longitude and latitude (this is a general reverse geocoding problem, which Google and Yahoo! have similar tools for dealing with. In my experience, I've only been able to make it work for pubs using Google's local business search as reverse geocoders seem to want street addresses).
With the location information, you're ready to put the information onto a map. Google Maps has the ability to create "My Maps", which can be collaborative. On to these maps, you can overlay KML (and other geo-format such as GeoRSS) files. I'd like to see if you can POST amendments to the data set, so that you could use the online map as the data storage for your tweets.
In addition to the standard view of My Maps, Google Maps provides mini-applications called "mapplets". These allow you go quite far in customising the look and behaviour, since they run JavaScript, as well as CSS and HTML. I've seen from [[this post|http://washingtonbike.wetpaint.com/thread/1557118/Arlington+Bikeway+Mapplet?t=anon]] that you can use My Maps as the data source for a mapplet, although the example in the post doesn't work, which is annoying as I can't see how it's done.
!!! Gaps in knowledge
# Finding out whether you can POST amendments to an online Google My Map.
# Finding out how you can use My Maps data in a mapplet
!! Finding places
I'd like to target an iPhone application, although if we have a mapplet, you can quite happily view the thing through a web browser. I'm imagining that an iPhone app is going to be able to find out your current location and use that as input to search on the map, then display the data that is relevant to that area.
!!! Gaps in knowledge
# Would the Google map try to overlay all the data or just figure out what is appropriate to show for the current zoom level. This might be important for performance if the data set gets large.
! Other ideas
# Being able to add places from the iPhone app
# A SMS service to send you the nearby recommended places
# A rating system for places
# A commenting system for places
-----
! Old notes
Using TiddlyCity - a little box to add a new place to your map... also supports adding from text via Twitter. Want a sharing story for places added via the little box, seeing as you don't tweet them... maybe the little box also goes via Twitter! It would be uber to have an iphone web app - so you'd visit a site with a little box you fill in... means you'd need to identify yourself with your twitter creds.
Idea: create invoices directly from tiddlers. "Solid" documents, like pdf's, not just html pages, which can be modified easily.
!Questions
#Does it need to use a program to create a pdf?
Mac has a "Save to PDF..." option and @psd mentioned that windows has little programs to do it. It would be great if you didn't need to install something or run another program to get your invoice out.
!Abstract
I want to text myself notes that appear as tiddlers in my TiddlyWiki. I want the notes to be private. I also want to make this without writing any code.
I'd like the tweet to be of the form:
{{{
d twt TiddlerName some tiddler text...
}}}
!Initial approach and problems
Original idea was to dm myself so that the TiddlyWiki could use my credentials to get into my Twitter account and retrieve messages. Unfortunately, you can't follow yourself, so you can't dm yourself.
This presents the problem of how to make this a distributable app without distributing the credentials to some Twitter account somewhere that receives the dm's.
The other problem that came up is how to respond to a new dm, which is necessary for getting the dm's out of Twitter, which I decided is necessary if I want to somehow get them to my TiddlyWiki at the end of the day.
!Exploration
!!Data momentum
I came up with the concept of "''data momentum''", which describes the continuing progress of data through a system without any external impetus. The initial impetus is the sending of a SMS to Twitter, and you want to be able to get the data through the intervening systems without providing any more impetus. You can say that a service preserves the data momentum, or that it absorbs it.
This led me on to finding out about web hooks (http://webhooks.org). A service uses a web hook if it makes a call-out (usually a POST) to another service when something happens inside it. An example is github, where you can set up what it calls "post-commit hooks", which github will POST to when you make a commit, with the details of that commit. Web hooks are an ideal way to maintain data momentum, as they use the HTTP protocol most services expect to start them off.
Some other ways to maintain data momentum are in the table below - the column labelled "push" is where the momentum is maintained inside the system; the column labelled "pull" shows where an additional impetus is injected from outside the system.
| ''push'' | ''pull'' |
| XMPP | "hand-cranking!" |
| SMS | cron |
| GET/POST (web hook) | META refresh |
| Special network routing (e.g. for a SMS) | XMPP reader |
| SMTP | |
!!Anonymous append
The second concept I thought of that would be useful for this application is "anonymous append", which is best demonstrated by a comments system. If you imagine a big bag of comments somewhere, you want visitors to your web site to be able to add another comment into this bag, but not necessarily to be able to read, write or delete from it.
Given that I want notes to be private, I imagined that if my system dumped tweets into a bag that only allowed anonymous append, then from there I could work out later how to get your own out.
!Getting the Tweets out of Twitter and stored somewhere else
I didn't want to have to inject any momentum into the system once I'd sent the SMS off to Twitter, so I needed services that are able to maintain data momentum. The following flow was what I came up with first:
| Step | momentum to get to next step |
| Thought -> | me, using fingers |
| SMS -> | Vodafone, using their network |
| Twitter SMS Gateway -> | Twitter, using software |
| Twitter datastore -> | ??? |
| Somewhere! | n/a |
So, I needed some way of getting tweets out of Twitter's datastore. A post on the Twitter dev blog (http://dev.twitter.com/2008/10/we-got-data.html) talks about several ways to get data out of Twitter. At some point, I happened across Twitter Do (http://twitter.com/tdo - "A web hooks powered programmable Twitter bot"), an app hosted on AppJet, which uses the fact that Twitter will send you an email when a direct message is sent. Twitter Do used http://mailhook.org, which would POST to a given URL when an email was sent, although that is now out of business. However, smtp2web (http://www.smtp2web.com/) does the same thing, so I'm planning to use that.
A problem with smtp2web is that I needed to create a file under my appjet.com domain to show that it was really mine (in this case smtp2web_ca1416e963053392.html), which is clearly a problem for me, as I don't own appjet! I've emailed the owner of smtp2web to see if he'll make an exception...
The data flow from the Twitter datastore has now become:
| Twitter datastore -> | SMTP |
| smtp2web inbox -> | HTTP POST |
| Somewhere! | n/a |
So, I'm still looking for somewhere put the emailed tweet...
!dm2me
I decided to split this problem up into two pieces, to tackle it more easily:
[[dm2me]] - a service to let you dm yourself on Twitter
[[TextTasks]] (or maybe TweetTasks?) - a service that pulls dm's from Twitter and puts them into Google Tasks
|''Type:''|file|
|''URL:''|http://jnthnlstr.tiddlyspot.com/|
|''Workspace:''|(default)|
This tiddler was automatically created to record the details of this server
!The problem
#You can't get an archive of all your tweets from Twitter. [[Some people|http://coconutheadsets.com/2009/02/28/insuring-your-lifestream/]] care about this. [[This guy|http://johannburkard.de/blog/programming/java/backup-twitter-tweets-with-twitterbackup.html]] made a java app to solve that problem; we thought we could do better.
#You can't search someone's old tweets directly and search.twitter.com only goes back a few weeks
!The approach
An online TiddlyWiki that gets all of someone's tweets and turns them into a suitable output format (csv). You can search them too.
The ToggleThemePlugin makes a button that swaps you between the web app and a normal TiddlyWiki, where you can more easily browse the tweets (now as tiddlers) and the plugins.
The app is at:
http://osmosoft.com/tiddlytweets
Ideas and feedback: http://tiddlytweets.uservoice.com
!Progress
We have a logo, thanks to @nickwebb. It's looking better too, thanks to @psd. Better handling of errors and presentation of search.
----
Having taken some feedback, looking now at focussing on people being able to search an entire archive of someone's tweets. Want to separate the web app bit from the Tiddly bit.
Initial TiddlyWiki group thread: http://groups.google.com/group/TiddlyWiki/browse_thread/thread/d929ac1ab7084de0
See TiddlyWeb group announcement:
Doing this with Fred. Currently stored in a gist (which I've forked) at http://gist.github.com/149056
Using the WireIt library for the interface.
The problem: creating a visual interface for configuring bag permissions and recipes for TiddlyWeb.
!Log
[[TiddlyWebConfigurator 17th July]]
[[TiddlyWebConfigurator 23rd July]]
!!Where'd we get?
We made something that allows you to wire bags to recipes, hit a button and have those recipes created in TiddlyWeb. I think we're quite happy with that!
!! What're we going to do next?
#Reading from TiddlyWeb to populate bag/recipe list
#Adding roles, so you can configure access to bags
#Eventually, will need to handle permissions on recipes e.g. with more inputs on the recipes
!!What new stuff have I done with WireIt?
#Used the basic library, without using the WiringEditor
#Used the InOut containers (new!)
#Added a function to add an extra input/output to the InOut container when it gets full up
#Found a new way to store custom properties on containers - directly modified the container object (but I can do this because I am working at the level where I am adding containers and getting the working directly, rather than the level of abstraction higher at which the WiringEditor works)
#Found and reported [[a bug|http://neyric.lighthouseapp.com/projects/25048/tickets/22-adding-a-doctype-causes-problems-with-wireitlayer]] where adding a DOCTYPE causes the Layer to mess up
#Found and reported [[a bug|http://neyric.lighthouseapp.com/projects/25048-wireit/tickets/23-wrapping-a-layer-with-a-div-with-marginauto-messes-up-the-display-of-wires-when-youre-dragging-them]] where having setting margin:auto on a wrapper div around a layer messes up the display for the wires when you're dragging and dropping
!Afternoon session (from 15:15)
#Sorted loading up modules from TiddlyWeb
#Trying to add connections on load
** To add the wires, I had to figure out which elements in the {{{layer.containers}}} array were the appropriate ones for the source and target of each wire. I wrote a function to lookup a module in the containers array based on a object pattern you pass to it. This entailed writing a function to compare the properties of two arbitrary objects.
# What we have now is a fledgling TiddlyWeb adaptor as well as an example of integrating the WireIt framework with this adaptor
This is a TiddlyWiki.
http://tiddlywiki.com
I've been contributing to this project since May 2007. TiddlyWiki is a web application contained within a single HTML page that shows off the power of JavaScript to create data-driven applications.
The novel properties of a HTML file running on a local file:/// URI has two fascinating consequences:
* TiddlyWiki behaves like a document, in that it can save itself
* it is a good environment for developing mashups due to the ability to GET data easily from across the web and the treatment of all data as "tiddlers" - a simple, hand-editable data-model.
<html><iframe src="http://spreadsheets.google.com/embeddedform?key=tylrE6xoaVitUqheKU9v-eA" width="500" height="255" frameborder="0" marginheight="0" marginwidth="0">Loading...</iframe></html>
Adds a button to switch between two themes.
Source: http://svn.tiddlywiki.org/Trunk/contributors/JonathanLister/plugins/ToggleThemePlugin.js
iPhone/iPad developer, works for Really Interesting Group; has worked on mag+ project with Berg. Was introduced by James Stewart. Asked him if he could do the [[DRAMA]] project, but he is too busy.
I stopped using this on 28th September 2009. Instead, see [[What I want]].
TrackVia call with Matt and Colin, 16:40 - 17:05, 5th August 2010
interaction - web2TrackVia interaction; server2server also possible; connectors need to be built by someone and hosted somewhere.
they have a PayPal connector being worked on
they can switch on visual interface editor (which they're going to do on my test account)
workflow done through email alerts
pretty cool filter editor, with grouping AND/OR possible
alerts can be set for when records come in and out of views, which is interesting…
email distribution lists combined with filters are pretty interesting… instead of having a separate "workflow" engine that handles whether things are unapproved/approved, or whatever, the views filter tables to show certain records e.g. those that are unapproved, and then send you lists of these things. I think this is an interesting way to do workflow…
import/update via spreadsheet
you can do mail merge & email campaigns via templates you make
you can set up email addresses to receive data for creating/updating records
The all powerful [[Google Analytics]], which is free.
[[StatCounter]], which is also free (to an extent, and does a HTML-only counter for non-JS situations)
I need to figure out a few things:
# What's the legal status of me? Am I a self-employed person or a director of my own company, or something else?
# What's the projections for the ideas I want to work on?
# Where's the stable income coming from (see [[Setting up freelance contracts]])?
# What's my brand name? What's my business card look like?
/***
Description: Contains the stuff you need to use Tiddlyspot
Note, you also need UploadPlugin, PasswordOptionPlugin and LoadRemoteFileThroughProxy
from http://tiddlywiki.bidix.info for a complete working Tiddlyspot site.
***/
//{{{
// edit this if you are migrating sites or retrofitting an existing TW
config.tiddlyspotSiteId = 'jnthnlstr';
// make it so you can by default see edit controls via http
config.options.chkHttpReadOnly = false;
window.readOnly = false; // make sure of it (for tw 2.2)
window.showBackstage = true; // show backstage too
// disable autosave in d3
if (window.location.protocol != "file:")
config.options.chkGTDLazyAutoSave = false;
// tweak shadow tiddlers to add upload button, password entry box etc
with (config.shadowTiddlers) {
SiteUrl = 'http://'+config.tiddlyspotSiteId+'.tiddlyspot.com';
SideBarOptions = SideBarOptions.replace(/(<<saveChanges>>)/,"$1<<tiddler TspotSidebar>>");
OptionsPanel = OptionsPanel.replace(/^/,"<<tiddler TspotOptions>>");
DefaultTiddlers = DefaultTiddlers.replace(/^/,"[[WelcomeToTiddlyspot]] ");
MainMenu = MainMenu.replace(/^/,"[[WelcomeToTiddlyspot]] ");
}
// create some shadow tiddler content
merge(config.shadowTiddlers,{
'WelcomeToTiddlyspot':[
"This document is a ~TiddlyWiki from tiddlyspot.com. A ~TiddlyWiki is an electronic notebook that is great for managing todo lists, personal information, and all sorts of things.",
"",
"@@font-weight:bold;font-size:1.3em;color:#444; //What now?// @@ Before you can save any changes, you need to enter your password in the form below. Then configure privacy and other site settings at your [[control panel|http://" + config.tiddlyspotSiteId + ".tiddlyspot.com/controlpanel]] (your control panel username is //" + config.tiddlyspotSiteId + "//).",
"<<tiddler TspotControls>>",
"See also GettingStarted.",
"",
"@@font-weight:bold;font-size:1.3em;color:#444; //Working online// @@ You can edit this ~TiddlyWiki right now, and save your changes using the \"save to web\" button in the column on the right.",
"",
"@@font-weight:bold;font-size:1.3em;color:#444; //Working offline// @@ A fully functioning copy of this ~TiddlyWiki can be saved onto your hard drive or USB stick. You can make changes and save them locally without being connected to the Internet. When you're ready to sync up again, just click \"upload\" and your ~TiddlyWiki will be saved back to tiddlyspot.com.",
"",
"@@font-weight:bold;font-size:1.3em;color:#444; //Help!// @@ Find out more about ~TiddlyWiki at [[TiddlyWiki.com|http://tiddlywiki.com]]. Also visit [[TiddlyWiki.org|http://tiddlywiki.org]] for documentation on learning and using ~TiddlyWiki. New users are especially welcome on the [[TiddlyWiki mailing list|http://groups.google.com/group/TiddlyWiki]], which is an excellent place to ask questions and get help. If you have a tiddlyspot related problem email [[tiddlyspot support|mailto:support@tiddlyspot.com]].",
"",
"@@font-weight:bold;font-size:1.3em;color:#444; //Enjoy :)// @@ We hope you like using your tiddlyspot.com site. Please email [[feedback@tiddlyspot.com|mailto:feedback@tiddlyspot.com]] with any comments or suggestions."
].join("\n"),
'TspotControls':[
"| tiddlyspot password:|<<option pasUploadPassword>>|",
"| site management:|<<upload http://" + config.tiddlyspotSiteId + ".tiddlyspot.com/store.cgi index.html . . " + config.tiddlyspotSiteId + ">>//(requires tiddlyspot password)//<br>[[control panel|http://" + config.tiddlyspotSiteId + ".tiddlyspot.com/controlpanel]], [[download (go offline)|http://" + config.tiddlyspotSiteId + ".tiddlyspot.com/download]]|",
"| links:|[[tiddlyspot.com|http://tiddlyspot.com/]], [[FAQs|http://faq.tiddlyspot.com/]], [[blog|http://tiddlyspot.blogspot.com/]], email [[support|mailto:support@tiddlyspot.com]] & [[feedback|mailto:feedback@tiddlyspot.com]], [[donate|http://tiddlyspot.com/?page=donate]]|"
].join("\n"),
'TspotSidebar':[
"<<upload http://" + config.tiddlyspotSiteId + ".tiddlyspot.com/store.cgi index.html . . " + config.tiddlyspotSiteId + ">><html><a href='http://" + config.tiddlyspotSiteId + ".tiddlyspot.com/download' class='button'>download</a></html>"
].join("\n"),
'TspotOptions':[
"tiddlyspot password:",
"<<option pasUploadPassword>>",
""
].join("\n")
});
//}}}
Problem: sometimes people send tweets when they meant to send text messages;
Related problem: sometimes people broadcast tweets when they meant to send dm's
Idea (for the first problem): a bot that you send your texts to in the place of Twitter; it texts you back to say "are you sure you wanted to send: xxx"; if you don't reply in two minutes, it sends the tweet, giving you a chance to stop the tweet. Charge £10/year for the service.
An alternative name for TextTasks
Written up here:
v2 - http://jaybyjayfresh.com/2009/02/19/improving-my-twitter-reply-notifications-with-yahoo-pipes/
v1 - http://jaybyjayfresh.com/2009/02/18/adding-new-features-to-twitter-with-notifyme/
!The problem
UnaMesa are setting up "VIC" - Virtual Interactive Classroom. They have a SMS short-code that works in Bangladesh. You send a SMS to the short-code with a word, and it replies with the definition.
!The approach
saqimtiaz: jayfresh: this is an example url: http://vicdemo.lewcid.org/dictionary.php?word=FND
saqimtiaz: its a dictionary
saqimtiaz: so direct messages starting with BOU d need the last word captured and then you GET the url accordingly, reply with the meaning. jayfresh am I explaining that well enough?
| !date | !user | !location | !storeUrl | !uploadDir | !toFilename | !backupdir | !origin |
| 13/09/2010 16:38:48 | jnthnlstr | [[jnthnlstr.html|file:///Users/jonathanlister/Documents/Workbooks/jnthnlstr.html]] | [[store.cgi|http://jnthnlstr.tiddlyspot.com/store.cgi]] | . | [[index.html | http://jnthnlstr.tiddlyspot.com/index.html]] | . |
| 13/09/2010 16:40:53 | jnthnlstr | [[jnthnlstr.html|file:///Users/jonathanlister/Documents/Workbooks/jnthnlstr.html#%5B%5BWhat%20I%20want%5D%5D]] | [[store.cgi|http://jnthnlstr.tiddlyspot.com/store.cgi]] | . | [[index.html | http://jnthnlstr.tiddlyspot.com/index.html]] | . | ok | ok |
| 20/09/2010 22:02:06 | jnthnlstr | [[jnthnlstr.html|file:///Users/jonathanlister/Documents/Workbooks/jnthnlstr.html]] | [[store.cgi|http://jnthnlstr.tiddlyspot.com/store.cgi]] | . | [[index.html | http://jnthnlstr.tiddlyspot.com/index.html]] | . | ok |
| 08/10/2010 10:05:34 | YourName | [[jnthnlstr.html|file:///Users/jonathanlister/Documents/Workbooks/jnthnlstr.html#%5B%5BStudent%20Design%20Lightning%20talks%5D%5D]] | [[store.cgi|http://jnthnlstr.tiddlyspot.com/store.cgi]] | . | [[index.html | http://jnthnlstr.tiddlyspot.com/index.html]] | . | ok |
| 18/10/2010 10:55:34 | YourName | [[jnthnlstr.html|file:///Users/jonathanlister/Documents/Workbooks/jnthnlstr.html]] | [[store.cgi|http://jnthnlstr.tiddlyspot.com/store.cgi]] | . | [[index.html | http://jnthnlstr.tiddlyspot.com/index.html]] | . | ok |
| 20/10/2010 16:28:35 | YourName | [[jnthnlstr.html|file:///Users/jonathanlister/Documents/Workbooks/jnthnlstr.html]] | [[store.cgi|http://jnthnlstr.tiddlyspot.com/store.cgi]] | . | [[index.html | http://jnthnlstr.tiddlyspot.com/index.html]] | . | ok |
| 21/10/2010 12:29:16 | YourName | [[jnthnlstr.html|file:///Users/jonathanlister/Documents/Workbooks/jnthnlstr.html]] | [[store.cgi|http://jnthnlstr.tiddlyspot.com/store.cgi]] | . | [[index.html | http://jnthnlstr.tiddlyspot.com/index.html]] | . | ok |
| 03/11/2010 17:56:28 | YourName | [[jnthnlstr.html|file:///Users/jonathanlister/Documents/Workbooks/jnthnlstr.html]] | [[store.cgi|http://jnthnlstr.tiddlyspot.com/store.cgi]] | . | [[index.html | http://jnthnlstr.tiddlyspot.com/index.html]] | . | ok |
| 11/11/2010 16:15:37 | YourName | [[jnthnlstr.html|file:///Users/jonathanlister/Documents/Workbooks/jnthnlstr.html]] | [[store.cgi|http://jnthnlstr.tiddlyspot.com/store.cgi]] | . | [[index.html | http://jnthnlstr.tiddlyspot.com/index.html]] | . | ok |
| 11/11/2010 16:17:36 | YourName | [[jnthnlstr.html|file:///Users/jonathanlister/Documents/Workbooks/jnthnlstr.html]] | [[store.cgi|http://jnthnlstr.tiddlyspot.com/store.cgi]] | . | [[index.html | http://jnthnlstr.tiddlyspot.com/index.html]] | . |
/***
|''Name:''|UploadPlugin|
|''Description:''|Save to web a TiddlyWiki|
|''Version:''|4.1.3|
|''Date:''|Feb 24, 2008|
|''Source:''|http://tiddlywiki.bidix.info/#UploadPlugin|
|''Documentation:''|http://tiddlywiki.bidix.info/#UploadPluginDoc|
|''Author:''|BidiX (BidiX (at) bidix (dot) info)|
|''License:''|[[BSD open source license|http://tiddlywiki.bidix.info/#%5B%5BBSD%20open%20source%20license%5D%5D ]]|
|''~CoreVersion:''|2.2.0|
|''Requires:''|PasswordOptionPlugin|
***/
//{{{
version.extensions.UploadPlugin = {
major: 4, minor: 1, revision: 3,
date: new Date("Feb 24, 2008"),
source: 'http://tiddlywiki.bidix.info/#UploadPlugin',
author: 'BidiX (BidiX (at) bidix (dot) info',
coreVersion: '2.2.0'
};
//
// Environment
//
if (!window.bidix) window.bidix = {}; // bidix namespace
bidix.debugMode = false; // true to activate both in Plugin and UploadService
//
// Upload Macro
//
config.macros.upload = {
// default values
defaultBackupDir: '', //no backup
defaultStoreScript: "store.php",
defaultToFilename: "index.html",
defaultUploadDir: ".",
authenticateUser: true // UploadService Authenticate User
};
config.macros.upload.label = {
promptOption: "Save and Upload this TiddlyWiki with UploadOptions",
promptParamMacro: "Save and Upload this TiddlyWiki in %0",
saveLabel: "save to web",
saveToDisk: "save to disk",
uploadLabel: "upload"
};
config.macros.upload.messages = {
noStoreUrl: "No store URL in parmeters or options",
usernameOrPasswordMissing: "Username or password missing"
};
config.macros.upload.handler = function(place,macroName,params) {
if (readOnly)
return;
var label;
if (document.location.toString().substr(0,4) == "http")
label = this.label.saveLabel;
else
label = this.label.uploadLabel;
var prompt;
if (params[0]) {
prompt = this.label.promptParamMacro.toString().format([this.destFile(params[0],
(params[1] ? params[1]:bidix.basename(window.location.toString())), params[3])]);
} else {
prompt = this.label.promptOption;
}
createTiddlyButton(place, label, prompt, function() {config.macros.upload.action(params);}, null, null, this.accessKey);
};
config.macros.upload.action = function(params)
{
// for missing macro parameter set value from options
if (!params) params = {};
var storeUrl = params[0] ? params[0] : config.options.txtUploadStoreUrl;
var toFilename = params[1] ? params[1] : config.options.txtUploadFilename;
var backupDir = params[2] ? params[2] : config.options.txtUploadBackupDir;
var uploadDir = params[3] ? params[3] : config.options.txtUploadDir;
var username = params[4] ? params[4] : config.options.txtUploadUserName;
var password = config.options.pasUploadPassword; // for security reason no password as macro parameter
// for still missing parameter set default value
if ((!storeUrl) && (document.location.toString().substr(0,4) == "http"))
storeUrl = bidix.dirname(document.location.toString())+'/'+config.macros.upload.defaultStoreScript;
if (storeUrl.substr(0,4) != "http")
storeUrl = bidix.dirname(document.location.toString()) +'/'+ storeUrl;
if (!toFilename)
toFilename = bidix.basename(window.location.toString());
if (!toFilename)
toFilename = config.macros.upload.defaultToFilename;
if (!uploadDir)
uploadDir = config.macros.upload.defaultUploadDir;
if (!backupDir)
backupDir = config.macros.upload.defaultBackupDir;
// report error if still missing
if (!storeUrl) {
alert(config.macros.upload.messages.noStoreUrl);
clearMessage();
return false;
}
if (config.macros.upload.authenticateUser && (!username || !password)) {
alert(config.macros.upload.messages.usernameOrPasswordMissing);
clearMessage();
return false;
}
bidix.upload.uploadChanges(false,null,storeUrl, toFilename, uploadDir, backupDir, username, password);
return false;
};
config.macros.upload.destFile = function(storeUrl, toFilename, uploadDir)
{
if (!storeUrl)
return null;
var dest = bidix.dirname(storeUrl);
if (uploadDir && uploadDir != '.')
dest = dest + '/' + uploadDir;
dest = dest + '/' + toFilename;
return dest;
};
//
// uploadOptions Macro
//
config.macros.uploadOptions = {
handler: function(place,macroName,params) {
var wizard = new Wizard();
wizard.createWizard(place,this.wizardTitle);
wizard.addStep(this.step1Title,this.step1Html);
var markList = wizard.getElement("markList");
var listWrapper = document.createElement("div");
markList.parentNode.insertBefore(listWrapper,markList);
wizard.setValue("listWrapper",listWrapper);
this.refreshOptions(listWrapper,false);
var uploadCaption;
if (document.location.toString().substr(0,4) == "http")
uploadCaption = config.macros.upload.label.saveLabel;
else
uploadCaption = config.macros.upload.label.uploadLabel;
wizard.setButtons([
{caption: uploadCaption, tooltip: config.macros.upload.label.promptOption,
onClick: config.macros.upload.action},
{caption: this.cancelButton, tooltip: this.cancelButtonPrompt, onClick: this.onCancel}
]);
},
options: [
"txtUploadUserName",
"pasUploadPassword",
"txtUploadStoreUrl",
"txtUploadDir",
"txtUploadFilename",
"txtUploadBackupDir",
"chkUploadLog",
"txtUploadLogMaxLine"
],
refreshOptions: function(listWrapper) {
var opts = [];
for(i=0; i<this.options.length; i++) {
var opt = {};
opts.push();
opt.option = "";
n = this.options[i];
opt.name = n;
opt.lowlight = !config.optionsDesc[n];
opt.description = opt.lowlight ? this.unknownDescription : config.optionsDesc[n];
opts.push(opt);
}
var listview = ListView.create(listWrapper,opts,this.listViewTemplate);
for(n=0; n<opts.length; n++) {
var type = opts[n].name.substr(0,3);
var h = config.macros.option.types[type];
if (h && h.create) {
h.create(opts[n].colElements['option'],type,opts[n].name,opts[n].name,"no");
}
}
},
onCancel: function(e)
{
backstage.switchTab(null);
return false;
},
wizardTitle: "Upload with options",
step1Title: "These options are saved in cookies in your browser",
step1Html: "<input type='hidden' name='markList'></input><br>",
cancelButton: "Cancel",
cancelButtonPrompt: "Cancel prompt",
listViewTemplate: {
columns: [
{name: 'Description', field: 'description', title: "Description", type: 'WikiText'},
{name: 'Option', field: 'option', title: "Option", type: 'String'},
{name: 'Name', field: 'name', title: "Name", type: 'String'}
],
rowClasses: [
{className: 'lowlight', field: 'lowlight'}
]}
};
//
// upload functions
//
if (!bidix.upload) bidix.upload = {};
if (!bidix.upload.messages) bidix.upload.messages = {
//from saving
invalidFileError: "The original file '%0' does not appear to be a valid TiddlyWiki",
backupSaved: "Backup saved",
backupFailed: "Failed to upload backup file",
rssSaved: "RSS feed uploaded",
rssFailed: "Failed to upload RSS feed file",
emptySaved: "Empty template uploaded",
emptyFailed: "Failed to upload empty template file",
mainSaved: "Main TiddlyWiki file uploaded",
mainFailed: "Failed to upload main TiddlyWiki file. Your changes have not been saved",
//specific upload
loadOriginalHttpPostError: "Can't get original file",
aboutToSaveOnHttpPost: 'About to upload on %0 ...',
storePhpNotFound: "The store script '%0' was not found."
};
bidix.upload.uploadChanges = function(onlyIfDirty,tiddlers,storeUrl,toFilename,uploadDir,backupDir,username,password)
{
var callback = function(status,uploadParams,original,url,xhr) {
if (!status) {
displayMessage(bidix.upload.messages.loadOriginalHttpPostError);
return;
}
if (bidix.debugMode)
alert(original.substr(0,500)+"\n...");
// Locate the storeArea div's
var posDiv = locateStoreArea(original);
if((posDiv[0] == -1) || (posDiv[1] == -1)) {
alert(config.messages.invalidFileError.format([localPath]));
return;
}
bidix.upload.uploadRss(uploadParams,original,posDiv);
};
if(onlyIfDirty && !store.isDirty())
return;
clearMessage();
// save on localdisk ?
if (document.location.toString().substr(0,4) == "file") {
var path = document.location.toString();
var localPath = getLocalPath(path);
saveChanges();
}
// get original
var uploadParams = new Array(storeUrl,toFilename,uploadDir,backupDir,username,password);
var originalPath = document.location.toString();
// If url is a directory : add index.html
if (originalPath.charAt(originalPath.length-1) == "/")
originalPath = originalPath + "index.html";
var dest = config.macros.upload.destFile(storeUrl,toFilename,uploadDir);
var log = new bidix.UploadLog();
log.startUpload(storeUrl, dest, uploadDir, backupDir);
displayMessage(bidix.upload.messages.aboutToSaveOnHttpPost.format([dest]));
if (bidix.debugMode)
alert("about to execute Http - GET on "+originalPath);
var r = doHttp("GET",originalPath,null,null,username,password,callback,uploadParams,null);
if (typeof r == "string")
displayMessage(r);
return r;
};
bidix.upload.uploadRss = function(uploadParams,original,posDiv)
{
var callback = function(status,params,responseText,url,xhr) {
if(status) {
var destfile = responseText.substring(responseText.indexOf("destfile:")+9,responseText.indexOf("\n", responseText.indexOf("destfile:")));
displayMessage(bidix.upload.messages.rssSaved,bidix.dirname(url)+'/'+destfile);
bidix.upload.uploadMain(params[0],params[1],params[2]);
} else {
displayMessage(bidix.upload.messages.rssFailed);
}
};
// do uploadRss
if(config.options.chkGenerateAnRssFeed) {
var rssPath = uploadParams[1].substr(0,uploadParams[1].lastIndexOf(".")) + ".xml";
var rssUploadParams = new Array(uploadParams[0],rssPath,uploadParams[2],'',uploadParams[4],uploadParams[5]);
var rssString = generateRss();
// no UnicodeToUTF8 conversion needed when location is "file" !!!
if (document.location.toString().substr(0,4) != "file")
rssString = convertUnicodeToUTF8(rssString);
bidix.upload.httpUpload(rssUploadParams,rssString,callback,Array(uploadParams,original,posDiv));
} else {
bidix.upload.uploadMain(uploadParams,original,posDiv);
}
};
bidix.upload.uploadMain = function(uploadParams,original,posDiv)
{
var callback = function(status,params,responseText,url,xhr) {
var log = new bidix.UploadLog();
if(status) {
// if backupDir specified
if ((params[3]) && (responseText.indexOf("backupfile:") > -1)) {
var backupfile = responseText.substring(responseText.indexOf("backupfile:")+11,responseText.indexOf("\n", responseText.indexOf("backupfile:")));
displayMessage(bidix.upload.messages.backupSaved,bidix.dirname(url)+'/'+backupfile);
}
var destfile = responseText.substring(responseText.indexOf("destfile:")+9,responseText.indexOf("\n", responseText.indexOf("destfile:")));
displayMessage(bidix.upload.messages.mainSaved,bidix.dirname(url)+'/'+destfile);
store.setDirty(false);
log.endUpload("ok");
} else {
alert(bidix.upload.messages.mainFailed);
displayMessage(bidix.upload.messages.mainFailed);
log.endUpload("failed");
}
};
// do uploadMain
var revised = bidix.upload.updateOriginal(original,posDiv);
bidix.upload.httpUpload(uploadParams,revised,callback,uploadParams);
};
bidix.upload.httpUpload = function(uploadParams,data,callback,params)
{
var localCallback = function(status,params,responseText,url,xhr) {
url = (url.indexOf("nocache=") < 0 ? url : url.substring(0,url.indexOf("nocache=")-1));
if (xhr.status == 404)
alert(bidix.upload.messages.storePhpNotFound.format([url]));
if ((bidix.debugMode) || (responseText.indexOf("Debug mode") >= 0 )) {
alert(responseText);
if (responseText.indexOf("Debug mode") >= 0 )
responseText = responseText.substring(responseText.indexOf("\n\n")+2);
} else if (responseText.charAt(0) != '0')
alert(responseText);
if (responseText.charAt(0) != '0')
status = null;
callback(status,params,responseText,url,xhr);
};
// do httpUpload
var boundary = "---------------------------"+"AaB03x";
var uploadFormName = "UploadPlugin";
// compose headers data
var sheader = "";
sheader += "--" + boundary + "\r\nContent-disposition: form-data; name=\"";
sheader += uploadFormName +"\"\r\n\r\n";
sheader += "backupDir="+uploadParams[3] +
";user=" + uploadParams[4] +
";password=" + uploadParams[5] +
";uploaddir=" + uploadParams[2];
if (bidix.debugMode)
sheader += ";debug=1";
sheader += ";;\r\n";
sheader += "\r\n" + "--" + boundary + "\r\n";
sheader += "Content-disposition: form-data; name=\"userfile\"; filename=\""+uploadParams[1]+"\"\r\n";
sheader += "Content-Type: text/html;charset=UTF-8" + "\r\n";
sheader += "Content-Length: " + data.length + "\r\n\r\n";
// compose trailer data
var strailer = new String();
strailer = "\r\n--" + boundary + "--\r\n";
data = sheader + data + strailer;
if (bidix.debugMode) alert("about to execute Http - POST on "+uploadParams[0]+"\n with \n"+data.substr(0,500)+ " ... ");
var r = doHttp("POST",uploadParams[0],data,"multipart/form-data; ;charset=UTF-8; boundary="+boundary,uploadParams[4],uploadParams[5],localCallback,params,null);
if (typeof r == "string")
displayMessage(r);
return r;
};
// same as Saving's updateOriginal but without convertUnicodeToUTF8 calls
bidix.upload.updateOriginal = function(original, posDiv)
{
if (!posDiv)
posDiv = locateStoreArea(original);
if((posDiv[0] == -1) || (posDiv[1] == -1)) {
alert(config.messages.invalidFileError.format([localPath]));
return;
}
var revised = original.substr(0,posDiv[0] + startSaveArea.length) + "\n" +
store.allTiddlersAsHtml() + "\n" +
original.substr(posDiv[1]);
var newSiteTitle = getPageTitle().htmlEncode();
revised = revised.replaceChunk("<title"+">","</title"+">"," " + newSiteTitle + " ");
revised = updateMarkupBlock(revised,"PRE-HEAD","MarkupPreHead");
revised = updateMarkupBlock(revised,"POST-HEAD","MarkupPostHead");
revised = updateMarkupBlock(revised,"PRE-BODY","MarkupPreBody");
revised = updateMarkupBlock(revised,"POST-SCRIPT","MarkupPostBody");
return revised;
};
//
// UploadLog
//
// config.options.chkUploadLog :
// false : no logging
// true : logging
// config.options.txtUploadLogMaxLine :
// -1 : no limit
// 0 : no Log lines but UploadLog is still in place
// n : the last n lines are only kept
// NaN : no limit (-1)
bidix.UploadLog = function() {
if (!config.options.chkUploadLog)
return; // this.tiddler = null
this.tiddler = store.getTiddler("UploadLog");
if (!this.tiddler) {
this.tiddler = new Tiddler();
this.tiddler.title = "UploadLog";
this.tiddler.text = "| !date | !user | !location | !storeUrl | !uploadDir | !toFilename | !backupdir | !origin |";
this.tiddler.created = new Date();
this.tiddler.modifier = config.options.txtUserName;
this.tiddler.modified = new Date();
store.addTiddler(this.tiddler);
}
return this;
};
bidix.UploadLog.prototype.addText = function(text) {
if (!this.tiddler)
return;
// retrieve maxLine when we need it
var maxLine = parseInt(config.options.txtUploadLogMaxLine,10);
if (isNaN(maxLine))
maxLine = -1;
// add text
if (maxLine != 0)
this.tiddler.text = this.tiddler.text + text;
// Trunck to maxLine
if (maxLine >= 0) {
var textArray = this.tiddler.text.split('\n');
if (textArray.length > maxLine + 1)
textArray.splice(1,textArray.length-1-maxLine);
this.tiddler.text = textArray.join('\n');
}
// update tiddler fields
this.tiddler.modifier = config.options.txtUserName;
this.tiddler.modified = new Date();
store.addTiddler(this.tiddler);
// refresh and notifiy for immediate update
story.refreshTiddler(this.tiddler.title);
store.notify(this.tiddler.title, true);
};
bidix.UploadLog.prototype.startUpload = function(storeUrl, toFilename, uploadDir, backupDir) {
if (!this.tiddler)
return;
var now = new Date();
var text = "\n| ";
var filename = bidix.basename(document.location.toString());
if (!filename) filename = '/';
text += now.formatString("0DD/0MM/YYYY 0hh:0mm:0ss") +" | ";
text += config.options.txtUserName + " | ";
text += "[["+filename+"|"+location + "]] |";
text += " [[" + bidix.basename(storeUrl) + "|" + storeUrl + "]] | ";
text += uploadDir + " | ";
text += "[[" + bidix.basename(toFilename) + " | " +toFilename + "]] | ";
text += backupDir + " |";
this.addText(text);
};
bidix.UploadLog.prototype.endUpload = function(status) {
if (!this.tiddler)
return;
this.addText(" "+status+" |");
};
//
// Utilities
//
bidix.checkPlugin = function(plugin, major, minor, revision) {
var ext = version.extensions[plugin];
if (!
(ext &&
((ext.major > major) ||
((ext.major == major) && (ext.minor > minor)) ||
((ext.major == major) && (ext.minor == minor) && (ext.revision >= revision))))) {
// write error in PluginManager
if (pluginInfo)
pluginInfo.log.push("Requires " + plugin + " " + major + "." + minor + "." + revision);
eval(plugin); // generate an error : "Error: ReferenceError: xxxx is not defined"
}
};
bidix.dirname = function(filePath) {
if (!filePath)
return;
var lastpos;
if ((lastpos = filePath.lastIndexOf("/")) != -1) {
return filePath.substring(0, lastpos);
} else {
return filePath.substring(0, filePath.lastIndexOf("\\"));
}
};
bidix.basename = function(filePath) {
if (!filePath)
return;
var lastpos;
if ((lastpos = filePath.lastIndexOf("#")) != -1)
filePath = filePath.substring(0, lastpos);
if ((lastpos = filePath.lastIndexOf("/")) != -1) {
return filePath.substring(lastpos + 1);
} else
return filePath.substring(filePath.lastIndexOf("\\")+1);
};
bidix.initOption = function(name,value) {
if (!config.options[name])
config.options[name] = value;
};
//
// Initializations
//
// require PasswordOptionPlugin 1.0.1 or better
bidix.checkPlugin("PasswordOptionPlugin", 1, 0, 1);
// styleSheet
setStylesheet('.txtUploadStoreUrl, .txtUploadBackupDir, .txtUploadDir {width: 22em;}',"uploadPluginStyles");
//optionsDesc
merge(config.optionsDesc,{
txtUploadStoreUrl: "Url of the UploadService script (default: store.php)",
txtUploadFilename: "Filename of the uploaded file (default: in index.html)",
txtUploadDir: "Relative Directory where to store the file (default: . (downloadService directory))",
txtUploadBackupDir: "Relative Directory where to backup the file. If empty no backup. (default: ''(empty))",
txtUploadUserName: "Upload Username",
pasUploadPassword: "Upload Password",
chkUploadLog: "do Logging in UploadLog (default: true)",
txtUploadLogMaxLine: "Maximum of lines in UploadLog (default: 10)"
});
// Options Initializations
bidix.initOption('txtUploadStoreUrl','');
bidix.initOption('txtUploadFilename','');
bidix.initOption('txtUploadDir','');
bidix.initOption('txtUploadBackupDir','');
bidix.initOption('txtUploadUserName','');
bidix.initOption('pasUploadPassword','');
bidix.initOption('chkUploadLog',true);
bidix.initOption('txtUploadLogMaxLine','10');
// Backstage
merge(config.tasks,{
uploadOptions: {text: "upload", tooltip: "Change UploadOptions and Upload", content: '<<uploadOptions>>'}
});
config.backstageTasks.push("uploadOptions");
//}}}
Josh on BPSF, using a text doc in DropBox
Josh: I write these things all the time, and put them in random places on my hard-drive. I find them years later and I'm like, "oh, that's what I was doing then".
Me: the pressure of knowing you're organising and writing for other people to read changes the way you do those things. Actually, we're organising and writing for our future self; we don't always respect that.
I wanted to be able to recommend a way for RareRetreats to charge their customers different prices depending on the service they wanted. I had imagined something along the lines of an online box where you put the price in and make a direct payment to the supplier.
I couldn't find a way to use Google Checkout or PayPal to allow people to make this kind of direct money transfer. However, I did discover the invoicing abilities.
Google Checkout: http://checkout.google.com/support/sell/bin/answer.py?hl=en&answer=53026
PayPal: https://www.paypal.com/cgi-bin/webscr?cmd=p/sell/invoicing_intro-outside
Here's the information about the services different rates. They're both identical - roughly 2-3.5% + £0.20 per transaction:
Google: http://checkout.google.com/seller/fees.html
PayPal: https://www.paypal.com/uk/cgi-bin/webscr?cmd=_display-receiving-fees-outside&countries=
Problem: want to use gDocs for its collaboration and composing niceness. Don't want to have hassle publishing content to a website.
I'm not the first person to want to do this - found one WordPress plugin: http://wordpress.org/extend/plugins/inline-google-docs/
trying this out... concern is that I think this is the source code site - http://code.google.com/p/inline-google-docs/ - and it says the plugin has been deprecated in favour of "better, more secure" ways to embed gDocs. This plugin doesn't use an iFrame. As I'm only seeking to embed published docs, I'm not sure security is a problemo.
This doesn't seem to work unless you provide it with your gDocs credentials, which I'm not going to do.
So... how about just using curl to get the file? Found on the gDocs List API that you can do something like:
{{{http://docs.google.com/feeds/download/documents/Export?docID={$id}&exportFormat=html}}}
But this doesn't work if you don't log in and I want to use unauthenticated...
The obvious unauthenticated URL is the published URL e.g.
{{{
https://docs.google.com/document/pub?id={$id}
}}}
See [[Violet Hill Studio]].
Making a website for them.
!Summary
We have graphs to show things:
# Line graph of bank balance, with projected future balance
# Pie chart of breakdown of total expenditure
# Bar chart of total expenditure by month
# Stacked bar chart of expenditure by category by month
!Four things wanted:
#Visualisation of account balance over time
** Exported CSV's from online banking
** Coverted comma-separated to tab-separated (so Google Spreadsheets would accept copy-and-paste)
** Put into Google Spreadsheets and created little graph
** Published date and balance as CSV
** Imported feed into Timemetric - hasn't worked yet...
** Having seen the graph in Google Docs, Josh thought it would be good to be able to click on a transaction to see the description that comes with that transaction
*** Google Docs doesn't seem to be able to do this; neither does Timetric
*** Perhaps via the Google Visualisation API?
#Tracking of future income and projected steady expenditure (for use in extending the above visualisation)
** First thing is adding an extra data series that shows the extra income if every outstanding invoice is paid
*** Created an extra column in the invoice processing sheet, which shows the amount outstanding
*** Totaled the outstanding amounts
*** Imported that into the balance spreadsheet and currently trying to get it to show up as another series
#Breakdown of expenditure into categories:
** Categories:
*** Pay
*** Project expenses
*** Corporation Tax
*** VAT
*** National insurance
*** Rent
*** Sundries (inc. office equipment, entertainment, eating, etc.)
** Extracted category totals from manual categorisation of expenditure
** Created dynamic pie chart in Google Spreadsheet to show categories
#Average monthly expenditure by category
** First, doing average monthly expenditure in total
*** This involved pulling the year and month from each item's date and then creating a new date with the day set to the first of each month; then you find the unique set of dates and do a conditional sum if the month matches
** Creating monthly expenditure by category
*** This makes use of the same set of data as the previous chart, but uses a two-condition filter to get at the items that match both category and month
Weird art collective thing started by Ben Lovedale and Clive Sall.
From Wants refresh Thursday 25th February '10 - items satisfied or removed are in [[What I Wanted]]
[[enjoy life, good things, food, music, people, friends]]
[[system, web projects, streamline, software, tools, different people]]
[[electronomads, promote lifestyle, individual importance, products, projects, creation]]
[[products, cashflow, stakes, consumers, creation, make people powerful]]
I'm writing this just over six months into the whole [[Productively]] thing. I have felt that my [[Weekly Reviews]] are lacking a certain long-termness that perhaps they once had, with the focus having drifted to short-term projects rather than the importance of the underlying [[Wants]]. I've decided to refresh what I want in order to give myself something more focused to think about each week.
Process:
# write down all wants, trying to be as specific as possible; don't order by anything; phrase constructively rather than focusing on avoiding things; don't be afraid to come back and modify this list at any time during the process - go through each step with the new item(s).
** if you have an intimidatingly long list, you can reduce it to the most important wants by doing two things: (a) realise when two or more wants are really different angles on a single want and replace them; (b) pick the most important want by imagining you could only have one of them and deciding which you'd have; repeat until you have a list of a size you feel comfortable with
# figure out how long you'd like each want to take to satisfy (I think of six months as the time when I'd want most to be done by); re-order them by time to show how your accomplishments are going to satisfy what you want if you do all these things; write down the months when these things will happen to ground the timescales in reality and make it feel real
# write a paragraph (or more) about each want to explain what things would look like if you satisfied the want (I also put down why I feel the want if necessary); write in the present tense as if you had satisfied the want when talking about how things are different; use sentences, don't write bullet-points
** took me 2 hours to get to this point
# for each want, write about what you feel when you think about that want, what's currently blocking you from satisfying it and any ideas that will shape how you will satisfy that want. You don't have to come up with all the solutions now, but do be honest with yourself when thinking about why you haven't yet satisfied that want.
# for each want, write down everything you can think of that you do now (or ideas you've had during the previous step) that serves that want; only write down things which you actively choose to do - not sleeping, hanging out in the pub or reading the paper - not habits, but things that you have control over whether you do or not
# write down anything you know you do but that you haven't pinned to a particular want; try to figure out how to see these things differently, or go about doing them differently so they do serve a want, and write that down; if you still can't pin them to a want, it's time to stop doing those things
** I have a snapshot [[Practical Projects]] list from July, as well as the items from the [[Weekly Reviews]]
** took me 4 hours in total to get to this point
# now take a break to let things sink in and for the 'big picture' to develop
** I ended up taking a week to do this
What I'm doing now:
Each week, I start from the list of wants, which I keep in a tiddler called [[What I want]]. I use them as headlines to write the most important things under. At the end of the week I write a "[[weekly review|Weekly reviews]]"; as part of that, I copy the contents of the [[What I want]] tiddler and mark whether things are done or not done. For the next week, I start fresh again. This avoids any build-up of stale things that I thought were important but aren't. I'm also trying to estimate how long it takes to do things and measure how accurate I've been. I'm using 31 hours as a limit to the amount of time I'll allocate in the plan and am aiming not to schedule that much, leaving space for spontaneity.
!!Step 1 - Wants
* I want my business to be so efficiently run that it takes me no time to administer
* I want to have a surplus of cash of a few hundred pounds a month in my personal account
* I want to remember a dozen or so guitar tracks - spanish, jazz, songs - and be able to play them
* I want to have enough money in my business to take three months out of corporate consultancy
* I want my business to be generating a few thousand a month in revenue that doesn't require any upkeep
* I want to have money available to help other projects out - startup businesses or art projects
* I want to create something that makes high-quality coffee-makers more prominent than current dominant coffee shops
* I want to be convinced that I'm a fluent, good cook who brings regular pleasure to myself and others through food
* I want to do the little things that make other people happy that don't seem big or important to me
* I want to create an effective model for running fair-reward open projects
* I want to make people waste less time
!!Step 2 - add times
* I want to do the little things that make other people happy that don't seem big or important to me - 2 months (November)
* I want my business to be so efficiently run that it takes me no time to administer - 2 months (November)
* I want to remember a dozen or so guitar tracks - spanish, jazz, songs - and be able to play them - 3 months (December)
* I want to invest in startup businesses or art projects - 4 months (January)
* I want to be convinced that I'm a fluent, good cook who brings regular pleasure to myself and others through food - 5 months (February)
* I want my business to be generating a few thousand a month in revenue that doesn't require any upkeep - 6 months (March)
* I want to have a surplus of cash of a few hundred pounds a month in my personal account - 6 months (March)
* I want to create something that makes high-quality coffee-makers more prominent than current dominant coffee shops - 6 months (March)
* I want to create an effective model for running fair-reward open projects - 4 months (January)
* I want to make people waste less time - 6 months (March)
!!Step 3 - how things will look; Step 4 - how things look now
* [[I want to do the little things that make other people happy that don't seem big or important to me]] - 2 months (November)
* [[I want my business to be so efficiently run that it takes me no time to administer]] - 2 months (November)
* [[I want to remember a dozen or so guitar tracks - spanish, jazz, songs - and be able to play them]] - 3 months (December)
* [[I want to create an effective model for running fair-reward open projects]] - 4 months (December)
* [[I want to invest in startup businesses or art projects]] - 4 months (January)
* [[I want to be convinced that I'm a fluent, good cook who brings regular pleasure to myself and others through food]] - 5 months (February)
* [[I want my business to be generating a few thousand a month in revenue that doesn't require any upkeep]] - 6 months (March)
* [[I want to have a surplus of cash of a few hundred pounds a month in my personal account]] - 6 months (March)
* [[I want to make people waste less time]] - 6 months (March)
* [[I want to create something that makes high-quality coffee-makers more prominent than current dominant coffee shops]] - 6 months (March)
!!Step 5 - what I'm doing now
* [[I want to do the little things that make other people happy that don't seem big or important to me]] - 2 months (November)
** [[Dad's IT problems]]
** [[Birthday Calendar]]
* [[I want my business to be so efficiently run that it takes me no time to administer]] - 2 months (November)
** [[yellowcar expenses]]
* [[I want to remember a dozen or so guitar tracks - spanish, jazz, songs - and be able to play them]] - 3 months (December)
** [[Getting good at the guitar again]]
** [[Finding guitar groups to play with]]
* [[I want to create an effective model for running fair-reward open projects]] - 4 months (December)
** there's also looking at existing open companies to learn from how they do things
*** could I visit these companies or talk to people who work from them?
** I should write about this so it's not just me doing this - there would be an online resource people can learn from
** A real example of a project with different bits run by different people - ElectroNomad?
* [[I want to invest in startup businesses or art projects]] - 4 months (January)
** [[WHO]]
** [[Paul Dyson]] - make their widget real good
** [[Paul Bacchus]] - expecting to hear from him in October and to figure out if we can do something together
** [[Notion Twitter/SMS]] - I should really see if I can get this started again, as it seems to close to being operational
** [[Flat Cap Coffee Co]] - what can we do to get them making more money that they can with just the cart and the passing trade?
** I have to make sure that my income is sufficient to invest my own time without my lifestyle deteriorating
*** [[Avox Wiki-data]] is the main income source at the moment
* [[I want to be convinced that I'm a fluent, good cook who brings regular pleasure to myself and others through food]] - 5 months (February)
** go shopping earlier!
* [[I want my business to be generating a few thousand a month in revenue that doesn't require any upkeep]] - 6 months (March)
** I need an example of a cash-generating business where all the sections are working without my involvement
* [[I want to have a surplus of cash of a few hundred pounds a month in my personal account]] - 6 months (March)
** I should figure out what I can put through the company and what that will save me
* [[I want to make people waste less time]] - 6 months (March)
** speaking to HSBC about [[fee-free micropayments]] app
** making the [[fee-free micropayments]] app for use as bank transfer app
*** getting it work with more banks
*** getting it working on the iPhone and approved on the Apple Store
** finding people who could make [[web-enabled electricity monitors]]
** finding people who'd buy [[web-enabled electricity monitors]]
** making a web-design/web-dev tool
*** testing with example projects: [[Violet Hill Studio]], [[Grace Coffee]], [[BonnieParsons.com]] (v2)
** writing more examples of [[Automating the Web]]
* [[I want to create something that makes high-quality coffee-makers more prominent than current dominant coffee shops]] - 6 months (March)
** ElectroNomad coffee section
** [[Flap Cap Coffee Co]] onine - selling products, twitter account, something else!
!!Step 6 - stuff won't don't fit
* Blogging - how to make this fit?
** I write about coffee, using JavaScript to write entire apps, creating API's for systems that don't have them, DIY-automation and I draw cartoons
** I've always found that blogging gets people interested in what I'm doing, but it's not the only way
** An alternative to blogging in one place is to do lots of writing in other places and link to them from a personal site - this might make sense if I'm wanting to write things about disparate subjects
*** I don't know where stuff like the JavaScript stuff would go - I suppose I could keep a personal blog, but I don't want it to be really infrequently posted to. I suppose I could make shorter posts as well as larger ones that don't fit elsewhere; plus, posting about what I'm working on is valid
Met w/c 15th Feb. Digital Agency specialising in Fast-Moving Consumer Goods (FMCG) sector (they say). Interested in everything from Arduino R&D to marketing micro-sites. Great at graphic design.
Met [[Danny Daley]] and [[Phil Parker]]
They are looking to hire 3 people. They do a lot of Flash. Good clients - BabyBel, Alfa Romeo, etc.
Projects involving large amounts of web design include:
[[BonnieParsons.com]]
[[Geekmap]]
[[ILGA]]
[[Anna Freud Centre]]
''Blogged at http://jaybyjayfresh.com/2010/01/27/web-designers/''
-----
//(I was thrilled to read Asana.com's [[description of a web designer|http://asana.com/#open-positions]], which inspired this post.)//
[img[http://farm2.static.flickr.com/1162/1085206378_b05f9ab939.jpg]]
It's not every industry where you can be a 30-year old and legitimately claim to have been working in the industry since it's birth. The field of web design is one of these industries, and the nature of the job has changed significantly since the days when animated gifs were the height of sophistication. I can't claim any knowledge of what it was like to be a web designer in the late 90's - the closest I can come is that I had a subscription to .Net magazine. However, I do know what it's like to be a web designer now and I know that it is a badly named job.
People in the web industry tend to be labelled either "web designers" or "web developers", the assumption being that you either draw the pictures or write the code. This may have been a reasonable separation at one point. The truth as I see it now is that the web designer has progressed from writing HTML and CSS to taking responsibility for everything that happens within the browser. Web developers have retreated to the increasingly complex job of moving and processing data, around and between servers.
This means that web design, in practice, is a combination of typography, human-computer interface design, graphic design, rapid prototyping, JavaScript programming, HTML/CSS, agile product development, to name a selection. The modern web designer understands the significance of REST and HTTP, and why they make her job easier. She may even know regular expressions. If there is another word to replace "designer", which captured the spirit of experimentation, creation and science that goes into modern web design, I am stuck to think of it.
Web design has flourished as a design profession - matured enough for an average project to bear the hallmarks of the kind of "design thinking" you might find amongst architects or industrial designers. This is all for the good. But web design is hard - there are barbed-wire boundaries between the work of web designers and those that surround them: the web developers, the content authors, the artists, the project managers, the business development consultants. Web design is relatively straightforward in isolation; meshed with all the other people involved in a project, it quickly becomes unstable. In my opinion, what holds the industry back from developing beyond a craft - that is, a complex and manual task - is the absence of good tools.
I overheard a conversation recently about why Flash became so popular with designers and has remained so, despite a strong feeling opposing its indiscriminate application. The problem boils down to one of tooling - Flash applications are built in beautiful, assistive development environments, which produce cross-browser and cross-platform applications. Web sites, on the other hand, are usually built in text editors and involve a significant investment of time to tweak and test for all the browsers in which they are likely to be run. Adobe produce compelling [[videos|http://labs.adobe.com/technologies/flashcatalyst/]] demonstrating how their tools simplify workflows and allow multiple professions to work together. Web designers generally have to "throw things over the wall" or duck things being chucked in their direction.
Specifically, I'm interested in seeing solutions to these sorts of problems:
* Why isn't it easy for content authors to decouple their stuff from the ongoing design of a website?
* Why do I design a website in a different environment from the one it is going to be deployed to?
* Why do I have to cut up PhotoShop comps into web pages and then cut up those into templates?
* Why can't I change the use of a single colour across a website by changing it once in the stylesheet?
* Why can't two web designers work on the same web page without it being a pain to maintain?
* Why does everyone have to diagnose, fix and learn the same cross-browser tricks?
* Why do people still think that "let's see several options" is an ok brief?
Doesn't need to be drag 'n' drop. In fact, I'm making a TW-based builder that isn't. You do need to be able to make forms at the least so you can get to other services.
| Name | Templating? | CSS control | Free? | Notes |
| [[WidgetFinger]] | Uses Liquid | ? | $10 a month to publish a site and activate email | |
| [[Google Pages]] | ? | ? | yes, but closed to new signups | |
| [[Synthasite]] | No | No | Yes | Editor is slow |
| [[Squarespace]] | No | Yes | Free trial; from $8/month | |
| [[Jumpchart]] | No | No | Yes; premium accounts start from $5/month | For site structure planning and content proofing |
Also:
http://www.stiqr.com/ - like PhotoShop for web design; embeds into any site to let you literally add stickers of content - is real in-browser in-situ development (although the file are hosted elsewhere); uses one line of PHP on your site to do readable text for SEO
http://www.refineryhq.com/ - $20/month, choice of layouts and has email enquiry form built-in
http://unbounce.com/ - A/B testing, for marketing campaign landing pages, you can mask behind your URL, also integrates with services like MailChimp and GAnalytics
http://kafafa.com - simple
http://www.lifeyo.com - simple, with blog and widgets
http://viviti.com/ - looks comprehensive, templates, email, domain, widgets, eCommerce, from free - $30/month
http://www.webs.com/ - forums, permissioned access, templates, not very sophisticated pages, analytics
http://www.speaklight.com/ - simple to setup, templates, easy to resell the service as a web designer - free - $99/month
http://www.embracewater.com - I think it's the same CMS as Light (speaklight.com), has an image editor, eCommerce setup (PayPal), forms, SEO, podcasts - free - $149/month
http://www.subhub.com/ - primarily about making money from subscriptions to the website you make with SubHub
http://virb.com
These started being weekly, but moved to fortnightly in March 2010. <<newTiddler title:"Review fortnight ending" label:"New review">>
<<list filter '[title[Review ]][sort[-modified]]'>>
Use to chronologically track [[Wants]] I think I've satisfied or changed my mind about.
-----
This list composed during [[Wants refresh 28th September 2009]].
* [[I want to create something that makes high-quality coffee-makers more prominent than current dominant coffee shops]] - 6 months (March)
* [[I want to have a surplus of cash of a few hundred pounds a month in my personal account]] - 6 months (March)
* [[I want my business to be generating a few thousand a month in revenue that doesn't require any upkeep]] - 6 months (March)
* [[I want to be convinced that I'm a fluent, good cook who brings regular pleasure to myself and others through food]] - 5 months (February)
* [[I want to invest in startup businesses or art projects]] - 4 months (January)
* [[I want to create an effective model for running fair-reward open projects]] - 4 months (December)
* [[I want to do the little things that make other people happy that don't seem big or important to me]] - 2 months (November)
* [[I want my business to be so efficiently run that it takes me no time to administer]] - 2 months (November)
* [[I want to remember a dozen or so guitar tracks - spanish, jazz, songs - and be able to play them]] - 3 months (December)
----
(THIS PART IS OUT-OF-DATE and was used as part of the development of [[Productively]])
What defines a "want" is something that happens as a result of a [[project|Practical Projects]] - it's stuff that comes in as a result of what you push out.
This list below came from a process probably best described in [[Productively, part 1]].
Top 2 (see [[Top Wants, Goals and Projects]] for more information):
# [[I want to be able to go freelance]]
# [[I want a balance between appreciating what other have created and creating things myself (aka sort your life out)]]
Others (it seems these are all vaguely derived from [[I want to be able to go freelance]]):
# [[I want to become known for making useful web apps and mashups without writing code]]
# [[I want to become known for running a successful open biz]]
# [[I want to become known for making useful web apps and mashups without writing code]]
# [[I want a touch-free-as-possible income of £1k/month]]
# [[I want to become known for helping people on an individual level and develop the "work for perks" ideas]]
Fortnight beginning 18th October 2010. Up to 62 hours (see [[Habits]]). Seeded from [[Wants]].
* [[enjoy life, good things, food, music, people, friends]]
** Updates to BonnieParsons.com - 2hrs
* [[products, cashflow, stakes, consumers, creation, make people powerful]]
** [[Kathryn McMann]] - build the site - 5 hrs
** [[What On Earth]] - improvements - 10hrs
** [[BPSF]] - improvements - 8hrs
** [[SweetSpot]] - plan Phase IX - 3hrs
** [[Escape]] - get Yaman's quote together - 2hrs
* [[system, web projects, streamline, software, tools, different people]]
** AttentionTracker - make bill-points work as lower bounds for a period as well as upper bounds - 2hrs
** meeting with Charlotte about booklets - 2hrs
** Set up meet with Paul Goodenough - 2hrs
** Set up coffee with Johanna Kollman - 2hrs
** Lunch with Sarb - 2hrs
* [[electronomads, promote lifestyle, individual importance, products, projects, creation]]
** J&J - get WithJ&J.com set up as a WordPress Network - 3hrs
** J&J - write another article - 3hrs
Total estimate: 46hrs
Billable estimate: 26hrs
See [[What On Earth Books]].
Project with Chris Lloyd started September 2010.
Project tracking details to follow.
You're best looking at [[What I want]] for what I'm doing right now and why.
Chronology is probably best understood by looking at the [[Weekly Reviews]].
Renamed to [[Why time booking isn't just for grown-ups]]
Blogged at http://jaybyjayfresh.com/2009/11/26/why-time-booking-isnt-just-for-grown-ups/
-----
According to the testimony of friends who work as lawyers, the worst part of the job (apart from putting paper into colour-coded files) is the hassle of accounting for every minute of every day. They do this so their practice can charge its outrageous fees to the right clients - it seems that anything not being directly charged for isn't worth doing (unless you can get someone commanding a lower hourly rate to do it). I feel palpable relief that I have never been subject to such officious management.
It has therefore been something of a shock that, over the last six months, I've become a convert to time booking. This transformation has been so complete that I book time to all the projects I work on, even if I'm not billing a customer. The reason I do this is simple - far apart from the homicidal minute-by-minute accounting of the lawyer, keeping a record of how I spend time brings a number of benefits, the most pronounced of which is a change of behaviour to spend time doing what I know I want to be doing. This might sound frankly un-earth-shattering, but as I hope to make clearer, knowing you're doing what you want to be doing feels good, especially if you can get there without it being burdensome or annoying.
!!Why I book time
This breaks down into three explanations, which build on top of each other: knowing what I'm doing, knowing I'm doing what I want to be doing and doing what I've said I'm going to do.
!!! Knowing what I'm doing
I recently carried out an experiment on myself, where I kept a diary of everything I spent time on for three months and then analysed the data to see where my time was going (I've yet to write this up, although the sister experiment on what I spend money on is [[here]]). In advance, I'd predicted what I spend time on - things like sleeping, eating, hanging out with friends - to see how accurate a picture I had of my own doings. The outcome showed that, although I'd been able to make a decent gut estimate of how I spent my time on habitual activities (such as sleeping), there was a wide gap of 30 or so hours (nearly 20% of the week) where I had no idea in advance what I was going to do.
It is not such a terrible thing not to be able to say what you've been doing, but it feels like we are under a lot of pressure to //increase efficiency//, //live life faster//, //sleep when you're dead//, etc. etc. Even if you don't buy into this nonsense, it's likely you've wished there were more hours in the day at least once. How can you improve something if you can't measure it? Writing down what I'm up to obviously means I have a much better idea of what I'm up to.
!!! Knowing I'm doing what I want to be doing
I don't think I'm alone in finding it easy to get to the end of a day and think "very good, martini time. Now then, what exactly have I achieved today?". It's a somewhat more pronounced feeling if I get to the end of a week and think similar thoughts. It's easy to brush off the question and determine to be more memorably constructive the next week, but at some point it starts to feel like there's a lack of control over your own time.
I do think it's important to feel that you're doing what you want to be doing, rather than just reacting to things, bouncing about. Having a written record of where my hours have gone means I can't just kid myself into thinking I've been terribly productive when I haven't - initially this record is an uncomfortable kind of crutch. However, I think the discomfort is worth it, as after a short time, making myself aware of what I'm doing means I change my behaviour to be happy with that record.
Another thing worth noting is that once you feel confident that you're doing things you want to be doing, all the other things that previously seemed so pressing now seem less so. In other words, you'll end up choosing how you spend time.
!!! Doing what I've said I'm going to do
Diets, fitness programmes, personal development plans, business development - many things take more than a week to come to fruition, but since it seems natural to think of time in a rhythm of the week and the weekend, it's easy to become dispirited if looked-for effects take longer than a single week to surface.
One way of making it harder to stop doing things you've previously decided you want to be doing is to have the fact that you're doing them written down - it feels way harder to stop something I've started when I can look back and see what progress I've made. It's also easier to succumb to continuity and copy across activities from an earlier week than it is to commit to new ones: if I can see I'm on the way to finishing something, that makes it harder to start something shiny and new. Which brings me on to a bonus benefit...
!!! Avoiding kittening
My habit of writing down what I'm doing means that I often have to expend effort to change what I'm doing. I like to avoid effort, so I've tended to want to avoid changing what I'm doing - my behaviour has drifted naturally towards spending longer periods of time on one thing rather than multi-tasking fruitlessly.
!! How I book time
Now you're totally convinced of the need to do some time booking of your own, you will be worrying that it is going to take you forever to keep up to date. Fear not! Even with a mild tendency towards data-OCD, it takes me somewhere between ten minutes and an hour per week. As I have said, I like to avoid effort, so I don't go much detail unless there's a good reason to (such as wanting a detailed record for billing).
At the start of the week, I take the time to write down what I want to be doing that week and an estimate of how long I think each thing will take. At the end of the week, I write down whether each thing has been finished or not and compare my estimate of how long something would take with how long it actually did.
At the moment, I'm using two methods to book time - a broad estimate of time-spent vs. an hour-by-hour account.
!!! Broad
At the end of a day, if I know I've spend a chunk of time on something, I'll write that next to the thing, with a brief note of what I did. This is pretty rough, and I'm only bothered about the accuracy being to the nearest 30 minutes or even hour. I think this probably takes ten minutes in total over a week.
!!! Detailed
If I'm doing something that I want to keep detailed notes for, I'll write down each day when I start spending time on it and what I'm doing as I go along. When I change to something else, I'll record the end of that period. The accuracy is to the nearest five minutes. I usually have one or two projects running like this at any one time and over a week it might take closer to an hour than ten minutes to make the record.
!! What time booking is not for
I do not advocate the measurement of time-spent as some sort of performance metric. It is not right to reward yourself or someone else just because a lot of time has been spent on something: reward outcomes, not efforts.
However, the ability to accurately forecast how long things will take you and to do what you say you will are both measurable traits you ought to encourage.
!The problem
This started with wanting to let people edit my CSS file for me. I want to have one resource that is can be used as the CSS file and allows people to edit it directly.
http://example.com/style.css - the CSS stylesheet
http://example.com/style - editing interface for the CSS stylesheet
!Progress
This post on the TWDev group has been very helpful: http://groups.google.com/group/TiddlyWikiDev/browse_thread/thread/d55a2645f62100d3
Trac ticket asking for documentation - http://trac.tiddlywiki.org/ticket/969
The plan is to add a '.css' serializer to TiddlyWeb and the TiddlyWeb editing plugin to add an 'edit' button to the raw resource.
The WireIt WiringEditor comes with load/save/etc. buttons. Some of these are controlled by a php RPC service. You define what this service can do in a .smd file, which is a JSON description of the methods available and what their arguments are. I was thinking you could write a client in the browser, to use instead of a php script, and then use ReverseHTTP to expose it. [tonyg :)]
A collection of modules and connections is called a "working".
Modules are of a type, called a "Container", which defines what they have in them. The Containers are: Container (base class), FormContainer (contains forms defined in InputEx), ImageContainer.
You create a new WiringEditor like this:
{{{
var editor = new WireIt.WiringEditor(demoLanguage);
}}}
{{{demoLanguage}}} is a JSON file that defines the containers and the .smd file.
!!Working structure
WiringEditor exposes a method called getValue, which is used in the saving process, which creates a JSON object describing the working; the object is created with this structure:
{{{
var obj = {modules: [], wires: [], properties: null};
}}}
A module has this structure:
{{{
{name:"", value: {}, config: {}}
}}}
Value is the result of a getValue call on a Container. At the moment, only the FormContainer has this method defined, and it returns a map of the inputs in the InputEx form. Config comes from the container's getConfig() call and contains the location and type of the container.
A wire has this structure:
{{{
{src:{}, target:{}}
}}}
Properties is a map of the inputs in the PropertiesForm.
There's a weird thing that a Layer.getWiring() method exists for when you're not using the WiringEditor, which returns an object with this structure:
{{{
{containers: [], wires: []}
}}}
A container just contains an object returned from the container's getConfig() method; wires are as above. I commented questioning this on the WireIt blog: http://javascript.neyric.com/blog/2009/05/17/wireit-040-released/#comment-662
Quick, hosted, collaborative.
My thoughts on wire-framing are that they should be done as real web-pages. What's the blocker to that?
| Name | Collaborative | Free? | Notes |
| [[balsamiq]] | no | from $79/user | Not hosted yet... that's being built |
| [[protoshare]] | yes | from $26/month | |
| [[Pencil]] | no | yes | Firefox addon as well as standalone |
| RapidRabbit | yes | from €30/month | Generates clickable prototypes |
| [[Mockingbird]] | no | yes | |
| [[Sketchables]] | ? | ? | blog post - http://www.hardcodet.net/2010/05/announcing-sketchables, based on Sketchflow/Blend, which are Silverlight things |
| [[Product Planner]] | ? | ? | more flows than wireframes |
Also:
http://lumzy.com - sounds good (image editor, annotations), but couldn't get it to work
http://recurseapp.com/ - comments on images (not positioned) and approvals
http://www.endloop.ca/ - iMockups - iPad only, but good looking. Does an interesting thing where your mockup area only takes up 1/4 of the available space, so you actually have room to write about it! You zoom in and out. It's neat.
http://www.mocklinkr.com/ - does have placeable annotations, but no gallery of pre-built bits to get going with
http://a.nnotate.com - annotation, free only 1 person
http://iplotz.com/ - trial account, snippets library, a bit hard to use
http://www.lucidchart.com/ - uses canvas, looks pretty good, lots of handy pre-built bits
http://www.justinmind.com/
https://cacoo.com - looks cool, collab
WOLF, 10:30am, Tuesday 3rd August
wolf website it written in WOLF.
forum - connect.wolfframeworks.com (written in Wolf).
2006
10k+ accounts
all data extractable, including app design
$7-$10/account/month
3-15 days development time
business rules let you call web services
…and you can send email/SMS
you can make up pages using Wolf widgets (done as iframes)
PayPal can set up a notification to another service
so, for example, PayPal can send a notification to a Wolf business rule
each business rule has a URL? no, but you can get to each business rule from query parameter
Wolf has a scheduler, built for tracking events between different Wolf applications; it's not scalable insofar as you could use it to poll external systems - they recommend writing a small program to check and then notify Wolf (they do provide example code for this).
is it all client-side then? no, you can have the server do logic too.
most complex apps: an artist patron registry; ecounting. Integration has not become complicated because everyone is supporting REST…
what's the non-JS story? there isn't one really.
Instead of doing work for money, you do what someone wants you to do for something you want from them. I thought about this after meeting Michael (???) from openmoney.org, which sounds like a flexible realisation of this simpler model.
The cool thing about working for perks is that you both give a service that's easy for you and don't charge the other person your markup. Even though, if you converted the exchanged goods into money at the going rate, someone might end up losing out, the point is that is up to the person asking for what they want to determine the relative value to them of what they are asking for. It's a reversal of the ordinary situation where the seller determines the value of the sold product on the scale of a currency.
WorkXpress call with Treff, 17:30-18:30 Friday 6th August
10 years in the making, customers since 2004
you can make an app and you can farm that out to others to sell or customise, which seems sophisticated
we maintain IP over app
we set own fees - they collect revenue share
we can private label all support functions e.g. password reset, billing
Treff says they have incredible support - we would be connected to a support person immediately who would help the project be developed (he's talking 1hr at start, 30mins twice in the next couple of weeks)
they are intending to partner with European clouds e.g. CloudSigma (Swiss)
applications get versioned
multitenancy within apps is possible (Treff suggests talking about this later)
you can sell the app from your own website without customers ever seeing WorkXpress
You are somewhat limited in how much you customise the HTML/CSS of a page. They want to expose the CSS.
Kinda slow
Integration with a 3rd-party service - you'd get something to use the API to create/edit/trigger
They've got an example done with PayPal - they're (Drew) going to send me the example on Monday.
Treff is going to provide me with some production apps trials so I can check performance
Open-sourced at: https://github.com/jayfresh/HSBC-ScreenScraper/tree
!Log
[[Writing a HSBC ScreenScraper 5th August 2009]]
[[Writing a HSBC ScreenScraper 6th August 2009]]
!Afternoon session (from 14:00 - 15:45)
#Setting up the application
** ''need to make sure that the jsAutomatic git submodule is initialized...''
** Using AjaxReq to provide local cross-domain HTTP requests - added to GitHub
** Using [[jsAutomatic]] to automate the interaction - added to GitHub
#Making sure [[jsAutomatic]] works outside a TiddlyWiki context and tweaking the JSSpec tests so it does
!Afternoon session (from 15:20 to 20:05)
#Setting up the steps to log into HSBC online banking and see if it works
** Having a problem with Firefox spinlocking after loading the first page - caused when using {{{writeln()}}} on the iframe in {{{HTMLParser.parseText}}} - fixed by removing scripts from responseText properly (wasn't picking up ones over multiple lines - removed line-breaks and carriage-returns)
** Have again discovered that jsAutomatic would be much better if the next steps were only evaluated at run-time i.e. they were described as functions, not objects - as it is, it's leading to unwieldiness
** I should probably put some tests together so you can see if the 'API' changes i.e. breaks the Automatic script
** Logging in works!
** Blogged a video at http://tinyurl.com/mpxbh2
#Improvement thoughts
** There's nothing handling any errors due to the wrong pages being loaded or wrong credentials being passed
I think that non-technical people can do an increasing amount that, previously, only technical people could do. I'm trying to create examples of non-trivial applications created without programming, and a directory of tools for people to use to do this.
Examples: see [[Mashups]] for now, until I get a better way of creating a list
Directory: see [[Hosted services]]
zoho call, with Mack, Mon 9th August 2010
200 people is Enterprise edition
ZC is a database app - you embed the forms and views in your own apps
Any views or forms can be made as custom HTML pages using Deluge scripts
They have a marketplace where you can post requirements
Mack is going to look into whether we can white-label the signup process (he said it would show the Zoho branding)
Mack is going to send a list of developer contacts
Best bit about Balsamiq is that is has a community of people contributing blobs - this is good thinking!
Something that I think is a good way to track how you spend your time. I suggest recording when you change what you're doing - change your context - rather than specifying the amount of time you spend on things.
If you record when you switch context, you keep a chronological record of how you've spent your time and it seems to be low hassle (possibly because you don't have to calculate how much time you've spent on something).
I think this discipline promotes something else which I think is important - [[monotasking]] - because you end up wanting to stick doing the same thing because you know you're going to have to record any change of context.
To design and build CSA's website.
Tracking using attention tracking tiddlers... display coming soon...
!Log
9th July 2010
12:30-12:40 - sorting out the site structure to automatically load the intro (i.e. making it 'index.htm')
8th July 2010
9:55 - 10:15 changing intro logo
6th July 2010
14:20 - 14:55 adding preload of images to the intro page
5th July 2010
10:50 - 11:10; 11:20 - 11:30; 12:20 - 12:25 Adding loading animation to site
2nd July 2010
14:20 - 14:50 making static site use fading, AJAX-y transitions
30th June
13:15 - 14:00 meeting about de Candole Robinson (not charging for this)
<<readLog>>
!The problem
You can't dm yourself on Twitter.
!Why would you care?
If you could dm yourself, you could build an app that took advantage of this as a SMS->web gateway, where you could get the data with your own Twitter credentials. If you have to dm a central service, the app needs knowledge of the central service's Twitter creds, which is a problem if you want to distribute the source for the app / run it as a self-contained local single-page web app (which is the kind of app I make a lot).
!The approach
I want to be able to dm a bot called [[dm2me|http://www.twitter.com/dm2me]] that will dm you straight back with what you sent. This way, the dm ends up in your inbox and you can get to it. This follows on from the initial exploration documented in TiddlySMS.
!Progress
The only missing piece is that I don't use the twitter id of the sender to send a dm back to the sender (obviously a key piece!); however, a major hurdle has been cleared as now I received emails to jnthnlstr@googlemail.com whenever anyone dm's [[dm2me|http://www.twitter.com/dm2me]]
!The flow so far
#person sends dm to dm2me
#Twitter sends email notification to dm2mebot@googlemail.com
#Google Mail forwards email to pipe@s2w.m.ac.nz
#s2w POSTs email to a Yahoo! Pipe, id [[ZsJuNEcr3hGw69BO3nBDOQ|http://pipes.yahoo.com/pipes/pipe.info?_id=ZsJuNEcr3hGw69BO3nBDOQ]]
#Yahoo! Pipes extracts subject, body of email and sent date and turns them into a RSS item with title, description and pubDate, and POSTs this to an AppJet app called [[PostIn|http://postin.appjet.net]] (I've introduced AppJet temporarily (it's a code-writing step - bad!) because I haven't figured out Tarpipe's REST API yet, so used email-in instead)
#AppJet records the POST and emails it to Tarpipe at the mailbox rise34ilka@tarpipe.net
#Tarpipe gets the email and sends it to jnthnlstr@googlemail.com and tweets the message using my [[jayfresh|http://www.twitter.com/jayfresh]] account - doesn't manage to dm the person who sent the tweet; I'm hopeful that using the REST API to run the Tarpipe workflow will sort that
!!In terms of [[data momentum]]
|''Step'' |''Momentum used to get to next step'' |
|Thought |me, using fingers |
|SMS |Vodafone, using their network |
|Twitter SMS Gateway |Twitter, using their software |
|Twitter datastore |SMTP |
|dm2me gmail inbox |Google, using their software |
|gmail email filter |smtp |
|smtp2web |??? |
|''something to get the right bits of text out''|REST |
|Tarpipe |HTTP |
|Twitter API |Twitter, using their software |
|Person's dm inbox|That's it! |
!Still to work out
----- old post -----
Frustratingly, I haven't been able to find a way to get the right bit of text of the email from Twitter and send that on to Tarpipe. The methods I've looked into are:
# sending email to Yahoo! Pipes, which could extract the message and create a message to send to a Web Service module, which would be Tarpipe. Unfortunately, Pipes doesn't take in emails, although they've [[picked up|http://suggestions.yahoo.com/detail/?prop=Pipes&fid=132942]] on my suggestion they support this.
# sending email to Tarpipe and letting Tarpipe extract the message before sending Twitter dm. Alas, there is no content extraction and processing on Tarpipe. Good news - they want to build this. See [[this|http://getsatisfaction.com/tarpipe/topics/manipulate_strings_in_incoming_emails]] on Get Satisfaction. And - they've [[just added|http://blog.tarpipe.com/2009/03/09/the-textinput-connector/]] a text input connector, so perhaps I can use regex with that to send the right string on...
# sending email to Appjet to process before sending on to Tarpipe. Downside, Appjet doesn't accept email.
----- old post -----
So, we have everything apart from how to get the right string of text out of the email from Twitter, which we send to Tarpipe. I'm thinking I could forward the Twitter email to smtp2web (if it ever worked, which it doesn't appear to), which could then POST the body to appjet; appjet could do the necessaries and then email Tarpipe.
For now, I'm grouping together websites that provide an all-in-one with atomic services.
|Name|Provides website?|Handle transactions?|Free?|
|BigCartel|Yes|Uses Paypal|$10/month to enable custom domains and email|
Also:
http://www.prostores.com/ - by eBay (US only I think)
http://smallbusiness.yahoo.com/ecommerce - possibly also only US
http://goodsie.com/ - shop-front, integrates with PayPal, Google Checkout
Just payments:
Google Checkout
PayPal
Amazon Simple Pay Standard - https://payments.amazon.com/sdui/sdui/business?sn=paynow/pn
Amazon checkout - https://payments.amazon.com/sdui/sdui/business/cba
!How things will be
Electronomad (as a thing) makes people feel more confident about becoming freelance/partnership web industry people. It also represents a resource for the lifestyle and a commercial brand associated with high quality, transparent and efficient web projects.
!How things are now
Electronomad only exists in my head and in the head of a few people I've told about it. I have an idea for a website, but nothing has been built. I haven't totally resolved how to present the commercial arm.
!How things will be
Frugal, controlled (but not too much), actively enjoying people's company and the city I live in.
!How things are now
Wanting to cook for people more, save money (why?), make more time for reading, not lose the work-life balance (I want to go to the Tate Modern), no clue about good bands
Issue tracker: http://fastdiff.lighthouseapp.com/
Interface design document: https://docs.google.com/document/edit?id=1Ey9B5FyDhaE3peBF1erECL4kbV-B9afqYKhOuo9VZdU&hl=en#
[[Documenting online banking API's]]
[[mobile bank transfer app]] design
Currently: How do you get a receipt for a bank transfer? (real-time payments)
This is interesting: http://www.ukpayments.org.uk/sort_code_checker/
Aden Davies (HSBC Innovations) put me on to these two things:
http://digitaldebateblogs.typepad.com/digital_money/2009/10/faster-faster.html
http://bankingblog.celent.com/?p=992
See [[ScreenScraping HSBC website]], [[Oyster Card payments]].
!!Things for website
client list e.g. dacre's
logos
-contractor+ scheme
-ccatf
-fmb
photos
!!Draft structure for website
Services
- maintenance
- something
Clients
Organisations/accreditations?
- FMB
- contractor plus
- CCATF
- world skills
Contact
- office
!! Log
[[19th February 2010 - Dad's IT problems]]
! The problem
Looking at something, can't read it, wondering if an application could help me out
! Use cases
* Bus stops
* Taking photos of phone numbers on e.g. restaurants or estate-agent boards and calling and/or saving them
* Adding events from posters to your calendar
! Approachs
1) on-device OCR and translation
with this, found that there is one company (abbyy.com) who do a SDK for on-device OCR, but it's expensive (like $50+ per device) and I have no idea about the performance
2) take a photo on the device and send to the web for OCR and translation
there are some open source OCR tools: http://delicious.com/jonathanlisterbtcom/ocr
! Constraints
# iPhone camera bad at small text because of focal length; see Snabiz and reviews panning performance (http://itunes.apple.com/WebObjects/MZStore.woa/wa/viewSoftware?id=295218938)
# you don't often find yourself in need of translating foreign text
# there are lots of apps for translating typed-in text, so why not just type in the text?
** unusual scripts (e.g. Chinese) make scanning more useful, 'cos you don't have the characters easily available
! Writing the app
It looks like PhoneGap is a good tool for allowing us to write in JavaScript but compile to run on mobile devices. http://phonegap.com
! Questions
!! Top 3 questions
* how do we optimize the OCR?
** see [[optimizing tesseract]]
* does the Wikipedia page - http://en.wikipedia.org/wiki/Optical_character_recognition - give us any alternative libraries to use?
* can we run the app entirely on the phone?
** is there a problem with using tesseract (et al.) because of Apple's proscription of "private API's"?
!! Other questions
* what's the cost of data when roaming?
** according to Phil Hawksworth, really expensive. No specifics yet
* Can we train tesseract to perform better?
** See [[training tesseract]]
* What happens with street sign photos, where there is often a bright sky background and a darker sign?
* what format does the iPhone save images as?
** we think JPG
* can we cope with cyrillic/greek alphabet/chinese/japanese/korean?
** Ben found CEDAR - http://www.cedar.buffalo.edu/ - mentions Japanese; needs looking into
* identify users - why they'd use it, in which scenarios, where are they from
** see this Google Doc for typed-up notes: http://docs.google.com/Doc?id=dgtzds63_8cx7z2tdx
* identify 5 scenarios in which people might use this - translating text
** see this Google Doc for typed-up notes: http://docs.google.com/Doc?id=dgtzds63_8cx7z2tdx
* what similar existing apps are there?
** OCR app Snabiz, bad performance due to iPhone camera (http://itunes.apple.com/WebObjects/MZStore.woa/wa/viewSoftware?id=295218938)
** Evernote app has OCR feature - ''need to try this one out''
** lots of translations apps using Google Translate
** skimbit project for similar apps - http://skimbit.com/BenLovedale/Mobile_Translator
* what are the ways you might use OCR and sending to the web in an app?
* figure out how to make PhoneGap work
** we did a workshop at Osmosoft about PhoneGap. Conclusion is that it lets you make UIWebView apps, so you do lose the native UI controls... there are libraries getting better at recreating these (MagicFramework).
* evaluate the open-source OCR tools from a speed and accuracy point of view with some photos taken on real mobile devices
** see [[testing OCR apps and tools]]
* see if there are open source translation tools we can make use of (I'm thinking Google - are there licensing problems with Google?) and how good are they?
* find out how the application submission process works for various online app stores and how long the processes take
* make a logo
* make a website
* choose a price
* think about features
! Features!
Oh yeah, what's this do? Is it literally "take a picture, choose your language to translate from and to and off you go?" Can we superimpose text back over the original?
Who for: Loic Dachary
RentACoder fee: 15%
Rate after RentACoder fee: €70 / hour
Rate before RentACoder fee: €82.35
Estimate of time: 9hrs 30mins
Total: €782.35
Status: on hold
! Description
Create a tiddlywiki plugin to switch languages. A hook can be registered to the plugin to allow a third party application to switch langage.
* User story: As a developer, I want to call a function that changes the language of jPoker and TiddlyWiki, where available, so that I can avoid having to distribute one file for each language
It can be an updated version of an existing plugin. The plugin must be designed to be used in the context of http://pokersource.eu/. The plugin code must be tested and each line covered.
* qunit for tests; or twUnit - see http://dachary.org/a/a.html for example
The plugin must be delivered in a tiddlywiki-2.3 page that runs the tests when loaded.
* jsCoverage for code coverage
A HOWTO integrate the plugin in an existing tiddlywiki must be provided.
The plugin must be published under a GPLv3+ compatible license.
! Deliverables
# Complete and fully-functional working program(s) in executable form as well as complete source code of all work done.
# Deliverables must be in ready-to-run condition, as follows (depending on the nature of the deliverables):
** For web sites or other server-side deliverables intended to only ever exist in one place in the Buyer's environment-Deliverables must be installed by the Seller in ready-to-run condition in the Buyer's environment.
** For all others including desktop software or software the buyer intends to distribute: A software installation package that will install the software in ready-to-run condition on the platform(s) specified in this bid request.
# All deliverables will be considered "work made for hire" under U.S. Copyright law. Buyer will receive exclusive and complete copyrights to all work purchased. (No GPL, GNU, 3rd party components, etc. unless all copyright ramifications are explained AND AGREED TO by the buyer on the site per the coder's Seller Legal Agreement).
! Spikes
# What is the difference between translating jpoker language and tiddlywiki language?
## The jpoker language is controlled (it seems) by config.localed and a json file for the language.
## The TiddlyWiki language is changed via the lingo file and local plugins contain the translations.
## The html file has the attribute of 'lang', which is used by the jpoker plugin - this is altered by the updateLanguageAttribute method in TiddlyWiki
*** {{{<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="it" lang="it">}}}
# What translations already exist?
| language | method |
| da | uses config.locale='da' in fakeTranslationPlugin and updateLanguageAttribute method; jpoker-da.json |
| de | as da |
| en | default - core lingo |
| en_CA | as da |
| en_US | as da |
| es | as da |
| fi | as da |
| fr | FrenchTranslationPlugin |
| fr_BE | as da |
| fr_CA | as da |
| fr_FX | as da |
| it | as da |
| ja | JapaneseTranslation. |
| nb | as da |
| nl | as da |
| pt | as da |
| sv | as da |
! Tests for user story (will be broken down)
** The hook should use the provided country code to call the functions that switch the jPoker language and the TiddlyWiki language
** The language switching functions should check to see whether the chosen language is available and use the default if not
** The jPoker language switching function should load the appropriate language json, invoke the
TiddlyWiki updateLanguageAttribute method and recreate the jpoker server
** The TiddlyWiki language switching function should apply the appropriate lingo file and refresh the on-screen content so that the text is translated
! Breakdown of work to satisfy user story:
# Research for estimate (this tiddler) - 1hr 30mins
# Describe the tests - 1 hr
# Set up the coverage harness - 30 mins
# Write the tests - 2 hrs
# Write the plugin to provide the hook to switch language - 30 mins
# Write the function to switch jPoker language - 2 hrs
# Write the function to switch TiddlyWiki language - 2 hrs
Open-sourced at: https://github.com/jayfresh/jsAutomatic/tree - wondering if I could make it run using SSJS...
Used to be known as [[ScreenScraping library]]
<<attentionTracker timeline kat>>
I open up my app, enter someone's sort-code and account number, an amount, a short reference and select "transfer". The money is transferred and I get a confirmation, which tells me my sort-code and account number in case I want to pass that on to my friend so they know it is me who has transferred them money. My friend, who is also using the app, gets a notification that there has been a new transaction for the same amount, from my sort-code and account number. If I have been added to their people list, the app mentions me by name - "Jonathan Lister has just paid you £22.50. The reference was 'TRAIN TICKET'". If I wasn't in my friend's people list, he is offered the option to save this account into her people list.
Another time, I want to remind someone that they owe me twenty quid, so I go to my mobile app and select "Pay up!" I'm asked to select someone from my address book to send a message to (that's my mobile address book), how much they owe me and what for. When I've done that and hit "poke them!", I'm told they will receive an email saying "We would like to remind you that you agreed to pay Jonathan Lister £20 for 'home shopping'. If you're using PayUp and reading this on your mobile, click the link below to make or schedule the transfer. If you're not using PayUp, you can get it here - PayUp brings online banking transfers to your mobile". When my friend, who is using PayUp, clicks the link in the email when using her mobile, PayUp boots up with a new transfer screen filled out with my details and my name automatically added to my friend's person list if I wasn't already. My friend can either make the transfer now or choose to schedule it to happen later. If they choose this, it uses the scheduling feature of her online banking rather than something built into the app. If her online banking doesn't provide scheduling, the app doesn't offer the option of scheduling.
Spikes:
* mobile apps at all!
* XHR
* running online banking API test pages
This is the opposite of multitasking - you concentrate on one thing at a time at the exlusion of everything else.
! Principles of any reward system
# Transparent
# Easy to understand
# Not gameable
# Doesn't create a sense of destructive competition; instead, creates a good feeling about being in a group
! Other considerations
# Tiering - allow for maybe three tiers - core tier who have all the initial power and have that replenished from time to time; voting tier who can distribute influence and rewards; right of passage tier, who can receive rewards but not influence
Also been reading about Hacker News, which uses voting and [[a discussion|http://blog.stackoverflow.com/2009/03/the-value-of-downvoting-or-how-hacker-news-gets-it-wrong/]] about downvoting by Stack Overflow - they make the point that downvoting is good if used correctly, so they add a ''cost'' to each downvote, where you lost 1 reputation point and the person you downvoted loses 2 - contrast this to the 10 reputation points you get if you are upvoted (which is free).
Further study of [[Stack Overflow]].
Further study of [[Advogato]].
Things: limited character set? greyscale? black and white? auto-levels? upping the black point?
!What have others done?
Reading the Google Group: http://groups.google.com/group/tesseract-ocr/
Found an interesting set of results on this post: "improving tesseract accuracy"
http://groups.google.com/group/tesseract-ocr/browse_thread/thread/f1691636212d43e/479e0a55857418ba?lnk=gst&q=improving#479e0a55857418ba
Followed this up on a b/w test image that was originally 496x76 pixels at 72 px/inch (don't know how it actually changes the image when you up the resolution, as neither image size nor pixel count change... I did notice that the depth went from 1 bit to 8 bits, with increased file size as a result, but that was only from the original image up to my test images - it never deviated from 8 bit after the initial conversion). The results:
|Filename |Size (px) |Resolution (px/inch) |Depth |Error count |
|bw_test.tif |496x76 |72 |1 |6 |
|bw_testb.tif |496x76 |72 |8 |1 |
|bw_test2.tif |496x76 |144 |8 |1 |
|bw_test3.tif |496x76 |300 |8 |1 |
|bw_test4.tif |992x152 |300 |8 |2 |
|bw_test6.tif |992x152 |144 |8 |2 |
|bw_test7.tif |992x152 |72 |8 |2 |
|bw_test8.tif |992x152 |288 |8 |2 |
|bw_testc.tif |496x76 |36 |8 |1 |
|bw_testd.tif |496x76 |36 |1 |6 |
This suggests a couple of things:
# tesseract handles 8 bit images fine; in fact, in this case with lower errors than 1 bit
# resampling to blow up image size can introduce artifacts
!!Post-processing - http://www.cs.toronto.edu/~mreimer/tesseract.html
Python script that attempts to replace similar-looking non-words with their real-word equivalents. Does not assume that all character strings are words. In that respect, it is different to giving tesseract a big dictionary. I tried it using a data set created by Michael and it broke. Emailed him to ask about error.
!!Whitelisting characters
From tesseract FAQ (http://code.google.com/p/tesseract-ocr/wiki/FAQ):
put this in a text file called tessdata/configs/digits: {{{tessedit_char_whitelist 0123456789}}}
and then your command line becomes: {{{tesseract image.tif outputbase nobatch digits}}}
Warning: Until the old and new config variables get merged, you must have the nobatch parameter too.
The order of the whitelist doesn't make any difference. I didn't test this much, but it didn't seem to help too much with an image that was already hard to read. ''Should test with better images.''
Tried this using the examples previously tabulated. Results using tesseract input output nobatch digits, where digits is:
{{{
tessedit_char_whitelist abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789
}}}
|Filename |Size (px) |Resolution (px/inch) |Depth |Error count |
|bw_test.tif |496x76 |72 |1 |7 |
|bw_testb.tif |496x76 |72 |8 |1 |
|bw_test2.tif |496x76 |144 |8 |1 |
|bw_test3.tif |496x76 |300 |8 |1 |
|bw_test4.tif |992x152 |300 |8 |2 |
|bw_test6.tif |992x152 |144 |8 |2 |
|bw_test7.tif |992x152 |72 |8 |2 |
|bw_test8.tif |992x152 |288 |8 |2 |
|bw_testc.tif |496x76 |36 |8 |1 |
|bw_testd.tif |496x76 |36 |1 |7 |
Compared to the examples above that didn't use the whitelisting, the error rates are almost identical and OCCUR IN THE SAME PLACE except for the worst-performing cases.
!!Turning off the dictionary
Found a post about turning the dictionary off, on the same "improving tesseract accuracy" thread as above: http://groups.google.com/group/tesseract-ocr/msg/cfa0f08e5f2b8431
<<<
Another thing I discovered - turning dictionary _off_ helps a lot in my case.
Write these lines to /usr/share/tesseract-ocr/tessdata/configs/nodict :
ok_word 0
good_word 0
non_word 0
Then run tess like this:
tesseract b.tif output /usr/share/tesseract-ocr/tessdata/configs/nodict
<<<
|Filename |Size (px) |Resolution (px/inch) |Depth |Error count |
|bw_test.tif |496x76 |72 |1 |7, but 1st half entirely correct |
|bw_testb.tif |496x76 |72 |8 |0 |
|bw_test2.tif |496x76 |144 |8 |0 |
|bw_test3.tif |496x76 |300 |8 |0 |
|bw_test4.tif |992x152 |300 |8 |0 |
|bw_test6.tif |992x152 |144 |8 |0 |
|bw_test7.tif |992x152 |72 |8 |0 |
|bw_test8.tif |992x152 |288 |8 |0 |
|bw_testc.tif |496x76 |36 |8 |0 |
|bw_testd.tif |496x76 |36 |1 | 7, but 1st half entirely correct|
This is the first time I've got a flawless transcript out of this image. Definitely worth investigating whether disabling the dictionary (if that is indeed what is happening) is a good practice.
!!Whitelisting AND turning off the dictionary
I believe you can run tesseract without the dictionary and with a whitelist like this:
{{{
tesseract input output /usr/local/share/tessdata/configs/nodict digits
}}}
The effect on full_sign3.tif (a colour image), relative to scanning with a dictionary:
With whitelist: small improvement recognising numbers
With no dictionary: words recognised worse
With no dictionary and a whitelist: similar worse performance as "With no dictionary"
In this case, the best performance came from a whitelist and a dictionary.
The effect on full_sign3b.tif (a greyscale image):
With no dictionary, no whitelist: missed some numbers and read some words badly
With dictionary, no whitelist: improved many words and numbers
With whitelist, no dictionary: improved some numbers and words from no-whitelist case
With whitelist and dictionary: best performance
The effect on full_sign3c.tif (a raised-black-point image):
No dictionary, no whitelist: improvement on greyscale performance, some word and number errors
No dictionary, whitelist: improved number recognition
Dictionary, no whitelist: improved word and number recognition
Dictionary, whitelist: almost identical to "Dictionary, no whitelist"
!!User dictionary
From same FAQ (http://code.google.com/p/tesseract-ocr/wiki/FAQ):
Replace tessdata/eng.user-words with your own word list, in the same format - UTF8 text, one word per line.
Google group posts report tesseract ignoring user-words. There is some other stuff about increasing the strength of the dictionary:
<<<
Try upping NON_WERD and GARBAGE_STRING in dict/permute.cpp to maybe 3 or even 5.
If the text fonts you are recognizing are significantly different from your training data, and you don't mind a slow-down, you could also try lowering ClassPrunerThreshold in classify/intmatcher.cpp to about 200 from 229. These measures should all improve the power of the dictionary to resolve words from non-words.
Of course any changes that up the power of the dictionary also up the ability to hallucinate dictionary words. If this is a problem, keep short words out of your dictionary, and don't add a vast list of words that are rarely used if they increase the number of ambiguities with more frequent words.
<<<
Experimenting with upping NON_WERD and GARBAGE_STRING in dict/permute.cpp (from 1.25 and 1.5, respectively) to 3. Requires a re-compile. Have run into a problem I had previously where my compiled tesseract only produces rubbish. Have emailed Dan to ask him to send me transcript of compile messages. Found this post that indicates compiling with libtiff support causes problems: http://groups.google.com/group/tesseract-ocr/browse_thread/thread/bc687b07cac549ed/1aeda6427e0576cf?lnk=gst&q=problem+compiled#1aeda6427e0576cf I then realised I have libtiff on my mac... Found that you can compile without libtiff by running (''need to check this''):
{{{
./configure --without-libtiff
}}}
/***
{{{
Usage: <<parseToDo [summary]>>
}}}
Description: this plugin looks at a tiddler's text for strings like " - get this thing planned" and converts them into to-do items you can check off.
Requires: CheckboxPlugin: http://www.tiddlytools.com/#CheckboxPlugin
BUGS:
- doesn't seem to create checkbox syntax correctly if there are parantheses in the tiddler title<br />
Ideas:
- create summary display of all to-do's according to tiddler
- support "parent" to-dos e.g.
-- this is a child to-do
- be able to show a list of to-dos
-- for that, perhaps I need to create the to-do tiddlers even when the checkbox is not checked
-- alternatively, I could just search using the regex
***/
//{{{
config.macros.parseToDo = {
regex: /^ *?- *([^ ].*)$/img,
parseTiddlerText: function(tiddler) {
var toDos = [];
var text = tiddler.text;
var toDoMatchRegex = config.macros.parseToDo.regex;
var matches = toDoMatchRegex.exec(text);
var match;
while(matches) {
match = matches[1];
toDos.push([match,toDoMatchRegex.lastIndex-match.length,toDoMatchRegex.lastIndex]);
matches = toDoMatchRegex.exec(text);
}
return toDos;
},
replaceBody: function(place,toDos,tiddler) {
var text = tiddler.text+"\n-----\n";
var replaceString, replaceCount, toDoTitle;
var accumulator = 0;
for(var i=0, il=toDos.length, toDo, start; i<il; i++) {
toDo = toDos[i];
toDoTitle = tiddler.title+" [todo] : "+toDo[0];
replaceString = "[["+toDo[0]+"|"+toDoTitle+"]] [ ("+toDoTitle+"|Done)]";
replaceCount = replaceString.length-toDo[0].length;
start = toDo[1]+accumulator;
text = text.substring(0,start)+replaceString+text.substring(start+toDo[0].length);
accumulator += replaceCount;
}
text = text.replace(/<<parseToDo>>/,""); // JRL: could pass macroName in from the handler
text = text.replace(/<<.+?>>/g,"//macro disabled//");
place.innerHTML = "";
wikify(text, place, null, tiddler);
},
handler: function(place,macroName,params,wikifier,paramString,tiddler) {
var cmd = params[0];
if(!cmd) {
var toDos = this.parseTiddlerText(tiddler);
this.replaceBody(place,toDos,tiddler);
} else if(cmd=="summary") {
this.createSummary(place);
} else {
wikify("usage: <<parseToDo [summary]>>", place);
}
},
createSummary: function(place) {
// JRL: this only looks at tiddlers that have the "parseToDo" macro call in them. I can't just look for tiddlers tagged with "to-do", since at the moment they don't get created at parse time
var regex = config.macros.parseToDo.regex;
var output = "", text, matches, match;
store.forEachTiddler(function(t, tiddler) {
text = tiddler.text;
if(text.indexOf("<<parseToDo>>")!==-1) {
matches = regex.exec(text);
while(matches) {
match = matches[1];
output += "|[["+tiddler.title+"]]|"+match+"|"+tiddler.modified.formatString("DD MMM YYYY")+"|\n";
matches = regex.exec(text);
}
}
});
if(output) {
output = "|Tiddler|To-do|Tiddler Modified Date|\n" + output;
wikify(output, place);
} else {
wikify("no to-do's found", place);
}
},
init: function() {
setStylesheet(".logEntryMatch, .logSummary { padding:2px; border: 1px solid #88bbff; background-color: #fff8be; }");
}
};
//}}}
[[PropertyWithJ&J]] general work.
[[Automating testing of PJJ]]
!How things will be
My business sells several products that make people powerful. These run with minimal intervention from people.
!How things are now
I have some ideas for products e.g. [[DesignView]] and [[fee-free micropayments]]. I have no understanding of how to automate the parts needed to run a product.
It seems to me that any tool that is being used to help a group collaborate on a project ought to follow some common guidelines.
Start with the individual: make the person's experience useful to them as a tool that makes them productive; collaboration is secondary to individual productivity
Make everything social: every piece of content in the tool should have a URL, so it can be a social object and benefit from any service that operates on URL's
It should be cheap to add content: adding new content should by easy and not require jumping through a set of intellectual hoops such as "is this a calendar item?", or "what does this link to?"
/*{{{*/
/*
read tiddlers are stored in config.options.txtReadTiddlers as a list of tiddler names separated by '~~'
NB: this plugin will have inaccurate behaviour if there are tiddlers with '~~' in their titles
*/
(function() {
var $ = jQuery;
var readCountStylesheet = "readCountStylesheet";
var cookie = "txtReadTiddlers";
var readCountHTML = '<div class="readCountContainer">' +
'<div class="progressBar">' +
'<div class="progress">progress through this document</div>' +
'</div>' +
'<div class="counts">' +
'<p class="read"><a class="count"></a>read</p>' +
'<p class="unread"><a class="count"></a>unread</p>' +
'</div>' +
'</div>';
$.twStylesheet(store.getRecursiveTiddlerText(readCountStylesheet,"",2), {"id":"readCountStylesheet"});
var plugin = config.macros.readCount = {};
// JRL: turn option into JS object and maintain it, but write to option as well
var readTiddlersRaw = config.options[cookie] ? config.options[cookie].split("~~") : [];
var _display = Story.prototype.displayTiddler;
Story.prototype.displayTiddler = function(srcElement,tiddler,template,animate,unused,customFields,toggle,animationSrc) {
var title = (tiddler instanceof Tiddler) ? tiddler.title : tiddler;
if(readTiddlersRaw.indexOf(title)===-1) {
readTiddlersRaw.push(title);
config.options[cookie] = readTiddlersRaw.join("~~");
saveOptionCookie(cookie);
$('.readCountContainer').each(function() { // JRL: instead of using TW refresh mechanism
plugin.refresh(this);
});
}
_display.apply(this, arguments);
};
plugin.createPopup = function(elem, readTiddlers, unreadTiddlers) {
var popup = Popup.create(elem);
var titles = $(elem).parent().text().indexOf("unread")!==-1 ? unreadTiddlers : readTiddlers;
if(popup) {
var lingo = config.views.wikified.tag;
var openAll = createTiddlyButton(createTiddlyElement(popup,"li"),lingo.openAllText,lingo.openAllTooltip,function() {
var tiddlers = story.displayTiddlers(null,titles);
});
createTiddlyElement(createTiddlyElement(popup,"li",null,"listBreak"),"div");
for(r=0; r<titles.length; r++) {
createTiddlyLink(createTiddlyElement(popup,"li"),titles[r],true);
}
}
Popup.show();
};
plugin.updateCounts = function(elem) {
var tiddlers = store.getTiddlers("modified", "excludeLists");
var readTiddlers = [];
var unreadTiddlers = [];
// JRL: not all tiddlers in readTiddlersRaw list may still exist, so we check
if(readTiddlersRaw) {
var title;
$.each(tiddlers, function(i, tiddler) {
title = tiddler.title;
if(readTiddlersRaw.indexOf(title)!==-1) {
readTiddlers.push(title);
} else {
unreadTiddlers.push(title);
}
});
} else {
// JRL: handle no read tiddlers case
}
var readCount = readTiddlers.length;
var unreadCount = unreadTiddlers.length;
var percentProgress = Math.ceil((readCount/tiddlers.length)*100);
$(elem)
.find('a.count')
.css('display','inline') // JRL: so this looks nice in the TW Sidebar
.click(function(e) {
e.preventDefault();
plugin.createPopup(e.target, readTiddlers, unreadTiddlers);
return false;
})
.end()
.find('div.progress')
.css('width',percentProgress+'%')
.attr('title',percentProgress+'%')
.end()
.find('.read .count')
.text(readCount)
.end()
.find('.unread .count')
.text(unreadCount);
};
plugin.handler = function(place, params) {
$(place).html(readCountHTML);
plugin.updateCounts(place);
};
plugin.refresh = function(elem) {
plugin.updateCounts(elem);
}
})();
/*}}}*/
/*{{{*/
.readCountContainer {
font-family: "Andale Mono", Courier, monospace;
font-size: 16px;
font-variant: small-caps;
}
.readCountContainer .progressBar {
background-color: #e6e6e6;
width: 100%;
height: 1em;
}
.readCountContainer .progressBar .progress {
background-color: #66ccff;
height: 100%;
width: 0;
white-space: nowrap;
overflow: hidden;
}
.readCountContainer .counts p {
margin: 0;
}
.readCountContainer .counts .count {
padding-right: 0.5em;
}
/*}}}*/
/***
Usage: {{{<<readLog>>}}}
Description: this looks through the tiddler it is used inside for time entries. It will read strings such as "3hrs", "15:00 - 17:00" (24hr clock). When a line in a tiddler includes one of these entries, it will be marked up with the number of hours found so you can check correctness.
Annotations will appear next to entries to show they've been found. Because this process involves re-wikifying the tiddler text, macros are disabled in the display of this tiddler.
A summary of the log will be written in place of the macro.
This is designed to read times of hours and minutes. It ignores seconds.
***/
//{{{
config.macros.readLog = {
parseTiddlerText: function(tiddler) {
var entries = [];
var text = tiddler.text;
var timeMatchRegex = /(\d+) ?(?:hrs|hr)(?: ?(\d+) ?(?:mins|minutes|min))?|(\d{1,2}):(\d\d) ?(?:-|to) ?(\d{1,2}):(\d\d)/img;
var matches = timeMatchRegex.exec(text);
var match, minutes, from, to;
while(matches) {
match = matches[0];
console.log(match,matches.index,timeMatchRegex.lastIndex);
if(match.indexOf('hr')!==-1 || match.indexOf('min')!==-1) {
minutes = matches[1]*60 + (matches[2] ? matches[2] : 0);
}
if(match.indexOf(':')!==-1) {
from = new Date(0);
from.setHours(matches[3]);
from.setMinutes(matches[4]);
if(parseInt(matches[5],10)<parseInt(matches[3],10)) { // we've gone over midnight
to = new Date(1000*3600*24);
} else {
to = new Date(0);
}
to.setHours(matches[5]);
to.setMinutes(matches[6]);
minutes = (to-from)/(1000*60);
}
entries.push([match,minutes,timeMatchRegex.lastIndex]);
matches = timeMatchRegex.exec(text);
}
return entries;
},
createSummary: function(place,entries,tiddler) {
var text = tiddler.text+"\n-----\n";
var insertBefore = "{{logEntryMatch{found ";
var insertAfter = " minutes}}}";
var insertCount = insertBefore.length+insertAfter.length;
var accumulator = 0;
var summary = "{{logSummary{\n|entry|minutes|\n";
var totalMinutes = 0;
for(var i=0, il=entries.length, entry, start; i<il; i++) {
entry = entries[i];
start = entry[2]+accumulator;
text = text.substring(0,start)+insertBefore+entry[1]+insertAfter+text.substring(start);
accumulator += insertCount+entry[1].toString().length;
summary += "|" + entry[0] + "|" + entry[1] + "|\n";
totalMinutes += entry[1];
}
summary += "|total|"+totalMinutes+"|\n}}}";
text = text +"\n" + summary;
text = text.replace(/<<readLog>>/,""); // JRL: could pass macroName in from the handler
text = text.replace(/<<.+?>>/g,"//macro disabled//");
place.innerHTML = wikifyStatic(text);
},
handler: function(place,macroName,params,wikifier,paramString,tiddler) {
console.log(place.parentNode.innerText || place.parentNode.textContent);
var entries = this.parseTiddlerText(tiddler);
this.createSummary(place,entries,tiddler);
},
init: function() {
setStylesheet(".logEntryMatch, .logSummary { padding:2px; border: 1px solid #88bbff; background-color: #fff8be; }");
}
};
//}}}
config.macros.saveDefaultTiddlers = {
label: "save view as default",
prompt: "click to save view as default"
};
config.macros.saveDefaultTiddlers.handler = function(place) {
createTiddlyButton(place,this.label,this.prompt,this.onClick);
};
config.macros.saveDefaultTiddlers.onClick = function(e)
{
var links = [];
story.forEachTiddler(function(title,element) {
links.push(String.encodeTiddlyLink(title));
});
var body = links.join("\n");
var t = store.getTiddler("DefaultTiddlers");
store.saveTiddler(t.title,t.title,body,t.modifier,t.modified,t.tags,t.fields,false,t.created);
displayMessage('DefaultTiddlers saved');
autoSaveChanges();
return false;
};
Person: Paul Bacchus
He's going to talk to me about what this is all about.
SweetSpot Phase 6 - see http://hoster.peermore.com/recipes/sweetsoft-editor/tiddlers.wiki#%5B%5BPhase%206%5D%5D
PayPal integration
- Pay Now buttons are simple enough to set up - I'm testing them at the moment. I'm looking for a field that will get passed along to the notification/transaction details that I can use for the house address. I'm also assuming holding deposits are always the same.
- It looks like you have to create a new button for each house as the custom variables are saved on the server... looking into passing along HTML hidden inputs from the client (which is less safe but more flexible).
-- there's an option to redirect to a page if the checkout is cancelled - ''should support this''
-- need a ''thank you page''
-- need to ''set image_url to SweetSpot logo - Josh making appropriately sized logo''
- PayPal has a sandbox for testing the buttons and the payment notifications out...
See SweetSpot
On the subject of WordPress MU suitability - got in touch with page.ly to ask if they'd be able to help with local->test->live scenario, and also if they'd generally be useful.
//Testing the idea of "to-do's as custom-post-types"//
- Figuring out how to schedule to-do creation as a reaction to property status changes and other things
- Feasibility of scheduler+to-do post type
//Dynamic account creation//
- Figuring out how to create a student account dynamically
//(Student account) Creation when booking a viewing//
- Booking system needs to send notification
- Process notification, create account not already created and store details about viewing
<<parseToDo>>
Having done the below, there is still an open question of what fields to send. I think this will primarily be driven by what I need to create a new account when receiving the message.
-----
The notification needs to be sent to a URL, which can be set in the admin section for each account. I'd like to be able to re-use this notification mechanism to send emails to a customisable group of people. One thing that I think I could do is use Notify.io - you'd configure your list of people you want to notify in the booking app's settings, and then you'd choose in Notify.io whether you get notified over email or webhook.
What's the downside of using Notify.io? I think it would be having another system involved. What's the alternative? Notifying directly from the booking app. This means I'd need to send email using the booking app. Can I do this on Smart Platform? Looks like not directly, though perhaps it's easy to send using the person's gmail address... nope, it doesn't look like it. A quick Google for alternative ways of sending email via API: http://elasticemail.com
There's also: http://emailyak.com/, http://www.programmableweb.com/apitag/smtp and http://www.programmableweb.com/apitag/email worth looking over.
Thinking about it, I think the first thing to do is add the generic capacity for the booking system to notify URL (or a set of URL's) when a booking happens.
Also, checking out http://tungle.me as an alternative to my booking app, as it might make system maintenance a lot easier. Dependent on: being able to get a notification when someone books a meeting; being able to set availability rather than use 24 hours (It would be handy if the interface managers used to set their availability could be branded SweetSpot, or be one of their Google Calendars); being able to get a list of which slots are available (so we can show them in the booking form)
Turns out I've sort of forgotten how my booking app is put together. I suspect that writing a test suite for the new functionality would be a good idea. It is certainly helping to look back at the original test suites. So, I want to test:
* admin view
** an endpoint for a form to POST to, to add a new URL for notification when events are booked
** a similar endpoint for deleting a URL
** a listing of endpoints
* notification
** that all the URL's in an account's list are called when an event is booked
Hitting a problem testing - "403 Unauthorized access to blocked host" when trying to use system.http.request locally... testing remotely instead
''need to do something about making sure people are logged-in when they are accessing the admin section''
Having figured out that hooking into property status changes is possible, it's now a question of building an example. I have to define what a to-do post type looks like and what determines whether it is scheduled according to a date in the future or a property status-change event.
according to our to-do scheduling [[mockup|https://docs.google.com/Doc?docid=0AQQJ7FGUGIp_ZHI0NG5mY18xOTdjdjZ4cjdnZg&hl=en]], a to-do has these elements:
* status it's attached to
** we've imagined a to-do being attached to one status change, so a drop-down list would work here
* description
** this is a text box
* assigned to (manager/applicant(s) - how will this be done?)
** you can get the applicants for a property from the property post
** you can get a property's manager from the property post
* due x days after status activated or due y days before <list of choices>
** stored as a flat date (to begin with; see below)
** in the scheduler, there would need to be a mapping between the choice and how you get the date
*** birthday - pulled from a property's tenants records
*** expiration of tenancy agreement - pulled from property record
* done
** this could be a checkbox
Thought: it seems property statuses could be moved through in more than one way e.g. if tenants references are accepted before security deposits are paid. It's looking like a bad idea to enforce a linear motion through statuses. Perhaps they're more like checkboxes - you can get your ticks in any order. You set the chronology through the due field of a to-do. So maybe what's happening is that the different you do are triggering hooks and these are what to-do's hang from - which means you'd need an event to register as a hook to begin with, so you can produce a list of these things when you're scheduling to-do's... but you could also hard-code it...
Going to have a look for wordpress to-do plugins... couldn't find an inspiration
Summarise Josh & I's chat about to-do's - choice between static due date field & dynamically-created field: use for dynamic due dates are: global updates to how a due date is calculated; being able to see where a due date has come from. We think: global updates could trigger a global batch update; being able to see where a due date has come from could be done with static information fields. So, in order to keep to-do post structure simple, due date will start off being a flat date field - the post's date field (i.e. when it's published).
When a post gets to its published date, that does trigger an action: publish_post
A good reference on scheduling: http://holisticnetworking.net/plugins/2008/10/18/scheduling-with-wordpress-cron-functions/
We made the decision to stick with user accounts only, with [[custom fields|http://justintadlock.com/archives/2009/09/10/adding-and-using-custom-user-profile-fields]].
So, how to create a new account dynamically? See here: http://codex.wordpress.org/Function_Reference/wp_create_user
A related topic - [[creating posts|http://www.webmaster-source.com/2010/02/09/programmatically-creating-posts-in-wordpress/]]
...and how to add custom fields to each user: using update_usermeta, after getting the user id back (or hooking into the user_register or profile_update action)
This could be about using HookPress... which is a webhooks layer on top of WP actions & filters. So maybe this is just about actions & filters.
Updating status = calling update_post_meta. Validated that the edit_post action isn't called when an update_post_meta is called. I'll need to think about other actions we could trigger.
There are custom hooks (http://digwp.com/2009/09/wordpress-action-hooks/) where you can create new actions, but they require initialising on a theme template, so you'd need to initialise them on each endpoint. I think. Really, I want functions to execute when stuff like update_post_meta is called...
Turns out that update_post_meta fires update_post_meta action and add_meta fires added_post_meta action.
So the flow for scheduling a new to-do as a reaction to property status change is:
something calls an endpoint -> endpoint changes property status (or some other field) -> update_post_meta action called -> handler figures out which to-do to create, based on to-do rules -> a function creates a to-do with the publish date set to the future
''synchronous / asynchronous??''
''how are to-do creation rules specified?'' - a to-do is set either for "x days after this is activated" or "x days before a date"; to-do's are all assigned for a property's status
http://codex.wordpress.org/Post_Status_Transitions - this shows that post statuses can be anything, not just private/public. There are actions for each status transition.
Event info going to be stored as an "event" post type for now.
Account username should be the email address.
Creating accounts - http://codex.wordpress.org/Function_Reference/wp_create_user
Processing is supported now. Chaned booking system to send "booked_by" and "address" parameters
need to add my account creation functions into the version of the SS Josh has set up
''see what happens if you try to create an account with an email address that exists for another account (with a different name)''
!How things will be
Good set of tools for people working on web projects. These are easy to use, and cheap or free. The tensions between people with different skills and experiences are resolved. [[Productively]] is useful as a system.
!How things are now
Bits of things are put together (PixelPatternMaster, DesignView), project tracking system is being developed through experience of using it.
Sourced from a Google Blog Search for "ocr service images":
|Tool |Languages |Tested with blurry image |Tested with cleaner image |Cost |Commercial use? |Time taken to OCR |Notes |
|Tesseract (http://code.google.com/p/tesseract-ocr/) |Vietnamese, Fraktur (Old German), Portuguese, Dutch, Spanish, German, Italian, French, English, training for others said to be possible |? |? |open source |yes |? |C++ lib |
|http://www.scribd.com |n/a |n/a |n/a |n/a |n/a |n/a |only uses documents |
|https://www.ocrterminal.com |English |awful - practically 0% |good - about 80% |Free for 30 a day |? |10-15 seconds |Emailed to ask about other (inc. Eastern) languages; more expected later in 2009 |
|http://www.abbyy.com |184 languages |not tested |not tested |don't know |? |? |Email conversation with 'dave'; asked for iPhone SDK for testing purposes |
|http://scanr.com |English, Japanese |... |very good - 95% |$2.50/month or £15/year for personal; for commercial, quoting from email: "The simplest mechanism for scanR remains a monthly service fee - we charge $5/month/user at the 'retail' level, and discount down to about $2/month/user in volume. A one-time $30/user fee at retail also works and could be discounted in volume. We probably can't afford to provide our service for a one-time $3/user fee." |Personal as standard; commercial - yes |About 1 minute |Has email in/out; have emailed support to ask about commercial use; kimberly suggested I mail info@scanr.com; have emailed info@scanr.com to ask about direct use of the library in the app and use of the hosted service commercially; Andy Mutz talking to me about licensing hosted service commercially; they are building their own iPhone app! Andy acknowledges problems with iphone camera and expects next iphone to have higher resolution camera; they do auto-repair of "mild blur" |
|http://www.p2escan.com/ocrtool.aspx |... |... |... |$40/month for "many images" |... |Didn't finish... |Java app; weird aggregate file list for all users |
|https://www.qipit.com |? |... |... |? |? |? |isn't OCR! |
|http://www.leadtools.net/ |Roman and Cyrillic alphabets (in the demo anyway) |... |not bad - 60% (could be optimized?) |? |10-20 seconds |? |Web services; asp.net client demo; lots of options to optimize OCR |
|http://primeocr.com |11 European; CJK; Russian |? |? |$1500/$4500 |Yes |? |runs on Windows; Alex Dahl is the contact |
|http://snapter.atiz.com |n/a |n/a |n/a |n/a |n/a |n/a |not OCR! |
Others to check:
Evernote, gocr, google - email attachment as pdf then "view as HTML", Acrobat, SimpleOCR (has a SDK for ActiveX and/or VB/C++), Wikipedia has long list - http://en.wikipedia.org/wiki/Optical_character_recognition, Omnipage (http://www.nuance.com/omnipage/)
Saalim Chowdbury
via Chris Adams
Mashups...
At workshop 4/5/09.
Reading Google's 'training tesseract' page - http://code.google.com/p/tesseract-ocr/wiki/TrainingTesseract
* Suggests training for Chinese not so easy 'cos there's loads of characters
Also, 1st-hand experiences of someone doing the training - http://www.win.tue.nl/~aeb/linux/ocr/tesseract.html
Took a bunch of photos with the iphone of street signs, shop signs, road signs and public service signs.
Figured out how to use a shell script and [[ImageMagick|http://imagemagick.org]] to batch convert images from the iphone to .tif files and then run them through tesseract. Something that I hadn't thought about was the exif orientation tag, which needed to be stripped out and the photo rotated, so that it was vertical, since tesseract will not rotate before attempting to read. Makes for badness.
The combination of, in Preview, auto-levels, saturation zero and slight increase of the black point gives better results. We don't yet know which of these makes the most difference.
Tried doubling the size of an image to see if it helped, and in this case, it got worse. Didn't try greyscaling and adjusting that one though...
!Further plans
#Try out training to see if it improves things for common fonts
#See whether use of different dictionaries helps
#Read up on tesseract google group to see if anyone has comments about need for training
#Try out 1-bit b/w to see if that helps
##play with tolerance
#Script the use of image magick to adjust the image before using tesseract
#Automation of testing results?
#See if iPhone SDK has spellcheck, or if there are libraries to do it; how does spellcheck get used to help out?
Why not beam the measurements directly to the web?
http://whoscelebratingtoday.com - example using DabbleDB to present information about public holidays around the world
!Log
Found this from Paul from ages ago, should follow-up mailing list suggestion:
http://mail.google.com/mail/?shva=1#search/companies+house/1245229a649118ba
Leigh Dodds - leigh.dodds [at] talis [dot] com - dropped an email - having a conversation about uses of RDF/RDFa - going to arrange to meet up
Jeni Tennison - jeni [at] jenitennison [dot] com - sent an email - and one more now the site's launched
Robert Brook - robertbrook [dot] com [at] gmail [dot] com - send an email - one more now site's launched
Working on [[wiki-data matcher]] with Chris Dent
Andrew Dancy (@a_dancy) sent me some material that he gets from [[RiskDisk|http://riskdisk.com]] that do company-information checking for fraud purposes (amongst other things). Had an idea about using wiki-data to provide a free small-business service for company information checking using the public data. Have emailed to ask him to put that on the ideas table. Shown [[wiki-data matcher]], waiting for response.
@monkchips said he couldn't get it to work - sent a dm asking what's wrong
Dan recommended speaking to Emma and James (@coupde) from rewired state to see what they thought of this new data source.
@delineator - meeting @ Hackspace on Thursday - he was team lead for open companies house at rewired state
Ian from Talis - have emailed to see what they think of this effort from a semantic web point of view (@psd suggested they'd be interested in helping) - had a twitter conversation and Ian said he was very interested
messaged @ldodds as @iand is going on holiday
PSD suggested getting on the semantic web IRC channel; should get on the open data one as well (if it exists)
@libbymiller wanted to do linked-data on directors but companies house is £1 a shot, so Avox's licence could come in handy
> libby: so one thing is that its a total nightmare to know if you have the company you are interested in onthe companies house site
> libby: so whether something is 'active' or not would save people money
> <jayfresh>what do you think jeni would make of wiki-data's data?
> libby: <libby>I'm not sure what she is working on right now - but it looks like pretty good data to me, and she's got interesting contacts at opsi / govt
> libby: <libby>anyway leigh is a good starting point too
> libby: <libby>delineator knows jeni too
> libby: <libby>delineator works with @robertbrook
> libby: <libby>putting parliament data online
> libby: <libby>wonder if they use companies hosue ids
> libby: <libby>http://hansard.millbanksystems.com/
> jayfresh: ooh
> libby: ah leigh is at ISWC in teh uS
James Darling (@abscond nee @coupde) - sent dm
http://github.com/cdent/wikidatamatcher
Point: free basic fraud-protection using the public data at wiki-data.com
!Developing - this is out-of-date now - put up in modified and expanded form on http://wiki-data.com/pages/api
!! JSONP requests
Chris is looking at adding jsonp to the wiki-data API so we don't have to use a proxy to make AJAX requests to the JSON API
** done
{{{
http://0.0.0.0:8080/search.json?q=whatever&jsonp_callback=myCallback
or
http://0.0.0.0:8080/search.json?q=whatever;jsonp_callback=myCallback
}}}
You can also use an ACCEPT header in the HTTP request, so you can just do:
{{{
Accept: application/json
http://0.0.0.0:8080/search?query&jsonp_callback=myCallback
}}}
!!! Request query format
You can specify the fields to filter on in two different ways:
!!!! key/value pairs
E.g. to search for all companies with the word "bank" in their name, operating in London, you can issue this query:
{{{
http://0.0.0.0:8080/search?query&jsonp_callback=myCallback&q=bank&operational_city=London
}}}
!!!! field/value mapping
E.g. to issue the same query:
{{{
http://0.0.0.0:8080/search?query&jsonp_callback=myCallback&q=bank&adv_0_field=operational_city&adv_0_value=London
}}}
This becomes useful if your inputs cannot provide the key/value query string that you need.
!!Fields to search by
You can search the public data by any of the following fields:
|''field''|''query string parameter''|
|AVID|q|
|Legal Name|legal_name|
|Previous Name(s)|previous_name_s_|
|Trades As Name(s)|trades_as_name_s_|
|Trading Status|trading_status|
|Company Website|company_website|
|Registered Country|registered_country|
|Operational PO Box|operational_po_box|
|Operational Floor|operational_floor|
|Operational Building|operational_building|
|Operational Street 1|operational_street_1|
|Operational Street 2|operational_street_2|
|Operational Street 3|operational_street_3|
|Operational City|operational_city|
|Operational State|operational_state|
|Operational Country|operational_country|
|Operational Postcode|operational_postcode|
!!!Country/state codes
When using any of the fields below in a request, you need to provide the 2 or 3 letter ISO 3166 codes for the country/state you want.
There is a JavaScript library at http://github.com/jayfresh/ISO_3166 which contains the mappings you need.
|field|code to use|reference|
|registered_country|ISO 3166-1 alpha 3|http://en.wikipedia.org/wiki/ISO_3166-1_alpha-3|
|operational_country|ISO 3166-1 alpha 3|http://en.wikipedia.org/wiki/ISO_3166-1_alpha-3|
|operational_state|ISO 3166-2|http://en.wikipedia.org/wiki/ISO_3166-2|
!!Response record structure
The data that is returned from by a JSON search has this structure:
{{{
[
{
title: 12345678, // AVID
fields: {
legal_name,
previous_name_s_,
trades_as_name_s_,
trading_status,
company_website,
registered_country,
operational_po_box,
operational_floor,
operational_building,
operational_street_1,
operational_street_2,
operational_street_3,
operational_city,
operational_state,
operational_country,
operational_postcode
},
// other meta-data fields about the record itself
},
...
]
}}}
September 30th, from 15:30 -
#Talking about data exposure
** Personal relationships with people to keep things low-risk
** Building to tiered access
#Getting the data out
** Greg needs to extract full data extract and another extract with the audit trail
#Mental search!
** http://217.9.192.9:8080/search?q=bank+of+america
*** Greg - perhaps something to do with the UTF-8 characters in the Polish name?
# what we do next
** ''copy any extra fixes and enhancements across to my list in [[Feedback on wiki-data phase 1]] from [[this Google doc|http://spreadsheets.google.com/ccc?key=ttRWnqY-yzX8wHVlQpo4wWw]]''
** developer access - dependent on extract and control
** access controls are going to be needed for both developer use and "my-wiki-data"
** we will try to do a developer event in parallel with creating my-wiki-data
Splitting up wiki-data and "My Avox Data" (MAD) and supporting sales teams in securing clients for MAD.
!!Log
[[21st January 2010 wiki-data]] - 5hrs 30mins
[[22nd January 2010 wiki-data]] - 1hr 40mins
[[25th January 2010 wiki-data]] - 20mins
[[29th January 2010 wiki-data]] - 1hr 50mins
[[30th January 2010 wiki-data]] - 2hrs 15mins
[[31st January 2010 wiki-data]] - 40mins
[[1st February 2010 wiki-data]] - 5hrs 35mins
[[2nd February 2010 wiki-data]] - 2hrs 55mins
[[3rd February 2010 wiki-data]] - 5hrs 45mins
[[4th February 2010 wiki-data]] - 6hrs
[[5th February 2010 wiki-data]] - 3hrs 35mins
[[6th February 2010 wiki-data]] - 4hrs 20mins
[[7th February 2010 wiki-data]] - 1hr 15mins
[[8th February 2010 wiki-data]] - 10hrs 55mins
[[9th February 2010 wiki-data]] - 1hr 40mins
[[10th February 2010 wiki-data]] - 4hrs 50mins
Sub-total from 21st Jan to 10th February: 59hrs 5mins
Total from 11th Jan to 10th Feb: 79hrs 15mins
[[Invoice 0014]]
[[11th February 2010 wiki-data]] - 3hrs 40mins
[[18th February 2010 wiki-data]] - 1hr 45mins
[[2nd March 2010 wiki-data]] - 1hr
[[4th March 2010 wiki-data]] - 1hr 25mins
[[9th March 2010 wiki-data]] - 3hrs 50mins
[[10th March 2010 wiki-data]] - 1hr 5mins
Sub-total from 11th Feb to 10th March: 12hrs 45mins
Update from Chris Dent: 31st Jan - 11th March: 31hrs
Sub-total for [[Invoice 0016]]: 43hrs 45mins
[[11th March 2010 wiki-data]] - 2hrs 5mins
[[12th March 2010 wiki-data]] - 5hrs 20mins
[[13th March 2010 wiki-data]] - 15mins
[[14th March 2010 wiki-data]] - 3hrs
[[15th March 2010 wiki-data]] - 1hr 10mins
[[18th March 2010 wiki-data]] - 2hrs 40mins
[[19th March 2010 wiki-data]] - 2hrs 40mins
[[22nd March 2010 wiki-data]] - 5hrs 10mins
[[25th March 2010 wiki-data]] - 3hrs 30mins
[[29th March 2010 wiki-data]] - 30mins
[[30th March 2010 wiki-data]] - 1hr 5mins
[[1st April 2010 wiki-data]] - 50mins
[[8th April 2010 wiki-data]] - 30mins
[[5th May 2010 wiki-data]] - 20mins
[[25th May 2010 wiki-data]] - 2hrs 15mins
Invoice 11th March - 10th June - [[Invoice 0022]] - 31hrs 20mins
[[11th June 2010 wiki-data]] - 30mins
[[14th June 2010 wiki-data]] - 35mins
[[15th June 2010 wiki-data]] - 3hrs 10mins
[[1st July 2010 wiki-data]] - 45mins
[[5th August 2010 wiki-data]] - 1hr 25mins
Sub-total 6hrs 25mins
At this point, I started using AttnTracker.
AttentionTracker to 13th Sep: 5hrs 58mins
Total: 12hrs 23mins.
Invoiced including Chris Dent's invoice May through August, plus 3 weeks database updates (3*£75). Invoice 0033.
see [[wiki-data apps]]
For future info: calvert22 costs about £1k for a 24-hour hire.
...is the limited company that I am an employee of. I try to automate as much of the operation of the company as possible, which at this stage mainly comes down to building useful Google Spreadsheets to give me some real-time reporting. I also make use of a bookkeeper to ensure the accounts are correct and think about tax and such. See [[Automating company workflows]] for more on this.