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;
}
}
}