Browse Source

Move stuff for Python API; lots of cleanup here and there.

Stefano Cossu 6 years ago
parent
commit
6498205eb5

+ 1 - 1
conftest.py

@@ -10,7 +10,7 @@ from PIL import Image
 
 from lakesuperior.app import create_app
 from lakesuperior.config_parser import config
-from lakesuperior.store_layouts.ldp_rs.lmdb_store import TxnManager
+from lakesuperior.store.ldp_rs.lmdb_store import TxnManager
 from util.generators import random_image
 from util.bootstrap import bootstrap_binary_store
 

BIN
doc/pdf/lakesuperior_recommendations.pdf


+ 481 - 0
doc/src/lakesuperior_arch.graphml

@@ -0,0 +1,481 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<graphml xmlns="http://graphml.graphdrawing.org/xmlns" xmlns:java="http://www.yworks.com/xml/yfiles-common/1.0/java" xmlns:sys="http://www.yworks.com/xml/yfiles-common/markup/primitives/2.0" xmlns:x="http://www.yworks.com/xml/yfiles-common/markup/2.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:y="http://www.yworks.com/xml/graphml" xmlns:yed="http://www.yworks.com/xml/yed/3" xsi:schemaLocation="http://graphml.graphdrawing.org/xmlns http://www.yworks.com/xml/schema/graphml/1.1/ygraphml.xsd">
+  <!--Created by yEd 3.17.2-->
+  <key attr.name="Description" attr.type="string" for="graph" id="d0"/>
+  <key for="port" id="d1" yfiles.type="portgraphics"/>
+  <key for="port" id="d2" yfiles.type="portgeometry"/>
+  <key for="port" id="d3" yfiles.type="portuserdata"/>
+  <key attr.name="url" attr.type="string" for="node" id="d4"/>
+  <key attr.name="description" attr.type="string" for="node" id="d5"/>
+  <key for="node" id="d6" yfiles.type="nodegraphics"/>
+  <key for="graphml" id="d7" yfiles.type="resources"/>
+  <key attr.name="url" attr.type="string" for="edge" id="d8"/>
+  <key attr.name="description" attr.type="string" for="edge" id="d9"/>
+  <key for="edge" id="d10" yfiles.type="edgegraphics"/>
+  <graph edgedefault="directed" id="G">
+    <data key="d0" xml:space="preserve"/>
+    <node id="n0">
+      <data key="d6">
+        <y:GenericNode configuration="com.yworks.flowchart.dataBase">
+          <y:Geometry height="73.0" width="115.26315789473685" x="612.8684210526316" y="685.9375"/>
+          <y:Fill color="#EFE7F7" color2="#CDB7E3" transparent="false"/>
+          <y:BorderStyle color="#472766" type="line" width="1.0"/>
+          <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Droid Sans" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="31.9375" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="55.134765625" x="30.06419613486844" xml:space="preserve" y="30.09375">Metadata
+Store<y:LabelModel><y:SmartNodeLabelModel distance="4.0"/></y:LabelModel><y:ModelParameter><y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.5" nodeRatioX="1.1102230246251565E-16" nodeRatioY="0.34974315068493156" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/></y:ModelParameter></y:NodeLabel>
+        </y:GenericNode>
+      </data>
+    </node>
+    <node id="n1">
+      <data key="d6">
+        <y:GenericNode configuration="com.yworks.flowchart.dataBase">
+          <y:Geometry height="73.0" width="115.26315789473685" x="339.86842105263156" y="685.9375"/>
+          <y:Fill color="#EFE7F7" color2="#CDB7E3" transparent="false"/>
+          <y:BorderStyle color="#472766" type="line" width="1.0"/>
+          <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Droid Sans" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="17.96875" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="60.4609375" x="27.40111019736844" xml:space="preserve" y="27.515625">Filesystem<y:LabelModel><y:SmartNodeLabelModel distance="4.0"/></y:LabelModel><y:ModelParameter><y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/></y:ModelParameter></y:NodeLabel>
+        </y:GenericNode>
+      </data>
+    </node>
+    <node id="n2" yfiles.foldertype="group">
+      <data key="d4" xml:space="preserve"/>
+      <data key="d6">
+        <y:ProxyAutoBoundsNode>
+          <y:Realizers active="0">
+            <y:GroupNode>
+              <y:Geometry height="173.625" width="651.0" x="209.5" y="245.875"/>
+              <y:Fill color="#B2E5FF" color2="#DFF4FF" transparent="false"/>
+              <y:BorderStyle color="#215A77" type="line" width="1.0"/>
+              <y:NodeLabel alignment="center" autoSizePolicy="node_width" borderDistance="0.0" fontFamily="Droid Sans" fontSize="16" fontStyle="bold" hasBackgroundColor="false" hasLineColor="false" height="22.625" horizontalTextPosition="center" iconTextGap="4" modelName="internal" modelPosition="t" textColor="#000000" verticalTextPosition="bottom" visible="true" width="651.0" x="0.0" xml:space="preserve" y="0.0">Python API</y:NodeLabel>
+              <y:Shape type="roundrectangle"/>
+              <y:State closed="false" closedHeight="50.0" closedWidth="50.0" innerGraphDisplayEnabled="false"/>
+              <y:Insets bottom="15" bottomF="15.0" left="15" leftF="15.0" right="15" rightF="15.0" top="15" topF="15.0"/>
+              <y:BorderInsets bottom="0" bottomF="0.0" left="0" leftF="0.0" right="0" rightF="0.0" top="0" topF="0.0"/>
+            </y:GroupNode>
+            <y:GroupNode>
+              <y:Geometry height="50.0" width="50.0" x="0.0" y="60.0"/>
+              <y:Fill color="#F5F5F5" transparent="false"/>
+              <y:BorderStyle color="#000000" type="dashed" width="1.0"/>
+              <y:NodeLabel alignment="right" autoSizePolicy="node_width" backgroundColor="#EBEBEB" borderDistance="0.0" fontFamily="Dialog" fontSize="15" fontStyle="plain" hasLineColor="false" height="21.4609375" horizontalTextPosition="center" iconTextGap="4" modelName="internal" modelPosition="t" textColor="#000000" verticalTextPosition="bottom" visible="true" width="84.4931640625" x="-17.24658203125" xml:space="preserve" y="0.0">Python API</y:NodeLabel>
+              <y:Shape type="roundrectangle"/>
+              <y:State closed="true" closedHeight="50.0" closedWidth="50.0" innerGraphDisplayEnabled="false"/>
+              <y:Insets bottom="5" bottomF="5.0" left="5" leftF="5.0" right="5" rightF="5.0" top="5" topF="5.0"/>
+              <y:BorderInsets bottom="0" bottomF="0.0" left="0" leftF="0.0" right="0" rightF="0.0" top="0" topF="0.0"/>
+            </y:GroupNode>
+          </y:Realizers>
+        </y:ProxyAutoBoundsNode>
+      </data>
+      <graph edgedefault="directed" id="n2:">
+        <node id="n2::n0">
+          <data key="d6">
+            <y:ShapeNode>
+              <y:Geometry height="121.0" width="181.0" x="224.5" y="283.5"/>
+              <y:Fill color="#7FFFC2" color2="#CBFFE6" transparent="false"/>
+              <y:BorderStyle color="#206946" raised="false" type="line" width="1.0"/>
+              <y:NodeLabel alignment="center" autoSizePolicy="content" borderDistance="8.0" fontFamily="Droid Sans" fontSize="12" fontStyle="bold" hasBackgroundColor="false" hasLineColor="false" height="17.96875" horizontalTextPosition="center" iconTextGap="4" modelName="internal" modelPosition="t" textColor="#000000" verticalTextPosition="bottom" visible="true" width="48.765625" x="66.1171875" xml:space="preserve" y="8.0">LDP API</y:NodeLabel>
+              <y:NodeLabel alignment="left" autoSizePolicy="content" fontFamily="Droid Sans" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="31.9375" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="98.44140625" x="41.279296875" xml:space="preserve" y="44.53125">• CRUD resources
+• Versioning<y:LabelModel><y:SmartNodeLabelModel distance="4.0"/></y:LabelModel><y:ModelParameter><y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/></y:ModelParameter></y:NodeLabel>
+              <y:Shape type="roundrectangle"/>
+            </y:ShapeNode>
+          </data>
+        </node>
+        <node id="n2::n1">
+          <data key="d6">
+            <y:ShapeNode>
+              <y:Geometry height="121.0" width="181.0" x="664.5" y="283.5"/>
+              <y:Fill color="#7FFFC2" color2="#CBFFE6" transparent="false"/>
+              <y:BorderStyle color="#206946" raised="false" type="line" width="1.0"/>
+              <y:NodeLabel alignment="center" autoSizePolicy="content" borderDistance="8.0" fontFamily="Droid Sans" fontSize="12" fontStyle="bold" hasBackgroundColor="false" hasLineColor="false" height="17.96875" horizontalTextPosition="center" iconTextGap="4" modelName="internal" modelPosition="t" textColor="#000000" verticalTextPosition="bottom" visible="true" width="62.0078125" x="59.49609375" xml:space="preserve" y="8.0">Query API</y:NodeLabel>
+              <y:NodeLabel alignment="left" autoSizePolicy="content" fontFamily="Droid Sans" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="31.9375" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="88.234375" x="46.3828125" xml:space="preserve" y="44.53125">• Term search
+• SPARQL query<y:LabelModel><y:SmartNodeLabelModel distance="4.0"/></y:LabelModel><y:ModelParameter><y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/></y:ModelParameter></y:NodeLabel>
+              <y:Shape type="roundrectangle"/>
+            </y:ShapeNode>
+          </data>
+        </node>
+        <node id="n2::n2">
+          <data key="d6">
+            <y:ShapeNode>
+              <y:Geometry height="121.0" width="181.0" x="444.5" y="283.5"/>
+              <y:Fill color="#7FFFC2" color2="#CBFFE6" transparent="false"/>
+              <y:BorderStyle color="#206946" raised="false" type="line" width="1.0"/>
+              <y:NodeLabel alignment="center" autoSizePolicy="content" borderDistance="8.0" fontFamily="Droid Sans" fontSize="12" fontStyle="bold" hasBackgroundColor="false" hasLineColor="false" height="17.96875" horizontalTextPosition="center" iconTextGap="4" modelName="internal" modelPosition="t" textColor="#000000" verticalTextPosition="bottom" visible="true" width="64.345703125" x="58.3271484375" xml:space="preserve" y="8.0">Admin API</y:NodeLabel>
+              <y:NodeLabel alignment="left" autoSizePolicy="content" fontFamily="Droid Sans" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="87.8125" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="140.611328125" x="20.1943359375" xml:space="preserve" y="28.420817669172948">• Bootstrap
+• Stats
+• Health checks
+   (fixity, consistency, etc.)
+• Backup &amp; restore
+• Import &amp; export<y:LabelModel><y:SmartNodeLabelModel distance="4.0"/></y:LabelModel><y:ModelParameter><y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.09774436090225569" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/></y:ModelParameter></y:NodeLabel>
+              <y:Shape type="roundrectangle"/>
+            </y:ShapeNode>
+          </data>
+        </node>
+      </graph>
+    </node>
+    <node id="n3">
+      <data key="d6">
+        <y:ImageNode>
+          <y:Geometry height="48.0" width="48.0" x="391.0" y="36.4375"/>
+          <y:Fill color="#CCCCFF" transparent="false"/>
+          <y:BorderStyle color="#000000" type="line" width="1.0"/>
+          <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Droid Sans" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="31.9375" horizontalTextPosition="center" iconTextGap="4" modelName="sandwich" modelPosition="n" textColor="#000000" verticalTextPosition="bottom" visible="true" width="81.4375" x="-16.71875" xml:space="preserve" y="-35.9375">Python Clients
+&amp; Plug-ins</y:NodeLabel>
+          <y:Image alphaImage="true" refid="1"/>
+        </y:ImageNode>
+      </data>
+    </node>
+    <node id="n4">
+      <data key="d6">
+        <y:ImageNode>
+          <y:Geometry height="48.0" width="48.0" x="511.0" y="36.4375"/>
+          <y:Fill color="#CCCCFF" transparent="false"/>
+          <y:BorderStyle color="#000000" type="line" width="1.0"/>
+          <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Droid Sans" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="17.96875" horizontalTextPosition="center" iconTextGap="4" modelName="sandwich" modelPosition="n" textColor="#000000" verticalTextPosition="bottom" visible="true" width="21.197265625" x="13.4013671875" xml:space="preserve" y="-21.96875">CLI</y:NodeLabel>
+          <y:Image alphaImage="true" refid="2"/>
+        </y:ImageNode>
+      </data>
+    </node>
+    <node id="n5">
+      <data key="d6">
+        <y:ShapeNode>
+          <y:Geometry height="68.0" width="181.0" x="590.5" y="123.0"/>
+          <y:Fill color="#7FFFC2" color2="#CBFFE6" transparent="false"/>
+          <y:BorderStyle color="#206946" raised="false" type="line" width="1.0"/>
+          <y:NodeLabel alignment="center" autoSizePolicy="content" borderDistance="8.0" fontFamily="Droid Sans" fontSize="12" fontStyle="bold" hasBackgroundColor="false" hasLineColor="false" height="17.96875" horizontalTextPosition="center" iconTextGap="4" modelName="internal" modelPosition="t" textColor="#000000" verticalTextPosition="bottom" visible="true" width="53.916015625" x="63.5419921875" xml:space="preserve" y="8.0">REST API</y:NodeLabel>
+          <y:NodeLabel alignment="left" autoSizePolicy="content" fontFamily="Droid Sans" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="31.9375" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="137.705078125" x="21.6474609375" xml:space="preserve" y="27.84412650602411">Map request parameters
+to Python API methods<y:LabelModel><y:SmartNodeLabelModel distance="4.0"/></y:LabelModel><y:ModelParameter><y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.5" nodeRatioX="0.0" nodeRatioY="0.37914156626506024" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/></y:ModelParameter></y:NodeLabel>
+          <y:Shape type="roundrectangle"/>
+        </y:ShapeNode>
+      </data>
+    </node>
+    <node id="n6">
+      <data key="d6">
+        <y:ImageNode>
+          <y:Geometry height="48.0" width="48.0" x="657.0" y="36.4375"/>
+          <y:Fill color="#CCCCFF" transparent="false"/>
+          <y:BorderStyle color="#000000" type="line" width="1.0"/>
+          <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Droid Sans" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="17.96875" horizontalTextPosition="center" iconTextGap="4" modelName="sandwich" modelPosition="n" textColor="#000000" verticalTextPosition="bottom" visible="true" width="65.904296875" x="-8.9521484375" xml:space="preserve" y="-21.96875">HTTP Client</y:NodeLabel>
+          <y:Image alphaImage="true" refid="3"/>
+        </y:ImageNode>
+      </data>
+    </node>
+    <node id="n7">
+      <data key="d6">
+        <y:ShapeNode>
+          <y:Geometry height="68.0" width="181.0" x="307.0" y="489.9375"/>
+          <y:Fill color="#7FFFC2" color2="#CBFFE6" transparent="false"/>
+          <y:BorderStyle color="#206946" raised="false" type="line" width="1.0"/>
+          <y:NodeLabel alignment="center" autoSizePolicy="content" borderDistance="8.0" fontFamily="Droid Sans" fontSize="12" fontStyle="bold" hasBackgroundColor="false" hasLineColor="false" height="17.96875" horizontalTextPosition="center" iconTextGap="4" modelName="internal" modelPosition="t" textColor="#000000" verticalTextPosition="bottom" visible="true" width="123.14453125" x="28.927734375" xml:space="preserve" y="8.0">LDP-NR Store Layout</y:NodeLabel>
+          <y:NodeLabel alignment="left" autoSizePolicy="content" fontFamily="Droid Sans" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="31.9375" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="153.162109375" x="13.9189453125" xml:space="preserve" y="27.844126506024054">Handle non-RDF (binary)
+resource and filesystem I/O<y:LabelModel><y:SmartNodeLabelModel distance="4.0"/></y:LabelModel><y:ModelParameter><y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.5" nodeRatioX="0.0" nodeRatioY="0.37914156626506024" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/></y:ModelParameter></y:NodeLabel>
+          <y:Shape type="roundrectangle"/>
+        </y:ShapeNode>
+      </data>
+    </node>
+    <node id="n8">
+      <data key="d6">
+        <y:ShapeNode>
+          <y:Geometry height="68.0" width="181.0" x="580.0" y="489.9375"/>
+          <y:Fill color="#7FFFC2" color2="#CBFFE6" transparent="false"/>
+          <y:BorderStyle color="#206946" raised="false" type="line" width="1.0"/>
+          <y:NodeLabel alignment="center" autoSizePolicy="content" borderDistance="8.0" fontFamily="Droid Sans" fontSize="12" fontStyle="bold" hasBackgroundColor="false" hasLineColor="false" height="17.96875" horizontalTextPosition="center" iconTextGap="4" modelName="internal" modelPosition="t" textColor="#000000" verticalTextPosition="bottom" visible="true" width="120.033203125" x="30.4833984375" xml:space="preserve" y="8.0">LDP-RS Store Layout</y:NodeLabel>
+          <y:NodeLabel alignment="left" autoSizePolicy="content" fontFamily="Droid Sans" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="31.9375" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="142.275390625" x="19.3623046875" xml:space="preserve" y="27.844126506024054">Arrange RDF data into
+triples and named graphs<y:LabelModel><y:SmartNodeLabelModel distance="4.0"/></y:LabelModel><y:ModelParameter><y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.5" nodeRatioX="0.0" nodeRatioY="0.37914156626506024" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/></y:ModelParameter></y:NodeLabel>
+          <y:Shape type="roundrectangle"/>
+        </y:ShapeNode>
+      </data>
+    </node>
+    <node id="n9">
+      <data key="d6">
+        <y:ShapeNode>
+          <y:Geometry height="68.0" width="181.0" x="580.0" y="587.9375"/>
+          <y:Fill color="#7FFFC2" color2="#CBFFE6" transparent="false"/>
+          <y:BorderStyle color="#206946" raised="false" type="line" width="1.0"/>
+          <y:NodeLabel alignment="center" autoSizePolicy="content" borderDistance="8.0" fontFamily="Droid Sans" fontSize="12" fontStyle="bold" hasBackgroundColor="false" hasLineColor="false" height="17.96875" horizontalTextPosition="center" iconTextGap="4" modelName="internal" modelPosition="t" textColor="#000000" verticalTextPosition="bottom" visible="true" width="143.734375" x="18.6328125" xml:space="preserve" y="8.0">Graph Store Abstraction</y:NodeLabel>
+          <y:NodeLabel alignment="left" autoSizePolicy="content" fontFamily="Droid Sans" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="31.9375" horizontalTextPosition="center" iconTextGap="4" modelName="custom" textColor="#000000" verticalTextPosition="bottom" visible="true" width="137.76953125" x="21.615234375" xml:space="preserve" y="27.844126506024054">Map graph operations to
+Key/Value store I/O<y:LabelModel><y:SmartNodeLabelModel distance="4.0"/></y:LabelModel><y:ModelParameter><y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.5" nodeRatioX="0.0" nodeRatioY="0.37914156626506024" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/></y:ModelParameter></y:NodeLabel>
+          <y:Shape type="roundrectangle"/>
+        </y:ShapeNode>
+      </data>
+    </node>
+    <edge id="e0" source="n1" target="n7">
+      <data key="d10">
+        <y:BezierEdge>
+          <y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
+          <y:LineStyle color="#4D6991" type="line" width="1.0"/>
+          <y:Arrows source="none" target="delta"/>
+          <y:EdgeLabel alignment="center" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" hasText="false" height="4.0" horizontalTextPosition="center" iconTextGap="4" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" verticalTextPosition="bottom" visible="true" width="4.0" x="28.0" y="-69.6912841796875">
+            <y:LabelModel>
+              <y:SmartEdgeLabelModel autoRotationEnabled="false" defaultAngle="0.0" defaultDistance="10.0"/>
+            </y:LabelModel>
+            <y:ModelParameter>
+              <y:SmartEdgeLabelModelParameter angle="0.0" distance="30.0" distanceToCenter="true" position="right" ratio="0.5" segment="0"/>
+            </y:ModelParameter>
+            <y:PreferredPlacementDescriptor angle="0.0" angleOffsetOnRightSide="0" angleReference="absolute" angleRotationOnRightSide="co" distance="-1.0" frozen="true" placement="anywhere" side="anywhere" sideReference="relative_to_edge_flow"/>
+          </y:EdgeLabel>
+        </y:BezierEdge>
+      </data>
+    </edge>
+    <edge id="e1" source="n8" target="n2">
+      <data key="d10">
+        <y:BezierEdge>
+          <y:Path sx="11.0" sy="-1.9375" tx="0.0" ty="0.0"/>
+          <y:LineStyle color="#4D6991" type="line" width="1.0"/>
+          <y:Arrows source="none" target="delta"/>
+          <y:EdgeLabel alignment="center" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" hasText="false" height="4.0" horizontalTextPosition="center" iconTextGap="4" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" verticalTextPosition="bottom" visible="true" width="4.0" x="4.276489062528299" y="-42.90852457234479">
+            <y:LabelModel>
+              <y:SmartEdgeLabelModel autoRotationEnabled="false" defaultAngle="0.0" defaultDistance="10.0"/>
+            </y:LabelModel>
+            <y:ModelParameter>
+              <y:SmartEdgeLabelModelParameter angle="0.0" distance="30.0" distanceToCenter="true" position="right" ratio="0.5" segment="0"/>
+            </y:ModelParameter>
+            <y:PreferredPlacementDescriptor angle="0.0" angleOffsetOnRightSide="0" angleReference="absolute" angleRotationOnRightSide="co" distance="-1.0" frozen="true" placement="anywhere" side="anywhere" sideReference="relative_to_edge_flow"/>
+          </y:EdgeLabel>
+        </y:BezierEdge>
+      </data>
+    </edge>
+    <edge id="e2" source="n7" target="n2">
+      <data key="d10">
+        <y:BezierEdge>
+          <y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
+          <y:LineStyle color="#4D6991" type="line" width="1.0"/>
+          <y:Arrows source="none" target="delta"/>
+          <y:EdgeLabel alignment="center" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" hasText="false" height="4.0" horizontalTextPosition="center" iconTextGap="4" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" verticalTextPosition="bottom" visible="true" width="4.0" x="56.47958955609175" y="-31.947523976898083">
+            <y:LabelModel>
+              <y:SmartEdgeLabelModel autoRotationEnabled="false" defaultAngle="0.0" defaultDistance="10.0"/>
+            </y:LabelModel>
+            <y:ModelParameter>
+              <y:SmartEdgeLabelModelParameter angle="0.0" distance="30.0" distanceToCenter="true" position="right" ratio="0.5" segment="0"/>
+            </y:ModelParameter>
+            <y:PreferredPlacementDescriptor angle="0.0" angleOffsetOnRightSide="0" angleReference="absolute" angleRotationOnRightSide="co" distance="-1.0" frozen="true" placement="anywhere" side="anywhere" sideReference="relative_to_edge_flow"/>
+          </y:EdgeLabel>
+        </y:BezierEdge>
+      </data>
+    </edge>
+    <edge id="e3" source="n2" target="n3">
+      <data key="d10">
+        <y:BezierEdge>
+          <y:Path sx="0.0" sy="0.0" tx="0.0" ty="24.005859375"/>
+          <y:LineStyle color="#4D6991" type="line" width="1.0"/>
+          <y:Arrows source="none" target="delta"/>
+          <y:EdgeLabel alignment="center" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" hasText="false" height="4.0" horizontalTextPosition="center" iconTextGap="4" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" verticalTextPosition="bottom" visible="true" width="4.0" x="-12.00968743856754" y="-91.63869552212697">
+            <y:LabelModel>
+              <y:SmartEdgeLabelModel autoRotationEnabled="false" defaultAngle="0.0" defaultDistance="10.0"/>
+            </y:LabelModel>
+            <y:ModelParameter>
+              <y:SmartEdgeLabelModelParameter angle="0.0" distance="30.0" distanceToCenter="true" position="right" ratio="0.5" segment="0"/>
+            </y:ModelParameter>
+            <y:PreferredPlacementDescriptor angle="0.0" angleOffsetOnRightSide="0" angleReference="absolute" angleRotationOnRightSide="co" distance="-1.0" frozen="true" placement="anywhere" side="anywhere" sideReference="relative_to_edge_flow"/>
+          </y:EdgeLabel>
+        </y:BezierEdge>
+      </data>
+    </edge>
+    <edge id="e4" source="n2" target="n5">
+      <data key="d10">
+        <y:BezierEdge>
+          <y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
+          <y:LineStyle color="#4D6991" type="line" width="1.0"/>
+          <y:Arrows source="none" target="delta"/>
+          <y:EdgeLabel alignment="center" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" hasText="false" height="4.0" horizontalTextPosition="center" iconTextGap="4" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" verticalTextPosition="bottom" visible="true" width="4.0" x="54.765877836260074" y="-23.370109097338286">
+            <y:LabelModel>
+              <y:SmartEdgeLabelModel autoRotationEnabled="false" defaultAngle="0.0" defaultDistance="10.0"/>
+            </y:LabelModel>
+            <y:ModelParameter>
+              <y:SmartEdgeLabelModelParameter angle="0.0" distance="30.0" distanceToCenter="true" position="right" ratio="0.5" segment="0"/>
+            </y:ModelParameter>
+            <y:PreferredPlacementDescriptor angle="0.0" angleOffsetOnRightSide="0" angleReference="absolute" angleRotationOnRightSide="co" distance="-1.0" frozen="true" placement="anywhere" side="anywhere" sideReference="relative_to_edge_flow"/>
+          </y:EdgeLabel>
+        </y:BezierEdge>
+      </data>
+    </edge>
+    <edge id="e5" source="n2" target="n4">
+      <data key="d10">
+        <y:BezierEdge>
+          <y:Path sx="0.0" sy="0.0" tx="0.0" ty="14.974609375"/>
+          <y:LineStyle color="#4D6991" type="line" width="1.0"/>
+          <y:Arrows source="none" target="delta"/>
+          <y:EdgeLabel alignment="center" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" hasText="false" height="4.0" horizontalTextPosition="center" iconTextGap="4" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" verticalTextPosition="bottom" visible="true" width="4.0" x="28.0" y="-82.69451904296875">
+            <y:LabelModel>
+              <y:SmartEdgeLabelModel autoRotationEnabled="false" defaultAngle="0.0" defaultDistance="10.0"/>
+            </y:LabelModel>
+            <y:ModelParameter>
+              <y:SmartEdgeLabelModelParameter angle="0.0" distance="30.0" distanceToCenter="true" position="right" ratio="0.5" segment="0"/>
+            </y:ModelParameter>
+            <y:PreferredPlacementDescriptor angle="0.0" angleOffsetOnRightSide="0" angleReference="absolute" angleRotationOnRightSide="co" distance="-1.0" frozen="true" placement="anywhere" side="anywhere" sideReference="relative_to_edge_flow"/>
+          </y:EdgeLabel>
+        </y:BezierEdge>
+      </data>
+    </edge>
+    <edge id="e6" source="n5" target="n6">
+      <data key="d10">
+        <y:BezierEdge>
+          <y:Path sx="0.0" sy="0.0" tx="0.0" ty="20.021484375"/>
+          <y:LineStyle color="#4D6991" type="line" width="1.0"/>
+          <y:Arrows source="none" target="delta"/>
+          <y:EdgeLabel alignment="center" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" hasText="false" height="4.0" horizontalTextPosition="center" iconTextGap="4" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" verticalTextPosition="bottom" visible="true" width="4.0" x="28.0" y="-21.308700561523438">
+            <y:LabelModel>
+              <y:SmartEdgeLabelModel autoRotationEnabled="false" defaultAngle="0.0" defaultDistance="10.0"/>
+            </y:LabelModel>
+            <y:ModelParameter>
+              <y:SmartEdgeLabelModelParameter angle="0.0" distance="30.0" distanceToCenter="true" position="right" ratio="0.5" segment="0"/>
+            </y:ModelParameter>
+            <y:PreferredPlacementDescriptor angle="0.0" angleOffsetOnRightSide="0" angleReference="absolute" angleRotationOnRightSide="co" distance="-1.0" frozen="true" placement="anywhere" side="anywhere" sideReference="relative_to_edge_flow"/>
+          </y:EdgeLabel>
+        </y:BezierEdge>
+      </data>
+    </edge>
+    <edge id="e7" source="n5" target="n5">
+      <data key="d10">
+        <y:BezierEdge>
+          <y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
+          <y:LineStyle color="#4D6991" type="line" width="1.0"/>
+          <y:Arrows source="none" target="delta"/>
+          <y:EdgeLabel alignment="center" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" hasText="false" height="4.0" horizontalTextPosition="center" iconTextGap="4" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" verticalTextPosition="bottom" visible="true" width="4.0" x="-90.5" y="-34.0">
+            <y:LabelModel>
+              <y:SmartEdgeLabelModel autoRotationEnabled="false" defaultAngle="0.0" defaultDistance="10.0"/>
+            </y:LabelModel>
+            <y:ModelParameter>
+              <y:SmartEdgeLabelModelParameter angle="0.0" distance="30.0" distanceToCenter="true" position="right" ratio="0.5" segment="0"/>
+            </y:ModelParameter>
+            <y:PreferredPlacementDescriptor angle="0.0" angleOffsetOnRightSide="0" angleReference="absolute" angleRotationOnRightSide="co" distance="-1.0" frozen="true" placement="anywhere" side="anywhere" sideReference="relative_to_edge_flow"/>
+          </y:EdgeLabel>
+        </y:BezierEdge>
+      </data>
+    </edge>
+    <edge id="e8" source="n7" target="n7">
+      <data key="d10">
+        <y:BezierEdge>
+          <y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
+          <y:LineStyle color="#4D6991" type="line" width="1.0"/>
+          <y:Arrows source="none" target="delta"/>
+          <y:EdgeLabel alignment="center" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" hasText="false" height="4.0" horizontalTextPosition="center" iconTextGap="4" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" verticalTextPosition="bottom" visible="true" width="4.0" x="-90.5" y="-34.0">
+            <y:LabelModel>
+              <y:SmartEdgeLabelModel autoRotationEnabled="false" defaultAngle="0.0" defaultDistance="10.0"/>
+            </y:LabelModel>
+            <y:ModelParameter>
+              <y:SmartEdgeLabelModelParameter angle="0.0" distance="30.0" distanceToCenter="true" position="right" ratio="0.5" segment="0"/>
+            </y:ModelParameter>
+            <y:PreferredPlacementDescriptor angle="0.0" angleOffsetOnRightSide="0" angleReference="absolute" angleRotationOnRightSide="co" distance="-1.0" frozen="true" placement="anywhere" side="anywhere" sideReference="relative_to_edge_flow"/>
+          </y:EdgeLabel>
+        </y:BezierEdge>
+      </data>
+    </edge>
+    <edge id="e9" source="n8" target="n8">
+      <data key="d10">
+        <y:BezierEdge>
+          <y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
+          <y:LineStyle color="#4D6991" type="line" width="1.0"/>
+          <y:Arrows source="none" target="delta"/>
+          <y:EdgeLabel alignment="center" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" hasText="false" height="4.0" horizontalTextPosition="center" iconTextGap="4" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" verticalTextPosition="bottom" visible="true" width="4.0" x="-90.5" y="-34.0">
+            <y:LabelModel>
+              <y:SmartEdgeLabelModel autoRotationEnabled="false" defaultAngle="0.0" defaultDistance="10.0"/>
+            </y:LabelModel>
+            <y:ModelParameter>
+              <y:SmartEdgeLabelModelParameter angle="0.0" distance="30.0" distanceToCenter="true" position="right" ratio="0.5" segment="0"/>
+            </y:ModelParameter>
+            <y:PreferredPlacementDescriptor angle="0.0" angleOffsetOnRightSide="0" angleReference="absolute" angleRotationOnRightSide="co" distance="-1.0" frozen="true" placement="anywhere" side="anywhere" sideReference="relative_to_edge_flow"/>
+          </y:EdgeLabel>
+        </y:BezierEdge>
+      </data>
+    </edge>
+    <edge id="e10" source="n9" target="n9">
+      <data key="d10">
+        <y:BezierEdge>
+          <y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
+          <y:LineStyle color="#4D6991" type="line" width="1.0"/>
+          <y:Arrows source="none" target="delta"/>
+          <y:EdgeLabel alignment="center" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" hasText="false" height="4.0" horizontalTextPosition="center" iconTextGap="4" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" verticalTextPosition="bottom" visible="true" width="4.0" x="-90.5" y="-34.0">
+            <y:LabelModel>
+              <y:SmartEdgeLabelModel autoRotationEnabled="false" defaultAngle="0.0" defaultDistance="10.0"/>
+            </y:LabelModel>
+            <y:ModelParameter>
+              <y:SmartEdgeLabelModelParameter angle="0.0" distance="30.0" distanceToCenter="true" position="right" ratio="0.5" segment="0"/>
+            </y:ModelParameter>
+            <y:PreferredPlacementDescriptor angle="0.0" angleOffsetOnRightSide="0" angleReference="absolute" angleRotationOnRightSide="co" distance="-1.0" frozen="true" placement="anywhere" side="anywhere" sideReference="relative_to_edge_flow"/>
+          </y:EdgeLabel>
+        </y:BezierEdge>
+      </data>
+    </edge>
+    <edge id="e11" source="n9" target="n8">
+      <data key="d10">
+        <y:BezierEdge>
+          <y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
+          <y:LineStyle color="#4D6991" type="line" width="1.0"/>
+          <y:Arrows source="none" target="delta"/>
+          <y:EdgeLabel alignment="center" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" hasText="false" height="4.0" horizontalTextPosition="center" iconTextGap="4" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" verticalTextPosition="bottom" visible="true" width="4.0" x="28.0" y="-16.9775390625">
+            <y:LabelModel>
+              <y:SmartEdgeLabelModel autoRotationEnabled="false" defaultAngle="0.0" defaultDistance="10.0"/>
+            </y:LabelModel>
+            <y:ModelParameter>
+              <y:SmartEdgeLabelModelParameter angle="0.0" distance="30.0" distanceToCenter="true" position="right" ratio="0.5" segment="0"/>
+            </y:ModelParameter>
+            <y:PreferredPlacementDescriptor angle="0.0" angleOffsetOnRightSide="0" angleReference="absolute" angleRotationOnRightSide="co" distance="-1.0" frozen="true" placement="anywhere" side="anywhere" sideReference="relative_to_edge_flow"/>
+          </y:EdgeLabel>
+        </y:BezierEdge>
+      </data>
+    </edge>
+    <edge id="e12" source="n0" target="n9">
+      <data key="d10">
+        <y:BezierEdge>
+          <y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
+          <y:LineStyle color="#4D6991" type="line" width="1.0"/>
+          <y:Arrows source="none" target="delta"/>
+          <y:EdgeLabel alignment="center" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" hasText="false" height="4.0" horizontalTextPosition="center" iconTextGap="4" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" verticalTextPosition="bottom" visible="true" width="4.0" x="28.0" y="-20.670654296875">
+            <y:LabelModel>
+              <y:SmartEdgeLabelModel autoRotationEnabled="false" defaultAngle="0.0" defaultDistance="10.0"/>
+            </y:LabelModel>
+            <y:ModelParameter>
+              <y:SmartEdgeLabelModelParameter angle="0.0" distance="30.0" distanceToCenter="true" position="right" ratio="0.5" segment="0"/>
+            </y:ModelParameter>
+            <y:PreferredPlacementDescriptor angle="0.0" angleOffsetOnRightSide="0" angleReference="absolute" angleRotationOnRightSide="co" distance="-1.0" frozen="true" placement="anywhere" side="anywhere" sideReference="relative_to_edge_flow"/>
+          </y:EdgeLabel>
+        </y:BezierEdge>
+      </data>
+    </edge>
+  </graph>
+  <data key="d7">
+    <y:Resources>
+      <y:Resource id="1" type="java.awt.image.BufferedImage" xml:space="preserve">iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAYAAABXAvmHAAADOUlEQVR4Xu3aWahNURwG8C/z+GAK&#13;
+eTAm8sAbMl2RTCmUKFMJCfFCERlfKHPyiAdeDEWmx0s8KIWMieIB0RUpupm/76wzrLP3Oufsve+5&#13;
+ax91v/q97P/ad691zp7WOhdoelrTSFpJB+k83aIH9Iye0n2qp3N0gFbTKKScXjCd+UB/E3pMc5BC&#13;
+htN7hDuU1H54zm0UDv4J5tR4Ym1LYhU8pR8KB9W30De7vRVdtGpxfaR28JA6FA56rLiEKVYtiUnw&#13;
+kJkoHFB3HDtLrFoSi+Ahs1E44B/aQD1pMr21akkshofYA6i2lgFESR09j0GnWbCjpXgZQNw0ItzR&#13;
+Uqo2gIG0ic7QNbrRBL8R7mgpGsCwwP5yifbSYFRIRzqOeAetJg1gjGN7TiMtQ4m0p5sI7+RTpQHI&#13;
+L5oARw4h3Ni3KAMQvZ4XpT/9QLihb1EHIONhZYujQRoW0jjHdhdNnvKpdzRIw2ha4Njukj+NNCX8&#13;
+5mjg2zuYvpxw1Fx0MXemzH03WEzDCupOXxy1UsZSs77bRKW5tXLaUStnuXbSakKw4MNPmNULfYDK&#13;
+bkebSrZpx42OQlR6zGsSMgRmThBVD2oLk6F0BeG/HUXmm1vvKFTSQNO1M8wKxVraQTtjOEJ3EO9t&#13;
+NegwxZ4GfqYRMBecppTBuk96wcNER6EcnTJd6aGj5tsaypyTwUIpj7QDc9RRS4M+/EzeOIou22Ee&#13;
+Ht8dNd/0at0J2Zx1NHCZB/MSFdyeBk208lnqaOCiO88sx/Y06OaTTxdEe4RrALXw5NZ6rGaPRdmM&#13;
+cMOgWhnAVjiiN8HLCDe21cIA7qHMIrAe77voK8I7SpoD0BP7AswDtGI6wEzttJCrDufoV5m4A5hr&#13;
+7Z+UVr1zy/hNjv5gsJPlTINZzYuiNzwk7gDiqNrKXLm0DADm51ctW2oebG//LwZwndrApBu9smpe&#13;
+BjAV4U7FobuSHXsqOT9Qa5boVqoljWDHolqH4py0apqaeol90LgaaAbMA0m/Dec+jKvwGM3I7iLc&#13;
+uaReUB94jiYTmplpeSTYoaj0eqC1IF3MqWUQ7aOXCHewlNcwi7Na0aipDIBZYd4Dc53oXw/kVHab&#13;
+FgQ04KrlH3FVdlkA86qWAAAAAElFTkSuQmCC</y:Resource>
+      <y:Resource id="2" type="java.awt.image.BufferedImage" xml:space="preserve">iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAYAAABXAvmHAAAA5UlEQVR4Xu2YywrCQBAE96J+vYLE&#13;
+7/TxBTqbU89uGkYCYRqmoA5bdoJ63NaKoiiKQpyzeTdf5lfEj7mYF3P98uNAxf4jpP750XfbiGpO&#13;
+Qc0pqOkPBLY5sjPpwwjbHNmZ9GGEbY7szClkFXF9HGYVcX0cZhVxnY0QtknR2QhhmxSdjRC2SdHx&#13;
+oOgU1JyCmv4AZOuI27AHsnXEbdgD2TriNnhQdApqTkFNfwD+7Xtg7wz10CjQ98DeGeqhUaDvgb0z&#13;
+1Ps1HQYl+6Vce2x8oOLVXC9I+x1jv6YbB1l9mjfz1IqiKIpCmR8gyWIDqr6GJQAAAABJRU5ErkJg&#13;
+gg==</y:Resource>
+      <y:Resource id="3" type="java.awt.image.BufferedImage" xml:space="preserve">iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAYAAABXAvmHAAAEjklEQVR4Xu2YScgdRRDHS6MGIq4x&#13;
+UfGmIiiGHJII0YQcFFHw4IIgeBINuBwMEYLeEjGBGBHUuCCK+0E8uIKiHsRAVAzqQQXXi/uC+4K7&#13;
+9XvT7dRU97zpft9LvLw//OD7uqtmeub11NIiM8000zS0l7JCuUZ5SvlQ+VH5M8DfHyhPKlcHW3z+&#13;
+dx2pbFG+VP6pBB98ucYe1yHKHcofki6sFq5xu3Kw7CGdp3wv6UI825WTAqcpa5VblPcytsA1z5Xd&#13;
+qHnSvHV/4z6+UG4eeaY6TvlaUh+4TZp7TVXzlWckvdkQ3+Cc0RpJbS1PS3PPqYi34Rf/i3KjNFuD&#13;
+iHKWco+ziewjXRF9ng1zlr/d/zzE3sFnTvLb5nVpPuKczlf+ktb2/e70aPEbJV38u8rxystu/NaR&#13;
+1xzEB2svuFNZ0LEQWS3Nloi6SVr7e804vwTfhF/8r8oJwWZ/5RU3P/GHzVu20YZE5N/8ujBHKNyq&#13;
+XK68EMbg7mBHrH/JjFuuCjZRhymfhjn4TiYMsXbrkE2XdadHb93vW89byoXSXZDlTclHnJXS3YrV&#13;
+W4k3ZpPUtu70SDuknf9duU953oyV8JvyqLJUUpEzoh1rOaI7PV6k+Oj8g3Jgd3qkj6WZf1uaDzDq&#13;
+4jBeyqvKopFnV4cqP0lrd113ul9Eiq+kdeRhclovzTbLPdzjki40x3PKAcEnpxuktSUxFhWAxHV7&#13;
+k2O700W6SNLFeh5R9osOPSI6WZ/l3em8KImjA9ujVvtKmvg8nwS7Er0jrd8GN5cV9Xx0IKbX6GRJ&#13;
+k5GHyHVXdCiQjYZszUHRjESHS9xcThdIE+93SbrYHOzrGl0hra/P7FnRPUUHap1xorvyCxzispFn&#13;
+uc6U1pfEOiiSVnSglu/TYqlvaLA/CucKnSJd/0H1PQCxepNyujQf4KnGrpQ7pV7VD2C30JVm/H4z&#13;
+TnPO9+EXOA6y9dFSr+otRNFmHWIeeFB5LYxTp1xr7EqgxZxE9iOmHR0Ub9fe+HNlVZijwfgojFMl&#13;
++kWOI1fvlMiG0cfcXFa5yMIbf1g5RzlDmo7M2wyxRCYTzU68RlEi86WEh47M1y/UQ3RjfScOQNW5&#13;
+MDoU6kTpXqOolKBgonDyC7BQ4+c0VAORhdmSlAcPyPCvQt8dffErKubQZklvbqHzyonwii+nEd4n&#13;
+BzVRX0HHr/WztLbF5TTyDY3n0tY0K7osOriHJPX1UD/lxGldtCEEVzU0iDbO3ywSe90h0eh4Xw9B&#13;
+w4uoZ9tVurNq0Uj3hUpOEkoOZddI6ut58T/rRpQo7Pc4z3Y8qGNRIY40/A0j1xu7PpG8vF+OmKGJ&#13;
+bm+4ubPD3MTqWwQZe5zYs99K6peDbMub94vvO1utEh+kbXIsxxg7KxJebPpLoLH3Ry9PyJSOFhEH&#13;
+rbmHYIzTAysy9dB50RAsfmqHu1H8EvasJkKspsuiAT9cyrdNH7SxU3vzObE9+hZpS/FaiDZz/mBL&#13;
+RYjl1yDB+IXUwjW41sShci4i0pDibdwu5bPgW51hd4cosqgUKXep2alKaYYoR4C/GWMOG2yLC7OZ&#13;
+ZpqpX/8CMj4N1UAQhFcAAAAASUVORK5CYII=</y:Resource>
+    </y:Resources>
+  </data>
+</graphml>

+ 8 - 24
etc.skeleton/application.yml

@@ -22,29 +22,13 @@ store:
     # The semantic store used for persisting LDP-RS (RDF Source) resources.
     # MUST support SPARQL 1.1 query and update.
     ldp_rs:
-        # Connector class. This corresponds to a sub-class of the
-        # `lakesuperior.store_layouts.rdf.base_connector.BaseConnector`.
-        connector:
-            module: sparql_connector
-            options:
-                # `location` must be consistent for all connector types. Other
-                # options may vary.
-                location: http://localhost:3030/fcrepo/
-                query_ep: query
-                update_ep: update
-                # Optional
-                #username: <set me>
-                #password: <set me>
-                #ssl_verify: false
-
-            # Connector for BerkeleyDB embedded store.
-            #module: bdb_connector
-            #options:
-            #    location: /data/fcrepo/ldprs_store/bdb
+        # Directory where the RDF data files are stored.
+        location: /tmp/fcrepo/data/ldprs_store
 
         # store layout. this corresponds to a sub-class of the
-        # `lakesuperior.store_layouts.rdf.base_rdf_layout/baserdflayout`.
-        layout: default_layout
+        # `lakesuperior.store.rdf.base_rdf_layout/baserdflayout`.
+        layout: rsrc_centric_layout
+
         # whether to check if the object of a client-provided triple is the uri
         # of a repository-managed resource and veify if that exists.
         # if set to false, properties are allowed to point to resources in the
@@ -55,9 +39,9 @@ store:
         # user-provided triple if its object violates referential integrity.
         # `strict` raises an exception.
         referential_integrity: lenient
+
         # this mimics fedora4 behavior which segments an identifier on post.
-        # hyrax will break if this is off.
-        legacy_ptree_split: false
+        legacy_ptree_split: False
 
     # The path used to persist LDP-NR (bitstreams).
     # This is for now a POSIX filesystem. Other solutions such as HDFS may be
@@ -66,7 +50,7 @@ store:
         # See store.ldp_rs.layout.
         layout: default_layout
         # The filesystem path to the root of the binary store.
-        path: /data/fcrepo/ldpnr_store
+        path: /tmp/fcrepo/data/ldpnr_store
 
         # How to split the balanced pairtree to generate a path. The hash
         # string is defined by the uuid.algo parameter value.

+ 2 - 10
etc.skeleton/test.yml

@@ -4,15 +4,7 @@
 
 store:
     ldp_rs:
-        connector:
-            options:
-                location: http://localhost:3030/fcrepo-test/
-                query_ep: query
-                update_ep: update
-                # Optional
-                #username: <set me>
-                #password: <set me>
-                #ssl_verify: false
+        location: /tmp/fcrepo_test/data/ldprs_store
     ldp_nr:
-        path: /tmp/fcrepo_test/ldpnr_store
+        path: /tmp/fcrepo_test/data/ldpnr_store
 

+ 61 - 0
lakesuperior/api/resource.py

@@ -0,0 +1,61 @@
+from functools import wraps
+from multiprocessing import Process
+from threading import Lock, Thread
+
+from flask import (
+        Blueprint, current_app, g, make_response, render_template,
+        request, send_file)
+
+from lakesuperior.store.ldp_rs.lmdb_store import TxnManager
+
+
+def transaction(write=False):
+    '''
+    Handle atomic operations in a store.
+
+    This wrapper ensures that a write operation is performed atomically. It
+    also takes care of sending a message for each resource changed in the
+    transaction.
+    '''
+    def _transaction_deco(fn):
+        @wraps(fn)
+        def _wrapper(*args, **kwargs):
+            if not hasattr(g, 'changelog'):
+                g.changelog = []
+            store = current_app.rdfly.store
+            with TxnManager(store, write=write) as txn:
+                ret = fn(*args, **kwargs)
+            if len(g.changelog):
+                job = Thread(target=process_queue)
+                job.start()
+            return ret
+
+        return _wrapper
+    return _transaction_deco
+
+
+def process_queue():
+    '''
+    Process the message queue on a separate thread.
+    '''
+    lock = Lock()
+    lock.acquire()
+    while len(g.changelog):
+        send_event_msg(g.changelog.pop())
+    lock.release()
+
+
+def send_event_msg(remove_trp, add_trp, metadata):
+    '''
+    Break down delta triples, find subjects and send event message.
+    '''
+    remove_grp = groupby(remove_trp, lambda x : x[0])
+    remove_dict = { k[0] : k[1] for k in remove_grp }
+
+    add_grp = groupby(add_trp, lambda x : x[0])
+    add_dict = { k[0] : k[1] for k in add_grp }
+
+    subjects = set(remove_dict.keys()) | set(add_dict.keys())
+    for rsrc_uri in subjects:
+        self._logger.info('subject: {}'.format(rsrc_uri))
+        #current_app.messenger.send

+ 3 - 12
lakesuperior/app.py

@@ -46,26 +46,17 @@ def create_app(app_conf, logging_conf):
     app.register_blueprint(query, url_prefix='/query')
     app.register_blueprint(admin, url_prefix='/admin')
 
-    # Initialize RDF store connector.
-    conn_mod_name = app_conf['store']['ldp_rs']['connector']['module']
-    conn_mod = import_module('lakesuperior.store_layouts.ldp_rs.{}'.format(
-            conn_mod_name))
-    conn_cls = getattr(conn_mod, camelcase(conn_mod_name))
-    rdf_store_conn = conn_cls(
-            **app_conf['store']['ldp_rs']['connector']['options'])
-    logger.info('RDF store: {}'.format(conn_mod_name))
-
     # Initialize RDF layout.
     rdfly_mod_name = app_conf['store']['ldp_rs']['layout']
-    rdfly_mod = import_module('lakesuperior.store_layouts.ldp_rs.{}'.format(
+    rdfly_mod = import_module('lakesuperior.store.ldp_rs.{}'.format(
             rdfly_mod_name))
     rdfly_cls = getattr(rdfly_mod, camelcase(rdfly_mod_name))
-    app.rdfly = rdfly_cls(rdf_store_conn, app_conf['store']['ldp_rs'])
+    app.rdfly = rdfly_cls(app_conf['store']['ldp_rs'])
     logger.info('RDF layout: {}'.format(rdfly_mod_name))
 
     # Initialize file layout.
     nonrdfly_mod_name = app_conf['store']['ldp_nr']['layout']
-    nonrdfly_mod = import_module('lakesuperior.store_layouts.ldp_nr.{}'.format(
+    nonrdfly_mod = import_module('lakesuperior.store.ldp_nr.{}'.format(
             nonrdfly_mod_name))
     nonrdfly_cls = getattr(nonrdfly_mod, camelcase(nonrdfly_mod_name))
     app.nonrdfly = nonrdfly_cls(app_conf['store']['ldp_nr'])

+ 2 - 2
lakesuperior/config_parser.py

@@ -40,8 +40,8 @@ Please review your configuration before starting.
 config['test'] = hiyapyco.load(CONFIG_DIR + '/application.yml',
         CONFIG_DIR + '/test.yml', method=hiyapyco.METHOD_MERGE)
 
-if config['application']['store']['ldp_rs']['connector']['options']['location'] \
-        == config['test']['store']['ldp_rs']['connector']['options']['location']:
+if config['application']['store']['ldp_rs']['location'] \
+        == config['test']['store']['ldp_rs']['location']:
             raise RuntimeError(error_msg.format('RDF'))
             sys.exit()
 

+ 1 - 1
lakesuperior/endpoints/admin.py

@@ -2,7 +2,7 @@ import logging
 
 from flask import Blueprint, current_app, g, request, render_template
 
-from lakesuperior.store_layouts.ldp_rs.lmdb_store import TxnManager
+from lakesuperior.store.ldp_rs.lmdb_store import TxnManager
 
 # Admin interface and API.
 

+ 2 - 68
lakesuperior/endpoints/ldp.py

@@ -2,7 +2,6 @@ import logging
 
 from collections import defaultdict
 from pprint import pformat
-from functools import wraps
 from uuid import uuid4
 
 import arrow
@@ -13,17 +12,17 @@ from flask import (
 from rdflib.namespace import XSD
 from rdflib.term import Literal
 
+from lakesuperior.api.resource import transaction
 from lakesuperior.dictionaries.namespaces import ns_collection as nsc
 from lakesuperior.dictionaries.namespaces import ns_mgr as nsm
 from lakesuperior.exceptions import (ResourceNotExistsError, TombstoneError,
         ServerManagedTermError, InvalidResourceError, SingleSubjectError,
         ResourceExistsError, IncompatibleLdpTypeError)
-from lakesuperior.model.generic_resource import PathSegment
 from lakesuperior.model.ldp_factory import LdpFactory
 from lakesuperior.model.ldp_nr import LdpNr
 from lakesuperior.model.ldp_rs import LdpRs
 from lakesuperior.model.ldpr import Ldpr
-from lakesuperior.store_layouts.ldp_rs.lmdb_store import LmdbStore, TxnManager
+from lakesuperior.store.ldp_rs.lmdb_store import TxnManager
 from lakesuperior.toolbox import Toolbox
 
 
@@ -103,70 +102,6 @@ def log_request_end(rsp):
     return rsp
 
 
-def transaction(write=False):
-    '''
-    Handle atomic operations in a store.
-
-    This wrapper ensures that a write operation is performed atomically. It
-    also takes care of sending a message for each resource changed in the
-    transaction.
-    '''
-    def _transaction_deco(fn):
-        @wraps(fn)
-        def _wrapper(*args, **kwargs):
-            g.changelog = []
-            store = current_app.rdfly.store
-            if isinstance(store, LmdbStore):
-                with TxnManager(store, write=write) as txn:
-                    ret = fn(*args, **kwargs)
-                return ret
-            else:
-                try:
-                    ret = fn(*args, **kwargs)
-                except:
-                    logger.warn('Rolling back transaction.')
-                    store.rollback()
-                    raise
-                else:
-                    logger.info('Committing transaction.')
-                    #if hasattr(store, '_edits'):
-                    #    # @FIXME ugly.
-                    #    self.rdfly._conn.optimize_edits()
-                    store.commit()
-                    return ret
-            # @TODO re-enable, maybe leave out the delta part
-            #for ev in g.changelog:
-            #    #self._logger.info('Message: {}'.format(pformat(ev)))
-            #    send_event_msg(*ev)
-
-        return _wrapper
-    return _transaction_deco
-
-
-def send_msg(self, ev_type, remove_trp=None, add_trp=None):
-    '''
-    Sent a message about a changed (created, modified, deleted) resource.
-    '''
-    try:
-        type = self.types
-        actor = self.metadata.value(nsc['fcrepo'].createdBy)
-    except (ResourceNotExistsError, TombstoneError):
-        type = set()
-        actor = None
-        for t in add_trp:
-            if t[1] == RDF.type:
-                type.add(t[2])
-            elif actor is None and t[1] == nsc['fcrepo'].createdBy:
-                actor = t[2]
-
-    g.changelog.append((set(remove_trp), set(add_trp), {
-        'ev_type' : ev_type,
-        'time' : g.timestamp,
-        'type' : type,
-        'actor' : actor,
-    }))
-
-
 ## REST SERVICES ##
 
 @ldp.route('/<path:uid>', methods=['GET'], strict_slashes=False)
@@ -202,7 +137,6 @@ def get_resource(uid, force_rdf=False):
         out_headers.update(rsrc.head())
         if (
                 isinstance(rsrc, LdpRs)
-                or isinstance(rsrc, PathSegment)
                 or is_accept_hdr_rdf_parsable()
                 or force_rdf):
             rsp = rsrc.get()

+ 1 - 1
lakesuperior/endpoints/query.py

@@ -5,7 +5,7 @@ from rdflib.plugin import PluginException
 
 from lakesuperior.dictionaries.namespaces import ns_mgr as nsm
 from lakesuperior.query import QueryEngine
-from lakesuperior.store_layouts.ldp_rs.lmdb_store import LmdbStore, TxnManager
+from lakesuperior.store.ldp_rs.lmdb_store import LmdbStore, TxnManager
 
 # Query endpoint. raw SPARQL queries exposing the underlying layout can be made
 # available. Also convenience methods that allow simple lookups based on simple

+ 0 - 83
lakesuperior/model/generic_resource.py

@@ -1,83 +0,0 @@
-from flask import current_app, g
-from rdflib.resource import Resource
-
-from lakesuperior.dictionaries.namespaces import ns_collection as nsc
-from lakesuperior.dictionaries.namespaces import ns_mgr as nsm
-from lakesuperior.store_layouts.ldp_rs.rsrc_centric_layout import PTREE_GR_URI
-
-
-class GenericResource:
-    '''
-    Generic RDF resource.
-
-    This may not have a dedicated named graph.
-    '''
-
-    def __init__(self, uid):
-        '''
-        Initialize a generic resource.
-        '''
-        self.uid = uid
-        self.urn = nsc['fcres'][uid]
-        self.rdfly = current_app.rdfly
-
-
-    @property
-    def metadata(self):
-        if not hasattr(self, '_metadata'):
-            gr = self.rdfly.get_raw(self.urn)
-            self._metadata = Resource(gr, self.urn)
-
-        return self._metadata
-
-
-    @property
-    def out_graph(self):
-        return self.metadata.graph
-
-
-    def head(self):
-        '''
-        No-op to keep consistency with methods that may request this
-        without knowing if it is a LDP resource or what else.
-        '''
-        return {}
-
-
-    def extract(self, p=None, o=None):
-        '''
-        Extract an in-memory copy of the resource containing either a
-        sub-graph, defined with the `p` and `o` parameters, or the whole
-        resource.
-        '''
-        # @TODO
-        pass
-
-
-class PathSegment(GenericResource):
-    '''
-    Represent a path segment in a URI.
-
-    A path segment is not an LDP resource, and its metadata should be confined
-    to a separate, generic named graph.
-    '''
-    @property
-    def metadata(self):
-        if not hasattr(self, '_metadata'):
-            gr = self.rdfly.get_raw(self.urn, PTREE_GR_URI)
-            self._metadata = Resource(gr, self.urn)
-
-        return self._metadata
-
-
-    def get(self):
-        '''
-        Get an RDF representation of the resource.
-
-        Internal URNs are replaced by global URIs using the endpoint webroot.
-        The resource has very few triples so no namespace manager is used to
-        reduce output size.
-        '''
-        return g.tbox.globalize_graph(self.out_graph)
-
-

+ 0 - 3
lakesuperior/model/ldp_factory.py

@@ -11,7 +11,6 @@ from rdflib.resource import Resource
 from rdflib.namespace import RDF
 
 from lakesuperior import model
-from lakesuperior.model.generic_resource import PathSegment
 from lakesuperior.dictionaries.namespaces import ns_collection as nsc
 from lakesuperior.exceptions import (
         IncompatibleLdpTypeError, InvalidResourceError, ResourceExistsError,
@@ -67,8 +66,6 @@ class LdpFactory:
         elif __class__.LDP_RS_TYPE in rdf_types:
             __class__._logger.info('Resource is a LDP-RS.')
             rsrc = model.ldp_rs.LdpRs(uid, repr_opts, **kwargs)
-        elif nsc['fcsystem']['PathSegment'] in rdf_types:
-            return PathSegment(uid)
         else:
             raise ResourceNotExistsError(uid)
 

+ 1 - 1
lakesuperior/model/ldp_rs.py

@@ -84,7 +84,7 @@ class LdpRs(Ldpr):
         self.rdfly.patch_rsrc(self.uid, update_str)
 
         if notify and current_app.config.get('messaging'):
-            self._send_msg(self.RES_UPDATED, check_del_gr, check_ins_gr)
+            self._enqueue_msg(self.RES_UPDATED, check_del_gr, check_ins_gr)
 
         # @FIXME Ugly workaround until we find how to recompose a SPARQL query
         # string from a parsed query object.

+ 7 - 47
lakesuperior/model/ldpr.py

@@ -2,8 +2,7 @@ import logging
 
 from abc import ABCMeta
 from collections import defaultdict
-from itertools import accumulate, groupby
-#from pprint import pformat
+from itertools import groupby
 from uuid import uuid4
 
 import arrow
@@ -21,8 +20,7 @@ from lakesuperior.dictionaries.srv_mgd_terms import  srv_mgd_subjects, \
 from lakesuperior.exceptions import (RefIntViolationError,
         ResourceNotExistsError, ServerManagedTermError, TombstoneError)
 from lakesuperior.model.ldp_factory import LdpFactory
-from lakesuperior.store_layouts.ldp_rs.rsrc_centric_layout import (
-        VERS_CONT_LABEL)
+from lakesuperior.store.ldp_rs.rsrc_centric_layout import VERS_CONT_LABEL
 
 
 ROOT_UID = ''
@@ -692,19 +690,15 @@ class Ldpr(metaclass=ABCMeta):
         @param add_trp (set) Triples to be added.
         @param notify (boolean) Whether to send a message about the change.
         '''
-        #for trp in [remove_trp, add_trp]:
-        #    if not isinstance(trp, set):
-        #        trp = set(trp)
-
         ret = self.rdfly.modify_rsrc(self.uid, remove_trp, add_trp)
 
-        #if notify and current_app.config.get('messaging'):
-        #    self._send_msg(ev_type, remove_trp, add_trp)
+        if notify and current_app.config.get('messaging'):
+            self._enqueue_msg(ev_type, remove_trp, add_trp)
 
         return ret
 
 
-    def _send_msg(self, ev_type, remove_trp=None, add_trp=None):
+    def _enqueue_msg(self, ev_type, remove_trp=None, add_trp=None):
         '''
         Sent a message about a changed (created, modified, deleted) resource.
         '''
@@ -720,6 +714,8 @@ class Ldpr(metaclass=ABCMeta):
                 elif actor is None and t[1] == nsc['fcrepo'].createdBy:
                     actor = t[2]
 
+        if not hasattr(g, 'changelog'):
+            g.changelog = []
         g.changelog.append((set(remove_trp), set(add_trp), {
             'ev_type' : ev_type,
             'time' : g.timestamp,
@@ -728,25 +724,6 @@ class Ldpr(metaclass=ABCMeta):
         }))
 
 
-    # Not used. @TODO Deprecate or reimplement depending on requirements.
-    #def _ensure_single_subject_rdf(self, gr, add_fragment=True):
-    #    '''
-    #    Ensure that a RDF payload for a POST or PUT has a single resource.
-    #    '''
-    #    for s in set(gr.subjects()):
-    #        # Fragment components
-    #        if '#' in s:
-    #            parts = s.split('#')
-    #            frag = s
-    #            s = URIRef(parts[0])
-    #            if add_fragment:
-    #                # @TODO This is added to the main graph. It should be added
-    #                # to the metadata graph.
-    #                gr.add((frag, nsc['fcsystem'].fragmentOf, s))
-    #        if not s == self.urn:
-    #            raise SingleSubjectError(s, self.uid)
-
-
     def _check_ref_int(self, config):
         gr = self.provided_imr.graph
 
@@ -925,20 +902,3 @@ class Ldpr(metaclass=ABCMeta):
             target_rsrc._modify_rsrc(self.RES_UPDATED, add_trp={(s, p, o)})
 
         self._modify_rsrc(self.RES_UPDATED, add_trp=add_trp)
-
-
-    # @TODO reenable at request level.
-    #def _send_event_msg(self, remove_trp, add_trp, metadata):
-    #    '''
-    #    Break down delta triples, find subjects and send event message.
-    #    '''
-    #    remove_grp = groupby(remove_trp, lambda x : x[0])
-    #    remove_dict = { k[0] : k[1] for k in remove_grp }
-
-    #    add_grp = groupby(add_trp, lambda x : x[0])
-    #    add_dict = { k[0] : k[1] for k in add_grp }
-
-    #    subjects = set(remove_dict.keys()) | set(add_dict.keys())
-    #    for rsrc_uri in subjects:
-    #        self._logger.info('subject: {}'.format(rsrc_uri))
-    #        #current_app.messenger.send

+ 0 - 0
lakesuperior/store_layouts/ldp_nr/base_non_rdf_layout.py → lakesuperior/store/ldp_nr/base_non_rdf_layout.py


+ 1 - 2
lakesuperior/store_layouts/ldp_nr/default_layout.py → lakesuperior/store/ldp_nr/default_layout.py

@@ -3,8 +3,7 @@ import os
 from hashlib import sha1
 from uuid import uuid4
 
-from lakesuperior.store_layouts.ldp_nr.base_non_rdf_layout import \
-        BaseNonRdfLayout
+from lakesuperior.store.ldp_nr.base_non_rdf_layout import BaseNonRdfLayout
 
 class DefaultLayout(BaseNonRdfLayout):
     '''

+ 10 - 56
lakesuperior/store_layouts/ldp_rs/lmdb_store.py → lakesuperior/store/ldp_rs/lmdb_store.py

@@ -3,11 +3,9 @@ import logging
 import os
 
 from contextlib import ContextDecorator, ExitStack
-from multiprocessing import Process
 from os import makedirs
 from os.path import exists, abspath
 from shutil import rmtree
-from threading import Lock, Thread
 from urllib.request import pathname2url
 
 import lmdb
@@ -34,14 +32,6 @@ def b2s(u, enc='UTF-8'):
     return bytes(u).decode(enc)
 
 
-class NoTxnError(Exception):
-    '''
-    Raised if a store operation is attempted while no transaction is present.
-    '''
-    def __str__(self):
-        return 'No transaction active in the store.'
-
-
 class TxnManager(ContextDecorator):
     '''
     Handle ACID transactions with an LmdbStore.
@@ -73,24 +63,9 @@ class TxnManager(ContextDecorator):
     def __exit__(self, exc_type, exc_value, traceback):
         if exc_type:
             self.store.rollback()
-            # If the tx fails, leave the index queue alone. There may still be
-            # jobs left from other requests.
         else:
             self.store.commit()
-            if self.write:
-                if len(self.store._data_queue):
-                    self.store._apply_changes()
-                if len(self.store._idx_queue):
-                    # Ditch index data. For testing data entry only.
-                    #self.store._idx_queue = []
-                    # Synchronous.
-                    self.store._run_indexing()
-                    # Threading.
-                    #job = Thread(target=self.store._run_indexing)
-                    # Multiprocess.
-                    #job = Process(target=self.store._run_indexing)
-                    #job.start()
-                    #logger.info('Started indexing job #{}'.format(job.ident))
+
 
 
 class LexicalSequence:
@@ -165,11 +140,15 @@ class LmdbStore(Store):
     '''
     LMDB-backed store.
 
+    This is an implementation of the RDFLib Store interface:
+    https://github.com/RDFLib/rdflib/blob/master/rdflib/store.py
+
+    Handles the interaction with a LMDB store and builds an abstraction layer
+    for triples.
+
     This store class uses two LMDB environments (i.e. two files): one for the
-    critical (preservation-worthy) data and the other for the index data which
-    can be rebuilt from the main database. @TODO For now, data and indices are
-    in the same environment due to complications in handling transaction
-    contexts.
+    main (preservation-worthy) data and the other for the index data which
+    can be rebuilt from the main database.
 
     There are 4 main data sets (preservation worthy data):
 
@@ -187,11 +166,8 @@ class LmdbStore(Store):
     - o:sp (O key: joined S, P keys; dupsort, dupfixed)
     - c:spo (context → triple association; dupsort, dupfixed)
     - ns:pfx (pickled namespace: prefix; 1:1)
-
-    These two data sets are stored in separate environments, i.e. separate
-    files in the filesystem. The index could be recreated from the main data
-    set in case of a disaster.
     '''
+
     context_aware = True
     # This is a hassle to maintain for no apparent gain. If some use is devised
     # in the future, it may be revised.
@@ -577,7 +553,6 @@ class LmdbStore(Store):
         Where the contexts generator lists all context that the triple appears
         in.
         '''
-        #import pdb; pdb.set_trace()
         #logger.debug('Getting triples for pattern: {} and context: {}'.format(
         #    triple_pattern, context))
         # This sounds strange, RDFLib should be passing None at this point,
@@ -749,27 +724,6 @@ class LmdbStore(Store):
         self.data_txn = self.idx_txn = self.is_txn_rw = None
 
 
-    def rebase(self, n, start=1):
-        '''
-        Create a bytearray translating an integer to an arbitrary base.
-
-        the base is between the `start` value and 255 to fit in one-byte
-        chunks.
-
-        @param n (int) Number to rebase.
-        @param start (int) Starting byte. This is useful to leave out "special"
-        bytes for purposes such as separators.
-
-        @return bytearray
-        '''
-        map = list(range(start, 255))
-        base = len(map)
-        if n < base:
-            return bytearray([map[n]])
-        else:
-            return self.rebase(n // base, start) + bytearray([map[n % base]])
-
-
     ## PRIVATE METHODS ##
 
     def _triple_keys(self, triple_pattern, context=None):

+ 11 - 11
lakesuperior/store_layouts/ldp_rs/rsrc_centric_layout.py → lakesuperior/store/ldp_rs/rsrc_centric_layout.py

@@ -4,11 +4,11 @@ from collections import defaultdict
 from itertools import chain
 
 from flask import g
-from rdflib import Graph, URIRef
+from rdflib import Dataset, Graph, Literal, URIRef, plugin
 from rdflib.namespace import RDF
 from rdflib.query import ResultException
 from rdflib.resource import Resource
-from rdflib.term import Literal
+from rdflib.store import Store
 
 from lakesuperior.dictionaries.namespaces import ns_collection as nsc
 from lakesuperior.dictionaries.namespaces import ns_mgr as nsm
@@ -16,9 +16,12 @@ from lakesuperior.dictionaries.srv_mgd_terms import  srv_mgd_subjects, \
         srv_mgd_predicates, srv_mgd_types
 from lakesuperior.exceptions import (InvalidResourceError,
         ResourceNotExistsError, TombstoneError, PathSegmentError)
-from lakesuperior.store_layouts.ldp_rs.lmdb_store import TxnManager
+from lakesuperior.store.ldp_rs.lmdb_store import TxnManager
 
 
+Lmdb = plugin.register('Lmdb', Store,
+        'lakesuperior.store.ldp_rs.lmdb_store', 'LmdbStore')
+
 META_GR_URI = nsc['fcsystem']['meta']
 HIST_GR_URI = nsc['fcsystem']['histmeta']
 PTREE_GR_URI = nsc['fcsystem']['pairtree']
@@ -38,14 +41,14 @@ class RsrcCentricLayout:
     contents are ingested in a repository, changing a layout will most likely
     require a migration.
 
-    The custom layout must be in the lakesuperior.store_layouts.rdf
+    The custom layout must be in the lakesuperior.store.rdf
     package and the class implementing the layout must be called
     `StoreLayout`. The module name is the one defined in the app
     configuration.
 
     E.g. if the configuration indicates `simple_layout` the application will
     look for
-    `lakesuperior.store_layouts.rdf.simple_layout.SimpleLayout`.
+    `lakesuperior.store.rdf.simple_layout.SimpleLayout`.
     '''
 
     _logger = logging.getLogger(__name__)
@@ -110,7 +113,7 @@ class RsrcCentricLayout:
 
     ## MAGIC METHODS ##
 
-    def __init__(self, conn, config):
+    def __init__(self, config):
         '''Initialize the graph store and a layout.
 
         NOTE: `rdflib.Dataset` requires a RDF 1.1 compliant store with support
@@ -120,11 +123,8 @@ class RsrcCentricLayout:
         which is currently the reference implementation.
         '''
         self.config = config
-        self._conn = conn
-        self.store = self._conn.store
-
-        #self.UNION_GRAPH_URI = self._conn.UNION_GRAPH_URI
-        self.ds = self._conn.ds
+        self.store = plugin.get('Lmdb', Store)(config['location'])
+        self.ds = Dataset(self.store, default_union=True)
         self.ds.namespace_manager = nsm
 
 

+ 0 - 76
lakesuperior/store_layouts/ldp_rs/base_connector.py

@@ -1,76 +0,0 @@
-import logging
-import traceback
-
-from abc import ABCMeta, abstractmethod
-
-from rdflib.term import URIRef
-
-from lakesuperior.dictionaries.namespaces import ns_collection as nsc
-
-
-class BaseConnector(metaclass=ABCMeta):
-    '''
-    Handles the connection and dataset information.
-
-    This is indpendent from the application context (production/test) and can
-    be passed any configuration options.
-    '''
-
-    UNION_GRAPH_URI = URIRef('urn:x-rdflib:default')
-
-    _logger = logging.getLogger(__name__)
-
-    def __init__(self, location, *args, **kwargs):
-        '''
-        Initialize the connection to the SPARQL endpoint.
-
-        If `update_ep` is not specified, the store is initialized as read-only.
-        '''
-        self._init_connection(location, *args, **kwargs)
-
-
-    @abstractmethod
-    def _init_connection(self, location, *args, **kwargs):
-        '''
-        Interface method. Connection steps go here.
-        '''
-        pass
-
-
-    def query(self, q, initBindings=None, nsc=nsc):
-        '''
-        Perform a SPARQL query on the triplestore.
-
-        This provides non-abstract access, independent from the layout.
-
-        @param q (string) SPARQL query.
-
-        @return rdflib.query.Result
-        '''
-        #self._logger.debug('Sending SPARQL Query: {}\nBindings: {}'.format(
-        #    q, initBindings))
-        #self._logger.debug('From:\n{}'.format(
-        #    (''.join(traceback.format_stack(limit=5)))))
-        return self.ds.query(q, initBindings=initBindings, initNs=nsc)
-
-
-    def update(self, q, initBindings=None, nsc=nsc):
-        '''
-        Perform a SPARQL update on the triplestore. This is only needed for
-        low-level, optimized operations that are not well performed by the
-        higher-level methods provided by RDFLib.
-
-        This provides non-abstract access, independent from the layout.
-
-        @param q (string) SPARQL-UPDATE query.
-
-        @return None
-        '''
-        #self._logger.debug('Sending SPARQL Update: {}\nBindings: {}'.format(
-        #    q, initBindings))
-        #self._logger.debug('From:\n{}'.format(
-        #    (''.join(traceback.format_stack(limit=5)))))
-        return self.ds.query(q, initBindings=initBindings)
-
-
-

+ 0 - 40
lakesuperior/store_layouts/ldp_rs/bdb_connector.py

@@ -1,40 +0,0 @@
-import logging
-
-from rdflib import Dataset, plugin
-from rdflib.store import Store
-from rdflib.term import URIRef
-from rdflib.plugins.stores.sparqlstore import SPARQLStore, SPARQLUpdateStore
-from SPARQLWrapper.Wrapper import POST
-
-from lakesuperior.dictionaries.namespaces import ns_collection as nsc
-from lakesuperior.store_layouts.ldp_rs.base_connector import BaseConnector
-
-
-class BdbConnector(BaseConnector):
-    '''
-    Handles the connection and dataset information.
-
-    This is indpendent from the application context (production/test) and can
-    be passed any configuration options.
-    '''
-
-    _logger = logging.getLogger(__name__)
-
-    def _init_connection(self, location):
-        '''
-        Initialize the connection to the BerkeleyDB (Sleepycat) store.
-
-        Also open the store, which must be closed by the __del__ method.
-        '''
-        self.store = plugin.get('Sleepycat', Store)(
-                identifier=URIRef('urn:fcsystem:lsup'))
-        self.ds = Dataset('Sleepycat', default_union=True)
-        #self.store = self.ds.store
-        self.ds.open(location, create=True)
-
-
-    def __del__(self):
-        '''
-        Close store connection.
-        '''
-        self.ds.close(commit_pending_transaction=False)

+ 0 - 34
lakesuperior/store_layouts/ldp_rs/lmdb_connector.py

@@ -1,34 +0,0 @@
-import logging
-
-from rdflib import Dataset, plugin
-from rdflib.store import Store
-from rdflib.term import URIRef
-from rdflib.plugins.stores.sparqlstore import SPARQLStore, SPARQLUpdateStore
-from SPARQLWrapper.Wrapper import POST
-
-from lakesuperior.dictionaries.namespaces import ns_collection as nsc
-from lakesuperior.store_layouts.ldp_rs.base_connector import BaseConnector
-
-Lmdb = plugin.register('Lmdb', Store,
-        'lakesuperior.store_layouts.ldp_rs.lmdb_store', 'LmdbStore')
-
-class LmdbConnector(BaseConnector):
-    '''
-    Handles the connection with a LMDB store.
-    '''
-
-    _logger = logging.getLogger(__name__)
-
-    def _init_connection(self, location):
-        '''
-        Initialize the connection to the LMDB store and open it.
-        '''
-        self.store = plugin.get('Lmdb', Store)(location)
-        self.ds = Dataset(self.store)
-
-
-    def __del__(self):
-        '''
-        Close store connection.
-        '''
-        self.ds.close(commit_pending_transaction=False)

+ 1 - 1
tests/store/test_lmdb_store.py

@@ -6,7 +6,7 @@ from rdflib import Namespace, URIRef
 from rdflib.graph import DATASET_DEFAULT_GRAPH_ID as RDFLIB_DEFAULT_GRAPH_URI
 from rdflib.namespace import RDF, RDFS
 
-from lakesuperior.store_layouts.ldp_rs.lmdb_store import LmdbStore, TxnManager
+from lakesuperior.store.ldp_rs.lmdb_store import LmdbStore, TxnManager
 
 
 @pytest.fixture(scope='class')

+ 1 - 1
util/bootstrap.py

@@ -7,7 +7,7 @@ sys.path.append('.')
 
 from lakesuperior.app import create_app
 from lakesuperior.config_parser import config
-from lakesuperior.store_layouts.ldp_rs.lmdb_store import TxnManager
+from lakesuperior.store.ldp_rs.lmdb_store import TxnManager
 from lakesuperior.model.ldpr import Ldpr
 
 __doc__ = '''