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