Repository: iterative/dvc

Issue:
Dependency files under output directory
i.e.
```
dvc run -c . -f unarchive.dvc -d data/Posts.xml.tgz -o data/xml/ tar zxf data/Posts.xml.tgz  -C data/xml/
dvc run -c . -f tsv.dvc -d data/xml/ -d code/xml_to_tsv.py -d code/conf.py -o data/tsv/ python code/xml_to_tsv.py
dvc run -c . -f split.dvc -d data/tsv/ -d code/split_train_test.py -d code/conf.py -o data/tt/  python code/split_train_test.py 0.33 20180319
dvc run -c . -f feature.dvc -d code/featurization.py -d code/conf.py -d data/tt -o data/matrix/ python code/featurization.py
dvc run -c . -f model.dvc -d data/matrix/matrix-train.p -d code/train_model.py -d code/conf.py -o data/model/model.p python code/train_model.py 20180319
dvc run -c . -f Dvcfile -d data/model/model.p -d data/matrix/matrix-test.p -d code/evaluate.py -d code/conf.py -o data/eval.txt python code/evaluate.py
```
Kudos @Nicolabo !

Discussion:

Patch:
diff --git a/dvc/dependency/base.py b/dvc/dependency/base.py
index ba993b21d..9290389d1 100644
--- a/dvc/dependency/base.py
+++ b/dvc/dependency/base.py
@@ -39,6 +39,10 @@ class DependencyBase(object):
     def supported(cls, url):
         return cls.match(url) != None
 
+    @property
+    def sep(self):
+        return '/'
+
     def changed(self):
         raise NotImplementedError
 
diff --git a/dvc/dependency/local.py b/dvc/dependency/local.py
index 081ae81e6..7336f9298 100644
--- a/dvc/dependency/local.py
+++ b/dvc/dependency/local.py
@@ -39,6 +39,10 @@ class DependencyLOCAL(DependencyBase):
         self.path_info = {'scheme': 'local',
                           'path': self.path}
 
+    @property
+    def sep(self):
+        return os.sep
+
     @property
     def rel_path(self):
         return os.path.relpath(self.path)
diff --git a/dvc/project.py b/dvc/project.py
index 1cea1725b..98c17d286 100644
--- a/dvc/project.py
+++ b/dvc/project.py
@@ -399,22 +399,20 @@ class Project(object):
     def graph(self):
         G = nx.DiGraph()
         stages = self.stages()
-
-        outs_map = {}
-        for stage in stages:
-            for o in stage.outs:
-                outs_map[o.path] = stage
+        outs = self.outs()
 
         for stage in stages:
             node = os.path.relpath(stage.path, self.root_dir)
             G.add_node(node, stage=stage)
             for dep in stage.deps:
-                dep_stage = outs_map.get(dep.path, None)
-                if not dep_stage:
-                    continue
-                dep_node = os.path.relpath(dep_stage.path, self.root_dir)
-                G.add_node(dep_node, stage=dep_stage)
-                G.add_edge(node, dep_node)
+                for out in outs:
+                    if out.path != dep.path and not dep.path.startswith(out.path + out.sep):
+                        continue
+
+                    dep_stage = out.stage
+                    dep_node = os.path.relpath(dep_stage.path, self.root_dir)
+                    G.add_node(dep_node, stage=dep_stage)
+                    G.add_edge(node, dep_node)
 
         return G
 


Test patch:
diff --git a/tests/test_repro.py b/tests/test_repro.py
index 64129fe22..7bc5b4b14 100644
--- a/tests/test_repro.py
+++ b/tests/test_repro.py
@@ -36,6 +36,27 @@ class TestRepro(TestDvc):
                      cmd='python {} {} {}'.format(self.CODE, self.FOO, self.file1))
 
 
+class TestReproDepUnderDir(TestDvc):
+    def test(self):
+        self.dir_stage = self.dvc.add(self.DATA_DIR)
+
+        self.file1 = 'file1'
+        self.file1_stage = self.file1 + '.dvc'
+        self.dvc.run(fname=self.file1_stage,
+                     outs=[self.file1],
+                     deps=[self.DATA, self.CODE],
+                     cmd='python {} {} {}'.format(self.CODE, self.DATA, self.file1))
+
+        self.assertTrue(filecmp.cmp(self.file1, self.DATA))
+
+        os.unlink(self.DATA)
+        shutil.copyfile(self.FOO, self.DATA)
+
+        stages = self.dvc.reproduce(self.file1_stage)
+        self.assertEqual(len(stages), 2)
+        self.assertTrue(filecmp.cmp(self.file1, self.FOO))
+
+
 class TestReproNoDeps(TestRepro):
     def test(self):
         out = 'out'