diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..af2f537 --- /dev/null +++ b/.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/ diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..8dada3e --- /dev/null +++ b/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. diff --git a/README.md b/README.md new file mode 100644 index 0000000..eb7638d --- /dev/null +++ b/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. diff --git a/doc/pdf/lakesuperior_recommendations.pdf b/doc/pdf/lakesuperior_recommendations.pdf new file mode 100644 index 0000000..f133fd3 Binary files /dev/null and b/doc/pdf/lakesuperior_recommendations.pdf differ diff --git a/doc/src/lakesuperior_recommendations.md b/doc/src/lakesuperior_recommendations.md new file mode 100644 index 0000000..22452cd --- /dev/null +++ b/doc/src/lakesuperior_recommendations.md @@ -0,0 +1,522 @@ +--- +title: LAKEsuperior Recommendations +author: "Stefano Cossu, the Art Institute of Chicago + Kevin Ford, the Art Institute of Chicago, " +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 . +} +~~~ + +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 `` and ``, if +`` is deleted, `` is also deleted. A tombstone +should be left on `` and surface if either `` or +`` 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 +`` 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 ; + 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 `/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] + + + a ldp:NonRdfSource ; + ebucore:height "1024" ; + ebucore:width "1920" ; + ebucore:filename "xyz.png" ; + premis:hasMessageDigest ; + iana:describedBy + 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, `` and ``: + +1. Client creates a relationship: ` ns:rel ` +2. Client deletes the relationship. This is no longer present in the current version of ``, +but it is still present in historical versions. +3. `` is deleted. The referential integrity is broken in previous versions of ``. +4. If `` 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: +PREFIX res: +PREFIX ltype: +PREFIX snap: +PREFIX aic: + +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 ; + 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 ; + 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 ; + 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] + + + fedora:hasVersion + , + . + + + fedora:created "2017-08-22T14:21:14.941Z"^^xsd:\#dateTime ; + fedora:hasVersionLabel "Version 2" . + + + 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 \ No newline at end of file diff --git a/doc/src/template.latex b/doc/src/template.latex new file mode 100644 index 0000000..7f4bbf4 --- /dev/null +++ b/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} diff --git a/etc.skeleton/README b/etc.skeleton/README new file mode 100644 index 0000000..8c174de --- /dev/null +++ b/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. diff --git a/etc.skeleton/application.yml b/etc.skeleton/application.yml new file mode 100644 index 0000000..eea9bb4 --- /dev/null +++ b/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: + password: + 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 + diff --git a/etc.skeleton/flask.yml b/etc.skeleton/flask.yml new file mode 100644 index 0000000..bbbb8e7 --- /dev/null +++ b/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: diff --git a/etc.skeleton/namespaces.yml b/etc.skeleton/namespaces.yml new file mode 100644 index 0000000..7b5753b --- /dev/null +++ b/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 + diff --git a/lakesuperior/config/README b/lakesuperior/config/README new file mode 100644 index 0000000..a35b2c3 --- /dev/null +++ b/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. diff --git a/lakesuperior/config/namespaces.py b/lakesuperior/config/namespaces.py new file mode 100644 index 0000000..74334ff --- /dev/null +++ b/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()) + diff --git a/lakesuperior/config_parser.py b/lakesuperior/config_parser.py new file mode 100644 index 0000000..3d13171 --- /dev/null +++ b/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) + + diff --git a/lakesuperior/connectors/filesystem_connector.py b/lakesuperior/connectors/filesystem_connector.py new file mode 100644 index 0000000..72c4bd9 --- /dev/null +++ b/lakesuperior/connectors/filesystem_connector.py @@ -0,0 +1,4 @@ +import logging + +class FilesystemConnector: + diff --git a/lakesuperior/connectors/graph_store_connector.py b/lakesuperior/connectors/graph_store_connector.py new file mode 100644 index 0000000..ae623ee --- /dev/null +++ b/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())) + + + diff --git a/lakesuperior/ldp/resource.py b/lakesuperior/ldp/resource.py new file mode 100644 index 0000000..b275e05 --- /dev/null +++ b/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 + + diff --git a/server.py b/server.py new file mode 100644 index 0000000..d1c3f2e --- /dev/null +++ b/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('/', methods=['GET']) +def get_resource(): + '''Add a new resource in a new URI.''' + rsrc = Resource.get(uuid) + return rsrc.path + + +@app.route('/', methods=['POST']) +def post_resource(): + '''Add a new resource in a new URI.''' + rsrc = Resource.post(path) + return rsrc.path + + +@app.route('/', methods=['PUT']) +def put_resource(): + '''Add a new resource in a new URI.''' + rsrc = Resource.put(path) + return rsrc.path + + diff --git a/toolbox/README b/toolbox/README new file mode 100644 index 0000000..53f05f2 --- /dev/null +++ b/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. diff --git a/toolbox/bootstrap.py b/toolbox/bootstrap.py new file mode 100755 index 0000000..ceda2a7 --- /dev/null +++ b/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 diff --git a/toolbox/mkdoc.sh b/toolbox/mkdoc.sh new file mode 100755 index 0000000..d2f6e5e --- /dev/null +++ b/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