2009-02-10 Christopher Blizzard <blizzard@0xdeadbeef.com>
[whoisi.git] / ChangeLog
1 2009-02-10  Christopher Blizzard  <blizzard@0xdeadbeef.com>
2
3         * whoisi/templates/api-top-doc.mak: Donload -> Download from Atul
4         Varma <avarma@mozilla.com>
5
6 2008-12-14  Christopher Blizzard  <blizzard@0xdeadbeef.com>
7
8         * whoisi/templates/master.mak: Add a link to the source code.
9
10 2008-11-27  Christopher Blizzard  <blizzard@0xdeadbeef.com>
11
12         * tests/twisted/network/test_feedrefresh.py (TestFeedRefresh.confirmEntityHit):
13         Check to make sure we changed last_poll when we hit an etag or
14         last_modified.
15
16         * services/command/siterefresh.py (RefreshSiteError.handleError):
17         Update the last_poll field in the site table with the date if we
18         hit the etag or last_modified value.
19
20 2008-11-22  Christopher Blizzard  <blizzard@0xdeadbeef.com>
21
22         * whoisi/test_controller.py (TestController.modified): Now returns
23         a valid RSS feed + data type for tests.
24
25         * whoisi/model.py (Site): Add etag, last_modified and entity_url
26         entries to the site table.
27
28         * tests/twisted/network/test_download.py: Lots of changes here to
29         support the new download command format (using hash instead of
30         direct call.)
31
32         * tests/twisted/local/test_feedparse_perf.py: Same.
33
34         * tests/twisted/local/test_feedparse.py: Same.
35
36         * tests/twisted/network/test_feedrefresh.py
37         (TestFeedRefresh.test_RefreshSiteManagerEntityProperties): This
38         test makes sure that we set entity properties in the site table
39         after we hit a site that includes them.
40         (TestFeedRefresh.test_RefreshSiteManagerEntityHit): This test
41         makes sure that we return early and don't parse when we send a
42         matching etag or last-modified along with a request.
43
44         * services/command/siterefresh.py (RefreshSiteDone.srDone): Save
45         etag, last-modified and entity_url info in the site if we have it.
46         (RefreshSiteDone.done): When returning the data to the master
47         process add a http_entity_hit=0 in the dict so we know we did a
48         download. (For future use.)
49         (RefreshSiteError.handleError): Handle the DownloadCommand
50         throwing a NotModifiedError which means that we don't have to do
51         any parsing or updating of information.  Short cut to exit.
52         Return value will include a http_entity_hit=1 for future use.  We
53         also set the error field to http_not_modified when we hit this
54         condition.  Also update the error field in the SiteRefresh table
55         when there's a real error.
56
57         * services/command/controller.py (RefreshManager.__init__): Use
58         new DownloadResourceSaveState after a download as part of a
59         refresh.
60
61         * services/command/newsite.py (NewSiteTryURL.doCommand): When
62         calling the download command pass in the url as part of a
63         dictionary.
64         (NewSiteTryURL.downloadDone): More args["filename"] changes.
65         (NewSiteTryURL.startSecondDownload): Same.
66         (NewSiteTryURL.secondDownloadDone): Same.
67         (NewSiteTryURL.tryFeed): Same.
68
69         * services/command/download.py (DownloadResourceSaveState): Shim
70         command that takes the download data and saves it into the state
71         for later commands.
72         (DownloadCommand.doCommand): New code to handle etag,
73         last_modified and entity_url info as arguments to this command.
74         (DownloadCommand.downloadDone): Data is now returned as a hash
75         that includes filename, etag, last_modified and the url stack of
76         downloads.
77
78         * services/command/feedparse.py (FeedRefreshSetup.gotNewSite):
79         Gets the etag, last_modified and entity_url out of the database
80         when setting up for a feed refresh.
81         (FeedRefreshSetup.gotFeed): When returning with a setup refresh
82         the next command is the download so set up everything the download
83         needs to send an etag + last-modified header if we can.
84         (FeedParseCommand.doCommand): Convert to use args["filename"]
85         instead of just filename since the downloadcommand now returns
86         more than just the filename.
87
88         * services/command/linkedin.py (LinkedInScrapeCommand.doCommand):
89         Convert linkedin code to use a hash["filename"] instead of just
90         the filename.
91
92 2008-11-18  Christopher Blizzard  <blizzard@0xdeadbeef.com>
93
94         * test-ws.cfg: Enable base_url_filter.base_url to localhost:9090
95         for testing to generate proper redirects for tests.
96
97         * whoisi/test_controller.py (TestController): Lots of new methods
98         that generate redirects to test url handling.
99
100         * tests/twisted/network/test_download.py: Lots of changes to
101         support the new entity_url argument for the download command.
102         (TestDownload.test_redirect): Test that tests working redirects.
103         (TestDownload.test_redirect_too_many): Test that will make sure we
104         throw an exception if there are too many redirects for a resource.
105
106         * services/command/exceptions.py (TooManyRedirectsError): New
107         exception when a resource has too many redirects.
108
109         * services/command/download.py: More ongoing work to handle etag
110         and last-modified headers.  Also first steps to improve support
111         for redirects. doCommand has been modified to require etag,
112         last_modified and the entry_url to which the etag and
113         last_modified apply.  (The original url which might generate
114         redirects might also not have the tags applied to it.)  There is
115         also a url stack that is saved as urls are followed.  This can be
116         used to match many possible urls to a single resource.  (i.e. what
117         feedburner does.)  This will also top out at 5 redirects for a
118         single resource and then throw a TooManyRedirectsError.
119
120 2008-11-12  Christopher Blizzard  <blizzard@0xdeadbeef.com>
121
122         * whoisi/test_controller.py: New test controller that is used by
123         the etag support handling.
124
125         * whoisi/controllers.py (Root): Add a test/ controller.
126
127         * tests/twisted/network/test_download.py: New tests for etag and
128         last-modified.
129
130         * services/command/exceptions.py (NotModifiedError): New
131         NotModifiedError that's thrown when a download gets a 304.
132
133         * services/command/download.py: Add support for setting and
134         handling etag changes in the http support code.  Not quite right
135         yet, especially with handling redirects and etag/last-modified,
136         but getting close.  You can set etag and last-modified via the
137         DownloadCommand now.  If it gets a 304, it will throw a
138         NotModifedError exception you can handle in your command error
139         handler.  Is a lot cleaner approach than the old monkeypatching
140         method.
141
142 2008-10-18  Christopher Blizzard  <blizzard@0xdeadbeef.com>
143
144         * services/command/feedparse.py (FeedUpdateDatabaseCommand.stupidEntryAlreadyThere):
145         Since all feeds now generate display_cache data we can use it for
146         stupid entires.  Like, say, for old vimeo entries before they had
147         a guid.
148
149 2008-10-18  Christopher Blizzard  <blizzard@0xdeadbeef.com>
150
151         * whoisi/templates/vimeo-widget.mak: Widget to render vimeo
152         content in person and follow pages.
153
154         * whoisi/templates/follow.mak: Add vimeo to the types we know how
155         to render
156
157         * whoisi/templates/unseen.mak: Add vimeo to the types we know how
158         to render.
159
160         * whoisi/templates/person-widget.mak: Add vimeo to the types we
161         know how to render.
162
163         * whoisi/utils/sites.py (site_value): Put vimeo before youtube in
164         the order in which we render sites.
165
166         * whoisi/controllers.py (Root.getDisplayDepth): Add vimeo display
167         depth so we render the right number of items in various contexts.
168         (Root.rendersite): If the site type if vimeo, use the vimeo
169         template for rendering.
170
171         * whoisi/static/images/sites/vimeo-favicon.png: Icon for vimeo
172         items.
173
174         * tests/nose/test_newsite.py (TestNewSite.test_vimeo): Code to
175         test the vimeo url detection code.
176
177         * services/command/vimeo.py (Vimeo): Class for vimeo url detection
178         and selecting a preferred feed.
179
180         * services/command/newsite.py (NewSiteTryURL.getPreferredFeed): If
181         the url is a vimeo url, pick the perferred feed from the list of
182         feeds left over from scraping the HTML.
183         (NewSiteTryURL.getFeedType): If the url is a vimeo url set the
184         site type to "vimeo."
185
186 2008-10-18  Christopher Blizzard  <blizzard@0xdeadbeef.com>
187
188         * whoisi/templates/youtube-widget.mak: Show 3 videos on one line.
189
190         * services/command/youtube.py: Add license header.
191
192 2008-10-18  Christopher Blizzard  <blizzard@0xdeadbeef.com>
193
194         * utils/convert-flickr-ids.py: Script to fix all of the !#@&*#^@*
195         flickr ids in the database.  Flickr uses different IDs for Atom
196         and RSS2 feeds!
197
198 2008-10-17  Christopher Blizzard  <blizzard@0xdeadbeef.com>
199
200         * feed-parse-service (FeedParseProtocol.runCommand): Someone get
201         me a brown paper bag.  Always set display_cache in the output to
202         something.
203
204 2008-10-17  Christopher Blizzard  <blizzard@0xdeadbeef.com>
205
206         * whoisi/utils/sites.py (site_value): Add youtube to the ordering
207         of sites in a profile.
208
209         * whoisi/utils/youtube.py (youtube_get_user): Returns a user for a
210         standard videos.rss-style youtube feed.
211
212         * whoisi/templates/unseen.mak: Add support for youtube.
213
214         * whoisi/templates/follow.mak: Add support for youtube.
215
216         * whoisi/templates/person-widget.mak: Add support for youtube.
217
218         * whoisi/templates/picasa-widget.mak: Support the thumb/JSON
219         format in display_cache.
220
221         * whoisi/templates/youtube-widget.mak (else): Widget for
222         displaying youtube.
223
224         * whoisi/controllers.py (Root.getDisplayDepth): Add support for
225         youtube.
226         (Root.rendersite): Add support for youtube.
227
228         * whoisi/static/images/sites/youtube-favicon.png: Icon for youtube.
229
230         * utils/convert-display-cache.py: Convert display cache for
231         picasa, not flickr.
232
233         * utils/convert-flickr-feeds.py: Script that converts flickr feeds
234         from atom to rss2 in the db.
235
236         * feed-parse-service (FeedParseProtocol.runCommand): Set the thumb
237         property in the display_cache if media_thumbnail is set.
238
239         * lib/feedparser.py: Patch to detect thumbnails.
240
241         * tests/nose/test_newsite.py (TestNewSite.test_youtube): New tests
242         for detecting youtube urls.  Also somewhat future-proofed for
243         eventual user detection.
244
245         * smoketest.txt: Test a youtube url.
246
247         * patches/README: Readme for new thumbnail patch.
248
249         * patches/feedparser-thumbnail.patch: Patch that adds support for
250         the media:thumbnail property to feedparser.  Taken from an
251         upstream bug.
252
253         * services/command/controller.py (PreviewSiteManager.__init__):
254         Don't call FlickrPreviewThumbnails anymore - we get it directly
255         from the feed now.
256
257         * services/command/newsite.py (NewSiteTryURL.getFeedType): If it's
258         a youtube url, set the type.
259
260         * services/command/flickr.py (Flickr.getPreferredFeed): We now use
261         the rss2 feed instead of the atom feed - it contains a thumbnail
262         url.
263
264         * services/command/picasa.py (Picasa.photoFeedForUser): Make
265         picasa work like flickr - set a "thumb" object as a JSON object in
266         the database row instead of just a raw url.
267
268         * services/command/youtube.py (Youtube): Class that supports the
269         current site model for detecting youtube feeds.  It's also
270         future-proofed to support urls and usernames at some point.
271
272         * services/master/database.py (DatabaseManager.__init__): Disable
273         flickr scan on startup.  New support for images doesn't require us
274         to do it anymore.  Yay!
275
276         * services/master/newsite.py (NewSite.normalize): Placeholder for
277         eventual normalization for youtube.  Doesn't do anything right
278         now.
279
280 2008-10-07  Christopher Blizzard  <blizzard@0xdeadbeef.com>
281
282         * services/command/controller.py (ProtoManager.start): Remove dead
283         code after return.
284
285         * tests/twisted/network/test_newsite.py (TestNewSite.confirmRelativeLinksReddit): 
286         Reddit urls keep changing.  Include a www now.
287         
288
289 2008-09-30  Christopher Blizzard  <blizzard@0xdeadbeef.com>
290
291         * tests/nose/test_newsite.py (TestNewSite.test_delicious): Add
292         test cases for new style delicious urls.
293
294         * services/command/delicious.py (Delicious.isDelicious): Add
295         support for new-style delicious urls.
296
297 2008-09-19  Christopher Blizzard  <blizzard@0xdeadbeef.com>
298
299         * utils/utils.cfg: New file for utils that includes database
300         config.
301
302         * Update all the config files to have bogus usernames and
303         passwords for final source release.
304
305 2008-09-18  Christopher Blizzard  <blizzard@0xdeadbeef.com>
306
307         * services/command/xmlnode.py: Add proper license information for
308         this file which was taken from the flickrapi code.  It's
309         MIT/python 2.5.
310
311 2008-09-17  Christopher Blizzard  <blizzard@0xdeadbeef.com>
312
313         * Add an MIT license to everything.
314
315 2008-09-05  Christopher Blizzard  <blizzard@0xdeadbeef.com>
316
317         * dev.cfg: Include a recaptcha private key placeholder.
318
319         * test-ws.cfg: Include a recaptcha private key placeholder.
320
321         * whoisi/utils/recaptcha.py (recaptcha_check_fail): Use the
322         recaptcha private key defined in the config.
323
324         * whoisi/templates/person.mak: Include keys.js.
325
326         * whoisi/templates/person-add.mak: Include keys.js.
327
328         * whoisi/templates/recommendations.mak: Include keys.js.
329
330         * whoisi/templates/search.mak: Make sure to include keys.js before
331         person.js.
332
333         * whoisi/static/javascript/keys.js.in: File to rename to keys.js
334         where you include your public key for recaptcha.
335
336         * whoisi/static/javascript/addform.js: Use the recaptcha public
337         key defined in keys.js.
338
339         * whoisi/static/javascript/person.js: Use the recaptcha public key
340         defined in keys.js.
341
342         * prod.cfg: Placeholder for recaptcha private key.
343
344         * start-whoisi.py: Warn about missing recaptcha private key.
345
346         * controller-1.cfg: Add placeholders for twitter + flickr account
347         info.
348
349         * tests/twisted/network/test_flickr.py (TestFlickr.test_NewFlickrCache):
350         This test is skipped right now because we don't have a way to pull
351         in the api key.
352
353         * services/command/flickr.py: Get the flickr api key from the
354         config file.
355
356         * services/command/download.py: Get the twitter username and
357         password from the config for the twitter download hack.  Also,
358         bonus bug fix - call clear_cache after urlparse.
359
360         * controller-service: Die if the config doesn't include api keys
361         for flickr or twitter.
362
363 2008-09-04  Christopher Blizzard  <blizzard@0xdeadbeef.com>
364
365         * utils/archive-site-history.py: Fix the other three bugs that
366         were moving and deleting the wrong records.
367
368 2008-09-04  Christopher Blizzard  <blizzard@0xdeadbeef.com>
369
370         * utils/archive-site-history.py (migrate_records): Argh, it
371         migrated all new records, not all old records.  Fail.
372
373 2008-09-04  Christopher Blizzard  <blizzard@0xdeadbeef.com>
374
375         * whoisi/model.py (SiteHistoryArchive): Dummy entry for the
376         archive table.
377
378         * whoisi/controllers.py (Root.l): Pull a url out of the archive if
379         we have to - urls must live forever!
380
381 2008-09-04  Christopher Blizzard  <blizzard@0xdeadbeef.com>
382
383         * README.txt: Add site_type_idx index to the site table in a
384         pathetic attempt to get the initial flickr query to go faster.
385
386         * whoisi/utils/fast_history.py: All of these methods now pull out
387         the list of ids and then generate a custom query based on them.
388         Why?  Because mysql's query optimizer just can't get it right and
389         this is all primary key driven - it's _much_ faster.
390
391         * whoisi/templates/unseen.mak: Add the "Caught Up!" button.
392
393         * whoisi/controllers.py (Root.caughtup): Little method that
394         updates the last seen id when we're caught up.
395
396         * whoisi/controllers.py (Root.unseen): Go back to the old
397         behaviour of having a "Caught Up" button.
398
399         * utils/archive-site-history.py: Utility that archives old
400         site_history items so that we have a max of 100 items in the
401         database for any site.
402
403         * tests/twisted/network/test_newsite.py (TestNewSite.confirmRelativeLinksReddit):
404         The reddit urls keep changing - update the test to make it
405         generic.
406
407         * services/command/feedparse.py (FeedUpdateDatabaseCommand.gotEntries):
408         Limit the number of entries to 99.
409
410         * services/command/picasa.py (Picasa.photoFeedForUser): Limit the
411         number of picasa results to 99.
412
413 2008-08-31  Christopher Blizzard  <blizzard@0xdeadbeef.com>
414
415         * README.txt: Add a bunch of new indexes required to make many of
416         the queries go fast.  Like, 4 mins to 0.05 seconds fast.
417
418         * whoisi/utils/fast_history.py: Update every call that uses
419         subqueries to use standard joins instead now that we have proper
420         indexes in place.
421
422         * whoisi/templates/index.mak: Remove beta-quality warning.  We're
423         doing fine.  Mostly.
424
425 2008-08-30  Christopher Blizzard  <blizzard@0xdeadbeef.com>
426
427         * utils/clean_site_history_dups.py: Utility to scan the entire
428         site_history database and clean out duplicate entries for a site.
429         Takes a long time to run.
430
431 2008-08-28  Christopher Blizzard  <blizzard@0xdeadbeef.com>
432
433         * whoisi/utils/fast_history.py (fast_recent_changes_for_follower):
434         Add a check to make sure we return None if there's no follower.
435         (fast_count_items_for_follower): Return None if nothing is
436         returned.
437         (fast_max_item_for_follower): Return None if nothing is returned.
438
439         * whoisi/templates/follow.mak: Remove the caught up link.
440
441         * whoisi/templates/login-info.mak: Limit width of the login
442         message to 60% wide as below.
443
444         * whoisi/templates/unseen.mak: Page for the unseen method.
445
446         * whoisi/templates/master.mak: For every page that is loaded
447         update the unread count.  Shouldn't be here, but it's fine for
448         now.
449
450         * whoisi/templates/unseen-no-entries.mak: New template for the
451         unseen page when there's nothing to show.
452
453         * whoisi/templates/follow-no-entries.mak: Hold the headline to 60%
454         wide to make sure it doesn't overrun the right hand nav info.
455
456         * whoisi/controllers.py (Root.follow): Remove the code from this
457         method that displays unseen bits and updates the counts.
458         (Root.unseen): Independent screen that shows the unseen items.
459
460 2008-08-25  Christopher Blizzard  <blizzard@0xdeadbeef.com>
461
462         * whoisi/controllers.py (Root.follow): When updating the
463         last_history value, make sure it's greater than the current value
464         to keep people from going backwards in time.
465
466 2008-08-24  Christopher Blizzard  <blizzard@0xdeadbeef.com>
467
468         * whoisi/utils/fast_history.py (fast_recent_changes_for_follower):
469         Change this method so it can handle either recent changes or
470         unseen changes with different where clauses.
471         (fast_count_items_for_follower): Add this method to get the recent
472         number of changes for a particular person.
473         (fast_max_item_for_follower): Add this method to get the last item
474         from the database.  Used for follower initialization.
475
476         * whoisi/utils/follow.py: Add count_history() and last_history()
477         methods to get the values from the current follow object.
478
479         * whoisi/templates/follow.mak: Add a hook to display the caught up
480         link on the follow page.
481
482         * whoisi/templates/master.mak: On the sidebar show the unread
483         count.  Add a hook to the sidebar so on the follow page we can
484         show a "caught up" link.  Split various types of actions in the
485         sidebar into their own sections.
486
487         * whoisi/controllers.py (Root.follow): When loading the follow
488         page make sure that we set default values for unread + last item
489         seen.  If someone passes in caught_up and history_id set the
490         values in the database.  Update the unread count on each
491         load (need to fix this later.)  Also pass down the had_start value
492         to indicate if this was the main follow page or looking at old
493         history.  On old history pages we don't show the "caught up" link.
494
495         * whoisi/model.py (Follower): Add last_history and count_history
496         items for unread and last item seen.
497
498         * master-service (print_usage): Add the -p option for publishing
499         updates.
500
501 2008-08-21  Christopher Blizzard  <blizzard@0xdeadbeef.com>
502
503         * services/command/feedparse.py (FeedUpdateDatabaseCommand.gotEntries):
504         When inserting a new entry make sure it's not already in the
505         database under a different entry_id.
506
507         * tests/twisted/network/test_newsite.py (TestNewSite.confirmRelativeLinksGitHub):
508         github now uses www.github.com in its feeds.  Update test.
509         (TestNewSite.confirmRelativeLinksReddit): reddit now uses
510         /comments/ for the top of the comments url in feeds.  Update test.
511
512 2008-08-18  Joe Shaw <joe@joeshaw.org>
513
514         * whoisi/utils/recommendations.py (get_last_activity): Function to
515         get the last activity for a particular follower.
516
517         * whoisi/utils/recommendations.py (get_recommendations): Decay the
518         value of a particular follower if they haven't visited the site
519         recently.  Anything < 14 days is considered active.  Beyond that
520         there's a 45 day half-life.
521
522 2008-08-09  Christopher Blizzard  <blizzard@0xdeadbeef.com>
523
524         * services/publisher/protocol.py (PublisherProtocol.dataReceived):
525         Check the buffer for the buffer length check, not the line.  Also
526         always return if the header isn't found.  While we're here fix up
527         a couple of error messages to give more relevant information to
528         the other end.
529
530 2008-08-09  Christopher Blizzard  <blizzard@0xdeadbeef.com>
531
532         * whoisi/api.py (ApiController.startRefresh): Add a new
533         startRefresh api that lets you start a site refresh from the
534         outside world.  Not public yet because it doesn't contain
535         protections against starting a billion refreshes.
536         
537 2008-08-09  Christopher Blizzard  <blizzard@0xdeadbeef.com>
538
539         * whoisi/templates/master.mak: Add an API link at the bottom of
540         every page.
541
542         * whoisi/templates/api-top-doc.mak: Add example scripts and clean
543         up a lot of the docs.  Add a table of contents at the top.
544
545 2008-08-08  Christopher Blizzard  <blizzard@0xdeadbeef.com>
546
547         * whoisi/api.py (ApiController.getURLForTinyLink): Add a "title"
548         to the return dictionary for getURLForTinyLink().  Also return
549         url=None if the url isn't found.
550
551 2008-08-08  Christopher Blizzard  <blizzard@0xdeadbeef.com>
552
553         * services/publisher/protocol.py (PublisherProtocol.dataReceived):
554         Add some more useful error messages.
555
556 2008-08-08  Christopher Blizzard  <blizzard@0xdeadbeef.com>
557
558         * firehose-client: Add host + port arguments on the command line.
559
560 2008-08-08  Christopher Blizzard  <blizzard@0xdeadbeef.com>
561
562         * publisher-service (PublisherService): Deliver the returned
563         object to the client as-is.
564
565         * services/publisher/lookup.py (MasterLookupQueue): Select the
566         site history information we need from all the various tables and
567         put it into a big dictionary for delivery to clients.
568
569         * firehose-client (ClientProtocol.handleMessage): Decode the new
570         weblog message.
571
572 2008-08-07  Christopher Blizzard  <blizzard@0xdeadbeef.com>
573
574         * master-dev.cfg: Add entries for a publisher.
575
576         * publisher-service: Very simple publisher server that handles
577         multiple clients connected and will publish updates.  Should scale
578         a bit, but not too much.  Connects to the new lookup code, the new
579         publisher protocol and hooks it all together to publish simple
580         updates.
581
582         * master-service: Add a new publish argument to start and handle
583         -p on the command line to publish updates.
584
585         * publisher-1.cfg: Config file for a sample publisher.
586
587         * services/publisher/server.py: Some of the classes required to
588         run a publisher server.  Includes the server protocol code that
589         connects to the main PublisherService class provided by the
590         server.
591
592         * services/publisher/protocol.py: First pass at a protocol class
593         that's used by both client and server.  Handles most of the
594         control code and publishes state information when it changes.
595         Client and server should only have to override to a subset of
596         methods to get something that works pretty well.  (Messages are
597         limited to 128kb each for now.)
598
599         * services/publisher/lookup.py: Code for the publisher that looks
600         up database entries based on ID.  (Master just publishes an ID and
601         it's up to the publisher to turn that into a full message.)
602         Includes an incoming queue that is processed one entry at a time.
603
604         * services/master/database.py (DatabaseManager.getFlickrImages):
605         Only get images for flickr sites that haven't been removed.
606
607         * services/master/feedrefresh.py (FeedRefresh.done): Make sure to
608         return new site history items when we're done with a picasa
609         refresh.
610
611         * services/master/picasa.py (PicasaRefresh.done): Make sure to
612         return new site history items when we're done with a picasa
613         refresh.
614
615         * services/master/publisher.py: Very simple first pass at code
616         that connects and reconnects to publishing services.  Will publish
617         information about new site history and new site items.
618
619         * services/master/worker.py (get_work_hosts): Print out if we're
620         adding a controller host.
621         (WorkManager): Remove TODO information.
622
623         * controller-service: Print out a message on startup that says
624         which port the controller is listening on.
625
626         * firehose-client: First pass at a very simple firehose client
627         that just prints out messages that it gets from the server.  Needs
628         a huge amount of work.
629
630 2008-07-27  Christopher Blizzard  <blizzard@0xdeadbeef.com>
631
632         * master-dev.cfg: Defaults for master process.
633
634         * master-service: Use config file for startup.
635
636         * controller-1.cfg: Config file for controller.
637
638         * services/master/database.py: Get database info from config file.
639
640         * services/master/worker.py: Get work hosts from a config file.
641
642         * services/master/refreshmanager.py: Use config file for getting
643         refresh interval.
644
645         * services/config/__init__.py: Global config option.
646
647         * controller-service: Use a config file for config options.
648
649 2008-07-25  Christopher Blizzard  <blizzard@0xdeadbeef.com>
650
651         * services/command/controller.py (ProtoManager.start): We don't
652         need the command as an argument anymore when we start.  (It wasn't
653         used anyway.)
654
655         * services/command/siterefresh.py (RefreshSiteDone.done): Return
656         the site_history_new_ids if they are in the state.  Getting ready
657         for live updates.
658
659         * services/command/newsite.py (NewSiteDone.done): Return the
660         site_history_new_ids if they are in the state.  Getting ready for
661         live updates.
662
663         * services/command/feedparse.py (FeedUpdateDatabaseCommand): Track
664         the ids that we insert into the database.  They are put in the
665         state as "site_history_new_ids".
666
667         * services/master/previewsite.py (PreviewSite.startProcess):
668         preview-site -> previewSite, preview-linkedin -> previewLinkedIn,
669         preview-picasa -> previewPicasa.
670
671         * services/master/feedrefresh.py (FeedRefresh): feed-refresh ->
672         feedRefresh.
673
674         * services/master/picasa.py (PicasaRefresh): picasa-refresh ->
675         picasaRefresh.
676
677         * services/master/newsite.py (NewSite.startProcess): new-site ->
678         newSite, new-linkedin -> newLinkedIn, new-picasa, newPicasa.
679
680         * services/master/linkedin.py (LinkedInRefresh): Use
681         linkedInRefresh instead of linkedin-refresh.
682
683         * services/master/flickr.py (FlickrCache): Use flickrCache instead
684         of flickr-cache.
685
686         * services/master/worker.py (Worker.dispatchCommand): Use the name
687         of the method, pass the uuid and pass the command arguments
688         directly instead of serializing them into a string to be
689         re-parsed.  Much cleaner.
690
691         * controller-service (Controller): Change the doCommand + dispatch
692         to use individual methods by name.  This should make it possible
693         to use named arguments in the future and have per-method return
694         values.  Been meaning to do this for months.
695
696 2008-07-23  Christopher Blizzard  <blizzard@0xdeadbeef.com>
697
698         * whoisi/static/txt/robots.txt: Don't allow robots to access the
699         api.
700
701 2008-07-19  Christopher Blizzard  <blizzard@0xdeadbeef.com>
702
703         * services/command/download.py (localDownloadPage): Use the whoisi
704         user, not the chrisblizzard user for twitter.
705
706 2008-07-19  Christopher Blizzard  <blizzard@0xdeadbeef.com>
707
708         * whoisi/static/txt/robots.txt: Don't let robots go to
709         recommendations or genrecommendations.
710
711 2008-07-19  Christopher Blizzard  <blizzard@0xdeadbeef.com>
712
713         * whoisi/utils/recommendations.py: Code from Joe Shaw!  Generates
714         a nice list of recommendations based on the people you're
715         following.  Also contains the unused "most popular" code.
716
717         * whoisi/templates/recommendations.mak: Adapted from Joe's
718         original template.  Gives instructions if you're not following
719         anyone.  Offers to generate a list of recommendations if you are
720         following someone.  Once you have a list it will display it.
721
722         * whoisi/templates/master.mak: Add Recommendations to the list of
723         items on the right hand side.
724
725         * whoisi/controllers.py (Root.recommendations): Page that shows
726         recommendations (paged like the search page.)
727         (Root.genrecommendations): Page that generates recomemndations and
728         stuffs it into the database.
729
730         * whoisi/model.py (FollowerRecommendations): Add
731         FollowerRecommendations to keep track of recommendations.
732
733 2008-07-18  Christopher Blizzard  <blizzard@0xdeadbeef.com>
734
735         * whoisi/templates/aliases-widget.mak: Move the coding comment to
736         the top of the file so it doesn't end up in the output sent to the
737         client.  Oops.
738
739 2008-07-18  Christopher Blizzard  <blizzard@0xdeadbeef.com>
740
741         * whoisi/templates/aliases-widget.mak: Expand group and event
742         aliases to point to search and/or event pages.
743
744         * whoisi/utils/display.py (is_event_alias): Add is_event_alias and
745         is_group_alias which hand back the search or event string if it's
746         one of those kinds of aliases.
747
748 2008-07-17  Christopher Blizzard  <blizzard@0xdeadbeef.com>
749
750         * whoisi/model.py (PeopleEvent): Add a banner item.
751
752         * whoisi/controllers.py (Root.e): Add a banner if it's set.
753
754         * whoisi/templates/event.mak: Add a banner to the top of the page
755         if it's set.
756
757 2008-07-17  Christopher Blizzard  <blizzard@0xdeadbeef.com>
758
759         * services/command/download.py (localDownloadPage): Jam my
760         personal username and password into a request if it is for a
761         twitter.com account.  I have never felt so dirty in all of my
762         life.  Except for that time with the Nun.  But nevermind about
763         that.
764
765 2008-07-16  Christopher Blizzard  <blizzard@0xdeadbeef.com>
766
767         * whoisi/templates/follow-no-entries.mak: Limit width to 60%.
768
769         * README.txt: Add an index to the name table.
770
771         * whoisi/utils/fast_history.py (fast_recent_changes_for_event):
772         Timeline for an event.
773
774         * whoisi/templates/person-add.mak: Limit some of the text to 60%
775         width.
776
777         * whoisi/templates/master.mak: Add a link to the events page.
778
779         * whoisi/templates/event.mak: New template for events!
780
781         * whoisi/templates/search.mak: Don't show the "add someone to the
782         site" for people searching for groups or an event.  Also limit
783         some of the text to 60% width.
784
785         * whoisi/templates/events.mak: New events template that describes
786         what events are happening and how you add yourself to one of them.
787
788         * whoisi/templates/nofollow.mak: Set the width for some text to
789         60%.
790
791         * whoisi/controllers.py (Root.everyone): Remove call to
792         datetime.utcnow() that wasn't needed anymore.
793         (Root.e): New method for events!  Uses
794         fast_recent_changes_for_event()
795
796         * whoisi/model.py (PeopleEvent): Add a PeopleEvent item that
797         contains a list of events and if they are active.
798
799 2008-07-14  Christopher Blizzard  <blizzard@0xdeadbeef.com>
800
801         * whoisi/controllers.py (Root.peopleListToFullDisplay): Refactored
802         function that is used from search and the follow display to gather
803         the data for display.
804         (Root.search): Use the refactored display function.
805         (Root.follow): Use the refactored display function.
806
807 2008-07-13  Christopher Blizzard  <blizzard@0xdeadbeef.com>
808
809         * tests/twisted/local/test_feedparse.py (TestFeedParse.test_feedParse):
810         We need to set feed_url in the state.
811         (TestFeedParse.test_stupidFeedParse): Same.
812
813         * tests/twisted/network/test_newsite.py (TestNewSite.confirmRelativeFeed):
814         Actually test to make sure we got the right relative urls resolved
815         to full urls.
816
817         * whoisi/static/tests/relative_feed.html: Use relative_feed.atom.
818
819         * whoisi/static/tests/relative_feed.atom: Test case that includes
820         a <link> that is relative.
821
822         * services/command/newsite.py (NewSiteCreate.createSite): Some
823         debug spew when we're creating the site.
824
825         * services/command/feedparse.py (FeedUpdateDatabaseCommand.updateSite):
826         Resolve relative urls when updating the url in the site.
827
828 2008-07-12  Christopher Blizzard  <blizzard@0xdeadbeef.com>
829
830         * services/master/refreshmanager.py (RefreshManager.getRandomRefreshTime):
831         Change the default time from 30 minutes to 60.
832
833         * services/master/worker.py (Worker.acceptingWork): Change the
834         default depth from 30 to 80 items in the work queue at once.
835
836         * services/command/feedparse.py (FeedUpdateDatabaseCommand.gotEntries):
837         Make sure to check if a link is null before trying to see if it's
838         relative.
839
840 2008-07-12  Christopher Blizzard  <blizzard@0xdeadbeef.com>
841
842         * blacklist_rss.txt: Add some more blacklist rss feeds.
843
844         * tests/twisted/local/test_feedparse.py (TestFeedParse.test_stupidFeedParse):
845         We now need the url set in the state to resolve relative urls.
846         (TestFeedParse.test_feedParse): Same.
847
848         * tests/twisted/network/test_newsite.py (TestNewSite.test_NewSiteRelativeEntries):
849         New test that tests what happens when we end up with relative
850         entries.
851         (TestNewSite.test_NewSiteRelativeEntriesReddit): Live test
852         vs. Reddit
853         (TestNewSite.test_NewSiteRelativeEntriesGitHub): Live test
854         vs. GitHub
855         (TestNewSite.test_NewSiteRelativeEntriesNoLink): Relative entries
856         with no link in the rss.
857         (TestNewSite.test_NewSiteRelativeEntriesRelativeLink): Testing
858         relative entries with a relative link in the feed.
859
860         * services/command/newsite.py (NewSiteTryURL.loadDone): Use the
861         resolve_relative_url function to resolve a relative url.
862         (NewSiteTryURL.feedLoadDone): If you see a link in the feed make
863         sure to resolve the relative url.
864
865         * services/command/previewsite.py (PreviewSiteDone.doCommand):
866         Resolve any relative urls that might be in the entries.
867
868         * services/command/picasa.py (PicasaSetup.gotNewSite): Get the url
869         as well as the feed so we can resolve relative urls.
870         (PicasaSetup.gotSite): Set the original url in the environment.
871
872         * services/command/utils.py (resolve_relative_url): Utility
873         function to resolve relative urls.
874
875         * services/command/feedparse.py (FeedRefreshSetup.gotNewSite):
876         Make sure to pull the base url for the refresh so we can resolve
877         relative urls.
878         (FeedRefreshSetup.gotFeed): Set the url that we get back and add
879         it to the debugging output.
880         (FeedUpdateDatabaseCommand.doCommand): Add the site id to the
881         debugging spew so we know what site id we're getting updates for.
882         (FeedUpdateDatabaseCommand.gotEntries): Fix up entries that might
883         be relative before we insert or compare them.
884
885 2008-07-04  Christopher Blizzard  <blizzard@0xdeadbeef.com>
886
887         * blacklist_rss.txt: Start a list of rss feeds we need to f-ing
888         ban.
889
890         * tests/nose/test_newsite.py (TestNewSite.test_delicious): Tests
891         for delicious url detection.
892         (TestNewSite.test_delicious_preferred): Tests for picking the
893         right delicious feed from the list.
894
895         * whoisi/utils/sites.py (site_value): Add delicious to the sort
896         list.
897
898         * whoisi/templates/delicious-widget.mak: Delicious widget derived
899         from the weblog widget.
900
901         * whoisi/templates/follow.mak: Add delicious.
902
903         * whoisi/templates/everyone.mak: Add delicious.
904
905         * whoisi/templates/person-widget.mak: Add delicious.
906
907         * whoisi/controllers.py (Root.getDisplayDepth): Add delicious.
908         (Root.rendersite): Add delicious.
909
910         * whoisi/static/css/style.css: Delicious entries.
911
912         * whoisi/static/images/sites/delicious.png: Delicious image. 
913
914         * services/command/newsite.py: Add support for delicious.
915
916         * services/command/flickr.py (Flickr.getPreferredFeed): Little bug
917         fix.  Return None if there's no match.
918
919 2008-07-03  Christopher Blizzard  <blizzard@0xdeadbeef.com>
920
921         * tests/twisted/network/test_previewsite.py: Some tests for the
922         previewsite code.  Right now we just test a common case and a
923         preview with no link in the rss to make sure that the feed link is
924         properly updated.
925
926         * tests/twisted/network/test_newsite.py: Add tests (one for
927         relative <link> in a feed, one for relative entries in a feed, one
928         for a combination of the two, one for relative entries with no
929         <link)) that will get filled in when I fix those bugs.
930
931         * whoisi/static/tests/no-link.atom: Test feed without a <link>.
932
933         * whoisi/static/tests/no-link.html: Test page for testing an rss
934         feed with no <link>.
935
936         * smoketest.txt: The site that I was using for testing flickr went
937         away!
938
939         * services/command/newsite.py (NewSiteTryURL.feedLoadDone): Add
940         some more debugging spew so we can diagnose problems later.
941
942         * services/command/previewsite.py (PreviewSiteDone.doCommand):
943         Some feeds don't include a <link> tag so we need to make sure we
944         get the link from the state url instead of strictly out of the
945         feed.
946
947 2008-07-02  Christopher Blizzard  <blizzard@0xdeadbeef.com>
948
949         * clean_site_refresh.py: Script that cleans out done and error
950         status from the site_refresh table.
951
952 2008-07-02  Christopher Blizzard  <blizzard@0xdeadbeef.com>
953
954         * whoisi/templates/identica-widget.mak: They changed the text to
955         the title instead of in the summary.  Oops.
956
957 2008-07-02  Christopher Blizzard  <blizzard@0xdeadbeef.com>
958
959         * tests/nose/test_newsite.py (TestNewSite.test_identica): Code to
960         test the identi.ca url detection.
961
962         * services/command/newsite.py (NewSiteTryURL.getFeedType): Set the
963         type for identi.ca urls.
964
965         * services/command/identica.py (Identica.isIdentica): New code to
966         detect a identi.ca url.
967
968         * whoisi/utils/sites.py (site_value): Sort identi.ca right after
969         twitter.
970
971         * whoisi/templates/follow.mak: Add identi.ca to the switch.
972
973         * whoisi/templates/identica-widget.mak: Direct copy of the twitter
974         code used to display identi.ca instead.
975
976         * whoisi/templates/twitter-widget.mak: When calling
977         expand_user_ref() make sure to pass in the twitter base url since
978         identi.ca uses it now.
979
980         * whoisi/templates/everyone.mak: Add the identi.ca widget to the
981         switch.
982
983         * whoisi/templates/person-widget.mak: Add the identi.ca widget to
984         the switch.
985
986         * whoisi/controllers.py (Root.getDisplayDepth): Add identi.ca to
987         the switch for display.
988         (Root.rendersite): Add identi.ca to the switch.
989
990         * whoisi/static/images/sites/identica.png: Image for identi.ca
991
992         * whoisi/utils/twitter.py (expand_user_ref): Add a "base_site" so
993         that we can render both twitter and identi.ca messages.
994
995 2008-07-02  Christopher Blizzard  <blizzard@0xdeadbeef.com>
996
997         * tests/twisted/local/data/no-link.atom: New test feed that
998         doesn't include a <link> in the <feed> section that found a bug in
999         the preview code.
1000
1001         * whoisi/utils/url_lookup.py (run_db_check): Don't include
1002         SiteHistory in the search.
1003
1004 2008-07-02  Christopher Blizzard  <blizzard@0xdeadbeef.com>
1005
1006         * whoisi/templates/index.mak: Add a little beta-quality warning.
1007
1008 2008-07-02  Christopher Blizzard  <blizzard@0xdeadbeef.com>
1009
1010         * whoisi/templates/contact.mak: New contact page.
1011
1012         * whoisi/templates/about.mak: More edits that taste like community
1013         guidelines.
1014
1015         
1016 2008-07-02  Christopher Blizzard  <blizzard@0xdeadbeef.com>
1017
1018         * whoisi/templates/about.mak: Add a nice about page instead of the
1019         angry one of old.
1020
1021 2008-06-30  Christopher Blizzard  <blizzard@0xdeadbeef.com>
1022
1023         * whoisi/controllers.py (Root.addpersonstatus): Don't try to
1024         search all of the links in the feed for a duplicate.  Turns out
1025         that links in google reader, delicious, etc, all point to the same
1026         stuff.  So we just look at the link instead of all of the links in
1027         the feed.
1028
1029 2008-06-29  Christopher Blizzard  <blizzard@0xdeadbeef.com>
1030
1031         * tests/twisted/local/data/GasteroProd: A feed that I found that
1032         totally busts the parser.  Good for a later test case.
1033
1034         * tests/twisted/local/test_feedparse_perf.py (TestFeedParsePerf):
1035         Woo, something that iterates with the parser and sees how fast it
1036         can go!
1037
1038         * master-service: Debugging spew for now.
1039
1040         * services/command/htmlscrape.py (ScrapeLinkCommand): Use the new
1041         sm.serviceFailed call.
1042
1043         * services/command/picasa.py (PicasaPollFeed): Use the new
1044         sm.serviceFailed call.
1045
1046         * services/command/service.py (ParseProcess): Lots of changes here
1047         to move from a parse-per-process-start to one where the process
1048         sits around and parses over and over again.  The SubService now
1049         gets information about when this class is done starting (via
1050         getStarted()) and when the process has exited (via getGone()).
1051         There's an explicit assert in here that makes sure you can't start
1052         a parse when one parse is already in progress.  This already found
1053         one bug so it's staying.  When we're done parsing we carefully
1054         save the deferred, reset our state, and call the callback.  We do
1055         this because the callback can cause another parse on this process
1056         to start so we need to be ready for that re-entrant case.
1057         (SubService): Minor changes here.  Just accessors to the various
1058         functions in ParseProcess.  Note the shutdown() accessor which
1059         helps with shutdown.
1060         (ServicePool): New class that mangages a pool of processes.  Right
1061         now it's hard coded at 2 because that's the smallest number of
1062         processes that seems to get a decent perf boost.  Adding more
1063         didn't help and it was a good bit faster than one process.
1064         Processes move through various states - starting, idle, working
1065         and shutting down.  It also has support for checking out a process
1066         and checking it back in.  You need to tell it if the process has
1067         failed.  So there's some fragility here.  It also keeps stats on
1068         how often something has been used.  Note that it supports a
1069         shutdown process as well.
1070         (ServiceManager): Use the ServicePools instead of just creating a
1071         new process every time someone wants access to the service.  It
1072         creates the pools on demand.  New callback is serviceFailed()
1073         instead of releaseService() when a process fails.  It has a
1074         shutdown() call as well and it works!  Yay!
1075
1076         * services/command/feedparse.py (FeedParseCommand): Call
1077         sm.serviceFailed if the parse service fails instead of just
1078         returning it to the good queue.
1079
1080         * services/master/refreshmanager.py (RefreshManager.checkForNewSites):
1081         Don't poll sites that have been removed.
1082
1083         * Random files: Debugging spew everywhere until I feel like the
1084         master + controllers are stable and well-tested.
1085
1086         * services/master/worker.py (WorkManager.dispatchCommands): Fix
1087         big performance problem when the command queue is very deep.  We
1088         walk the queue based on the available controllers instead of the
1089         depth of the queue and go until the controllers are full, not the
1090         other way around.  Trying to poll 5000 sites the early way
1091         completely locked the master process.  It does fine now.
1092
1093 2008-06-29  Christopher Blizzard  <blizzard@0xdeadbeef.com>
1094
1095         * whoisi/templates/about.mak: Remove the word stupid.  I felt like
1096         it was ruining the entire page.
1097
1098 2008-06-28  Christopher Blizzard  <blizzard@0xdeadbeef.com>
1099
1100         * Touch nearly every template and make sure that things are
1101         escaped to avoid XSS problems.  Too many changes to list here.
1102         Thanks to Shawn Lauriat <shawn@frozen-o.com> for the great bug
1103         reports.
1104
1105 2008-06-28  Joe Shaw  <joe@joeshaw.org>
1106
1107         * utils/follower_stats.py: Print out the 10 most followed people.
1108
1109 2008-06-28  Joe Shaw  <joe@joeshaw.org>
1110
1111         * utils/follower_stats.py: Print out some date usage histograms
1112         as well.  Clean up the code a bit.
1113
1114 2008-06-28  Christopher Blizzard  <blizzard@0xdeadbeef.com>
1115
1116         * whoisi/templates/flickr-widget.mak: Missing closing </div> tag.
1117
1118         * whoisi/templates/twitter-widget.mak: Missing closing </div> tag.
1119         Amazed the site rendered at all.
1120
1121 2008-06-28  Joe Shaw  <joe@joeshaw.org>
1122
1123         * utils/follower_stats.py: Print out some statistics about the
1124         userbase and how many people they follow.
1125
1126 2008-06-27  Christopher Blizzard  <blizzard@0xdeadbeef.com>
1127
1128         * whoisi/templates/search.mak: Escape unsafe data.
1129
1130         * whoisi/templates/search-widget.mak: Escape unsafe data.
1131
1132 2008-06-26  Christopher Blizzard  <blizzard@0xdeadbeef.com>
1133
1134         * utils/delete_user.py: Utility that deletes a user and all data
1135         associated with it.
1136
1137 2008-06-26  Christopher Blizzard  <blizzard@0xdeadbeef.com>
1138
1139         * whoisi/static/css/style.css: Add a background-color to the
1140         style for the body.
1141
1142 2008-06-26  Christopher Blizzard  <blizzard@0xdeadbeef.com>
1143
1144         * whoisi/controllers.py (Root.follow): Catch the IndexError that's
1145         generated by history_to_clusters when there's nothing new found
1146         and redirect to the follow-no-entries template.
1147
1148         * whoisi/templates/follow-no-entries.mak: New template for when
1149         people are following someone but they haven't posted anything new.
1150
1151 2008-06-26  Christopher Blizzard  <blizzard@0xdeadbeef.com>
1152
1153         * services/master/worker.py (Worker.acceptingWork): Bump the
1154         number of possible commands in progress to 30 instead of 15.
1155
1156         * whoisi/templates/about.mak: Fix some spacing.
1157
1158 2008-06-26  Christopher Blizzard  <blizzard@0xdeadbeef.com>
1159
1160         * whoisi/templates/master.mak: Add a footer.
1161
1162         * whoisi/controllers.py (Root.about): About method.
1163
1164         * whoisi/templates/about.mak: Silly about page.
1165
1166 2008-06-25  Christopher Blizzard  <blizzard@0xdeadbeef.com>
1167
1168         * whoisi/templates/nofollow.mak: Add some text to the follow page
1169         that teaches people how to follow others.
1170
1171 2008-06-25  Christopher Blizzard  <blizzard@0xdeadbeef.com>
1172
1173         * whoisi/templates/master.mak: If you're following anyone display
1174         a "login later" link.
1175
1176         * whoisi/templates/login-info.mak: Template that displays login
1177         info.
1178
1179         * whoisi/controllers.py (Root.logininfo): New method that gives
1180         you a link to log in later.
1181
1182 2008-06-23  Christopher Blizzard  <blizzard@0xdeadbeef.com>
1183
1184         * whoisi/api.py: Add getPersonForURL and getURLForTinyLink.
1185
1186         * Rename whoisi.utils.addperson.py to whoisi.utils.url_lookup.
1187         
1188         * whoisi/controllers.py: Rename addperson to url_lookup.
1189
1190 2008-06-23  Christopher Blizzard  <blizzard@0xdeadbeef.com>
1191
1192         * Remove all of widgets/ and all of the .kid files from
1193         whoisi/templates.
1194
1195 2008-06-22  Christopher Blizzard  <blizzard@0xdeadbeef.com>
1196
1197         * whoisi/utils/preview_site.py (convert_feed_to_fake_site): Make
1198         sure to clamp the max_depth that's passed in to the size that's
1199         actually in the feed.
1200
1201         * whoisi/controllers.py (Root.addpersonstatus): Make the depth
1202         that we get for previews the "max depth" instead of the final
1203         depth.
1204
1205 2008-06-22  Christopher Blizzard  <blizzard@0xdeadbeef.com>
1206
1207         * whoisi/config/app.cfg: Turn tg.empty_flash to False so we don't
1208         return a tg_flash on every json call, even if we didn't set it.
1209
1210         * whoisi/utils/fast_api.py: Add fast api sql calls.
1211
1212         * whoisi/api.py: Add an api controller.  Include getMaxPersonID,
1213         getPeople and getPerson.
1214
1215         * whoisi/controllers.py: Add an api contoller to the main
1216         controller.
1217         (Root.nameadd): Fix bug where we were passing newname instead of
1218         name and it was failing with an unknown variable.
1219
1220 2008-06-22  Christopher Blizzard  <blizzard@0xdeadbeef.com>
1221
1222         * whoisi/templates/everyone.mak: Point to the everyone page
1223         instead of the follow page for the More... link (thanks, Joe!)
1224
1225 2008-06-22  Christopher Blizzard  <blizzard@0xdeadbeef.com>
1226
1227         * whoisi/templates/twitter-widget.mak: Make a short link when
1228         we're not doing a preview and use the full link when we are doing
1229         a preview.  Had them reversed.  Oops.
1230         
1231 2008-06-22  Christopher Blizzard  <blizzard@0xdeadbeef.com>
1232
1233         * whoisi/utils/fast_follow.py (fast_people_ids_by_name_for_follower):
1234         Quick search that sorts by name for a particular follower.
1235
1236         * whoisi/templates/follow.mak: Add a way to sort by person instead
1237         of by time.
1238
1239         * whoisi/templates/follow-byname.mak: New template to show a list
1240         of people sorted by their name instead of entries by time.
1241
1242         * whoisi/controllers.py (Root.follow): Add new sort by name mode
1243         for the follow page.  Use a somewhat fast lookup to get the people
1244         ids for this follower, sorted by name.  Use the same code as
1245         search to generate a list of paged results and pass it down to the
1246         follow-byname template.
1247
1248         * whoisi/model.py (Follower.get_person_cache): Get at the person
1249         cache.  Not used by anything, but it's fine leaving it in here.
1250
1251 2008-06-22  Christopher Blizzard  <blizzard@0xdeadbeef.com>
1252
1253         * whoisi/templates/weblog-widget.mak: Fix problem where untitled
1254         topics were escaped so you saw the HTML markup.
1255
1256 2008-06-22  Christopher Blizzard  <blizzard@0xdeadbeef.com>
1257
1258         * whoisi/config/app.cfg: Default to Mako for templating.  Set
1259         default encoding and output encoding to utf-8.  Also turn off
1260         visit and identity tracking since we're not using it for anything.
1261
1262         * whoisi/controllers.py: Change everything to use Mako templating
1263         instead of crappy Kid templating.  Too many changes to document
1264         but most of the widget rendering is driven by render_template
1265         instead of the random widget rendering code.
1266
1267         * Tons and tons of changes to templates.  Look in
1268         whoisi/templates/*.mak for all the various templates.
1269
1270 2008-06-17  Christopher Blizzard  <blizzard@0xdeadbeef.com>
1271
1272         * whoisi/utils/addperson.py (run_db_check): Get rid of the lower()
1273         checks in the database query - they didn't add much and were slow
1274         as fuck.
1275
1276 2008-06-17  Christopher Blizzard  <blizzard@0xdeadbeef.com>
1277
1278         * whoisi/controllers.py (Root.unfollowperson): Make sure to
1279         unfollow once per follow/person match in the database.  There can
1280         be multiples.
1281
1282 2008-06-17  Christopher Blizzard  <blizzard@0xdeadbeef.com>
1283
1284         * whoisi/controllers.py (Root.nameadd): Should be using the alias
1285         id for the audit trail, not the person id.
1286
1287 2008-06-17  Christopher Blizzard  <blizzard@0xdeadbeef.com>
1288
1289         * whoisi/utils/recaptcha.py (recaptcha_check_fail): Don't check
1290         the recaptcha if recaptcha.enabled is set to False in the config
1291         file.
1292
1293         * whoisi/controllers.py (Root.addperson): Add track info to the
1294         new site request.
1295         (Root.addpersonpick): Make sure to pass along the old track info
1296         with the new site request.
1297         (Root.addpersonconfirm): Audit when we add a new person and also
1298         pass track_info from the old site request.
1299         (Root.l): Use function to get tracking info.
1300         (Root.siteaddpost): Add track info when adding a site.
1301         (Root.siteremove): Add audit trail for removing a site.
1302         (Root.nameupdate): Add audit trail for a name change.
1303         (Root.nameremove): Add audit trail for removing an alias.
1304         (Root.nameadd): Add audit trail for adding an alias.
1305         (Root.followperson): Add audit trail for when following someone.
1306         (Root.unfollowperson): Add audit trail for when unfollowing
1307         someone.
1308
1309         * whoisi/model.py (NewSite): Add a track_info field to the new
1310         site request.  We need this so that we can track the original data
1311         that was available when someone wanted to create the new site.
1312         Used by NewSiteAudit, eventually.
1313         (ChangeAudit): New class that holds auditing data.
1314
1315         * services/command/controller.py (NewSiteManager): Add
1316         NewSiteAudit after NewSiteCreate.
1317         (NewLinkedInManager): Same.
1318         (NewPicasaManager): Same.
1319
1320         * services/command/newsite.py (NewSiteSetup): Add the track_info
1321         field to what we pull from the database.  Save it in the new_site
1322         object saved in the state.
1323         (NewSiteAudit): New object that drops an audit item into the audit
1324         table for when we make new sites.  Takes info from the state that
1325         was added when the new site was pulled from the database.
1326
1327         * services/command/linkedin.py (NewLinkedInSetup): Add the
1328         track_info field to what we pull from the database.  Save it in
1329         the new_site object saved in the state.
1330
1331         * whoisi/utils/track.py (get_request_tracking): Make the tracking
1332         code a function.  Returns a tuple of remoteip, proxy + ua that's
1333         available in the request.
1334
1335 2008-06-15  Christopher Blizzard  <blizzard@0xdeadbeef.com>
1336
1337         * whoisi/tests/test_controllers.py: Add a quick and dirty test for
1338         measuring performance.
1339
1340         * test-ws.cfg: New cfg file for running the webserver for tests.
1341
1342         * start-test-whoisi.sh: Use the test-ws.cfg script to start up the
1343         test server.  We're using the test.cfg config for actual testing.
1344
1345 2008-06-15  Christopher Blizzard  <blizzard@0xdeadbeef.com>
1346
1347         * utils/query_everyone_perf.py: Simple test that runs the everyone
1348         query as fast as possible in a loop.
1349
1350 2008-06-15  Christopher Blizzard  <blizzard@0xdeadbeef.com>
1351
1352         * whoisi/controllers.py (Root.p): Add an f=1 optional argument to
1353         the /p method to follow someone in one click.
1354
1355 2008-06-14  Christopher Blizzard  <blizzard@0xdeadbeef.com>
1356
1357         * whoisi/utils/sites.py (fast_sites_for_person): Ignore sites that
1358         have been removed when getting sites for a person.
1359
1360         * whoisi/utils/addperson.py (run_db_check): When doing a db check
1361         for a dup site ignore sites that have been removed.
1362
1363         * whoisi/utils/fast_history.py: Don't show sites that have been
1364         removed for everyone or follow queries.
1365
1366         * whoisi/controllers.py (Root.siteremove): When removing a site
1367         just set the removed flag and set the removed time.
1368
1369         * whoisi/model.py (Site): Add isRemoved flag and removed date to
1370         the site column.  We need this so that we can mark things as
1371         removed, but don't actually remove them.  (This is so we can
1372         recover later.)
1373
1374 2008-06-14  Christopher Blizzard  <blizzard@0xdeadbeef.com>
1375
1376         * whoisi/utils/fast_history.py: For everyone and follow ignore
1377         site_history entries that have on_new set to 1.
1378
1379 2008-06-14  Christopher Blizzard  <blizzard@0xdeadbeef.com>
1380
1381         * services/command/feedparse.py (FeedUpdateDatabaseCommand.insertEntry):
1382         When inserting a new entry look to see if there's a new_site flag
1383         set in the state.  If there is, we're adding a new site and set
1384         the on_new flag for this particular entry.
1385
1386         * whoisi/model.py (SiteHistory): Add an "on_new" bool flag that
1387         tells us if this item was added when the site was first added.
1388
1389 2008-06-14  Christopher Blizzard  <blizzard@0xdeadbeef.com>
1390
1391         * whoisi/utils/fast_history.py: Don't bother trying to re-order
1392         entries as they come out of the database because the id of the
1393         items should be the rough living order of them from here on out.
1394
1395         * services/command/feedparse.py (FeedUpdateDatabaseCommand): Add
1396         an "inserts" array member that keeps track of the entry items we
1397         need to insert into the database.
1398         (FeedUpdateDatabaseCommand.gotEntries): Change the way that we
1399         figure out how to insert items into the database.  Updated items
1400         run first and inserts are done one at a time, iterating through
1401         the feed list from oldest to newest.  We also force the code to
1402         get the id of the new item to make sure it's complete before we
1403         move on to the next item.  This gives us a rough approximation of
1404         oldest-to-newest in the database based only on the id of the site
1405         entry.
1406
1407 2008-06-13  Christopher Blizzard  <blizzard@0xdeadbeef.com>
1408
1409         * whoisi/widgets/templates/person.kid: Make follow + unfollow
1410         different.
1411
1412         * whoisi/widgets/templates/twitter.kid: Make follow + unfollow
1413         different.
1414
1415         * whoisi/widgets/templates/picasa.kid: Make follow + unfollow
1416         different.
1417
1418         * whoisi/widgets/templates/weblog.kid: Make follow + unfollow
1419         different.
1420
1421         * whoisi/widgets/templates/flickr.kid: Make follow + unfollow
1422         different.
1423
1424         * whoisi/controllers.py (Root.followperson): Method now only adds
1425         a person to follow, not a toggle.
1426         (Root.unfollowperson): New method to stop following a person.
1427         (Root.get_follow_text): New method that's used by the follow and
1428         unfollow code to tell you how many people you're following.
1429
1430         * whoisi/model.py: Change set existence testing from if foo to if
1431         foo is None.  That was a bug.  Also make add_person a little more
1432         resistant to race conditions and make remove_person lossy.
1433
1434         * whoisi/static/javascript/follow.js: Re-do the way that we attach
1435         and update following status.  We now have explicit follow and
1436         unfollow classes instead of just a toggle.  We also update the
1437         "Follow Person" with a "Working..." while it's working and update
1438         everything on the page at once.
1439
1440         * whoisi/model.py (Follower.cache_people): Use a set constructor
1441         with a list comprehension - might go faster this way.
1442         (Follower.add_person): Only add a person to this follower if they
1443         aren't already in the database.  This isn't completely free of
1444         race conditions, but it will help.  If we're already following try
1445         to return the current one.
1446         (Follower.remove_person): Use .discard instead of .remove in case
1447         we try and stop following more than once.  This won't generate an
1448         exception.  Lossy, and that's fine.
1449
1450         * whoisi/utils/follow.py: Add a pile of documentation to the
1451         follow filter and follow manager code.  But no code changes.
1452
1453 2008-06-12  Christopher Blizzard  <blizzard@0xdeadbeef.com>
1454
1455         * whoisi/widgets/templates/search.kid: Set up search template to
1456         be used as a header at the top of pages.
1457
1458         * whoisi/templates/index.kid: Use a hand-created search widget
1459         instead of the standard one.  Stick a big logo in there.
1460
1461         * whoisi/templates/person.kid: Remove std header.
1462
1463         * whoisi/templates/search.kid: Remove std header.  Add a nice
1464         css-styled area that gives result info.
1465
1466         * whoisi/templates/follow.kid: Remove std header.
1467
1468         * whoisi/templates/nofollow.kid: Remove std header.
1469
1470         * whoisi/templates/addform.kid: Remove std header and use search
1471         widget instead.  Add a description in bold.
1472
1473         * whoisi/templates/everyone.kid: Remove std header.
1474
1475         * whoisi/static/images/*.png: New logos and images for headers.
1476
1477         * whoisi/templates/master.kid: Get rid of the visibility stuff.
1478         Return "no one" if we're not following anyone for the
1479         num_friends_text page.  Change the friendslink sidebar to an
1480         always visible sidebar.
1481
1482         * whoisi/controllers.py (Root.followperson): Return "no one" if
1483         we're not following anyone.  Also don't bother returning
1484         "still_following" to the calling script.  We're not using that
1485         flag anymore.
1486
1487         * whoisi/static/css/style.css: Change the friendslink to
1488         nav-sidebar since it contains a pile of stuff now.  Make sure the
1489         logo-header is aligned to the bottom.  Add a search-results-info
1490         blue background header. to separate it from the content and the
1491         header at the top of the page.
1492
1493         * whoisi/static/javascript/follow.js: Change to follownum to
1494         update.  Don't bother hiding and showing since we're always
1495         showing now.
1496
1497 2008-06-12  Christopher Blizzard  <blizzard@0xdeadbeef.com>
1498
1499         * whoisi/templates/search.kid: Clean up titles and naming.
1500
1501         * whoisi/templates/follow.kid: Clean up titles and naming.
1502
1503         * whoisi/templates/addform.kid: Clean up titles and naming.
1504
1505         * whoisi/templates/everyone.kid: Clean up titles and naming.
1506
1507         * whoisi/templates/nofollow.kid: Clean up titles and naming.
1508
1509         * whoisi/static/txt/robots.txt (Disallow): Add /addform and
1510         /search to the disallow list.
1511
1512         * whoisi/templates/search.kid: Put search results in the title.
1513
1514         * whoisi/templates/person.kid: Use the person's name in the title
1515         of the page.
1516
1517 2008-06-12  Christopher Blizzard  <blizzard@0xdeadbeef.com>
1518
1519         * whoisi/static/icons/favicon.ico: Updated favicon.
1520
1521         * sources/favicon.png: Mini favicon in png form.
1522
1523         * sources/favicon.ico: Mini favicon.
1524
1525         * sources/whoisi-icon.svg: Full logo.
1526
1527         * sources/whoisi-icon-mini.svg: Mini-icon - used for the favicon
1528         and eventually for the extension.
1529
1530 2008-06-12  Christopher Blizzard  <blizzard@0xdeadbeef.com>
1531
1532         * whoisi/config/app.cfg: Add static pointer to robots.txt.
1533
1534         * whoisi/static/txt/robots.txt: Add robots.txt.
1535
1536 2008-06-12  Christopher Blizzard  <blizzard@0xdeadbeef.com>
1537
1538         * whoisi/utils/preview_site.py (SiteHistoryFakePreview): Remove
1539         some debugging spew.
1540
1541 2008-06-11  Christopher Blizzard  <blizzard@0xdeadbeef.com>
1542
1543         * whoisi/utils/follow.py: Remove debugging spew.
1544         
1545 2008-06-11  Christopher Blizzard  <blizzard@0xdeadbeef.com>
1546
1547         * prod.cfg: Make server.environment="production" so people don't
1548         get random stack traces.
1549
1550         * whoisi/templates/nofollow.kid: Simple "you're not following
1551         anyone" page.
1552
1553         * whoisi/controllers.py (Root.p): If looking up a person doesn't
1554         work raise cherrypy.NotFound (a 404.)
1555         (Root.l): If looking up a short link fails, return a 404.
1556         (Root.follow): If we're not following anyone return a graceful not
1557         following anyone error page.
1558
1559 2008-06-11  Christopher Blizzard  <blizzard@0xdeadbeef.com>
1560
1561         * whoisi/templates/search.kid: Get rid of the number on the next
1562         and previous links - it wasn't accurate.
1563
1564 2008-06-11  Christopher Blizzard  <blizzard@0xdeadbeef.com>
1565
1566         * whoisi/templates/search.kid: Show the full number of results
1567         when displaying search results.  Also make sure to add a link to
1568         the add page when there are no results.
1569
1570         * whoisi/controllers.py (Root.search): Pass down how many search
1571         results there were and where we are in that process.
1572
1573         * whoisi/static/css/style.css: Add a small text subheader for
1574         under the search results.
1575         
1576 2008-06-11  Christopher Blizzard  <blizzard@0xdeadbeef.com>
1577
1578         * whoisi/static/css/style.css: Add a b.search-result-header for
1579         the search results page.
1580
1581         * whoisi/controllers.py (Root.search): Add page handling to the
1582         search results.
1583
1584         * whoisi/templates/search.kid: Add page handling to the search
1585         results page.
1586
1587         * whoisi/widgets/templates/person.kid: Remove the "this is me" and
1588         "spam" until they are done.
1589
1590 2008-06-11  Christopher Blizzard  <blizzard@0xdeadbeef.com>
1591
1592         * whoisi/controllers.py (Root.addpersonconfirm): Make sure that
1593         before allowing a person to be added that the old new_site is in a
1594         "preview_done" state.  This should at prevent someone from adding
1595         a person without having looked at a preview.
1596
1597         * smoketest.txt: Add a set of simple smoketests.
1598
1599 2008-06-10  Christopher Blizzard  <blizzard@0xdeadbeef.com>
1600
1601         * whoisi/widgets/widgets.py (AliasesWidget): Add "other_names"
1602         argument to the aliases widget.
1603
1604         * whoisi/controllers.py (Root.addpersonstatus): Add a
1605         "feed_not_found" error handler.
1606         (Root.nameremove): Add use fast_names_for_person to get the other
1607         names and pass them down into the widget to render.
1608         (Root.nameadd): Same.
1609
1610 2008-06-09  Christopher Blizzard  <blizzard@0xdeadbeef.com>
1611
1612         * whoisi/widgets/templates/person.kid: Use all the passed in data
1613         to render site objects.
1614
1615         * whoisi/widgets/templates/twitter.kid: Use the passed in
1616         other_names instead of generating it locally.
1617
1618         * whoisi/widgets/templates/picasa.kid: Use the passed in
1619         other_names instead of generating it locally.
1620
1621         * whoisi/widgets/templates/personaddconfirm.kid: New confirm
1622         widget to add a person.
1623
1624         * whoisi/widgets/templates/linkedin.kid: Support previews.
1625
1626         * whoisi/widgets/templates/aliases.kid: Use the passed in
1627         other_names instead of generating it locally.
1628
1629         * whoisi/widgets/templates/weblog.kid: Passing down site history
1630         means we don't have to figure it out here.  Support previews.
1631
1632         * whoisi/widgets/templates/flickr.kid: Passing down site history
1633         means we don't have to figure it out here.  Support previews.
1634
1635         * whoisi/widgets/templates/personaddpickfeed.kid: New code to pick
1636         a feed.  Stolen from the new site pick code.
1637
1638         * whoisi/widgets/widgets.py (PersonWidget): Pass down all the new
1639         items by name.
1640         (PersonAddPickFeedWidget): New widget to show a feed pick in the
1641         preview screen.
1642         (PersonAddConfirm): New widget to show a confirm question when
1643         someone is ready to add a new person.
1644
1645         * whoisi/utils/sites.py (fast_sites_for_person): Fast query to get
1646         sites for a person id.
1647         (SiteFake): New fake site object.
1648
1649         * whoisi/utils/preview_site.py (convert_feed_to_fake_site):
1650         Convers a feed from a preview into a fake site and site_history
1651         objects.
1652         (convert_linkedin_to_fake_site): Converts a linkedin preview query
1653         to a simple fake site object.
1654
1655         * whoisi/utils/site_history.py (history_to_clusters): Remove some
1656         debugging spew.
1657
1658         * whoisi/utils/names.py: New classes (NameFake) which is a fake
1659         name object for a fast name lookup query.
1660
1661         * whoisi/utils/addperson.py: New code to look up either a simple
1662         url or a complete feed and see if it's already in the database.
1663         Tries to resolve / vs. no / issues.  Is case-insensitive which is
1664         probably wrong.
1665
1666         * whoisi/utils/fast_history.py (fast_recent_changes_for_follower):
1667         Use SiteHistoryFakeFollower instead of SiteHistoryFake (which is
1668         now a root class that's used by a couple of other classes.)
1669         (fast_recent_changes_for_everyone): Same.
1670         (fast_site_history_for_site): Get site history for a specific site
1671         except faster.
1672         (PersonFake): Make the lookups here flat instead of using
1673         sub-objects.
1674         (SiteFake): Same.
1675         (SiteHistoryFake): Same.
1676         (SiteHistoryFakeBySite): Class that knows how to look up site
1677         history info by name from a query.
1678
1679         * whoisi/utils/flickr.py (flickr_fill_thumbnails): Use entries
1680         directly instead of passing in the entries and length to render.
1681
1682         * whoisi/templates/person.kid: New code to pass down all of the
1683         various vars that are now passed in instead of rendered by the
1684         site templates directly.
1685
1686         * whoisi/templates/search.kid: Don't include the new person form
1687         directly, instead jump to the add person page.
1688
1689         * whoisi/templates/follow.kid: New code that passes down the
1690         site_history object.
1691
1692         * whoisi/templates/addform.kid: New person add form.  Hard codes
1693         some js files, probably shouldn't, but it works for now.
1694
1695         * whoisi/templates/everyone.kid: New code that passes down the
1696         site_history object.
1697
1698         * whoisi/controllers.py (Root.search): Use fast search methods to
1699         get people, names, sites, site_history and anything else we can.
1700         (Root.addform): New method that returns a person add form.
1701         (Root.addperson): First call to create a new person.  Has a
1702         captcha that comes into it and will return state to kick off the
1703         add person cycle.  Checks the URL to see if it's valid and also
1704         checks the db for possible matches to the url that's passed in.
1705         (Root.addpersonstatus): The heart of the cycle to add a new
1706         person.  Checks for error state, if a preview is still in
1707         progress, if someone needs to pick a url, if a preview is
1708         complete, and will render a preview.
1709         (Root.addpersonpick): Code that lets someone pick a url.  Creates
1710         a new new_site request from the old one to get the master service
1711         to pick it up.
1712         (Root.addpersonconfirm): End of the add person progress.  Creates
1713         a new person and sends back a message to redirect.
1714         (Root.p): Clean up the new_site vs. site race condition.  Use fast
1715         queries to render a person.
1716         (Root.siterefresh): Use the new fast site history code.
1717         (Root.getDisplayDepth): Add a fast lookup path for getting the
1718         right depth to look up site history.  This is better than hard
1719         coding it everywhere.
1720         (Root.rendersite): New code that uses the separate site_history
1721         instead of letting the actual site widget figure it out.  Much
1722         cleaner.
1723
1724         * whoisi/search.py (fast_people_ids_by_name): Fast query that will
1725         try and return a set of person_ids for a name query.  Not that
1726         much faster than before, but somewhat.
1727         (SearchService.prettifyName): Don't touch queries that include '@'
1728         or ':'
1729
1730         * whoisi/static/css/style.css: Move out the width of the edit
1731         wrappers to 540px to handle the personadd page.
1732
1733         * whoisi/static/javascript/addform.js: New JS code to handle the
1734         person add form.  Basically includes an inner loop to cycle
1735         through the process of adding a new person.
1736
1737         * services/command/controller.py: New classes for preview for
1738         linkedin, picasa and feeds.
1739
1740         * services/command/newsite.py (NewSiteSetup.gotNewSite): New code
1741         to handle getting urls for previews as well as for a new state.
1742         This just kicks off the state process for a new site.
1743         (NewSiteTryURL.getPreferredFeed): Support getting Flickr preferred
1744         feeds.  Yay!
1745         (NewSiteTryURL.feedLoadDone): Make sure to get the feed type after
1746         the feed load is done.  We use it later in both the preview done
1747         code and the new site done code.
1748         (NewSiteTryURL.getFeedType): Moved here from the createsite code.
1749         (NewSiteCreate.createSite): Use the site type from the state
1750         instead of calling getFeedType().
1751
1752         * services/command/flickr.py: Change classes to allow us to pass
1753         in a photo id into the class that will get the flickr thumbnail
1754         address.  This lets us call it a few times from the preview code
1755         instead of having to get it from the database and put it in the
1756         state.
1757         (FlickrPreviewThumbnails): This class will take a parsed feed and
1758         get the thumbnails for every one that's in it.
1759         (Flickr.isFlickrURL): New class that will return if a particular
1760         url is a flickr url.
1761         (Flickr.getPreferredFeed): Returns a preferred feed for flickr.
1762
1763         * services/command/previewsite.py (PreviewSiteDone): Class that
1764         takes the data out of the state and shoves into the new_site
1765         table.  Basically stores a feed, the type of feed and/or the
1766         current state and saves it.  It tries to work around the mysql 65k
1767         rowsize limit by limiting the number of entries to 6 in total.
1768
1769         * services/command/twitter.py (Twitter.isTwitterURL): Make sure to
1770         return False if it's not a twitter feed.
1771         (Twitter.getPreferredFeed): Spit out some debugging spew if a
1772         preferred feed is detected.
1773
1774         * services/command/picasa.py (PicasaPreviewLoadFeed): A command
1775         that takes the filename of the parsed picasa feed and shoves into
1776         the state variable.
1777
1778         * services/command/linkedin.py (LinkedInPreviewSave): New code to
1779         save a linkedin preview to the new_site table.
1780
1781         * services/master/previewsite.py: New master classes that handle
1782         previewing sites.  Basically a copy of the newsite class for
1783         masters as well.  Knows a little bit about how to turn some URLs
1784         into specific requests.  (LinkedIn and Picasa, basically.)  Copies
1785         lots of detection code from newsite.py and should probably be
1786         merged into a single class at some point.
1787         
1788         * services/master/database.py (DatabaseManager.getPreviewSites):
1789         New methods to handle getting preview sites out of the database
1790         and dispatch them.
1791
1792         * controller-service: Implement the new preview commands.
1793
1794 2008-05-19  Christopher Blizzard  <blizzard@0xdeadbeef.com>
1795
1796         * whoisi/controllers.py (Root.l): Track the useragent field for a
1797         clickthrough.
1798
1799         * whoisi/model.py (ClickThrough): Add a useragent field to the
1800         click through.
1801
1802 2008-05-19  Christopher Blizzard  <blizzard@0xdeadbeef.com>
1803
1804         * whoisi/controllers.py (Root.l): Make sure that we look at the
1805         X-Forwarded-For header for the remote ip because of the proxy
1806         server.
1807
1808 2008-05-18  Christopher Blizzard  <blizzard@0xdeadbeef.com>
1809
1810         * whoisi/controllers.py (Root.l): Insert a record when someone
1811         clicks to another site using our tinyurl-like scheme.
1812
1813         * whoisi/model.py (ClickThrough): New ClickThrough object/table
1814         that tracks when people click through a tinyurl-style url.  Tracks
1815         who did it, when they did it, what their IP was and if there was a
1816         referer.
1817
1818 2008-05-18  Christopher Blizzard  <blizzard@0xdeadbeef.com>
1819         
1820         * whoisi/templates/master.kid: Add google analytics tracking code
1821         to the master template.
1822
1823 2008-05-14  Christopher Blizzard  <blizzard@0xdeadbeef.com>
1824
1825         * whoisi/utils/fast_history.py: For the everyone and follower
1826         functions sort the entries by time and then reverse them so that
1827         we return a time-stable list of links.
1828
1829         * whoisi/templates/follow.kid: Pass the minimum number to the
1830         start for the next page by walking the clusters.
1831
1832         * whoisi/templates/everyone.kid: Pass the minimum number to the
1833         start for the next page by walking the clusters.
1834
1835 2008-05-13  Christopher Blizzard  <blizzard@0xdeadbeef.com>
1836
1837         * whoisi/widgets/templates/picasa.kid: Change _new to _blank.
1838
1839         * whoisi/widgets/templates/weblog.kid: Change _new to _blank.
1840
1841         * whoisi/widgets/templates/flickr.kid): Change _new to _blank.
1842
1843         * whoisi/utils/display.py (expand_href): Change _new to _blank.
1844
1845         * whoisi/utils/twitter.py (expand_user_ref): Change _new to _blank.
1846
1847 2008-05-12  Christopher Blizzard  <blizzard@0xdeadbeef.com>
1848
1849         * whoisi/controllers.py: Add optional start arg to the follow
1850         item.  Use the fast recent changes method to get everything in one
1851         call.
1852
1853         * whoisi/utils/fast_history.py: Add a fast history query for
1854         people to load their history quickly.
1855
1856         * whoisi/templates/follow.kid: Add a More... link at the bottom.
1857
1858 2008-05-12  Christopher Blizzard  <blizzard@0xdeadbeef.com>
1859
1860         * whoisi/utils/fast_history.py: Some deep hacks that do a single
1861         query with fake classes to get history for everyone.  Basically
1862         loads everything we might need to display that data into fake
1863         classes that resolve to array offsets in the data we loaded.
1864
1865         * whoisi/templates/everyone.kid: Include a More... link at the
1866         bottom of the page to see more history for everyone.
1867
1868         * whoisi/controllers.py: Change everyone method to include an
1869         optional start argument.  Use the new
1870         fast_recent_changes_for_everyone call to get the history for
1871         everyone.
1872
1873 2008-05-12  Christopher Blizzard  <blizzard@0xdeadbeef.com>
1874
1875         * services/command/download.py (localDownloadPage): Add user agent
1876         for whoisi.
1877
1878 2008-05-07  Christopher Blizzard  <blizzard@0xdeadbeef.com>
1879
1880         * whoisi/utils/follow.py (add_person): Fix problem where the first
1881         time you followed someone the link didn't show up in the browser.
1882         Make sure to set the current follow in the cherrypy session after
1883         we create the follower for the first time.
1884
1885         * whoisi/widgets/templates/twitter.kid: Use a short link for the
1886         url to a twitter entry on the time display.
1887
1888         * whoisi/widgets/templates/picasa.kid: Use the short link for the
1889         url to a picasa photo.
1890
1891         * whoisi/widgets/templates/weblog.kid: Use the short link for the
1892         url to a weblog entry on the time display.
1893
1894         * whoisi/widgets/templates/flickr.kid: Use the short link for the
1895         url to a flickr photo.
1896
1897         * whoisi/utils/display.py (short_link_ref): New function that
1898         takes an id and returns a link to it.
1899
1900         * whoisi/utils/flickr.py (flickr_fill_thumbnails): Add the id to
1901         what we return for a set of flickr items.
1902
1903         * whoisi/controllers.py: Add an 'l' method that takes a
1904         hex-encoded link and redirects to an actual site.  How we drive
1905         our tinyurl-like functionality.
1906
1907         * whoisi/static/css/style.css: Add short-link rules so that we can
1908         make short links show as as black, not blue.
1909
1910 2008-05-06  Christopher Blizzard  <blizzard@0xdeadbeef.com>
1911
1912         * whoisi/controllers.py: Sigh.  Add unicode() calls to any widgets
1913         that are rendered and passed back as part of a json result.  Work
1914         around problems where utf-8 encoded byte strings were being
1915         re-encoded as utf-8 and getting corrupted.
1916
1917 2008-05-05  Christopher Blizzard  <blizzard@0xdeadbeef.com>
1918
1919         * prod.cfg: server.webpath should not have been set to the full
1920         website - just disable it now since our paths all start with '/'
1921         anyway.
1922
1923 2008-05-04  Christopher Blizzard  <blizzard@0xdeadbeef.com>
1924
1925         * whoisi/widgets/templates/person.kid: Change /person refs to /p.
1926
1927         * whoisi/widgets/templates/twitter.kid: Change /person refs to /p.
1928
1929         * whoisi/widgets/templates/picasa.kid: Change /person refs to /p.
1930
1931         * whoisi/widgets/templates/weblog.kid: Change /person refs to /p.
1932
1933         * whoisi/widgets/templates/flickr.kid: Change /person refs to /p.
1934
1935         * whoisi/controllers.py: Change /person refs to /p.
1936         
1937 2008-05-04  Christopher Blizzard  <blizzard@0xdeadbeef.com>
1938
1939         * prod.cfg: Woot.  Config file that handles the proxy setup stuff
1940         for running behind an apache reverse proxy.
1941
1942 2008-05-04  Christopher Blizzard  <blizzard@0xdeadbeef.com>
1943
1944         * html-feed-scrape-service (ScrapeProtocol.runCommand): Remove
1945         finally: block that only closed a file that would automatically be
1946         closed.  Needed for python 2.4.
1947
1948         * feed-parse-service (FeedParseProtocol.runCommand): Remove
1949         finally: block that only closed a file that would automatically be
1950         closed.  Needed for python 2.4.
1951
1952         * picasa-poll-service (PicasaProtocol.runCommand): Remove finally:
1953         block that only closed a file that would automatically be closed.
1954         Needed for python 2.4.
1955
1956         * services/command/setup.py (FileToStateCommand.doCommand): Remove
1957         exception block that we don't need.  File objects clean themselves
1958         up.
1959
1960 2008-05-03  Christopher Blizzard  <blizzard@0xdeadbeef.com>
1961
1962         * services/command/flickr.py (FlickrUpdateDatabase.doCommand):
1963         Oops, forgot to json encode the actual thumbnail location.
1964
1965 2008-05-03  Christopher Blizzard  <blizzard@0xdeadbeef.com>
1966
1967         * utils/clean_tmp.sh: Simple shell script that cleans up /tmp -
1968         run it if you're refreshing websites or you'll run out of disk
1969         pretty quickly.
1970
1971 2008-05-03  Christopher Blizzard  <blizzard@0xdeadbeef.com>
1972
1973         * utils/convert-display-cache.py: Utility here for the sake of
1974         history.  Converts the old flickr display_cache to the new
1975         json-enabled one.  DO NOT RUN AGAINST A PRODUCTION DATABASE.
1976
1977 2008-05-03  Christopher Blizzard  <blizzard@0xdeadbeef.com>
1978
1979         * services/command/controller.py (FlickrCacheManager): Attach the
1980         flickr cache error handler.
1981
1982         * services/command/flickr.py (FlickrCacheError): New class to
1983         handle flickr errors when getting thumbnails. It now catches the
1984         xmlrpclib.Fault that's generated when there's an error.  If it's
1985         the "photo not found" or "permission denied" error then we save
1986         that to the database for later processing.
1987
1988         * whoisi/utils/flickr.py (flickr_fill_thumbnails): Use the new
1989         format for the display_cache - serialized json data.  If there's a
1990         thumb var, set it.  If not, it's probably an error and just show
1991         the grey box but don't indicate that it needs a refresh.  NULL
1992         still means refresh later.
1993
1994 2008-05-03  Christopher Blizzard  <blizzard@0xdeadbeef.com>
1995
1996         * services/master/worker.py (Worker.commandComplete): Get rid of a
1997         pile of latency - start a new command after the last one completes
1998         until waiting for the next tick.  Should process commands a lot
1999         faster now.
2000
2001 2008-05-03  Christopher Blizzard  <blizzard@0xdeadbeef.com>
2002
2003         * services/master/sitelock.py: Remove debug spew.
2004
2005         * services/master/worker.py (WorkManager.reviveDeadWorkers): Fix
2006         bug where connecting workers would end up in limbo because they
2007         would be left off both the dead and connecting worker lists.  We
2008         fix this by making sure that we put everything on the connecting
2009         list before trying to process any of them and make sure the dead
2010         pool is actually empty before processing.
2011
2012 2008-04-28  Christopher Blizzard  <blizzard@0xdeadbeef.com>
2013
2014         * services/command/newsite.py (NewSiteTryURL.feedLoadDone): Call
2015         the done callback directly instead of creating the site object.
2016         (NewSiteCreate): New class that does what the end of NewSiteTryURL
2017         used to do.  It takes all the state from TryURL and creates the
2018         site object.  No changes to the functional code, just wrap it in a
2019         new command.  We're doing this to get ready for being able to
2020         "preview" a URL when we add a new person.  We don't want to
2021         actually create the site object in that case.
2022
2023 2008-04-27  Christopher Blizzard  <blizzard@0xdeadbeef.com>
2024
2025         * whoisi/utils/follow.py (FollowManager.follow_for_id): Make sure
2026         to update the last_visit time for a user when getting the cookie.
2027
2028 2008-04-27  Christopher Blizzard  <blizzard@0xdeadbeef.com>
2029
2030         * whoisi/static/javascript/person.js: Make sure that we don't load
2031         a new captcha when removing a name or changing a primary name if
2032         there's already one in progress.  Also make sure to return false;
2033         from all of the handlers if we don't load the captchas otherwise
2034         it tries to load urls.
2035
2036 2008-04-27  Christopher Blizzard  <blizzard@0xdeadbeef.com>
2037
2038         * whoisi/widgets/templates/person.kid: Changes for new name
2039         editing code.  Get rid of the old text areas and stick with a
2040         simple link that loads the form.
2041
2042         * whoisi/widgets/templates/nameupdate.kid: New widget for name
2043         update.
2044
2045         * whoisi/widgets/widgets.py (NameUpdateWidget): New name update
2046         widget.
2047
2048         * whoisi/controllers.py (Root.nameupdate): Handle captcha
2049         failures.  Also return the new name when we're done.  Used by the
2050         JS.
2051         (Root.nameupdateform): Code to return an empty form to update the
2052         name.
2053
2054         * whoisi/static/javascript/person.js: Add hooks to change name
2055         based on a captcha + loaded form.  Remove code for old name update
2056         system.
2057
2058 2008-04-27  Christopher Blizzard  <blizzard@0xdeadbeef.com>
2059
2060         * whoisi/widgets/templates/siteremove.kid: New form for removing a
2061         site.
2062
2063         * whoisi/widgets/widgets.py (SiteRemoveWidget): Add
2064         site_remove_widget.
2065
2066         * whoisi/controllers.py (Root.siteremove): Return an error if the
2067         captcha didn't verify.
2068         (Root.siteremoveform): New method that returns a form + captcha.
2069
2070         * whoisi/static/javascript/person.js: Add hooks to get a captcha
2071         when removing a site from a person.
2072
2073 2008-04-27  Christopher Blizzard  <blizzard@0xdeadbeef.com>
2074
2075         * whoisi/widgets/templates/nameremove.kid: New form for removing a
2076         name.
2077
2078         * whoisi/widgets/widgets.py (NameRemoveWidget): New name remove
2079         widget.
2080
2081         * whoisi/controllers.py: Add new "nameremoveform" method that
2082         returns a form for removing a name and also change "nameremove" so
2083         that it checks for captcha validity.
2084
2085         * whoisi/static/javascript/person.js: Add hooks to the name remove
2086         code so that it loads a form to load a name instead of just
2087         removing it directly.
2088
2089 2008-04-26  Christopher Blizzard  <blizzard@0xdeadbeef.com>
2090
2091         * whoisi/widgets/templates/nameadd.kid: Don't use spans and add a
2092         recaptcha widget.  Wrap with the nice person-edit-wrapper div so
2093         it has a nice look.  Add error text and re-render newname if it's
2094         there.
2095
2096         * whoisi/widgets/widgets.py (NameAddWidget): Add "error_text" and
2097         "newname" as arguments to the nameadd widget.
2098
2099         * whoisi/controllers.py (Root.nameadd): Check captcha results
2100         against service and return the form if there's an error.
2101
2102         * whoisi/static/css/style.css: Wrap name adds with a nice editing
2103         div.
2104
2105         * whoisi/static/javascript/person.js: Add hooks to pass captcha
2106         data when adding a new name.  Also support handling a non-done
2107         status when adding a name and re-show the widget if there was a
2108         captcha error.
2109
2110 2008-04-26  Christopher Blizzard  <blizzard@0xdeadbeef.com>
2111
2112         * whoisi/widgets/templates/siteadd.kid: Change the size from 50 to
2113         45 so it doesn't overrun on ff3/linux.
2114
2115 2008-04-26  Christopher Blizzard  <blizzard@0xdeadbeef.com>
2116
2117         * whoisi/controllers.py (Root.followperson): Fix bug where
2118         stopping following a person wasn't working.  Need to pass the
2119         person id to follow.is_following_person() not the whole person
2120         object.
2121
2122 2008-04-25  Christopher Blizzard  <blizzard@0xdeadbeef.com>
2123
2124         * whoisi/widgets/templates/siteaddpickfeed.kid: Use
2125         site-add-wrapper div so it has a nice look.
2126
2127         * whoisi/widgets/templates/recaptcha.kid: New widget to display a
2128         recaptcha.  Not used yet.
2129
2130         * whoisi/widgets/templates/siteadderror.kid: Use the
2131         site-add-wrapper div so it has a nice look.
2132
2133         * whoisi/widgets/templates/siteaddstatus.kid: Use the
2134         site-add-wrapper div so it has a nice look.
2135
2136         * whoisi/widgets/templates/siteadd.kid: Re-do a lot of this so
2137         that it uses the new yellow look, handles error text and includes
2138         a target for a recaptcha ajax load.  Add a cancel button.
2139
2140         * whoisi/widgets/widgets.py (ExtJSLink): New class used for
2141         external JS locations (instead of just local ones.)
2142         (PersonWidget): Person now requires the recaptcha widget.
2143         (SiteAddWidget): New param for this widget: error_text.
2144         (RecaptchaWidget): New widget (not used yet.)
2145
2146         * whoisi/utils/recaptcha.py (recaptcha_check_fail): New function
2147         to check if a recaptcha failed.  Returns None if there was no
2148         error.
2149
2150         * whoisi/controllers.py (Root.siteaddpost): Check adding a site
2151         vs. a recaptcha and return an error if it doesn't pass.
2152
2153         * whoisi/static/css/style.css: New div.site-add-wrapper class that
2154         has a yellowish background with a border so we can tell where the
2155         edges of the form are.  Don't use div.url-pick-list anymore so we
2156         remove it from here.
2157
2158         * whoisi/static/javascript/person.js: Add onCaptchaError callback
2159         to handle a "captcha_error" message from the server when getting a
2160         status update.  Break out attaching add site form events into its
2161         own function so we can do it from more than one place.  New
2162         function "request_recaptcha" to load a recaptcha widget when we
2163         add a new site form.  Add a cancel button so that we can stop
2164         adding a new account.  Pass recaptcha info to the server when we
2165         submit the form.  Make sure that if a captcha is in progress we
2166         don't try and add more than one site at a time - the recaptcha
2167         interfaces don't allow it.
2168
2169 2008-04-23  Christopher Blizzard  <blizzard@0xdeadbeef.com>
2170
2171         * whoisi/utils/display.py (confirm_escape): Deep hack that needs
2172         to be fixed.  URLs are everything until a spare or the end of the
2173         line.  Fixed a twitter entry like http://google.com&lt; that would
2174         end up generating invalid HTML.
2175
2176 2008-04-22  Christopher Blizzard  <blizzard@0xdeadbeef.com>
2177
2178         * whoisi/utils/twitter.py (expand_user_ref): Add the '-' symbol to
2179         the list of characters we expand for urls.
2180
2181 2008-04-21  Christopher Blizzard  <blizzard@0xdeadbeef.com>
2182
2183         * whoisi/search.py (SearchService.peopleByName): Support searching
2184         aliases.  This includes looking for exact and partial matches in
2185         aliases and searching on the right hand side of a group alias.
2186         You can also do something like mozilla: and see an entire group.
2187
2188 2008-04-20  Christopher Blizzard  <blizzard@0xdeadbeef.com>
2189
2190         * tests/twisted/network/test_picasa.py: Make sure to test for the
2191         "page_not_found" error is set when we failed to find a site.
2192
2193         * tests/twisted/network/test_newsite.py: Add new conditions to
2194         tests to make sure that we can tell the difference between a page
2195         not found or an invalid feed or a feed not found on a page.
2196
2197         * tests/twisted/network/test_linkedin.py: Add new conditions to
2198         tests to make sure that error codes are properly set when we fail
2199         to add a new linkedin page.
2200
2201         * whoisi/widgets/widgets.py (SiteAddErrorWidget): New site add
2202         error widget.
2203
2204         * whoisi/controllers.py (Root.person): Make sure that we don't
2205         load errors or done new sites when we look for sites in progress.
2206         (Root.siteaddstatus): Return errors when we fail to add a new
2207         site!
2208
2209         * whoisi/static/css/style.css: Add new div.error type that draws
2210         text as red.
2211
2212         * whoisi/static/javascript/person.js: Add a handler when we get an
2213         "error" status so that we log something reasonable.
2214
2215         * services/command/exceptions.py (FeedNotFoundError): Add another
2216         error type that we'll use to distinguish between not finding a
2217         feed and not being able to parse a feed.
2218
2219         * services/command/newsite.py (NewSiteError.handleError): Handle
2220         various exceptions and try and put a code in the error field that
2221         makes sense.
2222
2223         * services/command/picasa.py (PicasaPollFeed.parseError): Return a
2224         PageNotFoundError() when there's an error so we can return it to
2225         the user.
2226
2227 2008-04-20  Christopher Blizzard  <blizzard@0xdeadbeef.com>
2228
2229         * whoisi/utils/twitter.py (get_text): Add exception handler in
2230         case twitter hands us an empty string and we try to parse it.
2231
2232 2008-04-18  Christopher Blizzard  <blizzard@0xdeadbeef.com>
2233
2234         * whoisi/templates/master.kid: Remove the auto-focus code from the
2235         master template and move it into the index page - which is the
2236         only place we want it.
2237
2238         * whoisi/templates/index.kid: See above.
2239
2240 2008-04-05  Christopher Blizzard  <blizzard@0xdeadbeef.com>
2241
2242         * whoisi/widgets/templates/flickr.kid: Use personID instead of
2243         person.id.  Also pass the id to is_following_person() instead of
2244         the full person object.
2245
2246         * whoisi/widgets/templates/weblog.kid: Use personID instead of
2247         person.id.  Also pass the id to is_following_person() instead of
2248         the full person object.
2249
2250         * whoisi/widgets/templates/picasa.kid: Use personID instead of
2251         person.id.  Also pass the id to is_following_person() instead of
2252         the full person object.
2253
2254         * whoisi/widgets/templates/twitter.kid: Use personID instead of
2255         person.id.  Also pass the id to is_following_person() instead of
2256         the full person object.
2257
2258         * whoisi/widgets/templates/person.kid: Use personID instead of
2259         person.id.  Also pass the id to is_following_person() instead of
2260         the full person object.
2261
2262         * whoisi/utils/site_history.py:
2263         (get_recently_changed_site_history_for_follower): Vastly improve
2264         the queries that we do by doing a single query to collect all the
2265         data instead of lots of little ones.  Also use SiteHistory.siteID
2266         instead of SiteHistory.site.id which was triggering lots of extra
2267         queries.
2268
2269         * whoisi/utils/follow.py: Updates to use the new Follower
2270         interfaces in the model - pass more through to the model.
2271
2272         * whoisi/model.py: Add code to the Follower object to cache the
2273         people who are associated with the follower.  Also include
2274         interfaces to add and remove people to the follower - use these so
2275         that the cache is coherent.
2276
2277 2008-04-05  Christopher Blizzard  <blizzard@0xdeadbeef.com>
2278
2279         * whoisi/config/app.cfg: Change the identify failure url to
2280         '/userlogin' instead of '/login'.
2281
2282         * whoisi/utils/follow.py (login): New method that sets the current
2283         follower on the request method and sets the cookie.
2284
2285         * whoisi/templates/login_not_found.kid: New error page if a login
2286         isn't found.
2287
2288         * whoisi/controllers.py: Move 'login' and 'logout' methods to
2289         'userlogin' and 'userlogout' since we're not using the identity
2290         methods for the main login.  Add new 'login' method that looks for
2291         a private key on the command line and calls the follower login
2292         method if it's connected to a method.  From there it redirects to
2293         the '/follow' page.  If the follower isn't found it falls back to
2294         an error page.
2295
2296         * whoisi/static/css/style.css: Add a style for h2.error - make it
2297         red.
2298
2299 2008-04-03  Christopher Blizzard  <blizzard@0xdeadbeef.com>
2300
2301         * whoisi/widgets/templates/person.kid: Use the <span> tag for the
2302         link-action links instead of putting the class on the links
2303         themselves.
2304
2305         * whoisi/static/javascript/person.js: Change add site link to
2306         include the span that includes it now when loading the new form.
2307         Also the same when we're done loading the new site and also when
2308         we're editing aliases on the person edit page.
2309
2310         * whoisi/templates/person.kid: Don't show the search form on the
2311         person edit page.  It's just confusing.
2312
2313 2008-04-03  Christopher Blizzard  <blizzard@0xdeadbeef.com>
2314
2315         * whoisi/widgets/templates/twitter.kid: Fix how we get entries.
2316
2317         * whoisi/widgets/templates/picasa.kid: Fix how we get entries.
2318
2319         * whoisi/widgets/templates/weblog.kid: Fix how we get entries.
2320
2321         * whoisi/widgets/templates/flickr.kid: Fix how we get entries.
2322
2323 2008-04-02  Christopher Blizzard  <blizzard@0xdeadbeef.com>
2324
2325         * whoisi/widgets/templates/twitter.kid: Support follow display
2326         type.
2327
2328         * whoisi/widgets/templates/picasa.kid: Support follow display
2329         type.
2330
2331         * whoisi/widgets/templates/weblog.kid: Support follow display
2332         type.
2333
2334         * whoisi/widgets/templates/flickr.kid: Support follow display
2335         type.
2336
2337         * whoisi/templates/follow.kid: Pass in the "follow" display type
2338         instead of time.
2339
2340 2008-04-02  Christopher Blizzard  <blizzard@0xdeadbeef.com>
2341
2342         * whoisi/widgets/templates/person.kid: Add code to show "stop
2343         following" if you're following someone.
2344
2345         * whoisi/widgets/templates/weblog.kid: Make the follow
2346         functionality work for weblogs.
2347
2348         * whoisi/widgets/widgets.py (PersonWidget): Pull in the new
2349         follow.js code for the person widget.
2350
2351         * whoisi/utils/site_history.py:
2352         (get_recently_changed_site_history_for_follower): Crazy code that
2353         starts with a follower, gets the people they are following, gets
2354         the sites owned by those people and then the recent site history
2355         for each ones of those sites.
2356         (get_recently_changed_site_history): Change the call to get the
2357         recent site history for everyone to just use the added field
2358         instead of the publish and update dates - much more reliable if
2359         we're running constant refreshes.
2360
2361         * whoisi/utils/follow.py: The heart of the new follow code.  Most
2362         of this is the FollowManger class which is a singleton class and
2363         is called from multiple threads in the server.  It knows how to
2364         look up cookie IDs and set cookies.  It also sets the per-thread
2365         cherrypy.request.follow object that's used from inside of the web
2366         server.  Access to follow information is all done through this
2367         util class instead of through the web server.  A lot of this code
2368         is borrowed from the turbogears visit code and even starts up a
2369         thread to do caching.  We don't do that caching yet but we can if
2370         it becomes an issue.
2371
2372         * whoisi/templates/follow.kid: New template for following.
2373         Basically the same as the everyone page until we add person-based
2374         lists as well as date-driven lists.
2375
2376         * whoisi/templates/everyone.kid: Just jam the follow.js script in
2377         here for now.
2378
2379         * whoisi/templates/master.kid: Show the friendslink bit if the
2380         person is following anyone.
2381
2382         * whoisi/controllers.py (follow): New method that is the landing
2383         point for how you follow a set of people.  It uses the new
2384         site_history methods and then collects them into clusters.  Same
2385         as with the everyone page.
2386         (Root.followperson): Method that lets you toggle if you want to
2387         follow a person or not.  Only exposed through json.  Returns new
2388         content and the number of people that are being followed.
2389
2390         * whoisi/model.py (Visit): Make sure that we're using utcnow() for
2391         the created date.
2392         (Group): utcnow for created.
2393         (User): utcnow for created.
2394         (Follower): New class that describes someone who follows someone
2395         else.  Includes hashes for the cookie, a private and public key,
2396         the ability to store an email address and if they are associated
2397         with a particular person on the site.  Also track an exprires date
2398         and when they were created.  Also includes some classmethods for
2399         looking up by the various key types.
2400         (FollowPerson): New class that maps a follower to people they are
2401         following.
2402
2403         * whoisi/static/javascript/follow.js: Javascript file to handle
2404         following functionality.  Simple classes and ajax.
2405
2406         * start-whoisi.py: Add hooks to the startup and shutdown points in
2407         the server so we can register our global cherrypy filter to catch
2408         and set the following cookie.
2409
2410 2008-03-27  Christopher Blizzard  <blizzard@0xdeadbeef.com>
2411
2412         * whoisi/controllers.py (Root.everyone): Pass the search_widget in
2413         as a param to trigger jquery getting included.  Ugh.
2414
2415         * whoisi/templates/everyone.kid: Don't need to include the search
2416         widget from here since it's passed in from the controller now.
2417
2418 2008-03-27  Christopher Blizzard  <blizzard@0xdeadbeef.com>
2419
2420         * whoisi/utils/twitter.py (expand_user_ref): Add _ to the
2421         characters to expand for a twitter user.
2422
2423 2008-03-27  Christopher Blizzard  <blizzard@0xdeadbeef.com>
2424
2425         * whoisi/summary.py (SummaryCreator.output): Call confirm_escape()
2426         on text nodes we output.  Fix val's blog and havoc's blog that
2427         included & characters.
2428
2429 2008-03-26  Christopher Blizzard  <blizzard@0xdeadbeef.com>
2430
2431         * services/master/newsite.py (NewSite.startProcess): Bug fix.
2432         Adding new linkedin sites wasn't working because we were passing
2433         the wrong command to the contoller process.  The controller
2434         process wasn't returning an error when we did that and was
2435         silently returning success.
2436
2437         * services/command/exceptions.py (BadCommandException): Exception
2438         that's derived from pb.Error so we can throw it across processes.
2439
2440         * controller-service (Controller.remote_doCommand): Make things a
2441         little more robust by using if/elif instead of just if and fall
2442         through to throwing an exception if the master passes down a bad
2443         command.
2444
2445 2008-03-26  Christopher Blizzard  <blizzard@0xdeadbeef.com>
2446
2447         * whoisi/model.py (SiteHistory): Change getLastTouched to check
2448         the time reported by a feed against the added time in case clock
2449         skew pushes entries into the future.  This happens in the real
2450         world.  I AM LOOKING RIGHT AT YOU ZE FRANK.
2451
2452 2008-03-25  Christopher Blizzard  <blizzard@0xdeadbeef.com>
2453
2454         * whoisi/widgets/templates/twitter.kid: Add code that expands href
2455         and @something style references.
2456
2457         * whoisi/utils/display.py (expand_href): Code that expands
2458         http://foo urls for use on twitter.
2459
2460         * whoisi/utils/twitter.py (expand_user_ref): Utility that expands
2461         @something references for twitter pages.
2462
2463 2008-03-24  Christopher Blizzard  <blizzard@0xdeadbeef.com>
2464
2465         * master-service: Code to do automatic refreshes is checked in.
2466         Add a -r or --refresh to schedule refreshing sites.
2467
2468         * services/master/refreshmanager.py: New file that manages when we
2469         refresh sites.  It will schedule any site that hasn't been
2470         refreshed on startup for immediate refresh.  Any sites that have
2471         been refreshed in the last half an hour will be scheduled for a
2472         refresh at a random time in the next half an hour.  Not the most
2473         efficient code in the universe but it should work well for the
2474         time being.
2475
2476 2008-03-24  Christopher Blizzard  <blizzard@0xdeadbeef.com>
2477
2478         * whoisi/widgets/templates/twitter.kid: Everything I just said is
2479         a lie.  Sometimes twitter data is escaped, sometimes it is not.
2480         Yay for the intarweb.
2481
2482         * whoisi/utils/display.py (confirm_escape): Add gt + lt to the
2483         list of things we look for before escaping.
2484
2485 2008-03-24  Christopher Blizzard  <blizzard@0xdeadbeef.com>
2486
2487         * whoisi/widgets/templates/twitter.kid: Twitter entries are
2488         already escaped so don't escape them again.  Fixes things like
2489         '<8' and '->' in twitter displays.
2490
2491 2008-03-23  Christopher Blizzard  <blizzard@0xdeadbeef.com>
2492
2493         * whoisi/widgets/templates/search.kid: Add a nice "everyone" and
2494         "random" link to the search widget.
2495
2496         * whoisi/widgets/templates/twitter.kid: Code to support time-based
2497         display based on entries that are passed in.
2498
2499         * whoisi/widgets/templates/picasa.kid: Code to support time-based
2500         display based on entries that are passed in.
2501
2502         * whoisi/widgets/templates/weblog.kid: Code to support time-based
2503         display based on entries that are passed in.
2504
2505         * whoisi/widgets/templates/flickr.kid: Code to support time-based
2506         display based on entries that are passed in.
2507
2508         * whoisi/widgets/widgets.py: Pass in 'display_entries' to each of
2509         the site widgets.
2510
2511         * whoisi/utils/site_history.py: New file to help generate site
2512         history.  Code that will generate a list of site history newer
2513         than a passed in date.  Also code that will take a list of site
2514         history objects, order them based on their age and cluster them to
2515         the site they belong.
2516
2517         * whoisi/utils/flickr.py (flickr_fill_thumbnails): Pass in the len
2518         and the entries so that we can treat the list as an array.
2519
2520         * whoisi/templates/person.kid: Use the search widget so we get the
2521         nice random/everyone links under the box.
2522
2523         * whoisi/templates/everyone.kid: New template that returns an
2524         "everyone" page.  Right now it's pretty brute force, doesn't
2525         pageinate.  But it's a start.
2526
2527         * whoisi/controllers.py (Root.everyone): New everyone url that
2528         will return all the activity for the last day.  Not very fast or
2529         complete, but it works reasonably well.
2530         (Root.random): New method that returns a random person.  Fun!
2531
2532         * whoisi/model.py (Person.getRandom): New method that returns a
2533         random person id.
2534         (SiteHistory.getLastTouched): Better way of calculating the last
2535         touched value.  Should be faster, too.
2536
2537         * services/master/newsite.py (NewSite.pollDone): Fix bug where a
2538         new flickr add wasn't triggering a flickr thumbnail refresh.
2539
2540 2008-03-22  Christopher Blizzard  <blizzard@0xdeadbeef.com>
2541
2542         * services/command/linkedin.py (LinkedInCreateCommand.doCommand):
2543         Fix a bug test cases found!  Make sure to set the "type" in the
2544         state so that we can return it to the master service once a new
2545         site is added.
2546
2547         * services/command/picasa.py (PicasaCreateCommand.doCommand): Fix
2548         the same bug in the picasa code.
2549
2550 2008-03-21  Christopher Blizzard  <blizzard@0xdeadbeef.com>
2551
2552         * master-service (MasterService.start): Create a
2553         SiteLock() (site_lock) object during startup.
2554
2555         * services/command/controller.py (ProtoManager.succeeded): Make
2556         sure to pass through any return values that might be returned by a
2557         command.
2558
2559         * services/command/newsite.py: Return the new feed type and
2560         site_id when returning from a new site addition.
2561
2562         * services/command/feedparse.py (FeedUpdateDatabaseCommand.done):
2563         Return the site_id when we're done.
2564
2565         * services/master/database.py: Only do a full scan for unfinished
2566         flickr photos once at startup.  Pass down the site_id for
2567         refreshes.
2568
2569         * services/master/feedrefresh.py: Subclass from Command.  Also
2570         refresh flickr images for a flickr site if there was a change.
2571
2572         * services/master/picasa.py: Subclass from Command.
2573
2574         * services/master/newsite.py: Subclass from Command.  Also, if we
2575         add a flickr site kick off a new flickr refresh command instead of
2576         expecting the master process to find it by polling everything.
2577         Also support return values from the controller that's running the
2578         job thanks to the change to perspective broker (yay, return
2579         values!)
2580
2581         * services/master/sitelock.py: New code that maintains a global
2582         lock on which site has work being done in it.  It's attached to
2583         the master process + object.  Uses a simple set to maintain which
2584         sites currently have jobs attached to them.
2585
2586         * services/master/linkedin.py: Subclass from Command.
2587
2588         * services/master/flickr.py: Subclass from Command.
2589
2590         * services/master/worker.py Lots of changes to allow the Command
2591         class to be a base class for commands on the backend.  Also
2592         supports locking for sites so that we don't run more than one
2593         command per site at the same time.  Also support return values to
2594         the caller for a command.  We also support losing connections from
2595         the perspective broker code and restarting jobs.  Important note:
2596         re-adding to the worker queue happens from the lost connection
2597         handler, not from the error code for an individual job where an
2598         error is ignored.  Otherwise we can end up with more than one copy
2599         of a job in the queue.
2600
2601 2008-03-18  Christopher Blizzard  <blizzard@0xdeadbeef.com>
2602
2603         * services/command/controller.py: Simple changes to support using
2604         deferreds instead of a line-based protocol.
2605
2606         * services/master/worker.py: Tons of work here to use perspective
2607         broker instead of line-based protocols.  Get rid of the
2608         WorkerProtocol and use standard deferreds instead.  Gets the root
2609         object from the worker and makes calls to a remote doCommand
2610         method.  No change to the arguments or formats yet - that will
2611         come later when we want to restructure a bit.  (Should move to a
2612         method per type at some point.)  Support the worker going away by
2613         catching pb.PBConnectionLost when a command returns an error.
2614         Also catch bp.DeadReferenceError when we callRemote() to the
2615         remote end (untested.)
2616
2617         * controller-service: Refactor code to return deferreds directly
2618         and be a perspective broker service instead of having a custom
2619         line-based service.  Much more sane now.
2620
2621         * services/protocol/controller.py: Removed because we replaced it
2622         with the perspective broker code.
2623
2624 2008-03-15  Christopher Blizzard  <blizzard@0xdeadbeef.com>
2625
2626         * services/command/newsite.py (NewSiteTryURL.doCommand): Bug fix -
2627         make sure that we pass the url as a str(), not unicode to make
2628         sure that the DownloadCommand doesn't freak out.
2629         (NewSiteTryURL.startSecondDownload): Same with not passing
2630         unicode.
2631         (NewSiteTryURL.getFeedType): Call urlparse.clear_cache() after
2632         parsing the url - workaround as usual.
2633
2634         * services/command/twitter.py (Twitter.isTwitterURL): Bug fix -
2635         call urlparse.clear_cache() to work around bugs in urlparse
2636         module.
2637
2638         * whoisi/model.py (Person): Change the other_names to a
2639         MultipleJoin() instead of being a RelatedJoin().  Just easier to
2640         work with, even if you end up with more data.
2641         (Name): Same as with Person - change to a ForeignKey() instead of
2642         a RelatedJoin().
2643
2644         * whoisi/static/javascript/person.js: New support for js methods
2645         to support name editing and alias addition and removal.
2646
2647         * whoisi/static/css/style.css: New style for div.other-names that
2648         is for the alias display.
2649
2650         * whoisi/controllers.py: Support an optional 'mode' argument for
2651         the person display method.  Right now it's either 'edit' or
2652         defaults to 'full'.  New siteremove method.  New nameupdate
2653         method.  New nameremove method.  New nameaddform method.  New
2654         namedadd method.  All for supporting name editing and aliases.
2655
2656         * whoisi/templates/person.kid: Pass down the display value from
2657         the controller so we know if we're editing or not.  Also don't
2658         show the search form if we're editing a person.
2659
2660         * whoisi/widgets/widgets.py: New name_add_widget and
2661         aliases_widget.
2662
2663         * whoisi/widgets/templates/person.kid: Lots of changes to support
2664         editing of a person entry.  Remove the wikipedia entry that was
2665         commented out.
2666
2667         * whoisi/widgets/templates/nameadd.kid: New form for editing a
2668         person's primary name.  Only used during editing.
2669
2670         * whoisi/widgets/templates/aliases.kid: New widget to display
2671         aliases - supports editing or display.
2672
2673         * whoisi/widgets/templates/twitter.kid: Support for 'edit'
2674         display.
2675
2676         * whoisi/widgets/templates/picasa.kid: Support for 'edit' display.
2677
2678         * whoisi/widgets/templates/linkedin.kid: Support for 'edit'
2679         display.
2680
2681         * whoisi/widgets/templates/weblog.kid: Support for 'edit' display.
2682
2683         * whoisi/widgets/templates/flickr.kid: Support for 'edit' display.
2684
2685 2008-03-11  Christopher Blizzard  <blizzard@0xdeadbeef.com>
2686
2687         * lib/feedparser.py: Patch that fixes <media:title> entries
2688         overwriting global <title> setting.  Also see
2689         http://code.google.com/p/feedparser/issues/detail?id=18
2690
2691 2008-03-11  Christopher Blizzard  <blizzard@0xdeadbeef.com>
2692
2693         * patches/feedparser-title.patch: Patch that fixes feeds with that
2694         include <media:title> (like stuart.)
2695
2696         * patches/README: Description of patches.
2697
2698         * lib/feedparser.py: Add a local copy of feedparser 4.1 so we can
2699         add some patches.  Sigh.
2700
2701         * feed-parse-service: Use the local copy of feedparser.
2702
2703 2008-03-09  Christopher Blizzard  <blizzard@0xdeadbeef.com>
2704
2705         * whoisi/static/tests/one_entry.atom: New test feed with one item
2706         in it - cribbed right from Wikipedia (so we know it's valid -
2707         right?)
2708
2709         * whoisi/static/tests/relative_feed.html: New test page with a
2710         relative URL to a feed.
2711
2712         * tests/twisted/network/test_newsite.py: New test that adds a feed
2713         with a relative URL.
2714
2715         * services/command/newsite.py: Relative URL handling for feeds!
2716         Yay!  Found an awesome bug in the urlparse code in python related
2717         to the cache (basically it would return unicode typed strings when
2718         parsing a non-unicode string in some cases.)  Also clean up the
2719         way that we detect if a page has is a valid html file and how we
2720         deal with it if it's not.
2721
2722         * services/command/download.py: Print out the url being downloaded
2723         - useful for debugging!
2724
2725 2008-03-09  Christopher Blizzard  <blizzard@0xdeadbeef.com>
2726
2727         * html-feed-scrape-service: Only pull out link information if
2728         we're inside the <head> element.  This keeps us from accidentally
2729         picking up link information from other parts of the
2730         document (especially if it happens to be an rss feed!) and leaving
2731         us with false elements.  Frank Hecker's blog triggered this one. [
2732         NEEDS TEST CASE. ]
2733
2734 2008-03-09  Christopher Blizzard  <blizzard@0xdeadbeef.com>
2735
2736         * whoisi/summary.py: Code that probably needs test cases more than
2737         anything else in the entire universe.  When calling output make
2738         sure to call end_block() so that any leftover pieces are shoved
2739         into a block.  This fixes display problems on spot's blog and
2740         doesn't seem to affect other sites that I looked at (about 60 of
2741         them in my personal database.)
2742
2743 2008-03-08  Christopher Blizzard  <blizzard@0xdeadbeef.com>
2744
2745         * services/master/worker.py: Add some cheap timings to each
2746         command so we can start to get a sense of how long it takes to
2747         process a job.
2748
2749 2008-03-08  Christopher Blizzard  <blizzard@0xdeadbeef.com>
2750
2751         * services/command/controller.py (RefreshManager): Convert to
2752         using DownloadCommand instead of FeedDownloadCommand.
2753
2754         * services/command/feedparse.py: Get rid of the
2755         FeedDownloadCommand command and convert the one caller to use the
2756         standard DownloadCommand.  It had awful error handling and wasn't
2757         needed anyway.
2758
2759         * services/master/worker.py: Add very simple rate limiting to how
2760         many jobs we send to a worker.  Limited to 10 jobs at once for
2761         now.
2762
2763         * tests/twisted/network/test_feedrefresh.py: Add two more tests -
2764         timeout and a 404 test.
2765
2766 2008-03-08  Christopher Blizzard  <blizzard@0xdeadbeef.com>
2767
2768         * services/command/linkedin.py (LinkedInUpdateCommand.insertDone):
2769         Make sure to update the last_update field in the database whenever
2770         there's a change in the data.
2771
2772         * tests/twisted/network/test_linkedin_refresh.py: Add checking for
2773         update of lastUpdate on the model whenever there's an update of
2774         the linkedin current data.  This should also fix the spurious
2775         errors that happened when running some of the linkedin tests as
2776         well.
2777
2778 2008-03-08  Christopher Blizzard  <blizzard@0xdeadbeef.com>
2779
2780         * services/command/controller.py (PicasaRefreshManager): Add the
2781         RefreshSiteError error handler.
2782         
2783         * tests/twisted/network/test_picasa_refresh.py: Add two test cases
2784         for refreshing picasa - a success and a failure.
2785
2786 2008-03-08  Christopher Blizzard  <blizzard@0xdeadbeef.com>
2787
2788         * services/command/controller.py (RefreshManager): Add the
2789         RefreshSiteError error handler.
2790
2791         * tests/twisted/network/test_feedrefresh.py: Two tests added for a
2792         feed refresh: one success and one failure.
2793
2794 2008-03-08  Christopher Blizzard  <blizzard@0xdeadbeef.com>
2795
2796         * whoisi/static/javascript/person.js: Changes to support adding
2797         the person from the header instead of from the bottom of a
2798         person's display.  This actually simplifies the code a bit.  Also
2799         fix a bonus bug I ran across where the site-add-pick code was
2800         being run because I was checking for non-null instead of non-zero
2801         array len.
2802
2803         * whoisi/controllers.py (Root.search): Pass down the person_widget
2804         as one of the arguments.  (Can probably do this directly from the
2805         person widget, but we'll fix all that up later.)
2806
2807         * whoisi/templates/search.kid: Instead of using our own home-grown
2808         person display use the person widget and just pass down the
2809         display type.  This also means we can add new sites directly from
2810         the search page.
2811
2812         * whoisi/templates/person.kid: Add a display="full" argument to
2813         the person widget when we're displaying the person.
2814
2815         * whoisi/widgets/widgets.py (PersonWidget): Add a "display"
2816         argument to the person widget.
2817
2818         * whoisi/widgets/templates/person.kid: Move the "Add a New Site"
2819         link to the top of the person display widget so that it's more
2820         visible.  This also means that when you're adding a new site that
2821         the editing is done at the top of the entry instead of the bottom
2822         which feels much more natural.  Also add support for passing in
2823         the display type so we can re-use this template from the search
2824         page.  Fix the display of the person so it links to the actual
2825         person's page instead of to nothing.
2826
2827 2008-03-07  Bryan W Clark  <clarkbw@gnome.org>
2828
2829         * services/command/service.py (SubService): Removed the hardcoded
2830         home directory "/home/blizzard/src/whoisi/" and did a path lookup
2831         using the current flie.  We need the root directory so we have to
2832         join the current file with 2 ".."'s.  Also needed to use the
2833         os.path.join in the start function, replacing the self.dir +
2834         self.name since now the self.dir doesn't have a needed trailing
2835         slash for directory addition.
2836
2837 2008-03-07  Christopher Blizzard  <blizzard@0xdeadbeef.com>
2838
2839         * services/command/newsite.py (NewSiteTryURL): Change the way that
2840         we finish loading a feed.  In the case where someone gave us a url
2841         that turned out to be html and we scraped out a feed we now handle
2842         downloading that feed from NewSiteTryURL directly.  We used to
2843         pass that off to another command, but that meant we created a
2844         half-finished Site object which could leave a mess behind.  Now we
2845         don't create the Site object unless we know that we got a valid
2846         URL and a valid RSS feed.  Also clean up the way that we set "url"
2847         and "feed_url" so that it's consistent before we hit tryFeed
2848         and/or createSite.  This fixes not only the site creation bug but
2849         also means that we properly return errors whenever someone passes
2850         us an invalid URL or an invalid/unreadable feed.  Yay!
2851
2852         * services/command/controller.py (NewSiteManager): Remove the
2853         FeedDownloadCommand - we do that from inside of NewSiteTryURL
2854         instead.
2855
2856 2008-03-06  Christopher Blizzard  <blizzard@0xdeadbeef.com>
2857
2858         * whoisi/model.py (Site.getOrderedHistory): Add a second sort that
2859         will sort by the 'touched' column.  This is for blogs (I AM
2860         LOOKING AT YOU LUKE MACKEN) that don't have any date information
2861         in their feeds.
2862
2863 2008-03-06  Christopher Blizzard  <blizzard@0xdeadbeef.com>     
2864
2865         * services/command/newsite.py (NewSiteTryURL.loadDone): Use an
2866         exception to generate the need_pick signal when adding a new site.
2867         This will use the new exception and pass the feed data up to the
2868         handler through the exception's data member.  Remove the
2869         needPick() method and handlers as they are not needed anymore.
2870         (NewSiteError.handleError): Add code to handle the need pick
2871         exception and update the new_site table with the right data.  Also
2872         add a different callback that will return success from the error
2873         handler.  (Confused yet?)
2874
2875         * services/command/base.py (CommandManager.subSuccess): Sweep away
2876         the final remnants of the Old Republic by removing the state
2877         "stop" and "error" checks.  If we want to stop or generate an
2878         error we use exceptions.  Like adults.
2879
2880         * tests/twisted/network/test_newsite.py: New test for returning
2881         multiple feeds and making sure they are valid.
2882
2883 2008-03-05  Christopher Blizzard  <blizzard@0xdeadbeef.com>
2884
2885         * whoisi/widgets/templates/flickr.kid: Add size information to the
2886         image so that there's some feedback before images finish loading
2887         and it doesn't just look like a blank empty space.
2888
2889         * whoisi/widgets/templates/picasa.kid: Same.
2890
2891 2008-03-05  Christopher Blizzard  <blizzard@0xdeadbeef.com>
2892
2893         * whoisi/static/tests/*: Add some files for testing errors in the
2894         new site code.
2895
2896         * services/command/controller.py (NewSiteManager): Add the error
2897         handler to the new site code.  Yay!
2898
2899         * services/command/exceptions.py (InvalidFeedError): New exception
2900         for an invalid exception.
2901
2902         * services/command/newsite.py: Convert old state["error"] style
2903         error handling to the new twisted errback handling.  In the
2904         process fix a couple of bugs.  Some errors like an invalid url
2905         actually return an error now.  Invalid RSS needs work as the test
2906         cases reveal.
2907
2908         * tests/twisted/network/test_newsite.py: Add some tests for
2909         loading an page not found, a test for loading a page with no feed
2910         in it, a test for a link to a feed that's invalid and a direct
2911         link to an invalid feed.  Right now the html page -> invalid test
2912         still fails because it turns out the code is broken (and it's in
2913         the todo list.)
2914
2915 2008-03-04  Christopher Blizzard  <blizzard@0xdeadbeef.com>
2916
2917         * whoisi/widgets/templates/twitter.kid: Change the order of
2918         display so that the "7 minutes ago" comes after the message, just
2919         like every other type of element.
2920
2921 2008-03-04  Christopher Blizzard  <blizzard@0xdeadbeef.com>
2922
2923         * whoisi/utils/display.py (confirm_escape): Add html entities to
2924         the possible escape list - see shaver's blog.
2925
2926 2008-03-04  Christopher Blizzard  <blizzard@0xdeadbeef.com>
2927
2928         * services/command/newsite.py (NewSiteTryURL.getFeedType): Fix bug
2929         where I wasn't passing the url to isTwitterURL - breaking adding
2930         new sites.
2931
2932 2008-03-03  Christopher Blizzard  <blizzard@0xdeadbeef.com>
2933
2934         * tests/nose/test_newsite.py: Add simple tests that test the
2935         preferredURL handling for twitter.
2936
2937         * services/command/newsite.py (NewSiteTryURL.loadDone): Add hooks
2938         to get the preferred feed when there's a list of feeds.
2939         (NewSiteTryURL.getPreferredFeed): Actual code that picks the right
2940         feed.  This is the hook where we should add version stuff too.
2941         Most feeds have common names (Atom, Atom 1.0, RSS, RSS 0.9) and
2942         they are all very regular - should be easy to figure out the feed
2943         we should be using automatically.  We could also just drop
2944         anything with the word "comment" in it.
2945
2946         * services/command/twitter.py: New file for twitter-related
2947         command stuff.  Right now it just contains a helper class that
2948         lets you detect if you're looking at a twitter url and a class
2949         that will pick out the preferred feed from a list of twitter
2950         feeds.
2951
2952 2008-03-03  Christopher Blizzard  <blizzard@0xdeadbeef.com>
2953
2954         * whoisi/utils/display.py (confirm_escape): New function that
2955         looks at a string and makes sure it's escaped.  Yes, this should
2956         go in on the backend but a python stack trace is always sad-making
2957         no matter what.
2958
2959         * whoisi/widgets/templates/weblog.kid: Make sure that urls and
2960         titles are escaped.
2961
2962 2008-03-03  Christopher Blizzard  <blizzard@0xdeadbeef.com>
2963
2964         * tests/twisted/network/test_download.py: New 307 test case.
2965
2966         * services/command/feedparse.py (FeedDownloadCommand.doCommand):
2967         Use localDownloadPage instead of the one in the twisted web
2968         client.
2969
2970         * services/command/download.py: Make a localDownloadPage method
2971         that's a copy of downloadPage found in the twisted web client api.
2972         We need to do this because we need to add a 307 handler.  Phik's
2973         blog for some reason returns a 307 for the feed and it was just
2974         erroring out.  As Joe points out, that's probably not valid for a
2975         HTTP/1.0 request like twisted makes, but whatever.  We should
2976         handle it.
2977
2978 2008-03-03  Christopher Blizzard  <blizzard@0xdeadbeef.com>
2979
2980         * services/command/newsite.py (NewSiteTryURL.getFeedType): Fix bug
2981         where urls like 'flickr.com/photos/...' weren't being recognized
2982         as flickr feeds.
2983
2984 2008-03-03  Christopher Blizzard  <blizzard@0xdeadbeef.com>
2985
2986         * whoisi/widgets/templates/person.kid: Make sure to pass the
2987         display="full" to the widgets.
2988
2989         * whoisi/widgets/templates/twitter.kid: Display param.
2990
2991         * whoisi/widgets/templates/picasa.kid: Display param.
2992
2993         * whoisi/widgets/templates/weblog.kid: Display param.
2994
2995         * whoisi/widgets/templates/linkedin.kid: Display param.
2996
2997         * whoisi/widgets/templates/flickr.kid: Display param.
2998
2999         * whoisi/widgets/widgets.py: Change all the widgets to support the
3000         "display" param.  (Probably not needed, but it's here anyway.)
3001
3002         * whoisi/widgets/templates/personadd.kid: Move code to render the
3003         person add text from this widget as it's used in the search with
3004         results as well as without now.
3005
3006         * whoisi/templates/index.kid: -> whoisi.com
3007
3008         * whoisi/templates/search.kid: Lots of code and look changes so
3009         that the search results are pleasing.  We render sites for people
3010         now using the new display="search" param to a site render widget.
3011         I also added code that toggles the "Add X not found here" add
3012         field so we can add people from the search page.
3013
3014         * whoisi/controllers.py: Add code that passes down a
3015         "display=full" to sites when they are being rendered from a new
3016         site result.  Otherwise we just get the summaries when we've added
3017         a new site.
3018
3019 2008-03-02  Christopher Blizzard  <blizzard@0xdeadbeef.com>
3020
3021         * tests/twisted/network/test_feedparse.py: Add a pile of notes
3022         about what we need to add here for tests.
3023
3024         * tests/twisted/local/test_feedparse.py: Two tests that test at a
3025         very high level parsing and updating the database.  Right now
3026         there are some simple assertions to make sure that data is being
3027         put into the database and that the right number of entries is
3028         there.  Not comprehensive at all.  Also contains a test with a
3029         feed without IDs in it to test that code in the feedparser.py
3030         module.  Similarly incomplete, but it did find some bugs in the
3031         code I wrote.  Data for this test in the data/ dir.
3032         
3033         * whoisi/model.py: Add a note about what lastUpdate means to the
3034         Site object.  Add a 'touched' value to the SiteHistory object.
3035
3036         * services/command/database.py: We no longer catch database error
3037         when they happen and try to reconnect to the database.  The
3038         underlying bindings seem to do this whenever the server goes away
3039         anyway.  And it didn't work.
3040
3041         * services/command/feedparse.py: Lots of changes here with a few
3042         goals: we only update items when there's an actual change.  We
3043         also set a 'touched' timestamp on a site_history item when we make
3044         an insert or update on it.  We also support feeds that don't
3045         include IDs (which would have broken us _badly_.)  Also should be
3046         a lot more robust in a lot of areas.  Also, when we're doing a set
3047         of inserts + updates we also handle errors at the database level a
3048         lot more gracefully.  Had to re-read the docs on DeferredList a
3049         few times.  In the past last_update on the site object used to be
3050         compared to the feed last update and we would use it to skip the
3051         entire update process.  I don't trust feeds to do that right, so
3052         we're using it instead as a "last time something changed on the
3053         site" timestamp.  We only update it when we do an insert or update
3054         on the site_history.
3055
3056 2008-02-27  Christopher Blizzard  <blizzard@0xdeadbeef.com>
3057
3058         * tests/twisted/network/test_picasa.py: New tests for picasa
3059         additions.  Just a simple success + failure test for now.
3060
3061         * services/command/feedparse.py: Just some formatting cleanups.
3062
3063         * services/command/service.py (ParseProcess.lineReceived): Make
3064         sure to return twisted failure objects instead of strings.  If we
3065         got a string that we recognize from the subprocess, don't keep
3066         looking for matches.  If an error returns an arg, pass it along
3067         with the exception.
3068
3069         * services/command/picasa.py (Picasa.userForPath): Remove dead
3070         code that had a return before it - it was never called.
3071
3072         * services/command/newsite.py (NewSiteError.handleError): Fix bug
3073         where we were adding the callback to the wrong deferred.  (How did
3074         this ever work?)
3075
3076         * services/command/exceptions.py (ServiceSubprocessError): New
3077         exception that's thrown when we have a subprocess failure of some
3078         kind.
3079
3080         * services/command/controller.py (NewPicasaManager): Add the
3081         NewSiteError error handler.
3082
3083 2008-02-25  Christopher Blizzard  <blizzard@0xdeadbeef.com>
3084
3085         * services/command/feedparse.py (FeedUpdateDatabaseCommand): Bug
3086         fix.  We added some columns to the site_history table and we do
3087         have a bit of code that does select * and then picks off data by
3088         offset.  So flickr photos were coming up with bogus data in their
3089         display cache.
3090
3091 2008-02-25  Christopher Blizzard  <blizzard@0xdeadbeef.com>
3092
3093         * tests/twisted/network/test_linkedin_refresh.py: Add a check to
3094         make sure that the lastPoll value in the model is set after we do
3095         a refresh.
3096
3097         * services/command/siterefresh.py (RefreshSiteDone): Add code so
3098         that we update last_poll in the database after we're done with a
3099         refresh.  Needs site_id set in the state, which all of the refresh
3100         commands do.
3101
3102 2008-02-25  Christopher Blizzard  <blizzard@0xdeadbeef.com>
3103
3104         * services/command/linkedin.py: Convert our update command to use
3105         "site_id" instead of "id" in the state so we can use it later to
3106         update the database.
3107
3108 2008-02-24  Christopher Blizzard  <blizzard@0xdeadbeef.com>
3109
3110         * services/command/feedparse.py (FeedRefreshSetup): Take the site
3111         refresh id and get the site id before finishing.  Also save the
3112         site refresh id so that we can use it to update the database
3113         later.
3114
3115         * services/master/database.py (DatabaseManager.startRefresh):
3116         Change calling convention to just pass down the id, not the
3117         site_id.
3118
3119         * services/master/feedrefresh.py (FeedRefresh.startProcess): Don't
3120         update the database when we're done with a refresh - that's up to
3121         the controller now.
3122
3123         * services/command/controller.py (RefreshManager): Use the
3124         RefreshSiteDone() call to update our status when we're done with a
3125         refresh.
3126
3127 2008-02-24  Christopher Blizzard  <blizzard@0xdeadbeef.com>
3128
3129         * services/command/newsite.py (NewSiteTryURL.createSite): Set the
3130         "created" time when adding a site.
3131
3132         * services/command/picasa.py (PicasaCreateCommand.doCommand): Set
3133         the "created" time when adding a site.
3134
3135         * services/command/feedparse.py (FeedUpdateDatabaseCommand): Set
3136         the time for the "added" field when adding a site history entry.
3137
3138         * whoisi/model.py (Site): Add a "created" field to the site so we
3139         know when it was added to the database.
3140
3141 2008-02-23  Christopher Blizzard  <blizzard@0xdeadbeef.com>
3142
3143         * services/command/controller.py (PicasaRefreshManager): Add the
3144         RefreshSiteDone() command to the end of the chain - it updates the
3145         state in the database.
3146
3147         * services/command/picasa.py: Use the site_refresh id instead of
3148         the site id.  Requires looking up the site.  Store that id in the
3149         state as site_refresh_id which is used when we're done with a
3150         refresh to set the done or error state.
3151
3152         * services/master/database.py: Pass down the id of the
3153         site_refresh, not the id of the site.
3154
3155         * services/master/picasa.py: Use site_refresh id instead of the
3156         site id when telling the controller to start a refresh for picasa.
3157
3158 2008-02-23  Christopher Blizzard  <blizzard@0xdeadbeef.com>
3159
3160         * services/command/siterefresh.py: New file that contains some
3161         commands related to refreshing sites.  RefreshSiteDone() and
3162         RefreshSiteError() know how to update the site_refresh table with
3163         the right status.
3164
3165         * services/command/controller.py (LinkedInRefreshManager): Add an
3166         error handler.  Call RefreshSiteDone() once we're done with a
3167         refresh to update the database with a "done" flag.
3168
3169         * services/command/linkedin.py (RefreshLinkedInSetup): Add another
3170         call to the database to translate the site_refresh_id to the
3171         site_id.  We save the site_refresh_id in the state because it's
3172         used in the error and complete case once the linkedin refresh is
3173         done.
3174         (LinkedInScrapeCommand.doCommand): Testing hooks to test
3175         add/change/delete when we parse linkedin entries.
3176         
3177         * services/master/database.py (DatabaseManager.startRefresh): Only
3178         pass the id of the refresh, not the site id.
3179
3180         * services/master/linkedin.py: Change the way we start a linkedin
3181         refresh to use the refresh id instead of the site id.  This way
3182         the controller can update the status once it's done with something
3183         useful (an error, for example) instead of it being done in the
3184         master code.  Stop updating the site_refresh table from the
3185         linkedin code.  Will do the other refreshes later.
3186
3187         * whoisi/model.py (SiteRefresh): Add an 'error' field so we can
3188         save what kind of error we had.  Will be used later.
3189
3190         * tests/twisted/network/test_linkedin.py: Bug fix in
3191         confirmCreateTimes() - make sure to assert that we have a site
3192         before calling sync() on it.
3193         
3194         * tests/twisted/network/test_linkedin_refresh.py: Set of tests for
3195         testing linkedin refreshes.  Simple tests so far that test a
3196         simple success, a failure, no changes, an addition, and a
3197         deletion.  Also checks to make sure that the current entry has not
3198         changed and that the changelog that's created contains accurate
3199         information.
3200
3201 2008-02-21  Christopher Blizzard  <blizzard@0xdeadbeef.com>
3202
3203         * tests/twisted/local/test_newsite.py: New site test moved to the
3204         network tests.
3205
3206         * tests/twisted/local/test_commandmanager.py: Tests for the
3207         command manager.  Tests what happens if you raise an exception
3208         from a command or throw an errback.  Has success tests as well.
3209
3210         * tests/twisted/network/test_download.py: Add a check for
3211         RUN_LONG_TESTS before running the connection timeout test.  Will
3212         display [SKIPPED] if it's being skipped.
3213
3214         * tests/twisted/network/test_newsite.py: One simple test that adds
3215         a new site and tries to run it to completion.  Needs tons and tons
3216         more tests.
3217
3218         * tests/twisted/network/test_linkedin.py: New tests that test
3219         adding a new linkedin site.  Includes pretty good coverage, but
3220         far from complete.  Tests a lot of error conditions as well by
3221         poking errors into the command manager.
3222
3223         * runtests.sh: Set a variable RUN_LOG_TESTS=1 if you want a long
3224         test to run (connection timeouts, etc.)
3225
3226         * services/command/controller.py (NewLinkedInManager): Add
3227         NewSiteError as the error_handler on the manager.
3228
3229         * services/command/newsite.py (NewSiteError): New class that is
3230         the error handler for newsite style commands.  Only used by the
3231         linkedin new code so far.  Will set the "error" flag for a new
3232         site based on the original id that was passed into the command
3233         manager.
3234
3235         * services/command/download.py: the DownloadCommand now handles a
3236         couple of test states that will cause test failures.  It will also
3237         return a proper Failure object if a call or download fails.
3238
3239         * services/command/linkedin.py: Adding a new linkedin site now has
3240         proper error checking and test cases.  Yay!  Lots of use of the
3241         getTest(0 call to generate errors in a lot of places.  Inserting a
3242         new site now properly sets the dates on the site object.
3243
3244         * services/command/exceptions.py: New file that contains
3245         exceptions used by commands.  Just contains a PageNotFoundError
3246         exception so far.
3247
3248         * services/command/base.py: Solid error handling for the
3249         CommandManager class.  You can now set a error_handler on the
3250         command manager that will be called when there's an exception or
3251         failure.  We now properly handle direct exceptions from doCommand
3252         calls to subcommands and properly handle errbacks and callbacks
3253         from subcommands.  Subcommands are now required to return proper
3254         Failure objects via twisted.  You can also poke a value into the
3255         "testfail" state in order to generate failures in commands.  Also
3256         the BaseCommand class now has a getTest() method that will return
3257         the testfail value.
3258
3259         * whoisi/model.py (Site): Add 'created' column so we can know when
3260         a site was added.
3261         
3262 2008-02-17  Christopher Blizzard  <blizzard@0xdeadbeef.com>
3263
3264         * tests/twisted/local/test_newsite.py: First unit test for the
3265         entire NewSiteManager code.  Many more to follow.
3266
3267         * tests/twisted/network/test_download.py: Unit tests for the
3268         download code.  Test some various connection failures, DNS lookup
3269         failures and a commented out connection timed out error.  Really
3270         just testing the framework more than the code itself.
3271
3272         * start-test-db.py: Startup script for creating a new, clean test
3273         database and starting it up.  Need this for automated tests.
3274
3275         * start-test-whoisi.sh: Startup script for starting a test
3276         instance of the web server.  Used for running autmated tests.
3277
3278         * services/command/newsite.py: Minor changes - move the deferred
3279         creation to __init__ and comment out some debug spew.
3280
3281         * services/command/base.py (BaseCommand.doCommand): Change the
3282         signature to require a state that's passed in.  Everyone uses it,
3283         might as well change the base signature.
3284
3285         * services/command/download.py: Change the download command to be
3286         a little more like the other commands.  If there's an error in
3287         doCommand() directly try to catch it and still pass it as part of
3288         an errback so we don't need any special handling.  Use callbacks
3289         that are member methods instead of global functions so we can use
3290         the right errback as well.
3291
3292         * services/command/database.py: Add an arg to the
3293         DatabaseCommandManager startup code that takes the connection
3294         parameters to be used.
3295
3296         * controller-service: Add code to pass in the connection type to
3297         the DatabaseCommandManager - needed to connect to test databases.
3298
3299 2008-02-16  Christopher Blizzard  <blizzard@0xdeadbeef.com>
3300
3301         * controller-service: Moved code to controller.py.
3302
3303         * services/command/controller.py: Move code from
3304         controller-service to this file so that we can build tests aroudn
3305         it.  controller-service is a very simple chunk of code now.
3306
3307         * services/command/download.py (DownloadCommand.doCommand): If
3308         someone doesn't pass in the url as an argument then try and get it
3309         from the state instead.  Need to fix this later and have one way
3310         to do it.
3311
3312         * services/master/newsite.py (NewSite.normalize): Add code to
3313         recognize a linkedin.com/pub/ style public url.
3314
3315         * whoisi/utils/flickr.py: Make sure to escape the title for an
3316         image - it can contain html and make the xhtml stuff in tg freak
3317         out.
3318
3319         * tests/nose/test_newsite.py (TestNewSite.test_linkedin): Add a
3320         test for the linkedin.com/pub/* style url.
3321
3322 2008-02-15  Christopher Blizzard  <blizzard@0xdeadbeef.com>
3323
3324         * feed-parse-service (FeedParseProtocol.runCommand): Make sure to
3325         set an empty "display_cache" item on an entry item.
3326
3327         * whoisi/widgets/templates/person.kid: Add support for picasa.
3328
3329         * whoisi/widgets/templates/picasa.kid: Template for picasa
3330         widgets.  Pretty similar to the flickr widget.
3331
3332         * whoisi/widgets/templates/flickr.kid: Render 10 images (two rows)
3333         instead of one.
3334
3335         * whoisi/widgets/widgets.py: New code to support the picasa
3336         widgets.
3337
3338         * whoisi/utils/sites.py (site_value): Add picasa after flickr in
3339         the order of sites as they are rendered.
3340
3341         * whoisi/utils/picasa.py (picasa_get_summary): Not sure we need
3342         this.  Return a summary or "" if there isn't one.
3343
3344         * whoisi/controllers.py: Render the picasa widget when someone has
3345         it in their feed.
3346
3347         * tests/nose/test_newsite.py: Code to test linkedin and picasa new
3348         sites.  Make sure that picasa urls are parsed into usernames
3349         properly.
3350
3351         * picasa-poll-service: New service that will get the picasa data
3352         feeds and parse out the updates.  Largely a wrapper for code in
3353         the picasa command.
3354
3355         * services/command/picasa.py: New picasa commands and support.
3356         Parse usernames into urls.  Code that will also parse the output
3357         from a google data feed and output it into the same format that
3358         the feedupdate command will suck into the database.
3359
3360         * services/command/feedparse.py (FeedUpdateDatabaseCommand.doCommand):
3361         
3362         Add a new state "feed_parsed_filename" that lets us pass in a
3363         filename instead of just passing it in as an arg to the command.
3364         Fix issues where an update will remove cached display_info that's
3365         stored in the row.
3366
3367         * services/master/database.py: New code to support picasa.
3368
3369         * services/master/picasa.py: New class to handle picasa refreshes.
3370
3371         * services/master/newsite.py: Remove bogus docs.  Delete code that
3372         called startPoll() - that code hasn't existed for a while.  Add
3373         support for picasa.
3374
3375         * controller-service: Support for picasa feeds.
3376
3377 2008-02-06  Christopher Blizzard <blizzard@0xdeadbeef.com>
3378
3379         * Add linkedin widget for display.
3380
3381         * Add support to all the services to refresh linkedin
3382         pages (different than feeds.)
3383
3384 2008-02-05  Christopher Blizzard <blizzard@0xdeadbeef.com>
3385
3386         * Add support for twitter to the feed and display code.
3387
3388         * Start adding code to support linkedin.
3389
3390         * Start adding some simple test cases to start testing the
3391         linkedin code which is not done yet.  This is a huge chunk of
3392         code.  Most everything is in tests/ - need to support nose-style
3393         and trial-style tests.
3394
3395 2008-01-25  Christopher Blizzard  <blizzard@0xdeadbeef.com>
3396
3397         * services/command/newsite.py (NewSiteTryURL.feedLoadDone): Fix
3398         bug where the link that's included in a feed isn't used to update
3399         the URL in the database.
3400
3401         * whoisi/widgets/templates/weblog.kid: Move the widget into its
3402         own div so we can put properties on the link-collection-item div.
3403         We'll use this at some point in the future once weblog items
3404         contain images.
3405
3406         * whoisi/widgets/templates/flickr.kid: Put the widget in its own
3407         <div> that is removed so we can put properties on the
3408         link-collection-item div.  Use the new thumbnails helper function
3409         to fill in known thumbnails.
3410
3411         * whoisi/utils/flickr.py (flickr_fill_thumbnails): Helper function
3412         for the flickr widget that's used to figure out which thumbnails
3413         are found and which ones aren't.
3414
3415         * whoisi/source/flickr-blank-75x75.svg: Source image for the
3416         "blank" flickr image that's used when we don't have the thumbnail