<?xml version="1.0" encoding="UTF-8"?>
<commit>
  <added type="array">
    <added>
      <filename>test.rb</filename>
    </added>
  </added>
  <modified type="array">
    <modified>
      <diff>@@ -1,33 +1,76 @@
-
+require 'net/http'
+require 'uri'
+require 'open-uri'
 require 'lib/query_support.rb'
+require 'note.rb'
+
 #require 'json/pure'
 #require 'json/add/rails'
 
 class IndexController &lt; ApplicationController
 
+  def expand_url(url)
+    begin
+      timeout(60) do
+        uri = URI.parse(url)
+        http = Net::HTTP.new(uri.host)
+		http.open_timeout = 30
+		http.read_timeout = 60
+		if uri.host == &quot;ping.fm&quot; || uri.host == &quot;www.ping.fm&quot;
+			logger.info &quot;ping.fm requires us to fetch the body&quot;
+			response = http.get(uri.path)
+			return nil
+		else
+			response = http.head(uri.path) # get2(uri.path,{ 'Range' =&gt; 'bytes=0-1' })
+			if response.class == Net::HTTPRedirection || response.class == Net::HTTPMovedPermanently
+				logger.info &quot;expand_url #{response} looking at relationship #{r.value} to become #{response['location']}&quot;
+				return response['location']
+			end
+		end
+	  end
+    rescue =&gt; exception
+      logger.info &quot;expand_url inner rescue error #{exception} while fetching #{url}&quot;
+      return nil
+    end
+	return nil
+  end
+
+  def fix_relations
+    # fix the broken links
+    Relation.find(:all, :conditions =&gt; { :kind =&gt; Note::RELATION_URL } ).each do |r|
+      ActionController::Base.logger.info &quot;looking at relationship #{r.value}&quot;
+	  fixed = expand_url(r.value)
+	  if fixed
+	    r.value = fixed
+		r.save
+	  end
+	end
+  end
+
   #
   # The client side is javascript so this does very litle
   #
   def index
+
     # strive to supply session state of previous question if any to the map for json refresh
-	@map.question = nil
+    @map.question = nil
     @map.question = session[:q] = params[:q] if params[:q]
     @map.question = session[:q] if !params[:q]
-	# strive to supply a hint to the map regarding where to center 
-	@map.south = @map.west = @map.north = @map.east = 0.0
-	begin
-		# attempt to fetch map location from parameters
-		@map.south    = session[:s] = params[:s].to_f if params[:s]
-		@map.west     = session[:w] = params[:w].to_f if params[:w]
-		@map.north    = session[:n] = params[:n].to_f if params[:n]
-		@map.east     = session[:e] = params[:e].to_f if params[:e]
-		# otherwise fetch them from session state if present (or set to nil)
-		@map.south    = session[:s].to_f if !params[:s]
-		@map.west     = session[:w].to_f if !params[:w]
-		@map.north    = session[:n].to_f if !params[:n]
-		@map.east     = session[:e].to_f if !params[:e]
-	rescue
-	end
+    # strive to supply a hint to the map regarding where to center 
+    @map.south = @map.west = @map.north = @map.east = 0.0
+    begin
+	# attempt to fetch map location from parameters
+	@map.south    = session[:s] = params[:s].to_f if params[:s]
+	@map.west     = session[:w] = params[:w].to_f if params[:w]
+	@map.north    = session[:n] = params[:n].to_f if params[:n]
+	@map.east     = session[:e] = params[:e].to_f if params[:e]
+	# otherwise fetch them from session state if present (or set to nil)
+	@map.south    = session[:s].to_f if !params[:s]
+	@map.west     = session[:w].to_f if !params[:w]
+	@map.north    = session[:n].to_f if !params[:n]
+	@map.east     = session[:e].to_f if !params[:e]
+    rescue
+    end
   end
 
   #</diff>
      <filename>app/controllers/index_controller.rb</filename>
    </modified>
    <modified>
      <diff>@@ -32,26 +32,73 @@ class Note &lt; ActiveRecord::Base
   # acts_as_solr :fields =&gt; [:title, {:lat=&gt;:range_float}, {:lon=&gt;:range_float}] # slower than heck - using tsearch instead
   acts_as_tsearch :fields =&gt; [&quot;title&quot;,&quot;description&quot;]
 
+  #
+  # The database architecture consists of nodes and edges
+  #
+  # Edges may relate two nodes
+  # Edges may just be about one node
+  # Edges may be labelled
+  #
+  # There is a ongoing design question regarding if some attributes should be first class nodes or just edges
+  # RELATION_URL is being treated as both an edge and as a first class node for example.
+  #
+
+=begin
   RELATIONS = %w{
-					RELATION_TAG
+					RELATION_TAG			# a tag or hashtag
+					RELATION_CATEGORY		# a formal category object similar to a tag but from a named set
+					RELATION_ENTITY			# a citation of an entity [ also see first class entities in notes ]
+					RELATION_RELATION		# an unlabelled relation between two notes
+					RELATION_FRIEND			# a friendship relation between two notes
+					RELATION_URL			# a citation of an url [ see also first class treatment of urls ]
+					RELATION_IMAGE			# a citation of an image
+					RELATION_AUDIO			# a citation of an audio
+					RELATION_MOVIE			# a citation of a movie
+				}
+
+  KINDS = %w{
+					KIND_USER			# a stand in for a local user account - a user makes posts
+					KIND_FEED			# a feed - a feed makes posts
+					KIND_REPORTER			# a reporter person - makes posts
+					KIND_REPORT			# a kind of post (unused)
+					KIND_POST			# a user post
+					KIND_GROUP			# a grouping of posts
+					KIND_EVENT			# an event that groups posts
+					KIND_PLACE			# a place that groups posts
+					KIND_MAP			# a map that groups posts
+					KIND_FILTER			# a filter that groups posts
+					KIND_ENTITY			# an entity of some kind
+					KIND_URL			# a treatment of an url edge as a first class object [ used for client views ]
+					KIND_TAG			# a treatment of an edge as a first class object [ just an idea ]
+				}
+=end
+
+  RELATIONS = %w{
+					RELATION_TAG		
 					RELATION_CATEGORY
-					RELATION_ENTITY
+					RELATION_ENTITY	
 					RELATION_RELATION
-					RELATION_FRIEND
-					RELATION_URL
+					RELATION_FRIEND		
+					RELATION_URL	
+					RELATION_IMAGE
+					RELATION_AUDIO
+					RELATION_MOVIE
 				}
 
-   KINDS = %w{
-					KIND_USER
-					KIND_POST
+  KINDS = %w{
+					KIND_USER	
 					KIND_FEED
 					KIND_REPORTER
 					KIND_REPORT
+					KIND_POST	
 					KIND_GROUP
 					KIND_EVENT
 					KIND_PLACE
-					KIND_MAP
-					KIND_FILTER
+					KIND_MAP	
+					KIND_FILTER	
+					KIND_ENTITY	
+					KIND_URL
+					KIND_TAG
 				}
 
   STATEBITS_RESERVED = 0
@@ -110,6 +157,13 @@ class Note
     return r
   end
 
+  def relations_all(kind = nil, sibling_id = nil)
+    query = { :note_id =&gt; self.id }
+    query[:kind] = kind if kind
+    query[:sibling_id] = sibling_id if sibling_id
+    return Relation.find(:all,:conditions=&gt;query)
+  end
+
   # TODO rate limit
   # TODO use a join
   def relation_as_notes(kind = nil, sibling_id = nil)</diff>
      <filename>app/models/note.rb</filename>
    </modified>
    <modified>
      <diff>@@ -1 +1,9 @@
 &lt;%= @map.body() %&gt;
+&lt;table width=&quot;100%&quot;&gt;
+&lt;tr&gt;
+&lt;td width=&quot;33%&quot; valign=top&gt;&lt;div id=&quot;people_box&quot;&gt;People&lt;/div&gt;&lt;/td&gt;
+&lt;td width=&quot;33%&quot; valign=top&gt;&lt;div id=&quot;posts_box&quot;&gt;Posts&lt;/div&gt;&lt;/td&gt;
+&lt;td width=&quot;33%&quot; valign=top&gt;&lt;div id=&quot;urls_box&quot;&gt;Urls&lt;/div&gt;&lt;/td&gt;
+&lt;/tr&gt;
+&lt;/table&gt;
+</diff>
      <filename>app/views/index/index.html.erb</filename>
    </modified>
    <modified>
      <diff>@@ -500,12 +500,32 @@ function mapper_page_paint(blob) {
 	feature[&quot;iconWindowAnchor&quot;] = [ 9, 2 ];
 	mapper_inject_feature(feature);
 
-	// a list to draw text to
-	var map_list = document.getElementById(&quot;map_list&quot;);
+	var glyph_url = &quot;/dynamapper/icons/emblem-important.png&quot;;
+	var feature = {};
+	feature[&quot;kind&quot;] = &quot;icon&quot;;
+	feature[&quot;image&quot;] = glyph_url;
+	feature[&quot;iconSize&quot;] = [ 32, 32 ];
+	feature[&quot;iconAnchor&quot;] = [ 9, 34 ];
+	feature[&quot;iconWindowAnchor&quot;] = [ 9, 2 ];
+	mapper_inject_feature(feature);
 
 	// mark all objects as stale
 	mapper_mark_all_stale();
 
+	// a list to draw text to
+	var people_box = document.getElementById(&quot;people_box&quot;);
+	var posts_box = document.getElementById(&quot;posts_box&quot;);
+	var urls_box = document.getElementById(&quot;urls_box&quot;);
+	if ( people_box.hasChildNodes() ) {
+		while ( people_box.childNodes.length &gt;= 1 ) { people_box.removeChild( people_box.firstChild ); }
+	}
+	if ( posts_box.hasChildNodes() ) {
+		while ( posts_box.childNodes.length &gt;= 1 ) { posts_box.removeChild( posts_box.firstChild ); }
+	}
+	if ( urls_box.hasChildNodes() ) {
+		while ( urls_box.childNodes.length &gt;= 1 ) { urls_box.removeChild( urls_box.firstChild ); }
+	}
+
 	// visit all the markers and add them
 	for (var i=0; i&lt;markers.length; i++) {
 
@@ -543,19 +563,32 @@ function mapper_page_paint(blob) {
 		feature[&quot;lat&quot;] = lat;
 		feature[&quot;lon&quot;] = lon;
 		feature[&quot;glyph&quot;] = glyph_post;
-		if( kind != &quot;KIND_POST&quot; ) feature[&quot;glyph&quot;] = glyph_person;
+		if( kind == &quot;KIND_USER&quot; ) feature[&quot;glyph&quot;] = glyph_person;
+		if( kind == &quot;KIND_URL&quot; ) feature[&quot;glyph&quot;] = glyph_url;
 		mapper_inject_feature(feature);
 
 		// Draw a list of features as well
-		if(map_list) {
+		if(true) {
 			var div = document.createElement('div');
+			// build the children
 			if(div) {
-				div.innerHTML = &quot;&lt;img src='&quot;+feature[&quot;glyph&quot;]+&quot;'&gt;&lt;/img&gt; &quot;+title;
+
 				div.style.border = &quot;1px solid green&quot;;
-				map_list.appendChild(div);
+				div.style.width = &quot;100%&quot;;
+				if(kind == &quot;KIND_URL&quot;) {
+					div.innerHTML = &quot;&lt;img src='&quot;+feature[&quot;glyph&quot;]+&quot;'&gt;&lt;/img&gt; &lt;a href='&quot;+title+&quot;'&gt;&quot;+title+&quot;&lt;/a&gt;&quot;;
+					people_box.appendChild(div);
+				}
+				if(kind == &quot;KIND_USER&quot;) {
+					div.innerHTML = &quot;&lt;img src='&quot;+feature[&quot;glyph&quot;]+&quot;'&gt;&lt;/img&gt; &lt;a href='http://twitter.com/&quot;+title+&quot;'&gt;&quot;+title+&quot;&lt;/a&gt;&quot;;
+					posts_box.appendChild(div);
+				}
+				if(kind == &quot;KIND_POST&quot;) {
+					div.innerHTML = &quot;&lt;img src='&quot;+feature[&quot;glyph&quot;]+&quot;'&gt;&lt;/img&gt; &quot;+title;
+					urls_box.appendChild(div);
+				}
 			}
 		}
-
 	}
 
 	// sweep the ones that are not part of this display
@@ -621,6 +654,7 @@ function mapper_page_paint_request(recenter) {
 
 /// start mapping engine (invoked once only)
 function mapper_initialize() {
+
   if(map_div) return;
   map_div = document.getElementById(&quot;map&quot;);
   if(!map_div) return;</diff>
      <filename>lib/dynamapper/dynamapper.rb</filename>
    </modified>
    <modified>
      <diff>@@ -135,8 +135,13 @@ class QuerySupport
 			condition_arguments &lt;&lt; w;
 			condition_arguments &lt;&lt; e;
 		end
+		
+		# always disallow features with 0,0 as a location
+		if true
+			conditions &lt;&lt; &quot;lat &lt;&gt; 0 AND lon &lt;&gt; 0&quot;
+		end
 
-		# i am actually only interested in posts - not people right now
+		# filter for posts; we'll collect only people related to those posts later
 		if true
 			conditions &lt;&lt; &quot;kind = ?&quot;
 			condition_arguments &lt;&lt; Note::KIND_POST
@@ -149,7 +154,6 @@ class QuerySupport
 		results = []
 
 ActionController::Base.logger.info &quot;ABOUT TO QUERY #{conditions} and #{condition_arguments.join(' *** ' ) } &quot;
-# ABOUT TO QUERY description @@ to_tsquery(?) AND title @@ to_tsquery(?) AND kind = ? nullnullKIND_POST
 
 		conditions = [ conditions.join(' AND ') ] + condition_arguments
 
@@ -162,7 +166,7 @@ ActionController::Base.logger.info &quot;GOT #{results_length} posts &quot;
 
 		#
 		# lets go ahead and inject in only the people who were associated with the posts we found (so the user can see them)
-		# TODO this could be cleaned up massively using a bit of smarter SQL that finds uniques only
+		# TODO this could be cleaned up massively using a bit of smarter SQL that finds uniques only or at least a HASH join
 		#
 
 		people = {}
@@ -170,10 +174,36 @@ ActionController::Base.logger.info &quot;GOT #{results_length} posts &quot;
 			person = Note.find(:first,:conditions =&gt; { :id =&gt; post.owner_id } )
 			people[post.owner_id] = person if person != nil
 		end
+		people.each do |key,value|
+			results &lt;&lt; value
+			results_length = results_length + 1
+		end
 
-ActionController::Base.logger.info &quot;GOT #{results_length} people &quot;
+		#
+		# we also have a specific interest in 'entities' such as hashtags, urls, places and clusters
+		# here i am injecting 'fake' entities into the stream for now.
+		# later the thought is to promote some of the current edges to be first class entities
+		# annoyingly since these are not real notes they do not have unique ids.
+		# TODO should i promote urls and like concepts to be first class objects? [ probably ]
+		#
 
-		people.each do |key,value|
+		entities = {}
+		results.each do |post|
+			next if post.kind != Note::KIND_POST
+			post.relations_all(Note::RELATION_URL).each do |relation|
+				url = relation.value
+				next if entities[url]
+				note = Note.new
+				note.title = relation.value
+				note.id = post.id * 1000000 + relation.id  # TODO such a hack ugh.
+				note.lat = post.lat
+				note.lon = post.lon
+				note.owner_id = post.owner_id
+				note.kind = Note::KIND_URL
+				entities[url] = note
+			end
+		end
+		entities.each do |key,value|
 			results &lt;&lt; value
 			results_length = results_length + 1
 		end</diff>
      <filename>lib/query_support.rb</filename>
    </modified>
    <modified>
      <diff>@@ -293,6 +293,11 @@ end
 			# TODO expand all URLS and try to get to a duplicate URL consistency; perhaps fingerprint the target page???
 			begin
 				args[:title].split.grep(/http[s]?:\/\/\w/).each do |url|
+					response = Net::HTTP.get_response(URI.parse(uri_str))
+					case response
+						when Net::HTTPRedirection then puts response['location']
+						when Net::HTTPOK then puts uri_str
+					end
 					note.relation_add(Note::RELATION_URL,url)
 				end
 			rescue</diff>
      <filename>lib/twitter_support/twitter_base.rb</filename>
    </modified>
  </modified>
  <removed type="array"/>
  <parents type="array">
    <parent>
      <id>44db8159f283e72ce26ef7229f02e7a685d81cdb</id>
    </parent>
  </parents>
  <author>
    <name>anselm</name>
    <email>anselm@hook.org</email>
  </author>
  <url>http://github.com/anselm/angel/commit/42c341959ba18eeed9a0c28395e7e65a8aa81520</url>
  <id>42c341959ba18eeed9a0c28395e7e65a8aa81520</id>
  <committed-date>2009-08-22T13:43:39-07:00</committed-date>
  <authored-date>2009-08-22T13:43:39-07:00</authored-date>
  <message>showing people posts and urls
urls are implememented as pseudo objects
expanding urls from a variety of services although ping.fm doesn't send a redirect annoyingly</message>
  <tree>d724a363b36024710039c7276bb3e87e52af5316</tree>
  <committer>
    <name>anselm</name>
    <email>anselm@hook.org</email>
  </committer>
</commit>
