« Return to Thread: WIP Note Directory Watcher Patch (was Watch for Changes Patch)

Re: WIP Note Directory Watcher Patch (was Watch for Changes Patch)

by Sandy Armstrong :: Rate this Message:

Reply to Author | View in Thread

(Adding tomboy-list back to cc)

On Sat, May 2, 2009 at 6:13 PM, Michael Fletcher
<m.fletcher@...> wrote:

>> Okay, then is it alright to filter on *.note and *.note.tmp?
>> Technically, Tomboy should work fine with notes that have non-GUID
>> names, so I'd like to get rid of the "if (note_id.Length != 36)" check
>> and replace it with a file extension check.
> This code was meant to make sure the note was actually a note (instead
> of say a manifest.xml file).
>
> I think everything will still work with a "*.note" filter.  The code
> considers renaming to the file "a change" so it should see the note
> change.
>
> In an earlier conversation I had said that I was checking all files
> (*) so I could recognize the write,delete,rename pattern.  This wasn't
> necessary.  Seeing the rename event is sufficient to detect the
> change.
Okay, I made some minor changes in git, and I have a bigger patch
attached to this email that I intend to push, but thought it might be
useful to have you review it first if you have time.  The main changes
are:

* A lot of try/catching and error-checking to catch bad note file
edits (needs more testing)
* Add/Delete notes using proper Tomboy API
* Use LoadForeignNoteXml to update more than just note title and content
* Save DateTimes for Note.Saved events, use them to ignore file
changes that happen within 3 seconds of such an event.  I didn't do
this with Note deletion events because they error out pretty
gracefully already.
* Prefix all log statements with "NoteDirectoryWatcher: "

This will be part of Monday's release, so if you have time to review
that would rock.

Thanks,
Sandy

[0001-Update-NoteDirectoryWatcher-to-load-note-data-more-a.patch]

From e9e9a72a80a3495010e8fa11823ef0320dea6da1 Mon Sep 17 00:00:00 2001
From: Sandy Armstrong <sanfordarmstrong@...>
Date: Sat, 2 May 2009 20:28:09 -0700
Subject: [PATCH] Update NoteDirectoryWatcher to load note data more accurately and to be aware of Tomboy-generated file events.

---
 .../NoteDirectoryWatcherApplicationAddin.cs        |  123 ++++++++++++++------
 1 files changed, 88 insertions(+), 35 deletions(-)

diff --git a/Tomboy/Addins/NoteDirectoryWatcher/NoteDirectoryWatcherApplicationAddin.cs b/Tomboy/Addins/NoteDirectoryWatcher/NoteDirectoryWatcherApplicationAddin.cs
index 157328b..7802a61 100644
--- a/Tomboy/Addins/NoteDirectoryWatcher/NoteDirectoryWatcherApplicationAddin.cs
+++ b/Tomboy/Addins/NoteDirectoryWatcher/NoteDirectoryWatcherApplicationAddin.cs
@@ -2,6 +2,7 @@
 using System;
 using System.Collections.Generic;
 using System.IO;
+using System.Text.RegularExpressions;
 
 using Tomboy;
 
@@ -16,20 +17,24 @@ namespace Tomboy.NoteDirectoryWatcher
 
  public class NoteDirectoryWatcherApplicationAddin : ApplicationAddin
  {
+ // TODO: Use environment variable here?
  private static bool VERBOSE_LOGGING = false;
 
  private FileSystemWatcher file_system_watcher;
  private bool initialized;
 
  private Dictionary<string, NoteFileChangeRecord> file_change_records;
+ private Dictionary<string, DateTime> note_save_times;
 
  public override void Initialize ()
  {
  string note_path = Tomboy.DefaultNoteManager.NoteDirectoryPath;
+ Tomboy.DefaultNoteManager.NoteSaved += OnNoteSaved;
 
  file_change_records = new Dictionary<string, NoteFileChangeRecord> ();
+ note_save_times = new Dictionary<string, DateTime> ();
 
- file_system_watcher = new FileSystemWatcher (note_path);
+ file_system_watcher = new FileSystemWatcher (note_path, "*.note");
 
  file_system_watcher.Changed += HandleFileSystemChangeEvent;
  file_system_watcher.Deleted += HandleFileSystemChangeEvent;
@@ -55,6 +60,11 @@ namespace Tomboy.NoteDirectoryWatcher
  get { return initialized; }
  }
 
+ private void OnNoteSaved (Note note)
+ {
+ note_save_times [note.Id] = DateTime.Now;
+ }
+
  private void HandleFileSystemErrorEvent (Object sender, ErrorEventArgs arg)
  {
  // TODO Rescan the local notes in case some of them have changed.
@@ -65,15 +75,7 @@ namespace Tomboy.NoteDirectoryWatcher
  string note_id = GetId (arg.FullPath);
 
  if (VERBOSE_LOGGING)
- Logger.Debug ("{0} has {1} (note_id={2})", arg.FullPath, arg.ChangeType, note_id);
-
- // If the note_id is long 36 characters then the file probably wasn't a note.
- if (note_id.Length != 36) {
- if (VERBOSE_LOGGING)
- Logger.Debug ("Ignoring change to {0}", arg.FullPath);
-
- return;
- }
+ Logger.Debug ("NoteDirectoryWatcher: {0} has {1} (note_id={2})", arg.FullPath, arg.ChangeType, note_id);
 
  // Record that the file has been added/changed/deleted.  Adds/changes trump
  // deletes.  Record the date.
@@ -100,8 +102,10 @@ namespace Tomboy.NoteDirectoryWatcher
  if (!record.changed)
  record.deleted = true;
  } else {
- String message = "Unexpected WatcherChangeType " + arg.ChangeType;
+ string message = "NoteDirectoryWatcher: Unexpected WatcherChangeType " + arg.ChangeType;
  Logger.Error (message);
+ // TODO: Why are we throwing an exception here?
+ //       What are the repercussions of this?
  throw new Exception (message);
  }
 
@@ -118,7 +122,17 @@ namespace Tomboy.NoteDirectoryWatcher
 
  foreach (KeyValuePair<string, NoteFileChangeRecord> pair in file_change_records)  {
  if (VERBOSE_LOGGING)
- Logger.Debug ("Handling (timeout) {0}", pair.Key);
+ Logger.Debug ("NoteDirectoryWatcher: Handling (timeout) {0}", pair.Key);
+
+ // Check that Note.Saved event didn't occur within 3 seconds of last write
+ if (note_save_times.ContainsKey (pair.Key) &&
+    Math.Abs (note_save_times [pair.Key].Ticks - pair.Value.last_change.Ticks) <= 3000*10000) {
+ if (VERBOSE_LOGGING)
+ Logger.Debug ("NoteDirectoryWatcher: Ignoring (timeout) because it was probably a Tomboy write");
+ keysToRemove.Add (pair.Key);
+ continue;
+ }
+ // TODO: Take some actions to clear note_save_times? Not a large structure...
 
  if (DateTime.Now > pair.Value.last_change.Add (new TimeSpan (4000)) ) {
  if (pair.Value.deleted)
@@ -139,48 +153,87 @@ namespace Tomboy.NoteDirectoryWatcher
 
  private static void DeleteNote (string note_id)
  {
- Logger.Debug ("Deleting {0} because file deleted.", note_id);
+ Logger.Debug ("NoteDirectoryWatcher: Deleting {0} because file deleted.", note_id);
 
  string note_uri = MakeUri (note_id);
 
  Note note_to_delete = Tomboy.DefaultNoteManager.FindByUri (note_uri);
-
- Tomboy.DefaultNoteManager.Notes.Remove (note_to_delete);
-
- note_to_delete.Delete ();
+ if (note_to_delete != null)
+ Tomboy.DefaultNoteManager.Delete (note_to_delete);
+ else if (VERBOSE_LOGGING)
+ Logger.Debug ("NoteDirectoryWatcher: Did not delete {0} because note not found.", note_id);
  }
 
- private static void AddOrUpdateNote (string note_id)
+ private void AddOrUpdateNote (string note_id)
  {
  string note_path = Tomboy.DefaultNoteManager.NoteDirectoryPath +
  Path.DirectorySeparatorChar + note_id + ".note";
+ if (!File.Exists (note_path)) {
+ // TODO: Any need to handle a deletion here?
+ if (VERBOSE_LOGGING)
+ Logger.Debug ("NoteDirectoryWatcher: Not processing update of {0} because file does not exist.", note_path);
+ return;
+ }
+
+ string noteXml = null;
+ try {
+ using (StreamReader reader = new StreamReader (note_path)) {
+ noteXml = reader.ReadToEnd ();
+ }
+ } catch (Exception e) {
+ Logger.Error ("NoteDirectoryWatcher: Update aborted, error reading {0}: {1}", note_path, e);
+ }
+
+ if (string.IsNullOrEmpty (noteXml)) {
+ if (VERBOSE_LOGGING)
+ Logger.Debug ("NoteDirectoryWatcher: Update aborted, {0} had no contents.", note_path);
+ return;
+ }
 
  string note_uri = MakeUri (note_id);
 
  Note note = Tomboy.DefaultNoteManager.FindByUri (note_uri);
 
+ bool is_new_note = false;
+
  if (note == null) {
- Logger.Debug ("Adding {0} because file changed.", note_id);
- Note new_note = Note.Load (note_path, Tomboy.DefaultNoteManager);
- Tomboy.DefaultNoteManager.Notes.Add (new_note);
- } else {
- NoteData data = NoteArchiver.Instance.ReadFile (note_path, note_uri);
-
- // Only record changes if the note actually changes.  This prevents the Addin from
- // noticing changes from Tomboy itself.
- if (data.Text == note.XmlContent)
- {
- if (VERBOSE_LOGGING)
- Logger.Debug ("Ignoring {0} because contents identical", note_id);
- } else  {
- Logger.Debug ("Updating {0} because file changed.", note_id);
- note.XmlContent = data.Text;
- note.Title = data.Title;
+ is_new_note = true;
+ Logger.Debug ("NoteDirectoryWatcher: Adding {0} because file changed.", note_id);
+
+ string title = null;
+ const string title_group_name = "title";
+ Match match = Regex.Match (noteXml, "<title>(?<" + title_group_name + ">[^<]+)</title>");
+ if (match.Success)
+ title = match.Groups [title_group_name].Value;
+ else {
+ Logger.Error ("NoteDirectoryWatcher: Error reading note title from {0}", note_path);
+ return;
  }
+
+ try {
+ note = Tomboy.DefaultNoteManager.CreateWithGuid (title, note_id);
+ if (note == null) {
+ Logger.Error ("NoteDirectoryWatcher: Unknown error creating note from {0}", note_path);
+ return;
+ }
+ } catch (Exception e) {
+ Logger.Error ("NoteDirectoryWatcher: Error creating note from {0}: {1}", note_path, e);
+ return;
+ }
+ }
+
+ if (is_new_note)
+ Logger.Debug ("NoteDirectoryWatcher: Updating {0} because file changed.", note_id);
+ try {
+ note.LoadForeignNoteXml (noteXml, ChangeType.ContentChanged);
+ } catch (Exception e) {
+ Logger.Error ("NoteDirectoryWatcher: Update aborted, error parsing {0}: {1}", note_path, e);
+ if (is_new_note)
+ Tomboy.DefaultNoteManager.Delete (note);
  }
  }
 
- private static String MakeUri (string note_id)
+ private static string MakeUri (string note_id)
  {
  return "note://tomboy/" + note_id;
  }
--
1.6.2.4



_______________________________________________
Tomboy-list mailing list
Tomboy-list@...
http://lists.beatniksoftware.com/listinfo.cgi/tomboy-list-beatniksoftware.com

 « Return to Thread: WIP Note Directory Watcher Patch (was Watch for Changes Patch)