PhoneChainer backend



Hi all,

I've written a backend which will take phone clues and generate
placenames from the areacode. Areacode data (from free sources on the
web) for the UK and US is provided.

Problems:
I'm not sure how to setup the path to the areacode data. How should I do
this?
What kind of clue should this generate? Address? Keyword? Textblock? Or
should we consider a new keyword such as "place"? My own feeling is that
a "place" keyword for places which aren't full addresses is a good idea.

Longer term ideas:
I'd like to integrate this with Edd's lat/long work (generate lat/long
clues). Maybe do similar things with postcode / zip code data as well. 
More detailed data for the US might be useful, the area codes seem
pretty big. I like the idea of dashboard doing useful things (e.g. maps)
with geographic data, I think that has a lot of potential.

Please let me know what you think.

Cheers,

dave

Attachment: areacodes.tar.bz2
Description: application/bzip

//
// GNOME Dashboard
//
// PhoneChainerBackend.cs: 
//
// Author:
//         Dave Rodgman <davidr sucs org>
//

using System;
using System.IO;
using System.Collections;
using System.Text;
using System.Text.RegularExpressions;

namespace Dashboard {

	class PhoneChainerBackend:Backend {

		public override bool Startup () {
			Name = "Phone Chainer";

			// FIXME - this needs to be sorted out. Not sure of the best way to do it.
			String path = "areacodes";

			StreamReader sr;
			SortedList table;

			DirectoryInfo dir = new DirectoryInfo (path);
			foreach (FileInfo file in dir.GetFiles ("areacodes.*.csv")) {
				String Country;
				try {
					sr = new StreamReader (new FileStream (file.FullName, FileMode.Open));

					Country = sr.ReadLine ().Substring (1).Trim (null);
					if (!codeToAreaTable.ContainsKey (Country))
						codeToAreaTable.Add (Country, new SortedList ());
					table = (SortedList) codeToAreaTable [Country];

					RegularExpressions.Match intlCodes = Regex.Match (sr.ReadLine (), @"#\+(\d+)\s*(\d*)");
					table.Add ("CountryCode",   intlCodes.Groups [1].Captures [0].ToString ());
					table.Add ("CountryPrefix", intlCodes.Groups [2].Captures [0].ToString ());
				}
				catch (Exception e) {
					Console.WriteLine ("PhoneChainer: Could not load area codes database: " + file.FullName);
					continue;
				}

				String line, code, area;
				Regex extractData = new Regex (@"\x22(?<code>\d+)\x22,\x22(?<town>[^\x22]+)\x22", RegexOptions.Compiled);

				while ((line = sr.ReadLine ()) != null) {
					if (line [0] == '#')
					continue;

					try {
						RegularExpressions.Match m = extractData.Match (line);

						area = m.Groups ["town"].Captures [0].ToString ();
						code = m.Groups ["code"].Captures [0].ToString ();
						if ((area.Length == 0) || (code.Length == 0))
							throw new Exception ();
						}
					catch (Exception e) {
						Console.WriteLine ("PhoneChainer: error in {0} areacode data, line: {1}", Country, line);
						continue;
					}

					if (code.Length < minCodeLength)
						minCodeLength = code.Length;

					// We permit multiple towns per area code
					if (!table.ContainsKey (code))
						table.Add (code, new ArrayList ());
					((ArrayList) (table [code])).Add (area);
				}
				Console.WriteLine ("PhoneChainer: Loaded {0} area codes", Country);
			}
			Console.WriteLine ("PhoneChainer backend started");

			this.SubscribeToClues ("phone");
			this.Initialized = true;

			return true;
		}

		private Hashtable codeToAreaTable = new Hashtable ();
		private int minCodeLength = 999999;

		private ArrayList lookupArea (String originalNumber) {
			ArrayList result = new ArrayList ();

			String number = Regex.Replace (originalNumber, @"[^\d\+]", "");

			if (number.Length == 0)
				return result;

			IDictionaryEnumerator ie = codeToAreaTable.GetEnumerator ();
			while (ie.MoveNext ()) {
				SortedList table = (SortedList) (ie.Value);
				String code;

				// deal with international prefixes
				if (number [0] == '+') {
					code = number.Substring (1);
					if (!code.StartsWith ((String) (table ["CountryCode"])))
						continue;
					code = table ["CountryPrefix"] + code.Substring (((String) (table ["CountryCode"])).Length);
				} else
					code = number;

				String key = null;
				for (int len = minCodeLength; len <= code.Length; len++) {
					int i = table.IndexOfKey (code.Substring (0, len));
					if (i == -1)
						continue;
					key = code.Substring (0, len);
				}

				if (key == null) continue;

				foreach (String town in (ArrayList) table [key])
				result.Add (town + ", " + ie.Key);
			}
			return result;
		}

		public override BackendResult ProcessCluePacket (CluePacket cp) {
			BackendResult result = new BackendResult (this, cp);

			foreach (Clue clue in cp.Clues) {
				if (!ClueTypeSubscribed (clue))
					continue;

				ArrayList areas = lookupArea (clue.Text);

				foreach (String a in areas) {
					Clue areaClue = new Clue ("keyword", a, 10, clue);	// FIXME is this the right cluetype?
					result.AddChainedClue (areaClue);
				}
			}
			return result;
		}
	}
}


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]