Samwilson has submitted this change and it was merged.

Change subject: Beginnings of a tree-drawing system (with GraphViz).
......................................................................


Beginnings of a tree-drawing system (with GraphViz).

Change-Id: If783bc530f0fd1e152871863be62cd7655282532
---
M Core.php
M Genealogy.i18n.php
M Genealogy.php
M Person.php
M README.md
A Traverser.php
A Tree.php
7 files changed, 197 insertions(+), 9 deletions(-)

Approvals:
  Samwilson: Verified; Looks good to me, approved



diff --git a/Core.php b/Core.php
index ba5b80d..9ecc40a 100644
--- a/Core.php
+++ b/Core.php
@@ -47,7 +47,7 @@
                                break;
                        case 'parent':
                                $parentTitle = Title::newFromText($params[0]);
-                               if ($parentTitle->exists()) {
+                               if ($parentTitle and $parentTitle->exists()) {
                                        $person = new 
GenealogyPerson($parentTitle);
                                        $out .= $person->getWikiLink();
                                } else {
@@ -71,6 +71,19 @@
                                $person = new 
GenealogyPerson($parser->getTitle());
                                $out .= 
self::PeopleList($person->getChildren());
                                break;
+                       case 'tree':
+                               $tree = new GenealogyTree();
+                               if (isset($params['ancestors'])) {
+                                       $tree->addAncestors(explode("\n", 
$params['ancestors']));
+                               }
+                               //$tree->setAncestorDepth($params['ancestor 
depth']);
+                               if (isset($params['descendants'])) {
+                                       $tree->addDescendants(explode("\n", 
$params['descendants']));
+                               }
+                               //$tree->setDescendantDepth($params['descendant 
depth']);
+                               $graphviz = $tree->getGraphviz();
+                               $out .= 
$parser->recursiveTagParse("<graphviz>\n$graphviz\n</graphviz>");
+                               break;
                        default:
                                $out .= '<span class="error">'
                                        . 'Genealogy parser function type not 
recognised: "' . $type . '".'
diff --git a/Genealogy.i18n.php b/Genealogy.i18n.php
index 07784f9..44c2ba8 100644
--- a/Genealogy.i18n.php
+++ b/Genealogy.i18n.php
@@ -13,6 +13,10 @@
  * @author Sam Wilson <s...@samwilson.id.au>
  */
 $messages['en'] = array(
-       'genealogy' => "Genealogy",
+       'genealogy'      => "Genealogy",
        'genealogy-desc' => "Adds a parser function for easier linking between 
genealogical records",
+       'ancestor'       => 'Ancestor',
+       'descendant'     => 'Descendant',
+       'ancestors'      => 'Ancestors',
+       'descendants'    => 'Descendants',
 );
diff --git a/Genealogy.php b/Genealogy.php
index 49e8308..9c1f17d 100644
--- a/Genealogy.php
+++ b/Genealogy.php
@@ -34,13 +34,13 @@
 $wgExtensionMessagesFiles['GenealogyMagic'] = __DIR__ . 
'/Genealogy.i18n.magic.php';
 
 /**
- * Class loading and the Special page
+ * Class loading
  */
-$wgAutoloadClasses['Genealogy'] = __FILE__;
-$wgAutoloadClasses['GenealogyPerson'] = __DIR__ . '/Person.php';
-$wgAutoloadClasses['GenealogySpecial'] = __DIR__ . '/Special.php';
-$wgAutoloadClasses['GenealogyCore'] = __DIR__ . '/Core.php';
-$wgSpecialPages['Genealogy'] = 'GenealogySpecial';
+$wgAutoloadClasses['GenealogyPerson']    = __DIR__ . '/Person.php';
+$wgAutoloadClasses['GenealogySpecial']   = __DIR__ . '/Special.php';
+$wgAutoloadClasses['GenealogyCore']      = __DIR__ . '/Core.php';
+$wgAutoloadClasses['GenealogyTree']      = __DIR__ . '/Tree.php';
+$wgAutoloadClasses['GenealogyTraverser'] = __DIR__ . '/Traverser.php';
 
 /**
  * Parser function
diff --git a/Person.php b/Person.php
index 33ebabe..e3e4c9f 100644
--- a/Person.php
+++ b/Person.php
@@ -19,6 +19,15 @@
        }
 
        /**
+        * Get some basic info about this person.
+        * @todo Add dates.
+        * @return string
+        */
+       public function __toString() {
+               return $this->getTitle()->getPrefixedText();
+       }
+
+       /**
         * Get this person's wiki title.
         *
         * @return Title
diff --git a/README.md b/README.md
index 6412e98..e3d98e1 100644
--- a/README.md
+++ b/README.md
@@ -6,7 +6,7 @@
 Its first two parameters are unnamed (i.e. don't have equals signs), but all
 others must be (dates, etc.).
 
-The following functions are supported, three for defining data and three for
+The following functions are supported, three for defining data and four for
 reporting data:
 
 1. Define this person's dates.
@@ -21,6 +21,9 @@
    `{{#genealogy:partners}}`
 6. List all children:
    `{{#genealogy:children}}`
+7. Display a tree (a connected graph):
+   `{{#genealogy:tree|ancestors=List|descendants=List}}`
+   where each `List` is a newline-separated list of page titles.
 
 ## Development
 
diff --git a/Traverser.php b/Traverser.php
new file mode 100644
index 0000000..7d06ff4
--- /dev/null
+++ b/Traverser.php
@@ -0,0 +1,49 @@
+<?php
+
+class GenealogyTraverser {
+
+       private $callbacks;
+
+       private $ancestor_depth = 0;
+
+       private $descendant_depth = 0;
+
+       /**
+        * Callbacks will be called for each page crawled.
+        * 
+        * @param callable $callback The callable function etc.
+        */
+       public function register($callback) {
+               $this->callbacks[] = $callback;
+       }
+
+       public function ancestors(GenealogyPerson $person, $depth = null) {
+               $this->visit($person);
+               if ($this->ancestor_depth > $depth) {
+                       return;
+               }
+               foreach ($person->getParents() as $parent) {
+                       $this->ancestors($parent);
+               }
+               $this->ancestor_depth++;
+       }
+
+       public function descendants(GenealogyPerson $person, $depth = null) {
+               $this->visit($person);
+               if ($this->descendant_depth > $depth) {
+                       return;
+               }
+               foreach ($person->getChildren() as $parent) {
+                       $this->descendants($parent);
+               }
+               $this->descendant_depth++;
+       }
+
+       private function visit($person) {
+               // Call each callback
+               foreach ($this->callbacks as $callback) {
+                       call_user_func($callback, $person);
+               }
+       }
+
+}
diff --git a/Tree.php b/Tree.php
new file mode 100644
index 0000000..3b0dadd
--- /dev/null
+++ b/Tree.php
@@ -0,0 +1,110 @@
+<?php
+
+class GenealogyTree {
+
+       private $dot_source;
+
+       private $ancestors = array();
+
+       private $descendants = array();
+
+       private $ancestor_depth;
+
+       private $descendant_depth;
+
+       public function setAncestorDepth($ancestor_depth) {
+               $this->ancestor_depth = $ancestor_depth;
+       }
+
+       public function setDescendantDepth($descendant_depth) {
+               $this->descendant_depth = $descendant_depth;
+       }
+
+       public function addAncestors($ancestors) {
+               $this->addAncestorsOrDescendants('ancestors', $ancestors);
+       }
+
+       public function addDescendants($descendants) {
+               $this->addAncestorsOrDescendants('descendants', $descendants);
+       }
+
+       private function addAncestorsOrDescendants($type, $list) {
+               foreach ($list as $a) {
+                       $title = Title::newFromText($a);
+                       if ($title and $title->exists()) {
+                               $person = new GenealogyPerson($title);
+                               $this->{$type}[] = $person;
+                       }
+               }
+       }
+
+       public function getGraphviz() {
+               $this->out('top', 'digraph GenealogyTree {');
+               $this->out('top', 'graph [rankdir=LR, splines=ortho]');
+               $this->out('top', 'edge [arrowhead=none]');
+
+               $traverser = new GenealogyTraverser();
+               $traverser->register(array($this, 'visit'));
+
+               foreach ($this->ancestors as $ancestor) {
+                       $traverser->ancestors($ancestor, $this->ancestor_depth);
+               }
+
+               foreach ($this->descendants as $descendant) {
+                       $traverser->descendants($descendant, 
$this->descendant_depth);
+               }
+
+               // Do nothing if there are no people listed.
+               if (!isset($this->dot_source['person'])) {
+                       return 'No people found';
+               }
+
+               // Combine all parts of the graph output.
+               return join("\n", $this->dot_source['top']) . "\n\n"
+                       .join("\n", $this->dot_source['person']) . "\n\n"
+                       .join("\n", $this->dot_source['partner']) . "\n\n"
+                       .join("\n", $this->dot_source['child']) . "\n}";
+       }
+
+       public function visit(GenealogyPerson $person) {
+               $birthYear = $person->getBirthDate('Y');
+               $deathYear = $person->getDeathDate('Y');
+               if (!empty($birthYear) && !empty($deathYear)) {
+                       $date = '\n'.$birthYear.' &ndash; '.$deathYear;
+               } elseif (!empty($birthYear)) {
+                       $date = '\nb.&nbsp;'.$birthYear;
+               } elseif (!empty($deathYear)) {
+                       $date = '\nd.&nbsp;'.$deathYear;
+               } else {
+                       $date = '';
+               }
+               $personId = $this->esc($person->getTitle()->getDBkey());
+               $url = $person->getTitle()->getFullURL();
+               $title = $person->getTitle();
+               $line = $personId." [label=\"$title$date\", shape=plaintext, 
URL=\"$url\", tooltip=\"$title\"]";
+               $this->out('person', $line);
+               foreach ($person->getChildren() as $child) {
+                       $parents = 'parents_'.$this->esc(join('', 
$child->getParents()));
+                       $this->out('partner', $parents.' [label="", 
shape="point"]');
+                       $this->out('partner', $personId.' -> '.$parents.' 
[style=dotted]');
+                       $this->out('child', $parents.' -> 
'.$this->esc($child->getTitle()->getDBkey()));
+               }
+       }
+
+       private function out($group, $line, $permit_dupes = false) {
+               if (!is_array($this->dot_source)) {
+                       $this->dot_source = array();
+               }
+               if (!isset($this->dot_source[$group])) {
+                       $this->dot_source[$group] = array();
+               }
+               if (!in_array($line, $this->dot_source[$group]) || 
$permit_dupes) {
+                       $this->dot_source[$group][] = $line;
+               }
+       }
+
+       private function esc($title) {
+               return strtr($title, '( )', '___');
+       }
+
+}

-- 
To view, visit https://gerrit.wikimedia.org/r/321206
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings

Gerrit-MessageType: merged
Gerrit-Change-Id: If783bc530f0fd1e152871863be62cd7655282532
Gerrit-PatchSet: 1
Gerrit-Project: mediawiki/extensions/Genealogy
Gerrit-Branch: master
Gerrit-Owner: Samwilson <s...@samwilson.id.au>
Gerrit-Reviewer: Samwilson <s...@samwilson.id.au>

_______________________________________________
MediaWiki-commits mailing list
MediaWiki-commits@lists.wikimedia.org
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits

Reply via email to