Browse Source

Initial commit: some boilerplate borrowed from Combine, basic folder structure and documentation.

Stefano Cossu 6 years ago
commit
2fdc1b902e

+ 104 - 0
.gitignore

@@ -0,0 +1,104 @@
+# Byte-compiled / optimized / DLL files
+__pycache__/
+*.py[cod]
+*$py.class
+
+# C extensions
+*.so
+
+# Distribution / packaging
+.Python
+build/
+develop-eggs/
+dist/
+downloads/
+eggs/
+.eggs/
+lib/
+lib64/
+parts/
+sdist/
+var/
+wheels/
+*.egg-info/
+.installed.cfg
+*.egg
+MANIFEST
+
+# PyInstaller
+#  Usually these files are written by a python script from a template
+#  before PyInstaller builds the exe, so as to inject date/other infos into it.
+*.manifest
+*.spec
+
+# Installer logs
+pip-log.txt
+pip-delete-this-directory.txt
+
+# Unit test / coverage reports
+htmlcov/
+.tox/
+.coverage
+.coverage.*
+.cache
+nosetests.xml
+coverage.xml
+*.cover
+.hypothesis/
+
+# Translations
+*.mo
+*.pot
+
+# Django stuff:
+*.log
+.static_storage/
+.media/
+local_settings.py
+
+# Flask stuff:
+instance/
+.webassets-cache
+
+# Scrapy stuff:
+.scrapy
+
+# Sphinx documentation
+docs/_build/
+
+# PyBuilder
+target/
+
+# Jupyter Notebook
+.ipynb_checkpoints
+
+# pyenv
+.python-version
+
+# celery beat schedule file
+celerybeat-schedule
+
+# SageMath parsed files
+*.sage.py
+
+# Environments
+.env
+.venv
+env/
+venv/
+ENV/
+env.bak/
+venv.bak/
+
+# Spyder project settings
+.spyderproject
+.spyproject
+
+# Rope project settings
+.ropeproject
+
+# mkdocs documentation
+/site
+
+# mypy
+.mypy_cache/

+ 201 - 0
LICENSE

@@ -0,0 +1,201 @@
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "{}"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright {yyyy} {name of copyright owner}
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.

+ 48 - 0
README.md

@@ -0,0 +1,48 @@
+# LAKEsuperior
+
+LAKEsuperior is an experimental [Fedora Repository](http://fedorarepository.org)
+implementation.
+
+## Basic concepts
+
+LAKEsuperior stores LDP resources in a triplestore (currently Blazegraph) and
+non-RDF sources (i.e. binaries) in a filesystem.
+
+Resources are stored in discrete named graphs under the hood. A "main" graph
+contains metadata about the resources, e.g. provenance information (NOTE:
+versioning is not in the Level 1 scope).
+
+[@TODO more]
+
+## Installation
+
+This is currently just a couple of config files, so there is nothing to install
+yet. However, in the happiest possible outcome for Level 1, it should go like
+this:
+
+1. Install [Blazegraph](https://sourceforge.net/projects/bigdata/files/bigdata/)
+2. Create a folder to store binary contents
+3. Copy the `etc.skeleton` folder to a separate location
+4. Configure the application and optionally add custom namespaces
+5. Run `server.py`
+
+## Status and development
+
+LAKEsuperior is in **pre-alpha** status.
+
+Development will be planned in subsequent "levels", the scope of each depending
+on the outcomes of the previous level development and feedback:
+
+- Level 1: proof of concept. This implementation will adhere to the current
+Fedora Repository v4 specifications, in order to provide a testbed application
+that easily integrates with Samvera and/or Islandora and can be used to test
+and compare features with the official Fedora 4 implementation.
+- Level 2: After a review with community members, the goal is to produce a beta
+release that includes basic features to become a drop-in (or near there) with
+the official Fedora 4.
+- Level 3: production quality release.
+- Level 4: Conform to the new [Fedora API specifications](http://fedora.info/spec/)
+
+## Further docuentation
+
+The design documents are in the `doc/pdf` folder.

BIN
doc/pdf/lakesuperior_recommendations.pdf


+ 522 - 0
doc/src/lakesuperior_recommendations.md

@@ -0,0 +1,522 @@
+---
+title: LAKEsuperior Recommendations
+author: "Stefano Cossu, the Art Institute of Chicago <scossu@artic.edu>
+        Kevin Ford, the Art Institute of Chicago, <kford1@rtic.edu>"
+papersize: letter
+geometry: "margin=1in"
+output: pdf_document
+fontfamily: ebgaramond
+fontfamilyoptions: osf
+fontsize: 11pt
+urlcolor: blue
+---
+
+# General Remarks
+
+## Purpose
+
+`lakesuperior` is the code name of the current
+internal Fedora repository that is part of LAKE, the institutional
+repository and DAMS for the Art Institute of Chicago Collections. 
+
+In order to overcome the current technical limitations of the current
+Modeshape-backed Fedora 4 implementation, we want to propose an
+alternative implementation of Fedora. 
+
+End goal of this project is a repository system primarily, but not exclusively, supported and used by
+AIC, fully compliant to the official Fedora API specifications at
+[http://fedora.info]. 
+
+The full-scale project is divided into three levels,
+corresponding to three major milestones: 
+
+- **Level 1**: Proof of Concept(PoC): a product satisfying a minimum set of requirements for the sole
+purpose of evaluating feasibility and performance and to compare with
+existing alternative Fedora implementation efforts. 
+- **Level 2**: Minimum Viable Product (MVP): a production-ready Fedora implementation that
+satisfies the minimum requirements to interoperate with the current
+Hyrax version (at the time of completion), in addition to a small subset
+of features aimed at significant improvements in the Hydra/Fedora layer.
+The implementation of this level will be further subdivided into
+sub-projects: alpha, beta, release candidate and stable release. The
+main goal for this milestone is to provide a drop-in Fedora replacement
+that offers clear, quantifiable advantages over the Modeshape
+implementation and provides documented tools to migrate data from fedora
+4 in order to gain support from other Hydra and Islandora adopters.
+Slight modification to the client may be tolerable depending on
+stakeholders’ opinion. 
+- **Level 3**: Production-quality implementation. 
+- **Level 4**: Fedora-compliant implementation: a
+repository implementation that aligns with the official Fedora
+specification and passes all expected compatibility tests. This is
+pending the finalization of such specifications. The implementation of
+additional non-core specifications (which may be considered core by AIC
+or other stakeholders) is also to be determined based use cases and on
+the final status of such specification sets. 
+
+## General requirements 
+
+Our Fedora implementation should fulfill the following high-level
+requirements: 
+
+- Scale vertically and horizontally 
+- Support a large volume of data and perform efficient graph traversals
+- Implement a query language guarantee the maximum stability of content
+- Consist of a thinlayer of custom code on top of systems with as many required features as
+possible provided out of the box
+- Transparently store binary files (either in file system or in a dedicated store)
+
+## Scope of this document
+
+This is in no way an exhaustive path to development and less than ever a
+spec sheet. It is mostly a collection of recommended patterns to resolve
+specific issues for building an alternative implementation of Fedora.
+Some of these recommendations may differ from the final implementation.
+
+# Level 1 Recommendations 
+
+## Scope
+
+Level 1 implementation is aimed at
+providing a proof-of-concept (PoC) repository system with a minimal set
+of features. The resulting system should be easily deployable and offer
+most commonly used features and features that present particular
+challenges in the current Fedora implementation. 
+
+The Level 1 development cycle should delivery a system that can be tested by a variety of
+stakeholders with the purpose of gathering feedback to drive further
+development and possible collaboration with other Fedora 4 alternative
+implementation efforts. 
+
+## Compatibility 
+
+The Level 1 LAKEsuperior implementation supports a minimum set of features meeting the following
+requirements: 
+
+- Supports all the principal operations performed by Sufia
+7.2 and, specifically, LAKEshore 
+- Supports all operations performed by Combine
+- If a specific feature is particularly onerous to implement for
+Level 1 but is required by the above clients, it can be “mocked” for
+expediency’s sake based on how critical its use is, and documented as
+incomplete 
+
+## Technology Map
+
+### Graph Store
+
+Blazegraph is the most convenient choice for a PoC.
+
+Pros:
+
+- familiarity of AIC with the software
+- Ease of translation between
+Fedora API and back end
+- Supports transactions (to be verified)
+- Transparent import/export for migration (triples in, triples out)
+- Easy translation of LDP data exchange format (RDF) and query language
+(SPARQL)
+
+Cons:
+
+- Does not store binaries
+- No Python API (must use REST API)
+- No new releases since August 2016 (although development is active)
+- Need to purchase enterprise version for distributed setup
+
+Alternatives can be considered beyond Level 1 if significant hurdles or limitations are
+encountered during this development phase. Therefore, the code should
+use standard triplestore features and be explicitly document the
+implementation-specific features necessary for LAKEsuperior to function.
+
+## Binary Store
+
+Binaries are stored in the filesystem. See below for
+implementation details. 
+
+## Application Layer
+
+The application is written in Python 3 using the Python standard library and mature, well-respected
+3rd party libraries: Flask, Requests, RDFLib, etc. 
+
+Application configuration is stored in separate files in an appropriate structured
+format (e.g. YAML). 
+
+## Methodologies
+
+### Partitioning strategy
+
+Partitioning within a quad-store can be done via named graphs. Each “resource” as
+intended in LDP is contained in a separate named graph. This allows to
+clearly identify such LDP “resources”. 
+
+There are multiple approaches to
+this implementation, mostly differing in the handling of the graph name:
+
+#### 1. Graph name same as subject
+
+~~~
+[prefix definitions]
+res:16fb6c41-b862-4adc-8656-5f9c356b56bb {
+  res:16fb6c41-b862-4adc-8656-5f9c356b56bb a ldp:NonRdfSource ;
+  ebucore:height "1024" ;
+  ebucore:width "1920" ;
+  ebucore:filename "xyz.png" ;
+  premis:hasMessageDigest <urn:sha1:eaa3379d8415071369f3c8b3699fa91fcfc6888c> .
+} 
+~~~
+
+This is the most straightforward implementation and does not impose any additional
+construct on a straight SPARQL query. On the other hand, the repetition
+of the graph name in the subject is redundant and limits further
+strategic use of named graphs.
+
+#### 2. Named graphs as abstractions and proxies
+
+\[LEVEL 2?\]
+
+~~~
+[prefix definitions]
+main {
+  res:a a ltype:Resource ; 
+        fcrepo:hasVersion res:b , res:c ;
+   res:b a ltype:Snapshot .
+   res:c a ltype:Snapshot .
+}
+
+res:a {
+  lake:16fb6c41-b862-4adc-8656-5f9c356b56bb
+    a ldp:Container , aictype:Work ;
+    skos:prefLabel "Composition in Red" ;
+}
+
+res:b {
+  lake:16fb6c41-b862-4adc-8656-5f9c356b56bb
+    a ldp:Container , aictype:Work ;
+    skos:prefLabel "Composition in Reed" ;
+}
+
+res:c {
+  lake:16fb6c41-b862-4adc-8656-5f9c356b56bb
+    a ldp:Container , aictype:Work ;
+    skos:prefLabel "Composition in Read" ;
+} 
+~~~
+
+This other approach has a
+“main” graph holding metadata about the first-class resources: their
+provenance, relationships with other named graphs, etc. Multiple named
+graphs can be different representations of the same resource. This makes
+it easier to build provenance data, such as version snapshots. On the
+other hand it is a more indirect approach, in that LAKEsuperior has to
+find one or more named graphs that represent the resource that is being
+requested. In the example above, the repository application needs to get
+triples from the graph that has the `ltype:Resource` and skip the ones
+from the `ltype:Snapshot` graphs. 
+
+### Graph store to LDP mapping 
+
+This is done by the application layer. This implementation supports all necessary
+building blocks for interacting with ActiveFedora: LDP-NR, LDPC, LDP-DC,
+LDP-IC, etc. 
+
+Resources are stored internally with a prefixed namespace
+that is replaced by the domain-specific URI prefix. No host-specific
+information is stored in the triplestore in order to guarantee
+portability of the data set.
+
+### LDP containers
+
+LAKEsuperior supports all the LDP container types and
+their related behavior, especially with regard to direct and indirect
+containers.
+
+### Deletion strategy 
+
+#### Individual triples 
+
+For individually deleted triples (i.e. property values) versioning can be engaged.
+
+#### Resources 
+
+Allowing the deletion of a resource altogether poses several
+challenges (see also “Referential integrity” below). Non-destructive
+deletion and optional admin-restricted purging is the recommended
+approach. 
+
+Two “soft-delete” approaches are possible: 
+
+1. A resource is marked as “deleted” by a special status, either internally managed or
+transparently communicated to the client. 
+2. A new version of the resource is created with an empty graph. This allow to enforce
+referential integrity and history tracking.
+
+#### Tombstone 
+
+Deleted resources (with either method) should leave a tombstone. If the tombstone is
+deleted, the resource may be deleted permanently (barring referential
+integrity issues). 
+
+A tombstone, while available, may offer method to “resurrect” a deleted resource. 
+
+#### Hierarchies 
+
+Currently several clients rely on the mechanism by which, given `<main:a>` and `<main:a/b>`, if
+`<main:a>` is deleted, `<main:a/b>` is also deleted. A tombstone
+should be left on `<main:a>` and surface if either `<main:a>` or
+`<main:a/b>` is requested, as per current fcrepo4 behavior
+\[VERIFY\]. 
+
+### Explicit definition of LAKE resources
+
+\[DISCUSS utility, extent and pitfalls\] Since any triples can be inserted in a
+triplestore, it may be useful to explicitly identify a LAKEsuperior
+“resource” by adding e.g. a
+`<http://definitions.artic.edu/lake/type#Resource>` RDF type to all the
+resources directly managed and exposed by LAKEsuperior. This RDF type is
+server managed and not exposed in the LDP API. The presence of this type
+for a resource may trigger further validation of server-managed
+properties and other constraints. 
+
+The designated LAKE resource class
+should be included in each named graph for each LAKEsuperior resource
+appearing as a subject. 
+
+While it is not ideal to enforce low-level
+structural rules in a repository, experience has proven that when
+systems become out of sync or external clients behave in an unexpected
+way, a repository without any structural integrity rules becomes more
+easily corrupted and structural issues become harder to identify and
+fix. Therefore it may be valuable to place some basic restrictions for
+resources in the repository so that errors surface early and are more
+likely to be caught and repaired. 
+
+### Bitstream storage 
+
+Files are stored in the filesystem. The filesystem path for each LDP-NR is obtained by a
+root prefix defined in the application configuration and a balanced
+pairtree created from the file SHA1 checksum of the file content. This
+means that identical binaries can be represented by multiple LDP
+resources but are stored under the same file behind the scenes. 
+
+A server-managed triple contains the path to a file for each version.
+
+Sample data set:
+
+~~~
+[prefix definitions]
+
+area:main {
+  res:16fb6c41-b862-4adc-8656-5f9c356b56bb
+    a ldp:NonRdfSource , ltype:Resource ;
+    ebucore:height "1024" ;
+    ebucore:width "1920" ;
+    ebucore:filename "xyz.png" ;
+    premis:hasMessageDigest <urn:sha1:eaa3379d8415071369f3c8b3699fa91fcfc6888c> ;
+    fcr:content "/eaa3/379d8/4150/eaa3379d8415071369f3c8b3699fa91fcfc6888c" ;
+    aic:status res:58f00eca-c398-02f0-f9bb-6b2b6105c0ef .
+}
+~~~
+
+Note that `fcr:content` is redundant since it can be inferred by the
+`premis:hasMessageDigest` URI. 
+
+The binary file content is stored in `<bitstream folder path>/eaa3/379d8/4150/eaa3379d8415071369f3c8b3699fa91fcfc6888c`.
+
+Sample LDP requests and responses: 
+
+~~~
+GET http://lakesuperior.artic.edu/rest/main:16fb6c41-b862-4adc-8656-5f9c356b56bb
+
+[binary data]
+
+GET http://lakesuperior.artic.edu/rest/main:16fb6c41-b862-4adc-8656-5f9c356b56bb/fcr:metadata
+
+[prefix definitions]
+
+<http://lakesuperior.artic.edu/rest/main:16fb6c41-b862-4adc-8656-5f9c356b56bb>
+  a ldp:NonRdfSource ;
+  ebucore:height "1024" ;
+  ebucore:width "1920" ;
+  ebucore:filename "xyz.png" ;
+  premis:hasMessageDigest <urn:sha1:eaa3379d8415071369f3c8b3699fa91fcfc6888c> ;
+  iana:describedBy <http://lakesuperior.artic.edu/rest/main:16fb6c41-b862-4adc-8656-5f9c356b56bb/fcr:metadata>
+  aic:status res:58f00eca-c398-02f0-f9bb-6b2b6105c0ef .
+}
+~~~
+
+### Server-managed properties
+
+Server-managed properties should be supported the same way
+the current Fedora 4.x implementation does, to the extent that allows a
+drop-in replacement as a Samvera client. This includes "magic" LDP
+predicates for direct and indirect containers.
+
+### Internal use only properties
+
+Some server-managed properties are not exposed in the
+client-facing API. These properties have predicates within a dedicated
+namespace. If a client tries to insert a triple including an internal
+use only predicate, the application should return a `409 Conflict`
+response.
+
+Some internal use only predicates are:
+
+- content (reference to the filesystem path of the content of a binary resource)
+- Versions
+- \[…\]
+
+# Level 2 Recommendations
+
+## Scope
+
+The Level 2 implementation builds upon the
+Level 1 proof of concept and, ideally, possible feedback from a variety
+of testers.
+
+The goal for Level 2 is a feature-complete, beta-quality
+product compatible with Hyrax and Islandora (pending the presence of
+Islandora stakeholders)
+
+## Structural restrictions
+
+### Types of restrictions
+
+Structural restrictions can include:
+
+- Cardinality of properties
+- Uniqueness of property values across the repository
+- Domain and range of properties
+
+Enforcing these three types of restrictions may satisfy a
+very broad number of use cases. 
+
+### Enforcing restrictions
+
+Restrictions should be completely optional for implementers. 
+
+The subsystem responsible for enforcing restrictions should be close enough to the
+core repository to ensure that all interaction with the persistence
+layer passes through it; and isolated enough that it can always be
+configured and enabled or disabled separately. 
+
+### Referential integrity
+
+Referential integrity should be ideally enforced within resources which
+share the same domain, i.e. are managed by the same repository.
+
+Referential integrity is hard to maintain if the repository allows
+deletion of versioned resources. Consider two resources, `<a>` and `<b>`: 
+
+1. Client creates a relationship: `<a> ns:rel <b>` 
+2. Client deletes the relationship. This is no longer present in the current version of `<a>`,
+but it is still present in historical versions. 
+3. `<b>` is deleted. The referential integrity is broken in previous versions of `<a>`. 
+4. If `<a>` is restored to its previous version, this breakage surfaces in the main
+resource. 
+
+There are three possible ways to address this: 
+
+1. Not supporting referential integrity at all;
+2. Not supporting deletion of resources and replacing it with a “deleted” status (see above); 3. Only supporting deletion of a resource if this has no inbound links from any
+resource in any version (this can trigger a costly query in a large
+store). 
+
+Actual referential integrity is a better candidate for Level 2
+but some early decisions should be made with this setup in mind.
+
+## Versioning
+
+Previous versions of a resource are stored as sets of triples
+within a separate named graph:
+
+~~~
+PREFIX area: <http://definitions.artic.edu/lake/area#>
+PREFIX res: <http://definitions.artic.edu/lake/resource#> 
+PREFIX ltype: <http://definitions.artic.edu/lake/type#> 
+PREFIX snap: <http://definitions.artic.edu/lake/snapshot#> 
+PREFIX aic: <http://definitions.artic.edu/ontology/1.0/> 
+
+area:main {
+  res:16fb6c41-b862-4adc-8656-5f9c356b56bb
+    a ldp:NonRdfSource , ltype:Resource ;
+    ebucore:height "1024" ;
+    ebucore:width "1920" ;
+    ebucore:filename "xyz.png" ;
+    premis:hasMessageDigest <urn:sha1:eaa3379d8415071369f3c8b3699fa91fcfc6888c> ;
+    aic:status res:58f00eca-c398-02f0-f9bb-6b2b6105c0ef ;
+}
+
+area:historic {
+  res:16fb6c41-b862-4adc-8656-5f9c356b56bb
+    fedora:hasVersion
+      snap:fa297320-4ae1-46c9-8d2b-9356e713489f ,
+      snap:7c96c502-b101-410d-8119-3b690a38a46a .
+
+  snap:fa297320-4ae1-46c9-8d2b-9356e713489f
+    a ldp:NonRdfSource , ltype:Resource ;
+    fedora:hasVersionLabel "Version 2" ;
+    ebucore:height "1018" ;
+    ebucore:width "1318" ;
+    ebucore:filename "xyz_older.png" ;
+    premis:hasMessageDigest <urn:sha1:dae1230ad61117e33f4e338a1648983e1af84377> ;
+    fedora:created "2017-08-22T14:21:14.941Z"^^xsd:\#dateTime ;
+    aic:status res:58f00eca-c398-02f0-f9bb-6b2b6105c0ef .
+
+snap:7c96c502-b101-410d-8119-3b690a38a46a
+   a ldp:NonRdfSource , ltype:Resource ;
+   fedora:hasVersionLabel "Version 1" ;
+   ebucore:height "768" ;
+   ebucore:width "1024" ;
+   ebucore:filename "xyz_oldest.jpg" ;
+   premis:hasMessageDigest <urn:sha1:dae9d4b279aa27cde354e5a0a8e7da07c7560fbc> ;
+   fedora:created "2017-08-22T14:51:46.036Z"^^xsd:\#dateTime ;
+   aic:status res:58f00eca-c398-02f0-f9bb-6b2b6105c0ef . 
+} 
+~~~
+
+Versions are retrieved by a mechanism similar to fcrepo4, with some differences: 
+
+~~~
+GET http://lakesuperior.artic.edu/rest/historic:16fb6c41-b862-4adc-8656-5f9c356b56bb
+
+[prefix definitions]
+
+<http://lakesuperior.artic.edu/rest/historic:16fb6c41-b862-4adc-8656-5f9c356b56bb>
+  fedora:hasVersion
+    <http://lakesuperior.artic.edu/rest/fa297320-4ae1-46c9-8d2b-9356e713489f> ,
+    <http://lakesuperior.artic.edu/rest/7c96c502-b101-410d-8119-3b690a38a46a> .
+
+<http://lakesuperior.artic.edu/rest/historic:fa297320-4ae1-46c9-8d2b-9356e713489f>
+  fedora:created "2017-08-22T14:21:14.941Z"^^xsd:\#dateTime ;
+  fedora:hasVersionLabel "Version 2" .
+
+<http://lakesuperior.artic.edu/rest/historic:7c96c502-b101-410d-8119-3b690a38a46a>
+  fedora:created "2017-08-22T14:51:46.036Z"^^xsd:\#dateTime ;
+  fedora:hasVersionLabel "Version 1" .
+~~~
+
+Most importantly note the lack of
+`fedora:hasVersions` property that points to the `./fcr:versions` resource.
+This is replaced by a resource with the same URI as the main resource,
+within the `historic` namespace. If a client requires the
+`fedora:hasVersions` property for whatever reason, it may be reasonable to
+add it and have it point to the resource in the `historic` area.
+
+## Transactions
+
+Transactions are not supported by LAKEsuperior at the LDP
+API level, at least in Levels 1 and 2, and maybe not in early stable
+releases either. The pitfalls of implementing transactions are too many
+to tackle in early releases. 
+
+However, Level 1 should guarantee that a single client request is always atomic even if this entails multiple interactions with the underlying store. For this reason, any candidate
+backing triplestore must support transactions. 
+
+## Fixity
+
+\[TODO\] Fixity support should be implemented as per current fcrepo4 specs.
+
+# Level 3 Recommendations 
+
+TBD

+ 258 - 0
doc/src/template.latex

@@ -0,0 +1,258 @@
+\documentclass[$if(fontsize)$$fontsize$,$endif$$if(lang)$$babel-lang$,$endif$$if(papersize)$$papersize$paper,$endif$$for(classoption)$$classoption$$sep$,$endfor$]{$documentclass$}
+\usepackage{titlesec}
+\newcommand{\sectionbreak}{\clearpage}
+$if(fontfamily)$
+\usepackage[$for(fontfamilyoptions)$$fontfamilyoptions$$sep$,$endfor$]{$fontfamily$}
+$else$
+\usepackage{lmodern}
+$endif$
+$if(linestretch)$
+\usepackage{setspace}
+\setstretch{$linestretch$}
+$endif$
+\usepackage{amssymb,amsmath}
+\usepackage{ifxetex,ifluatex}
+\usepackage{fixltx2e} % provides \textsubscript
+\ifnum 0\ifxetex 1\fi\ifluatex 1\fi=0 % if pdftex
+  \usepackage[$if(fontenc)$$fontenc$$else$T1$endif$]{fontenc}
+  \usepackage[utf8]{inputenc}
+$if(euro)$
+  \usepackage{eurosym}
+$endif$
+\else % if luatex or xelatex
+  \ifxetex
+    \usepackage{mathspec}
+  \else
+    \usepackage{fontspec}
+  \fi
+  \defaultfontfeatures{Ligatures=TeX,Scale=MatchLowercase}
+$if(euro)$
+  \newcommand{\euro}{€}
+$endif$
+$if(mainfont)$
+    \setmainfont[$for(mainfontoptions)$$mainfontoptions$$sep$,$endfor$]{$mainfont$}
+$endif$
+$if(sansfont)$
+    \setsansfont[$for(sansfontoptions)$$sansfontoptions$$sep$,$endfor$]{$sansfont$}
+$endif$
+$if(monofont)$
+    \setmonofont[Mapping=tex-ansi$if(monofontoptions)$,$for(monofontoptions)$$monofontoptions$$sep$,$endfor$$endif$]{$monofont$}
+$endif$
+$if(mathfont)$
+    \setmathfont(Digits,Latin,Greek)[$for(mathfontoptions)$$mathfontoptions$$sep$,$endfor$]{$mathfont$}
+$endif$
+$if(CJKmainfont)$
+    \usepackage{xeCJK}
+    \setCJKmainfont[$for(CJKoptions)$$CJKoptions$$sep$,$endfor$]{$CJKmainfont$}
+$endif$
+\fi
+% use upquote if available, for straight quotes in verbatim environments
+\IfFileExists{upquote.sty}{\usepackage{upquote}}{}
+% use microtype if available
+\IfFileExists{microtype.sty}{%
+\usepackage{microtype}
+\UseMicrotypeSet[protrusion]{basicmath} % disable protrusion for tt fonts
+}{}
+$if(geometry)$
+\usepackage[$for(geometry)$$geometry$$sep$,$endfor$]{geometry}
+$endif$
+\usepackage{hyperref}
+$if(colorlinks)$
+\PassOptionsToPackage{usenames,dvipsnames}{color} % color is loaded by hyperref
+$endif$
+\hypersetup{unicode=true,
+$if(title-meta)$
+            pdftitle={$title-meta$},
+$endif$
+$if(author-meta)$
+            pdfauthor={$author-meta$},
+$endif$
+$if(keywords)$
+            pdfkeywords={$for(keywords)$$keywords$$sep$; $endfor$},
+$endif$
+$if(colorlinks)$
+            colorlinks=true,
+            linkcolor=$if(linkcolor)$$linkcolor$$else$Maroon$endif$,
+            citecolor=$if(citecolor)$$citecolor$$else$Blue$endif$,
+            urlcolor=$if(urlcolor)$$urlcolor$$else$Blue$endif$,
+$else$
+            pdfborder={0 0 0},
+$endif$
+            breaklinks=true}
+\urlstyle{same}  % don't use monospace font for urls
+$if(lang)$
+\ifnum 0\ifxetex 1\fi\ifluatex 1\fi=0 % if pdftex
+  \usepackage[shorthands=off,$for(babel-otherlangs)$$babel-otherlangs$,$endfor$main=$babel-lang$]{babel}
+$if(babel-newcommands)$
+  $babel-newcommands$
+$endif$
+\else
+  \usepackage{polyglossia}
+  \setmainlanguage[$polyglossia-lang.options$]{$polyglossia-lang.name$}
+$for(polyglossia-otherlangs)$
+  \setotherlanguage[$polyglossia-otherlangs.options$]{$polyglossia-otherlangs.name$}
+$endfor$
+\fi
+$endif$
+$if(natbib)$
+\usepackage{natbib}
+\bibliographystyle{$if(biblio-style)$$biblio-style$$else$plainnat$endif$}
+$endif$
+$if(biblatex)$
+\usepackage$if(biblio-style)$[style=$biblio-style$]$endif${biblatex}
+$if(biblatexoptions)$\ExecuteBibliographyOptions{$for(biblatexoptions)$$biblatexoptions$$sep$,$endfor$}$endif$
+$for(bibliography)$
+\addbibresource{$bibliography$}
+$endfor$
+$endif$
+$if(listings)$
+\usepackage{listings}
+$endif$
+$if(lhs)$
+\lstnewenvironment{code}{\lstset{language=Haskell,basicstyle=\small\ttfamily}}{}
+$endif$
+$if(highlighting-macros)$
+$highlighting-macros$
+$endif$
+$if(verbatim-in-note)$
+\usepackage{fancyvrb}
+\VerbatimFootnotes % allows verbatim text in footnotes
+$endif$
+$if(tables)$
+\usepackage{longtable,booktabs}
+$endif$
+$if(graphics)$
+\usepackage{graphicx,grffile}
+\makeatletter
+\def\maxwidth{\ifdim\Gin@nat@width>\linewidth\linewidth\else\Gin@nat@width\fi}
+\def\maxheight{\ifdim\Gin@nat@height>\textheight\textheight\else\Gin@nat@height\fi}
+\makeatother
+% Scale images if necessary, so that they will not overflow the page
+% margins by default, and it is still possible to overwrite the defaults
+% using explicit options in \includegraphics[width, height, ...]{}
+\setkeys{Gin}{width=\maxwidth,height=\maxheight,keepaspectratio}
+$endif$
+$if(links-as-notes)$
+% Make links footnotes instead of hotlinks:
+\renewcommand{\href}[2]{#2\footnote{\url{#1}}}
+$endif$
+$if(strikeout)$
+\usepackage[normalem]{ulem}
+% avoid problems with \sout in headers with hyperref:
+\pdfstringdefDisableCommands{\renewcommand{\sout}{}}
+$endif$
+$if(indent)$
+$else$
+\IfFileExists{parskip.sty}{%
+\usepackage{parskip}
+}{% else
+\setlength{\parindent}{0pt}
+\setlength{\parskip}{6pt plus 2pt minus 1pt}
+}
+$endif$
+\setlength{\emergencystretch}{3em}  % prevent overfull lines
+\providecommand{\tightlist}{%
+  \setlength{\itemsep}{0pt}\setlength{\parskip}{0pt}}
+$if(numbersections)$
+\setcounter{secnumdepth}{5}
+$else$
+\setcounter{secnumdepth}{0}
+$endif$
+$if(subparagraph)$
+$else$
+% Redefines (sub)paragraphs to behave more like sections
+\ifx\paragraph\undefined\else
+\let\oldparagraph\paragraph
+\renewcommand{\paragraph}[1]{\oldparagraph{#1}\mbox{}}
+\fi
+\ifx\subparagraph\undefined\else
+\let\oldsubparagraph\subparagraph
+\renewcommand{\subparagraph}[1]{\oldsubparagraph{#1}\mbox{}}
+\fi
+$endif$
+$if(dir)$
+\ifxetex
+  % load bidi as late as possible as it modifies e.g. graphicx
+  $if(latex-dir-rtl)$
+  \usepackage[RTLdocument]{bidi}
+  $else$
+  \usepackage{bidi}
+  $endif$
+\fi
+\ifnum 0\ifxetex 1\fi\ifluatex 1\fi=0 % if pdftex
+  \TeXXeTstate=1
+  \newcommand{\RL}[1]{\beginR #1\endR}
+  \newcommand{\LR}[1]{\beginL #1\endL}
+  \newenvironment{RTL}{\beginR}{\endR}
+  \newenvironment{LTR}{\beginL}{\endL}
+\fi
+$endif$
+$for(header-includes)$
+$header-includes$
+$endfor$
+
+$if(title)$
+\title{$title$$if(thanks)$\thanks{$thanks$}$endif$}
+$endif$
+$if(subtitle)$
+\providecommand{\subtitle}[1]{}
+\subtitle{$subtitle$}
+$endif$
+$if(author)$
+\author{$for(author)$$author$$sep$ \and $endfor$}
+$endif$
+\date{$date$}
+
+\begin{document}
+$if(title)$
+\maketitle
+$endif$
+$if(abstract)$
+\begin{abstract}
+$abstract$
+\end{abstract}
+$endif$
+
+$for(include-before)$
+$include-before$
+
+$endfor$
+$if(toc)$
+{
+$if(colorlinks)$
+\hypersetup{linkcolor=$if(toccolor)$$toccolor$$else$black$endif$}
+$endif$
+\setcounter{tocdepth}{$toc-depth$}
+\tableofcontents
+}
+$endif$
+$if(lot)$
+\listoftables
+$endif$
+$if(lof)$
+\listoffigures
+$endif$
+$body$
+
+$if(natbib)$
+$if(bibliography)$
+$if(biblio-title)$
+$if(book-class)$
+\renewcommand\bibname{$biblio-title$}
+$else$
+\renewcommand\refname{$biblio-title$}
+$endif$
+$endif$
+\bibliography{$for(bibliography)$$bibliography$$sep$,$endfor$}
+
+$endif$
+$endif$
+$if(biblatex)$
+\printbibliography$if(biblio-title)$[title=$biblio-title$]$endif$
+
+$endif$
+$for(include-after)$
+$include-after$
+
+$endfor$
+\end{document}

+ 5 - 0
etc.skeleton/README

@@ -0,0 +1,5 @@
+User-defined configuration goes here. Note that this folder is a
+version-controlled template and its contents should not be directly changed.
+Please copy this folder in a safe location outside source control and set the
+FCREPO_CONFIG_DIR environment variable in your web server/reverse proxy to
+point to that folder.

+ 64 - 0
etc.skeleton/application.yml

@@ -0,0 +1,64 @@
+# Configuration for repository.
+
+graph:
+    # Name of the "main" graph containing metadata about all resources.
+    #
+    # Default: main
+    main_graph: main
+
+# Configuration for binary path and fixity check generation. The hash is a
+# checksumn of the contents of the file.
+uuid:
+    # Algorithm used to calculate the hash that generates the content path.
+    # One of: sha1, sha224, sha256, sha384, or sha512, corresponding to the
+    # omonymous hashlib function.
+    #
+    # Default: sha1.
+    algo: sha1
+
+
+# Data store configuration.
+store:
+    # The semantic store used for persisting LDP-RS (RDF Source) resources.
+    # MUST support SPARQL 1.1 query and update.
+    ldp_rs:
+        webroot: http://localhost:9999/namespace/fcrepo/
+        query_ep: sparql
+        update_ep: sparql
+        default_graph: http://www.w3.org/ns/sparql-service-description#
+        # Optional
+        username: <set me>
+        password: <set me>
+        ssl_verify: false
+
+    # The path used to persist LDP-NR (bitstreams).
+    # This is for now a POSIX filesystem. Other solutions such as HDFS may be
+    # possible in the future.
+    ldp_nr:
+        # The filesystem path to the root of the binary store.
+        path: /data/fcrepo/ldpnr_store
+
+        # How to split the balanced pairtree to generate a path. The hash
+        # string is defined by the uuid.algo parameter value.
+        # This parameter defines how many characters are in each branch. 2-4 is
+        # the recommended setting. NOTE: a value of 2 will generate up to 256
+        # sub-folders in a folder; 3 will generate max. 4096 and 4 will
+        # generate max. 65536. Check your filesystem capabilities before
+        # setting this to a non-default value.
+        #
+        # Default: 2
+        pairtree_branch_length: 2
+
+        # Max. number of branches to generate. 0 will split the string until
+        # it reaches the end.
+        # E.g. if the hash value is 01234567-89ab-cdef-0123-4565789abcdef
+        # (dashes added for readability), and the branch length value is 2, and
+        # the branch number is 4, the path will be 
+        # 01/23/45/67/89abcdef01234565789abcdef. For a value of 0 it will be
+        # 01/23/45/67/89/ab/cd/ef/01/23/45/67/89/ab/cd/ef. Check your system
+        # capabilities for maximum nested directories before setting this to 0,
+        # especially with longer hash algorithms.
+        #
+        # Default: 4
+        pairtree_branches: 4
+

+ 6 - 0
etc.skeleton/flask.yml

@@ -0,0 +1,6 @@
+# Set to 1 in development.
+DEBUG: 1
+# Generate key with:
+# >>> import os
+# >>> os.urandom(24)
+SECRET_KEY: <set me>

+ 18 - 0
etc.skeleton/namespaces.yml

@@ -0,0 +1,18 @@
+# Place CUSTOM namespace prefix definitions here. Internal FCREPO definitions
+# are defined in the core application configuration and will OVERRIDE duplicate
+# prefixes defined here.
+
+aic : http://definitions.artic.edu/ontology/1.0/
+aicpc : http://definitions.artic.edu/publish_channel/
+aictype : http://definitions.artic.edu/ontology/1.0/type/
+dctype : http://purl.org/dc/dcmitype/
+exif : http://www.w3.org/2003/12/exif/ns#
+foaf : http://xmlns.com/foaf/0.1/
+geo : http://www.w3.org/2003/01/geo/wgs84_pos#
+hw : http://projecthydra.org/works/models#
+oa : http://www.w3.org/ns/oa#
+ore : http://www.openarchives.org/ore/terms/
+pcdm : http://pcdm.org/models#
+rel : http://id.loc.gov/vocabulary/relators/
+skos : http://www.w3.org/2004/02/skos/core
+

+ 2 - 0
lakesuperior/config/README

@@ -0,0 +1,2 @@
+This directory is for internal FCREPO configuration. It is version-controlled
+and should not be changed by users.

+ 46 - 0
lakesuperior/config/namespaces.py

@@ -0,0 +1,46 @@
+import rdflib
+
+from rdflib import Graph
+from rdflib.namespace import Namespace, NamespaceManager
+
+from lakesuperior.configparser import config
+
+# Core namespace prefixes. These add to and override any user-defined prefixes.
+# @TODO Some of these have been copy-pasted from FCREPO4 and may be deprecated.
+core_namespaces = {
+    'authz' : Namespace('http://fedora.info/definitions/v4/authorization#'),
+    'cnt' : Namespace('http://www.w3.org/2011/content#'),
+    'dc' : rdflib.namespace.DC,
+    'dcterms' : namespace.DCTERMS,
+    'ebucore' : Namespace('http://www.ebu.ch/metadata/ontologies/ebucore/ebucore#'),
+    'fedora' : Namespace('http://fedora.info/definitions/v4/repository#'),
+    'fedoraconfig' : Namespace('http://fedora.info/definitions/v4/config#'), # fcrepo =< 4.7
+    'gen' : Namespace('http://www.w3.org/2006/gen/ont#'),
+    'iana' : Namespace('http://www.iana.org/assignments/relation/'),
+    'ldp' : Namespace('http://www.w3.org/ns/ldp#'),
+    'owl' : rdflib.namespace.OWL,
+    'premis' : Namespace('http://www.loc.gov/premis/rdf/v1#'),
+    'rdf' : rdflib.namespace.RDF,
+    'rdfs' : rdflib.namespace.RDFS,
+    'res' : Namespace('http://definitions.artic.edu/lake/resource#'),
+    'snap' : Namespace('http://definitions.artic.edu/lake/snapshot#'),
+    'webac' : Namespace('http://www.w3.org/ns/auth/acl#'),
+    'xml' : Namespace('http://www.w3.org/XML/1998/namespace'),
+    'xsd' : rdflib.namespace.XSD,
+    'xsi' : Namespace('http://www.w3.org/2001/XMLSchema-instance'),
+}
+
+ns_collection = config['namespaces'][:]
+ns_collection.update(core_namespaces)
+
+ns_mgr = NamespaceManager(Graph())
+ns_pfx_sparql = dict()
+
+# Collection of prefixes in a dict.
+for ns,uri in ns_collection.items():
+    ns_mgr.bind(ns, uri, override=False)
+    #ns_pfx_sparql[ns] = 'PREFIX {}: <{}>'.format(ns, uri)
+
+# Prefix declarations formatted for SPARQL queries.
+#pfx_decl='\n'.join(ns_pfx_sparql.values())
+

+ 25 - 0
lakesuperior/config_parser.py

@@ -0,0 +1,25 @@
+import os
+
+import yaml
+
+configs = [
+    'application',
+    'namespaces',
+    'flask',
+]
+
+# This will hold a dict of all configuration values.
+config = {}
+
+# Parse configuration
+if 'FCREPO_CONFIG_DIR' in os.environ:
+    CONFIG_DIR = os.environ['FCREPO_CONFIG_DIR']
+else:
+    CONFIG_DIR = os.path.dirname(os.path.abspath(__file__)) + '/etc'
+
+for cname in configs:
+    file = '{}/{}.yml'.format(CONFIG_DIR , cname)
+    with open(file, 'r') as stream:
+        config[cname] = yaml.load(stream, yaml.SafeLoader)
+
+

+ 4 - 0
lakesuperior/connectors/filesystem_connector.py

@@ -0,0 +1,4 @@
+import logging
+
+class FilesystemConnector:
+

+ 70 - 0
lakesuperior/connectors/graph_store_connector.py

@@ -0,0 +1,70 @@
+import logging
+
+from rdflib import Dataset
+from rdflib.plugins.sparql import prepareQuery
+from rdflib.plugins.stores.sparqlstore import SPARQLUpdateStore
+
+from lakesuperior.config_parser import config
+
+class GraphStoreConnector:
+    '''Connector for LDP-RS (RDF Source) resources. Connects to a
+    triplestore.'''
+
+    _conf = config['application']['store']['ldp_rs']
+    _logger = logging.getLogger(__module__)
+
+
+    ## MAGIC METHODS ##
+
+    def __init__(self, method=POST):
+        '''Initialize the graph store.
+
+        @param method (string) HTTP method to use for the query. POST is the
+        default and recommended value since it places virtually no limitation
+        on the query string length.
+
+        NOTE: `rdflib.Dataset` requires a RDF 1.1 compliant store with support
+        for Graph Store HTTP protocol
+        (https://www.w3.org/TR/sparql11-http-rdf-update/). This may not be
+        viable with the current version of Blazegraph. It would with Fuseki,
+        but other considerations would have to be made (e.g. Jena has no REST
+        API for handling transactions).
+        '''
+
+        self._store = SPARQLUpdateStore(queryEnpdpoint=self._conf['query_ep'],
+                update_endpoint=self._conf['update_ep'])
+        try:
+            self._store.open(self._conf['base_url'])
+        except:
+            raise RuntimeError('Error opening remote graph store.')
+        self.dataset = Dataset(self._store)
+
+
+    def __del__(self):
+        '''Commit pending transactions and close connection.'''
+        self._store.close(True)
+
+
+    ## PUBLIC METHODS ##
+
+    def query(self, q, initNs=None, initBindings=None):
+        '''Query the triplestore.
+
+        @param q (string) SPARQL query.
+
+        @return rdflib.query.Result
+        '''
+        self._logger.debug('Querying SPARQL endpoint: {}'.format(q))
+        return self.dataset.query(q, initNs=initNs or nsc,
+                initBindings=initBindings)
+
+
+    def find_by_type(self, type):
+        '''Find all resources by RDF type.
+
+        @param type (rdflib.term.URIRef) RDF type to query.
+        '''
+        return self.query('SELECT ?s {{?s a {} . }}'.format(type.n3()))
+
+
+

+ 12 - 0
lakesuperior/ldp/resource.py

@@ -0,0 +1,12 @@
+from lakesuperior.connectors.graph_store_connector import GraphStoreConnector
+
+class Resource:
+    '''Model for LDP resources.'''
+
+    def __init__(self):
+        self.ds = GraphStoreConnector()
+
+    def get(uuid):
+        g = ds
+
+

+ 39 - 0
server.py

@@ -0,0 +1,39 @@
+import io
+import json
+import os.path
+
+from flask import Flask
+
+from lakesuperior.config_parser import config
+from lakesuperior.ldp.resource import Resource
+
+app = Flask(__name__)
+app.config.update(config['flask'])
+
+@app.route('/', methods=['GET'])
+def index():
+    '''Homepage'''
+    return 'Hello. This is LAKEsuperior.'
+
+
+@app.route('/<uuid>', methods=['GET'])
+def get_resource():
+    '''Add a new resource in a new URI.'''
+    rsrc = Resource.get(uuid)
+    return rsrc.path
+
+
+@app.route('/<path>', methods=['POST'])
+def post_resource():
+    '''Add a new resource in a new URI.'''
+    rsrc = Resource.post(path)
+    return rsrc.path
+
+
+@app.route('/<path>', methods=['PUT'])
+def put_resource():
+    '''Add a new resource in a new URI.'''
+    rsrc = Resource.put(path)
+    return rsrc.path
+
+

+ 8 - 0
toolbox/README

@@ -0,0 +1,8 @@
+Place various utility scripts here, for example for: bootstrap, migration,
+cleanup, recovery, etc.
+
+This directory can be added to a system user's PATH and used for cron jobs
+where relevant.
+
+While Python is preferred, other languages such as Bash and Perl are possible
+if some functions are best performed with those.

+ 8 - 0
toolbox/bootstrap.py

@@ -0,0 +1,8 @@
+#!/usr/bin/env python
+
+# This script will parse configuration files and initialize a filesystem and
+# triplestore with an empty FCREPO repository.
+#
+# Additional, scaffolding files may be parsed to create initial contents.
+
+# @TODO

+ 9 - 0
toolbox/mkdoc.sh

@@ -0,0 +1,9 @@
+#!/bin/bash
+
+dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )/../doc"
+for i in "${dir}/src/*.md"; do
+    #basename="${i##*/}"
+    basename=$(basename "${i}")
+    fname="${basename%%.*}" # Filename without ext
+    pandoc --toc --template "${dir}/src/template.latex" "${i}" -o "${dir}/pdf/${fname}.pdf"
+done