diff --git a/LICENCE.txt b/LICENCE.txt new file mode 100644 index 0000000..54fec5d --- /dev/null +++ b/LICENCE.txt @@ -0,0 +1,23 @@ +The MIT License + +Copyright (c) 2012, University of Oxford + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + + diff --git a/README.txt b/README.txt index bc4ad13..f0f52bc 100644 --- a/README.txt +++ b/README.txt @@ -8,102 +8,11 @@ RDF-enhanced storage API, pairtree-backed. Installation and dependancies ============================= -- [I would advise using virtualenv] +The dependancies are listed in the file docs/Dependancies.txt +The installation instructions are available at docs/Databank_VM_Installation.txt -Dependancies: +For further infromation visit http://www.dataflow.ox.ac.uk/ +The project wiki is available at http://dataflow-jira.bodleian.ox.ac.uk/jira/ or +https://damssupport.ouls.ox.ac.uk/trac/databank/wiki/ -pylons==0.9.7 -pairtree==0.5.6-T -rdfobject -recordsilo -simplejson -solrpy -redis (use the redis client that is compatible with your redis server - http://github.com/andymccurdy/redis-py/) - - -- Change the settings in development.ini to suit your uses, specifically: - -redis.host = localhost - -granary.store = silos # Directory to storage directory. Can be absolute: /var/silos -granary.uri_root = http://databank.bodleian.ox.ac.uk/objects/ # URI root - must end with /, # or : - -broadcast.to = redis -broadcast.queue = silochanges # redis 'queue' to push audit messages to (create, delete, update or embargo change) - -solr.host = http://localhost:8983/solr # Unset this to ignore solr while testing - -Setup: -====== - -Create a file 'passwd' in the root directory of the application using 'htpasswd' or similar: - -$ htpasswd -c passwd admin -[enter admin password] - -Add any users you wish to access or work with this application. - -Edit ./rdfdatabank/lib/ident_md.py - this has a variable _DATA which would best be replaced by a DB lookup or similar. For now, adjust it -to suit your users - the important thing is the 'role'. - -Start up the redis-server and confirm that you can access it. - -You should be able to start the application now (as long as the application has access to r+w to the aforementioned 'silos' directory.) - -paster serve development.ini - -Then, go to localhost:5000/admin, and try to log in as the 'admin' account. You should then be able to create and manage silos and assign write-access to them to users. - -WGSI deployment: -================ - -Copy 'development.ini' to a file called 'production.ini' and make sure debug = false and that the 'silos' directory is -owned by the web-server user (www-data for example). Recommendation is to have a separate silo directory for production use. - -Create a folder called 'egg-cache' and make sure that is writable by the web-server user too. - -Create a folder 'mod_wsgi' in the root of the application. - -Create a file within that called 'dispatch.wsgi' and put in the following: - ---------------- -# Add the virtual Python environment site-packages directory to the path - -import pkg_resources -pkg_resources.working_set.add_entry('/opt/rdfdatabank/src') # Path to application root - -# Avoid ``[Errno 13] Permission denied: '/var/www/.python-eggs'`` messages -import os -os.environ['PYTHON_EGG_CACHE'] = '/opt/rdfdatabank/src/egg-cache' # Path to writable directory for egg-cache - -# Load the Pylons application -from paste.deploy import loadapp -application = loadapp('config:/opt/rdfdatabank/src/production.ini') # Path to production.ini file - ---------------- - -In your apache2 configuration, you need an entry like: - - - ServerName foo.com - - # Logfiles - ErrorLog /opt/rdfdatabank/apachelog/error.log - CustomLog /opt/rdfdatabank/apachelog/access.log combined - - # Use only 1 Python sub-interpreter. Multiple sub-interpreters - # play badly with C extensions. - WSGIApplicationGroup %{GLOBAL} - WSGIPassAuthorization On - # Setup mod_wsgi - WSGIScriptAlias / /opt/rdfdatabank/src/mod_wsgi/dispatch.wsgi - - - Order deny,allow - Allow from all - - - - -Note the WSGIScriptAlias points to the dispatch.wsgi file. diff --git a/add_user.py b/add_user.py new file mode 100644 index 0000000..49f090b --- /dev/null +++ b/add_user.py @@ -0,0 +1,34 @@ +import sqlalchemy as sa +from rdfdatabank.model import init_model +from rdfdatabank.lib.auth_entry import add_user, add_user_groups +import ConfigParser +import sys, os + +if __name__ == "__main__": + #Initialize sqlalchemy + c = ConfigParser.ConfigParser() + f = '/var/lib/databank/production.ini' + c.read(f) + if not os.path.exists(f): + print "Config file not found" + elif not 'app:main' in c.sections(): + print "Section app:main not found in config file" + else: + engine = sa.create_engine(c.get('app:main', 'sqlalchemy.url')) + init_model(engine) + #add user + username = sys.argv[1] + password = sys.argv[2] + email = sys.argv[3] + user_details = { + 'username':u'%s'%username, + 'password':u"%s"%password, + 'name':u'Databank Administrator', + 'email':u"%s"%email + } + add_user(user_details) + #Add user membership + groups = [] + groups.append(('*', 'administrator')) + add_user_groups(username, groups) + diff --git a/debian/README b/debian/README new file mode 100644 index 0000000..aa4f8e6 --- /dev/null +++ b/debian/README @@ -0,0 +1,6 @@ +The Debian Package databank +---------------------------- + +Comments regarding the Package + + -- Anusha Ranganathan Thu, 03 May 2012 09:08:50 +0000 diff --git a/debian/changelog b/debian/changelog new file mode 100644 index 0000000..ed1d921 --- /dev/null +++ b/debian/changelog @@ -0,0 +1,43 @@ +databank (0.3rc3) unstable; urgency=low + + * Mod-wsgi and metadata update fixes to Sword server + * Minor user interface changes + + -- Anusha Ranganathan Thu, 03 May 2012 09:08:50 +0000 + +databank (0.3rc2) unstable; urgency=low + + * Extended API to post a file without first having to create a data package + * New Databank authentication and authorization module. + - Designed to work with repoze.what and sqlalchemy. + - User and membership information stored in a mysql database + * Databank API extended to administer silos, users, and user membership + - Databank ui has new adminstration pages in line with api functionality + * Sword server packaged with databank + + -- Anusha Ranganathan Fri, 27 Apr 2012 09:08:50 +0000 + +databank (0.3rc1) unstable; urgency=low + + * Basic support for sword deposit + * Databank search interface enhanced + * Added function to PUT a zipfile and unpack it + * content negotiation also on error messages + * Added message workers for data indexing + + -- Anusha Ranganathan Mon, 09 Apr 2012 17:08:50 +0000 + + +databank (0.2) unstable; urgency=low + + * Added message workers + * Databank root set to /var/lib/databank + + -- Anusha Ranganathan Fri, 09 Mar 2012 17:08:50 +0000 + + +databank (0.1) unstable; urgency=low + + * Initial Release. + + -- Anusha Ranganathan Tue, 21 Feb 2012 17:08:50 +0000 diff --git a/debian/compat b/debian/compat new file mode 100644 index 0000000..7f8f011 --- /dev/null +++ b/debian/compat @@ -0,0 +1 @@ +7 diff --git a/debian/config b/debian/config new file mode 100644 index 0000000..16c2db9 --- /dev/null +++ b/debian/config @@ -0,0 +1,43 @@ +#!/bin/sh -e + +# Source debconf library. +. /usr/share/debconf/confmodule + +# Admin email? +db_input critical databank/email || true +db_go + +# SMTP server? +db_input high databank/smtpserver || true +db_go + +# Storgae area? +db_input high databank/store || true +db_go + +# Databank URI? +db_input critical databank/uri || true +db_go + +#Configure SOLR +db_input high databank/confsolr || true +db_go + +#Note on configuring SOLR +#db_input high databank/nosolrconf || true +#db_go + +# Password? +db_input critical databank/password || true +db_go + +# Note on Password +#db_input critical databank/passwdnote || true +#db_go + +# Set default database name, database user +if [ -f /usr/share/dbconfig-common/dpkg/config ]; then + . /usr/share/dbconfig-common/dpkg/config.mysql + dbc_go databank $@ +fi + diff --git a/debian/control b/debian/control new file mode 100644 index 0000000..12f5a44 --- /dev/null +++ b/debian/control @@ -0,0 +1,44 @@ +Source: databank +Section: web +Priority: extra +Maintainer: Anusha Ranganathan +Build-Depends: debhelper (>= 7) +Standards-Version: 3.9.2 +Vcs-Git: git://github.com/dataflow/RDFDatabank.git +Homepage: https://github.com/dataflow/RDFDatabank +Uploaders: Anusha Ranganathan +XS-Python-Version: >= 2.6 + +Package: databank +Architecture: all +Depends: python, + python-dev, + python-setuptools, + python-virtualenv, + python-rdflib, + python-dateutil, + python-pairtree, + python-recordsilo, + python-solrpy, + python-redis, + mysql-server, + libmysql++-dev, + git-core, + unzip, + libxml2, + libxml2-dev, + libxslt1.1, + libxslt-dev, + apache2, + apache2-utils, + libapache2-mod-wsgi, + redis-server, + supervisor, + openjdk-6-jre, + debconf, + dbconfig-common +Recommends: solr-tomcat, + mysql-client +Description: RDF-enhanced, pairtree-backed storage API + Databank is a simple, RDF-enhanced storage API which is backed by pairtree, + for the safe storage of and access to data. diff --git a/debian/copyright b/debian/copyright new file mode 100644 index 0000000..0aed2ee --- /dev/null +++ b/debian/copyright @@ -0,0 +1,42 @@ +This work was packaged for Debian by: + + Anusha Ranganathan on Thu, 03 May 2012 09:08:50 +0000 + +It was downloaded from: + + https://github.com/dataflow/RDFDatabank + +Upstream Author(s): + + Anusha Ranganathan + Ben O'Steen + +Copyright: + + Copyright (C) 2012 University of Oxford + +License: + + MIT + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. + +The Debian packaging is: + + Copyright (C) 2012 University of Oxford + and is licensed under the MIT License. See above. diff --git a/debian/databank.install b/debian/databank.install new file mode 100644 index 0000000..f6abaf3 --- /dev/null +++ b/debian/databank.install @@ -0,0 +1,18 @@ +docs/apache_config/databank_wsgi /etc/default/databank/ +docs/apache_config/databank_ve26_wsgi /etc/default/databank/ +docs/apache_config/databank_ve27_wsgi /etc/default/databank/ +docs/solr_config/conf/schema.xml /etc/default/databank/ +production.ini /etc/default/databank/ +development.ini /etc/default/databank/ +sss.conf.json /etc/default/databank/ +MANIFEST.in /var/lib/databank/ +mod_wsgi /var/lib/databank/ +message_workers/ /var/lib/databank/ +rdfdatabank/ /var/lib/databank/ +rdfdatabank.egg-info/ /var/lib/databank +test.ini /var/lib/databank/ +who.ini /var/lib/databank/ +sss/ /var/lib/databank/ +add_user.py /var/lib/databank/ +setup_db.py /var/lib/databank/ +persisted_state.json /var/lib/databank/ diff --git a/debian/dirs b/debian/dirs new file mode 100644 index 0000000..8d0a38b --- /dev/null +++ b/debian/dirs @@ -0,0 +1,4 @@ +/usr/share/databank +usr/share/dbconfig-common/data/databank/install/mysql +usr/share/dbconfig-common/data/databank/upgrade/mysql +/etc/default/databank diff --git a/debian/docs b/debian/docs new file mode 100644 index 0000000..6bfb954 --- /dev/null +++ b/debian/docs @@ -0,0 +1,7 @@ +LICENCE.txt +README.txt +requirements.txt +docs/assigning_dois +docs/Databank_VM_Installation.txt +docs/Dependencies.txt +docs/using_databank_api diff --git a/debian/postinst b/debian/postinst new file mode 100644 index 0000000..cc5cb3a --- /dev/null +++ b/debian/postinst @@ -0,0 +1,216 @@ +#!/bin/sh +# postinst script for databank +# +# see: dh_installdeb(1) + +set -e + +# Source debconf library. +. /usr/share/debconf/confmodule +. /usr/share/dbconfig-common/dpkg/postinst.mysql + +paramfile=/etc/default/databank/db.sh +dbc_generate_include=sh:$paramfile +dbc_go databank $@ + +# Ask questions +db_get databank/email +local dbemail="$RET" +#echo "Email id is:" $dbemail + +db_get databank/smtpserver +local dbsmtp="$RET" + +db_get databank/store +local dbstore="$RET" + +db_get databank/uri +local dburi="$RET" + +db_get databank/password +local dbpasswd="$RET" + +#Modify the ini files +CONFIG_FILE1=/etc/default/databank/production.ini +CONFIG_FILE2=/etc/default/databank/development.ini +CONFIG_FILE3=/etc/default/databank/sss.conf.json +#replace email +TARGET_KEY=email_to +echo "Setting email Id: " $dbemail +#Sed syntax: sed s/a/b/ or sed s,a,b, +sed -i "s/\($TARGET_KEY *= *\).*/\1$dbemail/" $CONFIG_FILE1 +sed -i "s/\($TARGET_KEY *= *\).*/\1$dbemail/" $CONFIG_FILE2 +#replace smtp server +echo "Setting SMTP Server: " $dbsmtp +TARGET_KEY=smtp_server +sed -i "s,\($TARGET_KEY *= *\).*,\1$dbsmtp," $CONFIG_FILE1 +sed -i "s,\($TARGET_KEY *= *\).*,\1$dbsmtp," $CONFIG_FILE2 +#replace granary store +echo "Setting Data store: " $dbstore +TARGET_KEY=granary.store +sed -i "s,\($TARGET_KEY *= *\).*,\1$dbstore," $CONFIG_FILE1 +sed -i "s,\($TARGET_KEY *= *\).*,\1$dbstore," $CONFIG_FILE2 +#replace granary uri root +echo "Setting Base URI: " $dburi +TARGET_KEY=granary.uri_root +#[ $dburi != "*/" ] && dburi="$dburi""/" +dburi=${dburi%/} +dburi="$dburi""/" +sed -i "s,\($TARGET_KEY *= *\).*,\1$dburi," $CONFIG_FILE1 +sed -i "s,\($TARGET_KEY *= *\).*,\1$dburi," $CONFIG_FILE2 +TARGET_KEY1=' "base_url"' +TARGET_KEY2=' "db_base_url"' +val1='"'$dburi'swordv2/",' +val2='"'$dburi'",' +sed -i "s|\($TARGET_KEY1 *: *\).*|\1$val1|" $CONFIG_FILE3 +sed -i "s|\($TARGET_KEY2 *: *\).*|\1$val2|" $CONFIG_FILE3 +#replace the database connection string +TARGET_KEY=sqlalchemy.url +echo "Setting Database connection string: " +chmod 744 $paramfile +. $paramfile +dburl=mysql://$dbuser:$dbpass@localhost:3306/$dbname +sed -i "s^\($TARGET_KEY *= *\).*^\1$dburl^" $CONFIG_FILE1 +sed -i "s^\($TARGET_KEY *= *\).*^\1$dburl^" $CONFIG_FILE2 + +#Link config files +ln -sf /etc/default/databank/production.ini /var/lib/databank/production.ini +ln -sf /etc/default/databank/development.ini /var/lib/databank/development.ini +ln -sf /etc/default/databank/sss.conf.json /var/lib/databank/sss.conf.json +ln -sf /etc/default/databank/databank_wsgi /etc/apache2/sites-available/databank_wsgi +ln -sf /etc/default/databank/databank_ve26_wsgi /etc/apache2/sites-available/databank_ve26_wsgi +ln -sf /etc/default/databank/databank_ve27_wsgi /etc/apache2/sites-available/databank_ve27_wsgi + +#Create dirs and change ownership and permissions +# ownership and permissions for /var/lib/databank +chgrp -R www-data /var/lib/databank/ +chmod -R 770 /var/lib/databank/ +# ownership and permissions for /var/log/databank +mkdir -p /var/log/databank +chgrp -R www-data /var/log/databank/ +chmod -R 770 /var/log/databank/ +# ownership and permissions for /var/cache/databank +mkdir -p /var/cache/databank +chgrp -R www-data /var/cache/databank/ +chmod -R 770 /var/cache/databank/ +# ownership and permissions /etc/default/databank +chgrp -R www-data /etc/default/databank/ +chmod -R 770 /var/cache/databank/ +# ownership and permissions granary.store +mkdir -p $dbstore +cp /var/lib/databank/persisted_state.json $dbstore +chgrp -R www-data $dbstore +chmod -R 770 $dbstore + +#Setup the python virtual environment +virtualenv --no-site-packages /var/lib/databank/ +cd /var/lib/databank/ +. bin/activate +# Get the version of python being used +if [ -d /var/lib/databank/lib/python2.6/ ] +then + py_rs='https://github.com/anusharanganathan/RecordSilo/raw/master/dist/RecordSilo-0.4.15-py2.6.egg' + py_who='/var/lib/databank/lib/python2.6/site-packages/repoze.who-1.0.19-py2.6.egg/' + py_site=databank_ve26_wsgi + py_webob='/var/lib/databank/lib/python2.6/site-packages/WebOb-1.2b3-py2.7.egg' +else + py_rs='https://github.com/anusharanganathan/RecordSilo/raw/master/dist/RecordSilo-0.4.15-py2.7.egg' + py_who='/var/lib/databank/lib/python2.7/site-packages/repoze.who-1.0.19-py2.7.egg/' + py_site=databank_ve27_wsgi + py_webob='/var/lib/databank/lib/python2.7/site-packages/WebOb-1.2b3-py2.7.egg' +fi +easy_install python-dateutil==1.5 +easy_install pairtree==0.7.1-T +easy_install $py_rs +easy_install solrpy==0.9.5 +easy_install rdflib==2.4.2 +easy_install redis==2.4.11 +easy_install MySQL-python +easy_install pylons==1.0 +easy_install lxml==2.3.4 +easy_install web.py +easy_install sqlalchemy==0.7.6 +easy_install webob==1.0.8 +easy_install repoze.what-pylons +easy_install repoze.what-quickstart +if [ -d $py_who ] +then + rm -r $py_who +fi +if [ -d $py_webob ] +then + rm -r $py_webob +fi +easy_install repoze.who==2.0a4 + +#Setup the database and add the user password +cd /var/lib/databank +paster setup-app production.ini +python add_user.py admin $dbpasswd $dbemail + +#Configure SOLR and message workers in supervisor +db_get databank/confsolr +local dbconf="$RET" +installstatus=`dpkg-query -W -f='${Status}' solr-tomcat` +if [ "$installstatus" = "install ok installed" ] +then + if [ "$dbconf" = "true" ] + then + if [ -f /etc/init.d/tomcat6 ] + then + invoke-rc.d tomcat6 stop + fi + if [ -f /etc/init.d/supervisor ] + then + invoke-rc.d supervisor stop + fi + cp /etc/solr/conf/schema.xml /etc/solr/conf/schema.xml.bak + ln -sf /etc/default/databank/schema.xml /etc/solr/conf/schema.xml + ln -sf /var/lib/databank/message_workers/workers_available/worker_broker.conf /etc/supervisor/conf.d/worker_broker.conf + ln -sf /var/lib/databank/message_workers/workers_available/worker_solr.conf /etc/supervisor/conf.d/worker_solr.conf + if [ -f /etc/init.d/tomcat6 ] + then + invoke-rc.d tomcat6 start + fi + if [ -f /etc/init.d/supervisor ] + then + invoke-rc.d supervisor start + fi + else + echo "***********************************************************" + echo "SOLR has not been configured for Databank." + echo "The instructions for doing so are available at" + echo "/usr/share/doc/databank/ConfiguringSOLR.txt" + echo "***********************************************************" + fi +else + echo "***********************************************************" + echo "SOLR has not been configured for Databank." + echo "The instructions for doing so are available at" + echo "/usr/share/doc/databank/ConfiguringSOLR.txt" + echo "***********************************************************" +fi + +#Enable site in apache +a2dissite default +a2dissite default-ssl +a2ensite $py_site + +#Start Apache +invoke-rc.d apache2 reload +invoke-rc.d apache2 start + +echo =========================================================== +echo Databank has been successfully installed +echo +echo Your Databank instance is available at http://localhost +echo Databank\'s home directory is /var/lib/databank +echo Data is stored under $dbstore +echo +echo To get started, visit the Databank homepage at http://localhost +echo +echo This paackage is brought to you by the Dataflow Team +echo http://dataflow.ox.ac.uk +echo =========================================================== + +db_stop diff --git a/debian/postrm b/debian/postrm new file mode 100644 index 0000000..d1d6280 --- /dev/null +++ b/debian/postrm @@ -0,0 +1,22 @@ +#!/bin/bash +set -e + +if [ -f /usr/share/debconf/confmodule ]; then + . /usr/share/debconf/confmodule +fi +if [ -f /usr/share/dbconfig-common/dpkg/postrm.mysql ]; then + . /usr/share/dbconfig-common/dpkg/postrm.mysql + dbc_go databank $@ +fi + +if [ "$1" = "purge" ]; then + rm -f /etc/default/databank/db.sh + if which ucf >/dev/null 2>&1; then + ucf --purge /etc/default/databank/db.sh + ucfr --purge databank /etc/default/databank/db.sh + fi +fi + + +# Remove my changes to the db. +db_purge diff --git a/debian/preinst b/debian/preinst new file mode 100644 index 0000000..a7d0679 --- /dev/null +++ b/debian/preinst @@ -0,0 +1,10 @@ +#!/bin/bash +set -e + +# stop apache server +# stop django server +if [ -f /etc/init.d/apache2 ] +then + invoke-rc.d apache2 stop +fi + diff --git a/debian/prerm b/debian/prerm new file mode 100644 index 0000000..7a28f6d --- /dev/null +++ b/debian/prerm @@ -0,0 +1,10 @@ +#!/bin/sh + +set -e +#set -x + +. /usr/share/debconf/confmodule +. /usr/share/dbconfig-common/dpkg/prerm.mysql +dbc_go databank $@ + + diff --git a/debian/rules b/debian/rules new file mode 100644 index 0000000..917d9bf --- /dev/null +++ b/debian/rules @@ -0,0 +1,13 @@ +#!/usr/bin/make -f +# -*- makefile -*- +# Sample debian/rules that uses debhelper. +# This file was originally written by Joey Hess and Craig Small. +# As a special exception, when this file is copied by dh-make into a +# dh-make output file, you may use that output file without restriction. +# This special exception was added by Craig Small in version 0.37 of dh-make. + +# Uncomment this to turn on verbose mode. +#export DH_VERBOSE=1 + +%: + dh $@ diff --git a/debian/templates b/debian/templates new file mode 100644 index 0000000..f4592eb --- /dev/null +++ b/debian/templates @@ -0,0 +1,57 @@ +Template: databank/email +Type: string +Description: Adminstrator Email: + The email id will be used to send error messages from Databank. + +Template: databank/smtpserver +Type: string +Default: localhost +Description: SMTP Server: + This is used for sending emails from Databank + +Template: databank/store +Type: string +Default: /silos +Description: Data storage location: + The location where the data will be stored by Databank. + +Template: databank/uri +Type: string +Description: Base URI: + The base uri (domain name) of Databank. + Example: http://example.com/ + +Template: databank/password +Type: password +Description: Administrator password: + The password for user 'admin' + The user 'admin' is the main administartor for Databank and + has access to all parts of Databank + +Template: databank/confsolr +Type: boolean +Description: Can SOLR be configured for Databank? + The search facility in Databank is powered by SOLR. + . + If you choose to configure SOLR now, the existing schema + at /etc/solr/conf/schema.xml will be replaced with Databank's schema + . + If you choose to configure SOLR at a later time, + the instructions to do so are at /usr/share/doc/databank/ConfiguringSOLR.txt + +Template: databank/nosolrconf +Type: note +Description: Configuring SOLR + SOLR has not been configured for Databank. + If you choose to configure SOLR at a later time, + the instructions to do so are at /usr/share/doc/databank/ConfiguringSOLR.txt + +Template: databank/passwdnote +Type: note +Description: Change your password + Databank has been installed successfully and is available at http://localhost + . + The default credntials for the main user is - username: admin password: test + . + On logging in, Please change the default admin password + diff --git a/development.ini b/development.ini index 5eae311..9988a61 100644 --- a/development.ini +++ b/development.ini @@ -1,3 +1,23 @@ +# Copyright (c) 2012 University of Oxford +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, --INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # # rdfdatabank - Pylons development environment configuration # @@ -8,35 +28,63 @@ debug = true # Uncomment and replace with the address which should receive any error reports #email_to = you@yourdomain.com smtp_server = localhost -error_email_from = paste@localhost +error_email_from = paste@databank [server:main] use = egg:Paste#http -host = 127.0.0.1 -port = 5000 +#Use these setings to run pylons using mod_wsgi and apache +#host = 127.0.0.1 +#port = 5000 +#Use these settings tp run pylons from the commandline +host = 0.0.0.0 +port = 80 [app:main] use = egg:rdfdatabank full_stack = true static_files = true -cache_dir = %(here)s/data +sqlalchemy.url = mysql://databanksqladmin:d6sqL4dm;n@localhost:3306/databankauth +sqlalchemy.pool_recycle = 3600 + +cache_dir = /var/cache/databank beaker.session.key = rdfdatabank beaker.session.secret = somesecret -who.config_file = %(here)s/who.ini -who.log_level = info +who.config_file = /var/lib/databank/who.ini +who.log_level = debug who.log_file = stdout +#who.log_file = /var/log/databank/who.log redis.host = localhost -granary.store = silos -granary.uri_root = http://databank.bodleian.ox.ac.uk/objects/ +granary.store = /silos +granary.uri_root = http://databank/ + +#profile.log_filename = /var/log/databank/profile.log +#profile.path = /__profile__ + +#auth.file = /var/lib/databank/passwd +#auth.info = /var/lib/databank/rdfdatabank/config/users.py + +doi.config = /var/lib/databank/rdfdatabank/config/doi_config.py +doi.count = /var/lib/databank/rdfdatabank/config/doi_count broadcast.to = redis broadcast.queue = silochanges -solr.host = http://localhost:8983/solr +metadata.embargoed = False + +solr.host = http://localhost:8080/solr +naming_rule = [^0-9a-zA-Z_\-\:] +naming_rule_humanized = numbers, letters, '-' and ':', must be more than one character long and must not contain any spaces. +formats_served = text/html,text/xhtml,text/plain,application/json,application/rdf+xml,text/xml,text/rdf+n3,application/x-turtle,text/rdf+ntriples,text/rdf+nt +publisher = Bodleian Libraries, University of Oxford +rights = http://ora.ouls.ox.ac.uk/objects/uuid%3A1d00eebb-8fed-46ad-8e38-45dbdb4b224c +license = CC0 1.0 Universal (CC0 1.0). See http://creativecommons.org/publicdomain/zero/1.0/legalcode +#license = Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License. See http://creativecommons.org/licenses/by-nc-sa/3.0/ + +api.version = 0.3 # If you'd like to fine-tune the individual locations of the cache data dirs # for the Cache data, or the Session saves, un-comment the desired settings @@ -49,13 +97,12 @@ solr.host = http://localhost:8983/solr # execute malicious code after an exception is raised. #set debug = false - # Logging configuration [loggers] -keys = root, routes, rdfdatabank +keys = root, routes, rdfdatabank, sqlalchemy [handlers] -keys = console +keys = console, logfile [formatters] keys = generic @@ -66,21 +113,32 @@ handlers = console [logger_routes] level = INFO -handlers = +handlers = console qualname = routes.middleware # "level = DEBUG" logs the route matched and routing variables. [logger_rdfdatabank] level = DEBUG -handlers = +handlers = console qualname = rdfdatabank +[logger_sqlalchemy] +level = INFO +handlers = console +qualname = sqlalchemy.engine + [handler_console] class = StreamHandler args = (sys.stderr,) level = NOTSET formatter = generic +[handler_logfile] +class = FileHandler +level = INFO +formatter = generic +args = ('/var/log/databank/databank.log', 'w') + [formatter_generic] format = %(asctime)s,%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s datefmt = %H:%M:%S diff --git a/docs/Databank_VM_Installation.txt b/docs/Databank_VM_Installation.txt new file mode 100644 index 0000000..ef67d1b --- /dev/null +++ b/docs/Databank_VM_Installation.txt @@ -0,0 +1,537 @@ +Databank VM Setup - 0.3rc2 +This document details installing Databank from source + +For installing Databank from a debian package, visit http://apt-repo.bodleian.ox.ac.uk/databank/ +and follow the instruction under 'Using the repository' and 'Installing Databank' + +------------------------------------------------------------------------------------------------------ +I. Virtual machine details +------------------------------------------------------------------------------------------------------ +Ubuntu 11.10 server i386 +512MB RAM +8GB Harddrive (all of hard disk not allocated at the time of creation) +Network: NAT +1 processor + +hostname: databank +Partition disk - guided - use entire disk and set up LVM +Full name: Databank Admin +username: demoSystemUser +password: xxxxxxx +NO encryption of home dir +No proxy +No automatic updates +No predefined software +Install Grub boot loader to master boot record + +Installing VMWare tools + Select Install Vmware tools from the VMWare console + sudo mkdir /mnt/cdrom + sudo mount /dev/cdrom /mnt/cdrom + cd tmp + cd /tmp + ls -l + tar zxpf /mnt/cdrom/VMwareTools-7.7.6-203138.tar.gz vmware-tools-distrib/ + ls -l + sudo umount /dev/cdrom + sudo apt-get install linux-headers-virtual + sudo apt-get install psmisc + cd vmware-tools-distrib/ + sudo ./vmware-install.pl +Accept all of the default options + +------------------------------------------------------------------------------------------------------ +II. A. Packages to be Installed +------------------------------------------------------------------------------------------------------ + sudo apt-get install build-essential + sudo apt-get update + sudo apt-get install openssh-server + + sudo apt-get install python-dev + sudo apt-get install python-setuptools + sudo apt-get install python-virtualenv + + sudo apt-get install curl + sudo apt-get install links2 + sudo apt-get install unzip + sudo apt-get install libxml2-dev + sudo apt-get install libxslt-dev + sudo apt-get install libxml2 + sudo apt-get install libxslt1.1 + + sudo apt-get install redis-server + +------------------------------------------------------------------------------------------------------ +III. Create mysql user and database for Databank +------------------------------------------------------------------------------------------------------ + + # If you don't have mysql installed, run the following command + sudo apt-get install mysql-server libmysql++-dev + + # Create mysql user and database for Databank + # Create Database databankauth and user databanksqladmin. Give user databanksqladmin access to databankauth + # Set the password for user databanksqladmin - replace 'password' in the command below + mysql -u root -p +mysql> use mysql; +mysql> CREATE DATABASE databankauth DEFAULT CHARACTER SET utf8 COLLATE utf8_bin; +mysql> GRANT ALL ON databankauth.* TO databanksqladmin@localhost IDENTIFIED BY password; +mysql> exit + + # Test the user and database are created fine. + # You should be able to login as used databanksqladmin and use the database databankatuh. + # The database will be populated with the required tables when the databank application is setup + mysql -h localhost -u databanksqladmin -p +mysql> use databankauth; +mysql> show tables; +mysql> exit + +------------------------------------------------------------------------------------------------------ +IV. Install Databank, Sword server and python depedencies +------------------------------------------------------------------------------------------------------ + Databank's root folder is not /var/lib/databank + + # Create all of the folders needed for Databank and set the permission and owner + sudo mkdir /var/lib/databank + sudo mkdir /var/log/databank + sudo mkdir /var/cache/databank + sudo mkdir /etc/default/databank + sudo mkdir /silos + sudo chown -R databankadmin:www-data /var/lib/databank/ + sudo chown -R databankadmin:www-data /var/log/databank/ + sudo chown -R databankadmin:www-data /var/cache/databank/ + sudo chown -R databankadmin:www-data /etc/default/databank/ + sudo chown -R databankadmin:www-data /silos/ + sudo chmod -R 775 /var/lib/databank/ + sudo chmod -R 775 /var/log/databank/ + sudo chmod -R 775 /var/cache/databank/ + sudo chmod -R 775 /etc/default/databank/ + sudo chmod -R 775 /silos/ + + # Pull databank source code from Github into /var/lib/databank + sudo apt-get install git-core git-doc + git clone git://github.com/dataflow/RDFDatabank /var/lib/databank + + # Move all of the config files into /etc/default/databank so you don't overwrite them by mistake when updating the source code + cp production.ini /etc/default/databank/ + cp development.ini /etc/default/databank/ + cp -r docs/apache_config/*_wsgi /etc/default/databank/ + cp docs/solr_config/conf/schema.xml /etc/default/databank/ + + # Setup a virtual environemnt fro python and install all the python packages + virtualenv --no-site-packages /var/lib/databank/ + cd /var/lib/databank/ + source bin/activate + easy_install python-dateutil==1.5 + easy_install pairtree==0.7.1-T + easy_install https://github.com/anusharanganathan/RecordSilo/raw/master/dist/RecordSilo-0.4.15-py2.7.egg + easy_install solrpy==0.9.5 + easy_install rdflib==2.4.2 + easy_install redis==2.4.11 + easy_install MySQL-python + easy_install pylons==1.0 + easy_install lxml==2.3.4 + easy_install web.py + easy_install sqlalchemy==0.7.6 + easy_install repoze.what-pylons + easy_install repoze.what-quickstart + + # Repoze.what installs repoze.who version 1.0.19 while Databank uses repoze.who 2.0a4. So delete repoze.who 1.0.19 + rm -r lib/python2.7/site-packages/repoze.who-1.0.19-py2.7.egg/ + + # Pylons installs the latest version of WebOb, which expects all requests in utf-8 while earlier WebOb until 1.0.8 did't insist on utf-8. + # So remove the latest version of WebOb, which currently is 1.2b3 + rm -r lib/python2.7/site-packages/WebOb-1.2b3-py2.7.egg/ + + # Install the particular version of repoze.who and WebOb needed for Databank + easy_install repoze.who==2.0a4 + easy_install webob==1.0.8 + + # Pull the sword server from source forge and copy the folder sss within sword server into databank + cd ~ + wget http://sword-app.svn.sourceforge.net/viewvc/sword-app/sss/branches/sss-2/?view=tar + mv index.html\?view\=tar sword-server-2.tar.gz + tar xzvf sword-server-2.tar.gz + cp -r ./sss-2/sss/ ./ + cd /var/lib/databank + + Installing profilers in python and pylons to obtain run time performance and other stats + Note: This package is OPTIONAL and is only needed in development machines. + See the note below about running Pylons in debug mode (section B) + easy_install profiler + easy_install repoze.profile + +------------------------------------------------------------------------------------------------------ +V. Customizing Databank to your environment +------------------------------------------------------------------------------------------------------ +All of Databank's configuration settings are placed in the file production.ini or development.ini + * development.ini is configured to work in debug mode with all of the logs written to the console. + * production.ini is configured to not work in debug mode with all of the logs written to log files + +The following settings need to be configured +1. Adminsitrator email and smtp server for emails + The databank will email errors to the administrator + Edit the field 'email_to' for the email address + Edit the field 'smtp_server' for the smtp server to be used. The default value is 'localhost'. + +2. The location where all of Databank's data is to be stored + Edit the field 'granary.store' + The default value is '/silos' + +3^. The url where Databank will be available. + Examples for this are: + The server name like http://example.com/databank/ or + the ip address fo the machine,if it has no cname http://192.168.23.131/ or + just using localhost (development / evaluation) http://localhost/ or + Edit the field 'granary.uri_root' + The default value is 'http://databank/' + +4. The mysql database connction string for databank + The format of the connection string is mysql://username:password@localhost:3306/database_name + Replace username, password and database_name with the corect values. + The default username is databankdsqladmin + The default database name is databankauth + Edit the field 'sqlalchemy.url' + The default value is mysql://databanksqladmin:d6sqL4dm;n@localhost:3306/databankauth' + +5. The SOLR end point + Should point to the databank solr instance + Edit the field 'solr.host' + The default value is http://localhost:8080/solr, + +6. Default metadata values + The value of publisher and the defualt value of rights and license can be modified + These are treated as text strings and are currently used in the manifest.rdf + + ^ This setting will also need to be modified at /var/lib/databank/rdfdatabank/tests/RDFDatabankConfig.py + Change 'granary_uri_root'. + See section XVI for the significance of the base URI + +------------------------------------------------------------------------------------------------------ +VI. Customizing Databank Sword to your environment +------------------------------------------------------------------------------------------------------ +The sword configuration settings are placed in the file sss.conf.json + +The url where Databank will be available needs to be set +Without this, a sword client cannot talk to Databank through the sword interface + +Edit the field 'base_url' +The default value is http://localhost:5000/swordv2/ +Replace http://localhost/ with the correct base url +Examples for this are: + The server name like http://example.com/databank/ or + the ip address fo the machine,if it has no cname http://192.168.23.131/ or + just using localhost (development / evaluation) http://localhost/ or + + +Edit the field 'db_base_url' +The default value is http://192.168.23.133/ +Replace with the correct base url + +------------------------------------------------------------------------------------------------------ +VII. Intialize databank and Create the main admin user to access Databank +------------------------------------------------------------------------------------------------------ + paster setup-app production.ini + python add_user.py admin password dataflow-devel@googlegroups.com + + The second command is used to create the administrator user for databank. + * The administrator has a default username as 'admin'. + * This user is the root administrator for Databank and has access to all the silos in Databank. + * Please choose a strong password for the user and replace the string 'password' with the password. + +------------------------------------------------------------------------------------------------------ +VIII. Installing SOLR with Tomcat and cutomizing SOLR for Databank + * If you already have an existing SOLR installation and would like to use that, see section XVIII +------------------------------------------------------------------------------------------------------ + # Install solr with tomcat + sudo apt-get install openjdk-6-jre + sudo apt-get install solr-tomcat + + This will install Solr from Ubuntu's repositories as well as install and configure Tomcat. + Tomcat is installed with CATALINA_HOME in /usr/share/tomcat6 and CATALINA_BASE in /var/lib/tomcat6, + following the rules from /usr/share/doc/tomcat6-common/RUNNING.txt.gz. + The Catalaina configuration files are in /etc/tomcat6/ + + Solr itself lives in three spots, /usr/share/solr, /var/lib/solr/ and /etc/solr. + These directories contain the solr home director, data directory and configuration data respectively. + + You can visit the url http://localhost:8080 and http://localhost:8080/solr to make sure Tomcat and SOLR are working fine + + # Stop tomcat before customizing solr + /etc/init.d/tomcat6 stop + + # Backup the current solr schema + sudo cp /etc/solr/conf/schema.xml /etc/solr/conf/schema.xml.bak + + # Copy (sym link) the Databank SOLR Schema into Solr + sudo ln -sf /etc/default/databank/schema.xml /etc/solr/conf/schema.xml + + # Start tomcat and test solr is working fine by visting http://localhost:8080/solr + /etc/init.d/tomcat6 start + +------------------------------------------------------------------------------------------------------ +IX. Setting up Supervisor to manage the message workers +------------------------------------------------------------------------------------------------------ +Items are indexed in SOLR from Databank, through redis using message queues +The workers that run on these message queues are managed using supervisor + +# If you do not already have supervisor, install it + sudo apt-get install supervisor + +# Configuring Supervisor for Databank + + # Stop supervisor + sudo /etc/init.d/supervisor stop + + # Copy (sym link) the supervisor configuration files for the message workers + sudo ln -sf /var/lib/databank/message_workers/workers_available/worker_broker.conf /etc/supervisor/conf.d/worker_broker.conf + sudo ln -sf /var/lib/databank/message_workers/workers_available/worker_solr.conf /etc/supervisor/conf.d/worker_solr.conf + + sudo /etc/init.d/supervisor start + +# The controller for supervisor can be invoked with the command 'supervisorctl' + sudo supervisorctl + + This will list all of the jobs manged by supervisor and their current status. + You can start / stop / restart jobs from within the controller. + For more info on supervisor, read http://supervisord.org/index.html + +------------------------------------------------------------------------------------------------------ +X. Integrate Databank with Datacite, for minting DOIs (this section is optional) +------------------------------------------------------------------------------------------------------ +If you want to integrate Databank with Datacite for minting DOIs for each of the data-packages, then you would need to do the following: + +Create a file called doi_config.py which has all of the authentication information given to you by Datacite. Copy the lines below and +edit the values for each of the fields in "#Details pertaining to account with datacite" and "#Datacite api endpoint" if it is different + +By default, this file is palced in /var/lib/databank/rdfdatabank/config/doi_config.py. +If you want to place the file in a different location, make sure Datababk knows where to find the file. +The field 'doi.config' in section [app:main] in production.ini and development.ini has this setting. + +#-*- coding: utf-8 -*- +from pylons import config + +class OxDataciteDoi(): + def __init__(self): + """ + DOI service provided by the British Library on behalf of Datacite.org + API Doc: https://api.datacite.org/ + Metadata requirements: http://datacite.org/schema/DataCite-MetadataKernel_v2.0.pdf + """ + #Details pertaining to account with datacite + self.account = "BL.xxxx" + self.description = "Oxford University Library Service Databank" + self.contact = "Contact Name of person in your organisation" + self.email = "email of contact person in your organisation" + self.password = "password as given by DataCite" + self.domain = "ox.ac.uk" + self.prefix = "the prefix as given by DataCite" + self.quota = 500 + + if config.has_key("doi.count"): + self.doi_count_file = config['doi.count'] + + #Datacite api endpoint + self.endpoint_host = "api.datacite.org" + self.endpoint_path_doi = "/doi" + self.endpoint_path_metadata = "/metadata" + +------------------------------------------------------------------------------------------------------ +XI. Integrate Databank with Apache +------------------------------------------------------------------------------------------------------ +1. Install Apache and the required libraries + sudo apt-get install apache2 apache2-utils libapache2-mod-wsgi + +2. Stop Apache before making any modification + sudo /etc/init.d/apache2 stop + +3. Add a new site in apache sites-available called 'databank_ve27_wsgi' + sudo ln -sf /etc/default/databank/databank_ve27_wsgi /etc/apache2/sites-available/databank_ve27_wsgi + +4. Disable the default sites + # Check what default sites you have + sudo ls -l /etc/apache2/sites-available + sudo a2dissite default + sudo a2dissite default-ssl + sudo a2dissite 000-default + +5. Enable the site 'databank_ve27_wsgi' + sudo a2ensite databank_ve_27_wsgi + +6. Reload apache and start it + sudo /etc/init.d/apache2 reload + sudo /etc/init.d/apache2 start + +------------------------------------------------------------------------------------------------------ +XII. Making sure all of the needed folders are available and apache has access to all the needed parts +------------------------------------------------------------------------------------------------------ +Apache runs as user www-data. Make sure the user www-data is able to read write to the following locations + /var/lib/databank + /silos + /var/log/databank + /var/cache/databank + +Change permission, so www-data has access to RDFDatabank + sudo chgrp -R www-data path_to_dir + sudo chmod -R 775 $path_to_dir + +------------------------------------------------------------------------------------------------------ +XIII. Test your Pylons installation +------------------------------------------------------------------------------------------------------ +Visit the page http://localhost/ + +If you see an error message look at the logs at /var/log/apache2/databank-error.log and /var/log/databank/ + +------------------------------------------------------------------------------------------------------ +XIV. Run the test code and make sure all the tests pass +------------------------------------------------------------------------------------------------------ +The test code is located at /var/lib/databank/rdfdatabank/tests + +The test use the configuration file RDFDatabankConfig.py, which you may need to modify + granary_uri_root="http://databank" + This needs to be the same value as granary.uri_root in the production.ini file (or development.ini file if usign that instead) + endpointhost="localhost" + This should point to the url where the databank instance is running. + If it is running on http://localhost/, it should be localhost. If it is running on http://example.org it should be example.org. + if it is running on a non-standard port like port 5000 at http://localhost:5000, this would be localhost:5000 + endpointpath="/sandbox/" and endpointpath2="/sandbox2/" + The silos that are going to be used for testing. Currently only the silo defined in endpointpath is used. + The silos will be created by the test if they don't exist. + The rest of the file lists the credentials of the different users used for testing + +To run the tests + Make sure databank is running (see section IX) + cd /var/lib/databank + . bin/activate + cd rdfdatabank/tests + python TestSubmission.py + +----------------------------------------------------------------------------------------------------- +XV. Running Pylons from the command line in debug mode and dumping logs to stdout +----------------------------------------------------------------------------------------------------- +If you would like to run Pylons in debug mode from the command line and dump all of the log messages to stdout, stop apache and start paster from the command line. + +The configuration file development.ini has been setup to do just that. + +Make sure the user running paster has access to all the directories. + +Running Pylons on port 80 (host=0.0.0.0 and port=80 in development.ini) + you are now likely to be running databank as the super user and not user 'www-data' and so would have to revisit section XII and + change permissions giving the super user running paster access to the different directories. + +The commands to run pylons from the command line + sudo /etc/init.d/apache2 stop + sudo ./bin/paster serve development.ini + +To stop paster,press ctrl+c + +To run paster on another port, modify the fields host and port in development.ini. +For example, to run on port 5000, the settings would be +host = 127.0.0.1 +port = 5000 + +----------------------------------------------------------------------------------------------------- +XVI. The Base URI setting (granary.uri_root) for Databank and it's significance +----------------------------------------------------------------------------------------------------- +One of the configuration options available in Databank is the 'granary.uri_root' which is the base uri for Databank. +This value is used in the following: + * Each of the silos created in Databank will be intialized with this base URI + * In each of the data packages, the metadata (held in the manifest.rdf) will use this base URI in creating the URI for the data package + * The links to each data item in the package will be created using this base uri (aggregate map for each data package) + + If this base uri doesn't resolve, the links for each of the items in the data package will not resolve + +This base uri is regarded to be permanent. Modifying the base uri at some point in the future will create all new silos and the data packages within the new silos with the new base uri, but the existing silos and data packages will continue to have the old uri. + +----------------------------------------------------------------------------------------------------- +XVII. Recap of the services running in Databank +----------------------------------------------------------------------------------------------------- +Apache2 + Runs the databank web server (powered by Pylons) + at http://localhost or http://ip_address from your host machine + + Apache should start automatically on startup of the VM. + + The apache log files are at + /var/log/apache2/ + + The command to stop, start and restart apache are + sudo /etc/init.d/apache2 [ stop | start | restart ] + + +Tomcat + Tomcat runs the SOLR webservice. Tomcat should start automatically on startup of the VM. + Tomcat should be available at http://localhost:8080 and + SOLR should be available at http://localhost:8080/solr + + Tomcat is installed with + CATALINA_HOME in /usr/share/tomcat6, + CATALINA_BASE in /var/lib/tomcat6 and + configuration files in /etc/tomcat6/ + + SOLR itself lives in three spots, + /usr/share/solr - contains the SOLR home director, + /var/lib/solr/ - contains the data directory and + /etc/solr – contains the configuration data + + The command to stop, start and restart tomcat are + sudo /etc/init.d/tomcat6 [ stop | start | restart ] + + + Redis + Runs a basic messaging queue used by the API for indexing items into SOLR + and storing information that need to accessed quickly (like embargo information) + + Redis should start automatically on startup of the VM. + + The data directory is at /var/lib/redis and the configuration is at /etc/redis + + The command to stop, start and restart redis are + sudo /etc/init.d/redis-server [ stop | start | restart ] + + +Supervisor + Supervisor maintains the message workers run by Databank. + Run the supervisor controller to manage processes maintained by supervisor + sudo supervisorctl + +------------------------------------------------------------------------------------------------------ +XVIII. Integrating SOLR for Databank with an existing SOLR installation +------------------------------------------------------------------------------------------------------ +If you already have a SOLR instance running and would like to add databank to it + - either by creating a new core (https://wiki.apache.org/solr/CoreAdmin) + - or by creating a new instance + http://wiki.apache.org/solr/SolrTomcat#Multiple_Solr_Webapps + http://wiki.apache.org/solr/SolrJetty#Running_multiple_instances +you can do so. + + +Once you have created a new core or new instance, and verified it is wotking, + stop SOLR, + replace the example schema file for that core / instance with Databank's schema file. + It is available at /etc/default/databank/schema.xml + Start SOLR + + +Stop Databank web server (stop apache) and the solr worker (using supervisorctl) + + +You need to configure the solr end point in the config file production.ini or development.ini +(as mentioned in section V). + In the case of mmultiple cores, the solr end point would be something like http://localhost:8080/solr/core_databank + if you have called the databank core 'core_databank' + + In the case of mmultiple SOLR instances, the solr end point would be something like http://localhost:8080/solr_databank + if you have called the databank instance 'solr_databank' + + Edit the field 'solr.host'. + Replace the default value with your solr endpoint + + +You need to configure the solr end point in the config file loglines.cfg +located at /var/lib/databank/message_workers/ and used by the solr worker for indexing items into SOLR + Edit the field 'solrurl' in the section [worker_solr]. + Replace the default value with your solr endpoint + +Start the solr worker (using supervisorctl) and the Databank web server (start apache) + +----------------------------------------------------------------------------------------------------- \ No newline at end of file diff --git a/docs/DebianInstallation.txt b/docs/DebianInstallation.txt new file mode 100644 index 0000000..9cba592 --- /dev/null +++ b/docs/DebianInstallation.txt @@ -0,0 +1,112 @@ +------------------------------------------------------------------------------- +Installing Databank from the Debian package +------------------------------------------------------------------------------- +The debian package fro Databank is available at +http://apt-repo.bodleian.ox.ac.uk/databank/ + +To be able to install Databank using apt or aptitude, + +1. Add the repository to the file sources.list. +Add the following line to your /etc/apt/sources.list file: + +deb http://apt-repo.bodleian.ox.ac.uk/databank/ unstable main + +2. Import the following signing key: +wget http://apt-repo.bodleian.ox.ac.uk/datastage/dataflow.gpg +apt-key add dataflow.gpg + +3. Update the package index +sudo apt-get update + +4. Install Databank +sudo apt-get install databank + +------------------------------------------------------------------------------- +Questions asked during Databank's installation +------------------------------------------------------------------------------- +1. Adminstrator Email Id - +This will be used to send error messages from Databank to the administrator. + +2. SMTP Server used to send email messages. The default value is localhost + +3. Data storage location - the location where the data will be stored by Databank. + The default value is /silos + +4. The base uri (domain name) of Databank + Examples for this are: + Server name like http://example.com/databank/ or + Ip address of the machine, if it has no cname http://192.168.23.131/ or + Using localhost (development / evaluation) http://localhost/ or + + The default value is 'http://databank/' + + This value is used in the following: + * Each of the silos created in Databank will be intialized with the base URI + * In each of the data packages, the metadata (held in the manifest.rdf) will + use this base URI in creating the URI for the data package + * The links to each data item in the package will be created using this base + uri (aggregate map for each data package) + + If this base uri doesn't resolve, the links for each of the items in the + data package will not resolve + + The base uri is regarded to be permanent. Modifying the base uri at some + point in the future will create all new silos and the data packages within + the new silos with the new base uri, but the existing silos and data packages + will continue to have the old uri. + +5. The password for the administrator user of databank used by the web interface + for authentication and authorization. + * The administrator has a default username as 'admin'. + * This user is the root administrator for Databank and has access to all the silos in Databank. + * Please choose a strong password for the user + +6. Choosing a password for the MySQL user + Databank will install the MySQL database if it isn't alredy installed. + A database with the name 'databank' will be create during installation. + The database user 'databank' will also be craeted. + You will be asked for a password for the user 'databank' and the credentials + of the admin user of MySQL itself, so that the database and user can be created. + +7. Confirmation if Databank can be configured for SOLR + The search facility in Databank is powered by SOLR. + + If you choose to configure SOLR now, the existing schema + at /etc/solr/conf/schema.xml will be replaced with Databank's schema + + If you choose to configure SOLR at a later time, + the instructions to do so are at /usr/share/doc/databank/ConfiguringSOLR.txt + +------------------------------------------------------------------------------- +After installing Databank +------------------------------------------------------------------------------- +Visit http://localhost from the local browser to get started. + +------------------------------------------------------------------------------- +Customizing or debugging Databank +------------------------------------------------------------------------------- +Please read the document available at +/usr/share/doc/databank/Databank_VM_Installation.txt +or online at +https://github.com/dataflow/RDFDatabank/blob/master/docs/Databank_VM_Installation.txt + +If you are not interested in installing Databank from source, skip to Section V. +Section V and later sections describe the various settings for Databank. + +------------------------------------------------------------------------------- +Databank Documentation +------------------------------------------------------------------------------- +The databank source code is at https://github.com/dataflow/RDFDatabank + +Databank is provided to you as a part of the datafalow project. +You can find more information about Databank and Dataflow at +http://www.dataflow.ox.ac.uk/ + +The API documentation is available at https://databank-vm1.oerc.ox.ac.uk/api/ +or in your local instance of databank at http://localhost/api + +There are some notes on using the API at the following link, https://github.com/dataflow/RDFDatabank/tree/master/docs/using_databank_api + +The Databank wiki at https://github.com/dataflow/RDFDatabank/wiki has notes on the +current DataBank feature set, architecture, policies and the development roadmap +for the near future. diff --git a/docs/Dependencies.txt b/docs/Dependencies.txt new file mode 100644 index 0000000..41eddfa --- /dev/null +++ b/docs/Dependencies.txt @@ -0,0 +1,85 @@ +Databank is a web-based application for storing, curating and publishing data-packages, and is written using python and the pylons web framework. Its default deployment includes a message queue providing databank notifications to listener services. This message queue is handled using `Redis `_ and `Supervisor `_ to maintain the listener services. The search interface in Databank is powered by `Apache SOLR `_. + +Dependencies for databank + ++---------------------------------+-----------+ +| Package name | Version | ++=================================+===========+ +| python | >=2.6 | ++---------------------------------+-----------+ +| python-dev | | ++---------------------------------+-----------+ +| libxml2 | | ++---------------------------------+-----------+ +| libxslt1.1 | | ++---------------------------------+-----------+ +| libxml-dev | | ++---------------------------------+-----------+ +| libxslt-dev | | ++---------------------------------+-----------+ +| python-virtualenv | | ++---------------------------------+-----------+ +| python-pylons^ | >=0.9.7 | ++---------------------------------+-----------+ +| python-repoze.who^ | =2.0a4 | ++---------------------------------+-----------+ +| python-repoze.who-friendlyform^ | =1.0.8 | ++---------------------------------+-----------+ +| python-rdflib^ | =2.4.2 | ++---------------------------------+-----------+ +| python-dateutil^ | >=1.4.1-4 | ++---------------------------------+-----------+ +| python-lxml^ | >=2.3.4 | ++---------------------------------+-----------+ +| python-pairtree^ | >=0.7.1 | ++---------------------------------+-----------+ +| python-recordsilo^ | >=0.4.15 | ++---------------------------------+-----------+ +| python-solrpy^ | >=0.9.5 | ++---------------------------------+-----------+ +| python-redis^ | >=2.4.5-1 | ++---------------------------------+-----------+ +| unzip | >=6.0 | ++---------------------------------+-----------+ +| apache2 | >=2.2.20 | ++---------------------------------+-----------+ +| apache2-utils | >=2.2.20 | ++---------------------------------+-----------+ +| libapache2-mod-wsgi | >=3.3 | ++---------------------------------+-----------+ +| redis-server | >=2.2.11 | ++---------------------------------+-----------+ +| supervisor | >=3.0 | ++---------------------------------+-----------+ +| openjdk-6-jre | >=6b23 | ++---------------------------------+-----------+ +| solr-tomcat | >=1.4.1 | ++---------------------------------+-----------+ + + +In addition to the above, these are the depoendencies for databank veersion 0.3rc2 + ++---------------------------------+-----------+ +| Package name | Version | ++=================================+===========+ +| python-repoze.what^ | >=1.0.3 | ++---------------------------------+-----------+ +| python-repoze.who.plugins.sa^ | >=1.0.1 | ++---------------------------------+-----------+ +| python-repoze.who.plugins.sql^ | >=1.0rc2 | ++---------------------------------+-----------+ +| python-sqlalchemy^ | =0.6.8.1 | ++---------------------------------+-----------+ +| mysql-server | =5.1 | ++---------------------------------+-----------+ +| libmysql++-dev | =3.1.0 | ++---------------------------------+-----------+ +| python-mysqldb (mysql-python)^ | =1.2.3 | ++---------------------------------+-----------+ + +\^ these python packages are installed within a virtual environment no site packages used + + Databank 0.3rc2 has the Simple Sword Server also packaged with it. + * Availabale from http://sword-app.svn.sourceforge.net/viewvc/sword-app/sss/branches/sss-2/ + * This will be packaged separately for the next release of databank + * The simple swrod server depends on web.py and lxml version 2.3.4 diff --git a/docs/LookingatRedis.txt b/docs/LookingatRedis.txt new file mode 100644 index 0000000..869e8f3 --- /dev/null +++ b/docs/LookingatRedis.txt @@ -0,0 +1,77 @@ +Indexing items in Databank for search and retreival using SOLR, Redis and Supervisor + + * Records are indexed into SOLR from Databank asynchronously, using Redis and Supervisor to manage this process + + * The items to be indexed are written into a queue (silochanges) in redis from Databank. + * The library broadcast.py adds the silo name and data package id along with a tiemstamap and the type of action (create, update or delete) into redis + + + + * Supervisor manages the processes used to work on the items queued in redis + + * The configuration file for each of the processes maintained by supervisor is available at workers_available. + + * These are symlinked to /etc/supervisor/conf.d/--- + + + + * Managing the queue in redis - the queue workflow (redisqueue.py) + + * For each of the workers, when an item is popped out of a queue it is written into another temporary queue (for example temp:broker, temp:logger, temp:solr) recording the item currently being processed + + * When the worker marks the task as completed the item is deleted from the temporary queue + + * When the worker marks the task as failed, it is moved from the temporary queue into another queue (or back to the same queue if another queue isn't configured). For example: it is moved into the queue solrindexerror in worker_solr. + + + + * There are three workers working off the redis queue - worker_broker, worker_auditlogger and worker_solr + + * loglines.cfg has the configuration options which are parsed by LogConfigParser.py + + * worker_broker works off the items queued in the silochanges queue. It copies each item to two other queues - auditlog and solrindex + + * worker_solr works off the items queued in the solrindex queue. It is used to index documents / delete documents from solr + * For each item, it gets the manifest from for the item the granary store, walks through the triples, genrates a solr document and adds the document to solr. It commits the index every hour or when no futher items are queued for indexing. If there is an error, the item is pushed to the queue solrindexerror and the error is logged in the file /var/log/databank/solr_error.log. + + + +The following is the python code to query redis + +{{{ +from redis import Redis +r = Redis() +all_keys = r.keys() + +#Have a look at all the keys associated with supervisor workers +r.llen('silochanges') + +r.llen('solrindex') +r.lindex('solrindex', 0) + +r.llen('solrindexerror') + +r.llen('temp:broker_0') +r.llen('temp:broker_1') + +r.llen('temp:solr_0') +r.llen('temp:solr_1') + +#Have a look at all the embargoed and embargoed_until keys +em_keys = r.keys('*:*:embargoed') +if type(em_keys).__name__ != 'list': + em_keys = em_keys.split(' ') +len(em_keys) + +emu_keys = r.keys('*:*:embargoed_until') +if type(emu_keys).__name__ != 'list': + emu_keys = emu_keys.split(' ') +len(emu_keys) + +#To delete the embargoed keys +for i in em_keys: + r.delete(i) + +for i in emu_keys: + r.delete(i) +}}} diff --git a/docs/apache_config/databank_development_wsgi b/docs/apache_config/databank_development_wsgi new file mode 100644 index 0000000..b196759 --- /dev/null +++ b/docs/apache_config/databank_development_wsgi @@ -0,0 +1,20 @@ + + #ServerName databank.com + + # Logfiles + ErrorLog /var/log/apache2/databank-error.log + CustomLog /var/log/apache2/databank-access.log combined + + # Use only 1 Python sub-interpreter. Multiple sub-interpreters + # play badly with C extensions. + WSGIApplicationGroup %{GLOBAL} + WSGIPassAuthorization On + # Setup mod_wsgi + WSGIScriptAlias / /var/lib/databank/mod_wsgi/dispatch_development.wsgi + + + Order deny,allow + Allow from all + + + diff --git a/docs/apache_config/databank_ve26_wsgi b/docs/apache_config/databank_ve26_wsgi new file mode 100644 index 0000000..d8b2645 --- /dev/null +++ b/docs/apache_config/databank_ve26_wsgi @@ -0,0 +1,21 @@ + + #ServerName databank.com + + # Logfiles + ErrorLog /var/log/apache2/databank-error.log + CustomLog /var/log/apache2/databank-access.log combined + LogLevel warn + + # Use only 1 Python sub-interpreter. Multiple sub-interpreters + # play badly with C extensions. + WSGIApplicationGroup %{GLOBAL} + WSGIPassAuthorization On + # Setup mod_wsgi + WSGIScriptAlias / /var/lib/databank/mod_wsgi/dispatch_ve_26.wsgi + + + Order deny,allow + Allow from all + + + diff --git a/docs/apache_config/databank_ve27_wsgi b/docs/apache_config/databank_ve27_wsgi new file mode 100644 index 0000000..e8c0b1c --- /dev/null +++ b/docs/apache_config/databank_ve27_wsgi @@ -0,0 +1,21 @@ + + #ServerName databank.com + + # Logfiles + ErrorLog /var/log/apache2/databank-error.log + CustomLog /var/log/apache2/databank-access.log combined + LogLevel warn + + # Use only 1 Python sub-interpreter. Multiple sub-interpreters + # play badly with C extensions. + WSGIApplicationGroup %{GLOBAL} + WSGIPassAuthorization On + # Setup mod_wsgi + WSGIScriptAlias / /var/lib/databank/mod_wsgi/dispatch_ve_27.wsgi + + + Order deny,allow + Allow from all + + + diff --git a/docs/apache_config/databank_wsgi b/docs/apache_config/databank_wsgi new file mode 100644 index 0000000..e21f5a5 --- /dev/null +++ b/docs/apache_config/databank_wsgi @@ -0,0 +1,21 @@ + + #ServerName databank.com + + # Logfiles + ErrorLog /var/log/apache2/databank-error.log + CustomLog /var/log/apache2/databank-access.log combined + LogLevel warn + + # Use only 1 Python sub-interpreter. Multiple sub-interpreters + # play badly with C extensions. + WSGIApplicationGroup %{GLOBAL} + WSGIPassAuthorization On + # Setup mod_wsgi + WSGIScriptAlias / /var/lib/databank/mod_wsgi/dispatch.wsgi + + + Order deny,allow + Allow from all + + + diff --git a/docs/assigning_dois/DOISyntax.doc b/docs/assigning_dois/DOISyntax.doc new file mode 100644 index 0000000..f9c7866 Binary files /dev/null and b/docs/assigning_dois/DOISyntax.doc differ diff --git a/docs/assigning_dois/DataCite-MetadataKernel_v2.0.pdf b/docs/assigning_dois/DataCite-MetadataKernel_v2.0.pdf new file mode 100755 index 0000000..02d529b Binary files /dev/null and b/docs/assigning_dois/DataCite-MetadataKernel_v2.0.pdf differ diff --git a/docs/assigning_dois/ImplementingDOIs.ppt b/docs/assigning_dois/ImplementingDOIs.ppt new file mode 100755 index 0000000..6b41109 Binary files /dev/null and b/docs/assigning_dois/ImplementingDOIs.ppt differ diff --git a/docs/authWithSqlAlchemy.txt b/docs/authWithSqlAlchemy.txt new file mode 100644 index 0000000..0e1b85e --- /dev/null +++ b/docs/authWithSqlAlchemy.txt @@ -0,0 +1,35 @@ +Using Pylons with SQLAlchemy +=================================================== +sudo easy_install sqlalchemy + SQLAlchemy-0.7.6 + sudo apt-get install python-sqlalchemy + +sudo apt-get install mysql-server + +sudo apt-get install libmysql++-dev + +sudo easy_install MySQL-python + MySQL-python 1.2.3 + +sudo easy_install repoze.what-quickstart + This has repose.what, repoze,who, plugins.sa, plugins.sql and friendly-form. + Since I already install some fo these packages, I will need to package repoze.what, plugins.sa and plugins.sql + + The other approach would be to modify the version number of repoze.who in quickstart and just go with one package + I renamed the quickstart as quickstart 2.0 and modified the install requires. This seems like an easier approach + + sudo easy_install repoze.what-pylons + repoze.what 1.0.9 + sudo easy_install repoze.what.plugins.sql + sudo easy_install repoze.who.plugins.sa + + #Repoze.who version 1.0.19 gets installed. Remove thisand re-install repoze,who 2.0a4 + #Note: I need to check which of the above packages does this and not install it. Also is repoze.what-quickstart really needed? + sudo rm -r /usr/local/lib/python2.6/dist-packages/repoze.who-1.0.19-py2.6.egg/ + sudo easy_install repoze.who==2.0a4 + +# Create a database and a user for that database. See usingDatabase-databankauth.txt for instruction on doing so. +# Modify the mysql connection string in development.ini to match the database and user created + +paster setup-app development.ini + diff --git a/docs/solr_config/conf/admin-extra.html b/docs/solr_config/conf/admin-extra.html new file mode 100644 index 0000000..aa739da --- /dev/null +++ b/docs/solr_config/conf/admin-extra.html @@ -0,0 +1,31 @@ + + + diff --git a/docs/solr_config/conf/elevate.xml b/docs/solr_config/conf/elevate.xml new file mode 100644 index 0000000..7630ebe --- /dev/null +++ b/docs/solr_config/conf/elevate.xml @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + + + + diff --git a/docs/solr_config/conf/mapping-ISOLatin1Accent.txt b/docs/solr_config/conf/mapping-ISOLatin1Accent.txt new file mode 100644 index 0000000..ede7742 --- /dev/null +++ b/docs/solr_config/conf/mapping-ISOLatin1Accent.txt @@ -0,0 +1,246 @@ +# The ASF licenses this file to You 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. + +# Syntax: +# "source" => "target" +# "source".length() > 0 (source cannot be empty.) +# "target".length() >= 0 (target can be empty.) + +# example: +# "À" => "A" +# "\u00C0" => "A" +# "\u00C0" => "\u0041" +# "ß" => "ss" +# "\t" => " " +# "\n" => "" + +# À => A +"\u00C0" => "A" + +# à => A +"\u00C1" => "A" + +#  => A +"\u00C2" => "A" + +# à => A +"\u00C3" => "A" + +# Ä => A +"\u00C4" => "A" + +# Ã… => A +"\u00C5" => "A" + +# Æ => AE +"\u00C6" => "AE" + +# Ç => C +"\u00C7" => "C" + +# È => E +"\u00C8" => "E" + +# É => E +"\u00C9" => "E" + +# Ê => E +"\u00CA" => "E" + +# Ë => E +"\u00CB" => "E" + +# ÃŒ => I +"\u00CC" => "I" + +# à => I +"\u00CD" => "I" + +# ÃŽ => I +"\u00CE" => "I" + +# à => I +"\u00CF" => "I" + +# IJ => IJ +"\u0132" => "IJ" + +# à => D +"\u00D0" => "D" + +# Ñ => N +"\u00D1" => "N" + +# Ã’ => O +"\u00D2" => "O" + +# Ó => O +"\u00D3" => "O" + +# Ô => O +"\u00D4" => "O" + +# Õ => O +"\u00D5" => "O" + +# Ö => O +"\u00D6" => "O" + +# Ø => O +"\u00D8" => "O" + +# Å’ => OE +"\u0152" => "OE" + +# Þ +"\u00DE" => "TH" + +# Ù => U +"\u00D9" => "U" + +# Ú => U +"\u00DA" => "U" + +# Û => U +"\u00DB" => "U" + +# Ãœ => U +"\u00DC" => "U" + +# à => Y +"\u00DD" => "Y" + +# Ÿ => Y +"\u0178" => "Y" + +# à => a +"\u00E0" => "a" + +# á => a +"\u00E1" => "a" + +# â => a +"\u00E2" => "a" + +# ã => a +"\u00E3" => "a" + +# ä => a +"\u00E4" => "a" + +# Ã¥ => a +"\u00E5" => "a" + +# æ => ae +"\u00E6" => "ae" + +# ç => c +"\u00E7" => "c" + +# è => e +"\u00E8" => "e" + +# é => e +"\u00E9" => "e" + +# ê => e +"\u00EA" => "e" + +# ë => e +"\u00EB" => "e" + +# ì => i +"\u00EC" => "i" + +# í => i +"\u00ED" => "i" + +# î => i +"\u00EE" => "i" + +# ï => i +"\u00EF" => "i" + +# ij => ij +"\u0133" => "ij" + +# ð => d +"\u00F0" => "d" + +# ñ => n +"\u00F1" => "n" + +# ò => o +"\u00F2" => "o" + +# ó => o +"\u00F3" => "o" + +# ô => o +"\u00F4" => "o" + +# õ => o +"\u00F5" => "o" + +# ö => o +"\u00F6" => "o" + +# ø => o +"\u00F8" => "o" + +# Å“ => oe +"\u0153" => "oe" + +# ß => ss +"\u00DF" => "ss" + +# þ => th +"\u00FE" => "th" + +# ù => u +"\u00F9" => "u" + +# ú => u +"\u00FA" => "u" + +# û => u +"\u00FB" => "u" + +# ü => u +"\u00FC" => "u" + +# ý => y +"\u00FD" => "y" + +# ÿ => y +"\u00FF" => "y" + +# ff => ff +"\uFB00" => "ff" + +# ï¬ => fi +"\uFB01" => "fi" + +# fl => fl +"\uFB02" => "fl" + +# ffi => ffi +"\uFB03" => "ffi" + +# ffl => ffl +"\uFB04" => "ffl" + +# ſt => ft +"\uFB05" => "ft" + +# st => st +"\uFB06" => "st" diff --git a/docs/solr_config/conf/protwords.txt b/docs/solr_config/conf/protwords.txt new file mode 100644 index 0000000..1dfc0ab --- /dev/null +++ b/docs/solr_config/conf/protwords.txt @@ -0,0 +1,21 @@ +# The ASF licenses this file to You 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. + +#----------------------------------------------------------------------- +# Use a protected word file to protect against the stemmer reducing two +# unrelated words to the same base word. + +# Some non-words that normally won't be encountered, +# just to test that they won't be stemmed. +dontstems +zwhacky + diff --git a/docs/solr_config/conf/schema.xml b/docs/solr_config/conf/schema.xml new file mode 100644 index 0000000..1a4a692 --- /dev/null +++ b/docs/solr_config/conf/schema.xml @@ -0,0 +1,436 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + uuid + + text + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/solr_config/conf/scripts.conf b/docs/solr_config/conf/scripts.conf new file mode 100644 index 0000000..f58b262 --- /dev/null +++ b/docs/solr_config/conf/scripts.conf @@ -0,0 +1,24 @@ +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You 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. + +user= +solr_hostname=localhost +solr_port=8983 +rsyncd_port=18983 +data_dir= +webapp_name=solr +master_host= +master_data_dir= +master_status_dir= diff --git a/docs/solr_config/conf/solrconfig.xml b/docs/solr_config/conf/solrconfig.xml new file mode 100644 index 0000000..4a87355 --- /dev/null +++ b/docs/solr_config/conf/solrconfig.xml @@ -0,0 +1,1033 @@ + + + + + + ${solr.abortOnConfigurationError:true} + + + + + + + + + + + + + + + + ${solr.data.dir:./solr/data} + + + + + + false + + 10 + + + + + 32 + + 10000 + 1000 + 10000 + + + + + + + + + + + + + native + + + + + + + false + 32 + 10 + + + + + + + + false + + + true + + + + + + + + 1 + + 0 + + + + + false + + + + + + + + + + + + + + + + + + + + + + + + + + + 1024 + + + + + + + + + + + + + + + + true + + + + + + + + 20 + + + 200 + + + + + + + + + + + + + solr rocks010 + static firstSearcher warming query from solrconfig.xml + + + + + false + + + 2 + + + + + + + + + + + + + + + + + + + + + + + explicit + + + + + + + + + + + + + dismax + explicit + 0.01 + + text^0.5 features^1.0 name^1.2 sku^1.5 id^10.0 manu^1.1 cat^1.4 + + + text^0.2 features^1.1 name^1.5 manu^1.4 manu_exact^1.9 + + + popularity^0.5 recip(price,1,1000,1000)^0.3 + + + id,name,price,score + + + 2<-1 5<-2 6<90% + + 100 + *:* + + text features name + + 0 + + name + regex + + + + + + + dismax + explicit + text^0.5 features^1.0 name^1.2 sku^1.5 id^10.0 + 2<-1 5<-2 6<90% + + incubationdate_dt:[* TO NOW/DAY-1MONTH]^2.2 + + + + inStock:true + + + + cat + manu_exact + price:[* TO 500] + price:[500 TO *] + + + + + + + + + + textSpell + + + default + name + ./spellchecker + + + + + + + + + + + + false + + false + + 1 + + + spellcheck + + + + + + + + true + + + tvComponent + + + + + + + + + default + + org.carrot2.clustering.lingo.LingoClusteringAlgorithm + + 20 + + + stc + org.carrot2.clustering.stc.STCClusteringAlgorithm + + + + + true + default + true + + name + id + + features + + true + + + + false + + + clusteringComponent + + + + + + + + text + true + ignored_ + + + true + links + ignored_ + + + + + + + + + + true + + + termsComponent + + + + + + + + string + elevate.xml + + + + + + explicit + + + elevator + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + standard + solrpingquery + all + + + + + + + explicit + true + + + + + + + + + 100 + + + + + + + + 70 + + 0.5 + + [-\w ,/\n\"']{20,200} + + + + + + + ]]> + ]]> + + + + + + + + + + + + + 5 + + + + + + + + + + solr + + + + + diff --git a/docs/solr_config/conf/spellings.txt b/docs/solr_config/conf/spellings.txt new file mode 100644 index 0000000..d7ede6f --- /dev/null +++ b/docs/solr_config/conf/spellings.txt @@ -0,0 +1,2 @@ +pizza +history \ No newline at end of file diff --git a/docs/solr_config/conf/stopwords.txt b/docs/solr_config/conf/stopwords.txt new file mode 100644 index 0000000..b5824da --- /dev/null +++ b/docs/solr_config/conf/stopwords.txt @@ -0,0 +1,58 @@ +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You 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. + +#----------------------------------------------------------------------- +# a couple of test stopwords to test that the words are really being +# configured from this file: +stopworda +stopwordb + +#Standard english stop words taken from Lucene's StopAnalyzer +a +an +and +are +as +at +be +but +by +for +if +in +into +is +it +no +not +of +on +or +s +such +t +that +the +their +then +there +these +they +this +to +was +will +with + diff --git a/docs/solr_config/conf/synonyms.txt b/docs/solr_config/conf/synonyms.txt new file mode 100644 index 0000000..b0e31cb --- /dev/null +++ b/docs/solr_config/conf/synonyms.txt @@ -0,0 +1,31 @@ +# The ASF licenses this file to You 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. + +#----------------------------------------------------------------------- +#some test synonym mappings unlikely to appear in real input text +aaa => aaaa +bbb => bbbb1 bbbb2 +ccc => cccc1,cccc2 +a\=>a => b\=>b +a\,a => b\,b +fooaaa,baraaa,bazaaa + +# Some synonym groups specific to this example +GB,gib,gigabyte,gigabytes +MB,mib,megabyte,megabytes +Television, Televisions, TV, TVs +#notice we use "gib" instead of "GiB" so any WordDelimiterFilter coming +#after us won't split it into two words. + +# Synonym mappings can be used for spelling correction too +pixima => pixma + diff --git a/docs/solr_config/conf/xslt/example.xsl b/docs/solr_config/conf/xslt/example.xsl new file mode 100644 index 0000000..6832a1d --- /dev/null +++ b/docs/solr_config/conf/xslt/example.xsl @@ -0,0 +1,132 @@ + + + + + + + + + + + + + + + <xsl:value-of select="$title"/> + + + +

+
+ This has been formatted by the sample "example.xsl" transform - + use your own XSLT to get a nicer page +
+ + + +
+ + + +
+ + + + +
+
+
+ + + + + + + + + + + + + + javascript:toggle("");? +
+ + exp + + + + + +
+ + +
+ + + + + + + +
    + +
  • +
    +
+ + +
+ + + + + + + + + + + + + + + + + + + + +
diff --git a/docs/solr_config/conf/xslt/example_atom.xsl b/docs/solr_config/conf/xslt/example_atom.xsl new file mode 100644 index 0000000..e1c7d5a --- /dev/null +++ b/docs/solr_config/conf/xslt/example_atom.xsl @@ -0,0 +1,67 @@ + + + + + + + + + + + + + + Example Solr Atom 1.0 Feed + + This has been formatted by the sample "example_atom.xsl" transform - + use your own XSLT to get a nicer Atom feed. + + + Apache Solr + solr-user@lucene.apache.org + + + + + + tag:localhost,2007:example + + + + + + + + + <xsl:value-of select="str[@name='name']"/> + + tag:localhost,2007: + + + + + + diff --git a/docs/solr_config/conf/xslt/example_rss.xsl b/docs/solr_config/conf/xslt/example_rss.xsl new file mode 100644 index 0000000..3e09e65 --- /dev/null +++ b/docs/solr_config/conf/xslt/example_rss.xsl @@ -0,0 +1,66 @@ + + + + + + + + + + + + + Example Solr RSS 2.0 Feed + http://localhost:8983/solr + + This has been formatted by the sample "example_rss.xsl" transform - + use your own XSLT to get a nicer RSS feed. + + en-us + http://localhost:8983/solr + + + + + + + + + + + <xsl:value-of select="str[@name='name']"/> + + http://localhost:8983/solr/select?q=id: + + + + + + + http://localhost:8983/solr/select?q=id: + + + + diff --git a/docs/solr_config/conf/xslt/luke.xsl b/docs/solr_config/conf/xslt/luke.xsl new file mode 100644 index 0000000..6e9a064 --- /dev/null +++ b/docs/solr_config/conf/xslt/luke.xsl @@ -0,0 +1,337 @@ + + + + + + + + + Solr Luke Request Handler Response + + + + + + + + + <xsl:value-of select="$title"/> + + + + + +

+ +

+
+ +
+ +

Index Statistics

+ +
+ +

Field Statistics

+ + + +

Document statistics

+ + + + +
+ + + + + +
+ +
+ + +
+ +
+ +
+
+
+ + + + + + + + + + + + + + + + + + + + + +
+

+ +

+ +
+ +
+
+
+ + +
+ + 50 + 800 + 160 + blue + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ background-color: ; width: px; height: px; +
+
+ +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
  • + +
  • +
    +
+ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + + + + + + + + - + + - + + - + + - + + - + + - + + - + + - + + - + + - + + - + + - + + - + + + + + + + + + + + + + + + + + +
diff --git a/docs/usingDatabase-databankauth.txt b/docs/usingDatabase-databankauth.txt new file mode 100644 index 0000000..e8b7828 --- /dev/null +++ b/docs/usingDatabase-databankauth.txt @@ -0,0 +1,134 @@ +Note: If you have forgotten the password for the root user, you can rest the password bu running dpkg-reconfigure +$ dpkg -l mysql-server* | grep ii +$ dpkg-configure mysql-server-5.1 +$ sudo dpkg-reconfigure mysql-server-5.1 + +$ mysql -u root -p +same as sudo password + +#Create Database databankauth and user databanksqladmin. Give user databanksqladmin access to databankauth +mysql> use mysql; +mysql> CREATE DATABASE databankauth DEFAULT CHARACTER SET utf8 COLLATE utf8_bin; +mysql> GRANT ALL ON databankauth.* TO databanksqladmin@localhost IDENTIFIED BY 'password'; +mysql> exit + +$ mysql -h localhost -u databanksqladmin -p +password: ###### + +mysql> use databankauth; + +mysql> show tables; + +mysql> select * from silo; ++----+------------------------+----------+ +| id | group_name | silo | ++----+------------------------+----------+ +| 1 | sandbox_administrator | sandbox | +| 2 | sandbox_manager | sandbox | +| 3 | sandbox_submitter | sandbox | +| 4 | sandbox2_administrator | sandbox2 | +| 5 | sandbox2_manager | sandbox2 | +| 6 | sandbox2_submitter | sandbox2 | +| 7 | sandbox3_administrator | sandbox3 | +| 8 | sandbox3_manager | sandbox3 | +| 9 | sandbox3_submitter | sandbox3 | ++----+------------------------+----------+ + +mysql> select * from permission; ++----+-----------------+ +| id | permission_name | ++----+-----------------+ +| 1 | administrator | +| 2 | manager | +| 3 | submitter | ++----+-----------------+ +3 rows in set (0.00 sec) + +mysql> select * from user; ++----+------------------+----------+-------+------------------------+-----------+----------+ +| id | user_name | password | email | name | firstname | lastname | ++----+------------------+----------+-------+------------------------+-----------+----------+ +| 1 | sandbox_user | 2de12713 | NULL | Test User I | NULL | NULL | +| 2 | sandbox_user2 | aa585d66 | NULL | Test User II | NULL | NULL | +| 3 | sandbox_user3 | 8ad67e77 | NULL | Test User III | NULL | NULL | +| 4 | admin | 6f0006ba | NULL | Test Administrator I | NULL | NULL | +| 5 | admin2 | 0b62a04c | NULL | Test Administrator II | NULL | NULL | +| 6 | admin3 | 33bd9bbd | NULL | Test Administrator III | NULL | NULL | +| 7 | sandbox_manager | bc6592d3 | NULL | Test Manager I | NULL | NULL | +| 8 | sandbox_manager2 | 9c7643eb | NULL | Test Manager II | NULL | NULL | +| 9 | sandbox_manager3 | 7f000410 | NULL | Test Manager III | NULL | NULL | ++----+------------------+----------+-------+------------------------+-----------+----------+ +9 rows in set (0.00 sec) + +mysql> select * from user_group; ++---------+----------+ +| user_id | group_id | ++---------+----------+ +| 1 | 3 | +| 2 | 6 | +| 3 | 9 | +| 4 | 1 | +| 5 | 4 | +| 6 | 7 | +| 7 | 2 | +| 8 | 5 | +| 9 | 8 | ++---------+----------+ +9 rows in set (0.00 sec) + +mysql> select * from group_permission; ++----------+---------------+ +| group_id | permission_id | ++----------+---------------+ +| 1 | 1 | +| 2 | 2 | +| 3 | 3 | +| 4 | 1 | +| 5 | 2 | +| 6 | 3 | +| 7 | 1 | +| 8 | 2 | +| 9 | 3 | ++----------+---------------+ +9 rows in set (0.00 sec) + +mysql> SELECT ug.user_id, ug.group_id, gp.permission_id +FROM user_group ug +INNER JOIN group_permission on ug.group_id = gp.group_id ++---------+----------+---------------+ +| user_id | group_id | permission_id | ++---------+----------+---------------+ +| 1 | 3 | 3 | +| 2 | 6 | 3 | +| 3 | 9 | 3 | +| 4 | 1 | 1 | +| 5 | 4 | 1 | +| 6 | 7 | 1 | +| 7 | 2 | 2 | +| 8 | 5 | 2 | +| 9 | 8 | 2 | ++---------+----------+---------------+ +9 rows in set (0.00 sec) + +mysql> SELECT u.user_name, g.silo, p.permission_name +FROM user u +INNER JOIN user_group ug ON u.id = ug.user_id +INNER JOIN silo g ON ug.group_id = g.id +INNER JOIN group_permission gp ON g.id = gp.group_id +INNER JOIN permission p ON gp.permission_id = p.id; ++------------------+----------+-----------------+ +| user_name | silo | permission_name | ++------------------+----------+-----------------+ +| admin | sandbox | administrator | +| admin2 | sandbox2 | administrator | +| admin3 | sandbox3 | administrator | +| sandbox_manager | sandbox | manager | +| sandbox_manager2 | sandbox2 | manager | +| sandbox_manager3 | sandbox3 | manager | +| sandbox_user | sandbox | submitter | +| sandbox_user2 | sandbox2 | submitter | +| sandbox_user3 | sandbox3 | submitter | ++------------------+----------+-----------------+ +9 rows in set (0.00 sec) + + diff --git a/docs/using_databank_api/DatabankDemo.py b/docs/using_databank_api/DatabankDemo.py new file mode 100644 index 0000000..4519576 --- /dev/null +++ b/docs/using_databank_api/DatabankDemo.py @@ -0,0 +1,54 @@ +#Databank API demo + +import urllib2 +import base64 +import urllib +from lib.multipartform import MultiPartForm + +#=============================================================================== +#Using urllib2 to create a package in Databank +url = "http://databank-vm1.oerc.ox.ac.uk/test/datasets" +req = urllib2.Request(url) +USER = "admin" +PASS = "test" +identifier = "TestSubmission" +auth = 'Basic ' + base64.urlsafe_b64encode("%s:%s" % (USER, PASS)) +req.add_header('Authorization', auth) +req.add_header('Accept', 'application/JSON') +req.add_data(urllib.urlencode({'id': identifier})) + +# To verify the method is POST +req.get_method() + +ans = urllib2.urlopen(req) + +ans.read() +ans.msg +ans.code + +#=============================================================================== +#Using urllib2 to post a file in Databank +#Add a file +form = MultiPartForm() +filename = "solrconfig.xml" +filepath = "data/unicode07.xml" +form.add_file('file', filename, fileHandle=open(filepath)) + +# Build the request +url2 = "http://databank-vm1.oerc.ox.ac.uk/test/datasets/TestSubmission" +req2 = urllib2.Request(url2) +auth = 'Basic ' + base64.urlsafe_b64encode("admin:test") +req2.add_header('Authorization', auth) +req2.add_header('Accept', 'application/JSON') +body = str(form) +req2.add_header('Content-type', form.get_content_type()) +req2.add_header('Content-length', len(body)) +req2.add_data(body) + +print +print 'OUTGOING DATA:' +print req2.get_data() +ans2 = urllib2.urlopen(req2) +print +print 'SERVER RESPONSE:' +ans2.read() diff --git a/docs/using_databank_api/README b/docs/using_databank_api/README new file mode 100644 index 0000000..d5ef618 --- /dev/null +++ b/docs/using_databank_api/README @@ -0,0 +1,32 @@ +Guide to using the RDFDatabank API + +The directory contains example python code that services can use for interacting with the Databank API. +There are two sets of examples +1. main.py + This uses the helper class HTTP_request.py which in turn uses httplib to make http calls. + It has code on using GET, POST, PUT and DELETE methods for interacting with the API and works over both http / https. + +2. DatabankDemo.py or postingToDatabank.py + This is exmaple code to POST items to Databank using urllib2. + The form data is constructed is slightly differently in DatabankDemo.py and postingToDatabank.py which can be seen in multipartform.py and multipart.py respectively. + +RDFDatabank was developed by Ben O'Steen and Anusha Ranganathan at the Bodleian Libraries, Universiy of Oxford + +The purpose of DataBank is to provide a robust and efficient system for the safe storage of and open access to research data. + +The API documentation for using RDFdatabank is at +http://databank-vm1.oerc.ox.ac.uk/api or +https://github.com/databank/RDFDatabank/tree/master/rdfdatabank/public/static/api_files + +The source code for databank is available at https://github.com/dataflow/RDFDatabank. +The test code for databank can be found in https://github.com/dataflow/RDFDatabank/tree/master/rdfdatabank/tests/RDFDatabank + +Note: + DataBank is not intended to store large-scale data sets such as grid data or other vast data sets. + + All of the metadata relating to the dataset is in the file manifest.rdf. The system generated metadata will contain a listing of the files in the dataset, date of submission and the current version + + If the dataset contains a README file in the top level (i.e. not within a folder), the contents of this file will be displayed at the top of the page for that dataset within databank.ora.ox.ac.uk + + If the dataset contains a file called 'manifest.rdf' and it is valid rdf, the metadata in this file will be merged with the system geenrated metadata, when the dataset is unpacked within databnak.ora.ox.ac.uk + diff --git a/rdfdatabank/tests/functional/__init__.py b/docs/using_databank_api/__init__.py similarity index 100% rename from rdfdatabank/tests/functional/__init__.py rename to docs/using_databank_api/__init__.py diff --git a/docs/using_databank_api/data/testrdf.zip b/docs/using_databank_api/data/testrdf.zip new file mode 100644 index 0000000..a52cdce Binary files /dev/null and b/docs/using_databank_api/data/testrdf.zip differ diff --git a/docs/using_databank_api/data/testrdf4.zip b/docs/using_databank_api/data/testrdf4.zip new file mode 100644 index 0000000..376bd34 Binary files /dev/null and b/docs/using_databank_api/data/testrdf4.zip differ diff --git a/docs/using_databank_api/data/unicode07.xml b/docs/using_databank_api/data/unicode07.xml new file mode 100644 index 0000000..cd7a87c --- /dev/null +++ b/docs/using_databank_api/data/unicode07.xml @@ -0,0 +1,29 @@ + + + + Some verses in Sanskrit + The following is one stanza of canto â…¥ of the KumÄra-saṃbhava (“the birth of KumÄraâ€) by the great Sanskrit poet KÄlidÄsa: <br> + <br> + पशà¥à¤ªà¤¤à¤¿à¤°à¤ªà¤¿ तानà¥à¤¯à¤¹à¤¾à¤¨à¤¿ कृचà¥à¤›à¥à¤°à¤¾à¤¦à¥ <br> + अगमयददà¥à¤°à¤¿à¤¸à¥à¤¤à¤¾à¤¸à¤®à¤¾à¤—मोतà¥à¤•à¤ƒ । <br> + कमपरमवशं न विपà¥à¤°à¤•à¥à¤°à¥à¤¯à¥à¤°à¥ <br> + विभà¥à¤®à¤ªà¤¿ तं यदमी सà¥à¤ªà¥ƒà¤¶à¤¨à¥à¤¤à¤¿ भावाः ॥ <br> + <br> +And here is the transcription of it: <br> + <br> + PaÅ›upatirapi tÄnyahÄni ká¹›cchrÄd <br> + agamayadadrisutÄsamÄgamotkaḥ; <br> + kamaparamavaÅ›aṃ na viprakuryur <br> + vibhumapi taṃ yadamÄ« spṛśanti bhÄvÄḥ? <br> + <br> +A rough translation might be: <br> + <br> + And PaÅ›upati passed those days with hardship, / eager for union with the daughter of the mountain. / Which other powerless [creature] would they not torment, / such emotions, when they affect even the powerful [Åšiva]? + http://www.madore.org/~david/misc/unitest/ + + diff --git a/docs/using_databank_api/lib/HTTP_request.py b/docs/using_databank_api/lib/HTTP_request.py new file mode 100644 index 0000000..52d21fd --- /dev/null +++ b/docs/using_databank_api/lib/HTTP_request.py @@ -0,0 +1,190 @@ +""" +Copyright (c) 2012 University of Oxford + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, --INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +""" + +import logging +import mimetypes +import httplib +import base64 +import urlparse +import json as simplejson + +logger = logging.getLogger('Dataset') + +class HTTPRequest(): + def __init__(self, endpointhost=None): + if endpointhost: + self._endpointhost = endpointhost + self._endpointpath = None + + def get_content_type(self, filename): + # Originally copied from http://code.activestate.com/recipes/146306/: + return mimetypes.guess_type(filename)[0] or 'application/octet-stream' + + def get_data_type(self, params): + files = [] + fields = [] + decoded_params = params.items() + for i in decoded_params: + if len(i) == 2: + fields.append(i) + elif len(i) == 4: + files.append(i) + return fields, files + + def encode_multipart_formdata(self, fields, files): + # Originally copied from http://code.activestate.com/recipes/146306/: + """ + fields is a sequence of (name, value) elements for regular form fields. + files is a sequence of (name, filename, value, filetype) elements for data to be uploaded as files + Return (content_type, body) ready for httplib.HTTP instance + """ + BOUNDARY = '----------ThIs_Is_tHe_bouNdaRY_$' + CRLF = '\r\n' + L = [] + for (key, value) in fields: + L.append('--' + BOUNDARY) + L.append('Content-Disposition: form-data; name="%s"' % key) + L.append('') + L.append(value) + for (key, filename, value, filetype) in files: + L.append('--' + BOUNDARY) + L.append('Content-Disposition: form-data; name="%s"; filename="%s"' % (key, filename)) + L.append('Content-Type: %s' % (filetype or get_content_type(filename))) + L.append('') + L.append(value) + L.append('--' + BOUNDARY + '--') + L.append('') + body = CRLF.join(L) + content_type = 'multipart/form-data; boundary=%s' % BOUNDARY + return content_type, body + + def setRequestEndPoint(self, endpointhost=None, endpointpath=None): + if endpointhost or endpointpath: + if endpointhost: + self._endpointhost = endpointhost + # Reset credentials if setting host + self._endpointuser = None + self._endpointpass = None + logger.debug("setRequestEndPoint: endpointhost %s: " % self._endpointhost) + if endpointpath: + self._endpointpath = endpointpath + logger.debug("setRequestEndPoint: endpointpath %s: " % self._endpointpath) + return + + def setRequestUserPass(self, endpointuser=None, endpointpass=None): + if endpointuser: + self._endpointuser = endpointuser + self._endpointpass = endpointpass + logger.debug("setRequestEndPoint: endpointuser %s: " % self._endpointuser) + logger.debug("setRequestEndPoint: endpointpass %s: " % self._endpointpass) + else: + self._endpointuser = None + self._endpointpass = None + return + + def getRequestPath(self, rel): + rel = rel or "" + return urlparse.urljoin(self._endpointpath,rel) + + def getRequestUri(self, rel): + return "http://"+self._endpointhost+self.getRequestPath(rel) + + def encodeFormData(self, params): + (fields, files) = self.get_data_type(params) + (reqtype, reqdata) = self.encode_multipart_formdata(fields, files) + return reqtype, reqdata + + def doRequest(self, command, resource, reqdata=None, reqheaders={}): + #print "User:", self._endpointuser + #print "Host:", self._endpointhost + #print "Resource:", resource + if self._endpointuser: + auth = base64.encodestring("%s:%s" % (self._endpointuser, self._endpointpass)).strip() + reqheaders["Authorization"] = "Basic %s" % auth + hc = httplib.HTTPConnection(self._endpointhost) + #hc = httplib.HTTPSConnection(self._endpointhost) + #resource = self.getRequestPath(resource) + response = None + responsedata = None + repeat = 10 + while resource and repeat > 0: + repeat -= 1 + hc.request(command, resource, reqdata, reqheaders) + response = hc.getresponse() + if response.status != 301: break + path = response.getheader('Location', None) + if path[0:6] == "https:": + # close old connection, create new HTTPS connection + hc.close() + hc = httplib.HTTPSConnection(self._endpointhost) # Assume same host for https: + else: + response.read() # Seems to be needed to free up connection for new request + logger.debug("Status: %i %s" % (response.status, response.reason)) + responsedata = response.read() + #print "Response data", responsedata + #print "Response status", response.status + #print "Response reason", response.reason + hc.close() + return (response, responsedata) + + def doHTTP_GET(self, endpointhost=None, endpointpath=None, resource=None, expect_type="*/*"): + reqheaders = { + "Accept": expect_type + } + self.setRequestEndPoint(endpointhost, endpointpath) + (response, responsedata) = self.doRequest("GET", resource, reqheaders=reqheaders) + #ctype = response.getheader('content-type') + #if (responsedata and expect_type.lower() == "application/json"): responsedata = simplejson.loads(responsedata) + #if (responsedata and "application/json" in ctype): responsedata = simplejson.loads(responsedata) + return (response, responsedata) + + def doHTTP_POST(self, data, data_type="application/octet-strem", + endpointhost=None, endpointpath=None, resource=None, expect_type="*/*"): + reqheaders = { + "Content-type": data_type, + "Accept": expect_type + } + self.setRequestEndPoint(endpointhost, endpointpath) + (response, responsedata) = self.doRequest("POST", resource, reqdata=data, reqheaders=reqheaders) + #ctype = response.getheader('content-type') + #if (responsedata and expect_type.lower() == "application/json"): responsedata = simplejson.loads(responsedata) + #if (responsedata and "application/json" in ctype): responsedata = simplejson.loads(responsedata) + return (response, responsedata) + + def doHTTP_PUT(self, data, data_type="application/octet-strem", + endpointhost=None, endpointpath=None, resource=None, expect_type="*/*"): + reqheaders = { + "Content-type": data_type, + "Accept": expect_type + } + self.setRequestEndPoint(endpointhost, endpointpath) + (response, responsedata) = self.doRequest("PUT", resource, reqdata=data, reqheaders=reqheaders) + #ctype = response.getheader('content-type') + #if (responsedata and "application/json" in ctype): responsedata = simplejson.loads(responsedata) + return (response, responsedata) + + def doHTTP_DELETE(self, endpointhost=None, endpointpath=None, resource=None): + self.setRequestEndPoint(endpointhost, endpointpath) + (response, responsedata) = self.doRequest("DELETE", resource) + return (response, responsedata) + diff --git a/rdfdatabank/tests/test_models.py b/docs/using_databank_api/lib/__init__.py similarity index 100% rename from rdfdatabank/tests/test_models.py rename to docs/using_databank_api/lib/__init__.py diff --git a/docs/using_databank_api/lib/multipart.py b/docs/using_databank_api/lib/multipart.py new file mode 100644 index 0000000..14aa3e7 --- /dev/null +++ b/docs/using_databank_api/lib/multipart.py @@ -0,0 +1,78 @@ +# --------------------------------------------------------------------- +# +# Copyright (c) 2012 University of Oxford +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, --INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# +# --------------------------------------------------------------------- + +import mimetools +import mimetypes + +class MultiPartFormData(object): + def __init__(self, fields=None, files=None): + self._boundary = mimetools.choose_boundary() + self._fields = fields or () + self._files = files or () + for file in self._files: + file['mimetype'] = file.get('mimetype') or mimetypes.guess_type(file['filename'])[0] or 'application/octet-stream' + self._body = self._body_iterator() + + @property + def content_type(self): + return 'multipart/form-data; boundary=%s' % self._boundary + + @property + def content_length(self): + field_padding = '--\r\nContent-Disposition: form-data; name=""\r\n\r\n\r\n' + file_padding = '--\r\nContent-Disposition: form-data; name=""; filename=""\r\nContent-Type: \r\n\r\n' + + field_length = sum(sum(map(len, [self._boundary, field_padding, k, v])) for k,v in self._fields) + file_length = sum(f['size'] + sum(map(len, [self._boundary, file_padding, f['name'], f['filename'], f['mimetype']])) for f in self._files) + + return field_length + file_length + len('----\r\n') + len(self._boundary) + + def _body_iterator(self): + for (key, value) in self._fields: + yield '--%s\r\n' % self._boundary + yield 'Content-Disposition: form-data; name="%s"\r\n' % key + yield '\r\n' + if value: + yield value + yield '\r\n' + for file in self._files: + yield '--%s\r\n' % self._boundary + yield 'Content-Disposition: form-data; name="%s"; filename="%s"\r\n' % (file['name'], file['filename']) + yield 'Content-Type: %s\r\n' % file['mimetype'] + yield '\r\n' + + stream = file['stream'] + while True: + data = stream.read(4096) + if not data: + break + yield data + yield '--%s--\r\n' % self._boundary + + def read(self, blocksize): + try: + return self._body.next() + except StopIteration: + return '' diff --git a/docs/using_databank_api/lib/multipartform.py b/docs/using_databank_api/lib/multipartform.py new file mode 100644 index 0000000..11a301d --- /dev/null +++ b/docs/using_databank_api/lib/multipartform.py @@ -0,0 +1,66 @@ +import itertools +import mimetools +import mimetypes + +class MultiPartForm(object): + """Accumulate the data to be used when posting a form.""" + + def __init__(self): + self.form_fields = [] + self.files = [] + self.boundary = mimetools.choose_boundary() + return + + def get_content_type(self): + return 'multipart/form-data; boundary=%s' % self.boundary + + def add_field(self, name, value): + """Add a simple field to the form data.""" + self.form_fields.append((name, value)) + return + + def add_file(self, fieldname, filename, fileHandle, mimetype=None): + """Add a file to be uploaded.""" + body = fileHandle.read() + if mimetype is None: + mimetype = mimetypes.guess_type(filename)[0] or 'application/octet-stream' + self.files.append((fieldname, filename, mimetype, body)) + return + + def __str__(self): + """Return a string representing the form data, including attached files.""" + # Build a list of lists, each containing "lines" of the + # request. Each part is separated by a boundary string. + # Once the list is built, return a string where each + # line is separated by '\r\n'. + parts = [] + part_boundary = '--' + self.boundary + + # Add the form fields + parts.extend( + [ part_boundary, + 'Content-Disposition: form-data; name="%s"' % name, + '', + value, + ] + for name, value in self.form_fields + ) + + # Add the files to upload + parts.extend( + [ part_boundary, + 'Content-Disposition: file; name="%s"; filename="%s"' % \ + (field_name, filename), + 'Content-Type: %s' % content_type, + '', + body, + ] + for field_name, filename, content_type, body in self.files + ) + + # Flatten the list and add closing boundary marker, + # then return CR+LF separated data + flattened = list(itertools.chain(*parts)) + flattened.append('--' + self.boundary + '--') + flattened.append('') + return '\r\n'.join(flattened) \ No newline at end of file diff --git a/docs/using_databank_api/main.py b/docs/using_databank_api/main.py new file mode 100644 index 0000000..6a904df --- /dev/null +++ b/docs/using_databank_api/main.py @@ -0,0 +1,175 @@ +""" +Copyright (c) 2012 University of Oxford + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, --INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +""" + +# Using the databank API + +""" +Below is a guide on how to do HTTP GET, POST, PUT and DELETE in python. +To run the pyhon code here, you would also need the file HTTP_request.py. + +The full functionality of RDFDatabank is detailed in the API documentation at +http://databank.ora.ox.ac.uk/api +https://github.com/dataflow/RDFDatabank/tree/master/rdfdatabank/public/static/api_files +""" + +import json as simplejson +from lib.HTTP_request import HTTPRequest + +#--CONFIG------------------------------------------------------- +host = 'databank-vm1.oerc.ox.ac.uk' +user_name = 'admin' +password = 'test' +datastore = HTTPRequest(endpointhost=host) +datastore.setRequestUserPass(endpointuser=user_name, endpointpass=password) + +#--HTTP GET------------------------------------------------------- +#Get a list of silos accessible to the user +(resp, respdata) = datastore.doHTTP_GET(resource="/silos", expect_type="application/JSON") +print "Get list of silos" +print resp.status, resp.reason +if resp.status >= 200 and resp.status < 300: + silos_list = simplejson.loads(respdata) + print "number of silos", len(silos_list) +print "-"*40, "\n\n" + +#--HTTP GET------------------------------------------------------- +#Get a list of all the datasets in the silo 'sandbox' +(resp, respdata) = datastore.doHTTP_GET(resource="/sandbox", expect_type="application/JSON") +print "Get list of datasets" +print resp.status, resp.reason +if resp.status >= 200 and resp.status < 300: + dataset_list = simplejson.loads(respdata) + print "number of datasets", len(dataset_list.keys()) +else: + print "Error getting list of datasets" +print "-"*40, "\n\n" + +#--HTTP DELETE------------------------------------------------------- +#Delete the dataset 'TestSubmission' in the silo 'sandbox' +(resp, respdata) = datastore.doHTTP_DELETE(resource="/sandbox/datasets/TestSubmission") +print "deleting dataset" +print resp.status, resp.reason +print respdata +print "-"*40, "\n\n" + +#--HTTP POST------------------------------------------------------- +#Create a new dataset 'TestSubmission' in the silo 'sandbox' +fields = [ + ("id", "TestSubmission") +] +files =[] +(reqtype, reqdata) = datastore.encode_multipart_formdata(fields, files) +(resp, respdata) = datastore.doHTTP_POST(reqdata, data_type=reqtype, resource="/sandbox/datasets", expect_type="application/JSON") +print "Create new dataset" +print resp.status, resp.reason +if resp.status >= 200 and resp.status < 300: + print respdata +else: + print "Error creating dataset" +print "-"*40, "\n\n" + +#--HTTP POST------------------------------------------------------- +#Upload file to dataset - POST file to dataset 'TestSubmission' in silo 'sandbox' (path is /sandbox/datasets/TestSubmission) +file_name="testrdf4.zip" +file_path="data/testrdf4.zip" +fields = [] +zipdata = open(file_path).read() +files = [ + ("file", file_name, zipdata, "application/zip") +] +(reqtype, reqdata) = datastore.encode_multipart_formdata(fields, files) +(resp, respdata) = datastore.doHTTP_POST(reqdata, data_type=reqtype, resource="/sandbox/datasets/TestSubmission", expect_type="application/JSON") +print "Post file testrdf4.zip to dataset" +print resp.status, resp.reason +if resp.status >= 200 and resp.status < 300: + print respdata +else: + print "Error posting file to dataset" +print "-"*40, "\n\n" + +#--HTTP POST------------------------------------------------------- +#Upload file to dataset and test munging. POST file to dataset 'TestSubmission' in silo 'sandbox' (path is /sandbox/datasets/TestSubmission) +#file_name="unicode07.xml" +file_name="manifest.rdf" +file_path="data/unicode07.xml" +fields = [] +zipdata = open(file_path).read() +files = [ + ("file", file_name, zipdata, "application/rdf+xml") +] +(reqtype, reqdata) = datastore.encode_multipart_formdata(fields, files) +(resp, respdata) = datastore.doHTTP_POST(reqdata, data_type=reqtype, resource="/sandbox/datasets/TestSubmission", expect_type="application/JSON") +print "Post file unicode07.xml to dataset" +print resp.status, resp.reason +if resp.status >= 200 and resp.status < 300: + print respdata +else: + print "Error posting file to dataset" +print "-"*40, "\n\n" + +#--HTTP PUT------------------------------------------------------- +#example metadata constructed in rdf. Add this metadata to the manifest (PUT this in manifest.rdf file) +metadata_content = """ + + Test dataset + Carl Sagan + abstract + +""" + +(resp, respdata) = datastore.doHTTP_PUT(metadata_content, resource="/sandbox/datasets/TestSubmission/manifest.rdf", expect_type="text/plain") +print "Putting manifest data into dataset" +print resp.status, resp.reason +if resp.status >= 200 and resp.status < 300: + print respdata +else: + print "Error putting manifest data into dataset" +print "-"*40, "\n\n" + +#--HTTP POST------------------------------------------------------- +#Unpack zip file in dataset +file_name="testrdf4.zip" +fields = [] +fields = [ + ("filename", "testrdf4.zip"), + ("id", "TestSubmission") +] +zipdata = open(file_path).read() +files = [] +(reqtype, reqdata) = datastore.encode_multipart_formdata(fields, files) +(resp, respdata) = datastore.doHTTP_POST(reqdata, data_type=reqtype, resource="/sandbox/items/TestSubmission", expect_type="application/JSON") +print "Post file testrdf4.zip to dataset for unpacking" +print resp.status, resp.reason +if resp.status >= 200 and resp.status < 300: + print respdata +else: + print "Error unpacking file to dataset" +print "-"*40, "\n\n" + +#--------------------------------------------------------- + diff --git a/docs/using_databank_api/postingToDatabank.py b/docs/using_databank_api/postingToDatabank.py new file mode 100644 index 0000000..095f8b2 --- /dev/null +++ b/docs/using_databank_api/postingToDatabank.py @@ -0,0 +1,65 @@ +import urllib2 +import base64 +import urllib +from lib.multipart import MultiPartFormData +import os + +#=============================================================================== +#Using urllib2 to create a package in Databank +url = "http://databank-vm1.oerc.ox.ac.uk/test/datasets" +identifier = "TestSubmission" +req = urllib2.Request(url) +USER = "admin" +PASS = "test" +auth = 'Basic ' + base64.urlsafe_b64encode("%s:%s" % (USER, PASS)) +req.add_header('Authorization', auth) +req.add_header('Accept', 'application/JSON') +req.add_data(urllib.urlencode({'id': identifier})) + +# To verify the method is POST +req.get_method() + +ans = urllib2.urlopen(req) + +ans.read() +ans.msg +ans.code + +#=============================================================================== +#Using urllib2 to post a file +#Add a file + +filename = "solrconfig.xml" +filepath = "data/unicode07.xml" +f = open(filepath, 'rb') +stat_info = os.stat(filepath) + +file1_info = { + 'name':'file', + 'filename':filename, + 'mimetype':'application/xml', + 'stream': f, + 'size':int(stat_info.st_size)} + +data = MultiPartFormData(files=[file1_info]) + +# Build the request +url2 = "http://databank-vm1.oerc.ox.ac.uk/test/datasets/TestSubmission" +req2 = urllib2.Request(url2) +auth = 'Basic ' + base64.urlsafe_b64encode("admin:test") +req2.add_header('Authorization', auth) +req2.add_header('Accept', 'application/JSON') +req2.add_header('Content-type', data.content_type) +req2.add_header('Content-length', data.content_length) + +body = ''.join(list(data._body)) +req2.add_data(str(body)) + +#print +#print 'OUTGOING DATA:' +#print req2.get_data() +ans2 = urllib2.urlopen(req2) +#print +print 'SERVER RESPONSE:' +ans2.read() +#=============================================================================== diff --git a/docs/using_zipfile_library.py b/docs/using_zipfile_library.py new file mode 100644 index 0000000..9b31e8c --- /dev/null +++ b/docs/using_zipfile_library.py @@ -0,0 +1,123 @@ +from zipfile import ZipFile, BadZipfile as BZ +#================================================ +def read_zipfile(filepath): + try: + tmpfile = ZipFile(filepath, "r") + except BZ: + raise BadZipfile + zipfile_contents = {} + for info in tmpfile.infolist(): + zipfile_contents[info.filename] = (info.file_size, info.date_time) + tmpfile.close() + return zipfile_contents +#================================================ +def read_file_in_zipfile(filepath, filename): + try: + tmpfile = ZipFile(filepath, "r") + except BZ: + raise BadZipfile + try: + fileinfo = tmpfile.getinfo(filename) + except KeyError: + return False + if fileinfo.file_size == 0: + return 0 + file_contents = None + file_contents = tmpfile.read(filename) + tmpfile.close() + return file_contents +#================================================ +def get_file_in_zipfile(filepath, filename, targetdir): + try: + tmpfile = ZipFile(filepath, "r") + except BZ: + raise BadZipfile + try: + fileinfo = tmpfile.getinfo(filename) + except KeyError: + return False + if fileinfo.file_size == 0: + return 0 + targetfile = tmpfile.extract(filename, targetdir) + tmpfile.close() + return targetfile +#================================================ +path = 'silos/sandbox/pairtree_root/da/ta/se/t1/obj/__26/' +fp1 = path + 'test3.zip' +fp2 = path + 'read_test.zip' +fp3 = path + 'databank_logo.png' + +zc1 = read_zipfile(fp1) +zc2 = read_zipfile(fp2) +zc3 = read_zipfile(fp3) + +zc1_files = zc1.keys() +zc2_files = zc2.keys() + +ans11 = read_file_in_zipfile(fp1, zc1_files[1]) #expected: 0 +ans12 = read_file_in_zipfile(fp1, 'test') #expected: False +ans13 = read_file_in_zipfile(fp1, zc1_files[0]) #expected: file conts + +ans21 = read_file_in_zipfile(fp2, zc2_files[0]) #expected: file conts +ans22 = read_file_in_zipfile(fp2, zc2_files[1]) #expected: 0 +ans23 = read_file_in_zipfile(fp2, zc2_files[4]) #expected: binary output + +ans14 = get_file_in_zipfile(fp1, zc1_files[1], '/tmp') #expected: 0 +ans15 = get_file_in_zipfile(fp1, 'test', '/tmp') #expected: False +ans16 = get_file_in_zipfile(fp1, zc1_files[0], '/tmp') #expected: '/tmp/admiral-dataset.txt' + +ans24 = get_file_in_zipfile(fp2, zc2_files[0], '/tmp') #expected: '/tmp/read_test/Dir/TestScanFiles32.txt' +ans25 = get_file_in_zipfile(fp2, zc2_files[1], '/tmp') #expected: 0 +ans26 = get_file_in_zipfile(fp2, zc2_files[4], '/tmp') #expected: '/tmp/read_test/databank_logo.png' +#================================================ +#Expected Answers +""" +>>> zc1 +{'admiral-dataset.txt': (43, (2010, 11, 29, 16, 30, 52)), 'TestScanFilesSubDir/': (0, (2010, 11, 29, 17, 34, 42)), 'TestScanFilesSubDir/TestScanFiles31.txt': (9, (2010, 10, 4, 15, 39, 54)), 'TestScanFilesSubDir/TestScanFiles32.txt': (9, (2010, 10, 4, 15, 39, 54)), 'TestScanFilesSubDir/manifest.rdf': (511, (2010, 11, 29, 17, 42, 10))} + +>>> zc2 +{'read_test/Dir/TestScanFiles32.txt': (9, (2010, 10, 4, 15, 39, 54)), 'read_test/Dir/': (0, (2011, 1, 5, 13, 43, 30)), 'read_test/admiral-dataset.txt': (43, (2010, 11, 29, 16, 30, 52)), 'read_test/Dir/manifest.rdf': (511, (2010, 11, 29, 17, 42, 10)), 'read_test/databank_logo.png': (20220, (2010, 12, 6, 15, 11, 40)), 'read_test/': (0, (2011, 1, 5, 13, 44, 40)), 'read_test/Dir/TestScanFiles31.txt': (9, (2010, 10, 4, 15, 39, 54))} + +>>> zc1_files +['admiral-dataset.txt', 'TestScanFilesSubDir/', 'TestScanFilesSubDir/TestScanFiles31.txt', 'TestScanFilesSubDir/TestScanFiles32.txt', 'TestScanFilesSubDir/manifest.rdf'] + +>>> zc2_files +['read_test/Dir/TestScanFiles32.txt', 'read_test/Dir/', 'read_test/admiral-dataset.txt', 'read_test/Dir/manifest.rdf', 'read_test/databank_logo.png', 'read_test/', 'read_test/Dir/TestScanFiles31.txt'] + +>>> ans11 +0 + +>>> ans12 +False + +>>> ans13 +'This directory contains an ADMIRAL dataset\n' + +>>> ans21 +'Test file' + +>>> ans22 +0 + +>>> ans23 +'\x89PNG\..... + +>>> ans14 +0 + +>>> ans15 +False + +>>> ans16 +'/tmp/admiral-dataset.txt' + +>>> ans24 +'/tmp/read_test/Dir/TestScanFiles32.txt' + +>>> ans25 +0 + +>>> ans26 +'/tmp/read_test/databank_logo.png' +""" +#================================================ diff --git a/message_workers/LogConfigParser.py b/message_workers/LogConfigParser.py new file mode 100755 index 0000000..92b8531 --- /dev/null +++ b/message_workers/LogConfigParser.py @@ -0,0 +1,37 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +""" +Copyright (c) 2012 University of Oxford + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, --INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +""" + +import ConfigParser, os + +class Config(ConfigParser.ConfigParser): + DEFAULT_CONFIG_FILE = "loglines.cfg" + def __init__(self, config_file=DEFAULT_CONFIG_FILE): + ConfigParser.ConfigParser.__init__(self) + if os.path.exists(config_file) and os.path.isfile(config_file): + self.read(config_file) + self.validate() + + def validate(self): + pass diff --git a/message_workers/broker.py b/message_workers/broker.py new file mode 100755 index 0000000..6746857 --- /dev/null +++ b/message_workers/broker.py @@ -0,0 +1,86 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +""" +Copyright (c) 2012 University of Oxford + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, --INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +""" + +from redisqueue import RedisQueue + +from LogConfigParser import Config + +import sys + +from time import sleep + +if __name__ == "__main__": + c = Config() + redis_section = "redis" + worker_section = "worker_broker" + worker_number = sys.argv[1] + if len(sys.argv) == 3: + if "redis_%s" % sys.argv[2] in c.sections(): + redis_section = "redis_%s" % sys.argv[2] + + rq = RedisQueue(c.get(worker_section, "listento"), "broker_%s" % worker_number, + db=c.get(redis_section, "db"), + host=c.get(redis_section, "host"), + port=c.get(redis_section, "port") + ) + if c.has_option(worker_section, "fanout_status_queue"): + # keep a queue of messages to deliver for a given push'd item + # better resumeability at the cost of more redis operations + topushq = RedisQueue(c.get(worker_section, "fanout_status_queue"), "fanout_broker_%s" % worker_number, + db=c.get(redis_section, "db"), + host=c.get(redis_section, "host"), + port=c.get(redis_section, "port") + ) + fanout_queues = [x.strip() for x in c.get(worker_section, "fanout").split(",") if x] + + if c.has_option(worker_section, "idletime"): + try: + idletime = float(c.get(worker_section, "idletime")) + except ValueError: + idletime = 10 + + while(True): + line = rq.pop() + if line: + fanout_success = True + if topushq: + # if there are residual messages to send, restart with those: + if len(topushq) == 0: + # if the queue is empty, and this is a clean start + for q in fanout_queues: + topushq.push(q) + # Distribution: + while len(topushq) != 0: + q = topushq.pop() + rq.push(line, to_queue=q) + topushq.task_complete() + rq.task_complete() + else: + for q in fanout_queues: + rq.push(line, to_queue=q) + rq.task_complete() + else: + # ratelimit to stop it chewing through CPU cycles + sleep(idletime) diff --git a/message_workers/loglines.cfg b/message_workers/loglines.cfg new file mode 100644 index 0000000..647815d --- /dev/null +++ b/message_workers/loglines.cfg @@ -0,0 +1,59 @@ +#Copyright (c) 2012 University of Oxford +# +#Permission is hereby granted, free of charge, to any person obtaining +#a copy of this software and associated documentation files (the +#"Software"), to deal in the Software without restriction, including +#without limitation the rights to use, copy, modify, merge, publish, +#distribute, sublicense, and/or sell copies of the Software, and to +#permit persons to whom the Software is furnished to do so, subject to +#the following conditions: +# +#The above copyright notice and this permission notice shall be +#included in all copies or substantial portions of the Software. +# +#THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +#EXPRESS OR IMPLIED, --INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +#MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +#IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +#CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +#TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +#SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +# Configuring workers: +[worker_solr] +listento = solrindex +errorq = solrindexerror +command = ./solr_worker.py +solrurl = http://localhost:8080/solr +dbroot = /var/lib/databank +idletime = 1 +stdout_logfile = /var/log/databank/solr_worker.log +numprocs = 2 + +[worker_broker] +listento = silochanges +command = ./broker.py +#fanout = auditlog, solrindex +fanout = solrindex +fanout_status_queue = broker_temp +idletime = 1 +stdout_logfile = /var/log/databank/broker.log +numprocs = 2 + +[logger_auditlogger] +listento = auditlog +command = ./logfromqueue.py +logfile = logs/audit.log +stdout_logfile = /var/log/databank/auditlogger.log + +# DEFAULT VALUES FOLLOW +############################## +[redis] +host = localhost +port = 6379 +db = 0 + +[redis_test] +host = localhost +port = 6379 +db = 1 diff --git a/message_workers/redisqueue.py b/message_workers/redisqueue.py new file mode 100755 index 0000000..b76d3f6 --- /dev/null +++ b/message_workers/redisqueue.py @@ -0,0 +1,136 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +""" +Copyright (c) 2012 University of Oxford + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, --INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +""" + +from redis import Redis +from redis.exceptions import ConnectionError +from time import sleep + +WORKERPREFIX = "temp" +HOST = "localhost" +PORT = 6379 +DB = 0 + +import logging + +logger = logging.getLogger("redisqueue") +logger.setLevel(logging.INFO) +# create console handler and set level to debug +ch = logging.StreamHandler() +# create formatter +formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s") +# add formatter to ch +ch.setFormatter(formatter) +# add ch to logger +logger.addHandler(ch) + + +"""Simple wrapper around a redis queue that gives methods in line with the other Queue-style classes""" + +class RedisQueue(object): + def __init__(self, queuename, workername, db=DB, host=HOST, port=PORT, workerprefix=WORKERPREFIX, errorqueue=None): + self.host = host + if isinstance(port, str): + try: + self.port = int(port) + except ValueError: + self.port = PORT + else: + self.port = port + self.queuename = queuename + self.workername = workername + self.workeritem = ":".join([workerprefix, workername]) + self.errorqueue = errorqueue + if not errorqueue: + self.errorqueue = queuename + self.db = db + self._initclient() + + def _initclient(self): + logger.info("Initialising the redis queue %s for %s" % (self.queuename, self.workername)) + logger.info("Host:%s port:%s DB:%s" % (self.host, self.port, self.db)) + logger.debug("Debug messages detailing worker queue activity") + self._r = Redis(host=self.host, db=self.db, port=self.port) + + def check_connection(self): + #sleep(1) + try: + self._r.info() + except ConnectionError: + self._initclient() + + def __len__(self): + if self.inprogress(): + return self._r.llen(self.queuename) + 1 + else: + return self._r.llen(self.queuename) + + def __getitem__(self, index): + #self.check_connection() + return self._r.lrange(self.queuename, index, index) + + def inprogress(self): + #sleep(1) + #self.check_connection() + ip = self._r.lrange(self.workeritem, 0, 0) + if ip: + return ip.pop() + else: + return None + + def task_complete(self): + #sleep(1) + #self.check_connection() + logger.debug("Task completed by worker %s" % self.workername) + return self._r.rpop(self.workeritem) + + def task_failed(self): + #sleep(1) + #self.check_connection() + logger.error("Task FAILED by worker %s" % self.workername) + logger.debug(self.inprogress()) + return self._r.rpoplpush(self.workeritem, self.errorqueue) + + def push(self, item, to_queue=None): + #sleep(1) + #self.check_connection() + if to_queue: + logger.debug("{%s} put onto queue %s by worker %s" % (item, to_queue,self.workername)) + return self._r.lpush(to_queue, item) + else: + logger.debug("{%s} put onto queue %s by worker %s" % (item, self.queuename,self.workername)) + return self._r.lpush(self.queuename, item) + + def pop(self): + #sleep(1) + #self.check_connection() + logger.debug("In pop - Queuename: %s, workeritem:%s"%(self.queuename, self.workeritem)) + if self._r.llen(self.workeritem) == 0: + itemid = self._r.rpoplpush(self.queuename, self.workeritem) + if self.queuename != self.errorqueue: + self._r.lrem(self.errorqueue, itemid) + logger.debug("{%s} pulled from queue %s by worker %s" % (self.inprogress(), self.queuename,self.workername)) + else: + logger.debug("{%s} pulled from temporary worker queue by worker %s" % (self.inprogress(), self.workername)) + return self.inprogress() diff --git a/message_workers/solrFields.py b/message_workers/solrFields.py new file mode 100644 index 0000000..45dffe8 --- /dev/null +++ b/message_workers/solrFields.py @@ -0,0 +1,127 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +""" +Copyright (c) 2012 University of Oxford + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, --INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +""" + +solr_fields_mapping = { + "silo":"silo", + "id":"id", + "uuid":"uuid", + "http://www.w3.org/1999/02/22-rdf-syntax-ns#type":"type", + "http://vocab.ox.ac.uk/dataset/schema#isEmbargoed":"embargoStatus", + "http://purl.org/spar/pso/Status":"embargoStatus", + "http://vocab.ox.ac.uk/dataset/schema#embargoedUntil":"embargoedUntilDate", + "http://purl.org/spar/fabio/hasEmbargoDate":"embargoedUntilDate", + "http://vocab.ox.ac.uk/dataset/schema#currentVersion":"currentVersion", + "http://purl.org/ontology/bibo/doi":"doi", + "http://www.openarchives.org/ore/terms/aggregates":"aggregatedResource", + "http://purl.org/spar/fabio/publicationDate":"publicationDate", + "http://purl.org/dc/terms/abstract":"abstract", + "http://purl.org/dc/terms/accessRights":"accessRights", + "http://purl.org/dc/terms/accrualMethod":"accrualMethod", + "http://purl.org/dc/terms/accrualPeriodicity":"accrualPeriodicity", + "http://purl.org/dc/terms/accrualPolicy":"accrualPolicy", + "http://purl.org/dc/terms/alternative":"alternative", + "http://purl.org/dc/terms/audience":"audience", + "http://purl.org/dc/terms/available":"available", + "http://purl.org/dc/terms/bibliographicCitation":"bibliographicCitation", + "http://purl.org/dc/terms/conformsTo":"conformsTo", + "http://purl.org/dc/terms/contributor":"contributor", + "http://purl.org/dc/terms/coverage":"coverage", + "http://purl.org/dc/terms/created":"created", + "http://purl.org/dc/terms/creator":"creator", + "http://purl.org/dc/terms/date":"date", + "http://purl.org/dc/terms/dateAccepted":"dateAccepted", + "http://purl.org/dc/terms/dateCopyrighted":"dateCopyrighted", + "http://purl.org/dc/terms/dateSubmitted":"dateSubmitted", + "http://purl.org/dc/terms/description":"description", + "http://purl.org/dc/terms/educationLevel":"educationLevel", + "http://purl.org/dc/terms/extent":"extent", + "http://purl.org/dc/terms/format":"format", + "http://purl.org/dc/terms/hasFormat":"hasFormat", + "http://purl.org/dc/terms/hasPart":"hasPart", + "http://purl.org/dc/terms/hasVersion":"hasVersion", + "http://purl.org/dc/terms/identifier":"identifier", + "http://purl.org/dc/terms/instructionalMethod":"instructionalMethod", + "http://purl.org/dc/terms/isFormatOf":"isFormatOf", + "http://purl.org/dc/terms/isPartOf":"isPartOf", + "http://purl.org/dc/terms/isReferencedBy":"isReferencedBy", + "http://purl.org/dc/terms/isReplacedBy":"isReplacedBy", + "http://purl.org/dc/terms/isRequiredBy":"isRequiredBy", + "http://purl.org/dc/terms/issued":"issued", + "http://purl.org/dc/terms/isVersionOf":"isVersionOf", + "http://purl.org/dc/terms/language":"language", + "http://purl.org/dc/terms/license":"license", + "http://purl.org/dc/terms/mediator":"mediator", + "http://purl.org/dc/terms/medium":"medium", + "http://purl.org/dc/terms/modified":"modified", + "http://purl.org/dc/terms/provenance":"provenance", + "http://purl.org/dc/terms/publisher":"publisher", + "http://purl.org/dc/terms/references":"references", + "http://purl.org/dc/terms/relation":"relation", + "http://purl.org/dc/terms/replaces":"replaces", + "http://purl.org/dc/terms/requires":"requires", + "http://purl.org/dc/terms/rights":"rights", + "http://purl.org/dc/terms/rightsHolder":"rightsHolder", + "http://purl.org/dc/terms/source":"source", + "http://purl.org/dc/terms/spatial":"spatial", + "http://purl.org/dc/terms/subject":"subject", + "http://purl.org/dc/terms/tableOfContents":"tableOfContents", + "http://purl.org/dc/terms/temporal":"temporal", + "http://purl.org/dc/terms/title":"title", + "http://purl.org/dc/terms/type":"type", + "http://purl.org/dc/terms/valid":"valid", + "http://purl.org/dc/elements/1.1/contributor":"contributor", + "http://purl.org/dc/elements/1.1/coverage":"coverage", + "http://purl.org/dc/elements/1.1/creator":"creator", + "http://purl.org/dc/elements/1.1/date":"date", + "http://purl.org/dc/elements/1.1/description":"description", + "http://purl.org/dc/elements/1.1/format":"format", + "http://purl.org/dc/elements/1.1/identifier":"identifier", + "http://purl.org/dc/elements/1.1/language":"language", + "http://purl.org/dc/elements/1.1/publisher":"publisher", + "http://purl.org/dc/elements/1.1/relation":"relation", + "http://purl.org/dc/elements/1.1/rights":"rights", + "http://purl.org/dc/elements/1.1/source":"source", + "http://purl.org/dc/elements/1.1/subject":"subject", + "http://purl.org/dc/elements/1.1/title":"title", + "http://purl.org/dc/elements/1.1/type":"type" +} + +facets = [ + 'f_creator', + 'f_mediator', + 'f_embargoedUntilDate', + 'f_license', + 'f_rights', + 'f_type', + 'f_publisher', + 'f_isPartOf', + 'f_hasVersion', + 'f_publicationDate', + 'f_contributor', + 'f_language', + 'f_rightsHolder', + 'f_source', + 'f_subject' +] diff --git a/message_workers/solr_worker.py b/message_workers/solr_worker.py new file mode 100755 index 0000000..60ee535 --- /dev/null +++ b/message_workers/solr_worker.py @@ -0,0 +1,183 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +""" +Copyright (c) 2012 University of Oxford + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, --INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +""" + +from redisqueue import RedisQueue +from LogConfigParser import Config +from solrFields import solr_fields_mapping + +import sys +from time import sleep +from datetime import datetime, timedelta +from rdflib import URIRef +import simplejson +from collections import defaultdict +from uuid import uuid4 + +from recordsilo import Granary +from solr import SolrConnection + +import logging + +logger = logging.getLogger("redisqueue") +logger.setLevel(logging.INFO) +ch = logging.StreamHandler() +formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s") +ch.setFormatter(formatter) +logger.addHandler(ch) + +class NoSuchSilo(Exception): + pass + +def gather_document(silo_name, item): + graph = item.get_graph() + document = defaultdict(list) + document['uuid'].append(item.metadata['uuid']) + document['id'].append(item.item_id) + document['silo'].append(silo_name) + for (_,p,o) in graph.triples((URIRef(item.uri), None, None)): + if str(p) in solr_fields_mapping: + field = solr_fields_mapping[str(p)] + if field == "aggregatedResource": + if '/datasets/' in o: + fn = unicode(o).split('/datasets/') + if len(fn) == 2 and fn[1]: + document['filename'].append(unicode(fn[1]).encode("utf-8")) + if field == "embargoedUntilDate": + ans = u"%sZ"%unicode(o).split('.')[0] + document[field].append(unicode(ans).encode("utf-8")) + else: + document[field].append(unicode(o).encode("utf-8")) + else: + document['text'].append(unicode(o).encode("utf-8")) + document = dict(document) + return document + +if __name__ == "__main__": + c = Config() + redis_section = "redis" + worker_section = "worker_solr" + worker_number = sys.argv[1] + hours_before_commit = 1 + if len(sys.argv) == 3: + if "redis_%s" % sys.argv[2] in c.sections(): + redis_section = "redis_%s" % sys.argv[2] + + rq = RedisQueue(c.get(worker_section, "listento"), "solr_%s" % worker_number, + db=c.get(redis_section, "db"), + host=c.get(redis_section, "host"), + port=c.get(redis_section, "port"), + errorqueue=c.get(worker_section, "errorq") + ) + DB_ROOT = c.get(worker_section, "dbroot") + rdfdb_config = Config("%s/production.ini" % DB_ROOT) + granary_root = rdfdb_config.get("app:main", "granary.store", 0, {'here':DB_ROOT}) + + g = Granary(granary_root) + + solr = SolrConnection(c.get(worker_section, "solrurl")) + + idletime = 2 + commit_time = datetime.now() + timedelta(hours=hours_before_commit) + toCommit = False + while(True): + sleep(idletime) + + if datetime.now() > commit_time and toCommit: + solr.commit() + commit_time = datetime.now() + timedelta(hours=hours_before_commit) + toCommit = False + + line = rq.pop() + + if not line: + if toCommit: + solr.commit() + toCommit = False + commit_time = datetime.now() + timedelta(hours=hours_before_commit) + continue + + logger.debug("Got message %s" %str(line)) + + toCommit = True + msg = simplejson.loads(line) + # get silo name + try: + silo_name = msg['silo'] + except: + logger.error("Msg badly formed %s\n"%str(msg)) + rq.task_complete() + continue + # Re-initialize granary + if silo_name not in g.silos and not msg['type'] == "d": + g = Granary(granary_root) + g.state.revert() + g._register_silos() + if silo_name not in g.silos: + logger.error("Silo %s does not exist\n"%silo_name) + rq.task_complete() + #raise NoSuchSilo + continue + if msg['type'] == "c" or msg['type'] == "u" or msg['type'] == "embargo": + s = g.get_rdf_silo(silo_name) + # Creation, update or embargo change + itemid = msg.get('id', None) + logger.info("Got creation message on id:%s in silo:%s" % (itemid, silo_name)) + if itemid and s.exists(itemid): + item = s.get_item(itemid) + solr_doc = gather_document(silo_name, item) + try: + solr.add(_commit=False, **solr_doc) + except Exception, e : + logger.error("Error adding document to solr id:%s in silo:%s\n" % (itemid, silo_name)) + try: + logger.error("%s\n\n" %str(e)) + except: + pass + rq.task_failed() + continue + else: + silo_metadata = g.describe_silo(silo_name) + solr_doc = {'id':silo_name, 'silo':silo_name, 'type':'Silo', 'uuid':uuid4().hex} + solr_doc['title'] = '' + if 'title' in silo_metadata: + solr_doc['title'] = silo_metadata['title'] + solr_doc['description'] = '' + if 'description' in silo_metadata: + solr_doc['description'] = silo_metadata['description'] + solr.add(_commit=False, **solr_doc) + rq.task_complete() + elif msg['type'] == "d": + # Deletion + itemid = msg.get('id', None) + if itemid: + logger.info("Got deletion message on id:%s in silo:%s" % (itemid, silo_name)) + query='silo:"%s" AND id:"%s"'%(silo_name, itemid) + solr.delete_query(query) + elif silo_name: + logger.info("Got deletion message on silo:%s" %silo_name) + query='silo:"%s"'%silo_name + solr.delete_query(query) + #solr.commit() + rq.task_complete() diff --git a/message_workers/workers_available/worker_broker.conf b/message_workers/workers_available/worker_broker.conf new file mode 100644 index 0000000..577ef22 --- /dev/null +++ b/message_workers/workers_available/worker_broker.conf @@ -0,0 +1,14 @@ +[program:worker_broker] +autorestart = true +numprocs = 2 +startretries = 3 +redirect_stderr = True +stopwaitsecs = 10 +process_name = worker_broker_%(process_num)s +priority = 777 +directory = /var/lib/databank/message_workers/ +command = /var/lib/databank/message_workers/broker.py %(process_num)s +autostart = true +startsecs = 10 +stdout_logfile = /var/log/databank/worker_broker.log + diff --git a/message_workers/workers_available/worker_solr.conf b/message_workers/workers_available/worker_solr.conf new file mode 100644 index 0000000..3da3f8b --- /dev/null +++ b/message_workers/workers_available/worker_solr.conf @@ -0,0 +1,14 @@ +[program:worker_solr] +autorestart = true +numprocs = 2 +startretries = 3 +redirect_stderr = True +stopwaitsecs = 10 +process_name = worker_solr_%(process_num)s +priority = 888 +directory = /var/lib/databank/message_workers/ +command = /var/lib/databank/message_workers/solr_worker.py %(process_num)s +autostart = true +startsecs = 10 +stdout_logfile = /var/log/databank/worker_solr.log + diff --git a/mod_wsgi/dispatch.wsgi b/mod_wsgi/dispatch.wsgi new file mode 100644 index 0000000..7591098 --- /dev/null +++ b/mod_wsgi/dispatch.wsgi @@ -0,0 +1,42 @@ +""" +Copyright (c) 2012 University of Oxford + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, --INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +""" + +# Add the virtual Python environment site-packages directory to the path +#import site +#site.addsitedir('/home/simplesite/env/lib/python2.5/site-packages') +#site.addsitedir('/usr/local/lib/python2.6/dist-packages') + +import pkg_resources +pkg_resources.working_set.add_entry('/var/lib/databank') + +# Avoid ``[Errno 13] Permission denied: '/var/www/.python-eggs'`` messages +import os +os.environ['PYTHON_EGG_CACHE'] = '/var/cache/databank/egg-cache' + +import sys +sys.stdout = sys.stderr + +# Load the Pylons application +from paste.deploy import loadapp +application = loadapp('config:/var/lib/databank/production.ini') + diff --git a/mod_wsgi/dispatch_development.wsgi b/mod_wsgi/dispatch_development.wsgi new file mode 100644 index 0000000..3b8b054 --- /dev/null +++ b/mod_wsgi/dispatch_development.wsgi @@ -0,0 +1,39 @@ +""" +Copyright (c) 2012 University of Oxford + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, --INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +""" + +# Add the virtual Python environment site-packages directory to the path +#import site +#site.addsitedir('/home/simplesite/env/lib/python2.5/site-packages') +#site.addsitedir('/usr/local/lib/python2.6/dist-packages') + +import pkg_resources +pkg_resources.working_set.add_entry('/var/lib/databank') + +# Avoid ``[Errno 13] Permission denied: '/var/www/.python-eggs'`` messages +import os +os.environ['PYTHON_EGG_CACHE'] = '/var/cache/databank/egg-cache' + +# Load the Pylons application +from paste.deploy import loadapp +application = loadapp('config:/var/lib/databank/development.ini') + diff --git a/mod_wsgi/dispatch_ve_26.wsgi b/mod_wsgi/dispatch_ve_26.wsgi new file mode 100644 index 0000000..9c1085e --- /dev/null +++ b/mod_wsgi/dispatch_ve_26.wsgi @@ -0,0 +1,43 @@ +""" +Copyright (c) 2012 University of Oxford + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, --INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +""" + +# Add the virtual Python environment site-packages directory to the path +import site +site.addsitedir('/var/lib/databank/lib/python2.6/site-packages') +#site.addsitedir('/home/simplesite/env/lib/python2.5/site-packages') +#site.addsitedir('/usr/local/lib/python2.6/dist-packages') + +import pkg_resources +pkg_resources.working_set.add_entry('/var/lib/databank') + +# Avoid ``[Errno 13] Permission denied: '/var/www/.python-eggs'`` messages +import os +os.environ['PYTHON_EGG_CACHE'] = '/var/cache/databank/egg-cache' + +import sys +sys.stdout = sys.stderr + +# Load the Pylons application +from paste.deploy import loadapp +application = loadapp('config:/var/lib/databank/production.ini') + diff --git a/mod_wsgi/dispatch_ve_27.wsgi b/mod_wsgi/dispatch_ve_27.wsgi new file mode 100644 index 0000000..dfbc9d4 --- /dev/null +++ b/mod_wsgi/dispatch_ve_27.wsgi @@ -0,0 +1,43 @@ +""" +Copyright (c) 2012 University of Oxford + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, --INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +""" + +# Add the virtual Python environment site-packages directory to the path +import site +site.addsitedir('/var/lib/databank/lib/python2.7/site-packages') +#site.addsitedir('/home/simplesite/env/lib/python2.5/site-packages') +#site.addsitedir('/usr/local/lib/python2.6/dist-packages') + +import pkg_resources +pkg_resources.working_set.add_entry('/var/lib/databank') + +# Avoid ``[Errno 13] Permission denied: '/var/www/.python-eggs'`` messages +import os +os.environ['PYTHON_EGG_CACHE'] = '/var/cache/databank/egg-cache' + +import sys +sys.stdout = sys.stderr + +# Load the Pylons application +from paste.deploy import loadapp +application = loadapp('config:/var/lib/databank/production.ini') + diff --git a/passwd-default b/passwd-default new file mode 100644 index 0000000..c4930a9 --- /dev/null +++ b/passwd-default @@ -0,0 +1,9 @@ +admin:uaXjyn4Uw3qXo +admin2:IFWZaH87O7ZDg +admin3:b6TA/1MC7CD96 +sandbox_user:0kcQdq23ysbq. +sandbox_user2:Zg8jCIxXK8/Sc +sandbox_user3:qhc7aFzy.y5vU +sandbox_manager:ej4TeOgLu4GQ6 +sandbox_manager2:blUNnoUzOfRNM +sandbox_manager3:i/VlXSPqIgnwQ diff --git a/persisted_state.json b/persisted_state.json new file mode 100644 index 0000000..0967ef4 --- /dev/null +++ b/persisted_state.json @@ -0,0 +1 @@ +{} diff --git a/production-jenkins.ini b/production-jenkins.ini new file mode 100644 index 0000000..c0c6e0a --- /dev/null +++ b/production-jenkins.ini @@ -0,0 +1,138 @@ +# Copyright (c) 2012 University of Oxford +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, --INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# +# rdfdatabank - Pylons development environment configuration +# +# The %(here)s variable will be replaced with the parent directory of this file +# +[DEFAULT] +debug = false +# Uncomment and replace with the address which should receive any error reports +#email_to = you@yourdomain.com +smtp_server = localhost +error_email_from = paste@jenkins + +[server:main] +use = egg:Paste#http +#Use these setings to run pylons using mod_wsgi and apache +host = 127.0.0.1 +port = 5000 +#Use these settings tp run pylons from the commandline +#host = 0.0.0.0 +#port = 80 + +[app:main] +use = egg:rdfdatabank +full_stack = true +static_files = true + +sqlalchemy.url = mysql://databanksqladmin:d6sqL4dm;n@localhost:3306/databankauth +sqlalchemy.pool_recycle = 3600 + +cache_dir = /var/cache/databank +beaker.session.key = rdfdatabank +beaker.session.secret = somesecret + +who.config_file = %(here)s/who.ini +who.log_level = info +who.log_file = /var/log/databank/who.log + +redis.host = localhost + +granary.store = %(here)s/silos +granary.uri_root = http://dataflow-jenkins.bodleian.ox.ac.uk/ + +#profile.log_filename = /var/log/databank/profile.log +#profile.path = /__profile__ + +#auth.file = /var/lib/databank/passwd +#auth.info = /var/lib/databank/rdfdatabank/config/users.py + +doi.config = /var/lib/databank/rdfdatabank/config/doi_config.py +doi.count = /var/lib/databank/rdfdatabank/config/doi_count + +broadcast.to = redis +broadcast.queue = silochanges + +metadata.embargoed = False + +solr.host = http://localhost:8080/solr +naming_rule = [^0-9a-zA-Z_\-\:] +naming_rule_humanized = Numbers, alphabets and -: +formats_served = text/html,text/xhtml,text/plain,application/json,application/rdf+xml,text/xml,text/rdf+n3,application/x-turtle,text/rdf+ntriples,text/rdf+nt +publisher = Bodleian Libraries, University of Oxford +rights = http://ora.ouls.ox.ac.uk/objects/uuid%3A1d00eebb-8fed-46ad-8e38-45dbdb4b224c +license = CC0 1.0 Universal (CC0 1.0). See http://creativecommons.org/publicdomain/zero/1.0/legalcode +#license = Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License. See http://creativecommons.org/licenses/by-nc-sa/3.0/ + +api.version = 0.3 + +# If you'd like to fine-tune the individual locations of the cache data dirs +# for the Cache data, or the Session saves, un-comment the desired settings +# here: +#beaker.cache.data_dir = %(here)s/data/cache +#beaker.session.data_dir = %(here)s/data/sessions + +# WARNING: *THE LINE BELOW MUST BE UNCOMMENTED ON A PRODUCTION ENVIRONMENT* +# Debug mode will enable the interactive debugging tool, allowing ANYONE to +# execute malicious code after an exception is raised. +#set debug = false + +# Logging configuration +[loggers] +keys = root, routes, rdfdatabank + +[handlers] +keys = console, logfile + +[formatters] +keys = generic + +[logger_root] +level = INFO +handlers = logfile + +[logger_routes] +level = INFO +handlers = logfile +qualname = routes.middleware +# "level = DEBUG" logs the route matched and routing variables. + +[logger_rdfdatabank] +level = INFO +handlers = logfile +qualname = rdfdatabank + +[handler_console] +class = StreamHandler +args = (sys.stderr,) +level = NOTSET +formatter = generic + +[handler_logfile] +class = FileHandler +level = INFO +formatter = generic +args = ('/var/log/databank/databank.log', 'w') + +[formatter_generic] +format = %(asctime)s,%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s +datefmt = %H:%M:%S diff --git a/production.ini b/production.ini new file mode 100644 index 0000000..a7a6320 --- /dev/null +++ b/production.ini @@ -0,0 +1,140 @@ +# Copyright (c) 2012 University of Oxford +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, --INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# +# rdfdatabank - Pylons development environment configuration +# +# The %(here)s variable will be replaced with the parent directory of this file +# +[DEFAULT] +debug = false +# Uncomment and replace with the address which should receive any error reports +email_to = you@yourdomain.com +smtp_server = localhost +error_email_from = paste@databank + +[server:main] +use = egg:Paste#http +#Use these setings to run pylons using mod_wsgi and apache +host = 127.0.0.1 +port = 5000 +#Use these settings tp run pylons from the commandline +#host = 0.0.0.0 +#port = 80 + +[app:main] +use = egg:rdfdatabank +full_stack = true +static_files = true + +sqlalchemy.url = mysql://databanksqladmin:d6sqL4dm;n@localhost:3306/databankauth +sqlalchemy.pool_recycle = 3600 + +cache_dir = /var/cache/databank +beaker.session.key = rdfdatabank +beaker.session.secret = somesecret + +who.config_file = /var/lib/databank/who.ini +who.log_level = info +who.log_file = /var/log/databank/who.log + +redis.host = localhost + +granary.store = /silos +granary.uri_root = http://databank/ + +#auth.file = /var/lib/databank/passwd +#auth.info = /var/lib/databank/rdfdatabank/config/users.py + +doi.config = /var/lib/databank/rdfdatabank/config/doi_config.py +doi.count = /var/lib/databank/rdfdatabank/config/doi_count + +broadcast.to = redis +broadcast.queue = silochanges + +metadata.embargoed = False + +solr.host = http://localhost:8080/solr +naming_rule = [^0-9a-zA-Z_\-\:] +naming_rule_humanized = numbers, letters, '-' and ':', must be more than one character long and must not contain any spaces. +formats_served = text/html,text/xhtml,text/plain,application/json,application/rdf+xml,text/xml,text/rdf+n3,application/x-turtle,text/rdf+ntriples,text/rdf+nt +publisher = Bodleian Libraries, University of Oxford +rights = http://ora.ouls.ox.ac.uk/objects/uuid%3A1d00eebb-8fed-46ad-8e38-45dbdb4b224c +license = CC0 1.0 Universal (CC0 1.0). See http://creativecommons.org/publicdomain/zero/1.0/legalcode +#license = Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License. See http://creativecommons.org/licenses/by-nc-sa/3.0/ + +api.version = 0.3 + +# If you'd like to fine-tune the individual locations of the cache data dirs +# for the Cache data, or the Session saves, un-comment the desired settings +# here: +#beaker.cache.data_dir = %(here)s/data/cache +#beaker.session.data_dir = %(here)s/data/sessions +# +# WARNING: *THE LINE BELOW MUST BE UNCOMMENTED ON A PRODUCTION ENVIRONMENT* +# Debug mode will enable the interactive debugging tool, allowing ANYONE to +# execute malicious code after an exception is raised. +#set debug = false + +# Logging configuration +[loggers] +keys = root, routes, rdfdatabank, sqlalchemy + +[handlers] +keys = console, logfile + +[formatters] +keys = generic + +[logger_root] +level = INFO +handlers = logfile + +[logger_routes] +level = INFO +handlers = logfile +qualname = routes.middleware +# "level = DEBUG" logs the route matched and routing variables. + +[logger_rdfdatabank] +level = INFO +handlers = logfile +qualname = rdfdatabank + +[logger_sqlalchemy] +level = INFO +handlers = logfile +qualname = sqlalchemy.engine + +[handler_console] +class = StreamHandler +args = (sys.stderr,) +level = NOTSET +formatter = generic + +[handler_logfile] +class = FileHandler +level = INFO +formatter = generic +args = ('/var/log/databank/databank.log', 'w') + +[formatter_generic] +format = %(asctime)s,%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s +datefmt = %H:%M:%S diff --git a/rdfdatabank.egg-info/PKG-INFO b/rdfdatabank.egg-info/PKG-INFO index b1eff68..41ca43e 100644 --- a/rdfdatabank.egg-info/PKG-INFO +++ b/rdfdatabank.egg-info/PKG-INFO @@ -1,10 +1,10 @@ Metadata-Version: 1.0 Name: rdfdatabank -Version: 0.1dev -Summary: UNKNOWN -Home-page: UNKNOWN -Author: UNKNOWN -Author-email: UNKNOWN -License: UNKNOWN +Version: 0.3 +Summary: RDF-enhanced, pairtree-backed storage API +Home-page: https://github.com/dataflow/RDFDatabank +Author: Anusha Ranganathan, Ben O'Steen +Author-email: anusha.ranganathan@ieee.org, bosteen@gmail.com +License: MIT License Description: UNKNOWN Platform: UNKNOWN diff --git a/rdfdatabank.egg-info/SOURCES.txt b/rdfdatabank.egg-info/SOURCES.txt index 41a75aa..48c1f04 100644 --- a/rdfdatabank.egg-info/SOURCES.txt +++ b/rdfdatabank.egg-info/SOURCES.txt @@ -16,18 +16,160 @@ rdfdatabank/config/__init__.py rdfdatabank/config/deployment.ini_tmpl rdfdatabank/config/environment.py rdfdatabank/config/middleware.py +rdfdatabank/config/namespaces.py rdfdatabank/config/routing.py +rdfdatabank/config/users-default.py rdfdatabank/controllers/__init__.py +rdfdatabank/controllers/about.py +rdfdatabank/controllers/account.py +rdfdatabank/controllers/admin.py +rdfdatabank/controllers/api.py +rdfdatabank/controllers/datasets.py +rdfdatabank/controllers/doi.py rdfdatabank/controllers/error.py +rdfdatabank/controllers/home.py +rdfdatabank/controllers/items.py +rdfdatabank/controllers/keywords.py +rdfdatabank/controllers/search.py +rdfdatabank/controllers/searching.py +rdfdatabank/controllers/silos.py +rdfdatabank/controllers/states.py +rdfdatabank/controllers/users.py +rdfdatabank/lib/HTTP_request.py rdfdatabank/lib/__init__.py rdfdatabank/lib/app_globals.py +rdfdatabank/lib/auth.py rdfdatabank/lib/base.py +rdfdatabank/lib/broadcast.py +rdfdatabank/lib/conneg.py +rdfdatabank/lib/doi_helper.py +rdfdatabank/lib/doi_schema.py +rdfdatabank/lib/file_unpack.py rdfdatabank/lib/helpers.py +rdfdatabank/lib/htpasswd.py +rdfdatabank/lib/ident_md.py +rdfdatabank/lib/search_term.py +rdfdatabank/lib/short_pid.py +rdfdatabank/lib/utils.py rdfdatabank/model/__init__.py -rdfdatabank/public/bg.png -rdfdatabank/public/favicon.ico -rdfdatabank/public/index.html -rdfdatabank/public/pylons-logo.gif +rdfdatabank/public/static/databank_logo.png +rdfdatabank/public/static/databank_logo_generic.png +rdfdatabank/public/static/jquery.js +rdfdatabank/public/static/style.css +rdfdatabank/public/static/js/html5.js +rdfdatabank/public/static/json_data/DatasetStateInfo-dataset1-version0.txt +rdfdatabank/public/static/json_data/DatasetStateInfo-dataset1-version1.txt +rdfdatabank/public/static/json_data/DatasetStateInfo-dataset2.txt +rdfdatabank/public/static/json_data/SiloStateInfo.txt +rdfdatabank/public/static/json_data/adminInformation.txt +rdfdatabank/public/static/json_data/adminInformationForSilo.txt +rdfdatabank/public/static/json_data/datasetInformation-version0.txt +rdfdatabank/public/static/json_data/datasetInformation-version1.txt +rdfdatabank/public/static/json_data/datasetInformation.txt +rdfdatabank/public/static/json_data/datasetSubdirInformation-version1.txt +rdfdatabank/public/static/json_data/datasetSubdirInformation-version3.txt +rdfdatabank/public/static/json_data/datasetSubdirInformation.txt +rdfdatabank/public/static/json_data/datasetsInSiloInformation.txt +rdfdatabank/public/static/json_data/itemInformationForDataset-old.txt +rdfdatabank/public/static/json_data/itemInformationForDataset.txt +rdfdatabank/public/static/json_data/itemInformationForZipFileinDataset.txt +rdfdatabank/public/static/json_data/siloInformation.txt +rdfdatabank/public/static/json_data/silos.txt +rdfdatabank/public/static/styles/basic.css +rdfdatabank/public/static/styles/chimpanzee.css +rdfdatabank/public/static/styles/ie.css +rdfdatabank/public/static/styles/marmoset.css +rdfdatabank/public/static/styles/print.css +rdfdatabank/public/static/styles/reset.css +rdfdatabank/public/static/styles/silverback.css +rdfdatabank/public/static/styles/squirrelMonkey.css +rdfdatabank/public/static/styles/images/blkdiamond.gif +rdfdatabank/public/static/styles/images/blksquare.gif +rdfdatabank/public/static/styles/images/csv.png +rdfdatabank/public/static/styles/images/delete-icon-24.png +rdfdatabank/public/static/styles/images/down_arrow.png +rdfdatabank/public/static/styles/images/down_arrow_black.png +rdfdatabank/public/static/styles/images/down_arrow_blue.png +rdfdatabank/public/static/styles/images/file-add-icon-24.png +rdfdatabank/public/static/styles/images/file-new-icon-24.png +rdfdatabank/public/static/styles/images/fminus.png +rdfdatabank/public/static/styles/images/fplus.png +rdfdatabank/public/static/styles/images/go-up-icon-24.png +rdfdatabank/public/static/styles/images/help-icon-16.png +rdfdatabank/public/static/styles/images/help-icon-24.png +rdfdatabank/public/static/styles/images/info-icon-16.png +rdfdatabank/public/static/styles/images/json.png +rdfdatabank/public/static/styles/images/link.png +rdfdatabank/public/static/styles/images/page-edit-icon-24.png +rdfdatabank/public/static/styles/images/state-icon-24.png +rdfdatabank/public/static/styles/images/unzip-icon-24.png +rdfdatabank/public/static/styles/images/unzip-icon-32.png +rdfdatabank/public/static/styles/images/up_arrow.png +rdfdatabank/public/static/styles/images/up_arrow_black.png +rdfdatabank/public/static/styles/images/up_arrow_blue.png +rdfdatabank/public/static/styles/images/icons/breadcrumb-arrow.png +rdfdatabank/templates/about.html +rdfdatabank/templates/admin_api.html +rdfdatabank/templates/admin_siloview.html +rdfdatabank/templates/admin_user.html +rdfdatabank/templates/admin_users.html +rdfdatabank/templates/alter_silo.html +rdfdatabank/templates/api.html +rdfdatabank/templates/atom_results.html +rdfdatabank/templates/base.html +rdfdatabank/templates/create_doi.html +rdfdatabank/templates/create_new_item.html +rdfdatabank/templates/create_new_silo.html +rdfdatabank/templates/datasets_api.html +rdfdatabank/templates/datasetview.html +rdfdatabank/templates/datasetview_version.html +rdfdatabank/templates/delete_item.html +rdfdatabank/templates/doiview.html +rdfdatabank/templates/embargo_form.html +rdfdatabank/templates/file_upload.html +rdfdatabank/templates/files_unpack.html +rdfdatabank/templates/files_unpack2.html +rdfdatabank/templates/footer.html +rdfdatabank/templates/header.html +rdfdatabank/templates/home.html +rdfdatabank/templates/item_file_upload.html +rdfdatabank/templates/items_api.html +rdfdatabank/templates/itemview.html +rdfdatabank/templates/itemview_version.html +rdfdatabank/templates/keywords.html +rdfdatabank/templates/list_of_datasets.html +rdfdatabank/templates/list_of_silos.html +rdfdatabank/templates/list_of_zipfiles.html +rdfdatabank/templates/login.html +rdfdatabank/templates/logout.html +rdfdatabank/templates/part_list.html +rdfdatabank/templates/part_list_display.html +rdfdatabank/templates/raw_search.html +rdfdatabank/templates/rdf_manifest.html +rdfdatabank/templates/rdf_manifest_form.html +rdfdatabank/templates/readme_section.html +rdfdatabank/templates/register_new_user.html +rdfdatabank/templates/search.html +rdfdatabank/templates/search_advanced.html +rdfdatabank/templates/search_form.html +rdfdatabank/templates/search_response_display.html +rdfdatabank/templates/searching.html +rdfdatabank/templates/silo_admin.html +rdfdatabank/templates/silo_metadata.html +rdfdatabank/templates/silos_api.html +rdfdatabank/templates/siloview.html +rdfdatabank/templates/states_api.html +rdfdatabank/templates/update_user.html +rdfdatabank/templates/zipfilesubitemview.html +rdfdatabank/templates/zipfileview.html +rdfdatabank/tests/RDFDatabankConfig-Jenkins.py +rdfdatabank/tests/RDFDatabankConfig.py +rdfdatabank/tests/TestAdmin.py +rdfdatabank/tests/TestSubmission.py +rdfdatabank/tests/TestSubmission_load.py +rdfdatabank/tests/TestSubmission_submitter.py rdfdatabank/tests/__init__.py -rdfdatabank/tests/test_models.py -rdfdatabank/tests/functional/__init__.py \ No newline at end of file +rdfdatabank/tests/pylons_init.py +rdfdatabank/tests/testlib/SparqlQueryTestCase.py +rdfdatabank/tests/testlib/TestUtils.py +rdfdatabank/tests/testlib/__init__.py \ No newline at end of file diff --git a/rdfdatabank.egg-info/requires.txt b/rdfdatabank.egg-info/requires.txt index a6822b7..c0672f6 100644 --- a/rdfdatabank.egg-info/requires.txt +++ b/rdfdatabank.egg-info/requires.txt @@ -1 +1,9 @@ -Pylons>=0.9.7 \ No newline at end of file +Pylons>=0.9.7 +pairtree>=0.5.6-T +recordsilo +redis +repoze.who>=2.0a4 +repoze.who_friendlyform +solrpy +rdflib==2.4.2 +python-dateutil>=1.4.1 diff --git a/rdfdatabank/config/environment.py b/rdfdatabank/config/environment.py index f456ec5..fb39315 100644 --- a/rdfdatabank/config/environment.py +++ b/rdfdatabank/config/environment.py @@ -1,10 +1,38 @@ +#-*- coding: utf-8 -*- +""" +Copyright (c) 2012 University of Oxford + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, --INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +""" + """Pylons environment configuration""" + import os from mako.lookup import TemplateLookup from pylons import config from pylons.error import handle_mako_error +from sqlalchemy import engine_from_config +from rdfdatabank.model import init_model + import rdfdatabank.lib.app_globals as app_globals import rdfdatabank.lib.helpers from rdfdatabank.config.routing import make_map @@ -20,12 +48,17 @@ def load_environment(global_conf, app_conf): static_files=os.path.join(root, 'public'), templates=[os.path.join(root, 'templates')]) + engine = engine_from_config(app_conf, 'sqlalchemy.') + init_model(engine) + # Initialize config with the basic options config.init_app(global_conf, app_conf, package='rdfdatabank', paths=paths) config['routes.map'] = make_map() config['pylons.app_globals'] = app_globals.Globals() config['pylons.h'] = rdfdatabank.lib.helpers + config [ 'pylons.response_options' ][ 'charset' ] = 'utf-8' + config['pylons.strict_tmpl_context'] = False # Create the Mako TemplateLookup, with the default auto-escaping config['pylons.app_globals'].mako_lookup = TemplateLookup( diff --git a/rdfdatabank/config/middleware.py b/rdfdatabank/config/middleware.py index e2bb957..c08ce31 100644 --- a/rdfdatabank/config/middleware.py +++ b/rdfdatabank/config/middleware.py @@ -1,4 +1,30 @@ +#-*- coding: utf-8 -*- +""" +Copyright (c) 2012 University of Oxford + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, --INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +""" + """Pylons middleware initialization""" + +#from paste import httpexceptions from beaker.middleware import CacheMiddleware, SessionMiddleware from paste.cascade import Cascade from paste.registry import RegistryManager @@ -42,12 +68,23 @@ def make_app(global_conf, full_stack=True, static_files=True, **app_conf): # The Pylons WSGI app app = PylonsApp() + #app = httpexceptions.make_middleware(app, global_conf) + #if asbool(config['debug']): + # from repoze.profile.profiler import AccumulatingProfileMiddleware + # app = AccumulatingProfileMiddleware( + # app, + # log_filename=app_conf['profile.log_filename'], + # discard_first_request=True, + # flush_at_shutdown=True, + # path=app_conf['profile.path'] + # ) + # Routing/Session/Cache Middleware app = RoutesMiddleware(app, config['routes.map']) app = SessionMiddleware(app, config) app = CacheMiddleware(app, config) - + #TODO: Check if the new error controller works with sword server # CUSTOM MIDDLEWARE HERE (filtered by error handling middlewares) if asbool(full_stack): # Handle Python exceptions diff --git a/rdfdatabank/config/namespaces.py b/rdfdatabank/config/namespaces.py new file mode 100644 index 0000000..cbfd32f --- /dev/null +++ b/rdfdatabank/config/namespaces.py @@ -0,0 +1,23 @@ +from rdflib import Namespace + +NAMESPACES = {} +NAMESPACES['rdf'] = Namespace(u'http://www.w3.org/1999/02/22-rdf-syntax-ns#') +NAMESPACES['rdfs'] = Namespace(u'http://www.w3.org/2000/01/rdf-schema#') +NAMESPACES['dc'] = Namespace(u'http://purl.org/dc/elements/1.1/') +NAMESPACES['dcterms'] = Namespace(u'http://purl.org/dc/terms/') +NAMESPACES['foaf'] = Namespace(u'http://xmlns.com/foaf/0.1/') +NAMESPACES['oxds'] = Namespace(u'http://vocab.ox.ac.uk/dataset/schema#') +NAMESPACES['ore'] = Namespace(u'http://www.openarchives.org/ore/terms/') +NAMESPACES['bibo'] = Namespace(u'http://purl.org/ontology/bibo/') + +PREFIXES = {} +PREFIXES['http://www.w3.org/1999/02/22-rdf-syntax-ns#'] = 'rdf' +PREFIXES['http://www.w3.org/2000/01/rdf-schema#'] = 'rdfs' +PREFIXES['http://purl.org/dc/elements/1.1/'] = 'dc' +PREFIXES['http://purl.org/dc/terms/'] = 'dcterms' +PREFIXES['http://xmlns.com/foaf/0.1/'] = 'foaf' +PREFIXES['http://vocab.ox.ac.uk/dataset/schema#'] = 'oxds' +PREFIXES['http://www.openarchives.org/ore/terms/'] = 'ore' +PREFIXES['http://purl.org/ontology/bibo/'] = 'bibo' + + diff --git a/rdfdatabank/config/routing.py b/rdfdatabank/config/routing.py index 656fd25..bbc4756 100644 --- a/rdfdatabank/config/routing.py +++ b/rdfdatabank/config/routing.py @@ -1,3 +1,27 @@ +#-*- coding: utf-8 -*- +""" +Copyright (c) 2012 University of Oxford + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, --INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +""" + """Routes configuration The more specific and detailed routes should be defined first so they @@ -19,22 +43,61 @@ def make_map(): map.connect('/error/{action}/{id}', controller='error') # CUSTOM ROUTES HERE - map.redirect("/", "/objects") + + map.redirect('/*(url)/', '/{url}', + _redirect_code='301 Moved Permanently') + + #Special controller to redirect datasets from databank.ouls to databank.ora + #map.connect('/objects/{id}', controller='redirect', action='index') + + map.connect("/login", controller='account', action='login') + map.connect("/logout", controller='account', action='logout') + map.connect("/welcome", controller='account', action='welcome') + + map.connect('/', controller='home', action='index') + map.connect('/api', controller='api', action='index') + map.connect('/api/{api_name}', controller='api', action='apiview') + + map.connect('/keywords', controller='keywords', action='index') + map.connect('/about', controller='about', action='index') + map.connect('/cookies', controller='cookies', action='index') + map.connect('/searching', controller='searching', action='index') map.connect('/admin', controller='admin', action='index') - map.connect('/admin/{silo_name}', controller='admin', action='archive') - map.connect('/packages', controller='packages', action='index') - map.connect('/packages/{silo}', controller='packages', action='siloview') - map.connect('/packages/{silo}/upload', controller='packages', action='upload') - map.connect('/objects', controller='objects', action='index') - map.connect('/objects/{silo}', controller='objects', action='siloview') - map.connect('/objects/{silo}/{id}', controller='objects', action='itemview') - map.connect('/objects/{silo}/{id}/{path:.*}', controller='objects', action='subitemview') + map.connect('/users', controller='users', action='index') + map.connect('/users/{username}', controller='users', action='userview') + map.connect('/{silo}/users', controller='users', action='siloview') + map.connect('/{silo}/users/{username}', controller='users', action='silouserview') + map.connect('/{silo}/admin', controller='admin', action='siloview') + + map.connect('/silos', controller='silos', action='index') + #map.connect('/{silo}', controller='silos', action='siloview') + + map.connect('/{silo}', controller='datasets', action='siloview') + map.connect('/{silo}/datasets', controller='datasets', action='siloview') + map.connect('/{silo}/datasets/{id}', controller='datasets', action='datasetview') + map.connect('/{silo}/datasets/{id}/{path:.*}', controller='datasets', action='itemview') + + map.connect('/{silo}/items', controller='items', action='siloview') + map.connect('/{silo}/items/{id}', controller='items', action='datasetview') + map.connect('/{silo}/items/{id}/{path:.*?\.zip}', controller='items', action='itemview') + map.connect('/{silo}/items/{id}/{path:.*?\.zip}/{subpath:.*}', controller='items', action='subitemview') + #map.connect('/{silo}/items/{id}/{path:.*}', controller='items', action='itemview') # Use verb dataset instead + + map.connect('/{silo}/states', controller='states', action='siloview') + map.connect('/{silo}/states/{id}', controller='states', action='datasetview') + map.connect('/{silo}/doi/{id}', controller='doi', action='datasetview') + + # SWORDv2 Configuration + map.connect('/swordv2/service-document', controller="sword", action="service_document") # From which to retrieve the service document + map.connect('/swordv2/silo/{path:.*?}', controller="sword", action="collection") # Representing a Collection as listed in the service document + map.connect('/swordv2/edit-media/{path:.*?}', controller="sword", action="media_resource") # The URI used in atom:link@rel=edit-media + map.connect('/swordv2/edit/{path:.*?}', controller="sword", action="container") # The URI used in atom:link@rel=edit + map.connect('/swordv2/statement/{path:.*?}', controller="sword", action="statement") # The URI used in atom:link@rel=sword:statement + + map.connect('/{controller}') map.connect('/{controller}/{action}') map.connect('/{controller}/{action}/{id}') - - map.redirect('/*(url)/', '/{url}', - _redirect_code='301 Moved Permanently') return map diff --git a/rdfdatabank/config/users-default.py b/rdfdatabank/config/users-default.py new file mode 100644 index 0000000..2c25dac --- /dev/null +++ b/rdfdatabank/config/users-default.py @@ -0,0 +1,35 @@ +#-*- coding: utf-8 -*- +""" +Copyright (c) 2012 University of Oxford + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, --INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +""" + +_USERS = { +'admin': {'owner': '*', 'first_name': 'Databank', 'last_name': 'Admin', 'role': 'admin', 'description': 'Admin for all silos'}, +'admin2': {'owner': ['sandbox'], 'first_name': 'Databank', 'last_name': 'Admin-2', 'role': 'admin', 'description': 'Admin for silo Sandbox'}, +'admin3': {'owner': ['sandbox2'], 'first_name': 'Databank', 'last_name': 'Admin-3', 'role': 'admin', 'description': 'Admin for silo Sandbox2'}, +'sandbox_user': {'owner': ['sandbox'], 'role': 'user', 'name': 'Sandbox user', 'description': 'User for silo Sandbox'}, +'sandbox_user2': {'owner': ['sandbox'], 'role': 'user', 'name': 'Sandbox user-2', 'description': 'User for silo Sandbox'}, +'sandbox_user3': {'owner': ['sandbox2'], 'role': 'user', 'name': 'Sandbox user-3', 'description': 'User for silo Sandbox2'}, +'sandbox_manager': {'owner': ['sandbox'], 'role': 'manager', 'name': 'Sandbox manager', 'description': 'Manager for silo Sandbox'}, +'sandbox_manager2': {'owner': ['sandbox'], 'role': 'manager', 'name': 'Sandbox manager-2', 'description': 'Manager for silo Sandbox'}, +'sandbox_manager3': {'owner': ['sandbox2'], 'role': 'manager', 'name': 'Sandbox manager-3', 'description': 'Manager for silo Sandbox2'} +} diff --git a/rdfdatabank/controllers/about.py b/rdfdatabank/controllers/about.py new file mode 100644 index 0000000..b40290f --- /dev/null +++ b/rdfdatabank/controllers/about.py @@ -0,0 +1,31 @@ +#-*- coding: utf-8 -*- +""" +Copyright (c) 2012 University of Oxford + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, --INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +""" + +import logging + +from rdfdatabank.lib.base import BaseController, render + +class AboutController(BaseController): + def index(self): + return render('/about.html') diff --git a/rdfdatabank/controllers/account.py b/rdfdatabank/controllers/account.py new file mode 100644 index 0000000..a36b1d2 --- /dev/null +++ b/rdfdatabank/controllers/account.py @@ -0,0 +1,99 @@ +#-*- coding: utf-8 -*- +""" +Copyright (c) 2012 University of Oxford + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, --INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +""" + +from pylons import url +from pylons import request, response, session, tmpl_context as c, url, app_globals as ag +from rdfdatabank.lib.base import BaseController, render +from pylons.controllers.util import abort, redirect +from paste.request import get_cookies +from webob.exc import HTTPUnauthorized +from urllib import unquote + +class AccountController(BaseController): + def login(self): + #c.ident = None + #c.ident = request.environ.get('repoze.who.identity') + #script_name = request.environ.get('SCRIPT_NAME') or '/' + #referer = request.environ.get('HTTP_REFERER', script_name) + + #if not c.ident: + # abort(401, "Not Authorised") + c.login_counter = request.environ['repoze.who.logins'] + if c.login_counter > 0: + session['login_flash'] = """Wrong credentials. Have you been registered?""" + session.save() + c.came_from = request.params.get('came_from') or "/" + return render('/login.html') + + def welcome(self): + identity = request.environ.get("repoze.who.identity") + came_from = request.params.get('came_from') or "/" + came_from = unquote(came_from) + came_from = unquote(came_from) + came_from = unquote(came_from) + came_from = str(came_from) + if identity: + # Login succeeded + userid = identity['repoze.who.userid'] + #user_det = get_mediator_details(userid) + #if user_det['name']: + # session['user_name'] = user_det['name'] + #if user_det['uri']: + # session['user_uri'] = str(user_det['uri']) + session['user_id'] = userid + session.save() + return redirect(url(came_from)) + else: + # Login failed + try: + login_counter = request.environ['repoze.who.logins'] + 1 + except: + login_counter = 0 + destination = "/login?came_from=%s&logins=%s" % (came_from, login_counter) + return redirect(url(destination)) + + def logout(self): + c.userid = None + c.message = "We hope to see you soon!" + #display_message("We hope to see you soon!", status="success") + came_from = request.params.get('came_from') or "/" + #came_from = request.params.get('came_from', '') or "/" + came_from = unquote(came_from) + came_from = unquote(came_from) + came_from = unquote(came_from) + came_from = str(came_from) + if session.has_key('user_name'): + del session['user_name'] + if session.has_key('user_uri'): + del session['user_uri'] + if session.has_key('user_id'): + del session['user_id'] + if session.has_key('user_dept'): + del session['user_dept'] + if session.has_key('user_email'): + del session['user_email'] + session.save() + #return render('/logout.html') + #return redirect(url(came_from)) + return redirect(url("/")) diff --git a/rdfdatabank/controllers/admin.py b/rdfdatabank/controllers/admin.py index 239df1c..bd72bde 100644 --- a/rdfdatabank/controllers/admin.py +++ b/rdfdatabank/controllers/admin.py @@ -1,135 +1,400 @@ -import logging +#-*- coding: utf-8 -*- +""" +Copyright (c) 2012 University of Oxford -from pylons import request, response, session, config, tmpl_context as c -from pylons.controllers.util import abort, redirect_to -from pylons import app_globals as ag -from rdfdatabank.lib.base import BaseController, render +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: -import re, os +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. -from rdfdatabank.lib.unpack import store_zipfile, unpack_zip_item, BadZipfile +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, --INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +""" +import logging +import simplejson +from pylons import request, response, session, config, tmpl_context as c, url +from pylons.controllers.util import abort, redirect +from pylons.decorators import rest +from pylons import app_globals as ag +from rdfdatabank.lib.base import BaseController, render from rdfdatabank.lib.conneg import MimeType as MT, parse as conneg_parse +from rdfdatabank.lib.utils import allowable_id2 +from rdfdatabank.lib.auth_entry import add_silo, delete_silo, add_group_users, delete_group_users +from rdfdatabank.lib.auth_entry import add_user, update_user, list_usernames, list_user_groups +import codecs log = logging.getLogger(__name__) -accepted_params = ['title', 'description', 'notes', 'owners'] +accepted_params = ['title', 'description', 'notes', 'owners', 'disk_allocation', 'administrators', 'managers', 'submitters'] class AdminController(BaseController): + @rest.restrict('GET', 'POST') def index(self): if not request.environ.get('repoze.who.identity'): abort(401, "Not Authorised") ident = request.environ.get('repoze.who.identity') c.ident = ident - c.granary_list = ag.granary.silos - - # Admin only - if ident.get('role') == "admin": - http_method = request.environ['REQUEST_METHOD'] - if http_method == "GET": - c.granary = ag.granary - return render("/silo_admin.html") - elif http_method == "POST": - params = request.POST - if 'silo' in params and not ag.granary.issilo(params['silo']): - # Create new silo - silo_name = params['silo'] - g_root = config.get("granary.uri_root", "info:") - c.silo = ag.granary.get_rdf_silo(silo_name, uri_base="%s%s" % (g_root, silo_name)) - ag.granary._register_silos() - kw = {} - for term in accepted_params: - if term in params: - kw[term] = params[term] - ag.granary.describe_silo(silo_name, **kw) - ag.granary.sync() - # conneg return + c.granary_list = ag.authz(ident, permission=['administrator', 'manager']) + + http_method = request.environ['REQUEST_METHOD'] + + if http_method == 'GET': + if not 'administrator' in ident['permissions'] and not 'manager' in ident['permissions']: + abort(403, "Do not have administrator or manager credentials") + else: + if not 'administrator' in ident['permissions']: + abort(403, "Do not have administrator credentials") + + if http_method == "GET": + #c.granary = ag.granary + # conneg return + accept_list = None + if 'HTTP_ACCEPT' in request.environ: + try: accept_list = conneg_parse(request.environ['HTTP_ACCEPT']) - if not accept_list: - accept_list= [MT("text", "html")] + except: + accept_list= [MT("text", "html")] + if not accept_list: + accept_list= [MT("text", "html")] + mimetype = accept_list.pop(0) + while(mimetype): + if str(mimetype).lower() in ["text/html", "text/xhtml"]: + return render("/admin_silos.html") + elif str(mimetype).lower() in ["text/plain", "application/json"]: + response.content_type = 'application/json; charset="UTF-8"' + response.status_int = 200 + response.status = "200 OK" + return simplejson.dumps(list(c.granary_list)) + try: mimetype = accept_list.pop(0) - while(mimetype): - if str(mimetype) in ["text/html", "text/xhtml"]: - redirect_to(controller="admin", action="index") - else: - response.status_int = 201 - return "Created Silo %s" % silo_name - else: - abort(403) + except IndexError: + mimetype = None + #Whoops nothing satisfies - return text/html + return render("admin_silos.html") + elif http_method == "POST": + params = request.POST + if 'silo' in params: + if ag.granary.issilo(params['silo']): + abort(403, "The silo %s exists"%params['silo']) + if not allowable_id2(params['silo']): + response.content_type = "text/plain" + response.status_int = 400 + response.status = "400 Bad request. Silo name not valid" + return "Silo name can contain only the following characters - %s and has to be more than 1 character"%ag.naming_rule_humanized + #NOTE: + #If any userid in params['administrators']/params['managers']/params['submitters'] does not exist, return 403 + #if administartor list is empty, append current user to administartor list + #Owner is the superset of adminstrators, managers and submitters + existing_users = list_usernames() + owners = [] + admins = [] + managers = [] + submitters = [] + #if 'owners' in params and params['owners']: + # owners = [x.strip() for x in kw['owners'].split(",") if x] + if 'administrators' in params and params['administrators']: + admins = [x.strip() for x in params['administrators'].split(",") if x] + owners.extend(admins) + if 'managers' in params and params['managers']: + managers = [x.strip() for x in params['managers'].split(",") if x] + owners.extend(managers) + if 'submitters' in params and params['submitters']: + submitters = [x.strip() for x in params['submitters'].split(",") if x] + owners.extend(submitters) + if not admins: + owners.append(ident['user'].user_name) + admins.append(ident['user'].user_name) + owners = list(set(owners)) + for o in owners: + if not o in existing_users: + abort (403, "User %s does not exist"%o) + admins = list(set(admins)) + managers = list(set(managers)) + submitters = list(set(submitters)) + + # Create new silo + silo = params['silo'] + g_root = config.get("granary.uri_root", "info:") + c.silo = ag.granary.get_rdf_silo(silo, uri_base="%s%s/datasets/" % (g_root, silo)) + ag.granary._register_silos() + kw = {} + for term in accepted_params: + if term in params: + kw[term] = params[term] + kw['owners'] = ','.join(owners) + kw['administrators'] = ','.join(admins) + kw['managers'] = ','.join(managers) + kw['submitters'] = ','.join(submitters) + du = ag.granary.disk_usage_silo(silo) + kw['disk_usage'] = du + ag.granary.describe_silo(silo, **kw) + ag.granary.sync() + + # Add silo to database + add_silo(silo) + + try: + ag.b.silo_creation(silo, ident=ident['repoze.who.userid']) + except: + pass + + #Add users belonging to the silo, to the database + all_silo_users = [] + + for a in admins: + all_silo_users.append((a, 'administrator')) + for a in managers: + all_silo_users.append((a, 'manager')) + for a in submitters: + all_silo_users.append((a, 'submitter')) + add_group_users(params['silo'], all_silo_users) + + ag.granary.state.revert() + ag.granary._register_silos() + + # conneg return + accept_list = None + if 'HTTP_ACCEPT' in request.environ: + try: + accept_list = conneg_parse(request.environ['HTTP_ACCEPT']) + except: + accept_list= [MT("text", "html")] + if not accept_list: + accept_list= [MT("text", "html")] + mimetype = accept_list.pop(0) + while(mimetype): + if str(mimetype).lower() in ["text/html", "text/xhtml"]: + redirect(url(controller="datasets", action="siloview", silo=silo)) + elif str(mimetype).lower() in ["text/plain", "application/json"]: + response.content_type = "text/plain" + response.status_int = 201 + response.status = "201 Created" + response.headers['Content-Location'] = url(controller="datasets", action="siloview", silo=silo) + return "201 Created Silo %s" % silo + try: + mimetype = accept_list.pop(0) + except IndexError: + mimetype = None + # Whoops - nothing satisfies - return text/plain + response.content_type = "text/plain" + response.status_int = 201 + response.status = "201 Created" + response.headers['Content-Location'] = url(controller="datasets", action="siloview", silo=silo) + return "201 Created Silo %s" % silo + else: + response.content_type = "text/plain" + response.status_int = 400 + response.status = "400 Bad Request" + return "400 Bad request. No valid parameters found." - def archive(self, silo_name): + @rest.restrict('GET', 'POST', 'DELETE') + def siloview(self, silo): if not request.environ.get('repoze.who.identity'): abort(401, "Not Authorised") + if not ag.granary.issilo(silo): + abort(404) ident = request.environ.get('repoze.who.identity') c.ident = ident - c.granary_list = ag.granary.silos - c.silo_name = silo_name - # Admin only - if ident.get('role') == "admin": - http_method = request.environ['REQUEST_METHOD'] - if http_method == "GET": - if ag.granary.issilo(silo_name): - c.kw = ag.granary.describe_silo(silo_name) + c.silo = silo + silos = ag.authz(ident, permission=['administrator', 'manager']) + if not silo in silos: + abort(403, "Do not have administrator or manager credentials for silo %s"%silo) + user_groups = list_user_groups(ident['user'].user_name) + if ('*', 'administrator') in user_groups: + #User is super user + c.roles = ["admin", "manager", "user"] + elif (silo, 'administrator') in user_groups: + c.roles = ["admin", "manager", "user"] + elif (silo, 'manager') in user_groups: + c.roles = ["manager", "user"] + else: + abort(403, "Do not have administrator or manager credentials for silo %s"%silo) + http_method = request.environ['REQUEST_METHOD'] + + c.kw = ag.granary.describe_silo(silo) + if http_method == "GET": + accept_list = None + if 'HTTP_ACCEPT' in request.environ: + try: + accept_list = conneg_parse(request.environ['HTTP_ACCEPT']) + except: + accept_list= [MT("text", "html")] + if not accept_list: + accept_list= [MT("text", "html")] + mimetype = accept_list.pop(0) + while(mimetype): + if str(mimetype).lower() in ["text/html", "text/xhtml"]: return render("/admin_siloview.html") - else: - abort(404) - elif http_method == "POST": - params = request.POST - if ag.granary.issilo(silo_name): - kw = {} - for term in accepted_params: - if term in params: - kw[term] = params[term] - ag.granary.describe_silo(silo_name, **kw) - ag.granary.sync() - # conneg return + elif str(mimetype).lower() in ["text/plain", "application/json"]: + response.content_type = 'application/json; charset="UTF-8"' + response.status_int = 200 + response.status = "200 OK" + return simplejson.dumps(dict(c.kw)) + try: + mimetype = accept_list.pop(0) + except IndexError: + mimetype = None + #Whoops nothing satisfies - return text/html + return render("/admin_siloview.html") + elif http_method == "POST": + params = request.POST + #Get existing owners, admins, managers and submitters + owners = [] + admins = [] + managers = [] + submitters = [] + if 'owners' in c.kw and c.kw['owners']: + owners = [x.strip() for x in c.kw['owners'].split(",") if x] + if 'administrators' in c.kw and c.kw['administrators']: + admins = [x.strip() for x in c.kw['administrators'].split(",") if x] + if 'managers' in c.kw and c.kw['managers']: + managers = [x.strip() for x in c.kw['managers'].split(",") if x] + if 'submitters' in c.kw and c.kw['submitters']: + submitters = [x.strip() for x in c.kw['submitters'].split(",") if x] + + #Get new members + new_owners = [] + #Get new admins + new_admins = [] + if 'administrators' in params and params['administrators']: + returned_admins = [x.strip() for x in params['administrators'].split(",") if x] + new_admins = [x for x in returned_admins if not x in admins] + new_owners.extend(new_admins) + #Get new managers + new_managers = [] + if 'managers' in params and params['managers']: + returned_managers = [x.strip() for x in params['managers'].split(",") if x] + new_managers = [x for x in returned_managers if not x in managers] + new_owners.extend(new_managers) + #Get new submitters + new_submitters = [] + if 'submitters' in params and params['submitters']: + returned_submitters = [x.strip() for x in params['submitters'].split(",") if x] + new_submitters = [x for x in returned_submitters if not x in submitters] + new_owners.extend(new_submitters) + + #Check if the new members exist. If not return 403 + existing_users = list_usernames() + new_owners = list(set(new_owners)) + for o in new_owners: + if not o in existing_users: + abort (403, "User %s does not exist"%o) + + if new_admins and not 'admin' in c.roles: + abort (403, "Only administrators can assing users to role 'administrator'") + + owners.extend(new_owners) + new_admins = list(set(new_admins)) + admins.extend(new_admins) + new_managers = list(set(new_managers)) + managers.extend(new_managers) + new_submitters = list(set(new_submitters)) + submitters.extend(new_submitters) + + # Update silo info + updateMetadata = False + for term in accepted_params: + if term in params and not term in ['owners', 'administrators', 'managers', 'submitters'] and params[term]: + c.kw[term] = params[term] + updateMetadata = True + if new_owners or new_admins or new_managers or new_submitters or updateMetadata: + new_silo_users = [] + if new_owners: + c.kw['owners'] = ','.join(owners) + if new_admins: + c.kw['administrators'] = ','.join(admins) + for a in new_admins: + new_silo_users.append((a, 'administrator')) + if new_managers: + c.kw['managers'] = ','.join(managers) + for a in new_managers: + new_silo_users.append((a, 'manager')) + if new_submitters: + c.kw['submitters'] = ','.join(submitters) + for a in new_submitters: + new_silo_users.append((a, 'submitter')) + #Add metadat changes to the silo + ag.granary.describe_silo(silo, **c.kw) + ag.granary.sync() + #Add new silo users into database + if new_silo_users: + add_group_users(silo, new_silo_users) + if updateMetadata: + try: + ag.b.silo_change(silo, ident=ident['repoze.who.userid']) + except: + pass + + # conneg return + accept_list = None + if 'HTTP_ACCEPT' in request.environ: + try: accept_list = conneg_parse(request.environ['HTTP_ACCEPT']) - if not accept_list: - accept_list= [MT("text", "html")] + except: + accept_list= [MT("text", "html")] + if not accept_list: + accept_list= [MT("text", "html")] + mimetype = accept_list.pop(0) + while(mimetype): + if str(mimetype).lower() in ["text/html", "text/xhtml"]: + c.message = "Metadata updated" + c.kw = ag.granary.describe_silo(silo) + return render("/admin_siloview.html") + elif str(mimetype).lower() in ["text/plain", "application/json"]: + response.content_type = "text/plain" + response.status_int = 204 + response.status = "204 Updated" + #return "Updated Silo %s" % silo + return + try: mimetype = accept_list.pop(0) - while(mimetype): - if str(mimetype) in ["text/html", "text/xhtml"]: - c.message = "Metadata updated" - c.kw = ag.granary.describe_silo(silo_name) - return render("/admin_siloview.html") - else: - response.status_int = 204 - return "Updated Silo %s" % silo_name - else: - # Create new silo - g_root = config.get("granary.uri_root", "info:") - c.silo = ag.granary.get_rdf_silo(silo_name, uri_base="%s%s/" % (g_root, silo_name)) - ag.granary._register_silos() - kw = {} - for term in accepted_params: - if term in params: - kw[term] = params[term] - ag.granary.describe_silo(silo_name, **kw) - ag.granary.sync() - response.status_int = 201 - return "Created Silo %s" % silo_name - elif http_method == "DELETE": - if ag.granary.issilo(silo_name): - # Deletion of an entire Silo... - # Serious consequences follow this action - # Walk through all the items, emit a delete msg for each - # and then remove the silo - todelete_silo = ag.granary.get_rdf_silo(silo_name) - for item in todelete_silo.list_items(): - ag.b.deletion(silo_name, item, ident=ident['repoze.who.userid']) - ag.granary.delete_silo(silo_name) - ag.b.silo_deletion(silo_name, ident=ident['repoze.who.userid']) - try: - del ag.granary.state[silo_name] - except: - pass - ag.granary.sync() - ag.granary._register_silos() - response.status_int = 200 - return """{'status':'Silo %s deleted'}""" % silo_name - else: - abort(404) - else: - abort(403) - + except IndexError: + mimetype = None + # Whoops - nothing satisfies - return text/plain + response.content_type = "text/plain" + response.status_int = 204 + response.status = "204 Updated" + return + elif http_method == "DELETE": + # Deletion of an entire Silo... + # Serious consequences follow this action + # Walk through all the items, emit a delete msg for each + # and then remove the silo + todelete_silo = ag.granary.get_rdf_silo(silo) + #for item in todelete_silo.list_items(): + # try: + # ag.b.deletion(silo, item, ident=ident['repoze.who.userid']) + # except: + # pass + ag.granary.delete_silo(silo) + try: + ag.b.silo_deletion(silo, ident=ident['repoze.who.userid']) + except: + pass + try: + del ag.granary.state[silo] + except: + pass + ag.granary.sync() + ag.granary._register_silos() + #Delete silo from database + delete_silo(silo) + # conneg return + accept_list = None + response.content_type = "text/plain" + response.status_int = 200 + response.status = "200 OK" + return "{'ok':'true'}" diff --git a/rdfdatabank/controllers/api.py b/rdfdatabank/controllers/api.py new file mode 100644 index 0000000..ab8a779 --- /dev/null +++ b/rdfdatabank/controllers/api.py @@ -0,0 +1,40 @@ +#-*- coding: utf-8 -*- +""" +Copyright (c) 2012 University of Oxford + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, --INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +""" + +import logging + +from pylons import request, response, session, tmpl_context as c, url +from pylons.controllers.util import abort, redirect +from pylons import app_globals as ag +from rdfdatabank.lib.base import BaseController, render + +class ApiController(BaseController): + def index(self): + redirect(url(controller="api", action="apiview", api_name="silos")) + + def apiview(self, api_name): + if api_name not in ['silos', 'datasets', 'states', 'items']: + redirect(url(controller="api", action="apiview", api_name="silos")) + c.api_file = "%s_api.html"%api_name + return render('/api.html') diff --git a/rdfdatabank/controllers/cookies.py b/rdfdatabank/controllers/cookies.py new file mode 100755 index 0000000..33ec448 --- /dev/null +++ b/rdfdatabank/controllers/cookies.py @@ -0,0 +1,31 @@ +#-*- coding: utf-8 -*- +""" +Copyright (c) 2012 University of Oxford + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, --INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +""" + +import logging + +from rdfdatabank.lib.base import BaseController, render + +class CookiesController(BaseController): + def index(self): + return render('/cookies.html') diff --git a/rdfdatabank/controllers/datasets.py b/rdfdatabank/controllers/datasets.py new file mode 100644 index 0000000..7e21598 --- /dev/null +++ b/rdfdatabank/controllers/datasets.py @@ -0,0 +1,1092 @@ +#-*- coding: utf-8 -*- +""" +Copyright (c) 2012 University of Oxford + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, --INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +""" + +import logging +import re, os, shutil, codecs +import simplejson +from datetime import datetime, timedelta +from dateutil.relativedelta import * +#from dateutil.parser import parse +import time +from uuid import uuid4 +from pylons import request, response, session, tmpl_context as c, url, app_globals as ag +from pylons.controllers.util import abort, redirect +from pylons.decorators import rest +from paste.fileapp import FileApp +from rdfdatabank.lib.base import BaseController, render +from rdfdatabank.lib.utils import create_new, get_readme_text, serialisable_stat, allowable_id2, natural_sort +from rdfdatabank.lib.utils import is_embargoed, test_rdf, munge_manifest, get_embargo_values, get_rdf_template, extract_metadata +from rdfdatabank.lib.file_unpack import get_zipfiles_in_dataset +from rdfdatabank.lib.conneg import MimeType as MT, parse as conneg_parse +from rdfdatabank.lib.auth_entry import add_dataset, delete_dataset, get_datasets_count, get_datasets + +JAILBREAK = re.compile("[\/]*\.\.[\/]*") + +log = logging.getLogger(__name__) + +class DatasetsController(BaseController): + @rest.restrict('GET', 'POST') + def siloview(self, silo): + if not ag.granary.issilo(silo): + abort(404) + c.silo_name = silo + ident = request.environ.get('repoze.who.identity') + c.ident = ident + + http_method = request.environ['REQUEST_METHOD'] + + if http_method == "GET": + if silo in ['ww1archives', 'digitalbooks']: + abort(501, "The silo %s contains too many data packages to list"%silo) + c.editor = False + if ag.metadata_embargoed: + if not ident: + abort(401, "Not Authorised") + silos = ag.authz(ident) + if silo not in silos: + abort(403, "Forbidden") + c.editor = True + else: + if ident: + silos = ag.authz(ident) + if silo in silos: + c.editor = True + + options = request.GET + c.start = 0 + if 'start' in options and options['start']: + try: + c.start = int(options['start']) + except ValueError: + c.start = 0 + c.rows = 100 + if 'rows' in options and options['rows']: + try: + c.rows = int(options['rows']) + except ValueError: + c.rows = 100 + + c_silo = ag.granary.get_rdf_silo(silo) + # Get title of silo + state_info = ag.granary.describe_silo(silo) + if 'title' in state_info and state_info['title']: + c.title = state_info['title'] + # Get number of data packages in silo + numFound = get_datasets_count(silo) + try: + c.numFound = int(numFound) + except ValueError: + c.numFound = 0 + + #c.embargos = {'params':{'numFound':numFound, 'start':c.start, 'rows':c.rows}} + c.embargos = {} + #for item in c_silo.list_items(): + for item in get_datasets(silo, start=c.start, rows=c.rows): + try: + c.embargos[item] = is_embargoed(c_silo, item) + except: + c.embargos[item] = None + c.items = c.embargos.keys() + # conneg return + accept_list = None + if 'HTTP_ACCEPT' in request.environ: + try: + accept_list = conneg_parse(request.environ['HTTP_ACCEPT']) + except: + accept_list= [MT("text", "html")] + if not accept_list: + accept_list= [MT("text", "html")] + mimetype = accept_list.pop(0) + while(mimetype): + if str(mimetype).lower() in ["text/html", "text/xhtml"]: + #Calculate the pagination for display of data packages + c.permissible_offsets = [] + c.pages_to_show = 5 + print type(c.start), type(c.pages_to_show), type(c.rows), type(c.numFound) + print c.start, c.pages_to_show, c.rows, c.numFound + try: + remainder = c.numFound % c.rows + if remainder > 0: + c.lastPage = c.numFound - remainder + else: + c.lastPage = c.numFound - c.rows + + if c.numFound > c.rows: + offset_start = c.start - ( (c.pages_to_show/2) * c.rows ) + if offset_start < 0: + offset_start = 0 + + offset_end = offset_start + (c.pages_to_show * c.rows) + if offset_end > c.numFound: + offset_end = c.numFound + if remainder > 0: + offset_start = c.lastPage - (c.pages_to_show * c.rows) + else: + offset_start = c.lastPage - ((c.pages_to_show-1) * c.rows) + + if offset_start < 0: + offset_start = 0 + + c.permissible_offsets = list( xrange( offset_start, offset_end, c.rows) ) + except ValueError: + pass + return render('/siloview.html') + elif str(mimetype).lower() in ["text/plain", "application/json"]: + response.content_type = 'application/json; charset="UTF-8"' + response.status_int = 200 + response.status = "200 OK" + return simplejson.dumps(c.embargos) + try: + mimetype = accept_list.pop(0) + except IndexError: + mimetype = None + #Whoops nothing satisfies - return text/html + return render('/siloview.html') + elif http_method == "POST": + if not ident: + abort(401, "Not Authorised") + + silos = ag.authz(ident) + if silo not in silos: + abort(403, "Forbidden") + params = request.POST + + if not params.has_key("id"): + response.content_type = "text/plain" + response.status_int = 400 + response.status = "400 Bad Request: Parameter 'id' is not available" + return "Parameter 'id' is not available" + + c_silo = ag.granary.get_rdf_silo(silo) + if c_silo.exists(params['id']): + response.content_type = "text/plain" + response.status_int = 409 + response.status = "409 Conflict: Data package already exists" + return "Data package already exists" + + # Supported params: + # id, title, embargoed, embargoed_until, embargo_days_from_now + id = params['id'] + if not allowable_id2(id): + response.content_type = "text/plain" + response.status_int = 400 + response.status = "400 Bad request. Data package name not valid" + return "Data package name can only contain %s"%ag.naming_rule_humanized + + del params['id'] + item = create_new(c_silo, id, ident['repoze.who.userid'], **params) + add_dataset(silo, id) + # Broadcast change as message + try: + ag.b.creation(silo, id, ident=ident['repoze.who.userid']) + except: + pass + + # conneg return + accept_list = None + if 'HTTP_ACCEPT' in request.environ: + try: + accept_list = conneg_parse(request.environ['HTTP_ACCEPT']) + except: + accept_list= [MT("text", "html")] + if not accept_list: + accept_list= [MT("text", "html")] + mimetype = accept_list.pop(0) + while(mimetype): + if str(mimetype).lower() in ["text/html", "text/xhtml"]: + redirect(url(controller="datasets", action="datasetview", silo=silo, id=id)) + elif str(mimetype).lower() in ["text/plain", "application/json"]: + response.content_type = "text/plain" + response.status_int = 201 + response.status = "201 Created" + response.headers["Content-Location"] = url(controller="datasets", action="datasetview", silo=silo, id=id) + return "201 Created" + try: + mimetype = accept_list.pop(0) + except IndexError: + mimetype = None + # Whoops - nothing satisfies - return text/plain + response.content_type = "text/plain" + response.status_int = 201 + response.headers["Content-Location"] = url(controller="datasets", action="datasetview", silo=silo, id=id) + response.status = "201 Created" + return "201 Created" + + @rest.restrict('GET', 'POST', 'DELETE') + def datasetview(self, silo, id): + if not ag.granary.issilo(silo): + abort(404) + # Check to see if embargo is on: + c.silo_name = silo + c.id = id + + http_method = request.environ['REQUEST_METHOD'] + + ident = request.environ.get('repoze.who.identity') + c.ident = ident + c_silo = ag.granary.get_rdf_silo(silo) + + c.version = None + c.editor = False + + if not (http_method == "GET"): + #identity management of item + if not request.environ.get('repoze.who.identity'): + abort(401, "Not Authorised") + silos = ag.authz(ident) + if silo not in silos: + abort(403, "Forbidden") + silos_admin = ag.authz(ident, permission='administrator') + silos_manager = ag.authz(ident, permission='manager') + + if http_method in ["GET", "DELETE"]: + if not c_silo.exists(id): + abort(404) + + if http_method == "GET": + embargoed = False + item = c_silo.get_item(id) + + options = request.GET + + currentversion = str(item.currentversion) + c.version = currentversion + if 'version' in options: + if not options['version'] in item.manifest['versions']: + abort(404) + c.version = str(options['version']) + if c.version and not c.version == currentversion: + item.set_version_cursor(c.version) + + creator = None + if item.manifest and item.manifest.state and 'metadata' in item.manifest.state and item.manifest.state['metadata'] and \ + 'createdby' in item.manifest.state['metadata'] and item.manifest.state['metadata']['createdby']: + creator = item.manifest.state['metadata']['createdby'] + + if ag.metadata_embargoed: + if not ident: + abort(401, "Not Authorised") + silos = ag.authz(ident) + if silo not in silos: + abort(403, "Forbidden") + silos_admin = ag.authz(ident, permission='administrator') + silos_manager = ag.authz(ident, permission='manager') + if ident['repoze.who.userid'] == creator or silo in silos_admin or silo in silos_manager: + c.editor = True + elif item.metadata.get('embargoed') not in ["false", 0, False]: + #TODO: This will always provide the embargo information for the latest version. + # The embargo status should always reflect the latest version, but should the embargo information displayed be that of the vesion??? + embargoed = True + if ident: + silos = ag.authz(ident) + silos_admin = ag.authz(ident, permission='administrator') + silos_manager = ag.authz(ident, permission='manager') + if silo in silos: + #if ident['repoze.who.userid'] == creator or ident.get('role') in ["admin", "manager"]: + if ident['repoze.who.userid'] == creator or silo in silos_admin or silo in silos_manager: + c.editor = True + elif ident: + silos = ag.authz(ident) + silos_admin = ag.authz(ident, permission='administrator') + silos_manager = ag.authz(ident, permission='manager') + if silo in silos: + #if ident['repoze.who.userid'] == creator or ident.get('role') in ["admin", "manager"]: + if ident['repoze.who.userid'] == creator or silo in silos_admin or silo in silos_manager: + c.editor = True + + c.show_files = True + #Only the administrator, manager and creator can view embargoed files. + if embargoed and not c.editor: + c.show_files = False + + #Display but do not edit previous versions of files, since preious versions are read only. + if c.version and not c.version == currentversion: + c.editor = False + + # View options + if "view" in options and c.editor: + c.view = options['view'] + elif c.editor: + c.view = 'editor' + else: + c.view = 'user' + + c.embargos = {} + c.embargos[id] = is_embargoed(c_silo, id) + c.parts = item.list_parts(detailed=True) + c.manifest_pretty = item.rdf_to_string(format="pretty-xml") + c.metadata = None + c.metadata = extract_metadata(item) + c.versions = item.manifest['versions'] + c.versions = natural_sort(c.versions) + #c.manifest = item.rdf_to_string() + c.manifest = get_rdf_template(item.uri, id) + c.zipfiles = get_zipfiles_in_dataset(item) + c.readme_text = None + #if item.isfile("README"): + if "README" in c.parts.keys(): + c.readme_text = get_readme_text(item) + #if item.manifest: + # state = item.manifest.state + + # conneg: + accept_list = None + if 'HTTP_ACCEPT' in request.environ: + try: + accept_list = conneg_parse(request.environ['HTTP_ACCEPT']) + except: + accept_list= [MT("text", "html")] + if not accept_list: + accept_list= [MT("text", "html")] + mimetype = accept_list.pop(0) + + while(mimetype): + if str(mimetype).lower() in ["text/html", "text/xhtml"]: + return render('/datasetview.html') + elif str(mimetype).lower() in ["text/plain", "application/json"]: + response.content_type = 'application/json; charset="UTF-8"' + returndata = {} + returndata['embargos'] = c.embargos + returndata['view'] = c.view + returndata['show_files'] = c.show_files + returndata['editor'] = c.editor + returndata['parts'] = {} + for part in c.parts: + returndata['parts'][part] = serialisable_stat(c.parts[part]) + returndata['readme_text'] = c.readme_text + returndata['manifest_pretty'] = c.manifest_pretty + returndata['manifest'] = c.manifest + returndata['zipfiles'] = c.zipfiles + if c.version: + returndata['version'] = c.version + #items['state'] = state + response.status_int = 200 + response.status = "200 OK" + return simplejson.dumps(returndata) + elif str(mimetype).lower() in ["application/rdf+xml", "text/xml"]: + response.status_int = 200 + response.status = "200 OK" + response.content_type = 'application/rdf+xml; charset="UTF-8"' + return c.manifest_pretty + elif str(mimetype).lower() == "text/rdf+n3": + response.content_type = 'text/rdf+n3; charset="UTF-8"' + response.status_int = 200 + response.status = "200 OK" + return item.rdf_to_string(format="n3") + elif str(mimetype).lower() == "application/x-turtle": + response.content_type = 'application/x-turtle; charset="UTF-8"' + response.status_int = 200 + response.status = "200 OK" + return item.rdf_to_string(format="turtle") + elif str(mimetype).lower() in ["text/rdf+ntriples", "text/rdf+nt"]: + response.content_type = 'text/rdf+ntriples; charset="UTF-8"' + response.status_int = 200 + response.status = "200 OK" + return item.rdf_to_string(format="nt") + # Whoops - nothing satisfies + try: + mimetype = accept_list.pop(0) + except IndexError: + mimetype = None + #Whoops - nothing staisfies - default to text/html + return render('/datasetview.html') + elif http_method == "POST": + code = None + #Create new dataset if it does not exist + if not c_silo.exists(id): + if not allowable_id2(id): + response.content_type = "text/plain" + response.status_int = 400 + response.status = "400 Bad request. Data package name not valid" + return "Data package name can contain only the following characters - %s and has to be more than 1 character"%ag.naming_rule_humanized + params = {} + item = create_new(c_silo, id, ident['repoze.who.userid'], **params) + add_dataset(silo, id) + code = 201 + response.status = "201 Created" + response.status_int = 201 + response.headers["Content-Location"] = url(controller="datasets", action="datasetview", id=id, silo=silo) + response_message = "201 Created empyt data package" + #Update embargo info + params = request.POST + if params.has_key('embargoed') and params['embargoed']: + item = c_silo.get_item(id) + creator = None + if item.manifest and item.manifest.state and 'metadata' in item.manifest.state and item.manifest.state['metadata'] and \ + 'createdby' in item.manifest.state['metadata'] and item.manifest.state['metadata']['createdby']: + creator = item.manifest.state['metadata']['createdby'] + #if not (ident['repoze.who.userid'] == creator or ident.get('role') in ["admin", "manager"]): + if not (ident['repoze.who.userid'] == creator or silo in silos_admin or silo in silos_manager): + abort(403) + if not params['embargoed'].lower() in ['true', 'false', '0', '1']: + abort(400, "The value for embargoed has to be either 'True' or 'False'") + + item.increment_version_delta(clone_previous_version=True, copy_filenames=['manifest.rdf']) + if params.has_key('embargoed_until') and params['embargoed_until']: + e, e_d = get_embargo_values(embargoed=params['embargoed'], embargoed_until=params['embargoed_until']) + elif params.has_key('embargo_days_from_now') and params['embargo_days_from_now']: + e, e_d = get_embargo_values(embargoed=params['embargoed'], embargo_days_from_now=params['embargo_days_from_now']) + else: + e, e_d = get_embargo_values(embargoed=params['embargoed']) + item.metadata['embargoed_until'] = '' + item.del_triple(item.uri, u"oxds:isEmbargoed") + item.del_triple(item.uri, u"oxds:embargoedUntil") + try: + ag.r.set("%s:%s:embargoed_until" % (c_silo.state['storage_dir'], id), ' ') + except: + pass + + if e: + item.metadata['embargoed'] = True + item.add_triple(item.uri, u"oxds:isEmbargoed", 'True') + try: + ag.r.set("%s:%s:embargoed" % (c_silo.state['storage_dir'], id), True) + except: + pass + if e_d: + item.metadata['embargoed_until'] = e_d + item.add_triple(item.uri, u"oxds:embargoedUntil", e_d) + try: + ag.r.set("%s:%s:embargoed_until" % (c_silo.state['storage_dir'], id), e_d) + except: + pass + else: + item.metadata['embargoed'] = False + item.add_triple(item.uri, u"oxds:isEmbargoed", 'False') + try: + ag.r.set("%s:%s:embargoed" % (c_silo.state['storage_dir'], id), False) + except: + pass + + item.del_triple(item.uri, u"dcterms:modified") + item.add_triple(item.uri, u"dcterms:modified", datetime.now()) + item.del_triple(item.uri, u"oxds:currentVersion") + item.add_triple(item.uri, u"oxds:currentVersion", item.currentversion) + item.sync() + + if not code: + code = 204 + response.content_type = "text/plain" + response.status_int = 204 + response.status = "204 Updated" + response_message = None + if params.has_key('file'): + # File upload by a not-too-savvy method - Service-orientated fallback: + # Assume file upload to 'filename' + item = c_silo.get_item(id) + creator = None + if item.manifest and item.manifest.state and 'metadata' in item.manifest.state and item.manifest.state['metadata'] and \ + 'createdby' in item.manifest.state['metadata'] and item.manifest.state['metadata']['createdby']: + creator = item.manifest.state['metadata']['createdby'] + #if not (ident['repoze.who.userid'] == creator or ident.get('role') in ["admin", "manager"]): + if not (ident['repoze.who.userid'] == creator or silo in silos_admin or silo in silos_manager): + abort(403) + + upload = params.get('file') + #if not upload: + # abort(400, "No file was received") + filename = params.get('filename') + if not filename: + filename = params['file'].filename + if filename and JAILBREAK.search(filename) != None: + abort(400, "'..' cannot be used in the path or as a filename") + target_path = filename + + if item.isfile(target_path): + code = 204 + elif item.isdir(target_path): + response.content_type = "text/plain" + response.status_int = 403 + response.status = "403 Forbidden" + return "Cannot POST a file on to an existing directory" + else: + code = 201 + + if filename == "manifest.rdf": + #Copy the uploaded file to a tmp area + #mani_file = os.path.join('/tmp', filename.lstrip(os.sep)) + mani_file = os.path.join('/tmp', uuid4().hex) + mani_file_obj = open(mani_file, 'w') + shutil.copyfileobj(upload.file, mani_file_obj) + upload.file.close() + mani_file_obj.close() + #test rdf file + if not test_rdf(mani_file): + response.status_int = 400 + return "Bad manifest file" + #munge rdf + item.increment_version_delta(clone_previous_version=True, copy_filenames=['manifest.rdf']) + a = item.get_rdf_manifest() + b = a.to_string() + #munge_manifest(manifest_str, item) + munge_manifest(mani_file, item) + else: + if code == 204: + item.increment_version_delta(clone_previous_version=True, copy_filenames=['manifest.rdf', filename]) + else: + item.increment_version_delta(clone_previous_version=True, copy_filenames=['manifest.rdf']) + item.put_stream(target_path, upload.file) + upload.file.close() + item.del_triple(item.uri, u"dcterms:modified") + item.add_triple(item.uri, u"dcterms:modified", datetime.now()) + item.del_triple(item.uri, u"oxds:currentVersion") + item.add_triple(item.uri, u"oxds:currentVersion", item.currentversion) + item.sync() + + if code == 201: + try: + ag.b.creation(silo, id, target_path, ident=ident['repoze.who.userid']) + except: + pass + response.status = "201 Created" + response.status_int = 201 + response.headers["Content-Location"] = url(controller="datasets", action="itemview", id=id, silo=silo, path=filename) + response_message = "201 Created. Added file %s to item %s" % (filename, id) + else: + try: + ag.b.change(silo, id, target_path, ident=ident['repoze.who.userid']) + except: + pass + response.status = "204 Updated" + response.status_int = 204 + response_message = None + elif params.has_key('text'): + # Text upload convenience service + item = c_silo.get_item(id) + filename = params.get('filename') + if not filename: + abort(400, "Bad Request. Must supply a filename") + if JAILBREAK.search(filename) != None: + abort(400, "'..' cannot be used in the path or as a filename") + + creator = None + if item.manifest and item.manifest.state and 'metadata' in item.manifest.state and item.manifest.state['metadata'] and \ + 'createdby' in item.manifest.state['metadata'] and item.manifest.state['metadata']['createdby']: + creator = item.manifest.state['metadata']['createdby'] + #if not (ident['repoze.who.userid'] == creator or ident.get('role') in ["admin", "manager"]): + if not (ident['repoze.who.userid'] == creator or silo in silos_admin or silo in silos_manager): + abort(403) + + target_path = filename + + if item.isfile(target_path): + code = 204 + elif item.isdir(target_path): + response.content_type = "text/plain" + response.status_int = 403 + response.status = "403 forbidden" + return "Cannot POST a file on to an existing directory" + else: + code = 201 + + if filename == "manifest.rdf": + # valid to make sure it's valid RDF + # Otherwise this dataset will not be accessible + text = params['text'] + fname = '/tmp/%s'%uuid4().hex + f = codecs.open(fname, 'w', 'utf-8') + #f = open(fname, 'w') + f.write(text) + f.close() + #if not test_rdf(text): + if not test_rdf(fname): + abort(400, "Not able to parse RDF/XML") + item.increment_version_delta(clone_previous_version=True, copy_filenames=['manifest.rdf']) + a = item.get_rdf_manifest() + b = a.to_string() + munge_manifest(fname, item) + os.remove(fname) + else: + if code == 204: + item.increment_version_delta(clone_previous_version=True, copy_filenames=['manifest.rdf', filename]) + else: + item.increment_version_delta(clone_previous_version=True, copy_filenames=['manifest.rdf']) + item.put_stream(target_path, params['text'].encode("utf-8")) + item.del_triple(item.uri, u"dcterms:modified") + item.add_triple(item.uri, u"dcterms:modified", datetime.now()) + item.del_triple(item.uri, u"oxds:currentVersion") + item.add_triple(item.uri, u"oxds:currentVersion", item.currentversion) + item.sync() + + if code == 201: + try: + ag.b.creation(silo, id, target_path, ident=ident['repoze.who.userid']) + except: + pass + response.status = "201 Created" + response.status_int = 201 + response.headers["Content-Location"] = url(controller="datasets", action="datasetview", id=id, silo=silo) + response_message = "201 Created. Added file %s to item %s" % (filename, id) + else: + try: + ag.b.change(silo, id, target_path, ident=ident['repoze.who.userid']) + except: + pass + response.status = "204 Updated" + response.status_int = 204 + response_message = None + if not code: + response.content_type = "text/plain" + response.status_int = 400 + response.status = "400 Bad request" + return "400 Bad Request. No valid parameters found." + # conneg return + accept_list = None + if 'HTTP_ACCEPT' in request.environ: + try: + accept_list = conneg_parse(request.environ['HTTP_ACCEPT']) + except: + accept_list= [MT("text", "html")] + if not accept_list: + accept_list= [MT("text", "html")] + mimetype = accept_list.pop(0) + while(mimetype): + if str(mimetype).lower() in ["text/html", "text/xhtml"]: + redirect(url(controller="datasets", action="datasetview", id=id, silo=silo)) + elif str(mimetype).lower() in ["text/plain", "application/json"]: + response.content_type = "text/plain" + return response_message + try: + mimetype = accept_list.pop(0) + except IndexError: + mimetype = None + #Whoops - nothing satisfies - return text / plain + response.content_type = "text/plain" + return response_message + elif http_method == "DELETE": + item = c_silo.get_item(id) + creator = None + if item.manifest and item.manifest.state and 'metadata' in item.manifest.state and item.manifest.state['metadata'] and \ + 'createdby' in item.manifest.state['metadata'] and item.manifest.state['metadata']['createdby']: + creator = item.manifest.state['metadata']['createdby'] + #if not (ident['repoze.who.userid'] == creator or ident.get('role') in ["admin", "manager"]): + if not (ident['repoze.who.userid'] == creator or silo in silos_admin or silo in silos_manager): + abort(403) + + try: + ag.r.delete("%s:%s:embargoed_until" % (c_silo.state['storage_dir'], id)) + ag.r.delete("%s:%s:embargoed" % (c_silo.state['storage_dir'], id)) + except: + pass + + # Broadcast deletion + try: + ag.b.deletion(silo, id, ident=ident['repoze.who.userid']) + except: + pass + + c_silo.del_item(id) + delete_dataset(silo, id) + + response.content_type = "text/plain" + response.status_int = 200 + response.status = "200 OK" + return "{'ok':'true'}" # required for the JQuery magic delete to succede. + + @rest.restrict('GET', 'POST', 'PUT', 'DELETE') + def itemview(self, silo, id, path): + if not ag.granary.issilo(silo): + abort(404) + + c.silo_name = silo + c.id = id + c.path = path + + c_silo = ag.granary.get_rdf_silo(silo) + if not c_silo.exists(id): + abort(404) + + ident = request.environ.get('repoze.who.identity') + c.ident = ident + item = c_silo.get_item(id) + creator = None + if item.manifest and item.manifest.state and 'metadata' in item.manifest.state and item.manifest.state['metadata'] and \ + 'createdby' in item.manifest.state['metadata'] and item.manifest.state['metadata']['createdby']: + creator = item.manifest.state['metadata']['createdby'] + + c.version = None + c.editor = False + + http_method = request.environ['REQUEST_METHOD'] + + if not (http_method == "GET"): + #identity management of item + if not request.environ.get('repoze.who.identity'): + abort(401, "Not Authorised") + silos = ag.authz(ident) + if silo not in silos: + abort(403, "Forbidden") + silos_admin = ag.authz(ident, permission='administrator') + silos_manager = ag.authz(ident, permission='manager') + #if not (ident['repoze.who.userid'] == creator or ident.get('role') in ["admin", "manager"]): + if not (ident['repoze.who.userid'] == creator or silo in silos_admin or silo in silos_manager): + abort(403, "Forbidden") + elif http_method == "GET": + embargoed = False + options = request.GET + + currentversion = str(item.currentversion) + c.version = currentversion + if 'version' in options: + if not options['version'] in item.manifest['versions']: + abort(404) + c.version = str(options['version']) + if c.version and not c.version == currentversion: + item.set_version_cursor(c.version) + + if ag.metadata_embargoed: + if not ident: + abort(401, "Not Authorised") + silos = ag.authz(ident) + if silo not in silos: + abort(403, "Forbidden") + silos_admin = ag.authz(ident, permission='administrator') + silos_manager = ag.authz(ident, permission='manager') + #if ident['repoze.who.userid'] == creator or ident.get('role') in ["admin", "manager"]: + if ident['repoze.who.userid'] == creator or silo in silos_admin or silo in silos_manager: + c.editor = True + elif item.metadata.get('embargoed') not in ["false", 0, False]: + if not ident: + abort(401) + silos = ag.authz(ident) + if silo not in silos: + abort(403) + silos_admin = ag.authz(ident, permission='administrator') + silos_manager = ag.authz(ident, permission='manager') + #if not ident['repoze.who.userid'] == creator and not ident.get('role') in ["admin", "manager"]: + if not (ident['repoze.who.userid'] == creator or silo in silos_admin or silo in silos_manager): + abort(403) + embargoed = True + c.editor = True + elif ident: + silos = ag.authz(ident) + silos_admin = ag.authz(ident, permission='administrator') + silos_manager = ag.authz(ident, permission='manager') + if silo in silos: + #if ident['repoze.who.userid'] == creator or ident.get('role') in ["admin", "manager"]: + if ident['repoze.who.userid'] == creator or silo in silos_admin or silo in silos_manager: + c.editor = True + + c.show_files = True + #Only the administrator, manager and creator can view embargoed files. + if embargoed and not c.editor: + c.show_files = False + + #Display but do not edit previous versions of files, since preious versions are read only. + if c.version and not c.version == currentversion: + c.editor = False + + # View options + if "view" in options and c.editor: + c.view = options['view'] + elif c.editor: + c.view = 'editor' + else: + c.view = 'user' + + if http_method == "GET": + if item.isfile(path): + fileserve_app = FileApp(item.to_dirpath(path)) + return fileserve_app(request.environ, self.start_response) + elif item.isdir(path): + #c.parts = item.list_parts(detailed=True) + c.versions = item.manifest['versions'] + c.versions = natural_sort(c.versions) + c.parts = item.list_parts(path, detailed=True) + c.readme_text = None + if "README" in c.parts.keys(): + c.readme_text = get_readme_text(item, "%s/README" % path) + + accept_list = None + if 'HTTP_ACCEPT' in request.environ: + try: + accept_list = conneg_parse(request.environ['HTTP_ACCEPT']) + except: + accept_list= [MT("text", "html")] + if not accept_list: + accept_list= [MT("text", "html")] + mimetype = accept_list.pop(0) + while(mimetype): + if str(mimetype).lower() in ["text/html", "text/xhtml"]: + return render("/itemview.html") + elif str(mimetype).lower() in ["text/plain", "application/json"]: + response.content_type = 'application/json; charset="UTF-8"' + response.status_int = 200 + response.status = "200 OK" + returndata = {} + returndata['parts'] = {} + for part in c.parts: + returndata['parts'][part] = serialisable_stat(c.parts[part]) + returndata['readme_text'] = c.readme_text + return simplejson.dumps(returndata) + try: + mimetype = accept_list.pop(0) + except IndexError: + mimetype = None + #Whoops - nothing satisfies - return text/html + return render("/itemview.html") + else: + abort(404) + elif http_method == "PUT": + # Pylons loads the request body into request.body... + # This is not going to work for large files... ah well + # POST will handle large files as they are pushed to disc, + # but this won't + content = request.body + + if JAILBREAK.search(path) != None: + abort(400, "'..' cannot be used in the path") + + if item.isfile(path): + code = 204 + elif item.isdir(path): + response.content_type = "text/plain" + response.status_int = 403 + response.status = "403 Forbidden" + return "Cannot PUT a file on to an existing directory" + else: + code = 201 + + #Check if path is manifest.rdf - If, yes Munge + if "manifest.rdf" in path: + fname = '/tmp/%s'%uuid4().hex + f = open(fname, 'w') + f.write(content) + f.close() + #test content is valid rdf + #if not test_rdf(content): + "Manifest file created:", fname + if not test_rdf(fname): + response.status_int = 400 + return "Bad manifest file" + #munge rdf + item.increment_version_delta(clone_previous_version=True, copy_filenames=['manifest.rdf']) + a = item.get_rdf_manifest() + b = a.to_string() + #munge_manifest(content, item) + munge_manifest(fname, item) + os.remove(fname) + else: + if code == 204: + item.increment_version_delta(clone_previous_version=True, copy_filenames=['manifest.rdf', path]) + else: + item.increment_version_delta(clone_previous_version=True, copy_filenames=['manifest.rdf']) + item.put_stream(path, content) + item.del_triple(item.uri, u"dcterms:modified") + item.add_triple(item.uri, u"dcterms:modified", datetime.now()) + item.del_triple(item.uri, u"oxds:currentVersion") + item.add_triple(item.uri, u"oxds:currentVersion", item.currentversion) + item.sync() + + if code == 201: + try: + ag.b.creation(silo, id, path, ident=ident['repoze.who.userid']) + except: + pass + response.status = "201 Created" + response.status_int = 201 + response.headers["Content-Location"] = url(controller="datasets", action="itemview", id=id, silo=silo, path=path) + response_message = "201 Created" + else: + try: + ag.b.change(silo, id, path, ident=ident['repoze.who.userid']) + except: + pass + response.status = "204 Updated" + response.status_int = 204 + response_message = None + + # conneg return + accept_list = None + if 'HTTP_ACCEPT' in request.environ: + try: + accept_list = conneg_parse(request.environ['HTTP_ACCEPT']) + except: + accept_list= [MT("text", "html")] + if not accept_list: + accept_list= [MT("text", "html")] + mimetype = accept_list.pop(0) + while(mimetype): + if str(mimetype).lower() in ["text/html", "text/xhtml"]: + redirect(url(controller="datasets", action="itemview", id=id, silo=silo, path=path)) + elif str(mimetype).lower() in ["text/plain", "application/json"]: + response.content_type = "text/plain" + return response_message + try: + mimetype = accept_list.pop(0) + except IndexError: + mimetype = None + #Whoops - nothing satisfies - return text / plain + response.content_type = "text/plain" + return response_message + elif http_method == "POST": + # POST... differences from PUT: + # path = filepath that this acts on, should be dir, or non-existant + # if path is a file, this will revert to PUT's functionality and + # overwrite the file, if there is a multipart file uploaded + # Expected params: filename, file (uploaded file) + params = request.POST + if not params.has_key('file'): + abort(400, "No file was received") + filename = params.get('filename') + upload = params.get('file') + if not filename: + filename = params['file'].filename + if filename and JAILBREAK.search(filename) != None: + abort(400, "'..' cannot be used in the path or as a filename") + target_path = path + if item.isdir(path) and filename: + target_path = os.path.join(path, filename) + + if item.isfile(target_path): + code = 204 + elif item.isdir(target_path): + response.content_type = "text/plain" + response.status_int = 403 + response.status = "403 Forbidden" + return "Cannot POST a file on to an existing directory" + else: + code = 201 + + if filename == "manifest.rdf": + #Copy the uploaded file to a tmp area + #mani_file = os.path.join('/tmp', filename.lstrip(os.sep)) + mani_file = os.path.join('/tmp', uuid4().hex) + mani_file_obj = open(mani_file, 'w') + shutil.copyfileobj(upload.file, mani_file_obj) + upload.file.close() + mani_file_obj.close() + #test rdf file + if not test_rdf(mani_file): + response.status_int = 400 + return "Bad manifest file" + #munge rdf + item.increment_version_delta(clone_previous_version=True, copy_filenames=['manifest.rdf']) + a = item.get_rdf_manifest() + b = a.to_string() + #munge_manifest(manifest_str, item) + munge_manifest(mani_file, item) + os.remove(mani_file) + else: + if code == 204: + item.increment_version_delta(clone_previous_version=True, copy_filenames=['manifest.rdf', filename]) + else: + item.increment_version_delta(clone_previous_version=True, copy_filenames=['manifest.rdf']) + item.put_stream(target_path, upload.file) + upload.file.close() + item.del_triple(item.uri, u"dcterms:modified") + item.add_triple(item.uri, u"dcterms:modified", datetime.now()) + item.del_triple(item.uri, u"oxds:currentVersion") + item.add_triple(item.uri, u"oxds:currentVersion", item.currentversion) + item.sync() + + if code == 201: + try: + ag.b.creation(silo, id, target_path, ident=ident['repoze.who.userid']) + except: + pass + response.status = "201 Created" + response.status_int = 201 + response.headers["Content-Location"] = url(controller="datasets", action="itemview", id=id, silo=silo, path=path) + response_message = "201 Created" + else: + try: + ag.b.change(silo, id, target_path, ident=ident['repoze.who.userid']) + except: + pass + response.status = "204 Updated" + response.status_int = 204 + response_message = None + + # conneg return + accept_list = None + if 'HTTP_ACCEPT' in request.environ: + try: + accept_list = conneg_parse(request.environ['HTTP_ACCEPT']) + except: + accept_list= [MT("text", "html")] + if not accept_list: + accept_list= [MT("text", "html")] + mimetype = accept_list.pop(0) + while(mimetype): + if str(mimetype).lower() in ["text/html", "text/xhtml"]: + redirect(url(controller="datasets", action="itemview", id=id, silo=silo, path=path)) + elif str(mimetype).lower() in ["text/plain", "application/json"]: + response.content_type = "text/plain" + return response_message + try: + mimetype = accept_list.pop(0) + except IndexError: + mimetype = None + #Whoops - nothing satisfies - return text / plain + response.content_type = "text/plain" + return response_message + elif http_method == "DELETE": + if item.isfile(path): + if 'manifest.rdf' in path: + response.content_type = "text/plain" + response.status_int = 403 + response.status = "403 Forbidden" + return "Forbidden - Cannot delete the manifest" + if '3=' in path or '4=' in path: + response.content_type = "text/plain" + response.status_int = 403 + response.status = "403 Forbidden" + return "Forbidden - These files are generated by the system and connot be deleted" + item.increment_version_delta(clone_previous_version=True, copy_filenames=['manifest.rdf']) + item.del_stream(path) + item.del_triple(item.uri, u"dcterms:modified") + item.add_triple(item.uri, u"dcterms:modified", datetime.now()) + item.del_triple(item.uri, u"oxds:currentVersion") + item.add_triple(item.uri, u"oxds:currentVersion", item.currentversion) + item.sync() + try: + #ag.b.deletion(silo, id, path, ident=ident['repoze.who.userid']) + ag.b.change(silo, id, path, ident=ident['repoze.who.userid']) + except: + pass + response.content_type = "text/plain" + response.status_int = 200 + response.status = "200 OK" + return "{'ok':'true'}" # required for the JQuery magic delete to succede. + elif item.isdir(path): + item.increment_version_delta(clone_previous_version=True, copy_filenames=['manifest.rdf']) + item.del_triple(item.uri, u"oxds:currentVersion") + item.add_triple(item.uri, u"oxds:currentVersion", item.currentversion) + item.del_dir(path) + item.del_triple(item.uri, u"dcterms:modified") + item.add_triple(item.uri, u"dcterms:modified", datetime.now()) + item.sync() + try: + #ag.b.deletion(silo, id, path, ident=ident['repoze.who.userid']) + ag.b.change(silo, id, path, ident=ident['repoze.who.userid']) + except: + pass + response.content_type = "text/plain" + response.status_int = 200 + response.status = "200 OK" + return "{'ok':'true'}" # required for the JQuery magic delete to succede. + else: + abort(404) + diff --git a/rdfdatabank/controllers/doi.py b/rdfdatabank/controllers/doi.py new file mode 100644 index 0000000..9ea87cf --- /dev/null +++ b/rdfdatabank/controllers/doi.py @@ -0,0 +1,331 @@ +# -*- coding: utf-8 -*- +""" +Copyright (c) 2012 University of Oxford + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, --INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +""" + +from pylons import request, response, session, tmpl_context as c, url, app_globals as ag +from pylons.controllers.util import abort +from pylons.decorators import rest +from datetime import datetime +from rdflib import Literal, URIRef + +from rdfdatabank.lib.base import BaseController, render +from rdfdatabank.lib.conneg import MimeType as MT, parse as conneg_parse +from rdfdatabank.lib.HTTP_request import HTTPRequest +from rdfdatabank.lib import short_pid +from rdfdatabank.lib.auth_entry import list_silos +from rdfdatabank.lib.doi_helper import get_doi_metadata, doi_count + +from rdfdatabank.config.doi_config import OxDataciteDoi + +class DoiController(BaseController): + """Class to generate and register DOIs along with the metadata of the data-package (POST), + update the metadata registered with the DOI (PUT), delete the DOI (DELETE) and + to get the information (GET) registered with Datacite - the organization responsible for minting DOIs + + if the metadata for the data package is also under embargo, then a DOI cannot be registered for such data-packages + """ + @rest.restrict('GET', 'POST', 'PUT', 'DELETE') + def datasetview(self, silo, id): + c.silo_name = silo + c.id = id + + http_method = request.environ['REQUEST_METHOD'] + + granary_list = list_silos() + if not silo in granary_list: + abort(404) + + c_silo = ag.granary.get_rdf_silo(silo) + if not c_silo.exists(id): + abort(404) + + if ag.metadata_embargoed: + abort(403, "DOIs cannot be issued to data packages whose metadata ia also under embargo") + + ident = request.environ.get('repoze.who.identity') + c.ident = ident + + item = c_silo.get_item(id) + + creator = None + if item.manifest and item.manifest.state and 'metadata' in item.manifest.state and item.manifest.state['metadata'] and \ + 'createdby' in item.manifest.state['metadata'] and item.manifest.state['metadata']['createdby']: + creator = item.manifest.state['metadata']['createdby'] + + c.version = item.currentversion + c.version_doi = None + c.editor = False + + #Get version number + vnum = request.params.get('version', '') or "" + if vnum: + vnum = str(vnum) + if not vnum in item.manifest['versions']: + abort(404, "Version %s of data package %s not found"%(vnum, c.silo_name)) + c.version = vnum + + if not (http_method == "GET"): + #identity management of item + if not request.environ.get('repoze.who.identity'): + abort(401, "Not Authorised") + silos = ag.authz(ident) + if silo not in silos: + abort(403, "Forbidden") + silos_admin = ag.authz(ident, permission='administrator') + silos_manager = ag.authz(ident, permission='manager') + #if not (ident['repoze.who.userid'] == creator or ident.get('role') in ["admin", "manager"]): + if not (ident['repoze.who.userid'] == creator or silo in silos_admin or silo in silos_manager): + abort(403, "Forbidden") + elif http_method == "GET": + silos_admin = ag.authz(ident, permission='administrator') + silos_manager = ag.authz(ident, permission='manager') + #if ident['repoze.who.userid'] == creator or ident.get('role') in ["admin", "manager"]: + if ident['repoze.who.userid'] == creator or silo in silos_admin or silo in silos_manager: + c.editor = True + + version_uri = "%s/version%s"%(item.uri.rstrip('/'), c.version) + c.version_doi = item.list_rdf_objects(URIRef(version_uri), u"http://purl.org/ontology/bibo/doi") + if not c.version_doi or not c.version_doi[0]: + c.version_doi = None + else: + c.version_doi = c.version_doi[0] + + doi_conf = OxDataciteDoi() + doi_api = HTTPRequest(endpointhost=doi_conf.endpoint_host, secure=True) + doi_api.setRequestUserPass(endpointuser=doi_conf.account, endpointpass=doi_conf.password) + + # conneg: + accept_list = None + if 'HTTP_ACCEPT' in request.environ: + try: + accept_list = conneg_parse(request.environ['HTTP_ACCEPT']) + except: + accept_list= [MT("text", "html")] + if not accept_list: + accept_list= [MT("text", "html")] + + c.message = None + c.resp_status = None + c.resp_reason = None + c.metadata = None + + if http_method == "GET": + #Get a list of all dois registered for this data package + c.dois = {} + for v in item.manifest['versions']: + doi_ans = None + doi_ans = item.list_rdf_objects(URIRef("%s/version%s"%(item.uri.rstrip('/'), v)), u"http://purl.org/ontology/bibo/doi") + if doi_ans and doi_ans[0]: + c.dois[v] = doi_ans[0] + + c.heading = "Doi metadata information from Datacite" + if not c.version_doi: + mimetype = accept_list.pop(0) + while(mimetype): + if str(mimetype).lower() in ["text/html", "text/xhtml"]: + #Doint this to avoid displaying the erro page!!! + response.status_int = 200 + response.status = "200 OK" + c.metadata = None + return render('/doiview.html') + try: + mimetype = accept_list.pop(0) + except IndexError: + mimetype = None + c.message = 'DOI not registered for version %s of data package %s'%(c.version, c.silo_name) + return render('/doiview.html') + + resource = "%s?doi=%s"%(doi_conf.endpoint_path_metadata, c.version_doi) + (resp, respdata) = doi_api.doHTTP_GET(resource=resource, expect_type='application/xml') + c.resp_reason = resp.reason + c.resp_status = resp.status + if resp.status < 200 or resp.status >= 300: + response.status_int = 400 + response.status = "400 Bad Request" + response_msg = '' + c.metadata = '' + if resp.status == 403: + #TODO: Confirm 403 is not due to authorization + msg = "403 Forbidden - login error with Datacite or data package belongs to another party at Datacite." + elif resp.status == 404: + msg = "404 Not Found - DOI does not exist in DatCite's database" + elif resp.status == 410: + msg = "410 Gone - the requested data package was marked inactive (using DELETE method) at Datacite" + elif resp.status == 500: + msg = "500 Internal Server Error - Error retreiving the metadata from Datacite." + else: + msg = "Error retreiving the metadata from Datacite. %s"%str(resp.status) + c.message = msg + else: + response.status_int = 200 + response.status = "200 OK" + c.metadata = respdata + response_msg = respdata + # conneg: + mimetype = accept_list.pop(0) + while(mimetype): + if str(mimetype).lower() in ["text/html", "text/xhtml"]: + #Setting headers to 200 to avoid displaying the error page!!! + response.status_int = 200 + response.status = "200 OK" + return render('/doiview.html') + elif str(mimetype).lower() in ["text/plain", "application/json"]: + response.content_type = 'text/plain; charset="UTF-8"' + return str(respdata.decode('utf-8')) + elif str(mimetype).lower() in ["application/rdf+xml", "text/xml"]: + response.status_int = 200 + response.status = "200 OK" + response.content_type = 'text/xml; charset="UTF-8"' + return response_msg + try: + mimetype = accept_list.pop(0) + except IndexError: + mimetype = None + #Whoops - nothing staisfies - default to text/html + #Setting headers to 200 to avoid displaying the error page!!! + response.status_int = 200 + response.status = "200 OK" + return render('/doiview.html') + + if http_method == "POST": + item.set_version_cursor(c.version) + #1a. If doi doen not exist for this version, generate doi + register_doi = False + if not c.version_doi: + cnt = doi_count() + if not cnt: + abort(400, "Error generating DOI") + register_doi = True + tiny_pid = short_pid.encode_url(cnt) + c.version_doi = "%s/bodleian%s.%s"%(doi_conf.prefix, tiny_pid, c.version) + #1b. Construct XML metadata + xml_metadata = get_doi_metadata(c.version_doi, item) + c.metadata = xml_metadata + #FOR TEST PURPOSES ONLY + #xml_metadata = False + if not xml_metadata and not register_doi: + #2a. If the doi already exists and there is no xml metadata to update, return bad request + c.message = "Coud not update matadata" + response.status_int = 400 + response.status = "Bad request" + c.metadata = '' + elif not xml_metadata and register_doi: + #2b. If the doi is not registered, but there is no xml metadata to update, register just the doi with datacite + c.heading = "Registering new DOI with Datacite" + resource = "%s"%doi_conf.endpoint_path_doi + body = "%s\n%s"%(c.version_doi, version_uri) + #body_unicode = unicode(body, "utf-8") + body_unicode = unicode(body) + (resp, respdata) = doi_api.doHTTP_POST(body_unicode, resource=resource, data_type='text/plain;charset=UTF-8') + c.resp_reason = resp.reason + c.resp_status = resp.status + if resp.status < 200 or resp.status >= 300: + response.status_int = 400 + response.status = "400 Bad Request" + response_msg = "DOI not registered" + c.metadata = '' + if resp.status == 400: + msg = "400 Bad Request - Request body must be exactly two lines: DOI and URL" + elif resp.status == 403: + #TODO: Confirm 403 is not due to authorization + msg = "403 Forbidden - From Datacite: login problem, quota excceded, wrong domain, wrong prefix" + elif resp.status == 500: + msg = "500 Internal Server Error - Error registering the DOI." + else: + msg = "Error retreiving the metadata from Datacite. %s"%str(resp.status) + c.message = msg + else: + #3. Add the DOI to the rdf metadata + item.add_namespace('bibo', "http://purl.org/ontology/bibo/") + item.add_triple(URIRef(version_uri), u"bibo:doi", Literal(c.version_doi)) + item.del_triple(item.uri, u"dcterms:modified") + item.add_triple(item.uri, u"dcterms:modified", datetime.now()) + item.sync() + response.status_int = 200 + response.status = "200 OK" + response_msg = "DOI Registered. %s"%respdata + c.metadata = '' + c.message = "201 Created - DOI registered. %s"%respdata + else: + #register the DOI and metadata with Datacite + c.heading = "Registering new DOI along with its metadata with Datacite" + #body_unicode = unicode(xml_metadata, "utf-8") + #body_unicode = unicode(xml_metadata) + body_unicode = xml_metadata + resource = "%s?doi=%s&url=%s"%(doi_conf.endpoint_path_metadata, c.version_doi, version_uri) + (resp, respdata) = doi_api.doHTTP_POST(body_unicode, resource=resource, data_type='application/xml;charset=UTF-8') + c.resp_reason = resp.reason + c.resp_status = resp.status + if resp.status < 200 or resp.status >= 300: + response.status_int = 400 + response.status = "400 Bad Request" + response_msg = "DOI and metadata not registered" + c.metadata = body_unicode + if resp.status == 400: + msg = "400 Bad Request - Invalid XML metadata" + elif resp.status == 403: + #TODO: Confirm 403 is not due to authorization + msg = "403 Forbidden - From Datacite: login problem, quota excceded, wrong domain, wrong prefix" + elif resp.status == 500: + msg = "500 Internal Server Error - Error registering the DOI." + else: + msg = "Error retreiving the metadata from Datacite. %s"%str(resp.status) + c.message = msg + else: + #3. Add the DOI to the rdf metadata + item.add_namespace('bibo', "http://purl.org/ontology/bibo/") + item.add_triple(URIRef(version_uri), u"bibo:doi", Literal(c.version_doi)) + item.del_triple(item.uri, u"dcterms:modified") + item.add_triple(item.uri, u"dcterms:modified", datetime.now()) + item.sync() + response.status_int = 200 + response.status = "200 OK" + response_msg = body_unicode + c.metadata = body_unicode + c.message = "201 Created - DOI registered. %s"%respdata + # conneg: + mimetype = accept_list.pop(0) + while(mimetype): + if str(mimetype).lower() in ["text/html", "text/xhtml"]: + #Setting headers to 200 to avoid displaying the error page!!! + response.status_int = 200 + response.status = "200 OK" + return render('/doiview.html') + elif str(mimetype).lower() in ["text/plain", "application/json"]: + response.content_type = 'text/plain; charset="UTF-8"' + return str(respdata.decode('utf-8')) + elif str(mimetype).lower() in ["application/rdf+xml", "text/xml"]: + response.status_int = 200 + response.status = "200 OK" + response.content_type = 'text/xml; charset="UTF-8"' + return response_msg + try: + mimetype = accept_list.pop(0) + except IndexError: + mimetype = None + #Whoops - nothing staisfies - default to text/html + #Setting headers to 200 to avoid displaying the error page!!! + response.status_int = 200 + response.status = "200 OK" + return render('/doiview.html') + diff --git a/rdfdatabank/controllers/error.py b/rdfdatabank/controllers/error.py index f7bc3bc..78f5daf 100644 --- a/rdfdatabank/controllers/error.py +++ b/rdfdatabank/controllers/error.py @@ -1,12 +1,37 @@ +# -*- coding: utf-8 -*- +""" +Copyright (c) 2012 University of Oxford + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, --INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +""" + import cgi from paste.urlparser import PkgResourcesParser -from pylons import request +from pylons import request, response, tmpl_context as c from pylons.controllers.util import forward from pylons.middleware import error_document_template from webhelpers.html.builder import literal -from rdfdatabank.lib.base import BaseController +from rdfdatabank.lib.base import BaseController, render +from rdfdatabank.lib.conneg import MimeType as MT, parse as conneg_parse class ErrorController(BaseController): @@ -22,13 +47,64 @@ class ErrorController(BaseController): def document(self): """Render the error document""" + icode = 404 + code= "404" + status = "Not Found" resp = request.environ.get('pylons.original_response') - content = literal(resp.body) or cgi.escape(request.GET.get('message', '')) - page = error_document_template % \ - dict(prefix=request.environ.get('SCRIPT_NAME', ''), - code=cgi.escape(request.GET.get('code', str(resp.status_int))), - message=content) - return page + if resp and resp.body: + content = literal(resp.body) + else: + content = request.GET.get('message', '') + if content: + content = cgi.escape(content) + if resp and resp.status_int: + icode = resp.status_int + code = str(resp.status_int) + elif request.GET.get('code', ''): + code = request.GET.get('code') + if code: + code = cgi.escape(code) + else: + code = 404 + if resp and resp.status: + status = resp.status + c.message = request.GET.get('message', '') + if c.message: + c.message = cgi.escape(c.message) + else: + c.message = content + accept_list = None + if 'HTTP_ACCEPT' in request.environ: + try: + accept_list = conneg_parse(request.environ['HTTP_ACCEPT']) + except: + accept_list= [MT("text", "plain")] + if not accept_list: + accept_list= [MT("text", "plain")] + mimetype = accept_list.pop(0) + while(mimetype): + if str(mimetype).lower() in ["text/html", "text/xhtml"]: + #page = error_document_template % \ + #dict(prefix=request.environ.get('SCRIPT_NAME', ''), + # code=code, + # message=content) + #return page + c.status = status.replace(c.code, '').strip() + return render('/error.html') + elif str(mimetype).lower() in ["text/plain", "application/json"]: + response.content_type = 'text/plain; charset="UTF-8"' + response.status_int = icode + response.status = status + return content + try: + mimetype = accept_list.pop(0) + except IndexError: + mimetype = None + #Whoops nothing satisfies - return text/plain + response.content_type = 'text/plain; charset="UTF-8"' + response.status_int = resp.status_int + response.status = resp.status + return content def img(self, id): """Serve Pylons' stock images""" diff --git a/rdfdatabank/controllers/home.py b/rdfdatabank/controllers/home.py new file mode 100644 index 0000000..be6e28c --- /dev/null +++ b/rdfdatabank/controllers/home.py @@ -0,0 +1,31 @@ +# -*- coding: utf-8 -*- +""" +Copyright (c) 2012 University of Oxford + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, --INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +""" + +import logging + +from rdfdatabank.lib.base import BaseController, render + +class HomeController(BaseController): + def index(self): + return render('/home.html') diff --git a/rdfdatabank/controllers/items.py b/rdfdatabank/controllers/items.py new file mode 100644 index 0000000..08a9449 --- /dev/null +++ b/rdfdatabank/controllers/items.py @@ -0,0 +1,506 @@ +# -*- coding: utf-8 -*- +""" +Copyright (c) 2012 University of Oxford + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, --INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +""" + +import logging +import re, os, time +from datetime import datetime, timedelta +import simplejson +from pylons import request, response, session, tmpl_context as c, url, app_globals as ag +from pylons.controllers.util import abort, redirect +from pylons.decorators import rest + +from rdfdatabank.lib.base import BaseController, render +from rdfdatabank.lib.utils import create_new, allowable_id2 +from rdfdatabank.lib.file_unpack import check_file_mimetype, BadZipfile, get_zipfiles_in_dataset, unpack_zip_item, read_zipfile +from rdfdatabank.lib.conneg import MimeType as MT, parse as conneg_parse + +log = logging.getLogger(__name__) +JAILBREAK = re.compile("[\/]*\.\.[\/]*") + +class ItemsController(BaseController): + def siloview(self, silo): + abort(403, "Forbidden") + + @rest.restrict('GET', 'POST') + def datasetview(self, silo, id): + """Get a list of zipfiles in dataset 'id' within the silo 'silo' and unpack a dataset.""" + + if not ag.granary.issilo(silo): + abort(404) + + rdfsilo = ag.granary.get_rdf_silo(silo) + if not rdfsilo.exists(id): + abort (404) + + #tmpl_context variables needed: c.silo_name, c.zipfiles, c.ident, c.id, c.path + c.silo_name = silo + c.id = id + ident = request.environ.get('repoze.who.identity') + c.ident = ident + dataset = rdfsilo.get_item(id) + + creator = None + if dataset.manifest and dataset.manifest.state and 'metadata' in dataset.manifest.state and dataset.manifest.state['metadata'] and \ + 'createdby' in dataset.manifest.state['metadata'] and dataset.manifest.state['metadata']['createdby']: + creator = dataset.manifest.state['metadata']['createdby'] + + http_method = request.environ['REQUEST_METHOD'] + + if http_method == "GET": + c.editor = False + if ag.metadata_embargoed: + if not ident: + abort(401, "Not Authorised") + silos = ag.authz(ident) + if silo not in silos: + abort(403, "Forbidden") + silos_admin = ag.authz(ident, permission='administrator') + silos_manager = ag.authz(ident, permission='manager') + #if ident['repoze.who.userid'] == creator or ident.get('role') in ["admin", "manager"]: + if ident['repoze.who.userid'] == creator or silo in silos_admin or silo in silos_manager: + c.editor = True + elif ident: + silos = ag.authz(ident) + if silo in silos: + silos_admin = ag.authz(ident, permission='administrator') + silos_manager = ag.authz(ident, permission='manager') + #if ident['repoze.who.userid'] == creator or ident.get('role') in ["admin", "manager"]: + if ident['repoze.who.userid'] == creator or silo in silos_admin or silo in silos_manager: + c.editor = True + else: + #identity management of item + if not ident: + abort(401, "Not Authorised") + silos = ag.authz(ident) + if silo not in silos: + abort(403, "Forbidden") + silos_admin = ag.authz(ident, permission='administrator') + silos_manager = ag.authz(ident, permission='manager') + #if not (ident['repoze.who.userid'] == creator or ident.get('role') in ["admin", "manager"]): + if not (ident['repoze.who.userid'] == creator or silo in silos_admin or silo in silos_manager): + abort(403, "Forbidden") + + if http_method == "GET": + c.zipfiles = get_zipfiles_in_dataset(dataset) + # conneg return + accept_list = None + if 'HTTP_ACCEPT' in request.environ: + try: + accept_list = conneg_parse(request.environ['HTTP_ACCEPT']) + except: + accept_list= [MT("text", "html")] + if not accept_list: + accept_list= [MT("text", "html")] + mimetype = accept_list.pop(0) + while(mimetype): + if str(mimetype).lower() in ["text/html", "text/xhtml"]: + return render("/list_of_zipfiles.html") + elif str(mimetype).lower() in ["text/plain", "application/json"]: + response.content_type = 'application/json; charset="UTF-8"' + response.status_int = 200 + response.status = "200 OK" + #return simplejson.dumps(dict(c.zipfiles)) + return simplejson.dumps(list(c.zipfiles.keys())) + try: + mimetype = accept_list.pop(0) + except IndexError: + mimetype = None + #Whoops nothing satisfies - return text/html + return render("/list_of_zipfiles.html") + elif http_method == "POST": + params = request.POST + if not (params.has_key("filename") and params['filename']): + abort(400, "You must supply a filename to unpack") + + item_real_filepath = dataset.to_dirpath() + target_filepath = "%s/%s"%(item_real_filepath, params['filename']) + if not os.path.isfile(target_filepath): + abort(404, "File to unpack not found") + if not check_file_mimetype(target_filepath, 'application/zip'): + abort(415, "File is not of type application/zip") + + if params.has_key("id") and params['id']: + target_dataset_name = params['id'] + else: + #(head, fn) = os.path.split(params['filename']) + #(fn, ext) = os.path.splitext(fn) + #target_dataset_name = "%s-%s"%(id,fn) + target_dataset_name = id + + #step 1: Create / initialize target dataset + if not rdfsilo.exists(target_dataset_name): + if not allowable_id2(target_dataset_name): + response.content_type = "text/plain" + response.status_int = 400 + response.status = "400 Bad request. Data package name not valid" + return "Data package name can contain only the following characters - %s and has to be more than 1 character"%ag.naming_rule_humanized + target_dataset = create_new(rdfsilo, target_dataset_name, ident['repoze.who.userid']) + response.status_int = 201 + response.status = "201 Created" + response.headers["Content-Location"] = url(controller="datasets", action="datasetview", silo=silo, id=target_dataset_name) + response_message = "201 Created" + else: + target_dataset = rdfsilo.get_item(target_dataset_name) + response.status = "204 Updated" + response.status_int = 204 + response_message = None + + #step 2: Unpack zip item + try: + unpack_zip_item(target_dataset, dataset, params['filename'], rdfsilo, ident['repoze.who.userid']) + except BadZipfile: + abort(400, "BadZipfile: Couldn't unpack zipfile") + + target_dataset.sync() + target_dataset.sync() + target_dataset.sync() + + if response.status_int == 201: + try: + ag.b.creation(silo, id, ident=ident['repoze.who.userid']) + except: + pass + else: + try: + ag.b.change(silo, id, ident=ident['repoze.who.userid']) + except: + pass + + # conneg return + accept_list = None + if 'HTTP_ACCEPT' in request.environ: + try: + accept_list = conneg_parse(request.environ['HTTP_ACCEPT']) + except: + accept_list= [MT("text", "html")] + if not accept_list: + accept_list= [MT("text", "html")] + mimetype = accept_list.pop(0) + while(mimetype): + if str(mimetype).lower() in ["text/html", "text/xhtml"]: + redirect(url(controller="datasets", action="datasetview", silo=silo, id=target_dataset_name)) + elif str(mimetype).lower() in ["text/plain", "application/json"]: + response.content_type = "text/plain" + return response_message + try: + mimetype = accept_list.pop(0) + except IndexError: + mimetype = None + # Whoops - nothing satisfies - return text/plain + response.content_type = "text/plain" + return response_message + + @rest.restrict('GET', 'POST', 'PUT') + def itemview(self, silo, id, path): + """API call to + GET - read the contents of a zip-file (without having to unpack) and + POST- unpack a zip file into a new / existing dataset + PUT - Add the zipfile and unpack it onto the existing dataset""" + #tmpl_context variables needed: c.silo_name, c.zipfile_contents c.ident, c.id, c.path + if not path: + abort(400, "You must supply a filename to unpack") + + if not ag.granary.issilo(silo): + abort(404) + + rdfsilo = ag.granary.get_rdf_silo(silo) + if not rdfsilo.exists(id): + abort (404) + + c.silo_name = silo + c.id = id + c.path = path + + ident = request.environ.get('repoze.who.identity') + c.ident = ident + dataset = rdfsilo.get_item(id) + + creator = None + if dataset.manifest and dataset.manifest.state and 'metadata' in dataset.manifest.state and dataset.manifest.state['metadata'] and \ + 'createdby' in dataset.manifest.state['metadata'] and dataset.manifest.state['metadata']['createdby']: + creator = dataset.manifest.state['metadata']['createdby'] + + http_method = request.environ['REQUEST_METHOD'] + + if http_method == "GET": + if dataset.metadata.get('embargoed') not in ["false", 0, False]: + if not ident: + abort(401, "Not Authorised") + silos = ag.authz(ident) + if silo not in silos: + abort(403, "Forbidden") + else: + if not ident: + abort(401, "Not Authorised") + silos = ag.authz(ident) + if silo not in silos: + abort(403, "Forbidden") + silos_admin = ag.authz(ident, permission='administrator') + silos_manager = ag.authz(ident, permission='manager') + #if not (ident['repoze.who.userid'] == creator or ident.get('role') in ["admin", "manager"]): + if not (ident['repoze.who.userid'] == creator or silo in silos_admin or silo in silos_manager): + abort(403, "Forbidden") + + item_real_filepath = dataset.to_dirpath() + target_filepath = "%s/%s"%(item_real_filepath, path) + #c.parts = dataset.list_parts(detailed=False) + + if http_method in ["GET", "POST"]: + if not dataset.isfile(path): + abort(404, "File not found") + if not os.path.isfile(target_filepath): + abort(404, "File not found") + if not check_file_mimetype(target_filepath, 'application/zip'): + abort(415, "File is not of type application/zip") + + if http_method == "GET": + try: + c.zipfile_contents = read_zipfile(target_filepath) + except BadZipfile: + abort(400, "Could not read zipfile") + # conneg return + accept_list = None + if 'HTTP_ACCEPT' in request.environ: + try: + accept_list = conneg_parse(request.environ['HTTP_ACCEPT']) + except: + accept_list= [MT("text", "html")] + if not accept_list: + accept_list= [MT("text", "html")] + mimetype = accept_list.pop(0) + while(mimetype): + if str(mimetype).lower() in ["text/html", "text/xhtml"]: + return render("/zipfileview.html") + elif str(mimetype).lower() in ["text/plain", "application/json"]: + response.content_type = 'application/json; charset="UTF-8"' + response.status_int = 200 + response.status = "200 OK" + return simplejson.dumps(c.zipfile_contents) + try: + mimetype = accept_list.pop(0) + except IndexError: + mimetype = None + # Whoops - nothing satisfies - return text/html + return render("/zipfileview.html") + elif http_method == "POST": + params = request.POST + #if not (params.has_key("filename") and params['filename']): + # abort(400, "You must supply a filename to unpack") + + if params.has_key("id") and params['id']: + target_dataset_name = params['id'] + else: + #(head, fn) = os.path.split(path) + #(fn, ext) = os.path.splitext(fn) + #target_dataset_name = "%s-%s"%(id,fn) + target_dataset_name = id + + #step 1: Create / initialize target dataset + if not rdfsilo.exists(target_dataset_name): + if not allowable_id2(target_dataset_name): + response.content_type = "text/plain" + response.status_int = 400 + response.status = "400 Bad request. Data package name not valid" + return "Data package name can contain only the following characters - %s and has to be more than 1 character"%ag.naming_rule_humanized + target_dataset = create_new(rdfsilo, target_dataset_name, ident['repoze.who.userid']) + response.status_int = 201 + response.status = "201 Created" + response.headers["Content-Location"] = url(controller="datasets", action="datasetview", silo=silo, id=target_dataset_name) + response_message = "201 Created" + else: + target_dataset = rdfsilo.get_item(target_dataset_name) + response.status = "204 Updated" + response.status_int = 204 + response_message = None + + #step 2: Unpack zip item + try: + unpack_zip_item(target_dataset_name, dataset, path, rdfsilo, ident['repoze.who.userid']) + except BadZipfile: + abort(400, "Couldn't unpack zipfile") + + target_dataset.sync() + target_dataset.sync() + target_dataset.sync() + + if response.status_int == 201: + try: + ag.b.creation(silo, id, ident=ident['repoze.who.userid']) + except: + pass + else: + try: + ag.b.change(silo, id, ident=ident['repoze.who.userid']) + except: + pass + + # conneg return + accept_list = None + if 'HTTP_ACCEPT' in request.environ: + try: + accept_list = conneg_parse(request.environ['HTTP_ACCEPT']) + except: + accept_list= [MT("text", "html")] + if not accept_list: + accept_list= [MT("text", "html")] + mimetype = accept_list.pop(0) + while(mimetype): + if str(mimetype).lower() in ["text/html", "text/xhtml"]: + redirect(url(controller="datasets", action="datasetview", silo=silo, id=target_dataset_name)) + elif str(mimetype).lower() in ["text/plain", "application/json"]: + response.content_type = "text/plain" + return response_message + try: + mimetype = accept_list.pop(0) + except IndexError: + mimetype = None + # Whoops - nothing satisfies - return text/plain + response.content_type = "text/plain" + return response_message + elif http_method == "PUT": + # Pylons loads the request body into request.body... + # This is not going to work for large files... ah well + # POST will handle large files as they are pushed to disc, + # but this won't + content = request.body + + if JAILBREAK.search(path) != None: + abort(400, "'..' cannot be used in the path") + + #Step 1: Put zipfile in dataset + if dataset.isdir(path): + response.content_type = "text/plain" + response.status_int = 403 + response.status = "403 Forbidden" + return "Cannot PUT a file on to an existing directory" + + if dataset.isfile(path): + code = 204 + else: + code = 201 + + if code == 204: + dataset.increment_version_delta(clone_previous_version=True, copy_filenames=['manifest.rdf', path]) + else: + dataset.increment_version_delta(clone_previous_version=True, copy_filenames=['manifest.rdf']) + dataset.put_stream(path, content) + dataset.del_triple(dataset.uri, u"dcterms:modified") + dataset.add_triple(dataset.uri, u"dcterms:modified", datetime.now()) + dataset.del_triple(dataset.uri, u"oxds:currentVersion") + dataset.add_triple(dataset.uri, u"oxds:currentVersion", dataset.currentversion) + dataset.sync() + + target_dataset = rdfsilo.get_item(id) + #step 2: Unpack zip item + if not check_file_mimetype(target_filepath, 'application/zip'): + abort(415, "File is not of type application/zip") + try: + unpack_zip_item(target_dataset, dataset, path, rdfsilo, ident['repoze.who.userid']) + except BadZipfile: + abort(400, "Couldn't unpack zipfile") + + target_dataset.sync() + target_dataset.sync() + target_dataset.sync() + + response.status = "204 Updated" + response.status_int = 204 + response_message = None + try: + ag.b.change(silo, id, path, ident=ident['repoze.who.userid']) + except: + pass + + # conneg return + accept_list = None + if 'HTTP_ACCEPT' in request.environ: + try: + accept_list = conneg_parse(request.environ['HTTP_ACCEPT']) + except: + accept_list= [MT("text", "html")] + if not accept_list: + accept_list= [MT("text", "html")] + mimetype = accept_list.pop(0) + while(mimetype): + if str(mimetype).lower() in ["text/html", "text/xhtml"]: + redirect(url(controller="datasets", action="datasetview", silo=silo, id=id)) + elif str(mimetype).lower() in ["text/plain", "application/json"]: + response.content_type = "text/plain" + return response_message + try: + mimetype = accept_list.pop(0) + except IndexError: + mimetype = None + # Whoops - nothing satisfies - return text/plain + response.content_type = "text/plain" + return response_message + + @rest.restrict('GET') + def subitemview(self, silo, id, path, subpath): + #Function to retreive a file from the zipfile + #TODO + # I check to see the path is avlid and it is a zip file. + # I do not deal with subpath. if it is a file - serve it. If it is a dir, show the contents of it. + + #tmpl_context variables needed: c.silo_name, c.zipfile_contents c.ident, c.id, c.path + if not ag.granary.issilo(silo): + abort(404) + + if not (path or subpath): + abort(400, "You must supply a filename to unpack") + + rdfsilo = ag.granary.get_rdf_silo(silo) + if not rdfsilo.exists(id): + abort (404) + + c.silo_name = silo + c.id = id + c.path = path + c.subpath = subpath + + ident = request.environ.get('repoze.who.identity') + c.ident = ident + dataset = rdfsilo.get_item(id) + + if dataset.metadata.get('embargoed') not in ["false", 0, False]: + if not ident: + abort(401, "Not Authorised") + silos = ag.authz(ident) + if silo not in silos: + abort(403, "Forbidden") + + item_real_filepath = dataset.to_dirpath() + target_filepath = "%s/%s"%(item_real_filepath, path) + #c.parts = dataset.list_parts(detailed=False) + if not dataset.isfile(path): + abort(404, "File not found") + if not os.path.isfile(target_filepath): + abort(404, "File not found") + if not check_file_mimetype(target_filepath, 'application/zip'): + abort(415, "File is not of type application/zip") + + #TODO : if subpath is a file - serve it. If subpath is a dir, show the contents of the dir + + return render("/zipfilesubitemview.html") + diff --git a/rdfdatabank/controllers/keywords.py b/rdfdatabank/controllers/keywords.py new file mode 100644 index 0000000..b3f25af --- /dev/null +++ b/rdfdatabank/controllers/keywords.py @@ -0,0 +1,31 @@ +# -*- coding: utf-8 -*- +""" +Copyright (c) 2012 University of Oxford + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, --INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +""" + +import logging + +from rdfdatabank.lib.base import BaseController, render + +class KeywordsController(BaseController): + def index(self): + return render('/keywords.html') diff --git a/rdfdatabank/controllers/objects.py b/rdfdatabank/controllers/objects.py deleted file mode 100644 index b2868f8..0000000 --- a/rdfdatabank/controllers/objects.py +++ /dev/null @@ -1,568 +0,0 @@ -import logging - -from pylons import request, response, session, tmpl_context as c -from pylons.controllers.util import abort, redirect_to -from pylons import app_globals as ag -from rdfdatabank.lib.base import BaseController, render -from rdfdatabank.lib.utils import create_new, is_embargoed, get_readme_text, test_rdf - -from rdfdatabank.lib.conneg import MimeType as MT, parse as conneg_parse - -from datetime import datetime, timedelta -from paste.fileapp import FileApp - -import re, os - -JAILBREAK = re.compile("[\/]*\.\.[\/]*") - -import simplejson - -log = logging.getLogger(__name__) - -class ObjectsController(BaseController): - def index(self): - if not request.environ.get('repoze.who.identity'): - abort(401, "Not Authorised") - ident = request.environ.get('repoze.who.identity') - granary_list = ag.granary.silos - c.silos = ag.authz(granary_list, ident) - c.ident = ident - return render('/list_of_archives.html') - - def siloview(self, silo): - if not request.environ.get('repoze.who.identity'): - abort(401, "Not Authorised") - ident = request.environ.get('repoze.who.identity') - c.ident = ident - granary_list = ag.granary.silos - c.silos = ag.authz(granary_list, ident) - if silo not in c.silos: - abort(403, "Forbidden") - - c.silo_name = silo - c.silo = ag.granary.get_rdf_silo(silo) - - http_method = request.environ['REQUEST_METHOD'] - if http_method == "GET": - c.embargos = {} - for item in c.silo.list_items(): - c.embargos[item] = is_embargoed(c.silo, item) - c.items = c.silo.list_items() - # conneg return - accept_list = conneg_parse(request.environ['HTTP_ACCEPT']) - if not accept_list: - accept_list= [MT("text", "html")] - mimetype = accept_list.pop(0) - while(mimetype): - if str(mimetype) in ["text/html", "text/xhtml"]: - return render('/siloview.html') - elif str(mimetype) in ["text/plain", "application/json"]: - response.content_type = "text/plain" - items = {} - for item_id in c.items: - items[item_id] = {} - items[item_id]['embargo_info'] = c.embargos[item_id] - return simplejson.dumps(items) - try: - mimetype = accept_list.pop(0) - except IndexError: - mimetype = None - - return render('/siloview.html') - elif http_method == "POST": - params = request.POST - if params.has_key("id"): - if c.silo.exists(params['id']): - response.content_type = "text/plain" - response.status_int = 409 - response.status = "409 Conflict: Object Already Exists" - return "Object Already Exists" - else: - # Supported params: - # id, title, embargoed, embargoed_until, embargo_days_from_now - id = params['id'] - del params['id'] - item = create_new(c.silo, id, ident['repoze.who.userid'], **params) - - # Broadcast change as message - ag.b.creation(silo, id, ident=ident['repoze.who.userid']) - - # conneg return - accept_list = conneg_parse(request.environ['HTTP_ACCEPT']) - if not accept_list: - accept_list= [MT("text", "html")] - mimetype = accept_list.pop(0) - while(mimetype): - if str(mimetype) in ["text/html", "text/xhtml"]: - # probably a browser - redirect to newly created object - redirect_to(controller="objects", action="itemview", silo=silo, id=id) - elif str(mimetype) in ["text/plain"]: - response.content_type = "text/plain" - response.status_int = 201 - response.status = "201 Created" - #response.headers.add("Content-Location", item.uri) - return "Created" - try: - mimetype = accept_list.pop(0) - except IndexError: - mimetype = None - # Whoops - nothing satisfies - response.content_type = "text/plain" - response.status_int = 201 - #response.headers.add("Content-Location", item.uri) - response.status = "201 Created" - return "Created" - - def itemview(self, silo, id): - # Check to see if embargo is on: - c.silo_name = silo - c.id = id - c.silo = ag.granary.get_rdf_silo(silo) - - c.embargoed = False - if c.silo.exists(id): - c.item = c.silo.get_item(id) - - if c.item.metadata.get('embargoed') not in ["false", 0, False]: - c.embargoed = True - c.embargos = {} - c.embargos[id] = is_embargoed(c.silo, id) - http_method = request.environ['REQUEST_METHOD'] - - c.editor = False - - if not (http_method == "GET" and not c.embargoed): - #identity management if item - if not request.environ.get('repoze.who.identity'): - abort(401, "Not Authorised") - ident = request.environ.get('repoze.who.identity') - c.ident = ident - granary_list = ag.granary.silos - if ident: - c.silos = ag.authz(granary_list, ident) - if silo not in c.silos: - abort(403, "Forbidden") - else: - abort(403, "Forbidden") - - c.editor = silo in c.silos - - # Method determination - if http_method == "GET": - if c.silo.exists(id): - # conneg: - c.item = c.silo.get_item(id) - - c.parts = c.item.list_parts(detailed=True) - - if "README" in c.parts.keys(): - c.readme_text = get_readme_text(c.item) - - # View options - options = request.GET - if "view" in options: - c.view = options['view'] - elif c.editor: - c.view = 'editor' - else: - c.view = 'user' - - accept_list = conneg_parse(request.environ['HTTP_ACCEPT']) - if not accept_list: - accept_list= [MT("text", "html")] - mimetype = accept_list.pop(0) - while(mimetype): - if str(mimetype) in ["text/html", "text/xhtml"]: - return render('/itemview.html') - elif str(mimetype) in ["text/plain", "application/json"]: - response.content_type = 'application/json; charset="UTF-8"' - def serialisable_stat(stat): - stat_values = {} - for f in ['st_atime', 'st_blksize', 'st_blocks', 'st_ctime', 'st_dev', 'st_gid', 'st_ino', 'st_mode', 'st_mtime', 'st_nlink', 'st_rdev', 'st_size', 'st_uid']: - try: - stat_values[f] = stat.__getattribute__(f) - except AttributeError: - pass - return stat_values - items = {} - items['parts'] = {} - for part in c.parts: - items['parts'][part] = serialisable_stat(c.parts[part]) - if c.readme_text: - items['readme_text'] = c.readme_text - if c.item.manifest: - items['state'] = c.item.manifest.state - return simplejson.dumps(items) - elif str(mimetype) in ["application/rdf+xml", "text/xml"]: - response.content_type = 'application/rdf+xml; charset="UTF-8"' - return c.item.rdf_to_string(format="pretty-xml") - elif str(mimetype) == "text/rdf+n3": - response.content_type = 'text/rdf+n3; charset="UTF-8"' - return c.item.rdf_to_string(format="n3") - elif str(mimetype) == "application/x-turtle": - response.content_type = 'application/x-turtle; charset="UTF-8"' - return c.item.rdf_to_string(format="turtle") - elif str(mimetype) in ["text/rdf+ntriples", "text/rdf+nt"]: - response.content_type = 'text/rdf+ntriples; charset="UTF-8"' - return c.item.rdf_to_string(format="nt") - # Whoops - nothing satisfies - try: - mimetype = accept_list.pop(0) - except IndexError: - mimetype = None - abort(406) - else: - abort(404) - elif http_method == "POST" and c.editor: - params = request.POST - if not c.silo.exists(id): - if 'id' in params.keys(): - del params['id'] - item = create_new(c.silo, id, ident['repoze.who.userid'], **params) - - # Broadcast change as message - ag.b.creation(silo, id, ident=ident['repoze.who.userid']) - - # conneg return - accept_list = conneg_parse(request.environ['HTTP_ACCEPT']) - if not accept_list: - accept_list= [MT("text", "html")] - mimetype = accept_list.pop(0) - while(mimetype): - if str(mimetype) in ["text/html", "text/xhtml"]: - # probably a browser - redirect to newly created object - redirect_to(controller="objects", action="itemview", silo=silo, id=id) - elif str(mimetype) in ["text/plain"]: - response.content_type = "text/plain" - response.status_int = 201 - response.status = "201 Created" - #response.headers.add("Content-Location", item.uri) - return "Created" - try: - mimetype = accept_list.pop(0) - except IndexError: - mimetype = None - # Whoops - nothing satisfies - response.content_type = "text/plain" - response.status_int = 201 - #response.headers.add("Content-Location", item.uri) - response.status = "201 Created" - return "Created" - elif params.has_key('embargo_change'): - item = c.silo.get_item(id) - if params.has_key('embargoed'): - item.metadata['embargoed'] = True - else: - #if is_embargoed(c.silo, id)[0] == True: - item.metadata['embargoed'] = False - if params.has_key('embargoed_until'): - item.metadata['embargoed_until'] = params['embargoed_until'] - item.sync() - e, e_d = is_embargoed(c.silo, id, refresh=True) - - # Broadcast change as message - ag.b.embargo_change(silo, id, item.metadata['embargoed'], item.metadata['embargoed_until'], ident=ident['repoze.who.userid']) - - response.content_type = "text/plain" - response.status_int = 200 - return simplejson.dumps({'embargoed':e, 'embargoed_until':e_d}) - elif params.has_key('file'): - # File upload by a not-too-savvy method - Service-orientated fallback: - # Assume file upload to 'filename' - params = request.POST - item = c.silo.get_item(id) - filename = params.get('filename') - if not filename: - filename = params['file'].filename - upload = params.get('file') - if JAILBREAK.search(filename) != None: - abort(400, "'..' cannot be used in the path or as a filename") - target_path = filename - - if item.isfile(target_path): - code = 200 - elif item.isdir(target_path): - response.status_int = 403 - return "Cannot POST a file on to an existing directory" - else: - code = 201 - item.put_stream(target_path, upload.file) - upload.file.close() - - if code == 201: - ag.b.creation(silo, id, target_path, ident=ident['repoze.who.userid']) - else: - ag.b.change(silo, id, target_path, ident=ident['repoze.who.userid']) - response.status_int = code - # conneg return - accept_list = conneg_parse(request.environ['HTTP_ACCEPT']) - if not accept_list: - accept_list= [MT("text", "html")] - mimetype = accept_list.pop(0) - while(mimetype): - if str(mimetype) in ["text/html", "text/xhtml"]: - redirect_to(controller="objects", action="itemview", id=id, silo=silo) - elif str(mimetype) in ["text/plain"]: - response.status_int = code - return "Added file %s to item %s" % (filename, id) - try: - mimetype = accept_list.pop(0) - except IndexError: - mimetype = None - - response.status_int = code - return "Added file %s to item %s" % (filename, id) - elif params.has_key('text'): - # Text upload convenience service - params = request.POST - item = c.silo.get_item(id) - filename = params.get('filename') - if not filename: - abort(406, "Must supply a filename") - - if JAILBREAK.search(filename) != None: - abort(400, "'..' cannot be used in the path or as a filename") - target_path = filename - - if item.isfile(target_path): - code = 204 - elif item.isdir(target_path): - response.status_int = 403 - return "Cannot POST a file on to an existing directory" - else: - code = 201 - - if filename == "manifest.rdf": - # valid to make sure it's valid RDF - # Otherwise this object will not be accessible - text = params['text'] - if not test_rdf(text): - abort(406, "Not able to parse RDF/XML") - - item.put_stream(target_path, params['text'].encode("utf-8")) - - if code == 201: - ag.b.creation(silo, id, target_path, ident=ident['repoze.who.userid']) - else: - ag.b.change(silo, id, target_path, ident=ident['repoze.who.userid']) - response.status_int = code - # conneg return - accept_list = conneg_parse(request.environ['HTTP_ACCEPT']) - if not accept_list: - accept_list= [MT("text", "html")] - mimetype = accept_list.pop(0) - while(mimetype): - if str(mimetype) in ["text/html", "text/xhtml"]: - redirect_to(controller="objects", action="itemview", id=id, silo=silo) - elif str(mimetype) in ["text/plain"]: - response.status_int = 200 - return "Added file %s to item %s" % (filename, id) - try: - mimetype = accept_list.pop(0) - except IndexError: - mimetype = None - - response.status_int = 200 - return "Added file %s to item %s" % (filename, id) - else: - ## TODO apply changeset handling - ## 1 - store posted CS docs in 'version' "___cs" - ## 2 - apply changeset to RDF manifest - ## 3 - update state to reflect latest CS applied - response.status_int = 204 - return - - elif http_method == "DELETE" and c.editor: - if c.silo.exists(id): - c.silo.del_item(id) - - # Broadcast deletion - ag.b.deletion(silo, id, ident=ident['repoze.who.userid']) - - response.status_int = 200 - return "{'ok':'true'}" # required for the JQuery magic delete to succede. - else: - abort(404) - - def subitemview(self, silo, id, path): - # Check to see if embargo is on: - c.silo_name = silo - c.id = id - c.silo = ag.granary.get_rdf_silo(silo) - - embargoed = False - if c.silo.exists(id): - c.item = c.silo.get_item(id) - - if c.item.metadata.get('embargoed') not in ["false", 0, False]: - embargoed = True - - http_method = request.environ['REQUEST_METHOD'] - - c.editor = False - - if not (http_method == "GET" and not embargoed): - #identity management if item - if not request.environ.get('repoze.who.identity'): - abort(401, "Not Authorised") - ident = request.environ.get('repoze.who.identity') - c.ident = ident - granary_list = ag.granary.silos - if ident: - c.silos = ag.authz(granary_list, ident) - if silo not in c.silos: - abort(403, "Forbidden") - else: - abort(403, "Forbidden") - - c.editor = silo in c.silos - - c.path = path - - http_method = request.environ['REQUEST_METHOD'] - - if http_method == "GET": - if c.silo.exists(id): - c.item = c.silo.get_item(id) - if c.item.isfile(path): - fileserve_app = FileApp(c.item.to_dirpath(path)) - return fileserve_app(request.environ, self.start_response) - elif c.item.isdir(path): - c.parts = c.item.list_parts(path, detailed=True) - - if "README" in c.parts.keys(): - c.readme_text = get_readme_text(c.item, "%s/README" % path) - accept_list = conneg_parse(request.environ['HTTP_ACCEPT']) - if not accept_list: - accept_list= [MT("text", "html")] - mimetype = accept_list.pop(0) - while(mimetype): - if str(mimetype) in ["text/html", "text/xhtml"]: - return render("/subitemview.html") - elif str(mimetype) in ["text/plain", "application/json"]: - def serialisable_stat(stat): - stat_values = {} - for f in ['st_atime', 'st_blksize', 'st_blocks', 'st_ctime', 'st_dev', 'st_gid', 'st_ino', 'st_mode', 'st_mtime', 'st_nlink', 'st_rdev', 'st_size', 'st_uid']: - try: - stat_values[f] = stat.__getattribute__(f) - except AttributeError: - pass - return stat_values - response.content_type = "text/plain" - items = {} - items['parts'] = {} - for part in c.parts: - items['parts'][part] = serialisable_stat(c.parts[part]) - if c.readme_text: - items['readme_text'] = c.readme_text - return simplejson.dumps(items) - try: - mimetype = accept_list.pop(0) - except IndexError: - mimetype = None - return render("/subitemview.html") - else: - abort(404) - elif http_method == "PUT" and c.editor: - if c.silo.exists(id): - # Pylons loads the request body into request.body... - # This is not going to work for large files... ah well - # POST will handle large files as they are pushed to disc, - # but this won't - content = request.body - item = c.silo.get_item(id) - - if JAILBREAK.search(path) != None: - abort(400, "'..' cannot be used in the path") - - if item.isfile(path): - code = 204 - elif item.isdir(path): - response.status_int = 403 - return "Cannot PUT a file on to an existing directory" - else: - code = 201 - - item.put_stream(path, content) - - if code == 201: - ag.b.creation(silo, id, path, ident=ident['repoze.who.userid']) - else: - ag.b.change(silo, id, path, ident=ident['repoze.who.userid']) - - response.status_int = code - return - else: - # item in which to store file doesn't exist yet... - # DECISION: Auto-instantiate object and then put file there? - # or error out with perhaps a 404? - # Going with error out... - response.status_int = 404 - return "Object %s doesn't exist" % id - elif http_method == "POST" and c.editor: - if c.silo.exists(id): - # POST... differences from PUT: - # path = filepath that this acts on, should be dir, or non-existant - # if path is a file, this will revert to PUT's functionality and - # overwrite the file, if there is a multipart file uploaded - # Expected params: filename, file (uploaded file) - params = request.POST - item = c.silo.get_item(id) - filename = params.get('filename') - upload = params.get('file') - if JAILBREAK.search(filename) != None: - abort(400, "'..' cannot be used in the path or as a filename") - target_path = path - if item.isdir(path) and filename: - target_path = os.path.join(path, filename) - - if item.isfile(target_path): - code = 204 - elif item.isdir(target_path): - response.status_int = 403 - return "Cannot POST a file on to an existing directory" - else: - code = 201 - item.put_stream(target_path, upload.file) - upload.file.close() - - if code == 201: - ag.b.creation(silo, id, target_path, ident=ident['repoze.who.userid']) - else: - ag.b.change(silo, id, target_path, ident=ident['repoze.who.userid']) - response.status_int = code - return - else: - # item doesn't exist yet... - # DECISION: Auto-instantiate object and then put file there? - # or error out with perhaps a 404? - # Going with error out... - response.status_int = 404 - return "Object %s doesn't exist" % id - elif http_method == "DELETE" and c.editor: - if c.silo.exists(id): - item = c.silo.get_item(id) - if item.isfile(path): - item.del_stream(path) - - ag.b.deletion(silo, id, path, ident=ident['repoze.who.userid']) - response.status_int = 200 - return "{'ok':'true'}" # required for the JQuery magic delete to succede. - elif item.isdir(path): - parts = item.list_parts(path) - for part in parts: - if item.isdir(os.path.join(path, part)): - # TODO implement proper recursive delete, with RDF aggregation - # updating - abort(400, "Directory is not empty of directories") - for part in parts: - item.del_stream(os.path.join(path, part)) - ag.b.deletion(silo, id, os.path.join(path, part), ident=ident['repoze.who.userid']) - item.del_stream(path) - ag.b.deletion(silo, id, path, ident=ident['repoze.who.userid']) - response.status_int = 200 - return "{'ok':'true'}" # required for the JQuery magic delete to succede. - else: - abort(404) - else: - abort(404) diff --git a/rdfdatabank/controllers/packages.py b/rdfdatabank/controllers/packages.py deleted file mode 100644 index b92d352..0000000 --- a/rdfdatabank/controllers/packages.py +++ /dev/null @@ -1,102 +0,0 @@ -import logging - -from pylons import request, response, session, tmpl_context as c -from pylons.controllers.util import abort, redirect_to - -from pylons import app_globals as ag -from rdfdatabank.lib.base import BaseController, render - -import re, os - -from rdfdatabank.lib.unpack import store_zipfile, unpack_zip_item, BadZipfile - -from rdfdatabank.lib.conneg import MimeType as MT, parse as conneg_parse - -log = logging.getLogger(__name__) - -class PackagesController(BaseController): - def index(self): - if not request.environ.get('repoze.who.identity'): - abort(401, "Not Authorised") - ident = request.environ.get('repoze.who.identity') - c.ident = ident - granary_list = ag.granary.silos - c.silos = ag.authz(granary_list, ident) - - return render('/list_of_zipfile_archives.html') - - def success(self, message): - c.message = message - return render("/success_message.html") - - def siloview(self, silo): - if not request.environ.get('repoze.who.identity'): - abort(401, "Not Authorised") - ident = request.environ.get('repoze.who.identity') - c.ident = ident - granary_list = ag.granary.silos - c.silos = ag.authz(granary_list, ident) - if silo not in c.silos: - abort(403, "Forbidden") - - c.silo_name = silo - c.silo = ag.granary.get_rdf_silo(silo) - - http_method = request.environ['REQUEST_METHOD'] - if http_method == "GET": - return render("/package_form_upload.html") - elif http_method == "POST": - params = request.POST - if params.has_key("id") and params.has_key("file") and params['id'] and params['file'].filename: - target_uri = "%s%s" % (c.silo.state['uri_base'], params['id']) - info = {} - info['package_filename'] = params['file'].filename - zip_item = store_zipfile(c.silo, target_uri, params['file'], ident['repoze.who.userid']) - - # Broadcast zipfile creation - ag.b.creation(silo, params['id'], ident=ident['repoze.who.userid'], package_type="zipfile") - - info['zip_id'] = zip_item.item_id - info['zip_uri'] = zip_item.uri - info['zip_target'] = target_uri - info['zip_file_stat'] = zip_item.stat(info['package_filename']) - info['zip_file_size'] = info['zip_file_stat'].st_size - try: - unpack_zip_item(zip_item, c.silo, ident['repoze.who.userid']) - - except BadZipfile: - # Bad zip file - info['unpacking_status'] = "FAIL - Couldn't unzip package" - abort(500, "Couldn't unpack zipfile") - # Broadcast derivative creation - ag.b.creation(silo, params['id'], ident=ident['repoze.who.userid']) - - # 302 Redirect to new resource? 201 with Content-Location? - # For now, content-location - #response.headers.add("Content-Location", target_uri) - # conneg return - accept_list = conneg_parse(request.environ['HTTP_ACCEPT']) - if not accept_list: - accept_list= [MT("text", "html")] - mimetype = accept_list.pop(0) - while(mimetype): - if str(mimetype) in ["text/html", "text/xhtml"]: - c.info = info - return render('/successful_package_upload.html') - elif str(mimetype) == "application/json": - response.status_int = 201 - response.content_type = 'application/json; charset="UTF-8"' - return simplejson.dumps(info) - elif str(mimetype) in ["application/rdf+xml", "text/xml"]: - response.status_int = 201 - response.content_type = 'application/rdf+xml; charset="UTF-8"' - return zip_item.rdf_to_string(format="pretty-xml") - try: - mimetype = accept_list.pop(0) - except IndexError: - mimetype = None - # Whoops - nothing satisfies - abort(406) - else: - abort(400, "You must supply a valid id") - abort(404) diff --git a/rdfdatabank/controllers/redirect.py b/rdfdatabank/controllers/redirect.py new file mode 100644 index 0000000..444e142 --- /dev/null +++ b/rdfdatabank/controllers/redirect.py @@ -0,0 +1,21 @@ +# -*- coding: utf-8 -*- +import logging +from pylons import url + +from pylons.controllers.util import redirect +from rdfdatabank.lib.base import BaseController + +class RedirectController(BaseController): + def index(self, id): + if id.lower().endswith(('.html', '.rdf', '.json')): + id = id.rsplit('.', 1)[0] + lid = id.lower() + if lid == 'dataset%3a1' or lid == 'dataset:1': + redirect(url(controller="datasets", action="datasetview", silo="general", id='Tick1AudioCorpus')) + elif lid == 'dataset%3A2.html' or lid == 'dataset:2': + redirect(url(controller="datasets", action="datasetview", silo="general", id='RobertDarnton')) + if lid == 'dataset%3A3' or lid == 'dataset:3': + redirect(url(controller="datasets", action="datasetview", silo="general", id='MostynBrown')) + else: + redirect(url(controller="datasets", action="datasetview", silo="general", id=id)) + diff --git a/rdfdatabank/controllers/search.py b/rdfdatabank/controllers/search.py index 09aa0c4..b481bd2 100644 --- a/rdfdatabank/controllers/search.py +++ b/rdfdatabank/controllers/search.py @@ -1,28 +1,522 @@ +# -*- coding: utf-8 -*- +""" +Copyright (c) 2012 University of Oxford + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, --INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +""" + import logging +from urllib import urlencode, unquote, quote +import json from pylons import request, response, session, tmpl_context as c -from pylons.controllers.util import abort, redirect_to +from pylons.controllers.util import abort from pylons import app_globals as ag from rdfdatabank.lib.base import BaseController, render +from rdfdatabank.lib.search_term import term_list +from rdfdatabank.lib.conneg import MimeType as MT, parse as conneg_parse log = logging.getLogger(__name__) class SearchController(BaseController): + def __before__(self): + c.all_fields = term_list().get_all_search_fields() + c.field_names = term_list().get_search_field_dictionary() + c.facetable_fields = term_list().get_all_facet_fields() + c.types = term_list().get_type_field_dictionary() + c.search_fields = ['silo', 'id', 'title', 'uuid', 'embargoStatus', 'embargoedUntilDate', 'currentVersion', 'doi', 'publicationDate', 'abstract', 'description', 'creator', 'isVersionOf', 'isPartOf', 'subject', 'type'] + c.sort_options = {'score desc':'Relevance', 'publicationDate desc':'Date (Latest to oldest)','publicationDate asc':'Date (Oldest to Latest)','silo asc':'Silo A to Z','silo desc':'Silo Z to A'} + def raw(self): - http_method = request.environ['REQUEST_METHOD'] + ident = request.environ.get('repoze.who.identity') + c.ident = ident + + silos = None + if ag.metadata_embargoed: + if not ident: + abort(401, "Not Authorised") + silos = ag.authz(ident) + + if silos and not isinstance(silos, basestring) and type(silos).__name__ == 'list': + silos = ' '.join(silos) + + http_method = request.environ['REQUEST_METHOD'] if http_method == "GET": params = request.GET elif http_method == "POST": params = request.POST - if "q" in params and "wt" in params: + + if not "q" in params: + abort(400, "Parameter 'q' is not available") + + #If ag.metadata_embargoed, search only within your silos + if params['q'] == '*': + if silos: + params['q'] = """silo:(%s)"""%silos + else: + params['q'] = "*:*" + elif silos and not 'silo:' in params['q']: + params['q'] = """%s AND silo:(%s)"""%(params['q'], silos) + + accept_list = None + if 'wt' in params and params['wt'] == "json": + accept_list = [MT("application", "json")] + elif 'wt' in params and params['wt'] == "xml": + accept_list = [MT("text", "xml")] + else: + if 'HTTP_ACCEPT' in request.environ: + try: + accept_list = conneg_parse(request.environ['HTTP_ACCEPT']) + except: + accept_list= [MT("text", "html")] + if not accept_list: + accept_list= [MT("text", "html")] + + mimetype = accept_list.pop(0) + while(mimetype): + if str(mimetype).lower() in ["text/html", "text/xhtml"]: + params['wt'] = 'json' + accept_list= [MT("text", "html")] + break + elif str(mimetype).lower() in ["text/plain", "application/json"]: + params['wt'] = 'json' + accept_list= [MT("application", "json")] + break + elif str(mimetype).lower() in ["application/rdf+xml", "text/xml"]: + params['wt'] = 'xml' + accept_list = [MT("text", "xml")] + break + # Whoops - nothing satisfies + try: + mimetype = accept_list.pop(0) + except IndexError: + mimetype = None + + if not 'wt' in params or not params['wt'] in ['json', 'xml']: + params['wt'] = 'json' + accept_list= [MT("text", "html")] + if not 'fl' in params or not params['fl']: + #Also include the following fields - date modified, publication year / publication date, embargo status, embargo date, version + params['fl'] = "id,silo,mediator,creator,title,score" + if not 'start' in params or not params['start']: + params['start'] = '0' + if not 'rows' in params or not params['rows']: + params['rows'] = '100' + try: result = ag.solr.raw_query(**params) - if params['wt'] == "json": + except: + result = {} + + mimetype = accept_list.pop(0) + while(mimetype): + if str(mimetype).lower() in ["text/html", "text/xhtml"]: + c.result = result + return render('/raw_search.html') + elif str(mimetype).lower() in ["text/plain", "application/json"]: + response.content_type = 'application/json; charset="UTF-8"' + response.status_int = 200 + response.status = "200 OK" + return result + elif str(mimetype).lower() in ["application/rdf+xml", "text/xml"]: + response.content_type = 'text/xml; charset="UTF-8"' + response.status_int = 200 + response.status = "200 OK" + return result + # Whoops - nothing satisfies + try: + mimetype = accept_list.pop(0) + except IndexError: + mimetype = None + #Whoops - nothing staisfies - default to text/html + c.result = result + return render('/raw_search.html') + + def detailed(self, query=None, additional_fields=[]): + + if query: + c.q = query + else: + c.q = request.params.get('q', None) + try: + c.q = unquote(c.q) + except: + pass + + c.typ = 'all' + if request.params.get("type", None): + c.typ = request.params.get("type") + + if not c.q or c.q == '*' or c.q == "": + c.q = "*:*" + + # Search controls + truncate = request.params.get('truncate', None) + start = request.params.get('start', None) + rows = request.params.get('rows', None) + sort = request.params.get('sort', None) + fields = request.params.get('fl', None) + res_format = request.params.get('format', None) + if not res_format: + accept_list = None + if 'HTTP_ACCEPT' in request.environ: + try: + accept_list = conneg_parse(request.environ['HTTP_ACCEPT']) + except: + accept_list= [MT("text", "html")] + if not accept_list: + accept_list= [MT("text", "html")] + mimetype = accept_list.pop(0) + while(mimetype): + if str(mimetype).lower() in ["text/html", "text/xhtml"]: + res_format = 'html' + break + elif str(mimetype).lower() in ["text/plain", "application/json"]: + res_format = 'json' + break + elif str(mimetype).lower() in ["text/xml"]: + res_format = 'xml' + break + elif str(mimetype).lower() in ["text/csv"]: + res_format = 'csv' + break + try: + mimetype = accept_list.pop(0) + except IndexError: + mimetype = None + # Whoops - nothing satisfies - return text/plain + if not res_format: + res_format = 'html' + + c.sort = 'score desc' + # Lock down the sort parameter. + if sort and sort in c.sort_options: + c.sort = sort + c.sort_text = c.sort_options[c.sort] + + c.chosen_fields = [] + c.chosen_fields.extend(c.search_fields) + + if fields: + fields = fields.split(',') + if fields and type(fields).__name__ == 'list': + fields = [x.strip() for x in fields] + for fld in fields: + if fld in c.all_fields and not fld in c.chosen_fields: + c.chosen_fields.append(fld) + + for fld in additional_fields: + if not fld in c.chosen_fields: + c.chosen_fields.append(fld) + + c.fields_to_facet = [] + c.fields_to_facet.extend(c.facetable_fields) + + c.facet_limit = 10 + + c.chosen_facets = {} + + query_filter = "" + + #Setup to capture all the url parameters needed to regenerate this search + c.search = {} + filter_url = "" + + for field in c.all_fields: + if request.params.get("filter"+field, None): + multi = request.params.getall("filter"+field) + c.chosen_facets[field] = [] + #c.search["filter"+field] = "" + for m in multi: + try: + m = unquote(m) + except: + pass + m = m.strip() + m = m.strip('"') + c.chosen_facets[field].append(m) + query_filter += ' AND %s:"%s"'%(field, m) + try: + filter_url += '&filter%s=%s'%(field, quote('"%s"'%m)) + except: + filter_url += '&filter%s=%s'%(field, '"%s"'%m) + #if field in c.fields_to_facet: + # del c.fields_to_facet[field] + + for field in c.chosen_facets: + if field not in c.chosen_fields: + c.chosen_fields.append(field) + + c.truncate = 450 + c.start = 0 + c.rows = 25 + + # Parse/Validate search controls + if truncate: + try: + c.truncate = int(truncate) + except ValueError: + pass + if c.truncate < 10: + c.truncate = 10 + if c.truncate > 1000: + c.truncate = 1000 + + if start: + try: + c.start = int(start) + except ValueError: + pass + if c.start < 0: + c.start = 0 + + if rows: + try: + c.rows = int(rows) + except ValueError: + pass + if c.rows < 5: + c.rows = 5 + elif c.rows > 5000: + c.rows=5000 + + #c.search['rows'] = c.rows + c.search['truncate'] = c.truncate + c.search['type'] = c.typ + #c.search['start'] = c.start + #c.search['sort'] = c.sort + #if c.q: + # c.search['q'] = c.q.encode('utf-8') + solr_params = {} + + if c.q: + if c.typ and 'silo' in c.typ: + solr_params['q'] = c.q.encode('utf-8')+query_filter+" AND type:silo" + elif c.typ and 'dataset' in c.typ: + solr_params['q'] = c.q.encode('utf-8')+query_filter+" AND type:dataset" + elif c.typ and 'item' in c.typ and c.q != "*:*": + #solr_params['q'] = """aggregatedResource:"%s" %s"""%(c.q.encode('utf-8'),query_filter) + solr_params['q'] = """filename:"%s" %s"""%(c.q.encode('utf-8'),query_filter) + else: + solr_params['q'] = c.q.encode('utf-8')+query_filter + + if res_format in ['json', 'xml', 'python', 'php']: + solr_params['wt'] = res_format + else: + solr_params['wt'] = 'json' + + solr_params['fl'] = ','.join(c.chosen_fields) + solr_params['rows'] = c.rows + solr_params['start'] = c.start + + if c.sort: + solr_params['sort'] = c.sort + + if c.fields_to_facet: + solr_params['facet'] = 'true' + solr_params['facet.limit'] = c.facet_limit + solr_params['facet.mincount'] = 1 + solr_params['facet.field'] = [] + for facet in c.fields_to_facet: + solr_params['facet.field'].append(facet) + + solr_response = None + try: + solr_response = ag.solr.raw_query(**solr_params) + except: + pass + + c.add_facet = u"%ssearch/detailed?q=%s&" % (ag.root, c.q.encode('utf-8')) + c.add_facet = c.add_facet + urlencode(c.search) + filter_url + + if not solr_response: + # conneg return + response.status_int = 200 + response.status = "200 OK" + if res_format == "html": + c.numFound = 0 + c.message = 'Sorry, either that search "%s" resulted in no matches, or the search service is not functional.' % c.q + return render('/search.html') + elif res_format == 'xml': + response.headers['Content-Type'] = 'application/xml' + response.charset = 'utf8' + c.atom = {} + return render('/atom_results.html') + elif res_format == 'json': + response.headers['Content-Type'] = 'application/json' + response.charset = 'utf8' + return {} + else: + response.headers['Content-Type'] = 'application/text' + response.charset = 'utf8' + return solr_response + + response.status_int = 200 + response.status = "200 OK" + if res_format == 'xml': + response.headers['Content-Type'] = 'application/xml' + response.charset = 'utf8' + c.atom = solr_response + return render('/atom_results.html') + elif res_format == 'json': response.headers['Content-Type'] = 'application/json' - elif params['wt'] == "xml": - response.headers['Content-Type'] = 'text/xml' - return result - else: - return render("/raw_search.html") + response.charset = 'utf8' + return solr_response + elif res_format in ['csv', 'python', 'php']: + response.headers['Content-Type'] = 'application/text' + response.charset = 'utf8' + return solr_response + + search = json.loads(solr_response) + + numFound = search['response'].get('numFound',None) + + c.numFound = 0 + c.permissible_offsets = [] + + c.pages_to_show = 5 + + try: + c.numFound = int(numFound) + remainder = c.numFound % c.rows + if remainder > 0: + c.lastPage = c.numFound - remainder + else: + c.lastPage = c.numFound - c.rows + + if c.numFound > c.rows: + offset_start = c.start - ( (c.pages_to_show/2) * c.rows ) + if offset_start < 0: + offset_start = 0 + + offset_end = offset_start + (c.pages_to_show * c.rows) + if offset_end > c.numFound: + offset_end = c.numFound + if remainder > 0: + offset_start = c.lastPage - (c.pages_to_show * c.rows) + else: + offset_start = c.lastPage - ((c.pages_to_show-1) * c.rows) + + if offset_start < 0: + offset_start = 0 + + c.permissible_offsets = list( xrange( offset_start, offset_end, c.rows) ) + except ValueError: + pass + + c.docs = search['response'].get('docs',None) + + if c.fields_to_facet: + c.returned_facets = {} + for facet in search['facet_counts']['facet_fields']: + facet_list = search['facet_counts']['facet_fields'][facet] + keys = facet_list[::2] + values = facet_list[1::2] + c.returned_facets[facet] = [] + for index in range(len(keys)): + c.returned_facets[facet].append((keys[index],values[index])) + + return render('/search.html') + + + def advanced(self): + + c.q = "*:*" + c.typ = 'all' + + # Search controls + format = 'html' + c.sort = 'score desc' + c.sort_text = c.sort_options[c.sort] + + c.chosen_fields = [] + c.chosen_fields.extend(c.search_fields) + + c.fields_to_facet = [] + c.fields_to_facet.extend(c.facetable_fields) + + c.facet_limit = 10 + + c.chosen_facets = {} + + query_filter = "" + + #Setup to capture all the url parameters needed to regenerate this search + c.search = {} + filter_url = "" + + c.truncate = 450 + c.start = 0 + c.rows = 25 + c.search['truncate'] = c.truncate + c.search['type'] = c.typ + + solr_params = {} + + if c.q: + solr_params['q'] = c.q.encode('utf-8')+query_filter + solr_params['wt'] = 'json' + solr_params['fl'] = ','.join(c.chosen_fields) + solr_params['rows'] = c.rows + solr_params['start'] = c.start + if c.sort: + solr_params['sort'] = c.sort + if c.fields_to_facet: + solr_params['facet'] = 'true' + solr_params['facet.limit'] = c.facet_limit + solr_params['facet.mincount'] = 1 + solr_params['facet.field'] = [] + for facet in c.fields_to_facet: + solr_params['facet.field'].append(facet) + try: + solr_response = ag.solr.raw_query(**solr_params) + except: + solr_response = None + + c.add_facet = u"%ssearch/detailed?q=%s&" % (ag.root, c.q.encode('utf-8')) + c.add_facet = c.add_facet + urlencode(c.search) + filter_url + + if not solr_response: + # FAIL - do something here: + c.message = 'Sorry, either that search "%s" resulted in no matches, or the search service is not functional.' % c.q + h.redirect_to(controller='/search', action='index') + + search = json.loads(solr_response) + + numFound = search['response'].get('numFound',None) + try: + c.numFound = int(numFound) + except: + c.numFound = 0 + c.docs = search['response'].get('docs',None) + + if c.fields_to_facet: + c.returned_facets = {} + for facet in search['facet_counts']['facet_fields']: + facet_list = search['facet_counts']['facet_fields'][facet] + keys = facet_list[::2] + values = facet_list[1::2] + c.returned_facets[facet] = [] + for index in range(len(keys)): + c.returned_facets[facet].append((keys[index],values[index])) + + return render('/search_advanced.html') diff --git a/rdfdatabank/controllers/searching.py b/rdfdatabank/controllers/searching.py new file mode 100644 index 0000000..d215996 --- /dev/null +++ b/rdfdatabank/controllers/searching.py @@ -0,0 +1,31 @@ +# -*- coding: utf-8 -*- +""" +Copyright (c) 2012 University of Oxford + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, --INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +""" + +import logging + +from rdfdatabank.lib.base import BaseController, render + +class SearchingController(BaseController): + def index(self): + return render('/searching.html') diff --git a/rdfdatabank/controllers/silos.py b/rdfdatabank/controllers/silos.py new file mode 100644 index 0000000..0c7c277 --- /dev/null +++ b/rdfdatabank/controllers/silos.py @@ -0,0 +1,155 @@ +# -*- coding: utf-8 -*- +""" +Copyright (c) 2012 University of Oxford + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, --INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +""" + +import logging +from datetime import datetime, timedelta +import re +import simplejson + +from pylons import request, response, session, tmpl_context as c, app_globals as ag, url +from pylons.controllers.util import abort +from pylons.decorators import rest +from paste.fileapp import FileApp + +from rdfdatabank.lib.base import BaseController, render +from rdfdatabank.lib.utils import is_embargoed, getSiloModifiedDate +from rdfdatabank.lib.auth_entry import list_silos, get_datasets_count +from rdfdatabank.lib.conneg import MimeType as MT, parse as conneg_parse + +JAILBREAK = re.compile("[\/]*\.\.[\/]*") + +log = logging.getLogger(__name__) + +class SilosController(BaseController): + @rest.restrict('GET') + def index(self): + ident = request.environ.get('repoze.who.identity') + c.ident = ident + #granary_list = ag.granary.silos + #c.silos = granary_list + c.silos = list_silos() + if ag.metadata_embargoed: + if not ident: + abort(401, "Not Authorised") + c.silos = ag.authz(ident) + + c.silo_infos = {} + for silo in c.silos: + c.silo_infos[silo] = [] + state_info = ag.granary.describe_silo(silo) + if 'title' in state_info and state_info['title']: + c.silo_infos[silo].append(state_info['title']) + else: + c.silo_infos[silo].append(silo) + c.silo_infos[silo].append(get_datasets_count(silo)) + c.silo_infos[silo].append(getSiloModifiedDate(silo)) + + # conneg return + accept_list = None + if 'HTTP_ACCEPT' in request.environ: + try: + accept_list = conneg_parse(request.environ['HTTP_ACCEPT']) + except: + accept_list= [MT("text", "html")] + if not accept_list: + accept_list= [MT("text", "html")] + mimetype = accept_list.pop(0) + while(mimetype): + if str(mimetype).lower() in ["text/html", "text/xhtml"]: + return render('/list_of_silos.html') + elif str(mimetype).lower() in ["text/plain", "application/json"]: + response.content_type = 'application/json; charset="UTF-8"' + response.status_int = 200 + response.status = "200 OK" + return simplejson.dumps(c.silos) + try: + mimetype = accept_list.pop(0) + except IndexError: + mimetype = None + #Whoops nothing satisfies - return text/html + return render('/list_of_silos.html') + + @rest.restrict('GET') + def siloview(self, silo): + if not ag.granary.issilo(silo): + abort(404) + + ident = request.environ.get('repoze.who.identity') + c.ident = ident + c.silo_name = silo + c.editor = False + if ag.metadata_embargoed: + if not ident: + abort(401, "Not Authorised") + silos = ag.authz(ident) + if silo not in silos: + abort(403, "Forbidden") + c.editor = True + elif ident: + silos = ag.authz(ident) + if silo in silos: + c.editor = True + + if silo in ['ww1archives', 'digitalbooks']: + abort(501, "The silo %s contains too many data packages to list"%silo) + + rdfsilo = ag.granary.get_rdf_silo(silo) + state_info = ag.granary.describe_silo(silo) + if 'title' in state_info and state_info['title']: + c.title = state_info['title'] + c.embargos = {} + c.items = [] + for item in rdfsilo.list_items(): + c.embargos[item] = None + try: + c.embargos[item] = is_embargoed(rdfsilo, item) + except: + pass + c.items.append(item) + #c.embargos[item] = () + + # conneg return + accept_list = None + if 'HTTP_ACCEPT' in request.environ: + try: + accept_list = conneg_parse(request.environ['HTTP_ACCEPT']) + except: + accept_list= [MT("text", "html")] + if not accept_list: + accept_list= [MT("text", "html")] + mimetype = accept_list.pop(0) + while(mimetype): + if str(mimetype).lower() in ["text/html", "text/xhtml"]: + return render('/siloview.html') + elif str(mimetype).lower() in ["text/plain", "application/json"]: + response.content_type = 'application/json; charset="UTF-8"' + response.status_int = 200 + response.status = "200 OK" + return simplejson.dumps(c.embargos) + try: + mimetype = accept_list.pop(0) + except IndexError: + mimetype = None + #Whoops nothing satisfies - return text/html + return render('/siloview.html') diff --git a/rdfdatabank/controllers/states.py b/rdfdatabank/controllers/states.py new file mode 100644 index 0000000..29a653b --- /dev/null +++ b/rdfdatabank/controllers/states.py @@ -0,0 +1,167 @@ +# -*- coding: utf-8 -*- +""" +Copyright (c) 2012 University of Oxford + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, --INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +""" + +import logging +import simplejson + +from pylons import request, response, app_globals as ag +from pylons.controllers.util import abort +from pylons.decorators import rest + +from rdfdatabank.lib.base import BaseController +from rdfdatabank.lib.utils import is_embargoed, serialisable_stat +from rdfdatabank.lib.auth_entry import get_datasets_count, get_datasets + +log = logging.getLogger(__name__) + +class StatesController(BaseController): + @rest.restrict('GET') + def siloview(self, silo): + """ + Returns the state information of a silo. + Only authorized users with role 'admin' or 'manager' can view this information + + The state information for a silo contains the following: + Name of the silo (machine name, used in uris) - ans["silo"] + Base URI for the silo - ans["uri_base"] + Users who can access the silo (silo owners) - ans["owners"] + Silo description - ans["description"] + Title of the silo (human readable) - ans["title"] + Disk allocation for the silo (in kB) - ans["disk_allocation"] + List of datasets in the silo (ans["datasets"]) + with embargo information for each of the datasets + (ans["datasets"]["dataset_name"]["embargo_info"]) + """ + + # Only authorized users can view state information. + # Should this be restricted to admins and managers only, or shoud users too be able to see this information? + # Going with restricting this information to admins and managers + if not ag.granary.issilo(silo): + abort(404) + + ident = request.environ.get('repoze.who.identity') + if not ident: + abort(401, "Not Authorised") + silos = ag.authz(ident) + if silo not in silos: + abort(403, "Forbidden") + silos_admin = ag.authz(ident, permission='administrator') + silos_manager = ag.authz(ident, permission='manager') + #if not ident.get('role') in ["admin", "manager"]: + if not (silo in silos_admin or silo in silos_manager): + abort(403, "Forbidden. You should be an administrator or manager to view this information") + + options = request.GET + start = 0 + if 'start' in options and options['start']: + try: + start = int(options['start']) + except: + start = 0 + rows = 1000 + if 'rows' in options and options['rows']: + try: + rows = int(options['rows']) + except: + rows = 1000 + + rdfsilo = ag.granary.get_rdf_silo(silo) + state_info = ag.granary.describe_silo(silo) + state_info['silo'] = silo + state_info['uri_base'] = '' + if rdfsilo.state and rdfsilo.state['uri_base']: + state_info['uri_base'] = rdfsilo.state['uri_base'] + state_info['number of data packages'] = get_datasets_count(silo) + state_info['params'] = {'start':start, 'rows':rows} + items = {} + #for item in rdfsilo.list_items(): + for item in get_datasets(silo, start=start, rows=rows): + items[item] = {} + try: + items[item]['embargo_info'] = is_embargoed(rdfsilo, item) + except: + pass + state_info['datasets'] = items + + # conneg return + # Always return application/json + response.content_type = 'application/json; charset="UTF-8"' + response.status_int = 200 + response.status = "200 OK" + return simplejson.dumps(state_info) + + @rest.restrict('GET') + def datasetview(self, silo, id): + if not ag.granary.issilo(silo): + abort(404) + + ident = request.environ.get('repoze.who.identity') + + if not ident: + abort(401, "Not Authorised") + + silos = ag.authz(ident) + if silo not in silos: + abort(403, "Forbidden") + silos_admin = ag.authz(ident, permission='administrator') + silos_manager = ag.authz(ident, permission='manager') + + rdfsilo = ag.granary.get_rdf_silo(silo) + if not rdfsilo.exists(id): + abort(404) + + item = rdfsilo.get_item(id) + + creator = None + if item.manifest and item.manifest.state and 'metadata' in item.manifest.state and item.manifest.state['metadata'] and \ + 'createdby' in item.manifest.state['metadata'] and item.manifest.state['metadata']['createdby']: + creator = item.manifest.state['metadata']['createdby'] + #if not (ident['repoze.who.userid'] == creator or ident.get('role') in ["admin", "manager"]): + if not (ident['repoze.who.userid'] == creator or silo in silos_admin or silo in silos_manager): + abort(403, "Forbidden. You should be the creator or manager or administrator to view this information") + + options = request.GET + if 'version' in options and options['version']: + if not options['version'] in item.manifest['versions']: + abort(404) + currentversion = str(item.currentversion) + vnum = str(options['version']) + if vnum and not vnum == currentversion: + item.set_version_cursor(vnum) + + parts = item.list_parts(detailed=True) + + dataset = {} + dataset['parts'] = {} + for part in parts: + dataset['parts'][part] = serialisable_stat(parts[part]) + if item.manifest: + dataset['state'] = item.manifest.state + + # Always return application/json + response.content_type = 'application/json; charset="UTF-8"' + response.status_int = 200 + response.status = "200 OK" + return simplejson.dumps(dataset) + diff --git a/rdfdatabank/controllers/sword.py b/rdfdatabank/controllers/sword.py new file mode 100644 index 0000000..c908777 --- /dev/null +++ b/rdfdatabank/controllers/sword.py @@ -0,0 +1,2 @@ +from sss.pylons_sword_controller import SwordController +__controller__ = "SwordController" diff --git a/rdfdatabank/controllers/users.py b/rdfdatabank/controllers/users.py new file mode 100644 index 0000000..1cc7b7d --- /dev/null +++ b/rdfdatabank/controllers/users.py @@ -0,0 +1,538 @@ +# -*- coding: utf-8 -*- +""" +Copyright (c) 2012 University of Oxford + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, --INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +""" + +import logging +import simplejson +import codecs +from pylons import request, response, session, config, tmpl_context as c, url +from pylons.controllers.util import abort, redirect +from pylons.decorators import rest +from pylons import app_globals as ag +from rdfdatabank.lib.base import BaseController, render +from rdfdatabank.lib.conneg import MimeType as MT, parse as conneg_parse +from rdfdatabank.lib.utils import allowable_id2 +from rdfdatabank.lib.auth_entry import add_user, update_user, delete_user, add_group_users, delete_group_users +from rdfdatabank.lib.auth_entry import list_users, list_usernames, list_user_groups, list_group_users, list_user, list_group_usernames + +#from rdfdatabank.config import users + +log = logging.getLogger(__name__) + +accepted_params = ['title', 'description', 'notes', 'owners', 'disk_allocation'] + +class UsersController(BaseController): + @rest.restrict('GET', 'POST') + def index(self): + if not request.environ.get('repoze.who.identity'): + abort(401, "Not Authorised") + ident = request.environ.get('repoze.who.identity') + if not ('administrator' in ident['permissions'] or 'manager' in ident['permissions']): + abort(403, "Do not have administrator or manager credentials") + + c.ident = ident + #silos = ag.authz(ident, permission=['administrator', 'manager']) + c.users = list_users() + if 'administrator' in ident['permissions']: + c.roles = ["admin", "manager", "user"] + else: + c.roles = ["manager", "user"] + + http_method = request.environ['REQUEST_METHOD'] + + if http_method == "GET": + accept_list = None + if 'HTTP_ACCEPT' in request.environ: + try: + accept_list = conneg_parse(request.environ['HTTP_ACCEPT']) + except: + accept_list= [MT("text", "html")] + if not accept_list: + accept_list= [MT("text", "html")] + mimetype = accept_list.pop(0) + while(mimetype): + if str(mimetype).lower() in ["text/html", "text/xhtml"]: + return render("/users.html") + elif str(mimetype).lower() in ["text/plain", "application/json"]: + response.content_type = 'application/json; charset="UTF-8"' + response.status_int = 200 + response.status = "200 OK" + return simplejson.dumps(c.users) + try: + mimetype = accept_list.pop(0) + except IndexError: + mimetype = None + #Whoops nothing satisfies - return text/plain + response.content_type = 'application/json; charset="UTF-8"' + response.status_int = 200 + response.status = "200 OK" + return simplejson.dumps(c.users) + elif http_method == "POST": + params = request.POST + if not ('username' in params and params['username'] and 'password' in params and params['password']): + abort(400, "username and password not supplied") + if not allowable_id2(params['username']): + response.content_type = "text/plain" + response.status_int = 400 + response.status = "400 Bad request. Username not valid" + return "username can contain only the following characters - %s and has to be more than 1 character"%ag.naming_rule_humanized + + existing_users = list_usernames() + if params['username'] in existing_users: + abort(403, "User exists") + if (('firstname' in params and params['firstname'] and 'lastname' in params and params['lastname']) \ + or 'name' in params and params['name']): + add_user(params) + else: + abort(400, "The following parameters have to be supplied: username, pasword and name (or firstname and lastname)") + response.status_int = 201 + response.status = "201 Created" + response.headers['Content-Location'] = url(controller="users", action="userview", username=params['username']) + response_message = "201 Created" + + if 'HTTP_ACCEPT' in request.environ: + try: + accept_list = conneg_parse(request.environ['HTTP_ACCEPT']) + except: + accept_list= [MT("text", "html")] + if not accept_list: + accept_list= [MT("text", "html")] + mimetype = accept_list.pop(0) + while(mimetype): + if str(mimetype).lower() in ["text/html", "text/xhtml"]: + redirect(url(controller="users", action="userview", username=params['username'])) + elif str(mimetype).lower() in ["text/plain", "application/json"]: + response.content_type = "text/plain" + return response_message + try: + mimetype = accept_list.pop(0) + except IndexError: + mimetype = None + # Whoops - nothing satisfies - return text/plain + response.content_type = "text/plain" + return response_message + + @rest.restrict('GET', 'POST', 'DELETE') + def userview(self, username): + if not request.environ.get('repoze.who.identity'): + abort(401, "Not Authorised") + + ident = request.environ.get('repoze.who.identity') + + http_method = request.environ['REQUEST_METHOD'] + + if http_method == 'GET' or 'DELETE': + #Admins, managers and user can see user data / delete the user + if not ('administrator' in ident['permissions'] or \ + 'manager' in ident['permissions'] or ident['user'].user_name == username): + abort(403, "Do not have administrator or manager credentials to view profiles of other users") + elif http_method == 'POST': + #Only user can updte their data + if not ident['user'].user_name == username: + abort(403, "Login as %s to edit profile"%username) + + existing_users = list_usernames() + if not username in existing_users: + abort(404, "User not found") + + c.ident = ident + c.username = username + + if http_method == "GET": + c.user = list_user(username) + accept_list = None + if 'HTTP_ACCEPT' in request.environ: + try: + accept_list = conneg_parse(request.environ['HTTP_ACCEPT']) + except: + accept_list= [MT("text", "html")] + if not accept_list: + accept_list= [MT("text", "html")] + mimetype = accept_list.pop(0) + while(mimetype): + if str(mimetype).lower() in ["text/html", "text/xhtml"]: + return render("/admin_user.html") + elif str(mimetype).lower() in ["text/plain", "application/json"]: + response.content_type = 'application/json; charset="UTF-8"' + response.status_int = 200 + response.status = "200 OK" + return simplejson.dumps(c.user) + try: + mimetype = accept_list.pop(0) + except IndexError: + mimetype = None + #Whoops nothing satisfies - return text/html + response.content_type = 'application/json; charset="UTF-8"' + response.status_int = 200 + response.status = "200 OK" + return simplejson.dumps(c.user) + elif http_method == "POST": + params = request.POST + if not('password' in params or 'name' in params or \ + 'email' in params or 'firstname' in params or 'lastname' in params): + abort(400, "No valid parameters found") + params['username'] = username + update_user(params) + response.status_int = 204 + response.status = "204 Updated" + response_message = None + # conneg return + accept_list = None + if 'HTTP_ACCEPT' in request.environ: + try: + accept_list = conneg_parse(request.environ['HTTP_ACCEPT']) + except: + accept_list= [MT("text", "html")] + if not accept_list: + accept_list= [MT("text", "html")] + mimetype = accept_list.pop(0) + while(mimetype): + if str(mimetype).lower() in ["text/html", "text/xhtml"]: + redirect(url(controller="users", action="userview", username=username)) + elif str(mimetype).lower() in ["text/plain", "application/json"]: + response.content_type = "text/plain" + return response_message + try: + mimetype = accept_list.pop(0) + except IndexError: + mimetype = None + # Whoops - nothing satisfies - return text/plain + response.content_type = "text/plain" + return response_message + elif http_method == "DELETE": + user_groups = list_user_groups(username) + if user_groups: + abort(403, "User is member of silos. Remove user from all silos before deleting them") + #Delete user from database + delete_user(username) + #Get all the silos user belomgs to, remove them from each silo and sync silo metadata + # conneg return + accept_list = None + response.content_type = "text/plain" + response.status_int = 200 + response.status = "200 OK" + return "{'ok':'true'}" + + @rest.restrict('GET') + def siloview(self, silo): + if not request.environ.get('repoze.who.identity'): + abort(401, "Not Authorised") + if not ag.granary.issilo(silo): + abort(404) + ident = request.environ.get('repoze.who.identity') + c.ident = ident + silos = ag.authz(ident, permission=['administrator', 'manager']) + if not silo in silos: + abort(403, "Do not have administrator or manager credentials for silo %s"%silo) + user_groups = list_user_groups(ident['user'].user_name) + if ('*', 'administrator') in user_groups: + c.roles = ["admin", "manager", "user"] + elif (silo, 'administrator') in user_groups: + c.roles = ["admin", "manager", "user"] + elif (silo, 'manager') in user_groups: + c.roles = ["manager", "user"] + else: + abort(403, "Do not have administrator or manager credentials for silo %s"%silo) + c.silo = silo + + http_method = request.environ['REQUEST_METHOD'] + + if http_method == "GET": + c.users = list_group_users(silo) + accept_list = None + if 'HTTP_ACCEPT' in request.environ: + try: + accept_list = conneg_parse(request.environ['HTTP_ACCEPT']) + except: + accept_list= [MT("text", "html")] + if not accept_list: + accept_list= [MT("text", "html")] + mimetype = accept_list.pop(0) + while(mimetype): + if str(mimetype).lower() in ["text/html", "text/xhtml"]: + return render("/silo_users.html") + elif str(mimetype).lower() in ["text/plain", "application/json"]: + response.content_type = 'application/json; charset="UTF-8"' + response.status_int = 200 + response.status = "200 OK" + return simplejson.dumps(c.users) + try: + mimetype = accept_list.pop(0) + except IndexError: + mimetype = None + #Whoops nothing satisfies - return text/plain + response.content_type = 'application/json; charset="UTF-8"' + response.status_int = 200 + response.status = "200 OK" + return simplejson.dumps(c.users) + + @rest.restrict('GET', 'POST', 'DELETE') + def silouserview(self, silo, username): + if not request.environ.get('repoze.who.identity'): + abort(401, "Not Authorised") + + if not ag.granary.issilo(silo): + abort(404) + + ident = request.environ.get('repoze.who.identity') + + http_method = request.environ['REQUEST_METHOD'] + if http_method == 'GET': + silos = ag.authz(ident) + if not silo in silos: + abort(403, "User is not a member of the silo %s"%silo) + if not ('administrator' in ident['permissions'] or \ + 'manager' in ident['permissions'] or ident['user'].user_name == username): + abort(403, "Do not have administrator or manager credentials to view profiles of other users") + else: + silos = ag.authz(ident, permission=['administrator', 'manager']) + if not silo in silos: + abort(403, "Do not have administrator or manager credentials for silo %s"%silo) + if not ('administrator' in ident['permissions'] or 'manager' in ident['permissions']): + abort(403, "Do not have administrator or manager credentials") + + existing_users = list_usernames() + if not username in existing_users: + abort(404, "User not found") + + c.ident = ident + c.silo = silo + c.username = username + + if http_method == "GET": + a, m, s = list_group_usernames(silo) + if not (username in a or username in m or username in s): + abort(404, "User not found in silo") + c.user = list_user(username) + #if 'groups' in c.user and c.user['groups']: + # for i in c.user['groups']: + # if i[0] != silo: + # c.user['groups'].remove(i) + accept_list = None + if 'HTTP_ACCEPT' in request.environ: + try: + accept_list = conneg_parse(request.environ['HTTP_ACCEPT']) + except: + accept_list= [MT("text", "html")] + if not accept_list: + accept_list= [MT("text", "html")] + mimetype = accept_list.pop(0) + while(mimetype): + if str(mimetype).lower() in ["text/html", "text/xhtml"]: + return render("/silo_user.html") + elif str(mimetype).lower() in ["text/plain", "application/json"]: + response.content_type = 'application/json; charset="UTF-8"' + response.status_int = 200 + response.status = "200 OK" + return simplejson.dumps(c.user) + try: + mimetype = accept_list.pop(0) + except IndexError: + mimetype = None + #Whoops nothing satisfies - return text/plain + response.content_type = 'application/json; charset="UTF-8"' + response.status_int = 200 + response.status = "200 OK" + return simplejson.dumps(c.user) + elif http_method == "POST": + params = request.POST + if not ('role' in params and params['role'] and params['role'] in ['administrator', 'manager', 'submitter']): + abort(400, "Parameters 'role' not found or is invalid") + kw = ag.granary.describe_silo(silo) + #Get existing owners, admins, managers and users + owners = [] + admins = [] + managers = [] + submitters = [] + if 'owners' in kw and kw['owners']: + owners = [x.strip() for x in kw['owners'].split(",") if x] + if 'administrators' in kw and kw['administrators']: + admins = [x.strip() for x in kw['administrators'].split(",") if x] + if 'managers' in kw and kw['managers']: + managers = [x.strip() for x in kw['managers'].split(",") if x] + if 'submitters' in kw and kw['submitters']: + submitters = [x.strip() for x in kw['submitters'].split(",") if x] + to_remove = [] + to_add = [] + if params['role'] == 'administrator': + if not 'administrator' in ident['permissions']: + abort(403, "Need to be administrator to add user to role admin") + if not username in admins: + to_add.append((username, 'administrator')) + admins.append(username) + if not username in owners: + owners.append(username) + if username in managers: + managers.remove(username) + to_remove.append((username, 'manager')) + if username in submitters: + submitters.remove(username) + to_remove.append((username, 'submitter')) + elif params['role'] == 'manager': + if not username in managers: + to_add.append((username, 'manager')) + managers.append(username) + if not username in owners: + owners.append(username) + if username in admins: + if not 'administrator' in ident['permissions']: + abort(403, "Need to be admin to modify user of role admin") + if len(admins) == 1: + abort(403, "Add another administrator to silo before updating user role") + admins.remove(username) + to_remove.append((username, 'administrator')) + if username in submitters: + submitters.remove(username) + to_remove.append((username, 'submitter')) + elif params['role'] == 'submitter': + if not username in submitters: + to_add.append((username, 'submitter')) + submitters.append(username) + if not username in owners: + owners.append(username) + if username in admins: + if not 'administrator' in ident['permissions']: + abort(403, "Need to be admin to modify user of role admin") + if len(admins) == 1: + abort(403, "Add another administrator to silo before updating user role") + admins.remove(username) + to_remove.append((username, 'administrator')) + if username in managers: + if len(managers) == 1 and len(admins) == 0: + abort(403, "Add another administrator or manager to silo before updating user role") + managers.remove(username) + to_remove.append((username, 'manager')) + + owners = list(set(owners)) + admins = list(set(admins)) + managers = list(set(managers)) + submitters = list(set(submitters)) + + # Update silo info + if to_remove or to_add: + kw['owners'] = ','.join(owners) + kw['administrators'] = ','.join(admins) + kw['managers'] = ','.join(managers) + kw['submitters'] = ','.join(submitters) + ag.granary.describe_silo(silo, **kw) + ag.granary.sync() + + #Add new silo users into database + if to_add: + add_group_users(silo, to_add) + response.status_int = 201 + response.status = "201 Created" + response.headers['Content-Location'] = url(controller="users", action="silouserview", silo=silo, username=username) + response_message = "201 Created" + + if to_remove: + delete_group_users(silo, to_remove) + response.status_int = 204 + response.status = "204 Updated" + response_message = None + else: + response.status_int = 400 + response.status = "400 Bad Request" + response_message = "No updates to user role" + + #Conneg return + accept_list = None + if 'HTTP_ACCEPT' in request.environ: + try: + accept_list = conneg_parse(request.environ['HTTP_ACCEPT']) + except: + accept_list= [MT("text", "html")] + if not accept_list: + accept_list= [MT("text", "html")] + mimetype = accept_list.pop(0) + while(mimetype): + if str(mimetype).lower() in ["text/html", "text/xhtml"]: + redirect(url(controller="users", action="silouserview", silo=silo, username=username)) + elif str(mimetype).lower() in ["text/plain", "application/json"]: + response.content_type = 'application/json; charset="UTF-8"' + return response_message + try: + mimetype = accept_list.pop(0) + except IndexError: + mimetype = None + #Whoops nothing satisfies - return text/plain + response.content_type = 'application/json; charset="UTF-8"' + return response_message + elif http_method == "DELETE": + kw = ag.granary.describe_silo(silo) + #Get existing owners, admins, managers and users + owners = [] + admins = [] + managers = [] + submitters = [] + if 'owners' in kw and kw['owners']: + owners = [x.strip() for x in kw['owners'].split(",") if x] + if 'administrators' in kw and kw['administrators']: + admins = [x.strip() for x in kw['administrators'].split(",") if x] + if 'managers' in kw and kw['managers']: + managers = [x.strip() for x in kw['managers'].split(",") if x] + if 'submitters' in kw and kw['submitters']: + submitters = [x.strip() for x in kw['submitters'].split(",") if x] + + #Gather user roles to delete + to_remove = [] + if username in admins: + if not 'administrator' in ident['permissions']: + abort(403, "Need to be admin to modify user of role admin") + if len(admins) == 1: + abort(403, "Add another administrator to silo before deleting user") + to_remove.append((username, 'administrator')) + admins.remove(username) + if username in managers: + if len(managers) == 1 and len(admins) == 0: + abort(403, "Add another administrator or manager to silo before deleting user") + managers.remove(username) + to_remove.append((username, 'manager')) + if username in submitters: + submitters.remove(username) + to_remove.append((username, 'submitter')) + if username in owners: + owners.remove(username) + + owners = list(set(owners)) + admins = list(set(admins)) + managers = list(set(managers)) + submitters = list(set(submitters)) + + if to_remove: + # Update silo info + kw['owners'] = ','.join(owners) + kw['administrators'] = ','.join(admins) + kw['managers'] = ','.join(managers) + kw['submitters'] = ','.join(submitters) + ag.granary.describe_silo(silo, **kw) + ag.granary.sync() + delete_group_users(silo, to_remove) + else: + abort(400, "No user to delete") + accept_list = None + response.content_type = "text/plain" + response.status_int = 200 + response.status = "200 OK" + return "{'ok':'true'}" + diff --git a/rdfdatabank/lib/HTTP_request.py b/rdfdatabank/lib/HTTP_request.py new file mode 100644 index 0000000..ad2a63c --- /dev/null +++ b/rdfdatabank/lib/HTTP_request.py @@ -0,0 +1,188 @@ +# -*- coding: utf-8 -*- +""" +Copyright (c) 2012 University of Oxford + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, --INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +""" + +import logging +import mimetypes +import httplib +import base64 +import urlparse +import json as simplejson + +logger = logging.getLogger('Dataset') + +class HTTPRequest(): + def __init__(self, endpointhost=None, secure=False): + if endpointhost: + self._endpointhost = endpointhost + self._endpointpath = None + self.secure = secure + + def get_content_type(self, filename): + # Originally copied from http://code.activestate.com/recipes/146306/: + return mimetypes.guess_type(filename)[0] or 'application/octet-stream' + + def get_data_type(self, params): + files = [] + fields = [] + decoded_params = params.items() + for i in decoded_params: + if len(i) == 2: + fields.append(i) + elif len(i) == 4: + files.append(i) + return fields, files + + def encode_multipart_formdata(self, fields, files): + # Originally copied from http://code.activestate.com/recipes/146306/: + """ + fields is a sequence of (name, value) elements for regular form fields. + files is a sequence of (name, filename, value, filetype) elements for data to be uploaded as files + Return (content_type, body) ready for httplib.HTTP instance + """ + BOUNDARY = '----------ThIs_Is_tHe_bouNdaRY_$' + CRLF = '\r\n' + L = [] + for (key, value) in fields: + L.append('--' + BOUNDARY) + L.append('Content-Disposition: form-data; name="%s"' % key) + L.append('') + L.append(value) + for (key, filename, value, filetype) in files: + L.append('--' + BOUNDARY) + L.append('Content-Disposition: form-data; name="%s"; filename="%s"' % (key, filename)) + L.append('Content-Type: %s' % (filetype or get_content_type(filename))) + L.append('') + L.append(value) + L.append('--' + BOUNDARY + '--') + L.append('') + body = CRLF.join(L) + content_type = 'multipart/form-data; boundary=%s' % BOUNDARY + return content_type, body + + def setRequestEndPoint(self, endpointhost=None, endpointpath=None): + if endpointhost or endpointpath: + if endpointhost: + self._endpointhost = endpointhost + # Reset credentials if setting host + self._endpointuser = None + self._endpointpass = None + logger.debug("setRequestEndPoint: endpointhost %s: " % self._endpointhost) + if endpointpath: + self._endpointpath = endpointpath + logger.debug("setRequestEndPoint: endpointpath %s: " % self._endpointpath) + return + + def setRequestUserPass(self, endpointuser=None, endpointpass=None): + if endpointuser: + self._endpointuser = endpointuser + self._endpointpass = endpointpass + logger.debug("setRequestEndPoint: endpointuser %s: " % self._endpointuser) + logger.debug("setRequestEndPoint: endpointpass %s: " % self._endpointpass) + else: + self._endpointuser = None + self._endpointpass = None + return + + def getRequestPath(self, rel): + rel = rel or "" + return urlparse.urljoin(self._endpointpath,rel) + + def getRequestUri(self, rel): + return "http://"+self._endpointhost+self.getRequestPath(rel) + + def encodeFormData(self, params): + (fields, files) = self.get_data_type(params) + (reqtype, reqdata) = self.encode_multipart_formdata(fields, files) + return reqtype, reqdata + + def doRequest(self, command, resource, reqdata=None, reqheaders={}): + if self._endpointuser: + auth = base64.encodestring("%s:%s" % (self._endpointuser, self._endpointpass)).strip() + reqheaders["Authorization"] = "Basic %s" % auth + if self.secure: + hc = httplib.HTTPSConnection(self._endpointhost) + else: + hc = httplib.HTTPConnection(self._endpointhost) + path = self.getRequestPath(resource) + response = None + responsedata = None + repeat = 10 + while path and repeat > 0: + repeat -= 1 + hc.request(command, path, reqdata, reqheaders) + response = hc.getresponse() + if response.status != 301: break + path = response.getheader('Location', None) + if path[0:6] == "https:": + # close old connection, create new HTTPS connection + hc.close() + hc = httplib.HTTPSConnection(self._endpointhost) # Assume same host for https: + else: + response.read() # Seems to be needed to free up connection for new request + logger.debug("Status: %i %s" % (response.status, response.reason)) + responsedata = response.read() + hc.close() + return (response, responsedata) + + def doHTTP_GET(self, endpointhost=None, endpointpath=None, resource=None, expect_type="*/*"): + reqheaders = { + "Accept": expect_type + } + self.setRequestEndPoint(endpointhost, endpointpath) + (response, responsedata) = self.doRequest("GET", resource, reqheaders=reqheaders) + #ctype = response.getheader('content-type') + #if (responsedata and expect_type.lower() == "application/json"): responsedata = simplejson.loads(responsedata) + #if (responsedata and "application/json" in ctype): responsedata = simplejson.loads(responsedata) + return (response, responsedata) + + def doHTTP_POST(self, data, data_type="application/octet-strem", + endpointhost=None, endpointpath=None, resource=None, expect_type="*/*"): + reqheaders = { + "Content-type": data_type, + "Accept": expect_type + } + self.setRequestEndPoint(endpointhost, endpointpath) + (response, responsedata) = self.doRequest("POST", resource, reqdata=data, reqheaders=reqheaders) + #ctype = response.getheader('content-type') + #if (responsedata and expect_type.lower() == "application/json"): responsedata = simplejson.loads(responsedata) + #if (responsedata and "application/json" in ctype): responsedata = simplejson.loads(responsedata) + return (response, responsedata) + + def doHTTP_PUT(self, data, data_type="application/octet-strem", + endpointhost=None, endpointpath=None, resource=None, expect_type="*/*"): + reqheaders = { + "Content-type": data_type, + "Accept": expect_type + } + self.setRequestEndPoint(endpointhost, endpointpath) + (response, responsedata) = self.doRequest("PUT", resource, reqdata=data, reqheaders=reqheaders) + #ctype = response.getheader('content-type') + #if (responsedata and "application/json" in ctype): responsedata = simplejson.loads(responsedata) + return (response, responsedata) + + def doHTTP_DELETE(self, endpointhost=None, endpointpath=None, resource=None): + self.setRequestEndPoint(endpointhost, endpointpath) + (response, responsedata) = self.doRequest("DELETE", resource) + return (response, responsedata) + diff --git a/rdfdatabank/lib/app_globals.py b/rdfdatabank/lib/app_globals.py index 4a916a0..591f143 100644 --- a/rdfdatabank/lib/app_globals.py +++ b/rdfdatabank/lib/app_globals.py @@ -1,3 +1,27 @@ +# -*- coding: utf-8 -*- +""" +Copyright (c) 2012 University of Oxford + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, --INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +""" + """The application's Globals object""" from pylons import config @@ -6,9 +30,13 @@ from redis import Redis from rdfdatabank.lib.utils import authz - +from rdfdatabank.lib.data_sync import sync_members +from rdfdatabank.lib.htpasswd import HtpasswdFile from rdfdatabank.lib.broadcast import BroadcastToRedis +#from rdfdatabank.config.users import _USERS +from rdfdatabank.config.namespaces import NAMESPACES, PREFIXES + class Globals(object): """Globals acts as a container for objects available throughout the @@ -24,19 +52,91 @@ def __init__(self): """ self.authz = authz - + #self.users = _USERS + self.NAMESPACES = NAMESPACES + self.PREFIXES = PREFIXES + + if config.has_key("granary.uri_root"): + self.root = config['granary.uri_root'] + if config.has_key("granary.store"): self.granary = Granary(config['granary.store']) if config.has_key("redis.host"): self.redishost = config['redis.host'] - self.r = Redis(self.redishost) + try: + self.r = Redis(self.redishost) + except: + self.r = None + if self.r and config.has_key("broadcast.to") and config['broadcast.to'] == "redis" and config.has_key("broadcast.queue"): + self.b = BroadcastToRedis(config['redis.host'], config['broadcast.queue']) + else: + self.r = None + self.redishost = None + self.b = None if config.has_key("solr.host"): from solr import SolrConnection self.solrhost = config['solr.host'] - self.solr = SolrConnection(self.solrhost) + try: + self.solr = SolrConnection(self.solrhost) + except: + self.solr = None + else: + self.solrhost = None + self.solr = None - if config.has_key("broadcast.to"): - if config['broadcast.to'] == "redis": - self.b = BroadcastToRedis(config['redis.host'], config['broadcast.queue']) + if config.has_key("naming_rule"): + self.naming_rule = config['naming_rule'] + + if config.has_key("naming_rule_humanized"): + self.naming_rule_humanized = config['naming_rule_humanized'] + elif config.has_key("naming_rule"): + self.naming_rule_humanized = config['naming_rule'] + + if config.has_key("metadata.embargoed"): + self.metadata_embargoed = config['metadata.embargoed'] + if isinstance(self.metadata_embargoed, basestring): + if self.metadata_embargoed.lower().strip() == 'true': + self.metadata_embargoed = True + else: + self.metadata_embargoed = False + elif not type(self.metadata_embargoed).__name__ == 'bool': + self.metadata_embargoed = False + else: + self.metadata_embargoed = False + + if config.has_key("auth.file"): + pwdfile = config['auth.file'] + self.passwdfile = HtpasswdFile(pwdfile) + self.passwdfile.load() + + if config.has_key("auth.info"): + self.userfile = config['auth.info'] + + if config.has_key("doi.count"): + self.doi_count_file = config['doi.count'] + + if config.has_key("formats_served"): + self.formats_served = config['formats_served'] + else: + self.formats_served = ["text/html", "text/xhtml", "text/plain", "application/json", "application/rdf+xml", "text/xml"] + + if config.has_key("publisher"): + self.publisher = config['publisher'] + else: + self.publisher = "Bodleian Libraries, University of Oxford" + + if config.has_key("rights"): + self.rights = config['rights'] + + if config.has_key("license"): + self.license = config['license'] + + if config.has_key("api.version"): + self.api_version = config['api.version'] + + try: + sync_members(self.granary) + except: + pass diff --git a/rdfdatabank/lib/auth.py b/rdfdatabank/lib/auth.py new file mode 100644 index 0000000..733f5be --- /dev/null +++ b/rdfdatabank/lib/auth.py @@ -0,0 +1,81 @@ +# -*- coding: utf-8 -*- +""" +Copyright (c) 2012 University of Oxford + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, --INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +""" + +"""Intended to work like a quick-started SQLAlchemy plugin""" + +from repoze.what.middleware import AuthorizationMetadata +from repoze.what.plugins.pylonshq import booleanize_predicates +from repoze.what.plugins.sql import configure_sql_adapters +from repoze.who.plugins.sa import SQLAlchemyAuthenticatorPlugin +from repoze.who.plugins.sa import SQLAlchemyUserMDPlugin + +from rdfdatabank.model import meta, User, Group, Permission + +# authenticator plugin +authenticator = SQLAlchemyAuthenticatorPlugin(User, meta.Session) +#authenticator.translations['user_name'] = 'username' + +# metadata provider plugins +# +# From the documentation in repoze.what.plugins.sql.adapters package +# +# For developers to be able to use the names they want in their model, both the +# groups and permissions source adapters use a "translation table" for the +# field and table names involved: +# * Group source adapter: +# * "section_name" (default: "group_name"): The name of the table field that +# contains the primary key in the groups table. +# * "sections" (default: "groups"): The groups to which a given user belongs. +# * "item_name" (default: "user_name"): The name of the table field that +# contains the primary key in the users table. +# * "items" (default: "users"): The users that belong to a given group. +# * Permission source adapter: +# * "section_name" (default: "permission_name"): The name of the table field +# that contains the primary key in the permissions table. +# * "sections" (default: "permissions"): The permissions granted to a given +# group. +# * "item_name" (default: "group_name"): The name of the table field that +# contains the primary key in the groups table. +# * "items" (default: "groups"): The groups that are granted a given +# permission. + +#adapters = configure_sql_adapters(User, Group, Permission, meta.Session, +# group_translations={'section_name': 'name', +# 'item_name': 'username'}, +# permission_translations={'section_name': 'name', +# 'item_name': 'username'}) +adapters = configure_sql_adapters(User, Group, Permission, meta.Session) + +user = SQLAlchemyUserMDPlugin(User, meta.Session) +#user.translations['user_name'] = 'username' + +group = AuthorizationMetadata( + {'sqlauth': adapters['group']}, + {'sqlauth': adapters['permission']} +) + +# THIS IS CRITICALLY IMPORTANT! Without this your site will +# consider every repoze.what predicate True! +booleanize_predicates() + diff --git a/rdfdatabank/lib/auth_entry.py b/rdfdatabank/lib/auth_entry.py new file mode 100644 index 0000000..4dc13b8 --- /dev/null +++ b/rdfdatabank/lib/auth_entry.py @@ -0,0 +1,437 @@ +# -*- coding: utf-8 -*- +""" +Copyright (c) 2012 University of Oxford + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, --INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +""" + +from rdfdatabank.model import meta, User, Group, Permission, Datasets +from sqlalchemy.exc import IntegrityError +#import traceback +#import logging +#log = logging.getLogger(__name__) + +def add_silo(silo_name): + try: + p_q = meta.Session.query(Permission) + + ga = Group() + if silo_name == '*': + ga.group_name = u'databank_administrator' + else: + ga.group_name = u'%s_administrator'%silo_name + ga.silo = u"%s"%silo_name + meta.Session.add(ga) + + p_q_admin = p_q.filter(Permission.permission_name == u'administrator').one() + p_q_admin.groups.append(ga) + + gb = Group() + if silo_name == '*': + gb.group_name = u'databank_manager' + else: + gb.group_name = u'%s_manager'%silo_name + gb.silo = u"%s"%silo_name + meta.Session.add(gb) + + p_q_manager = p_q.filter(Permission.permission_name == u'manager').one() + p_q_manager.groups.append(gb) + + gc = Group() + if silo_name == '*': + gc.group_name = u'databank_submitter' + else: + gc.group_name = u'%s_submitter'%silo_name + gc.silo = u'%s'%silo_name + meta.Session.add(gc) + + p_q_submitter = p_q.filter(Permission.permission_name == u'submitter').one() + p_q_submitter.groups.append(gc) + + meta.Session.commit() + except IntegrityError: + #log.error('Error adding new silo %s'%silo_name) + #print traceback.format_exc() + meta.Session.rollback() + return False + return True + +def delete_silo(silo_name): + try: + g_q = meta.Session.query(Group) + if silo_name == '*': + g_q_group1 = g_q.filter(Group.group_name == u'databank_administrator').one() + g_q_group2 = g_q.filter(Group.group_name == u'databank_manager').one() + g_q_group3 = g_q.filter(Group.group_name == u'databank_submitter').one() + else: + g_q_group1 = g_q.filter(Group.group_name == u'%s_administrator'%silo_name).one() + g_q_group2 = g_q.filter(Group.group_name == u'%s_manager'%silo_name).one() + g_q_group3 = g_q.filter(Group.group_name == u'%s_submitter'%silo_name).one() + meta.Session.delete(g_q_group1) + meta.Session.delete(g_q_group2) + meta.Session.delete(g_q_group3) + meta.Session.commit() + except IntegrityError: + #log.error('Error deleting silo %s'%silo_name) + #print traceback.format_exc() + meta.Session.rollback() + return False + return True + +def add_user(user_details): + u = User() + u.user_name = user_details['username'] + u._set_password(u'%s'%user_details['password']) + + if 'name' in user_details and user_details['name']: + u.name = u'%s'%user_details['name'] + + if 'firstname' in user_details and user_details['firstname']: + u.firstname = u'%s'%user_details['firstname'] + + if 'lastname' in user_details and user_details['lastname']: + u.lastname = u'%s'%user_details['lastname'] + + if 'email' in user_details and user_details['email']: + u.email = u'%s'%user_details['email'] + try: + meta.Session.add(u) + meta.Session.commit() + except IntegrityError: + #log.error('Error adding user %s'%user_details['username']) + #print traceback.format_exc() + meta.Session.rollback() + return False + return True + +def update_user(user_details): + if not ('username' in user_details and user_details['username']): + return False + try: + u_q = meta.Session.query(User) + u = u_q.filter(User.user_name == u'%s'%user_details['username']).one() + + if 'password' in user_details and user_details['password']: + u._set_password(u'%s'%user_details['password']) + + if 'name' in user_details and user_details['name']: + u.name = u'%s'%user_details['name'] + + if 'firstname' in user_details and user_details['firstname']: + u.firstname = u'%s'%user_details['firstname'] + + if 'lastname' in user_details and user_details['lastname']: + u.lastname = u'%s'%user_details['lastname'] + + if 'email' in user_details and user_details['email']: + u.email = u'%s'%user_details['email'] + + meta.Session.commit() + except IntegrityError: + #log.error('Error updating user data for user %s'%user_details['username']) + #print traceback.format_exc() + meta.Session.rollback() + return False + return True + +def delete_user(username): + try: + u_q = meta.Session.query(User) + u = u_q.filter(User.user_name == u'%s'%username).one() + meta.Session.delete(u) + meta.Session.commit() + except IntegrityError: + #log.error('Error deleting user %s. Does the user exist?'%username) + #print traceback.format_exc() + meta.Session.rollback() + return False + return True + +def add_user_groups(username, groups): + #groups is a list of (silo_name, permission_name) tuples + try: + u_q = meta.Session.query(User) + u = u_q.filter(User.user_name == u'%s'%username).one() + g_q = meta.Session.query(Group) + for silo_name, permission_name in groups: + if silo_name =='*': + g_q_group = g_q.filter(Group.group_name == u'databank_%s'%permission_name).one() + else: + g_q_group = g_q.filter(Group.group_name == u'%s_%s'%(silo_name, permission_name)).one() + u.groups.append(g_q_group) + meta.Session.commit() + except IntegrityError: + #log.error('Error adding user %s to group %s'%(username, unicode(groups))) + #print traceback.format_exc() + meta.Session.rollback() + return False + return True + +def delete_user_groups(username, groups): + #groups is a list of (silo_name, permission_name) tuples + try: + u_q = meta.Session.query(User) + u = u_q.filter(User.user_name == u'%s'%username).one() + g_q = meta.Session.query(Group) + for silo_name, permission_name in groups: + if silo_name =='*': + g = g_q.filter(Group.group_name == u'databank_%s'%permission_name).one() + else: + g = g_q.filter(Group.group_name == u'%s_%s'%(silo_name, permission_name)).one() + query = "DELETE FROM user_group WHERE user_id=%d and group_id=%d"%(u.id, g.id) + meta.Session.execute(query) + meta.Session.commit() + except IntegrityError: + #log.error('Error deleting user %s from group %s'%(username, unicode(groups))) + #print traceback.format_exc() + meta.Session.rollback() + return False + return True + +def add_group_users(silo_name, user_groups): + #user_groups is a list of (user_name, permission_name) tuples + try: + u_q = meta.Session.query(User) + g_q = meta.Session.query(Group) + for username, permission_name in user_groups: + u = u_q.filter(User.user_name == u'%s'%username).one() + if u: + if silo_name =='*': + g_q_group = g_q.filter(Group.group_name == u'databank_%s'%permission_name).one() + else: + g_q_group = g_q.filter(Group.group_name == u'%s_%s'%(silo_name, permission_name)).one() + u.groups.append(g_q_group) + meta.Session.commit() + except IntegrityError: + #log.error( 'Error adding users %s to group %s'%(unicode(user_groups), silo_name)) + #print traceback.format_exc() + meta.Session.rollback() + return False + return True + +def delete_group_users(silo_name, user_groups): + #user_groups is a list of (user_name, permission_name) tuples + try: + u_q = meta.Session.query(User) + g_q = meta.Session.query(Group) + for username, permission_name in user_groups: + u = u_q.filter(User.user_name == u'%s'%username).one() + if silo_name =='*': + g = g_q.filter(Group.group_name == u'databank_%s'%permission_name).one() + else: + g = g_q.filter(Group.group_name == u'%s_%s'%(silo_name, permission_name)).one() + query = "DELETE FROM user_group WHERE user_id=%d and group_id=%d"%(u.id, g.id) + meta.Session.execute(query) + meta.Session.commit() + except IntegrityError: + #log.error('Error deleting users %s from group %s'%(unicode(user_groups), silo_name)) + #print traceback.format_exc() + meta.Session.rollback() + return False + return True + +def list_users(): + users = meta.Session.query(User) + users_list = [] + for u in users: + # print u.id, u.user_name, u.email, u.name, u.firstname, u.lastname + # u._get_password + user_details = {} + user_details['user_name'] = u.user_name + user_details['name'] = u.name + user_details['firstname'] = u.firstname + user_details['lastname'] = u.lastname + user_details['email'] = u.email + user_details['groups'] = [] + # groups user belongs to + for g in u.groups: + # print g.id, g.group_name, g.silo + # permissions associated with the group + for p in g.permissions: + # print p.id, p.permission_name + user_details['groups'].append((g.silo, p.permission_name)) + users_list.append(user_details) + return users_list + +def list_groups(): + groups = meta.Session.query(Group) + #for g in groups: + # print g.id, g.group_name, g.silo + # permissions associated with the group + # for p in g.permissions: + # print p.id, p.permission_name + return groups + +def list_silos(star=False): + silos = [] + groups = meta.Session.query(Group) + for g in groups: + if g.silo == "*" and star and not g.silo in silos: + silos.append(g.silo) + elif not g.silo in silos and g.silo != "*": + silos.append(g.silo) + return silos + +def list_permissions(): + permissions = meta.Session.query(Permission) + #for p in permissions: + # print p.id, p.permission_name + return permissions + +def list_usernames(): + users = meta.Session.query(User) + usernames = [] + for u in users: + usernames.append(u.user_name) + return usernames + +def list_user_permissions(username, siloname): + u_q = meta.Session.query(User) + u = u_q.filter(User.user_name == u'%s'%username).one() + p_list = [] + #groups user belongs to + for g in u.groups: + if g.silo == siloname: + #permissions associated with the group + for p in g.permissions: + p_list.append(p.permission_name) + return p_list + +def list_user_groups(username): + u_q = meta.Session.query(User) + u = u_q.filter(User.user_name == u'%s'%username).one() + groups =[] + for g in u.groups: + for p in g.permissions: + groups.append((g.silo, p.permission_name)) + return groups + +def list_group_users(siloname): + all_users = meta.Session.query(User) + group_users =[] + for u in all_users: + for g in u.groups: + if g.silo == siloname: + #permissions associated with the group + for p in g.permissions: + #TODO:Check if I am getting only those permission associated with the user and group + group_users.append({ + 'user_name':u.user_name, + 'permission':p.permission_name, + 'name':u.name, + 'firstname':u.firstname, + 'lastname':u.lastname}) + return group_users + +def list_group_usernames(siloname): + all_users = meta.Session.query(User) + admins = [] + managers = [] + submitters = [] + for u in all_users: + for g in u.groups: + if g.silo == siloname: + for p in g.permissions: + if p.permission_name == 'administrator' and not u.user_name in admins: + admins.append(u.user_name) + if p.permission_name == 'manager' and not u.user_name in managers: + managers.append(u.user_name) + if p.permission_name == 'submitter' and not u.user_name in submitters: + submitters.append(u.user_name) + return (admins, managers, submitters) + +def list_new_users(): + #Users not a part of any group + all_users = meta.Session.query(User) + ungrouped_users =[] + for u in all_users: + if not u.groups: + ungrouped_users.append({ + 'user_name':u.user_name, + 'permission':None, + 'name':u.name, + 'firstname':u.firstname, + 'lastname':u.lastname}) + return ungrouped_users + +def list_user(username): + u_q = meta.Session.query(User) + u = u_q.filter(User.user_name == u'%s'%username).one() + user_details = {} + user_details['id'] = u.id + user_details['user_name'] = u.user_name + user_details['name'] = u.name + user_details['firstname'] = u.firstname + user_details['lastname'] = u.lastname + user_details['email'] = u.email + user_details['groups'] = [] + for g in u.groups: + for p in g.permissions: + user_details['groups'].append((g.silo, p.permission_name)) + return user_details + +def add_dataset(silo_name, id): + d = Datasets() + d.silo = u"%s"%silo_name + d.id = u"%s"%id + try: + meta.Session.add(d) + meta.Session.commit() + except IntegrityError: + #log.error('Error adding dataset %s in silo %s'%(id, silo_name)) + #print traceback.format_exc() + meta.Session.rollback() + return False + return True + +def delete_dataset(silo_name, id): + try: + d_q = meta.Session.query(Datasets) + d_q_id = d_q.filter(Datasets.silo == u'%s'%silo_name).filter(Datasets.id == u'%s'%id).one() + meta.Session.delete(d_q_id) + meta.Session.commit() + except IntegrityError: + #log.error('Error deleting dataset %s in silo %s'%(id, silo_name)) + #print traceback.format_exc() + meta.Session.rollback() + return False + return True + +def get_datasets_count(silo_name): + d_q = meta.Session.query(Datasets) + d_q_silo = d_q.filter(Datasets.silo == u'%s'%silo_name).count() + return d_q_silo + +def get_datasets(silo_name, start=0, rows=100): + d_q = meta.Session.query(Datasets) + try: + start = int(start) + except: + start = 0 + try: + rows = int(rows) + except: + rows = 100 + d_q_silo = d_q.filter(Datasets.silo == u'%s'%silo_name).limit(rows).offset(start).all() + datasets = [] + for s in d_q_silo: + datasets.append(s.id) + return datasets + diff --git a/rdfdatabank/lib/base.py b/rdfdatabank/lib/base.py index 56a090b..0ed3113 100644 --- a/rdfdatabank/lib/base.py +++ b/rdfdatabank/lib/base.py @@ -1,9 +1,34 @@ +# -*- coding: utf-8 -*- +""" +Copyright (c) 2012 University of Oxford + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, --INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +""" + """The base Controller API Provides the BaseController class for subclassing. """ from pylons.controllers import WSGIController from pylons.templating import render_mako as render +from rdfdatabank.model import meta class BaseController(WSGIController): @@ -12,4 +37,7 @@ def __call__(self, environ, start_response): # WSGIController.__call__ dispatches to the Controller method # the request is routed to. This routing information is # available in environ['pylons.routes_dict'] - return WSGIController.__call__(self, environ, start_response) + try: + return WSGIController.__call__(self, environ, start_response) + finally: + meta.Session.remove() diff --git a/rdfdatabank/lib/broadcast.py b/rdfdatabank/lib/broadcast.py index f320331..31044ce 100644 --- a/rdfdatabank/lib/broadcast.py +++ b/rdfdatabank/lib/broadcast.py @@ -1,3 +1,27 @@ +# -*- coding: utf-8 -*- +""" +Copyright (c) 2012 University of Oxford + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, --INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +""" + from redis import Redis from redis.exceptions import ConnectionError @@ -16,7 +40,7 @@ def lpush(self, msg): self.r.lpush(self.queue, msg) except ConnectionError: # The client can sometimes be timed out and disconnected at the server. self.r = Redis(self.redis_host) - self.lpush(self.queue, msg) + self.r.lpush(self.queue, msg) def change(self, silo, id, filepath=None, **kw): msg = {} @@ -51,6 +75,14 @@ def deletion(self, silo, id, filepath=None, **kw): msg['filepath'] = filepath self.lpush(simplejson.dumps(msg)) + def silo_creation(self, silo, **kw): + msg = {} + msg.update(kw) + msg['_timestamp'] = datetime.now().isoformat() + msg.update({'type':'c', + 'silo':silo}) + self.lpush(simplejson.dumps(msg)) + def silo_deletion(self, silo, **kw): msg = {} msg.update(kw) @@ -59,6 +91,14 @@ def silo_deletion(self, silo, **kw): 'silo':silo}) self.lpush(simplejson.dumps(msg)) + def silo_change(self, silo, **kw): + msg = {} + msg.update(kw) + msg['_timestamp'] = datetime.now().isoformat() + msg.update({'type':'u', + 'silo':silo}) + self.lpush(simplejson.dumps(msg)) + def embargo_change(self, silo, id, embargoed=None, until=None, **kw): msg = {} msg.update(kw) diff --git a/rdfdatabank/lib/conneg.py b/rdfdatabank/lib/conneg.py index 6f03d3c..0eced7b 100644 --- a/rdfdatabank/lib/conneg.py +++ b/rdfdatabank/lib/conneg.py @@ -1,5 +1,30 @@ #!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +Copyright (c) 2012 University of Oxford +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, --INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +""" + +from pylons import app_globals as ag +from datetime import datetime def skipws(next): skip = 1 if not skip: @@ -205,7 +230,7 @@ def param(self): key = self.ml.next() eq = self.ml.next() if eq != "=": - raise ParseError("Expected =, got: " + sl) + raise ParseError("Expected =, got: " + eq) val = self.ml.next() return (key, val) @@ -231,8 +256,20 @@ def best(client, server): def parse(data): lex = MiniLex(data) + p = Parser(lex) mts = p.process() + + #Accept headers added using javascript are appended to the end of the list of default accept headers + #This behaviour observed in Opera 9.80, Chrome 10.0, MSIE 7.0, MSIE 8.0. + #In Firefox 3.6.14 and Firefox 3.6.15, only the new headers set in ajax is sent + #See doc accessLogEWithHeaderInfo_2011_03_16 + #So moving the last accept header to the front + tmp = str(mts[-1]).lower() + if tmp in ag.formats_served: + last_mt = mts.pop() + mts.insert(0, last_mt) + mts.sort(key=lambda x: x.sort2(), reverse=True) mts.sort(key=lambda x: x.qval, reverse=True) return mts @@ -249,5 +286,4 @@ def parse(data): mts2 = p2.process() b = best(mts, mts2) - print b diff --git a/rdfdatabank/lib/data_sync.py b/rdfdatabank/lib/data_sync.py new file mode 100644 index 0000000..790435f --- /dev/null +++ b/rdfdatabank/lib/data_sync.py @@ -0,0 +1,101 @@ +# -*- coding: utf-8 -*- +""" +Copyright (c) 2012 University of Oxford + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, --INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +""" + +from rdfdatabank.lib.auth_entry import list_silos, list_usernames, list_group_usernames, add_silo, add_group_users + +def sync_members(g): + # NOTE: g._register_silos() IS AN EXPENSIVE OPERATION. + # THIS FUNCTION IS EXPENSIVE AND SHOULD BE CALLED ONLY IF REALLY NECESSARY + #g = ag.granary + g.state.revert() + g._register_silos() + granary_list = g.silos + + granary_list_database = list_silos() + usernames = list_usernames() + for silo in granary_list: + if not silo in granary_list_database: + add_silo(silo) + kw = g.describe_silo(silo) + + #Get existing owners, admins, managers and submitters from silo metadata + owners = [] + admins = [] + managers = [] + submitters = [] + if 'administrators' in kw and kw['administrators']: + admins = [x.strip() for x in kw['administrators'].split(",") if x] + if 'managers' in kw and kw['managers']: + managers = [x.strip() for x in kw['managers'].split(",") if x] + if 'submitters' in kw and kw['submitters']: + submitters = [x.strip() for x in kw['submitters'].split(",") if x] + + # Check users in silo metadata are valid users + owners = [x for x in owners if x in usernames] + admins = [x for x in admins if x in usernames] + managers = [x for x in managers if x in usernames] + submitters = [x for x in submitters if x in usernames] + + #Synchronize members in silo metadata with users in database + d_admins = [] + d_managers = [] + d_sunbmitters = [] + if silo in granary_list_database: + d_admins, d_managers, d_submitters = list_group_usernames(silo) + admins.extend(d_admins) + managers.extend(d_managers) + submitters.extend(d_submitters) + + # Ensure users are listed just once in silo metadata and owner is superset + owners.extend(admins) + owners.extend(managers) + owners.extend(submitters) + admins = list(set(admins)) + managers = list(set(managers)) + submitters = list(set(submitters)) + owners = list(set(owners)) + + # Add users in silo metadata to the database + new_silo_users = [] + for a in admins: + if not a in d_admins: + new_silo_users.append((a, 'administrator')) + for a in managers: + if not a in d_managers: + new_silo_users.append((a, 'manager')) + for a in new_submitters: + if not a in d_submitters: + new_silo_users.append((a, 'submitter')) + if new_silo_users: + add_group_users(silo, new_silo_users) + + #Write members into silo + kw['owners'] = ','.join(owners) + kw['administrators'] = ','.join(admins) + kw['managers'] = ','.join(managers) + kw['submitters'] = ','.join(submitters) + g.describe_silo(silo, **kw) + + g.sync() + return diff --git a/rdfdatabank/lib/doi_helper.py b/rdfdatabank/lib/doi_helper.py new file mode 100644 index 0000000..5cd5ef9 --- /dev/null +++ b/rdfdatabank/lib/doi_helper.py @@ -0,0 +1,143 @@ +# -*- coding: utf-8 -*- +""" +Copyright (c) 2012 University of Oxford + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, --INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +""" + +from rdfdatabank.lib.doi_schema import DataciteDoiSchema +from pylons import app_globals as ag +import os, codecs, uuid + +def get_doi_metadata(doi, item): + schema = DataciteDoiSchema() + xml_metadata = {} + xml_metadata['identifier']= schema.xml_schema['identifier']%doi + for key, predicates in schema.mandatory_metadata.iteritems(): + answers = None + for p in predicates: + answers = item.list_rdf_objects(item.uri, p) + if answers: + break + if not answers: + return False + if key == 'publicationYear': + xml_metadata[key] = schema.xml_schema[key]%answers[0].split('-')[0] + elif key not in schema.parent_tags: + xml_metadata[key] = schema.xml_schema[key]%answers[0] + else: + xml_subset = [] + for ans in answers: + if key == 'creator': + if len(ans.split(',')) == 2: + xml_subset.append(" "+schema.xml_schema[key]%ans) + else: + xml_subset.append(" "+schema.xml_schema[key]%ans) + if not xml_subset: + return False + xml_subset.insert(0, "<%s>"%schema.parent_tags[key]) + xml_subset.append(""%schema.parent_tags[key]) + xml_subset = "\n ".join(xml_subset) + xml_metadata[key] = xml_subset + + for grp, keys in schema.groups.iteritems(): + xml_subset = {} + for k in keys: + predicates = schema.optional_metadata['%s:%s'%(grp, k)] + answers = None + for p in predicates: + answers = item.list_rdf_objects(item.uri, p) + if answers: + break + if not answers or not answers[0]: + continue + if grp =='date': + xml_subset[k] = " "+schema.xml_schema[k]%answers[0].split('T')[0] + else: + xml_subset[k] = " "+schema.xml_schema[k]%answers[0] + if xml_subset: + xml_subset_str = ["<%s>"%schema.parent_tags[grp]] + for o in schema.schema_order[grp]: + if o in xml_subset.keys(): + xml_subset_str.append(xml_subset[o]) + xml_subset_str.append(""%schema.parent_tags[grp]) + xml_subset_str = "\n ".join(xml_subset_str) + xml_metadata[grp] = xml_subset_str + + for key, predicates in schema.optional_metadata.iteritems(): + if ':' in key and key.split(':')[0] in schema.groups.keys(): + continue + answers = None + for p in predicates: + answers = item.list_rdf_objects(item.uri, p) + if answers: + break + if not answers: + continue + if key not in schema.parent_tags: + xml_metadata[key] = schema.xml_schema[key]%answers[0] + else: + xml_subset = [] + for ans in answers: + xml_subset.append(" "+schema.xml_schema[key]%ans) + if xml_subset: + xml_subset.insert(0, "<%s>"%schema.parent_tags[key]) + xml_subset.append(""%schema.parent_tags[key]) + xml_subset = "\n ".join(xml_subset) + xml_metadata[key] = xml_subset + if not xml_metadata: + return False + fn = "/tmp/%s"%uuid.uuid4() + f = open(fn, 'w') + f.write("%s\n"%schema.xml_schema['header']) + for o in schema.schema_order['all']: + if o in xml_metadata: + f.write(" %s\n "%xml_metadata[o]) + f.write("%s\n"%schema.xml_schema['footer']) + f.close() + unicode_metadata = codecs.open(fn, 'r', encoding='utf-8').read() + return unicode_metadata + +def doi_count(increase=True): + if not os.path.isfile(ag.doi_count_file): + count = 0 + if increase: + count += 1 + f = open(ag.doi_count_file, 'w') + f.write(str(count)) + f.close() + return count + + f = open(ag.doi_count_file, 'r') + count = f.read() + f.close() + count = count.replace('\n', '').strip() + try: + count = int(count) + except: + return False + if not increase: + return str(count) + + count += 1 + f = open(ag.doi_count_file, 'w') + f.write(str(count)) + f.close() + return count diff --git a/rdfdatabank/lib/doi_schema.py b/rdfdatabank/lib/doi_schema.py new file mode 100755 index 0000000..1619ad1 --- /dev/null +++ b/rdfdatabank/lib/doi_schema.py @@ -0,0 +1,117 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +""" +Copyright (c) 2012 University of Oxford + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, --INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +""" + +class DataciteDoiSchema(): + def __init__(self): + """ + DOI service provided by the British Library on behalf of Datacite.org + API Doc: https://api.datacite.org/ + Metadata requirements: http://datacite.org/schema/DataCite-MetadataKernel_v2.0.pdf + """ + #Mandatory metadata + self.mandatory_metadata={ + #'identifier':['bibo:doi'], + 'creator':['dc:creator', 'dcterms:creator'], + 'title':['dc:title', 'dcterms:title'], + 'publisher':['dc:publisher', 'dcterms:publisher'], + 'publicationYear':['oxds:embargoedUntil', 'dcterms:issued', 'dcterms:modified', 'dc:date'] + } + + self.optional_metadata={ + 'subject':['dc:subject', 'dcterms:subject'], + #'contributor':['dc:contributor', 'dcterms:contributor'], + 'date:accepted':['dcterms:dateAccepted'], + 'date:available':['oxds:embargoedUntil'], + 'date:copyrighted':['dcterms:dateCopyrighted'], + 'date:created':['dcterms:created'], + 'date:issued':['dcterms:issued'], + 'date:submitted':['dcterms:dateSubmitted'], + 'date:updated':['dcterms:modified'], + #'date:valid':['dcterms:date'], + 'language':['dc:language', 'dcterms:language'], + 'resourceType':['dc:type','dcterms:type'], + 'alternateIdentifier':['dc:identifier', 'dcterms:identifier'], + #'RelatedIdentifier':[], + 'size':['dcterms:extent'], + 'format':['dc:format', 'dcterms:format'], + 'version':['oxds:currentVersion'], + 'rights':['dc:rights', 'dcterms:rights'], + 'description:other':['dc:description', 'dcterms:description'], + 'description:abstract':['dcterms:abstract'] + } + + self.schema_order={ + 'all':('identifier', 'creator', 'title', 'publisher', 'publicationYear', 'subject', 'contributor', 'date', 'language', 'resourceType', \ + 'alternateIdentifier', 'RelatedIdentifier', 'size', 'format', 'version', 'rights', 'description'), + 'date':('accepted', 'available', 'copyrighted', 'created', 'issued', 'submitted', 'updated', 'valid'), + 'description':('other', 'abstract') + } + + self.xml_schema={ + 'header':""" +""", + #'header':"""""", + #'header':"""""", + 'identifier':"""%s""", + 'creator':"""%s""", + 'title':"""%s""", + 'publisher':"""%s""", + 'publicationYear':"""%s""", + 'subject':"""%s""", + 'accepted':"""%s""", + 'available':"""%s""", + 'copyrighted':"""%s""", + 'created':"""%s""", + 'issued':"""%s""", + 'submitted':"""%s""", + 'updated':"""%s""", + 'valid':"""%s""", + 'language':"""%s""", + 'resourceType':"""%s""", + 'alternateIdentifier':"""%s""", + 'size':"""%s""", + 'format':"""%s""", + 'version':"""%s""", + 'rights':"""%s""", + 'other':"""%s""", + 'abstract':"""%s""", + 'footer':"""""" + } + + self.parent_tags={ + 'creator':'creators', + 'title':'titles', + 'subject':'subjects', + 'date':'dates', + 'alternateIdentifier':'alternateIdentifiers', + 'size':'sizes', + 'format':'formats', + 'description':'descriptions' + } + + self.groups={ + 'date':['accepted', 'available', 'copyrighted', 'created', 'issued', 'submitted', 'updated'], + 'description':['other', 'abstract'] + } diff --git a/rdfdatabank/lib/file_unpack.py b/rdfdatabank/lib/file_unpack.py new file mode 100644 index 0000000..90a1521 --- /dev/null +++ b/rdfdatabank/lib/file_unpack.py @@ -0,0 +1,338 @@ +# -*- coding: utf-8 -*- +""" +Copyright (c) 2012 University of Oxford + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, --INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +""" + +import subprocess +from threading import Thread +from datetime import datetime, timedelta +import os, shutil +from uuid import uuid4 +from rdflib import URIRef, Literal +from rdfdatabank.lib.utils import create_new, munge_manifest, test_rdf + +from pylons import app_globals as ag + +#import checkm +from zipfile import ZipFile, BadZipfile as BZ + +zipfile_root = "zipfile:" + +class BadZipfile(Exception): + """Cannot open zipfile using commandline tool 'unzip' to target directory""" + +def check_file_mimetype(real_filepath, mimetype): + if os.path.isdir(real_filepath): + return False + if os.path.islink(real_filepath): + real_filepath = os.readlink(real_filepath) + if not os.path.isfile(real_filepath): + return False + p = subprocess.Popen("file -ib '%s'" %(real_filepath), shell=True, stdout=subprocess.PIPE) + output_file = p.stdout + output_str = output_file.read() + if mimetype in output_str: + return True + else: + return False + +def get_zipfiles_in_dataset(dataset): + derivative = dataset.list_rdf_objects("*", "ore:aggregates") + zipfiles = {} + #if derivative and derivative.values() and derivative.values()[0]: + if derivative: + #for file_uri in derivative.values()[0]: + for file_uri in derivative: + if not file_uri.lower().endswith('.zip'): + continue + filepath = file_uri[len(dataset.uri)+1:] + real_filepath = dataset.to_dirpath(filepath) + if os.path.islink(real_filepath): + real_filepath = os.readlink(real_filepath) + if check_file_mimetype(real_filepath, 'application/zip'): + (fn, ext) = os.path.splitext(filepath) + #zipfiles[filepath]="%s-%s"%(dataset.item_id, fn) + zipfiles[filepath]=dataset.item_id + return zipfiles + +def store_zipfile(silo, target_item_uri, POSTED_file, ident): + zipfile_id = get_next_zipfile_id(silo.state['storage_dir']) + while(silo.exists("%s%s" % (zipfile_root, zipfile_id))): + zipfile_id = get_next_zipfile_id(silo.state['storage_dir']) + + #zip_item = silo.get_item("%s%s" % (zipfile_root, zipfile_id)) + zip_item = create_new(silo, "%s%s" % (zipfile_root, zipfile_id), ident) + zip_item.add_triple("%s/%s" % (zip_item.uri, POSTED_file.filename.lstrip(os.sep)), "dcterms:hasVersion", target_item_uri) + zip_item.put_stream(POSTED_file.filename, POSTED_file.file) + try: + POSTED_file.file.close() + except: + pass + zip_item.sync() + return zip_item + +def read_zipfile(filepath): + try: + tmpfile = ZipFile(filepath, "r") + except BZ: + raise BadZipfile + + # list filenames + #list_of_files = tmpfile.namelist() + + # file information + zipfile_contents = {} + for info in tmpfile.infolist(): + zipfile_contents[info.filename] = (info.file_size, info.date_time) + tmpfile.close() + return zipfile_contents + +def read_file_in_zipfile(filepath, filename): + try: + tmpfile = ZipFile(filepath, "r") + except BZ: + raise BadZipfile + + try: + fileinfo = tmpfile.getinfo(filename) + except KeyError: + return False + if fileinfo.file_size == 0: + return 0 + + # read file + file_contents = None + file_contents = tmpfile.read(filename) + tmpfile.close() + return file_contents + +def get_file_in_zipfile(filepath, filename, targetdir): + try: + tmpfile = ZipFile(filepath, "r") + except BZ: + raise BadZipfile + + try: + fileinfo = tmpfile.getinfo(filename) + except KeyError: + return False + if fileinfo.file_size == 0: + return 0 + + # extract file + targetfile = tmpfile.extract(filename, targetdir) + tmpfile.close() + return targetfile + +def unzip_file(filepath, target_directory=None): + # TODO add the checkm stuff back in + if not target_directory: + target_directory = "/tmp/%s" % (uuid4().hex) + p = subprocess.Popen("unzip -qq -d %s %s" % (target_directory, filepath), shell=True, stdout=subprocess.PIPE) + p.wait() + if p.returncode != 0: + raise BadZipfile + else: + return target_directory + +def get_items_in_dir(items_list, dirname, fnames): + for fname in fnames: + items_list.append(os.path.join(dirname,fname)) + return + +def unpack_zip_item(target_dataset, current_dataset, zip_item, silo, ident): + filepath = current_dataset.to_dirpath(zip_item) + if os.path.islink(filepath): + filepath = os.readlink(filepath) + emb = target_dataset.metadata.get('embargoed') + emb_until = target_dataset.metadata.get('embargoed_until') + + # -- Step 1 ----------------------------- + unpacked_dir = unzip_file(filepath) + + # -- Step 2 ----------------------------- + file_uri = current_dataset.uri + if not file_uri.endswith('/'): + file_uri += '/' + file_uri = "%s%s?version=%s"%(file_uri,zip_item,current_dataset.currentversion) + + items_list = [] + os.path.walk(unpacked_dir,get_items_in_dir,items_list) + + # -- Step 3 ----------------------------- + mani_file = None + #Read manifest + for i in items_list: + if 'manifest.rdf' in i and os.path.isfile(i): + mani_file = os.path.join('/tmp', uuid4().hex) + shutil.move(i, mani_file) + items_list.remove(i) + #os.remove(i) + break + + # -- Step 4 ----------------------------- + #Copy unpacked dir as new version + target_dataset.move_directory_as_new_version(unpacked_dir, log="Unpacked file %s. Contents"%zip_item) + + # -- Step 5 ----------------------------- + #Add type and isVersionOf metadata + target_dataset.add_namespace('oxds', "http://vocab.ox.ac.uk/dataset/schema#") + target_dataset.add_triple(target_dataset.uri, u"rdf:type", "oxds:Grouping") + target_dataset.add_triple(target_dataset.uri, "dcterms:isVersionOf", file_uri) + #TODO: Adding the following metadata again as moving directory deletes all this information. Need to find a better way + if emb: + target_dataset.add_triple(target_dataset.uri, u"oxds:isEmbargoed", 'True') + if emb_until: + target_dataset.add_triple(target_dataset.uri, u"oxds:embargoedUntil", emb_until) + else: + target_dataset.add_triple(target_dataset.uri, u"oxds:isEmbargoed", 'False') + #The embargo + #embargoed_until_date = (datetime.now() + timedelta(days=365*70)).isoformat() + #target_dataset.add_triple(target_dataset.uri, u"oxds:embargoedUntil", embargoed_until_date) + target_dataset.add_triple(target_dataset.uri, u"dcterms:identifier", target_dataset.item_id) + target_dataset.add_triple(target_dataset.uri, u"dcterms:mediator", ident) + target_dataset.add_triple(target_dataset.uri, u"dcterms:publisher", ag.publisher) + if ag.rights and ag.rights.startswith('http'): + target_dataset.add_triple(target_dataset.uri, u"dcterms:rights", URIRef(ag.rights)) + elif ag.rights: + target_dataset.add_triple(target_dataset.uri, u"dcterms:rights", Literal(ag.rights)) + if ag.license and ag.license.startswith('http'): + target_dataset.add_triple(target_dataset.uri, u"dcterms:license", URIRef(ag.license)) + elif ag.license: + target_dataset.add_triple(target_dataset.uri, u"dcterms:license", Literal(ag.license)) + target_dataset.add_triple(target_dataset.uri, u"dcterms:created", datetime.now()) + target_dataset.add_triple(target_dataset.uri, u"oxds:currentVersion", target_dataset.currentversion) + #Adding ore aggregates + unp_dir = unpacked_dir + if not unp_dir.endswith('/'): + unp_dir += '/' + target_uri_base = target_dataset.uri + if not target_uri_base.endswith('/'): + target_uri_base += '/' + for i in items_list: + i = i.replace(unp_dir, '') + target_dataset.add_triple(target_dataset.uri, "ore:aggregates", "%s%s"%(target_uri_base,i)) + target_dataset.add_triple(target_dataset.uri, u"dcterms:modified", datetime.now()) + target_dataset.sync() + + # -- Step 6 ----------------------------- + #Munge rdf + #TODO: If manifest is not well formed rdf - inform user. Currently just ignored. + if mani_file and os.path.isfile(mani_file) and test_rdf(mani_file): + munge_manifest(mani_file, target_dataset) + os.remove(mani_file) + + # -- Step 7 ----------------------------- + #uri_s = "%s/%s" % (current_dataset.uri, zip_item.lstrip(os.sep)) + #uri_p = "%s?version=%s" % (target_dataset.uri, target_dataset.currentversion) + #current_dataset.add_triple(uri_s, "dcterms:hasVersion", uri_p) + #current_dataset.sync() + + target_dataset.sync() + target_dataset.sync() + target_dataset.sync() + return True + +""" +class unpack_zip_item(Thread): + def __init__ (self, target_dataset, current_dataset, zip_item, silo, ident): + Thread.__init__(self) + self.target_dataset = target_dataset + self.current_dataset = current_dataset + self.zip_item = zip_item + self.silo =silo + self.ident = ident + + def run(self): + filepath = self.current_dataset.to_dirpath(self.zip_item) + if os.path.islink(filepath): + filepath = os.readlink(filepath) + + # -- Step 1 ----------------------------- + unpacked_dir = unzip_file(filepath) + + # -- Step 2 ----------------------------- + file_uri = self.current_dataset.uri + if not file_uri.endswith('/'): + file_uri += '/' + file_uri = "%s%s"%(file_uri,self.zip_item) + + items_list = [] + os.path.walk(unpacked_dir,get_items_in_dir,items_list) + + # -- Step 3 ----------------------------- + mani_file = None + #Read manifest + for i in items_list: + if 'manifest.rdf' in i and os.path.isfile(i): + mani_file = os.path.join('/tmp', uuid4().hex) + shutil.move(i, mani_file) + items_list.remove(i) + #os.remove(i) + break + + # -- Step 4 ----------------------------- + #Copy unpacked dir as new version + self.target_dataset.move_directory_as_new_version(unpacked_dir) + + # -- Step 5 ----------------------------- + #Add type and isVersionOf metadata + self.target_dataset.add_namespace('oxds', "http://vocab.ox.ac.uk/dataset/schema#") + self.target_dataset.add_triple(self.target_dataset.uri, u"rdf:type", "oxds:Grouping") + self.target_dataset.add_triple(self.target_dataset.uri, "dcterms:isVersionOf", file_uri) + #TODO: Adding the following metadata again as moving directory deletes all this information. Need to find a better way + embargoed_until_date = (datetime.now() + timedelta(days=365*70)).isoformat() + self.target_dataset.add_triple(self.target_dataset.uri, u"oxds:isEmbargoed", 'True') + self.target_dataset.add_triple(self.target_dataset.uri, u"oxds:embargoedUntil", embargoed_until_date) + self.target_dataset.add_triple(self.target_dataset.uri, u"dcterms:identifier", self.target_dataset.item_id) + self.target_dataset.add_triple(self.target_dataset.uri, u"dcterms:mediator", self.ident) + self.target_dataset.add_triple(self.target_dataset.uri, u"dcterms:publisher", ag.publisher) + self.target_dataset.add_triple(self.target_dataset.uri, u"dcterms:created", datetime.now()) + self.target_dataset.add_triple(self.target_dataset.uri, u"oxds:currentVersion", self.target_dataset.currentversion) + #Adding ore aggregates + unp_dir = unpacked_dir + if not unp_dir.endswith('/'): + unp_dir += '/' + target_uri_base = self.target_dataset.uri + if not target_uri_base.endswith('/'): + target_uri_base += '/' + for i in items_list: + i = i.replace(unp_dir, '') + self.target_dataset.add_triple(self.target_dataset.uri, "ore:aggregates", "%s%s"%(target_uri_base,i)) + self.target_dataset.add_triple(self.target_dataset.uri, u"dcterms:modified", datetime.now()) + self.target_dataset.sync() + + # -- Step 6 ----------------------------- + #Munge rdf + #TODO: If manifest is not well formed rdf - inform user. Currently just ignored. + if mani_file and os.path.isfile(mani_file) and test_rdf(mani_file): + munge_manifest(mani_file, self.target_dataset, manifest_type='http://vocab.ox.ac.uk/dataset/schema#Grouping') + + # -- Step 7 ----------------------------- + #Delete the status + self.target_dataset.del_triple(self.target_dataset.uri, u"dcterms:status") + self.target_dataset.sync() + self.target_dataset.sync() + self.target_dataset.sync() + self.current_dataset.add_triple("%s/%s" % (self.current_dataset.uri, self.zip_item.lstrip(os.sep)), "dcterms:hasVersion", self.target_dataset.uri) + self.current_dataset.sync() +""" diff --git a/rdfdatabank/lib/helpers.py b/rdfdatabank/lib/helpers.py index e30d8fd..aff6947 100644 --- a/rdfdatabank/lib/helpers.py +++ b/rdfdatabank/lib/helpers.py @@ -1,3 +1,27 @@ +# -*- coding: utf-8 -*- +""" +Copyright (c) 2012 University of Oxford + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, --INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +""" + """Helper functions Consists of functions to typically be used within templates, but also diff --git a/rdfdatabank/lib/htpasswd.py b/rdfdatabank/lib/htpasswd.py new file mode 100644 index 0000000..e4da240 --- /dev/null +++ b/rdfdatabank/lib/htpasswd.py @@ -0,0 +1,155 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +"""Replacement for htpasswd +Downloaded from: http://trac.edgewall.org/browser/trunk/contrib/htpasswd.py +Original author: Eli Carter + +Copyright (C) 2003-2012 Edgewall Software +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + 3. The name of the author may not be used to endorse or promote + products derived from this software without specific prior + written permission. + +THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS +OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE +GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER +IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN +IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +""" + +import os +import sys +import random +from optparse import OptionParser + +# We need a crypt module, but Windows doesn't have one by default. Try to find +# one, and tell the user if we can't. +try: + import crypt +except ImportError: + try: + import fcrypt as crypt + except ImportError: + #sys.stderr.write("Cannot find a crypt module. " + # "Possibly http://carey.geek.nz/code/python-fcrypt/\n") + sys.exit(1) + + +def salt(): + """Returns a string of 2 randome letters""" + letters = 'abcdefghijklmnopqrstuvwxyz' \ + 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' \ + '0123456789/.' + return random.choice(letters) + random.choice(letters) + + +class HtpasswdFile: + """A class for manipulating htpasswd files.""" + + def __init__(self, filename, create=False): + self.entries = [] + self.filename = filename + if not create: + if os.path.exists(self.filename): + self.load() + else: + raise Exception("%s does not exist" % self.filename) + + def load(self): + """Read the htpasswd file into memory.""" + lines = open(self.filename, 'r').readlines() + self.entries = [] + for line in lines: + username, pwhash = line.split(':') + entry = [username, pwhash.rstrip()] + self.entries.append(entry) + + def save(self): + """Write the htpasswd file to disk""" + open(self.filename, 'w').writelines(["%s:%s\n" % (entry[0], entry[1]) + for entry in self.entries]) + + def update(self, username, password): + """Replace the entry for the given user, or add it if new.""" + pwhash = crypt.crypt(password, salt()) + matching_entries = [entry for entry in self.entries + if entry[0] == username] + if matching_entries: + matching_entries[0][1] = pwhash + else: + self.entries.append([username, pwhash]) + + def delete(self, username): + """Remove the entry for the given user.""" + self.entries = [entry for entry in self.entries + if entry[0] != username] + + +def main(): + """%prog [-c] -b filename username password + Create or update an htpasswd file""" + # For now, we only care about the use cases that affect tests/functional.py + parser = OptionParser(usage=main.__doc__) + parser.add_option('-b', action='store_true', dest='batch', default=False, + help='Batch mode; password is passed on the command line IN THE CLEAR.' + ) + parser.add_option('-c', action='store_true', dest='create', default=False, + help='Create a new htpasswd file, overwriting any existing file.') + parser.add_option('-D', action='store_true', dest='delete_user', + default=False, help='Remove the given user from the password file.') + + options, args = parser.parse_args() + + def syntax_error(msg): + """Utility function for displaying fatal error messages with usage + help. + """ + #sys.stderr.write("Syntax error: " + msg) + #sys.stderr.write(parser.get_usage()) + sys.exit(1) + + if not options.batch: + syntax_error("Only batch mode is supported\n") + + # Non-option arguments + if len(args) < 2: + syntax_error("Insufficient number of arguments.\n") + filename, username = args[:2] + if options.delete_user: + if len(args) != 2: + syntax_error("Incorrect number of arguments.\n") + password = None + else: + if len(args) != 3: + syntax_error("Incorrect number of arguments.\n") + password = args[2] + + passwdfile = HtpasswdFile(filename, create=options.create) + + if options.delete_user: + passwdfile.delete(username) + else: + passwdfile.update(username, password) + + passwdfile.save() + + +if __name__ == '__main__': + main() diff --git a/rdfdatabank/lib/ident_md.py b/rdfdatabank/lib/ident_md.py index 6c7969c..d7fc87a 100644 --- a/rdfdatabank/lib/ident_md.py +++ b/rdfdatabank/lib/ident_md.py @@ -1,8 +1,28 @@ -_DATA = { - 'admin': {'first_name':'ben', 'last_name':'OSteen', 'owner':'*', 'role':'admin'}, - 'admiral': {'name':'ADMIRAL Project', 'description':'ADMIRAL: A Data Management Infrastructure for Research', 'owner':['admiral'], 'role':'user'}, - 'eidcsr': {'name':'EIDCSR Project', 'description':'The Embedding Institutional Data Curation Services in Research (EIDCSR) project is addressing the research data management and curation challenges of three research groups in the University of Oxford.', 'owner':['eidcsr'], 'role':'user'}, - } +# -*- coding: utf-8 -*- +""" +Copyright (c) 2012 University of Oxford + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, --INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +""" + +from rdfdatabank.config.users import _USERS as _DATA class IdentMDProvider(object): diff --git a/rdfdatabank/lib/reqclassifier.py b/rdfdatabank/lib/reqclassifier.py new file mode 100644 index 0000000..9bffb8d --- /dev/null +++ b/rdfdatabank/lib/reqclassifier.py @@ -0,0 +1,66 @@ +# -*- coding: utf-8 -*- +""" +Copyright (c) 2012 University of Oxford + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, --INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +""" + +from webob import Request +import zope.interface +from repoze.who.classifiers import default_request_classifier +from repoze.who.interfaces import IRequestClassifier +import ConfigParser +from pylons import config + +def custom_request_classifier(environ): + """ Returns one of the classifiers 'app', 'browser' or any + standard classifiers returned by + repoze.who.classifiers:default_request_classifier + """ + + + classifier = default_request_classifier(environ) + if classifier == 'browser': + login_form_url = '/login' + login_handler = '/login_handler' + logout_handler = '/logout_handler' + logout_url = '/logout' + # Decide if the client is a (user-driven) browser or an application + if config.has_key("who.config_file"): + config_file = config["who.config_file"] + config_who = ConfigParser.ConfigParser() + config_who.readfp(open(config_file)) + login_form_url = config_who.get("plugin:friendlyform", "login_form_url") + login_handler = config_who.get("plugin:friendlyform", "login_handler_path") + logout_handler = config_who.get("plugin:friendlyform", "logout_handler_path") + logout_url = config_who.get("plugin:friendlyform", "post_logout_url") + + path_info = environ['PATH_INFO'] + #request = Request(environ) + #if not request.accept.best_match(['application/xhtml+xml', 'text/html']): + # # In our view, any client who doesn't support HTML/XHTML is an "app", + # # not a (user-driven) "browser". + # classifier = 'app' + if not path_info in [login_form_url, login_handler, logout_handler, logout_url]: + # In our view, any client who hasn't come in from the login url is an app + classifier = 'app' + return classifier +zope.interface.directlyProvides(custom_request_classifier, IRequestClassifier) + diff --git a/rdfdatabank/lib/search_term.py b/rdfdatabank/lib/search_term.py new file mode 100644 index 0000000..9ae3737 --- /dev/null +++ b/rdfdatabank/lib/search_term.py @@ -0,0 +1,230 @@ +# -*- coding: utf-8 -*- +""" +Copyright (c) 2012 University of Oxford + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, --INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +""" + + +class term_list(): + def get_all_search_fields(self): + return [ + "silo", + "id", + "uuid", + "embargoStatus", + "embargoedUntilDate", + "currentVersion", + "doi", + "aggregatedResource", + "publicationDate", + "abstract", + "accessRights", + "accrualMethod", + "accrualPeriodicity", + "accrualPolicy", + "alternative", + "audience", + "available", + "bibliographicCitation", + "conformsTo", + "contributor", + "coverage", + "created", + "creator", + "date", + "dateAccepted", + "dateCopyrighted", + "dateSubmitted", + "description", + "educationLevel", + "extent", + "format", + "hasFormat", + "hasPart", + "hasVersion", + "identifier", + "instructionalMethod", + "isFormatOf", + "isPartOf", + "isReferencedBy", + "isReplacedBy", + "isRequiredBy", + "issued", + "isVersionOf", + "language", + "license", + "mediator", + "medium", + "modified", + "provenance", + "publisher", + "references", + "relation", + "replaces", + "requires", + "rights", + "rightsHolder", + "source", + "spatial", + "subject", + "tableOfContents", + "temporal", + "title", + "type", + "valid", + "f_creator", + "f_mediator", + "f_embargoedUntilDate", + "f_license", + "f_rights", + "f_type", + "f_publisher", + "f_isPartOf", + "f_hasVersion", + "f_publicationDate", + "f_contributor", + "f_language", + "f_rightsHolder", + "f_source", + "f_subject", + "timestamp" + ] + + def get_search_field_dictionary(self): + field_names = { + "silo":"Silo", + "id":"Identifier", + "uuid":"Unique Identifier", + "embargoStatus":"Embargo status", + "embargoedUntilDate":"Embargoed until date", + "currentVersion":"Current version", + "doi":"DOI", + "aggregatedResource":"Aggregated resource", + "publicationDate":"Publication date", + "abstract":"Abstract", + "accessRights":"Access rights", + "accrualMethod":"Accrual method", + "accrualPeriodicity":"Accrual periodicity", + "accrualPolicy":"Accrual policy", + "alternative":"Alternative title", + "audience":"Audience", + "available":"Availability", + "bibliographicCitation":"Bibliographic citation", + "conformsTo":"Conforms to", + "contributor":"Contributor", + "coverage":"Coverage", + "created":"Date created", + "creator":"Creator", + "date":"Date", + "dateAccepted":"Date accepted", + "dateCopyrighted":"Date copyrighted", + "dateSubmitted":"Date submitted", + "description":"Description", + "educationLevel":"Education level", + "extent":"Extent", + "format":"Format", + "hasFormat":"Has format", + "hasPart":"Has part", + "hasVersion":"Has version", + "identifier":"Identifier", + "instructionalMethod":"Instructional method", + "isFormatOf":"Is format of", + "isPartOf":"Is part of", + "isReferencedBy":"Is referenced by", + "isReplacedBy":"Is replaced by", + "isRequiredBy":"Is required by", + "issued":"Date issued", + "isVersionOf":"Is version Of", + "language":"Language", + "license":"License", + "mediator":"Mediator", + "medium":"Medium", + "modified":"Date modified", + "provenance":"Provenance", + "publisher":"Publisher", + "references":"References", + "relation":"Relation", + "replaces":"Replaces", + "requires":"Requires", + "rights":"Rights", + "rightsHolder":"Rights holder", + "source":"Source", + "spatial":"Spatial coverage", + "subject":"Subject", + "tableOfContents":"Table of contents", + "temporal":"Temporal coverage", + "title":"Title", + "type":"Type", + "valid":"Valid", + "f_creator":"Creator", + "f_mediator":"Mediator", + "f_embargoedUntilDate":"Embargoed until date", + "f_license":"License", + "f_rights":"Rights", + "f_type":"Type", + "f_publisher":"Publisher", + "f_isPartOf":"Is part of", + "f_hasVersion":"Has version", + "f_publicationDate":"Publication date", + "f_contributor":"Contributor", + "f_language":"Language", + "f_rightsHolder":"Rights holder", + "f_source":"Source", + "f_subject":"Subject", + "timestamp":"Information indexed on" + } + return field_names + + def get_type_field_dictionary(self): + type_names = { + "silo":'Silos', + "dataset":"Data packages", + "item":"File names", + "all":"Any level" + } + return type_names + + def get_all_facet_fields(self): + return [ + "silo", + "embargoStatus", + "f_creator", + "f_mediator", + "f_embargoedUntilDate", + "f_license", + "f_rights", + "f_type", + "f_publisher", + "f_isPartOf", + "f_hasVersion", + "f_publicationDate", + "f_contributor", + "f_language", + "f_rightsHolder", + "f_source", + "f_subject" + ] + + def get_range_facet_fields(self): + return [ + "f_embargoedUntilDate", + "f_publicationDate" + ] diff --git a/rdfdatabank/lib/short_pid.py b/rdfdatabank/lib/short_pid.py new file mode 100644 index 0000000..d46e946 --- /dev/null +++ b/rdfdatabank/lib/short_pid.py @@ -0,0 +1,86 @@ +# -*- coding: utf-8 -*- +""" +#Downloaded from http://code.activestate.com/recipes/576918/ +#Created by Michael Fogleman +#Short URL Generator +""" + +#DEFAULT_ALPHABET = 'JedR8LNFY2j6MrhkBSADUyfP5amuH9xQCX4VqbgpsGtnW7vc3TwKE' +#DEFAULT_BLOCK_SIZE = 22 +DEFAULT_ALPHABET = 'ed82j6rh1kyfo5almu9x4iqzbgpstn7vc3w' +DEFAULT_BLOCK_SIZE = 18 + +class UrlEncoder(object): + def __init__(self, alphabet=DEFAULT_ALPHABET, block_size=DEFAULT_BLOCK_SIZE): + self.alphabet = alphabet + self.block_size = block_size + self.mask = (1 << block_size) - 1 + self.mapping = range(block_size) + self.mapping.reverse() + def encode_url(self, n, min_length=0): + return self.enbase(self.encode(n), min_length) + def decode_url(self, n): + return self.decode(self.debase(n)) + def encode(self, n): + return (n & ~self.mask) | self._encode(n & self.mask) + def _encode(self, n): + result = 0 + for i, b in enumerate(self.mapping): + if n & (1 << i): + result |= (1 << b) + return result + def decode(self, n): + return (n & ~self.mask) | self._decode(n & self.mask) + def _decode(self, n): + result = 0 + for i, b in enumerate(self.mapping): + if n & (1 << b): + result |= (1 << i) + return result + def enbase(self, x, min_length=0): + result = self._enbase(x) + padding = self.alphabet[0] * (min_length - len(result)) + return '%s%s' % (padding, result) + def _enbase(self, x): + n = len(self.alphabet) + if x < n: + return self.alphabet[x] + return self.enbase(x/n) + self.alphabet[x%n] + def debase(self, x): + n = len(self.alphabet) + result = 0 + for i, c in enumerate(reversed(x)): + result += self.alphabet.index(c) * (n**i) + return result + +DEFAULT_ENCODER = UrlEncoder() + +def encode(n): + return DEFAULT_ENCODER.encode(n) + +def decode(n): + return DEFAULT_ENCODER.decode(n) + +def enbase(n, min_length=0): + return DEFAULT_ENCODER.enbase(n, min_length) + +def debase(n): + return DEFAULT_ENCODER.debase(n) + +def encode_url(n, min_length=0): + return DEFAULT_ENCODER.encode_url(n, min_length) + +def decode_url(n): + return DEFAULT_ENCODER.decode_url(n) + +if __name__ == '__main__': + for a in range(0, 200000, 37): + b = encode(a) + c = enbase(b) + d = debase(c) + e = decode(d) + assert a == e + assert b == d + c = (' ' * (7 - len(c))) + c + #print '%6d %12d %s %12d %6d' % (a, b, c, d, e) + diff --git a/rdfdatabank/lib/sword_server.py b/rdfdatabank/lib/sword_server.py new file mode 100644 index 0000000..986dc64 --- /dev/null +++ b/rdfdatabank/lib/sword_server.py @@ -0,0 +1,793 @@ +from rdfdatabank.lib.utils import allowable_id2, create_new +from rdfdatabank.lib.auth_entry import list_silos, add_dataset +from sss import SwordServer, Authenticator, Auth, ServiceDocument, SDCollection, DepositResponse, SwordError, EntryDocument, Statement, Namespaces, AuthException +from sss.negotiator import AcceptParameters, ContentType + +from pylons import app_globals as ag + +import uuid, re, logging, urllib +from datetime import datetime +from rdflib import URIRef + +ssslog = logging.getLogger(__name__) + +JAILBREAK = re.compile("[\/]*\.\.[\/]*") + +class SwordDataBank(SwordServer): + """ + The main SWORD Server class. This class deals with all the CRUD requests as provided by the web.py HTTP + handlers + """ + def __init__(self, config, auth): + # get the configuration + self.config = config + self.auth_credentials = auth + + self.um = URLManager(config) + self.ns = Namespaces() + + def container_exists(self, path): + # extract information from the path + silo, dataset_id, accept_parameters = self.um.interpret_path(path) + + # is this a silo? + if not ag.granary.issilo(silo): + return False + + # is this an authorised silo? + silos = ag.authz(self.auth_credentials.identity) + if silo not in silos: + return False + + # get a full silo object + rdf_silo = ag.granary.get_rdf_silo(silo) + + # is the dataset in the authorised silo? + if not rdf_silo.exists(dataset_id): + return False + + # if we get here without failing, then the container exists (from the + # perspective of the user) + return True + + def media_resource_exists(self, path): + raise NotImplementedError() + + def service_document(self, path=None): + """ + Construct the Service Document. This takes the set of collections that are in the store, and places them in + an Atom Service document as the individual entries + """ + service = ServiceDocument(version=self.config.sword_version, + max_upload_size=self.config.max_upload_size) + + # get the authorised list of silos + silos = ag.authz(self.auth_credentials.identity) + + # now for each collection create an sdcollection + collections = [] + for col_name in silos: + href = self.um.silo_url(col_name) + title = col_name + mediation = self.config.mediation + + # content types accepted + accept = [] + multipart_accept = [] + if not self.config.accept_nothing: + if self.config.app_accept is not None: + for acc in self.config.app_accept: + accept.append(acc) + + if self.config.multipart_accept is not None: + for acc in self.config.multipart_accept: + multipart_accept.append(acc) + + # SWORD packaging formats accepted + accept_package = [] + for format in self.config.sword_accept_package: + accept_package.append(format) + + col = SDCollection(href=href, title=title, accept=accept, multipart_accept=multipart_accept, + accept_package=accept_package, mediation=mediation) + + collections.append(col) + + service.add_workspace("Silos", collections) + + # serialise and return + return service.serialise() + + def list_collection(self, path): + """ + List the contents of a collection identified by the supplied id + """ + raise NotImplementedError() + + def _get_authorised_rdf_silo(self, silo): + + if not ag.granary.issilo(silo): + return SwordError(status=404, empty=True) + + # get the authorised list of silos + #granary_list = ag.granary.silos + granary_list = list_silos() + silos = ag.authz(self.auth_credentials.identity) + + # does the collection/silo exist? If not, we can't do a deposit + if silo not in silos: + # if it's not in the silos it is either non-existant or it is + # forbidden... + if silo in granary_list: + # forbidden + raise SwordError(status=403, empty=True) + else: + # not found + raise SwordError(status=404, empty=True) + + # get a full silo object + rdf_silo = ag.granary.get_rdf_silo(silo) + return rdf_silo + + def deposit_new(self, silo, deposit): + """ + Take the supplied deposit and treat it as a new container with content to be created in the specified collection + Args: + -collection: the ID of the collection to be deposited into + -deposit: the DepositRequest object to be processed + Returns a DepositResponse object which will contain the Deposit Receipt or a SWORD Error + """ + # check against the authorised list of silos + rdf_silo = self._get_authorised_rdf_silo(silo) + + # ensure that we have a slug + if deposit.slug is None: + deposit.slug = str(uuid.uuid4()) + + # weed out unacceptable deposits + if rdf_silo.exists(deposit.slug): + raise SwordError(error_uri=DataBankErrors.dataset_conflict, msg="A Dataset with the name " + deposit.slug + " already exists") + if not allowable_id2(deposit.slug): + raise SwordError(error_uri=Errors.bad_request, msg="Dataset name can contain only the following characters - " + + ag.naming_rule_humanized + " and has to be more than 1 character") + + # NOTE: we pass in an empty dictionary of metadata on create, and then run + # _ingest_metadata to augment the item from the deposit + item = create_new(rdf_silo, deposit.slug, self.auth_credentials.username, {}) + add_dataset(silo, deposit.slug) + self._ingest_metadata(item, deposit) + + # NOTE: left in for reference for the time being, but deposit_new + # only support entry only deposits in databank. This will need to be + # re-introduced for full sword support + # store the content file if one exists, and do some processing on it + #deposit_uri = None + #derived_resource_uris = [] + #if deposit.content is not None: + + # if deposit.filename is None: + # deposit.filename = "unnamed.file" + # fn = self.dao.store_content(collection, id, deposit.content, deposit.filename) + + # now that we have stored the atom and the content, we can invoke a package ingester over the top to extract + # all the metadata and any files we want + + # FIXME: because the deposit interpreter doesn't deal with multipart properly + # we don't get the correct packaging format here if the package is anything + # other than Binary + # ssslog.info("attempting to load ingest packager for format " + str(deposit.packaging)) + # packager = self.configuration.get_package_ingester(deposit.packaging)(self.dao) + # derived_resources = packager.ingest(collection, id, fn, deposit.metadata_relevant) + + # An identifier which will resolve to the package just deposited + # deposit_uri = self.um.part_uri(collection, id, fn) + + # a list of identifiers which will resolve to the derived resources + # derived_resource_uris = self.get_derived_resource_uris(collection, id, derived_resources) + + # the aggregation uri + agg_uri = self.um.agg_uri(silo, deposit.slug) + + # the Edit-URI + edit_uri = self.um.edit_uri(silo, deposit.slug) + + # create the initial statement + s = Statement(aggregation_uri=agg_uri, rem_uri=edit_uri, states=[DataBankStates.initial_state]) + + # FIXME: need to sort out authentication before we can do this ... + # FIXME: also, it's not relevant unless we take a binary-only deposit, which + # we currently don't + # User already authorized to deposit in this silo (_get_authorised_rdf_silo). + # This is to augment metadata with details like who created, on behalf of, when + # + #by = deposit.auth.username if deposit.auth is not None else None + #obo = deposit.auth.on_behalf_of if deposit.auth is not None else None + #if deposit_uri is not None: + # s.original_deposit(deposit_uri, datetime.now(), deposit.packaging, by, obo) + #s.aggregates = derived_resource_uris + + # In creating the statement we use the existing manifest.rdf file in the + # item: + manifest = item.get_rdf_manifest() + f = open(manifest.filepath, "r") + rdf_string = f.read() + + # create the new manifest and store it + #Serialize rdf adds the sword statement - state, depositedOn, by, onBehalfOf, stateDesc + new_manifest = s.serialise_rdf(rdf_string) + item.put_stream("manifest.rdf", new_manifest) + + # FIXME: here is where we have to put the correct treatment in + # now generate a receipt for the deposit + # TODO: Add audit log from item.manifest in place of "created new item" + receipt = self.deposit_receipt(silo, deposit.slug, item, "created new item") + + # FIXME: while we don't have full text deposit, we don't need to augment + # the deposit receipt + + # now augment the receipt with the details of this particular deposit + # this handles None arguments, and converts the xml receipt into a string + # receipt = self.augmented_receipt(receipt, deposit_uri, derived_resource_uris) + + # finally, assemble the deposit response and return + dr = DepositResponse() + dr.receipt = receipt.serialise() + dr.location = receipt.edit_uri + + # Broadcast change as message + ag.b.creation(silo, deposit.slug, ident=self.auth_credentials.username) + + return dr + + def get_media_resource(self, path, accept_parameters): + """ + Get a representation of the media resource for the given id as represented by the specified content type + -id: The ID of the object in the store + -content_type A ContentType object describing the type of the object to be retrieved + """ + raise NotImplementedError() + + def replace(self, path, deposit): + """ + Replace all the content represented by the supplied id with the supplied deposit + Args: + - oid: the object ID in the store + - deposit: a DepositRequest object + Return a DepositResponse containing the Deposit Receipt or a SWORD Error + """ + silo, dataset_id, accept_parameters = self.um.interpret_path(path) + rdf_silo = self._get_authorised_rdf_silo(silo) + + # now get the dataset object itself + dataset = rdf_silo.get_item(dataset_id) + + # deal with possible problems with the filename + if deposit.filename is None or deposit.filename == "": + raise SwordError(error_uri=Errors.bad_request, msg="You must supply a filename to unpack") + if JAILBREAK.search(deposit.filename) != None: + raise SwordError(error_uri=Errors.bad_request, msg="'..' cannot be used in the path or as a filename") + + # FIXME: at the moment this metadata operation is not supported by DataBank + # + # first figure out what to do about the metadata + keep_atom = False + metadata_state = None # This will be used to store any state information associated + # with a metadata update. It gets tied up with the content state + # and any pre-existing states further down + #if deposit.atom is not None: + # ssslog.info("Replace request has ATOM part - updating") + # entry_ingester = self.configuration.get_entry_ingester()(self.dao) + # entry_ingester.ingest(collection, id, deposit.atom) + # keep_atom = True + + content_state = None + deposit_uri = None + derived_resource_uris = [] + if deposit.content is not None: + ssslog.info("Replace request has file content - updating") + + # remove all the old files before adding the new. We always leave + # behind the metadata; this will be overwritten later if necessary + #self.dao.remove_content(collection, id, True, keep_atom) + #Increment the version, but do not clone the previous version. + # An update will replace the entire contents of the container (if previously unpacked) with the bagit file + dataset.increment_version_delta(clone_previous_version=True, copy_filenames=['manifest.rdf']) + + # store the content file + dataset.put_stream(deposit.filename, deposit.content) + ssslog.debug("New incoming file stored with filename " + deposit.filename) + + # FIXME: unpacking doesn't happen here ... (keeping for the time being for reference) + # Broadcast to unpack and add sword:state in manifest + # + + # now that we have stored the atom and the content, we can invoke a package ingester over the top to extract + # all the metadata and any files we want. Notice that we pass in the metadata_relevant flag, so the + # packager won't overwrite the existing metadata if it isn't supposed to + #packager = self.configuration.get_package_ingester(deposit.packaging)(self.dao) + #derived_resources = packager.ingest(collection, id, fn, deposit.metadata_relevant) + #ssslog.debug("Resources derived from deposit: " + str(derived_resources)) + + # a list of identifiers which will resolve to the derived resources + #derived_resource_uris = self.get_derived_resource_uris(collection, id, derived_resources) + + # An identifier which will resolve to the package just deposited + deposit_uri = self.um.file_uri(silo, dataset_id, deposit.filename) + ssslog.debug("Incoming file has been stored at URI " + deposit_uri) + + # register a new content state to be used + content_state = DataBankStates.zip_file_added + + # Taken from dataset.py, seems to be the done thing when adding an item. + # NOTE: confirmed with Anusha that this is correct + dataset.del_triple(dataset.uri, u"dcterms:modified") + dataset.add_triple(dataset.uri, u"dcterms:modified", datetime.now()) + dataset.del_triple(dataset.uri, u"oxds:currentVersion") + dataset.add_triple(dataset.uri, u"oxds:currentVersion", dataset.currentversion) + + # before we do any state management, we have to be sure that the sword namespace + # is registered + dataset.get_rdf_manifest().add_namespace("sword", "http://purl.org/net/sword/terms/") + dataset.sync() + + # sort out the new list of states for the item + current_states = self._extract_states(dataset) + new_states = [] + + # for each existing state, consider whether to carry it over + ssslog.info("new content state: " + str(content_state)) + for state_uri, state_desc in current_states: + keep = True + if metadata_state is not None and state_uri in DataBankStates.metadata_states: + # we do not want the state if it is a metadata state and we have been given + # a new metadata state + keep = False + if content_state is not None and state_uri in DataBankStates.content_states: + ssslog.debug("Removing state: " + state_uri) + # we do not want the state if it is a content state and we have been given + # a new content state + keep = False + if keep: + ssslog.debug("carrying over state: " + state_uri) + new_states.append((state_uri, state_desc)) + + # add the new metadata and content states provided from above + if metadata_state is not None: + new_states.append(metadata_state) + if content_state is not None: + ssslog.debug("adding new content state: " + str(content_state)) + new_states.append(content_state) + + ssslog.debug("New Dataset States: " + str(new_states)) + + # FIXME: how safe is this? What other ore:aggregates might there be? + # we need to back out some of the triples in preparation to update the + # statement + # NOTE AR: I have commented the following lines. + # For aggregates this is not needed. put_stream will add the aggregate into the URI. + # Why delete other triples in the manifest - ?? + # sword:originalDeposit point to isVersionOf + + aggregates = dataset.list_rdf_objects(dataset.uri, u"ore:aggregates") + original_deposits = dataset.list_rdf_objects(dataset.uri, u"sword:originalDeposit") + states = dataset.list_rdf_objects(dataset.uri, u"sword:state") + + for a in aggregates: + dataset.del_triple(a, "*") + for od in original_deposits: + dataset.del_triple(od, "*") + for s in states: + dataset.del_triple(s, "*") + dataset.del_triple(dataset.uri, u"ore:aggregates") + dataset.del_triple(dataset.uri, u"sword:originalDeposit") + dataset.del_triple(dataset.uri, u"sword:state") + + # FIXME: also unsafe in the same way as above + # Write the md5 checksum into the manifest + # A deposit contains just the new stuff so no harm in deleting all triples + dataset.del_triple("*", u"oxds:hasMD5") + #dataset.del_triple(deposit_uri, u"oxds:hasMD5") + if deposit.content_md5 is not None: + dataset.add_triple(deposit_uri, u"oxds:hasMD5", deposit.content_md5) + + dataset.sync() + + # the aggregation uri + agg_uri = self.um.agg_uri(silo, dataset_id) + + # the Edit-URI + edit_uri = self.um.edit_uri(silo, dataset_id) + + # FIXME: here we also need to keep existing states where relevant. + # A state will continue to be relevant if it applies to an area of the + # item (i.e. the container or the media resource) for which this operation + # has no effect. + # for example: + # this is a metadata replace, but a status on the item is set to say that + # the item's zip file is corrupt and needs replacing. The new status + # should leave this alone (and probably not do anything, tbh), no matter + # what else it does + # create the statement outline + # FIXME: there is something weird going on with instantiating this object without the original_deposits argument + # apparently if I don't explicitly say there are no original deposits, then it "remembers" original deposits + # from previous uses of the object + s = Statement(aggregation_uri=agg_uri, rem_uri=edit_uri, states=new_states, original_deposits=[]) + + # set the original deposit (which sorts out the aggregations for us too) + by = deposit.auth.username if deposit.auth is not None else None + obo = deposit.auth.on_behalf_of if deposit.auth is not None else None + if deposit_uri is not None: + s.original_deposit(deposit_uri, datetime.now(), deposit.packaging, by, obo) + + # create the new manifest and store it + manifest = dataset.get_rdf_manifest() + f = open(manifest.filepath, "r") + rdf_string = f.read() + + new_manifest = s.serialise_rdf(rdf_string) + dataset.put_stream("manifest.rdf", new_manifest) + + # FIXME: add in proper treatment here + # now generate a receipt. + # TODO: Include audit log instead of 'added zip to dataset' + receipt = self.deposit_receipt(silo, dataset_id, dataset, "added zip to dataset") + + # now augment the receipt with the details of this particular deposit + # this handles None arguments, and converts the xml receipt into a string + receipt = self.augmented_receipt(receipt, deposit_uri, derived_resource_uris) + + # finally, assemble the deposit response and return + dr = DepositResponse() + dr.receipt = receipt.serialise() + dr.location = receipt.edit_uri + return dr + + def delete_content(self, path, delete): + """ + Delete all of the content from the object identified by the supplied id. the parameters of the delete + request must also be supplied + - oid: The ID of the object to delete the contents of + - delete: The DeleteRequest object + Return a DeleteResponse containing the Deposit Receipt or the SWORD Error + """ + raise NotImplementedError() + + def add_content(self, path, deposit): + """ + Take the supplied deposit and treat it as a new container with content to be created in the specified collection + Args: + -collection: the ID of the collection to be deposited into + -deposit: the DepositRequest object to be processed + Returns a DepositResponse object which will contain the Deposit Receipt or a SWORD Error + """ + raise NotImplementedError() + + def get_container(self, path, accept_parameters): + """ + Get a representation of the container in the requested content type + Args: + -oid: The ID of the object in the store + -content_type A ContentType object describing the required format + Returns a representation of the container in the appropriate format + """ + # by the time this is called, we should already know that we can return this type, so there is no need for + # any checking, we just get on with it + + ssslog.info("Container requested in mime format: " + accept_parameters.content_type.mimetype()) + silo, dataset_id, _ = self.um.interpret_path(path) + rdf_silo = self._get_authorised_rdf_silo(silo) + + # now get the dataset object itself + dataset = rdf_silo.get_item(dataset_id) + + # pick either the deposit receipt or the pure statement to return to the client + if accept_parameters.content_type.mimetype() == "application/atom+xml;type=entry": + # Supply audit log as treatment, in place of 'no treatment' + receipt = self.deposit_receipt(silo, dataset_id, dataset, "no treatment") # FIXME: what should the treatment here be + return receipt.serialise() + # FIXME: at the moment we don't support conneg on the edit uri + #elif accept_parameters.content_type.mimetype() == "application/rdf+xml": + # return self.dao.get_statement_content(collection, id) + #elif accept_parameters.content_type.mimetype() == "application/atom+xml;type=feed": + # return self.dao.get_statement_feed(collection, id) + else: + ssslog.info("Requested mimetype not recognised/supported: " + accept_parameters.content_type.mimetype()) + return None + + def deposit_existing(self, path, deposit): + """ + Deposit the incoming content into an existing object as identified by the supplied identifier + Args: + -oid: The ID of the object we are depositing into + -deposit: The DepositRequest object + Returns a DepositResponse containing the Deposit Receipt or a SWORD Error + """ + raise NotImplementedError() + + def delete_container(self, path, delete): + """ + Delete the entire object in the store + Args: + -oid: The ID of the object in the store + -delete: The DeleteRequest object + Return a DeleteResponse object with may contain a SWORD Error document or nothing at all + """ + raise NotImplementedError() + + def get_statement(self, path): + silo, dataset_id, accept_parameters = self.um.interpret_path(path) + rdf_silo = self._get_authorised_rdf_silo(silo) + + # now get the dataset object itself + dataset = rdf_silo.get_item(dataset_id) + + if accept_parameters.content_type.mimetype() == "application/rdf+xml": + return self.get_rdf_statement(dataset) + elif accept_parameters.content_type.mimetype() == "application/atom+xml;type=feed": + return self.get_atom_statement(dataset) + else: + return None + + # NOT PART OF STANDARD, BUT USEFUL + # These are used by the webpy interface to provide easy access to certain + # resources. Not implementing them is fine. If they are not implemented + # then you just have to make sure that your file paths don't rely on the + # Part http handler + + def get_part(self, path): + """ + Get a file handle to the part identified by the supplied path + - path: The URI part which is the path to the file + """ + raise NotImplementedError() + + def get_edit_uri(self, path): + raise NotImplementedError() + + def get_rdf_statement(self, dataset): + # The RDF statement is just the manifest file... + manifest = dataset.get_rdf_manifest() + f = open(manifest.filepath, "r") + return f.read() + + def get_atom_statement(self, dataset): + # FIXME: there isn't a requirement at this stage to support the atom + # statment for DataBank + return None + + def deposit_receipt(self, silo, identifier, item, treatment, verbose_description=None): + """ + Construct a deposit receipt document for the provided URIs + Returns an EntryDocument object + """ + # FIXME: we don't know what the item's API looks like yet; it's probably + # from somewhere within RecordSilo or Pairtree. Suck it and see ... + + # assemble the URIs we are going to need + + # the atom entry id + drid = self.um.atom_id(silo, identifier) + + # the Cont-URI + cont_uri = self.um.cont_uri(silo, identifier) + + # the EM-URI + em_uri = self.um.em_uri(silo, identifier) + em_uris = [(em_uri, None), (em_uri + ".atom", "application/atom+xml;type=feed")] + + # the Edit-URI and SE-IRI + edit_uri = self.um.edit_uri(silo, identifier) + se_uri = edit_uri + + # the splash page URI + splash_uri = self.um.html_url(silo, identifier) + + # the two statement uris + atom_statement_uri = self.um.state_uri(silo, identifier, "atom") + ore_statement_uri = self.um.state_uri(silo, identifier, "ore") + state_uris = [(atom_statement_uri, "application/atom+xml;type=feed"), (ore_statement_uri, "application/rdf+xml")] + + # ensure that there is a metadata object, and that it is populated with enough information to build the + # deposit receipt + dc_metadata, other_metadata = self._extract_metadata(item) + ssslog.debug("Incorporating metadata: " + str(dc_metadata)) + if dc_metadata is None: + dc_metadata = {} + if not dc_metadata.has_key("title"): + dc_metadata["title"] = ["SWORD Deposit"] + if not dc_metadata.has_key("creator"): + dc_metadata["creator"] = ["SWORD Client"] + if not dc_metadata.has_key("abstract"): + dc_metadata["abstract"] = ["Content deposited with SWORD client"] + + packaging = [] + for disseminator in self.config.sword_disseminate_package: + packaging.append(disseminator) + + # Now assemble the deposit receipt + dr = EntryDocument(atom_id=drid, alternate_uri=splash_uri, content_uri=cont_uri, + edit_uri=edit_uri, se_uri=se_uri, em_uris=em_uris, + packaging=packaging, state_uris=state_uris, dc_metadata=dc_metadata, + verbose_description=verbose_description, treatment=treatment) + + return dr + + # FIXME: currently this only deals with DC metadata as per the SWORD spec. + # If possible, we should extract other metadata from the item too, but since + # it is in RDF it's not so obvious how best to do it. Just pull out rdf + # terms? + def _extract_metadata(self, item): + graph = item.get_graph() + dc_metadata = {} + other_metadata = {} + # we're just going to focus on DC metadata, to comply with the SWORD + # spec + dc_offset = len(self.ns.DC_NS) + + for s, p, o in graph.triples((URIRef(item.uri), None, None)): + if p.startswith(self.ns.DC_NS): + # it is Dublin Core + field = p[dc_offset:] + if dc_metadata.has_key(field): + dc_metadata[field].append(o) + else: + dc_metadata[field] = [o] + return dc_metadata, other_metadata + + def augmented_receipt(self, receipt, original_deposit_uri, derived_resource_uris=[]): + receipt.original_deposit_uri = original_deposit_uri + receipt.derived_resource_uris = derived_resource_uris + return receipt + + def _ingest_metadata(self, item, deposit): + ed = deposit.get_entry_document() + entry_ingester = self.config.get_entry_ingester()() + entry_ingester.ingest(item, ed) + + def _extract_states(self, dataset): + states = [] + state_uris = dataset.list_rdf_objects(dataset.uri, u"sword:state") + for su in state_uris: + descriptions = dataset.list_rdf_objects(su, u"sword:stateDescription") + sd = None + if len(descriptions) > 0: + sd = str(descriptions[0]) # just take the first one, there should only be one + states.append((str(su), sd)) + return states + +class DefaultEntryIngester(object): + def __init__(self): + self.ns = Namespaces() + + # FIXME: could we put this into configuration? + # or we could define handlers for each element rather than + # just a field to put the value in. This will allow us to + # handle hierarchical metadata (e.g. atom:author), but without + # having to go down the route of building XSLT xwalks + # FIXME: a fuller treatment of atom metadata may be appropriate here + self.metadata_map = { + self.ns.ATOM + "title" : u"dcterms:title", + self.ns.ATOM + "summary" : u"dcterms:abstract" + } + # NOTE: much atom metadata is hierarchical so this approach may + # not work + + def ingest(self, item, entry, additive=False): + ssslog.debug("Ingesting Metadata; Additive? " + str(additive)) + + ssslog.debug("Non DC Metadata: " + str(entry.other_metadata)) + for element in entry.other_metadata: + if not self.metadata_map.has_key(element.tag): + # FIXME: only process metadata we recognise + ssslog.debug("skipping unrecognised metadata: " + element.tag) + continue + if element.text is not None: + item.add_triple(item.uri, self.metadata_map[element.tag], element.text.strip()) + + # explicitly handle the DC + for dc, values in entry.dc_metadata.iteritems(): + for v in values: + item.add_triple(item.uri, u"dcterms:" + dc, v) + + item.sync() + +class DataBankAuthenticator(Authenticator): + def __init__(self, config): + self.config = config + + def basic_authenticate(self, username, password, obo): + # In [AtomPub] Section 14, implementations MUST support HTTP Basic Authentication + # in conjunction with a TLS connection. The SWORD Profile relaxes this requirement: + # SWORD client and server implementations SHOULD be capable of being configured to + # use HTTP Basic Authentication [RFC2617] in conjunction with a TLS connection + # as specified by [RFC2818]. + + # FIXME: basic authentication does not attempt to actually authenticate + # anyone, it simply rejects any such request. This is in-line with SWORD + # above, but it would be better if it did authenticate. + + # Nonetheless, in general, databank will use repoze for everything including + # HTTP basic, so this method should never be activated + #return Auth(username, obo) + raise AuthException(authentication_failed=True, msg="HTTP Basic Auth without repoze.who not permitted on this server") + + def repoze_who_authenticate(self, identity, obo): + # the authentication is actually already done, so all we need to do is + # populate the Auth object + return DataBankAuth(identity["repoze.who.userid"], obo, identity) + +class DataBankAuth(Auth): + def __init__(self, username, on_behalf_of, identity): + Auth.__init__(self, username, on_behalf_of) + self.identity = identity + +class URLManager(object): + def __init__(self, config): + self.config = config + + def silo_url(self, silo): + return self.config.base_url + "silo/" + urllib.quote(silo) + + def atom_id(self, silo, identifier): + return "tag:container@databank/" + urllib.quote(silo) + "/" + urllib.quote(identifier) + + def cont_uri(self, silo, identifier): + return self.config.base_url + "edit-media/" + urllib.quote(silo) + "/" + urllib.quote(identifier) + + def em_uri(self, silo, identifier): + """ The EM-URI """ + return self.config.base_url + "edit-media/" + urllib.quote(silo) + "/" + urllib.quote(identifier) + + def edit_uri(self, silo, identifier): + """ The Edit-URI """ + return self.config.base_url + "edit/" + urllib.quote(silo) + "/" + urllib.quote(identifier) + + def agg_uri(self, silo, identifier): + return self.config.db_base_url + urllib.quote(silo) + "/datasets/" + urllib.quote(identifier) + + def html_url(self, silo, identifier): + """ The url for the HTML splash page of an object in the store """ + return self.agg_uri(silo, identifier) + + def state_uri(self, silo, identifier, type): + root = self.config.base_url + "statement/" + urllib.quote(silo) + "/" + urllib.quote(identifier) + if type == "atom": + return root + ".atom" + elif type == "ore": + return root + ".rdf" + + def file_uri(self, silo, identifier, filename): + """ The URL for accessing the parts of an object in the store """ + return self.config.db_base_url + urllib.quote(silo) + "/datasets/" + urllib.quote(identifier) + "/" + urllib.quote(filename) + + def interpret_path(self, path): + accept_parameters = None + silo = None + dataset = None + + # first figure out the accept parameters from the path suffix and chomp + # the path down to size + if path.endswith("rdf"): + accept_parameters = AcceptParameters(ContentType("application/rdf+xml")) + path = path[:-4] + elif path.endswith("atom"): + accept_parameters = AcceptParameters(ContentType("application/atom+xml;type=feed")) + path = path[:-5] + + # check to see if this has a / separator + if "/" in path: + # deconstruct the path into silo/dataset (if possible) + silo, dataset_id = path.split("/", 1) + else: + silo = path + + return silo, dataset_id, accept_parameters + +class DataBankErrors(object): + dataset_conflict = "http://databank.ox.ac.uk/errors/DatasetConflict" + +class DataBankStates(object): + initial_state = ("http://databank.ox.ac.uk/state/EmptyContainer", "Only the container for the dataset has been created so far") + zip_file_added = ("http://databank.ox.ac.uk/state/ZipFileAdded", "The dataset contains only the zip file") + content_states = [u"http://databank.ox.ac.uk/state/EmptyContainer", u"http://databank.ox.ac.uk/state/ZipFileAdded"] + metadata_states = [] diff --git a/rdfdatabank/lib/text.zip b/rdfdatabank/lib/text.zip deleted file mode 100644 index 1ec731a..0000000 Binary files a/rdfdatabank/lib/text.zip and /dev/null differ diff --git a/rdfdatabank/lib/unpack.py b/rdfdatabank/lib/unpack.py deleted file mode 100644 index ea72167..0000000 --- a/rdfdatabank/lib/unpack.py +++ /dev/null @@ -1,77 +0,0 @@ -import subprocess - -import os - -from redis import Redis - -from uuid import uuid4 - -from rdfdatabank.lib.utils import create_new - -#import checkm - -zipfile_root = "zipfile:" - -class BadZipfile(Exception): - """Cannot open zipfile using commandline tool 'unzip' to target directory""" - -def get_next_zipfile_id(siloname): - # TODO make this configurable - r = Redis() - return str(r.incr("%s:zipfile" % (siloname))) - -def find_last_zipfile(silo): - siloname = silo.state['storage_dir'] - r = Redis() - r.set("%s:zipfile" % (siloname), 0) - zipfile_id = 0 - while(silo.exists("%s%s" % (zipfile_root, zipfile_id))): - zipfile_id = r.incr("%s:zipfile" % (siloname)) - return zipfile_id - -def store_zipfile(silo, target_item_uri, POSTED_file, ident): - zipfile_id = get_next_zipfile_id(silo.state['storage_dir']) - while(silo.exists("%s%s" % (zipfile_root, zipfile_id))): - zipfile_id = get_next_zipfile_id(silo.state['storage_dir']) - - #zip_item = silo.get_item("%s%s" % (zipfile_root, zipfile_id)) - zip_item = create_new(silo, "%s%s" % (zipfile_root, zipfile_id), ident) - zip_item.add_triple("%s/%s" % (zip_item.uri, POSTED_file.filename.lstrip(os.sep)), "dcterms:hasVersion", target_item_uri) - zip_item.put_stream(POSTED_file.filename, POSTED_file.file) - try: - POSTED_file.file.close() - except: - pass - zip_item.sync() - return zip_item - -def unzip_file(filepath, target_directory=None): - # TODO add the checkm stuff back in - if not target_directory: - target_directory = "/tmp/%s" % (uuid4().hex) - p = subprocess.Popen("unzip -d %s %s" % (target_directory, filepath), shell=True, stdout=subprocess.PIPE) - _,_ = p.communicate() - if p.returncode != 0: - raise BadZipfile - else: - return target_directory - -def unpack_zip_item(zip_item, silo, ident): - derivative = zip_item.list_rdf_objects("*", "dcterms:hasVersion") - # 1 object holds 1 zipfile - may relax this easily given demand - assert len(derivative.keys()) == 1 - for file_uri in derivative.keys(): - filepath = file_uri[len(zip_item.uri)+1:] - real_filepath = zip_item.to_dirpath(filepath) - target_item = derivative[file_uri][0][len(silo.state['uri_base']):] - - # Overwrite current version instead of making new version? - - to_item = create_new(silo, target_item, ident) - #to_item = silo.get_item(target_item) - unpacked_dir = unzip_file(real_filepath) - to_item.move_directory_as_new_version(unpacked_dir) - to_item.add_triple(to_item.uri, "dcterms:isVersionOf", file_uri) - to_item.sync() - return True - diff --git a/rdfdatabank/lib/utils.py b/rdfdatabank/lib/utils.py index 9a0cbeb..b45d3ea 100644 --- a/rdfdatabank/lib/utils.py +++ b/rdfdatabank/lib/utils.py @@ -1,50 +1,116 @@ -from datetime import datetime, timedelta +# -*- coding: utf-8 -*- +""" +Copyright (c) 2012 University of Oxford + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, --INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +""" -from redis import Redis +from datetime import datetime, timedelta +from dateutil.relativedelta import * +from dateutil.parser import parse +from time import sleep, strftime +import os import simplejson from pylons import app_globals as ag -from rdfobject.constructs import Manifest +from rdflib import ConjunctiveGraph +from StringIO import StringIO +from rdflib import StringInputSource +from rdflib import Namespace, RDF, RDFS, URIRef, Literal, BNode from uuid import uuid4 - import re +from collections import defaultdict + +from rdfdatabank.lib.auth_entry import list_silos, list_user_groups ID_PATTERN = re.compile(r"^[0-9A-z\-\:]+$") -def authz(granary_list,ident): - g = ag.granary - g.state.revert() - g._register_silos() - granary_list = g.silos - def _parse_owners(silo_name): - kw = g.describe_silo(silo_name) - if "owners" in kw.keys(): - owners = [x.strip() for x in kw['owners'].split(",") if x] - return owners - else: - return [] - - if ident['role'] == "admin": - return granary_list - else: - authd = [] - for item in granary_list: - owners = _parse_owners(item) - if ident['repoze.who.userid'] in owners: - authd.append(item) - return authd +def authz(ident, permission=[]): + #NOTE: g._register_silos() IS AN EXPENSIVE OPERATION. LISTING SILOS FROM DATABASE INSTEAD + #g = ag.granary + #g.state.revert() + #g._register_silos() + #granary_list = g.silos + granary_list = list_silos() + if permission and not type(permission).__name__ == 'list': + permission = [permission] + if not permission: + permission = [] + silos = [] + for i in ident['user'].groups: + if i.silo == '*': + return granary_list + if i.silo in granary_list and not i.silo in silos: + if not permission: + silos.append(i.silo) + else: + for p in i.permissions: + if p.permission_name in permission: + silos.append(i.silo) + """ + user_groups = list_user_groups(ident['repoze.who.userid']) + for g,p in user_groups: + if g == '*': + f = open('/var/log/databank/authz.log', 'a') + f.write('List of all Silos: %s\n'%str(granary_list)) + f.write('List of user groups: %s\n'%str(user_groups)) + f.write('Permissions to match: %s\n'%str(permission)) + f.write('Group is *. Returning all silos\n\n') + f.close() + return granary_list + if g in granary_list and not g in silos: + if not permission: + silos.append(g) + elif p in permission: + silos.append(g) + f = open('/var/log/databank/authz.log', 'a') + f.write('List of all Silos: %s\n'%str(granary_list)) + f.write('List of user groups: %s\n'%str(user_groups)) + f.write('Permissions to match: %s\n'%str(permission)) + f.write('List of auth Silos: %s\n\n'%str(silos)) + f.close() + """ + return silos def allowable_id(identifier): if ID_PATTERN.match(identifier): return identifier +def allowable_id2(strg): + if len(strg) < 2 or ' ' in strg: + return False + search=re.compile(r'%s'%ag.naming_rule).search + return not bool(search(strg)) + def is_embargoed(silo, id, refresh=False): - # TODO evaluate r.expire settings for these keys - popularity resets ttl or increases it? - r = Redis() - e = r.get("%s:%s:embargoed" % (silo.state['storage_dir'], id)) - e_d = r.get("%s:%s:embargoed_until" % (silo.state['storage_dir'], id)) + # TODO evaluate ag.r.expire settings for these keys - popularity resets ttl or increases it? + e = None + e_d = None + try: + e = ag.r.get("%s:%s:embargoed" % (silo.state['storage_dir'], id)) + e_d = ag.r.get("%s:%s:embargoed_until" % (silo.state['storage_dir'], id)) + except: + pass + if refresh or (not e or not e_d): if silo.exists(id): item = silo.get_item(id) @@ -54,23 +120,100 @@ def is_embargoed(silo, id, refresh=False): e = True else: e = False - r.set("%s:%s:embargoed" % (silo.state['storage_dir'], id), e) - r.set("%s:%s:embargoed_until" % (silo.state['storage_dir'], id), e_d) + try: + ag.r.set("%s:%s:embargoed" % (silo.state['storage_dir'], id), e) + ag.r.set("%s:%s:embargoed_until" % (silo.state['storage_dir'], id), e_d) + except: + pass return (e, e_d) -def create_new(silo, id, creator, title=None, embargoed=True, embargoed_until=None, embargo_days_from_now=None, **kw): - item = silo.get_item(id) - item.metadata['createdby'] = creator - item.metadata['embargoed'] = embargoed - item.metadata['uuid'] = uuid4().hex - if embargoed: +def get_embargo_values(embargoed=None, embargoed_until=None, embargo_days_from_now=None): + if isinstance(embargoed, basestring): + embargoed = embargoed.strip() + if isinstance(embargoed_until, basestring): + embargoed_until = embargoed_until.strip() + if isinstance(embargo_days_from_now, basestring): + embargo_days_from_now = embargo_days_from_now.strip() + e_status=None + e_date=None + if embargoed == None: + #No embargo details are supplied by user + e_status = True + e_date = (datetime.now() + relativedelta(years=+70)).isoformat() + elif embargoed==True or embargoed.lower() in ['true', '1'] : + #embargo status is True + e_status = True + e_date = None if embargoed_until: - item.metadata['embargoed_until'] = embargoed_until + try: + e_date = parse(embargoed_until, dayfirst=True, yearfirst=False).isoformat() + except: + e_date = (datetime.now() + relativedelta(years=+70)).isoformat() elif embargo_days_from_now: - item.metadata['embargoed_until'] = (datetime.now() + timedelta(days=embargo_days_from_now)).isoformat() - else: - item.metadata['embargoed_until'] = (datetime.now() + timedelta(days=365*70)).isoformat() - item.add_triple(item.uri, u"dcterms:dateSubmitted", datetime.now()) + if embargo_days_from_now.isdigit(): + e_date = (datetime.now() + timedelta(days=int(embargo_days_from_now))).isoformat() + else: + e_date = (datetime.now() + relativedelta(years=+70)).isoformat() + elif embargoed==False or embargoed.lower() in ['false', '0'] : + e_status = False + else: + #Default case: Treat it as though embargo=None + e_status = True + e_date = (datetime.now() + relativedelta(years=+70)).isoformat() + return (e_status, e_date) + +def create_new(silo, id, creator, title=None, embargoed=None, embargoed_until=None, embargo_days_from_now=None, **kw): + item = silo.get_item(id, startversion="0") + item.metadata['createdby'] = creator + item.metadata['uuid'] = uuid4().hex + item.add_namespace('oxds', "http://vocab.ox.ac.uk/dataset/schema#") + item.add_triple(item.uri, u"rdf:type", "oxds:DataSet") + + item.metadata['embargoed_until'] = '' + item.del_triple(item.uri, u"oxds:isEmbargoed") + item.del_triple(item.uri, u"oxds:embargoedUntil") + try: + ag.r.set("%s:%s:embargoed_until" % (silo.state['storage_dir'], id), ' ') + except: + pass + e, e_d = get_embargo_values(embargoed=embargoed, embargoed_until=embargoed_until, embargo_days_from_now=embargo_days_from_now) + if e: + item.metadata['embargoed'] = True + item.add_triple(item.uri, u"oxds:isEmbargoed", 'True') + try: + ag.r.set("%s:%s:embargoed" % (silo.state['storage_dir'], id), True) + except: + pass + if e_d: + item.metadata['embargoed_until'] = e_d + item.add_triple(item.uri, u"oxds:embargoedUntil", e_d) + try: + ag.r.set("%s:%s:embargoed_until" % (silo.state['storage_dir'], id), e_d) + except: + pass + else: + item.metadata['embargoed'] = False + item.add_triple(item.uri, u"oxds:isEmbargoed", 'False') + try: + ag.r.set("%s:%s:embargoed" % (silo.state['storage_dir'], id), False) + except: + pass + + item.add_triple(item.uri, u"dcterms:identifier", id) + item.add_triple(item.uri, u"dcterms:mediator", creator) + item.add_triple(item.uri, u"dcterms:publisher", ag.publisher) + item.add_triple(item.uri, u"dcterms:created", datetime.now()) + item.add_triple(item.uri, u"oxds:currentVersion", item.currentversion) + if ag.rights and ag.rights.startswith('http'): + item.add_triple(item.uri, u"dcterms:rights", URIRef(ag.rights)) + elif ag.rights: + item.add_triple(item.uri, u"dcterms:rights", Literal(ag.rights)) + if ag.license and ag.license.startswith('http'): + item.add_triple(item.uri, u"dcterms:license", URIRef(ag.license)) + elif ag.license: + item.add_triple(item.uri, u"dcterms:license", Literal(ag.license)) + + #TODO: Add current version metadata if title: item.add_triple(item.uri, u"rdfs:label", title) item.sync() @@ -81,10 +224,261 @@ def get_readme_text(item, filename="README"): text = fn.read().decode("utf-8") return u"%s\n\n%s" % (filename, text) -def test_rdf(text): +def get_rdf_template(item_uri, item_id): + g = ConjunctiveGraph(identifier=item_uri) + g.bind('rdf', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#') + g.bind('dcterms', 'http://purl.org/dc/terms/') + g.add((URIRef(item_uri), URIRef('http://purl.org/dc/terms/identifier'), Literal(item_id))) + data2 = g.serialize(format='xml', encoding="utf-8") + '\n' + return data2 + +#def test_rdf(text): +def test_rdf(mfile): + g = ConjunctiveGraph() try: - mani = Manifest() - mani.from_string(text) + g = g.parse(mfile, format='xml') return True - except: + except Exception as inst: return False + +def munge_manifest(manifest_file, item): + #Get triples from the manifest file and remove the file + triples = None + ns = None + seeAlsoFiles = None + ns, triples, seeAlsoFiles = read_manifest(item, manifest_file) + if ns and triples: + for k, v in ns.iteritems(): + item.add_namespace(k, v) + for (s, p, o) in triples: + if str(p) == 'http://purl.org/dc/terms/title': + try: + item.del_triple(URIRef(s), u"dcterms:title") + except: + pass + if str(p) == 'http://purl.org/dc/terms/license': + try: + item.del_triple(URIRef(s), u"dcterms:license") + except: + pass + if str(p) == 'http://purl.org/dc/terms/rights': + try: + item.del_triple(URIRef(s), u"dcterms:rights") + except: + pass + for (s, p, o) in triples: + item.add_triple(s, p, o) + manifest_file_name = os.path.basename(manifest_file) + item.manifest['versionlog'][item.currentversion].append('Updated file manifest.rdf') + item.sync() + if seeAlsoFiles: + for fileuri in seeAlsoFiles: + fullfilepath = None + filepath = fileuri.replace(item.uri, '').strip().lstrip('/') + fullfilepath = item.to_dirpath(filepath=filepath) + if fullfilepath and item.isfile(fullfilepath): + ans = test_rdf(fullfilepath) + #with item.get_stream(filepath) as fn: + # text = fn.read() + #if test_rdf(text): + # munge_manifest(text, item) + if ans: + munge_manifest(fullfilepath, item) + return True + +def read_manifest(item, manifest_file): + triples = [] + namespaces = {} + seeAlsoFiles = [] + oxdsClasses = ['http://vocab.ox.ac.uk/dataset/schema#Grouping', 'http://vocab.ox.ac.uk/dataset/schema#DataSet'] + + aggregates = item.list_rdf_objects(item.uri, "ore:aggregates") + + g = ConjunctiveGraph() + gparsed = g.parse(manifest_file, format='xml') + namespaces = dict(g.namespaces()) + #Get the subjects + subjects = {} + for s in gparsed.subjects(): + if s in subjects: + continue + if type(s).__name__ == 'URIRef': + if str(s).startswith('file://'): + ss = str(s).replace('file://', '') + if manifest_file in ss: + subjects[s] = URIRef(item.uri) + else: + manifest_file_path, manifest_file_name = os.path.split(manifest_file) + ss = ss.replace(manifest_file_path, '').strip('/') + for file_uri in aggregates: + if ss in str(file_uri): + subjects[s] = URIRef(file_uri) + break + if not s in subjects: + subjects[s] = URIRef(item.uri) + else: + subjects[s] = URIRef(s) + elif type(s).__name__ == 'BNode': + replace_subject = True + for o in gparsed.objects(): + if o == s: + replace_subject = False + if replace_subject: + subjects[s] = URIRef(item.uri) + else: + subjects[s] = s + #Get the dataset type + #set the subject uri to item uri if it is of type as defined in oxdsClasses + datasetType = False + for s,p,o in gparsed.triples((None, RDF.type, None)): + if str(o) in oxdsClasses: + if type(s).__name__ == 'URIRef' and len(s) > 0 and str(s) != str(item.uri) and str(subjects[s]) != str(item.uri): + namespaces['owl'] = URIRef("http://www.w3.org/2002/07/owl#") + triples.append((item.uri, 'owl:sameAs', s)) + triples.append((item.uri, RDF.type, o)) + elif type(s).__name__ == 'BNode' or len(s) == 0 or str(s) == str(item.uri) or str(subjects[s]) == str(item.uri): + gparsed.remove((s, p, o)) + subjects[s] = item.uri + + #Get the uri for the see also files + for s,p,o in gparsed.triples((None, URIRef('http://www.w3.org/2000/01/rdf-schema#seeAlso'), None)): + if type(o).__name__ == 'URIRef' and len(o) > 0: + obj = str(o) + if obj.startswith('file://'): + obj_path, obj_name = os.path.split(obj) + obj = obj.replace(obj_path, '').strip('/') + for file_uri in aggregates: + if obj in str(file_uri): + seeAlsoFiles.append(file_uri) + gparsed.remove((s, p, o)) + + #Add remaining triples + for s,p,o in gparsed.triples((None, None, None)): + triples.append((subjects[s], p, o)) + return namespaces, triples, seeAlsoFiles + +def manifest_type(manifest_file): + mani_types = [] + g = ConjunctiveGraph() + gparsed = g.parse(manifest_file, format='xml') + for s,p,o in gparsed.triples((None, RDF.type, None)): + mani_types.append(str(o)) + if "http://vocab.ox.ac.uk/dataset/schema#DataSet" in mani_types: + return "http://vocab.ox.ac.uk/dataset/schema#DataSet" + elif "http://vocab.ox.ac.uk/dataset/schema#Grouping" in mani_types: + return "http://vocab.ox.ac.uk/dataset/schema#Grouping" + return None + +def serialisable_stat(stat): + stat_values = {} + for f in ['st_atime', 'st_blksize', 'st_blocks', 'st_ctime', 'st_dev', 'st_gid', 'st_ino', 'st_mode', 'st_mtime', 'st_nlink', 'st_rdev', 'st_size', 'st_uid']: + try: + stat_values[f] = stat.__getattribute__(f) + except AttributeError: + pass + return stat_values + +def natural_sort(l): + convert = lambda text: int(text) if text.isdigit() else text.lower() + alphanum_key = lambda key: [ convert(c) for c in re.split('([0-9]+)', key) ] + return sorted(l, key = alphanum_key) + +def extract_metadata(item): + g = item.get_graph() + m = defaultdict(list) + #for s,p,o in g.triples((URIRef(item.uri), ag.NAMESPACES[dc]['identifier'], None)): + for o in g.objects(URIRef(item.uri), ag.NAMESPACES['dc']['title']): + m['title'].append(o) + for o in g.objects(URIRef(item.uri), ag.NAMESPACES['dc']['identifier']): + m['identifier'].append(o) + for o in g.objects(URIRef(item.uri), ag.NAMESPACES['dc']['description']): + m['description'].append(o) + for o in g.objects(URIRef(item.uri), ag.NAMESPACES['dc']['creator']): + m['creator'].append(o) + for o in g.objects(URIRef(item.uri), ag.NAMESPACES['dc']['subject']): + m['subject'].append(o) + + for o in g.objects(URIRef(item.uri), ag.NAMESPACES['dcterms']['abstract']): + m['abstract'].append(o) + for o in g.objects(URIRef(item.uri), ag.NAMESPACES['dcterms']['created']): + try: + dt = formatDate(str(o)) + except: + dt = o + m['created'].append(dt) + for o in g.objects(URIRef(item.uri), ag.NAMESPACES['dcterms']['description']): + m['description'].append(o) + for o in g.objects(URIRef(item.uri), ag.NAMESPACES['dcterms']['hasVersion']): + m['hasVersion'].append(o) + for o in g.objects(URIRef(item.uri), ag.NAMESPACES['dcterms']['identifier']): + m['identifier'].append(o) + for o in g.objects(URIRef(item.uri), ag.NAMESPACES['dcterms']['isVersionOf']): + m['isVersionOf'].append(o) + for o in g.objects(URIRef(item.uri), ag.NAMESPACES['dcterms']['license']): + m['license'].append(o) + for o in g.objects(URIRef(item.uri), ag.NAMESPACES['dcterms']['mediator']): + m['mediator'].append(o) + for o in g.objects(URIRef(item.uri), ag.NAMESPACES['dcterms']['modified']): + try: + dt = formatDate(str(o)) + except: + dt = o + m['modified'].append(dt) + for o in g.objects(URIRef(item.uri), ag.NAMESPACES['dcterms']['publisher']): + m['publisher'].append(o) + for o in g.objects(URIRef(item.uri), ag.NAMESPACES['dcterms']['rights']): + m['rights'].append(o) + for o in g.objects(URIRef(item.uri), ag.NAMESPACES['dcterms']['subject']): + m['subject'].append(o) + for o in g.objects(URIRef(item.uri), ag.NAMESPACES['dcterms']['title']): + m['title'].append(o) + for o in g.objects(URIRef(item.uri), ag.NAMESPACES['oxds']['isEmbargoed']): + m['isEmbargoed'].append(o) + for o in g.objects(URIRef(item.uri), ag.NAMESPACES['oxds']['embargoedUntil']): + try: + dt = formatDate(str(o)) + except: + dt = o + m['embargoedUntil'].append(dt) + for o in g.objects(URIRef(item.uri), ag.NAMESPACES['oxds']['currentVersion']): + m['currentVersion'].append(o) + for o in g.objects(URIRef(item.uri), ag.NAMESPACES['bibo']['doi']): + m['doi'].append(o) + for o in g.objects(URIRef(item.uri), ag.NAMESPACES['ore']['aggregates']): + m['aggregates'].append(o) + return dict(m) + +def formatDate(dt): + dt_human = dt + try: + dt_obj = parse(dt, dayfirst=True, yearfirst=False) + dt_human = dt_obj.strftime("%B %d %Y, %I:%M %p") + except: + return dt + return dt_human + +def getSiloModifiedDate(silo_name): + solr_params = {} + solr_params['q'] = "silo:%s"%silo_name + solr_params['wt'] = 'json' + solr_params['start'] = 0 + solr_params['rows'] = 1 + solr_params['sort'] = "modified desc" + solr_params['fl'] = 'modified' + solr_response = None + try: + solr_response = ag.solr.raw_query(**solr_params) + except: + pass + if not solr_response: + return '' + result = simplejson.loads(solr_response) + docs = result['response'].get('docs',None) + numFound = result['response'].get('numFound',None) + if docs and len(docs) > 0 and docs[0] and 'modified' in docs[0] and len(docs[0]['modified']) > 0: + dt = docs[0]['modified'][0] + else: + return '' + dt = formatDate(dt) + return dt + diff --git a/rdfdatabank/model/__init__.py b/rdfdatabank/model/__init__.py index e69de29..ba02feb 100644 --- a/rdfdatabank/model/__init__.py +++ b/rdfdatabank/model/__init__.py @@ -0,0 +1,21 @@ +# -*- coding: utf-8 -*- + +import sqlalchemy as sa +from sqlalchemy import orm +from rdfdatabank.model import meta +from rdfdatabank.model.auth import User, Group, Permission, Datasets + +def init_model(engine): + """Call me before using any of the tables or classes in the model""" + ## Reflected tables must be defined and mapped here + #global reflected_table + #reflected_table = sa.Table("Reflected", meta.metadata, autoload=True, + # autoload_with=engine) + #orm.mapper(Reflected, reflected_table) + + # We are using SQLAlchemy 0.5 so transactional=True is replaced by + # autocommit=False + sm = orm.sessionmaker(autoflush=True, autocommit=False, bind=engine) + + meta.engine = engine + meta.Session = orm.scoped_session(sm) diff --git a/rdfdatabank/model/auth.py b/rdfdatabank/model/auth.py new file mode 100644 index 0000000..b2c21dd --- /dev/null +++ b/rdfdatabank/model/auth.py @@ -0,0 +1,176 @@ +# -*- coding: utf-8 -*- +""" +SQLAlchemy-powered model definitions for repoze.what SQL plugin. +Sets up Users, Groups and Permissions +""" + +from sqlalchemy import Table, ForeignKey, Column +from sqlalchemy.types import Unicode, Integer +from sqlalchemy.orm import relation +from rdfdatabank.model.meta import metadata, Base +import os +from hashlib import sha1 + +__all__ = ['User', 'Group', 'Permission', 'Datasets'] + +# This is the association table for the many-to-many relationship between +# groups and permissions. This is required by repoze.what. +group_permission_table = Table('group_permission', metadata, + Column('group_id', Integer, ForeignKey('silo.id', + onupdate="CASCADE", ondelete="CASCADE"), primary_key=True), + Column('permission_id', Integer, ForeignKey('permission.id', + onupdate="CASCADE", ondelete="CASCADE"), primary_key=True) +) + +# This is the association table for the many-to-many relationship between +# groups and members - this is, the memberships. It's required by repoze.what. +user_group_table = Table('user_group', metadata, + Column('user_id', Integer, ForeignKey('user.id', + onupdate="CASCADE", ondelete="CASCADE"), primary_key=True), + Column('group_id', Integer, ForeignKey('silo.id', + onupdate="CASCADE", ondelete="CASCADE"), primary_key=True) +) + +class Group(Base): + """ + Group definition for :mod:`repoze.what`. + + Only the ``group_name`` column is required by :mod:`repoze.what`. + + """ + + __tablename__ = 'silo' + + # columns + + id = Column(Integer, autoincrement=True, primary_key=True) + group_name = Column(Unicode(255), unique=True, nullable=False) + silo = Column(Unicode(255)) + + # relations + + users = relation('User', secondary=user_group_table, backref='groups') + + # special methods + + #def __repr__(self): + # return '' % self.group_name + + #def __unicode__(self): + # return self.group_name + +class Permission(Base): + """ + Permission definition for :mod:`repoze.what`. + + Only the ``permission_name`` column is required by :mod:`repoze.what`. + + """ + + __tablename__ = 'permission' + + # columns + + id = Column(Integer, autoincrement=True, primary_key=True) + permission_name = Column(Unicode(255), unique=True, nullable=False) + + # relations + + groups = relation(Group, secondary=group_permission_table, backref='permissions') + + # special methods + + #def __repr__(self): + # return '' % self.permission_name + + #def __unicode__(self): + # return self.permission_name + +class User(Base): + + """ + User definition. + + This is the user definition used by :mod:`repoze.who`, which requires at + least the ``user_name`` column. + + """ + __tablename__ = 'user' + + #column + + id = Column(Integer, autoincrement=True, primary_key=True) + user_name = Column(Unicode(255), unique=True, nullable=False) + password = Column(Unicode(80), nullable=False) + email = Column(Unicode(255)) + name = Column(Unicode(255)) + firstname = Column(Unicode(255)) + lastname = Column(Unicode(255)) + + def _set_password(self, password): + """Hash password on the fly.""" + hashed_password = password + + if isinstance(password, unicode): + password_8bit = password.encode('UTF-8') + else: + password_8bit = password + + salt = sha1() + salt.update(os.urandom(60)) + hash = sha1() + hash.update(password_8bit + salt.hexdigest()) + hashed_password = salt.hexdigest() + hash.hexdigest() + + # Make sure the hased password is an UTF-8 object at the end of the + # process because SQLAlchemy _wants_ a unicode object for Unicode + # fields + if not isinstance(hashed_password, unicode): + hashed_password = hashed_password.decode('UTF-8') + + self.password = hashed_password + + def _get_password(self): + """Return the password hashed""" + return self.password + + def validate_password(self, password): + """ + Check the password against existing credentials. + + :param password: the password that was provided by the user to + try and authenticate. This is the clear text version that we will + need to match against the hashed one in the database. + :type password: unicode object. + :return: Whether the password is valid. + :rtype: bool + + """ + hashed_pass = sha1() + hashed_pass.update(password + self.password[:40]) + return self.password[40:] == hashed_pass.hexdigest() + + def permissions(self): + """Return a set with all permissions granted to the user.""" + perms = set() + for g in self.groups: + perms = perms | set(g.permissions) + return perms + + #def __repr__(self): + # return '' % (self.name, self.email) + + #def __unicode__(self): + # return self.name + +class Datasets(Base): + """ + Table to store index of datasets + """ + + __tablename__ = 'datasets' + + # columns + silo = Column(Unicode(50), primary_key=True) + id = Column(Unicode(75), primary_key=True) + diff --git a/rdfdatabank/model/meta.py b/rdfdatabank/model/meta.py new file mode 100644 index 0000000..2f2a256 --- /dev/null +++ b/rdfdatabank/model/meta.py @@ -0,0 +1,19 @@ +# -*- coding: utf-8 -*- + +"""Creates SQLAlchemy Metadata and Session object""" +from sqlalchemy.orm import scoped_session, sessionmaker +from sqlalchemy import MetaData +from sqlalchemy.ext.declarative import declarative_base + +__all__ = ['engine', 'Session', 'metadata'] + +# SQLAlchemy database engine. Updated by model.init_model() +engine = None + +# SQLAlchemy session manager. Updated by model.init_model() +Session = scoped_session(sessionmaker()) + +#metadata = MetaData() +Base = declarative_base() +metadata = Base.metadata + diff --git a/rdfdatabank/public/api.html b/rdfdatabank/public/api.html deleted file mode 100644 index d61a349..0000000 --- a/rdfdatabank/public/api.html +++ /dev/null @@ -1,263 +0,0 @@ - - - - - - - - - -Converted document - -
- -

-/objects -

-
-GETAccept: *, authd - Returns an HTML -page listing the ’silos’ your account can see -
-
-POST,PUT,DELETENOOP -
-

-/objects/{silo name} -

-
-GETAccept:text/html - Returns an HTML -page listing the ids, along with a form for item creation -
-
-GETAccept: text/plain, application/json - - Returns a JSON-encoded hash/dict, keys map to exising item ids, and -include embargo information -
-
-POSTCreate new object -
-
    - -
  • - -
    -Parameters-> id = {id to create}, -embargoed = {true|false}, embargoed_until = {ISO8601 date}, -title={(Optional)} -
    -
    -Returns:Accept:text/html - 302 to splash - page for newly created object, 201 otherwise on success. Code 409 if -object already exists. -
    -
  • -
-
-PUT,DELETENOOP -
-

-/objects/{silo name}/{id} -

-
-GETAccept: text/html - returns HTML -splash page for item id -
-
-GETAccept: application/json - returns -item’s JSON-encoded state -
-
-GETAccept: application/rdf+xml, text/xml - - returns item’s RDF manifest as RDF/XML -
-
-GETAccept: text/rdf+n3 - returns item’s -RDF manifest as N3 -
-
-GETAccept: application/x-turtle - -returns item’s RDF manifest as Turtle -
-
-GETAccept: text/rdf+ntriples, -text/rdf+nt - returns item’s RDF manifest as ntriples -
-
-POSTIf Item id doesn’t -exist, create it. (As POST /objects/{silo} id=...) -
-
-POSTItem id does exist: -
-
    - -
  • - -
    -Embargo-controlChange embargo -information on item id -
    -
      - -
    • - -
      -Parameters-> embargo_change = true, -embargoed = {true|false}, embargoed_until = {ISO8601 date} -
      -
      -Returns:JSON-encoded statement of new -embargo information with a 200 on success. -
      -
    • -
    -
    -File-upload:Upload file to root -directory (makes HTML forms easier) -
    -
      - -
    • - -
      -Parameters-> file = Multipart-encoded - (HTML) file upload, filename = {Optional filename for upload} -
      -
      -Returns:Accept: text/html - 302 redirect - to top-level item view on success -
      -
      -Returns:Accept: * - 200/201 on -update/creation of file with filename 403 if filename == existing - subdirectory name. (For the sake of web UI) -
      -
    • -
    -
    -Text-uploadConvenience function for HTML - to update/create text files -
    -
      - -
    • - -
      -Parameters-> text = {UTF text to -store}, filename = {desired filename} -
      -
      -Returns:As File upload, but with the -following changes: if ’filename’ isn’t set, 406 error will result. If -filename == ’manifest.rdf’, and the text isn’t valid RDF/XML, again, 406 - will result. -
      -
    • -
    -
  • -
-
-DELETEDeletes the item id - 200 -on success, 404 if id doesn’t exist -
-
-PUTNOOP -
-

-/objects/{silo -name}/{id}/{subpath} -

-
-GETAccept: text/html - HTML page listing - the files within a given subpath -
-
-GETAccept: text/plain, application/json - - JSON-encoded listing of files {’parts’: { ’filename’: {python os.stat -results}, ...} with ’readme_text’ being a potential top-level key-value -pair -
-
-PUTresults in a 403 - cannot put content - onto an existing directory -
-
-POSTUpload a file within the subpath -
-
    - -
  • - -
    -Parameters-> file = Multipart-encoded - (HTML) file upload, filename = {Optional filename for upload} -
    -
    -Returns:Accept: * - 200/201 on -update/creation of file with filename 403 if filename == existing - subdirectory name. (For the sake of web UI) -
    -
  • -
-
-DELETETODO - recursive delete. Returns -400 if the directory has subdirectories but this is not intended to be a - long-term API choice. -
-

-/objects/{silo -name}/{id}[/{subpath}]/{filename} -

-
-GETGETs the file at -[/{subpath}]/{filename}, 404 otherwise -
-
-PUTPUTs the request body in the place of - the [/subpath]/filename, whether it already exists or not. -
-
    - -
  • - -
    -Returns200/201 for update/creation -
    -
  • -
-
-POSTUpload a file within the same -subpath as the file (HTML convenience) -
-
    - -
  • - -
    -Parameters-> file = Multipart-encoded - (HTML) file upload, filename = {Optional filename for upload} -
    -
    -Returns:Accept: * - 200/201 on -update/creation of file with filename 403 if filename == existing - subdirectory name. (For the sake of web UI) -
    -
  • -
-
-DELETERemoves the filename from the item - and subpath. -
-
    - -
  • - -
    -Returns:200 on success. -
    -
  • -
- -
- \ No newline at end of file diff --git a/rdfdatabank/public/api_files/lyx.css b/rdfdatabank/public/api_files/lyx.css deleted file mode 100644 index fd1dc3a..0000000 --- a/rdfdatabank/public/api_files/lyx.css +++ /dev/null @@ -1,699 +0,0 @@ -/* -* Styles for lyx document. -*/ -body { - font: 90% sans-serif; - background: #f9f9f9; - color: black; - margin: 0; - padding: 0; -} - -#globalWrapper { - margin: 10px 80px 10px 80px; - padding: 20px; - background: #ffffff; - line-height: 1.5em; - overflow: hidden; -} -a { - text-decoration: none; - color: #0030c0; - background: none; -} -a:visited { - color: #603090; -} -a:active { - color: #ffa000; -} -a:hover { - text-decoration: underline; -} -h1 { - margin-top: 1em; - line-height: 1.5em; -} -h1.Part { - text-align: center; -} -h1.Part- { - text-align: center; -} -sup { - font-size: 0.75em; - line-height: 0.5em; - vertical-align: text-top; -} -sub { - font-size: 0.75em; - line-height: 0.5em; - vertical-align: text-bottom; -} -div.Standard { - margin: 1em 0; -} -div.Unindented { - margin: 0; -} -div.Indented { - text-indent: 30pt; - margin: 0; -} -div.Indented * { - text-indent: 0pt; -} -p.dir { - float: right; -} -p.printindex { - font-size: 0.90em; -} -a.printindex { - color: black; -} -img.embedded { - width: 100%; - _width: auto; -} -div.float { - margin-top: 1ex; - margin-bottom: 1ex; - text-align: center; -} -span.float { - margin-top: 1ex; - margin-bottom: 1ex; - text-align: center; -} -div.figure { - display: inline-block; - padding: 1ex; - margin-left: auto; - margin-right: auto; - border: thin solid #c0c0c0; -} -div.table { - display: inline-block; - padding: 1ex; - margin-left: auto; - margin-right: auto; - border: thin solid #c0c0c0; -} -div.algorithm { - display: inline-block; - text-align: left; - padding: 1ex; - margin-left: auto; - margin-right: auto; - border: thin solid #c0c0c0; -} -div.caption { - text-align: center; - font-family: sans-serif; - margin-left: auto; - margin-right: auto; - padding: 0.5ex; -} -img.figure { - width: 100%; - _width: auto; -} -div.multifigure { - padding: 1ex; - width: 100%; -} -div.multitable { - display: inline-block; - padding: 1ex; - margin-left: auto; - margin-right: auto; - border: thin solid #c0c0c0; -} -div.multialgorithm { - display: inline-block; - padding: 1ex; - margin-left: auto; - margin-right: auto; - border: thin solid #c0c0c0; -} -table { - display: inline-table; - text-align: center; - border-collapse: collapse; - margin-top: 1em; - margin-bottom: 1em; -} -tr.header { - border-bottom: thin solid #c0c0c0; - background: #ffffff; - font-weight: bold; -} -td { - padding: 1ex; - border: thin solid #f0f0f0; -} -td div.Standard { - margin: 0ex; - padding: 0ex; -} -div.caption div.Standard, div.table div.Standard { - margin: 0ex; - padding: 0ex; -} -td.numeric { - text-align: right; -} -td.empty { - text-align: center; -} -.right { - text-align: right; -} -.center { - text-align: center; - margin-left: auto; - margin-right: auto; -} -p.biblio { - font-size: 0.90em; -} -div.Paragraph, div.Paragraph- { - font-weight: bold; - font-size: 103%; -} -span.versalitas { - font-variant: small-caps; -} -span.sans { - font-family: sans-serif; -} -span.mathsf { - font-family: sans-serif; - font-style: normal; -} -span.red { - color: #c00000; -} -span.blue { - color: #0000c0; -} -span.green { - color: #00c000; -} -span.magenta { - color: #c000c0; -} -span.cyan { - color: #00c0c0; -} -span.yellow { - color: #c0c000; -} -span.white { - color: #ffffff; -} -.formula { - color: #000040; - text-align: center; - font-family: serif; - margin: 1.2em 0; -} -div.formula { - padding: 0.5ex; - margin-left: auto; - margin-right: auto; -} -span.overline { - text-decoration: overline; -} -span.bar { - text-decoration: overline; -} -.fraction { - display: inline-block; - vertical-align: middle; - text-align: center; -} -.fraction .fraction { - font-size: 80%; - line-height: 100%; -} -.fullfraction { - display: inline-block; - vertical-align: middle; - text-align: center; -} -span.numerator { - display: block; -} -span.numerator-l { - display: block; - text-align: left; -} -span.numerator-r { - display: block; - text-align: right; -} -span.numeratorl { - display: block; - text-align: left; -} -span.numeratorr { - display: block; - text-align: right; -} -span.denominator { - display: block; - padding: 0; - border-top: thin solid #000040; -} -span.sqrt { - display: inline-block; - vertical-align: middle; -} -span.radical { - display: inline-block; - padding: 0; - font-size: 150%; - vertical-align: top; -} -span.root { - display: inline-block; - border-top: thin #000040 solid; - padding: 0; - vertical-align: middle; -} -span.symbol { - font-size: 125%; -} -span.bigsymbol { - font-size: 150%; -} -span.largesymbol { - font-size: 175%; -} -span.hugesymbol { - font-size: 200%; -} -span.limits { - display: inline-table; - vertical-align: middle; -} -sub.bigsymbol { - display: table-row; - text-align: left; - line-height: 150%; -} -sup.bigsymbol { - display: table-row; - text-align: left; - line-height: 150%; -} -span.symbolover { - display: inline-block; - text-align: center; - position: relative; - float: right; - right: 100%; - bottom: 0.5em; - width: 0px; -} -span.withsymbol { - display: inline-block; -} -span.symbolunder { - display: inline-block; - text-align: center; - position: relative; - float: right; - right: 80%; - top: 0.3em; - width: 0px; -} -span.dotted { - border-top: thin #000040 dotted; -} -span.FootMarker { - color: #004000; -} -span.Foot { - float: right; - clear: right; - margin: 0.2ex; - border: thin solid #c0c0c0; - background: #ffffff; - width: 30%; - padding: 0.5ex; - font: small normal; - text-align: left; -} -span.Marginal { - float: right; - clear: right; - margin: 0.2ex; - border: thin solid #c0c0c0; - background: #ffffff; - width: 30%; - padding: 0.5ex; - font: small normal; - text-align: left; -} -span.Note { - display: none; -} -span.code { - font-family: monospace; -} -div.Plain { - display: inline; - width: auto; -} -h1.title { - text-align: center; -} -h2.author { - text-align: center; -} -h2.Date { - text-align: center; -} -span.text { - font-style: normal; -} -span.textipa { - color: #008080; -} -span.Description-entry { - font-weight: bold; -} -span.List-entry { - display: inline-block; - width: 25%; - vertical-align: text-top; -} -span.List-contents { - display: inline-block; - width: 75%; - vertical-align: text-top; -} -div.Space { - display: none; -} -span.fraktur { - font-family: "Lucida Blackletter", eufm10, blackletter; -} -span.blackboard { - font-family: Blackboard, msbm10, serif; -} -span.script { - font-family: "Monotype Corsiva", "Apple Chancery", "URW Chancery L", cursive; -} -span.arraydef { - display: none; -} -span.appendix { - display: none; -} -h1.biblio { - font-size: 140%; -} -table.formula { - display: inline-block; - text-align: center; - border-collapse: collapse; - margin: 0em; - vertical-align: middle; -} -td.formula-l { - text-align: left; - padding: 0.2ex; - border: 0ex; -} -td.formula-c { - text-align: center; - padding: 0.2ex; - border: 0ex; -} -td.formula-r { - text-align: right; - padding: 0.2ex; - border: 0ex; -} -table.cases { - display: inline-block; - text-align: center; - border-collapse: collapse; - margin: 0.2em; - border-left: thin solid #000040; - vertical-align: middle; -} -table.cases tr td { - padding-left: 1ex; - padding-right: 1em; -} -table.environment { - display: inline-block; - text-align: right; - margin: 0; - vertical-align: middle; -} -table.environment tr td { - padding: 0 1em; -} -span.greyedout { - color: #808080; -} -div.Description, div.List, li { - margin: 1em 0; -} -li.nested { - list-style-type: none; -} -span.Info { - background: #f0f0f0; - border: thin solid #c0c0c0; -} -pre { - padding: 0em; - margin: 0em; - width: auto; - font-family: monospace; - line-height: 1.5em; -} -pre.LyX-Code { - margin: 0.5em 3em; -} -a.Label { - text-decoration: none; - color: #000000; -} -div.wrap-l, div.wrap-o, div.wrap-i { - margin: 2ex; - float: left; -} -div.wrap-r { - margin: 2ex; - float: right; -} -div.abstract { - margin: 3em; - text-align: center; -} -p.abstract-message { - font-weight: bold; -} -div.listing { - display: inline-block; - text-align: left; - margin-left: auto; - margin-right: auto; - padding: 0.5ex; - border: thin solid #c0c0c0; -} -div.framed { - outline-style: solid; -} -div.frameless { -} -div.Framed { - outline-style: double; -} -div.Frameless { - outline-style: outset; -} -div.Doublebox { - outline-style: double; -} -div.Shadowbox { - outline-style: outset; -} -div.Shaded { - outline-style: inset; -} -div.ovalbox { - outline-style: groove; -} -div.Ovalbox { - outline-style: ridge; -} -span.number-left { - float: left; - background: #f0f0f0; - width: 3em; - text-align: right; - margin-right: 1em; -} -span.number-right { - float: right; - background: #f0f0f0; - width: 3em; - text-align: right; - margin-left: 1em; -} -a.eqnumber { - display: inline-block; - float: right; - clear: right; - font-weight: bold; - color: #000040; -} -span.unknown { - color: #800000; -} -span.phantom { - color: #ffffff; -} -div.tocheader { - margin: 1em 0; - font-size: large; -} -a.toc { - color: black; -} -a.biblioentry { - color: black; -} -div.tocindent { - padding: 0 0 0 2em; -} -div.toc { - margin: 0.5em 0; - font-size: 0.95em; -} -div.fulltoc { - padding: 1em; -} -span.hspace { - display: inline-block; -} -span.vspace { - display: inline-block; -} -span.hfill { - display: inline-block; - margin-left: auto; - margin-right: auto; - min-width: 20mm; - background: #fff0f0; -} -span.binom { - display: inline-block; - vertical-align: middle; - text-align: center; - font-size: 80%; -} -span.fullbinom { - display: inline-block; - vertical-align: middle; - text-align: center; -} -span.upbinom { - display: block; - padding: 0em; -} -span.downbinom { - display: block; - padding: 0em; -} -div.defskip { - display: block; - height: 1em; -} -div.smallskip { - display: block; - height: 0.5em; -} -div.medskip { - display: block; - height: 1em; -} -div.bigskip { - display: block; - height: 2em; -} -div.vfill { - display: block; - height: 30em; -} -span.menuitem { - font-size: 105%; -} -div.chunk { - width: auto; -} -span.chunkleft { - font-style: normal; -} -span.chunkright { - font-style: normal; -} -span.chunkdecl { - font-style: italic; - padding: 0em 2em; -} -span.chunkref { - font-style: italic; -} -span.normal { - font-style: normal; -} -span.overbrace { - border-top: 2pt solid #000000; -} -span.underbrace { - border-bottom: 2pt solid #000000; -} -div.warning { - font-size: 120%; - color:#cc0000; -} -div.PlainVisible { - width: auto; -} -div.splitheader { - margin: 0em; - text-align: center; - background: #f9f9f9; -} -span.next { - float: right; - width: 15em; - text-align: right; -} -span.up { - display: inline-block; - text-align: center; -} -span.prev { - float: left; - width: 15em; - text-align: left; -} -hr.footer { - margin-top: 2em; -} -div.footer { - font-size: 0.90em; - margin: 1em 0; -} diff --git a/rdfdatabank/public/static/databank_logo.png b/rdfdatabank/public/static/databank_logo.png new file mode 100644 index 0000000..2fe501d Binary files /dev/null and b/rdfdatabank/public/static/databank_logo.png differ diff --git a/rdfdatabank/public/static/databank_logo_generic.png b/rdfdatabank/public/static/databank_logo_generic.png new file mode 100644 index 0000000..dbb7695 Binary files /dev/null and b/rdfdatabank/public/static/databank_logo_generic.png differ diff --git a/rdfdatabank/public/static/jquery.js b/rdfdatabank/public/static/jquery.js new file mode 100644 index 0000000..b1ae21d --- /dev/null +++ b/rdfdatabank/public/static/jquery.js @@ -0,0 +1,19 @@ +/* + * jQuery JavaScript Library v1.3.2 + * http://jquery.com/ + * + * Copyright (c) 2009 John Resig + * Dual licensed under the MIT and GPL licenses. + * http://docs.jquery.com/License + * + * Date: 2009-02-19 17:34:21 -0500 (Thu, 19 Feb 2009) + * Revision: 6246 + */ +(function(){var l=this,g,y=l.jQuery,p=l.$,o=l.jQuery=l.$=function(E,F){return new o.fn.init(E,F)},D=/^[^<]*(<(.|\s)+>)[^>]*$|^#([\w-]+)$/,f=/^.[^:#\[\.,]*$/;o.fn=o.prototype={init:function(E,H){E=E||document;if(E.nodeType){this[0]=E;this.length=1;this.context=E;return this}if(typeof E==="string"){var G=D.exec(E);if(G&&(G[1]||!H)){if(G[1]){E=o.clean([G[1]],H)}else{var I=document.getElementById(G[3]);if(I&&I.id!=G[3]){return o().find(E)}var F=o(I||[]);F.context=document;F.selector=E;return F}}else{return o(H).find(E)}}else{if(o.isFunction(E)){return o(document).ready(E)}}if(E.selector&&E.context){this.selector=E.selector;this.context=E.context}return this.setArray(o.isArray(E)?E:o.makeArray(E))},selector:"",jquery:"1.3.2",size:function(){return this.length},get:function(E){return E===g?Array.prototype.slice.call(this):this[E]},pushStack:function(F,H,E){var G=o(F);G.prevObject=this;G.context=this.context;if(H==="find"){G.selector=this.selector+(this.selector?" ":"")+E}else{if(H){G.selector=this.selector+"."+H+"("+E+")"}}return G},setArray:function(E){this.length=0;Array.prototype.push.apply(this,E);return this},each:function(F,E){return o.each(this,F,E)},index:function(E){return o.inArray(E&&E.jquery?E[0]:E,this)},attr:function(F,H,G){var E=F;if(typeof F==="string"){if(H===g){return this[0]&&o[G||"attr"](this[0],F)}else{E={};E[F]=H}}return this.each(function(I){for(F in E){o.attr(G?this.style:this,F,o.prop(this,E[F],G,I,F))}})},css:function(E,F){if((E=="width"||E=="height")&&parseFloat(F)<0){F=g}return this.attr(E,F,"curCSS")},text:function(F){if(typeof F!=="object"&&F!=null){return this.empty().append((this[0]&&this[0].ownerDocument||document).createTextNode(F))}var E="";o.each(F||this,function(){o.each(this.childNodes,function(){if(this.nodeType!=8){E+=this.nodeType!=1?this.nodeValue:o.fn.text([this])}})});return E},wrapAll:function(E){if(this[0]){var F=o(E,this[0].ownerDocument).clone();if(this[0].parentNode){F.insertBefore(this[0])}F.map(function(){var G=this;while(G.firstChild){G=G.firstChild}return G}).append(this)}return this},wrapInner:function(E){return this.each(function(){o(this).contents().wrapAll(E)})},wrap:function(E){return this.each(function(){o(this).wrapAll(E)})},append:function(){return this.domManip(arguments,true,function(E){if(this.nodeType==1){this.appendChild(E)}})},prepend:function(){return this.domManip(arguments,true,function(E){if(this.nodeType==1){this.insertBefore(E,this.firstChild)}})},before:function(){return this.domManip(arguments,false,function(E){this.parentNode.insertBefore(E,this)})},after:function(){return this.domManip(arguments,false,function(E){this.parentNode.insertBefore(E,this.nextSibling)})},end:function(){return this.prevObject||o([])},push:[].push,sort:[].sort,splice:[].splice,find:function(E){if(this.length===1){var F=this.pushStack([],"find",E);F.length=0;o.find(E,this[0],F);return F}else{return this.pushStack(o.unique(o.map(this,function(G){return o.find(E,G)})),"find",E)}},clone:function(G){var E=this.map(function(){if(!o.support.noCloneEvent&&!o.isXMLDoc(this)){var I=this.outerHTML;if(!I){var J=this.ownerDocument.createElement("div");J.appendChild(this.cloneNode(true));I=J.innerHTML}return o.clean([I.replace(/ jQuery\d+="(?:\d+|null)"/g,"").replace(/^\s*/,"")])[0]}else{return this.cloneNode(true)}});if(G===true){var H=this.find("*").andSelf(),F=0;E.find("*").andSelf().each(function(){if(this.nodeName!==H[F].nodeName){return}var I=o.data(H[F],"events");for(var K in I){for(var J in I[K]){o.event.add(this,K,I[K][J],I[K][J].data)}}F++})}return E},filter:function(E){return this.pushStack(o.isFunction(E)&&o.grep(this,function(G,F){return E.call(G,F)})||o.multiFilter(E,o.grep(this,function(F){return F.nodeType===1})),"filter",E)},closest:function(E){var G=o.expr.match.POS.test(E)?o(E):null,F=0;return this.map(function(){var H=this;while(H&&H.ownerDocument){if(G?G.index(H)>-1:o(H).is(E)){o.data(H,"closest",F);return H}H=H.parentNode;F++}})},not:function(E){if(typeof E==="string"){if(f.test(E)){return this.pushStack(o.multiFilter(E,this,true),"not",E)}else{E=o.multiFilter(E,this)}}var F=E.length&&E[E.length-1]!==g&&!E.nodeType;return this.filter(function(){return F?o.inArray(this,E)<0:this!=E})},add:function(E){return this.pushStack(o.unique(o.merge(this.get(),typeof E==="string"?o(E):o.makeArray(E))))},is:function(E){return !!E&&o.multiFilter(E,this).length>0},hasClass:function(E){return !!E&&this.is("."+E)},val:function(K){if(K===g){var E=this[0];if(E){if(o.nodeName(E,"option")){return(E.attributes.value||{}).specified?E.value:E.text}if(o.nodeName(E,"select")){var I=E.selectedIndex,L=[],M=E.options,H=E.type=="select-one";if(I<0){return null}for(var F=H?I:0,J=H?I+1:M.length;F=0||o.inArray(this.name,K)>=0)}else{if(o.nodeName(this,"select")){var N=o.makeArray(K);o("option",this).each(function(){this.selected=(o.inArray(this.value,N)>=0||o.inArray(this.text,N)>=0)});if(!N.length){this.selectedIndex=-1}}else{this.value=K}}})},html:function(E){return E===g?(this[0]?this[0].innerHTML.replace(/ jQuery\d+="(?:\d+|null)"/g,""):null):this.empty().append(E)},replaceWith:function(E){return this.after(E).remove()},eq:function(E){return this.slice(E,+E+1)},slice:function(){return this.pushStack(Array.prototype.slice.apply(this,arguments),"slice",Array.prototype.slice.call(arguments).join(","))},map:function(E){return this.pushStack(o.map(this,function(G,F){return E.call(G,F,G)}))},andSelf:function(){return this.add(this.prevObject)},domManip:function(J,M,L){if(this[0]){var I=(this[0].ownerDocument||this[0]).createDocumentFragment(),F=o.clean(J,(this[0].ownerDocument||this[0]),I),H=I.firstChild;if(H){for(var G=0,E=this.length;G1||G>0?I.cloneNode(true):I)}}if(F){o.each(F,z)}}return this;function K(N,O){return M&&o.nodeName(N,"table")&&o.nodeName(O,"tr")?(N.getElementsByTagName("tbody")[0]||N.appendChild(N.ownerDocument.createElement("tbody"))):N}}};o.fn.init.prototype=o.fn;function z(E,F){if(F.src){o.ajax({url:F.src,async:false,dataType:"script"})}else{o.globalEval(F.text||F.textContent||F.innerHTML||"")}if(F.parentNode){F.parentNode.removeChild(F)}}function e(){return +new Date}o.extend=o.fn.extend=function(){var J=arguments[0]||{},H=1,I=arguments.length,E=false,G;if(typeof J==="boolean"){E=J;J=arguments[1]||{};H=2}if(typeof J!=="object"&&!o.isFunction(J)){J={}}if(I==H){J=this;--H}for(;H-1}},swap:function(H,G,I){var E={};for(var F in G){E[F]=H.style[F];H.style[F]=G[F]}I.call(H);for(var F in G){H.style[F]=E[F]}},css:function(H,F,J,E){if(F=="width"||F=="height"){var L,G={position:"absolute",visibility:"hidden",display:"block"},K=F=="width"?["Left","Right"]:["Top","Bottom"];function I(){L=F=="width"?H.offsetWidth:H.offsetHeight;if(E==="border"){return}o.each(K,function(){if(!E){L-=parseFloat(o.curCSS(H,"padding"+this,true))||0}if(E==="margin"){L+=parseFloat(o.curCSS(H,"margin"+this,true))||0}else{L-=parseFloat(o.curCSS(H,"border"+this+"Width",true))||0}})}if(H.offsetWidth!==0){I()}else{o.swap(H,G,I)}return Math.max(0,Math.round(L))}return o.curCSS(H,F,J)},curCSS:function(I,F,G){var L,E=I.style;if(F=="opacity"&&!o.support.opacity){L=o.attr(E,"opacity");return L==""?"1":L}if(F.match(/float/i)){F=w}if(!G&&E&&E[F]){L=E[F]}else{if(q.getComputedStyle){if(F.match(/float/i)){F="float"}F=F.replace(/([A-Z])/g,"-$1").toLowerCase();var M=q.getComputedStyle(I,null);if(M){L=M.getPropertyValue(F)}if(F=="opacity"&&L==""){L="1"}}else{if(I.currentStyle){var J=F.replace(/\-(\w)/g,function(N,O){return O.toUpperCase()});L=I.currentStyle[F]||I.currentStyle[J];if(!/^\d+(px)?$/i.test(L)&&/^\d/.test(L)){var H=E.left,K=I.runtimeStyle.left;I.runtimeStyle.left=I.currentStyle.left;E.left=L||0;L=E.pixelLeft+"px";E.left=H;I.runtimeStyle.left=K}}}}return L},clean:function(F,K,I){K=K||document;if(typeof K.createElement==="undefined"){K=K.ownerDocument||K[0]&&K[0].ownerDocument||document}if(!I&&F.length===1&&typeof F[0]==="string"){var H=/^<(\w+)\s*\/?>$/.exec(F[0]);if(H){return[K.createElement(H[1])]}}var G=[],E=[],L=K.createElement("div");o.each(F,function(P,S){if(typeof S==="number"){S+=""}if(!S){return}if(typeof S==="string"){S=S.replace(/(<(\w+)[^>]*?)\/>/g,function(U,V,T){return T.match(/^(abbr|br|col|img|input|link|meta|param|hr|area|embed)$/i)?U:V+">"});var O=S.replace(/^\s+/,"").substring(0,10).toLowerCase();var Q=!O.indexOf("",""]||!O.indexOf("",""]||O.match(/^<(thead|tbody|tfoot|colg|cap)/)&&[1,"","
"]||!O.indexOf("",""]||(!O.indexOf("",""]||!O.indexOf("",""]||!o.support.htmlSerialize&&[1,"div
","
"]||[0,"",""];L.innerHTML=Q[1]+S+Q[2];while(Q[0]--){L=L.lastChild}if(!o.support.tbody){var R=/"&&!R?L.childNodes:[];for(var M=N.length-1;M>=0;--M){if(o.nodeName(N[M],"tbody")&&!N[M].childNodes.length){N[M].parentNode.removeChild(N[M])}}}if(!o.support.leadingWhitespace&&/^\s/.test(S)){L.insertBefore(K.createTextNode(S.match(/^\s*/)[0]),L.firstChild)}S=o.makeArray(L.childNodes)}if(S.nodeType){G.push(S)}else{G=o.merge(G,S)}});if(I){for(var J=0;G[J];J++){if(o.nodeName(G[J],"script")&&(!G[J].type||G[J].type.toLowerCase()==="text/javascript")){E.push(G[J].parentNode?G[J].parentNode.removeChild(G[J]):G[J])}else{if(G[J].nodeType===1){G.splice.apply(G,[J+1,0].concat(o.makeArray(G[J].getElementsByTagName("script"))))}I.appendChild(G[J])}}return E}return G},attr:function(J,G,K){if(!J||J.nodeType==3||J.nodeType==8){return g}var H=!o.isXMLDoc(J),L=K!==g;G=H&&o.props[G]||G;if(J.tagName){var F=/href|src|style/.test(G);if(G=="selected"&&J.parentNode){J.parentNode.selectedIndex}if(G in J&&H&&!F){if(L){if(G=="type"&&o.nodeName(J,"input")&&J.parentNode){throw"type property can't be changed"}J[G]=K}if(o.nodeName(J,"form")&&J.getAttributeNode(G)){return J.getAttributeNode(G).nodeValue}if(G=="tabIndex"){var I=J.getAttributeNode("tabIndex");return I&&I.specified?I.value:J.nodeName.match(/(button|input|object|select|textarea)/i)?0:J.nodeName.match(/^(a|area)$/i)&&J.href?0:g}return J[G]}if(!o.support.style&&H&&G=="style"){return o.attr(J.style,"cssText",K)}if(L){J.setAttribute(G,""+K)}var E=!o.support.hrefNormalized&&H&&F?J.getAttribute(G,2):J.getAttribute(G);return E===null?g:E}if(!o.support.opacity&&G=="opacity"){if(L){J.zoom=1;J.filter=(J.filter||"").replace(/alpha\([^)]*\)/,"")+(parseInt(K)+""=="NaN"?"":"alpha(opacity="+K*100+")")}return J.filter&&J.filter.indexOf("opacity=")>=0?(parseFloat(J.filter.match(/opacity=([^)]*)/)[1])/100)+"":""}G=G.replace(/-([a-z])/ig,function(M,N){return N.toUpperCase()});if(L){J[G]=K}return J[G]},trim:function(E){return(E||"").replace(/^\s+|\s+$/g,"")},makeArray:function(G){var E=[];if(G!=null){var F=G.length;if(F==null||typeof G==="string"||o.isFunction(G)||G.setInterval){E[0]=G}else{while(F){E[--F]=G[F]}}}return E},inArray:function(G,H){for(var E=0,F=H.length;E0?this.clone(true):this).get();o.fn[F].apply(o(L[K]),I);J=J.concat(I)}return this.pushStack(J,E,G)}});o.each({removeAttr:function(E){o.attr(this,E,"");if(this.nodeType==1){this.removeAttribute(E)}},addClass:function(E){o.className.add(this,E)},removeClass:function(E){o.className.remove(this,E)},toggleClass:function(F,E){if(typeof E!=="boolean"){E=!o.className.has(this,F)}o.className[E?"add":"remove"](this,F)},remove:function(E){if(!E||o.filter(E,[this]).length){o("*",this).add([this]).each(function(){o.event.remove(this);o.removeData(this)});if(this.parentNode){this.parentNode.removeChild(this)}}},empty:function(){o(this).children().remove();while(this.firstChild){this.removeChild(this.firstChild)}}},function(E,F){o.fn[E]=function(){return this.each(F,arguments)}});function j(E,F){return E[0]&&parseInt(o.curCSS(E[0],F,true),10)||0}var h="jQuery"+e(),v=0,A={};o.extend({cache:{},data:function(F,E,G){F=F==l?A:F;var H=F[h];if(!H){H=F[h]=++v}if(E&&!o.cache[H]){o.cache[H]={}}if(G!==g){o.cache[H][E]=G}return E?o.cache[H][E]:H},removeData:function(F,E){F=F==l?A:F;var H=F[h];if(E){if(o.cache[H]){delete o.cache[H][E];E="";for(E in o.cache[H]){break}if(!E){o.removeData(F)}}}else{try{delete F[h]}catch(G){if(F.removeAttribute){F.removeAttribute(h)}}delete o.cache[H]}},queue:function(F,E,H){if(F){E=(E||"fx")+"queue";var G=o.data(F,E);if(!G||o.isArray(H)){G=o.data(F,E,o.makeArray(H))}else{if(H){G.push(H)}}}return G},dequeue:function(H,G){var E=o.queue(H,G),F=E.shift();if(!G||G==="fx"){F=E[0]}if(F!==g){F.call(H)}}});o.fn.extend({data:function(E,G){var H=E.split(".");H[1]=H[1]?"."+H[1]:"";if(G===g){var F=this.triggerHandler("getData"+H[1]+"!",[H[0]]);if(F===g&&this.length){F=o.data(this[0],E)}return F===g&&H[1]?this.data(H[0]):F}else{return this.trigger("setData"+H[1]+"!",[H[0],G]).each(function(){o.data(this,E,G)})}},removeData:function(E){return this.each(function(){o.removeData(this,E)})},queue:function(E,F){if(typeof E!=="string"){F=E;E="fx"}if(F===g){return o.queue(this[0],E)}return this.each(function(){var G=o.queue(this,E,F);if(E=="fx"&&G.length==1){G[0].call(this)}})},dequeue:function(E){return this.each(function(){o.dequeue(this,E)})}}); +/* + * Sizzle CSS Selector Engine - v0.9.3 + * Copyright 2009, The Dojo Foundation + * Released under the MIT, BSD, and GPL Licenses. + * More information: http://sizzlejs.com/ + */ +(function(){var R=/((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^[\]]*\]|['"][^'"]*['"]|[^[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?/g,L=0,H=Object.prototype.toString;var F=function(Y,U,ab,ac){ab=ab||[];U=U||document;if(U.nodeType!==1&&U.nodeType!==9){return[]}if(!Y||typeof Y!=="string"){return ab}var Z=[],W,af,ai,T,ad,V,X=true;R.lastIndex=0;while((W=R.exec(Y))!==null){Z.push(W[1]);if(W[2]){V=RegExp.rightContext;break}}if(Z.length>1&&M.exec(Y)){if(Z.length===2&&I.relative[Z[0]]){af=J(Z[0]+Z[1],U)}else{af=I.relative[Z[0]]?[U]:F(Z.shift(),U);while(Z.length){Y=Z.shift();if(I.relative[Y]){Y+=Z.shift()}af=J(Y,af)}}}else{var ae=ac?{expr:Z.pop(),set:E(ac)}:F.find(Z.pop(),Z.length===1&&U.parentNode?U.parentNode:U,Q(U));af=F.filter(ae.expr,ae.set);if(Z.length>0){ai=E(af)}else{X=false}while(Z.length){var ah=Z.pop(),ag=ah;if(!I.relative[ah]){ah=""}else{ag=Z.pop()}if(ag==null){ag=U}I.relative[ah](ai,ag,Q(U))}}if(!ai){ai=af}if(!ai){throw"Syntax error, unrecognized expression: "+(ah||Y)}if(H.call(ai)==="[object Array]"){if(!X){ab.push.apply(ab,ai)}else{if(U.nodeType===1){for(var aa=0;ai[aa]!=null;aa++){if(ai[aa]&&(ai[aa]===true||ai[aa].nodeType===1&&K(U,ai[aa]))){ab.push(af[aa])}}}else{for(var aa=0;ai[aa]!=null;aa++){if(ai[aa]&&ai[aa].nodeType===1){ab.push(af[aa])}}}}}else{E(ai,ab)}if(V){F(V,U,ab,ac);if(G){hasDuplicate=false;ab.sort(G);if(hasDuplicate){for(var aa=1;aa":function(Z,U,aa){var X=typeof U==="string";if(X&&!/\W/.test(U)){U=aa?U:U.toUpperCase();for(var V=0,T=Z.length;V=0)){if(!V){T.push(Y)}}else{if(V){U[X]=false}}}}return false},ID:function(T){return T[1].replace(/\\/g,"")},TAG:function(U,T){for(var V=0;T[V]===false;V++){}return T[V]&&Q(T[V])?U[1]:U[1].toUpperCase()},CHILD:function(T){if(T[1]=="nth"){var U=/(-?)(\d*)n((?:\+|-)?\d*)/.exec(T[2]=="even"&&"2n"||T[2]=="odd"&&"2n+1"||!/\D/.test(T[2])&&"0n+"+T[2]||T[2]);T[2]=(U[1]+(U[2]||1))-0;T[3]=U[3]-0}T[0]=L++;return T},ATTR:function(X,U,V,T,Y,Z){var W=X[1].replace(/\\/g,"");if(!Z&&I.attrMap[W]){X[1]=I.attrMap[W]}if(X[2]==="~="){X[4]=" "+X[4]+" "}return X},PSEUDO:function(X,U,V,T,Y){if(X[1]==="not"){if(X[3].match(R).length>1||/^\w/.test(X[3])){X[3]=F(X[3],null,null,U)}else{var W=F.filter(X[3],U,V,true^Y);if(!V){T.push.apply(T,W)}return false}}else{if(I.match.POS.test(X[0])||I.match.CHILD.test(X[0])){return true}}return X},POS:function(T){T.unshift(true);return T}},filters:{enabled:function(T){return T.disabled===false&&T.type!=="hidden"},disabled:function(T){return T.disabled===true},checked:function(T){return T.checked===true},selected:function(T){T.parentNode.selectedIndex;return T.selected===true},parent:function(T){return !!T.firstChild},empty:function(T){return !T.firstChild},has:function(V,U,T){return !!F(T[3],V).length},header:function(T){return/h\d/i.test(T.nodeName)},text:function(T){return"text"===T.type},radio:function(T){return"radio"===T.type},checkbox:function(T){return"checkbox"===T.type},file:function(T){return"file"===T.type},password:function(T){return"password"===T.type},submit:function(T){return"submit"===T.type},image:function(T){return"image"===T.type},reset:function(T){return"reset"===T.type},button:function(T){return"button"===T.type||T.nodeName.toUpperCase()==="BUTTON"},input:function(T){return/input|select|textarea|button/i.test(T.nodeName)}},setFilters:{first:function(U,T){return T===0},last:function(V,U,T,W){return U===W.length-1},even:function(U,T){return T%2===0},odd:function(U,T){return T%2===1},lt:function(V,U,T){return UT[3]-0},nth:function(V,U,T){return T[3]-0==U},eq:function(V,U,T){return T[3]-0==U}},filter:{PSEUDO:function(Z,V,W,aa){var U=V[1],X=I.filters[U];if(X){return X(Z,W,V,aa)}else{if(U==="contains"){return(Z.textContent||Z.innerText||"").indexOf(V[3])>=0}else{if(U==="not"){var Y=V[3];for(var W=0,T=Y.length;W=0)}}},ID:function(U,T){return U.nodeType===1&&U.getAttribute("id")===T},TAG:function(U,T){return(T==="*"&&U.nodeType===1)||U.nodeName===T},CLASS:function(U,T){return(" "+(U.className||U.getAttribute("class"))+" ").indexOf(T)>-1},ATTR:function(Y,W){var V=W[1],T=I.attrHandle[V]?I.attrHandle[V](Y):Y[V]!=null?Y[V]:Y.getAttribute(V),Z=T+"",X=W[2],U=W[4];return T==null?X==="!=":X==="="?Z===U:X==="*="?Z.indexOf(U)>=0:X==="~="?(" "+Z+" ").indexOf(U)>=0:!U?Z&&T!==false:X==="!="?Z!=U:X==="^="?Z.indexOf(U)===0:X==="$="?Z.substr(Z.length-U.length)===U:X==="|="?Z===U||Z.substr(0,U.length+1)===U+"-":false},POS:function(X,U,V,Y){var T=U[2],W=I.setFilters[T];if(W){return W(X,V,U,Y)}}}};var M=I.match.POS;for(var O in I.match){I.match[O]=RegExp(I.match[O].source+/(?![^\[]*\])(?![^\(]*\))/.source)}var E=function(U,T){U=Array.prototype.slice.call(U);if(T){T.push.apply(T,U);return T}return U};try{Array.prototype.slice.call(document.documentElement.childNodes)}catch(N){E=function(X,W){var U=W||[];if(H.call(X)==="[object Array]"){Array.prototype.push.apply(U,X)}else{if(typeof X.length==="number"){for(var V=0,T=X.length;V";var T=document.documentElement;T.insertBefore(U,T.firstChild);if(!!document.getElementById(V)){I.find.ID=function(X,Y,Z){if(typeof Y.getElementById!=="undefined"&&!Z){var W=Y.getElementById(X[1]);return W?W.id===X[1]||typeof W.getAttributeNode!=="undefined"&&W.getAttributeNode("id").nodeValue===X[1]?[W]:g:[]}};I.filter.ID=function(Y,W){var X=typeof Y.getAttributeNode!=="undefined"&&Y.getAttributeNode("id");return Y.nodeType===1&&X&&X.nodeValue===W}}T.removeChild(U)})();(function(){var T=document.createElement("div");T.appendChild(document.createComment(""));if(T.getElementsByTagName("*").length>0){I.find.TAG=function(U,Y){var X=Y.getElementsByTagName(U[1]);if(U[1]==="*"){var W=[];for(var V=0;X[V];V++){if(X[V].nodeType===1){W.push(X[V])}}X=W}return X}}T.innerHTML="";if(T.firstChild&&typeof T.firstChild.getAttribute!=="undefined"&&T.firstChild.getAttribute("href")!=="#"){I.attrHandle.href=function(U){return U.getAttribute("href",2)}}})();if(document.querySelectorAll){(function(){var T=F,U=document.createElement("div");U.innerHTML="

";if(U.querySelectorAll&&U.querySelectorAll(".TEST").length===0){return}F=function(Y,X,V,W){X=X||document;if(!W&&X.nodeType===9&&!Q(X)){try{return E(X.querySelectorAll(Y),V)}catch(Z){}}return T(Y,X,V,W)};F.find=T.find;F.filter=T.filter;F.selectors=T.selectors;F.matches=T.matches})()}if(document.getElementsByClassName&&document.documentElement.getElementsByClassName){(function(){var T=document.createElement("div");T.innerHTML="
";if(T.getElementsByClassName("e").length===0){return}T.lastChild.className="e";if(T.getElementsByClassName("e").length===1){return}I.order.splice(1,0,"CLASS");I.find.CLASS=function(U,V,W){if(typeof V.getElementsByClassName!=="undefined"&&!W){return V.getElementsByClassName(U[1])}}})()}function P(U,Z,Y,ad,aa,ac){var ab=U=="previousSibling"&&!ac;for(var W=0,V=ad.length;W0){X=T;break}}}T=T[U]}ad[W]=X}}}var K=document.compareDocumentPosition?function(U,T){return U.compareDocumentPosition(T)&16}:function(U,T){return U!==T&&(U.contains?U.contains(T):true)};var Q=function(T){return T.nodeType===9&&T.documentElement.nodeName!=="HTML"||!!T.ownerDocument&&Q(T.ownerDocument)};var J=function(T,aa){var W=[],X="",Y,V=aa.nodeType?[aa]:aa;while((Y=I.match.PSEUDO.exec(T))){X+=Y[0];T=T.replace(I.match.PSEUDO,"")}T=I.relative[T]?T+"*":T;for(var Z=0,U=V.length;Z0||T.offsetHeight>0};F.selectors.filters.animated=function(T){return o.grep(o.timers,function(U){return T===U.elem}).length};o.multiFilter=function(V,T,U){if(U){V=":not("+V+")"}return F.matches(V,T)};o.dir=function(V,U){var T=[],W=V[U];while(W&&W!=document){if(W.nodeType==1){T.push(W)}W=W[U]}return T};o.nth=function(X,T,V,W){T=T||1;var U=0;for(;X;X=X[V]){if(X.nodeType==1&&++U==T){break}}return X};o.sibling=function(V,U){var T=[];for(;V;V=V.nextSibling){if(V.nodeType==1&&V!=U){T.push(V)}}return T};return;l.Sizzle=F})();o.event={add:function(I,F,H,K){if(I.nodeType==3||I.nodeType==8){return}if(I.setInterval&&I!=l){I=l}if(!H.guid){H.guid=this.guid++}if(K!==g){var G=H;H=this.proxy(G);H.data=K}var E=o.data(I,"events")||o.data(I,"events",{}),J=o.data(I,"handle")||o.data(I,"handle",function(){return typeof o!=="undefined"&&!o.event.triggered?o.event.handle.apply(arguments.callee.elem,arguments):g});J.elem=I;o.each(F.split(/\s+/),function(M,N){var O=N.split(".");N=O.shift();H.type=O.slice().sort().join(".");var L=E[N];if(o.event.specialAll[N]){o.event.specialAll[N].setup.call(I,K,O)}if(!L){L=E[N]={};if(!o.event.special[N]||o.event.special[N].setup.call(I,K,O)===false){if(I.addEventListener){I.addEventListener(N,J,false)}else{if(I.attachEvent){I.attachEvent("on"+N,J)}}}}L[H.guid]=H;o.event.global[N]=true});I=null},guid:1,global:{},remove:function(K,H,J){if(K.nodeType==3||K.nodeType==8){return}var G=o.data(K,"events"),F,E;if(G){if(H===g||(typeof H==="string"&&H.charAt(0)==".")){for(var I in G){this.remove(K,I+(H||""))}}else{if(H.type){J=H.handler;H=H.type}o.each(H.split(/\s+/),function(M,O){var Q=O.split(".");O=Q.shift();var N=RegExp("(^|\\.)"+Q.slice().sort().join(".*\\.")+"(\\.|$)");if(G[O]){if(J){delete G[O][J.guid]}else{for(var P in G[O]){if(N.test(G[O][P].type)){delete G[O][P]}}}if(o.event.specialAll[O]){o.event.specialAll[O].teardown.call(K,Q)}for(F in G[O]){break}if(!F){if(!o.event.special[O]||o.event.special[O].teardown.call(K,Q)===false){if(K.removeEventListener){K.removeEventListener(O,o.data(K,"handle"),false)}else{if(K.detachEvent){K.detachEvent("on"+O,o.data(K,"handle"))}}}F=null;delete G[O]}}})}for(F in G){break}if(!F){var L=o.data(K,"handle");if(L){L.elem=null}o.removeData(K,"events");o.removeData(K,"handle")}}},trigger:function(I,K,H,E){var G=I.type||I;if(!E){I=typeof I==="object"?I[h]?I:o.extend(o.Event(G),I):o.Event(G);if(G.indexOf("!")>=0){I.type=G=G.slice(0,-1);I.exclusive=true}if(!H){I.stopPropagation();if(this.global[G]){o.each(o.cache,function(){if(this.events&&this.events[G]){o.event.trigger(I,K,this.handle.elem)}})}}if(!H||H.nodeType==3||H.nodeType==8){return g}I.result=g;I.target=H;K=o.makeArray(K);K.unshift(I)}I.currentTarget=H;var J=o.data(H,"handle");if(J){J.apply(H,K)}if((!H[G]||(o.nodeName(H,"a")&&G=="click"))&&H["on"+G]&&H["on"+G].apply(H,K)===false){I.result=false}if(!E&&H[G]&&!I.isDefaultPrevented()&&!(o.nodeName(H,"a")&&G=="click")){this.triggered=true;try{H[G]()}catch(L){}}this.triggered=false;if(!I.isPropagationStopped()){var F=H.parentNode||H.ownerDocument;if(F){o.event.trigger(I,K,F,true)}}},handle:function(K){var J,E;K=arguments[0]=o.event.fix(K||l.event);K.currentTarget=this;var L=K.type.split(".");K.type=L.shift();J=!L.length&&!K.exclusive;var I=RegExp("(^|\\.)"+L.slice().sort().join(".*\\.")+"(\\.|$)");E=(o.data(this,"events")||{})[K.type];for(var G in E){var H=E[G];if(J||I.test(H.type)){K.handler=H;K.data=H.data;var F=H.apply(this,arguments);if(F!==g){K.result=F;if(F===false){K.preventDefault();K.stopPropagation()}}if(K.isImmediatePropagationStopped()){break}}}},props:"altKey attrChange attrName bubbles button cancelable charCode clientX clientY ctrlKey currentTarget data detail eventPhase fromElement handler keyCode metaKey newValue originalTarget pageX pageY prevValue relatedNode relatedTarget screenX screenY shiftKey srcElement target toElement view wheelDelta which".split(" "),fix:function(H){if(H[h]){return H}var F=H;H=o.Event(F);for(var G=this.props.length,J;G;){J=this.props[--G];H[J]=F[J]}if(!H.target){H.target=H.srcElement||document}if(H.target.nodeType==3){H.target=H.target.parentNode}if(!H.relatedTarget&&H.fromElement){H.relatedTarget=H.fromElement==H.target?H.toElement:H.fromElement}if(H.pageX==null&&H.clientX!=null){var I=document.documentElement,E=document.body;H.pageX=H.clientX+(I&&I.scrollLeft||E&&E.scrollLeft||0)-(I.clientLeft||0);H.pageY=H.clientY+(I&&I.scrollTop||E&&E.scrollTop||0)-(I.clientTop||0)}if(!H.which&&((H.charCode||H.charCode===0)?H.charCode:H.keyCode)){H.which=H.charCode||H.keyCode}if(!H.metaKey&&H.ctrlKey){H.metaKey=H.ctrlKey}if(!H.which&&H.button){H.which=(H.button&1?1:(H.button&2?3:(H.button&4?2:0)))}return H},proxy:function(F,E){E=E||function(){return F.apply(this,arguments)};E.guid=F.guid=F.guid||E.guid||this.guid++;return E},special:{ready:{setup:B,teardown:function(){}}},specialAll:{live:{setup:function(E,F){o.event.add(this,F[0],c)},teardown:function(G){if(G.length){var E=0,F=RegExp("(^|\\.)"+G[0]+"(\\.|$)");o.each((o.data(this,"events").live||{}),function(){if(F.test(this.type)){E++}});if(E<1){o.event.remove(this,G[0],c)}}}}}};o.Event=function(E){if(!this.preventDefault){return new o.Event(E)}if(E&&E.type){this.originalEvent=E;this.type=E.type}else{this.type=E}this.timeStamp=e();this[h]=true};function k(){return false}function u(){return true}o.Event.prototype={preventDefault:function(){this.isDefaultPrevented=u;var E=this.originalEvent;if(!E){return}if(E.preventDefault){E.preventDefault()}E.returnValue=false},stopPropagation:function(){this.isPropagationStopped=u;var E=this.originalEvent;if(!E){return}if(E.stopPropagation){E.stopPropagation()}E.cancelBubble=true},stopImmediatePropagation:function(){this.isImmediatePropagationStopped=u;this.stopPropagation()},isDefaultPrevented:k,isPropagationStopped:k,isImmediatePropagationStopped:k};var a=function(F){var E=F.relatedTarget;while(E&&E!=this){try{E=E.parentNode}catch(G){E=this}}if(E!=this){F.type=F.data;o.event.handle.apply(this,arguments)}};o.each({mouseover:"mouseenter",mouseout:"mouseleave"},function(F,E){o.event.special[E]={setup:function(){o.event.add(this,F,a,E)},teardown:function(){o.event.remove(this,F,a)}}});o.fn.extend({bind:function(F,G,E){return F=="unload"?this.one(F,G,E):this.each(function(){o.event.add(this,F,E||G,E&&G)})},one:function(G,H,F){var E=o.event.proxy(F||H,function(I){o(this).unbind(I,E);return(F||H).apply(this,arguments)});return this.each(function(){o.event.add(this,G,E,F&&H)})},unbind:function(F,E){return this.each(function(){o.event.remove(this,F,E)})},trigger:function(E,F){return this.each(function(){o.event.trigger(E,F,this)})},triggerHandler:function(E,G){if(this[0]){var F=o.Event(E);F.preventDefault();F.stopPropagation();o.event.trigger(F,G,this[0]);return F.result}},toggle:function(G){var E=arguments,F=1;while(F=0){var E=G.slice(I,G.length);G=G.slice(0,I)}var H="GET";if(J){if(o.isFunction(J)){K=J;J=null}else{if(typeof J==="object"){J=o.param(J);H="POST"}}}var F=this;o.ajax({url:G,type:H,dataType:"html",data:J,complete:function(M,L){if(L=="success"||L=="notmodified"){F.html(E?o("
").append(M.responseText.replace(//g,"")).find(E):M.responseText)}if(K){F.each(K,[M.responseText,L,M])}}});return this},serialize:function(){return o.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?o.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||/select|textarea/i.test(this.nodeName)||/text|hidden|password|search/i.test(this.type))}).map(function(E,F){var G=o(this).val();return G==null?null:o.isArray(G)?o.map(G,function(I,H){return{name:F.name,value:I}}):{name:F.name,value:G}}).get()}});o.each("ajaxStart,ajaxStop,ajaxComplete,ajaxError,ajaxSuccess,ajaxSend".split(","),function(E,F){o.fn[F]=function(G){return this.bind(F,G)}});var r=e();o.extend({get:function(E,G,H,F){if(o.isFunction(G)){H=G;G=null}return o.ajax({type:"GET",url:E,data:G,success:H,dataType:F})},getScript:function(E,F){return o.get(E,null,F,"script")},getJSON:function(E,F,G){return o.get(E,F,G,"json")},post:function(E,G,H,F){if(o.isFunction(G)){H=G;G={}}return o.ajax({type:"POST",url:E,data:G,success:H,dataType:F})},ajaxSetup:function(E){o.extend(o.ajaxSettings,E)},ajaxSettings:{url:location.href,global:true,type:"GET",contentType:"application/x-www-form-urlencoded",processData:true,async:true,xhr:function(){return l.ActiveXObject?new ActiveXObject("Microsoft.XMLHTTP"):new XMLHttpRequest()},accepts:{xml:"application/xml, text/xml",html:"text/html",script:"text/javascript, application/javascript",json:"application/json, text/javascript",text:"text/plain",_default:"*/*"}},lastModified:{},ajax:function(M){M=o.extend(true,M,o.extend(true,{},o.ajaxSettings,M));var W,F=/=\?(&|$)/g,R,V,G=M.type.toUpperCase();if(M.data&&M.processData&&typeof M.data!=="string"){M.data=o.param(M.data)}if(M.dataType=="jsonp"){if(G=="GET"){if(!M.url.match(F)){M.url+=(M.url.match(/\?/)?"&":"?")+(M.jsonp||"callback")+"=?"}}else{if(!M.data||!M.data.match(F)){M.data=(M.data?M.data+"&":"")+(M.jsonp||"callback")+"=?"}}M.dataType="json"}if(M.dataType=="json"&&(M.data&&M.data.match(F)||M.url.match(F))){W="jsonp"+r++;if(M.data){M.data=(M.data+"").replace(F,"="+W+"$1")}M.url=M.url.replace(F,"="+W+"$1");M.dataType="script";l[W]=function(X){V=X;I();L();l[W]=g;try{delete l[W]}catch(Y){}if(H){H.removeChild(T)}}}if(M.dataType=="script"&&M.cache==null){M.cache=false}if(M.cache===false&&G=="GET"){var E=e();var U=M.url.replace(/(\?|&)_=.*?(&|$)/,"$1_="+E+"$2");M.url=U+((U==M.url)?(M.url.match(/\?/)?"&":"?")+"_="+E:"")}if(M.data&&G=="GET"){M.url+=(M.url.match(/\?/)?"&":"?")+M.data;M.data=null}if(M.global&&!o.active++){o.event.trigger("ajaxStart")}var Q=/^(\w+:)?\/\/([^\/?#]+)/.exec(M.url);if(M.dataType=="script"&&G=="GET"&&Q&&(Q[1]&&Q[1]!=location.protocol||Q[2]!=location.host)){var H=document.getElementsByTagName("head")[0];var T=document.createElement("script");T.src=M.url;if(M.scriptCharset){T.charset=M.scriptCharset}if(!W){var O=false;T.onload=T.onreadystatechange=function(){if(!O&&(!this.readyState||this.readyState=="loaded"||this.readyState=="complete")){O=true;I();L();T.onload=T.onreadystatechange=null;H.removeChild(T)}}}H.appendChild(T);return g}var K=false;var J=M.xhr();if(M.username){J.open(G,M.url,M.async,M.username,M.password)}else{J.open(G,M.url,M.async)}try{if(M.data){J.setRequestHeader("Content-Type",M.contentType)}if(M.ifModified){J.setRequestHeader("If-Modified-Since",o.lastModified[M.url]||"Thu, 01 Jan 1970 00:00:00 GMT")}J.setRequestHeader("X-Requested-With","XMLHttpRequest");J.setRequestHeader("Accept",M.dataType&&M.accepts[M.dataType]?M.accepts[M.dataType]+", */*":M.accepts._default)}catch(S){}if(M.beforeSend&&M.beforeSend(J,M)===false){if(M.global&&!--o.active){o.event.trigger("ajaxStop")}J.abort();return false}if(M.global){o.event.trigger("ajaxSend",[J,M])}var N=function(X){if(J.readyState==0){if(P){clearInterval(P);P=null;if(M.global&&!--o.active){o.event.trigger("ajaxStop")}}}else{if(!K&&J&&(J.readyState==4||X=="timeout")){K=true;if(P){clearInterval(P);P=null}R=X=="timeout"?"timeout":!o.httpSuccess(J)?"error":M.ifModified&&o.httpNotModified(J,M.url)?"notmodified":"success";if(R=="success"){try{V=o.httpData(J,M.dataType,M)}catch(Z){R="parsererror"}}if(R=="success"){var Y;try{Y=J.getResponseHeader("Last-Modified")}catch(Z){}if(M.ifModified&&Y){o.lastModified[M.url]=Y}if(!W){I()}}else{o.handleError(M,J,R)}L();if(X){J.abort()}if(M.async){J=null}}}};if(M.async){var P=setInterval(N,13);if(M.timeout>0){setTimeout(function(){if(J&&!K){N("timeout")}},M.timeout)}}try{J.send(M.data)}catch(S){o.handleError(M,J,null,S)}if(!M.async){N()}function I(){if(M.success){M.success(V,R)}if(M.global){o.event.trigger("ajaxSuccess",[J,M])}}function L(){if(M.complete){M.complete(J,R)}if(M.global){o.event.trigger("ajaxComplete",[J,M])}if(M.global&&!--o.active){o.event.trigger("ajaxStop")}}return J},handleError:function(F,H,E,G){if(F.error){F.error(H,E,G)}if(F.global){o.event.trigger("ajaxError",[H,F,G])}},active:0,httpSuccess:function(F){try{return !F.status&&location.protocol=="file:"||(F.status>=200&&F.status<300)||F.status==304||F.status==1223}catch(E){}return false},httpNotModified:function(G,E){try{var H=G.getResponseHeader("Last-Modified");return G.status==304||H==o.lastModified[E]}catch(F){}return false},httpData:function(J,H,G){var F=J.getResponseHeader("content-type"),E=H=="xml"||!H&&F&&F.indexOf("xml")>=0,I=E?J.responseXML:J.responseText;if(E&&I.documentElement.tagName=="parsererror"){throw"parsererror"}if(G&&G.dataFilter){I=G.dataFilter(I,H)}if(typeof I==="string"){if(H=="script"){o.globalEval(I)}if(H=="json"){I=l["eval"]("("+I+")")}}return I},param:function(E){var G=[];function H(I,J){G[G.length]=encodeURIComponent(I)+"="+encodeURIComponent(J)}if(o.isArray(E)||E.jquery){o.each(E,function(){H(this.name,this.value)})}else{for(var F in E){if(o.isArray(E[F])){o.each(E[F],function(){H(F,this)})}else{H(F,o.isFunction(E[F])?E[F]():E[F])}}}return G.join("&").replace(/%20/g,"+")}});var m={},n,d=[["height","marginTop","marginBottom","paddingTop","paddingBottom"],["width","marginLeft","marginRight","paddingLeft","paddingRight"],["opacity"]];function t(F,E){var G={};o.each(d.concat.apply([],d.slice(0,E)),function(){G[this]=F});return G}o.fn.extend({show:function(J,L){if(J){return this.animate(t("show",3),J,L)}else{for(var H=0,F=this.length;H").appendTo("body");K=I.css("display");if(K==="none"){K="block"}I.remove();m[G]=K}o.data(this[H],"olddisplay",K)}}for(var H=0,F=this.length;H=0;H--){if(G[H].elem==this){if(E){G[H](true)}G.splice(H,1)}}});if(!E){this.dequeue()}return this}});o.each({slideDown:t("show",1),slideUp:t("hide",1),slideToggle:t("toggle",1),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"}},function(E,F){o.fn[E]=function(G,H){return this.animate(F,G,H)}});o.extend({speed:function(G,H,F){var E=typeof G==="object"?G:{complete:F||!F&&H||o.isFunction(G)&&G,duration:G,easing:F&&H||H&&!o.isFunction(H)&&H};E.duration=o.fx.off?0:typeof E.duration==="number"?E.duration:o.fx.speeds[E.duration]||o.fx.speeds._default;E.old=E.complete;E.complete=function(){if(E.queue!==false){o(this).dequeue()}if(o.isFunction(E.old)){E.old.call(this)}};return E},easing:{linear:function(G,H,E,F){return E+F*G},swing:function(G,H,E,F){return((-Math.cos(G*Math.PI)/2)+0.5)*F+E}},timers:[],fx:function(F,E,G){this.options=E;this.elem=F;this.prop=G;if(!E.orig){E.orig={}}}});o.fx.prototype={update:function(){if(this.options.step){this.options.step.call(this.elem,this.now,this)}(o.fx.step[this.prop]||o.fx.step._default)(this);if((this.prop=="height"||this.prop=="width")&&this.elem.style){this.elem.style.display="block"}},cur:function(F){if(this.elem[this.prop]!=null&&(!this.elem.style||this.elem.style[this.prop]==null)){return this.elem[this.prop]}var E=parseFloat(o.css(this.elem,this.prop,F));return E&&E>-10000?E:parseFloat(o.curCSS(this.elem,this.prop))||0},custom:function(I,H,G){this.startTime=e();this.start=I;this.end=H;this.unit=G||this.unit||"px";this.now=this.start;this.pos=this.state=0;var E=this;function F(J){return E.step(J)}F.elem=this.elem;if(F()&&o.timers.push(F)&&!n){n=setInterval(function(){var K=o.timers;for(var J=0;J=this.options.duration+this.startTime){this.now=this.end;this.pos=this.state=1;this.update();this.options.curAnim[this.prop]=true;var E=true;for(var F in this.options.curAnim){if(this.options.curAnim[F]!==true){E=false}}if(E){if(this.options.display!=null){this.elem.style.overflow=this.options.overflow;this.elem.style.display=this.options.display;if(o.css(this.elem,"display")=="none"){this.elem.style.display="block"}}if(this.options.hide){o(this.elem).hide()}if(this.options.hide||this.options.show){for(var I in this.options.curAnim){o.attr(this.elem.style,I,this.options.orig[I])}}this.options.complete.call(this.elem)}return false}else{var J=G-this.startTime;this.state=J/this.options.duration;this.pos=o.easing[this.options.easing||(o.easing.swing?"swing":"linear")](this.state,J,0,1,this.options.duration);this.now=this.start+((this.end-this.start)*this.pos);this.update()}return true}};o.extend(o.fx,{speeds:{slow:600,fast:200,_default:400},step:{opacity:function(E){o.attr(E.elem.style,"opacity",E.now)},_default:function(E){if(E.elem.style&&E.elem.style[E.prop]!=null){E.elem.style[E.prop]=E.now+E.unit}else{E.elem[E.prop]=E.now}}}});if(document.documentElement.getBoundingClientRect){o.fn.offset=function(){if(!this[0]){return{top:0,left:0}}if(this[0]===this[0].ownerDocument.body){return o.offset.bodyOffset(this[0])}var G=this[0].getBoundingClientRect(),J=this[0].ownerDocument,F=J.body,E=J.documentElement,L=E.clientTop||F.clientTop||0,K=E.clientLeft||F.clientLeft||0,I=G.top+(self.pageYOffset||o.boxModel&&E.scrollTop||F.scrollTop)-L,H=G.left+(self.pageXOffset||o.boxModel&&E.scrollLeft||F.scrollLeft)-K;return{top:I,left:H}}}else{o.fn.offset=function(){if(!this[0]){return{top:0,left:0}}if(this[0]===this[0].ownerDocument.body){return o.offset.bodyOffset(this[0])}o.offset.initialized||o.offset.initialize();var J=this[0],G=J.offsetParent,F=J,O=J.ownerDocument,M,H=O.documentElement,K=O.body,L=O.defaultView,E=L.getComputedStyle(J,null),N=J.offsetTop,I=J.offsetLeft;while((J=J.parentNode)&&J!==K&&J!==H){M=L.getComputedStyle(J,null);N-=J.scrollTop,I-=J.scrollLeft;if(J===G){N+=J.offsetTop,I+=J.offsetLeft;if(o.offset.doesNotAddBorder&&!(o.offset.doesAddBorderForTableAndCells&&/^t(able|d|h)$/i.test(J.tagName))){N+=parseInt(M.borderTopWidth,10)||0,I+=parseInt(M.borderLeftWidth,10)||0}F=G,G=J.offsetParent}if(o.offset.subtractsBorderForOverflowNotVisible&&M.overflow!=="visible"){N+=parseInt(M.borderTopWidth,10)||0,I+=parseInt(M.borderLeftWidth,10)||0}E=M}if(E.position==="relative"||E.position==="static"){N+=K.offsetTop,I+=K.offsetLeft}if(E.position==="fixed"){N+=Math.max(H.scrollTop,K.scrollTop),I+=Math.max(H.scrollLeft,K.scrollLeft)}return{top:N,left:I}}}o.offset={initialize:function(){if(this.initialized){return}var L=document.body,F=document.createElement("div"),H,G,N,I,M,E,J=L.style.marginTop,K='
';M={position:"absolute",top:0,left:0,margin:0,border:0,width:"1px",height:"1px",visibility:"hidden"};for(E in M){F.style[E]=M[E]}F.innerHTML=K;L.insertBefore(F,L.firstChild);H=F.firstChild,G=H.firstChild,I=H.nextSibling.firstChild.firstChild;this.doesNotAddBorder=(G.offsetTop!==5);this.doesAddBorderForTableAndCells=(I.offsetTop===5);H.style.overflow="hidden",H.style.position="relative";this.subtractsBorderForOverflowNotVisible=(G.offsetTop===-5);L.style.marginTop="1px";this.doesNotIncludeMarginInBodyOffset=(L.offsetTop===0);L.style.marginTop=J;L.removeChild(F);this.initialized=true},bodyOffset:function(E){o.offset.initialized||o.offset.initialize();var G=E.offsetTop,F=E.offsetLeft;if(o.offset.doesNotIncludeMarginInBodyOffset){G+=parseInt(o.curCSS(E,"marginTop",true),10)||0,F+=parseInt(o.curCSS(E,"marginLeft",true),10)||0}return{top:G,left:F}}};o.fn.extend({position:function(){var I=0,H=0,F;if(this[0]){var G=this.offsetParent(),J=this.offset(),E=/^body|html$/i.test(G[0].tagName)?{top:0,left:0}:G.offset();J.top-=j(this,"marginTop");J.left-=j(this,"marginLeft");E.top+=j(G,"borderTopWidth");E.left+=j(G,"borderLeftWidth");F={top:J.top-E.top,left:J.left-E.left}}return F},offsetParent:function(){var E=this[0].offsetParent||document.body;while(E&&(!/^body|html$/i.test(E.tagName)&&o.css(E,"position")=="static")){E=E.offsetParent}return o(E)}});o.each(["Left","Top"],function(F,E){var G="scroll"+E;o.fn[G]=function(H){if(!this[0]){return null}return H!==g?this.each(function(){this==l||this==document?l.scrollTo(!F?H:o(l).scrollLeft(),F?H:o(l).scrollTop()):this[G]=H}):this[0]==l||this[0]==document?self[F?"pageYOffset":"pageXOffset"]||o.boxModel&&document.documentElement[G]||document.body[G]:this[0][G]}});o.each(["Height","Width"],function(I,G){var E=I?"Left":"Top",H=I?"Right":"Bottom",F=G.toLowerCase();o.fn["inner"+G]=function(){return this[0]?o.css(this[0],F,false,"padding"):null};o.fn["outer"+G]=function(K){return this[0]?o.css(this[0],F,false,K?"margin":"border"):null};var J=G.toLowerCase();o.fn[J]=function(K){return this[0]==l?document.compatMode=="CSS1Compat"&&document.documentElement["client"+G]||document.body["client"+G]:this[0]==document?Math.max(document.documentElement["client"+G],document.body["scroll"+G],document.documentElement["scroll"+G],document.body["offset"+G],document.documentElement["offset"+G]):K===g?(this.length?o.css(this[0],J):null):this.css(J,typeof K==="string"?K:K+"px")}})})(); \ No newline at end of file diff --git a/rdfdatabank/public/static/js/html5.js b/rdfdatabank/public/static/js/html5.js new file mode 100644 index 0000000..6692336 --- /dev/null +++ b/rdfdatabank/public/static/js/html5.js @@ -0,0 +1,4 @@ +// iepp v2.1pre @jon_neal & @aFarkas github.com/aFarkas/iepp +// html5shiv @rem remysharp.com/html5-enabling-script +// Dual licensed under the MIT or GPL Version 2 licenses +/*@cc_on(function(a,b){function r(a){var b=-1;while(++b + + + blockbooks + True + 0 + 2011-02-10T17:47:50.808243 + admin + 2081-01-23T17:47:50.807854 + + +", + "parts": { + "3=2011-02-10T17+47+50,802336": { + "st_ctime": 1304607671.3867805, + "st_rdev": 0, + "st_mtime": 1297360070.794066, + "st_blocks": 8, + "st_nlink": 1, + "st_gid": 33, + "st_blksize": 4096, + "st_dev": 2049, + "st_size": 26, + "st_atime": 1301575708.2040699, + "st_uid": 1000, + "st_ino": 659627, + "st_mode": 33277 + }, + "manifest.rdf": { + "st_ctime": 1304607671.3867805, + "st_rdev": 0, + "st_mtime": 1297360070.8040543, + "st_blocks": 8, + "st_nlink": 1, + "st_gid": 33, + "st_blksize": 4096, + "st_dev": 2049, + "st_size": 799, + "st_atime": 1308219429.3470435, + "st_uid": 1000, + "st_ino": 659629, + "st_mode": 33277 + }, + "4=blockbooks": { + "st_ctime": 1304607671.3867805, + "st_rdev": 0, + "st_mtime": 1297360070.8040543, + "st_blocks": 8, + "st_nlink": 1, + "st_gid": 33, + "st_blksize": 4096, + "st_dev": 2049, + "st_size": 10, + "st_atime": 1301575708.1240709, + "st_uid": 1000, + "st_ino": 659628, + "st_mode": 33277 + } + } +} \ No newline at end of file diff --git a/rdfdatabank/public/static/json_data/datasetInformation-version1.txt b/rdfdatabank/public/static/json_data/datasetInformation-version1.txt new file mode 100644 index 0000000..ca9dc2b --- /dev/null +++ b/rdfdatabank/public/static/json_data/datasetInformation-version1.txt @@ -0,0 +1,86 @@ +Information obtained for version 1 of dataset 'blockbooks' in silo 'sandbox' +(at /sandbox/datasets/blockbooks/version1) in text/plain or application/json + +data_returned = { + "readme_text": null, + "embargos": {"blockbooks": ["True", "2081-01-23T14:25:28.548643"]}, + "editor": true, + "view": "", + "manifest_pretty": " + + + 1 + True + 2011-02-10T17:47:51.954809 + 2011-02-10T17:47:51.985054 + admin + 2081-01-23T17:47:51.954384 + blockbooks + + + + + + + + + + + .... + + + +", + "parts": { + "3=2011-02-10T17+47+51,950519": { + "st_ctime": 1304607671.3867805, + "st_rdev": 0, + "st_mtime": 1297360071.9440658, + "st_blocks": 8, + "st_nlink": 1, + "st_gid": 33, + "st_blksize": 4096, + "st_dev": 2049, + "st_size": 26, + "st_atime": 1301575707.6940622, + "st_uid": 1000, + "st_ino": 141255, + "st_mode": 33277 + }, + "blockbooks_src": {}, + "manifest.rdf": { + "st_ctime": 1304607671.3867805, + "st_rdev": 0, + "st_mtime": 1297360072.1640515, + "st_blocks": 192, + "st_nlink": 1, + "st_gid": 33, + "st_blksize": 4096, + "st_dev": 2049, + "st_size": 95535, + "st_atime": 1308219419.8216116, + "st_uid": 1000, + "st_ino": 141257, + "st_mode": 33277}, + "4=blockbooks": { + "st_ctime": 1304607671.3867805, + "st_rdev": 0, + "st_mtime": 1297360071.9440658, + "st_blocks": 8, + "st_nlink": 1, + "st_gid": 33, + "st_blksize": 4096, + "st_dev": 2049, + "st_size": 10, + "st_atime": 1301575707.5941813, + "st_uid": 1000, + "st_ino": 141256, + "st_mode": 33277 + } + } +} \ No newline at end of file diff --git a/rdfdatabank/public/static/json_data/datasetInformation.txt b/rdfdatabank/public/static/json_data/datasetInformation.txt new file mode 100644 index 0000000..41c7322 --- /dev/null +++ b/rdfdatabank/public/static/json_data/datasetInformation.txt @@ -0,0 +1,114 @@ +Information obtained for dataset 'blockbooks' in silo 'sandbox' (at /sandbox/datasets/blockbooks) in text/plain or application/json + +data_returned = { + "readme_text": null, + "embargos": {"blockbooks": ["True", "2081-01-23T14:25:28.548643"]}, + "editor": true, + "zipfiles": {}, + "view": "editor", + "manifest": " + + + + + True + blockbooks + 2081-01-23T17:47:51.954384 + 2 + 2011-04-03T19:23:05.004127 + admin + 2011-02-10T17:47:51.954809 + + + + + + + + + .... + + +", + "manifest_pretty": " + + + + True + blockbooks + 2081-01-23T17:47:51.954384 + 2 + 2011-04-03T19:23:05.004127 + admin + 2011-02-10T17:47:51.954809 + + + + + + + + + .... + + +", + "parts": { + "blockbooks_src": {}, + "4=blockbooks": { + "st_ctime": 1304607671.3867805, + "st_rdev": 0, + "st_mtime": 1301854983.7875431, + "st_blocks": 8, + "st_nlink": 1, + "st_gid": 33, + "st_blksize": 4096, + "st_dev": 2049, + "st_size": 10, + "st_atime": 1301854983.7875431, + "st_uid": 1000, + "st_ino": 799025, + "st_mode": 33277 + }, + "3=2011-04-03T19+23+03,782105": { + "st_ctime": 1304607671.3867805, + "st_rdev": 0, + "st_mtime": 1301854983.7741106, + "st_blocks": 8, + "st_nlink": 1, + "st_gid": 33, + "st_blksize": 4096, + "st_dev": 2049, + "st_size": 26, + "st_atime": 1301854983.7741106, + "st_uid": 1000, + "st_ino": 799024, + "st_mode": 33277 + }, + "manifest.rdf": { + "st_ctime": 1304607671.3867805, + "st_rdev": 0, + "st_mtime": 1301854985.0441027, + "st_blocks": 192, + "st_nlink": 1, + "st_gid": 33, + "st_blksize": 4096, + "st_dev": 2049, + "st_size": 95657, + "st_atime": 1308152532.4668002, + "st_uid": 1000, + "st_ino": 799027, + "st_mode": 33277 + } + } +} \ No newline at end of file diff --git a/rdfdatabank/public/static/json_data/datasetSubdirInformation-version1.txt b/rdfdatabank/public/static/json_data/datasetSubdirInformation-version1.txt new file mode 100644 index 0000000..5bcc444 --- /dev/null +++ b/rdfdatabank/public/static/json_data/datasetSubdirInformation-version1.txt @@ -0,0 +1,196 @@ +Information obtained for version 1 of the sub-directory 'blockbooks_src2' in dataset 'blockbooks2' in silo 'sandbox' +(at http://databank.ora.ox.ac.uk/sandbox/datasets/blockbooks2/blockbooks_src2/version1) in text/plain or application/json + +data_returned = { + "readme_text": null, + "parts": { + "templates": {}, + "deleteSolrEntries.py": { + "st_ctime": 1304607671.3867805, + "st_rdev": 0, + "st_mtime": 1271430644.0, + "st_blocks": 8, + "st_nlink": 1, + "st_gid": 33, + "st_blksize": 4096, + "st_dev": 2049, + "st_size": 199, + "st_atime": 1301575535.9440448, + "st_uid": 1000, + "st_ino": 659027, + "st_mode": 33277 + }, + "main.py": { + "st_ctime": 1304607671.3867805, + "st_rdev": 0, + "st_mtime": 1274346250.0, + "st_blocks": 16, + "st_nlink": 1, + "st_gid": 33, + "st_blksize": 4096, + "st_dev": 2049, + "st_size": 5972, + "st_atime": 1301575536.2440608, + "st_uid": 1000, + "st_ino": 659048, + "st_mode": 33277 + }, + "lib": {}, + "blockbooks.py": { + "st_ctime": 1304607671.3867805, + "st_rdev": 0, + "st_mtime": 1288193466.0, + "st_blocks": 112, + "st_nlink": 1, + "st_gid": 33, + "st_blksize": 4096, + "st_dev": 2049, + "st_size": 56230, + "st_atime": 1301575535.9944112, + "st_uid": 1000, + "st_ino": 656963, + "st_mode": 33277 + }, + "landingPage.py": { + "st_ctime": 1304607671.3867805, + "st_rdev": 0, + "st_mtime": 1283938772.0, + "st_blocks": 40, + "st_nlink": 1, + "st_gid": 33, + "st_blksize": 4096, + "st_dev": 2049, + "st_size": 18892, + "st_atime": 1301575536.1140733, + "st_uid": 1000, + "st_ino": 659035, + "st_mode": 33277 + }, + "orientalCollection.py": { + "st_ctime": 1304607671.3867805, + "st_rdev": 0, + "st_mtime": 1283870624.0, + "st_blocks": 16, + "st_nlink": 1, + "st_gid": 33, + "st_blksize": 4096, + "st_dev": 2049, + "st_size": 8109, + "st_atime": 1301575536.2840626, + "st_uid": 1000, + "st_ino": 659051, + "st_mode": 33277 + }, + "scripts": {}, + "entityFiles": {}, + "__init__.py": { + "st_ctime": 1304607671.3867805, + "st_rdev": 0, + "st_mtime": 1264173670.0, + "st_blocks": 0, + "st_nlink": 1, + "st_gid": 33, + "st_blksize": 4096, + "st_dev": 2049, + "st_size": 0, + "st_atime": 1264173670.0, + "st_uid": 1000, + "st_ino": 659622, + "st_mode": 33277 + }, + "logFiles": {}, + "static": {}, + "updateEntente.py": { + "st_ctime": 1304607671.3867805, + "st_rdev": 0, + "st_mtime": 1271248754.0, + "st_blocks": 8, + "st_nlink": 1, + "st_gid": 33, + "st_blksize": 4096, + "st_dev": 2049, + "st_size": 1007, + "st_atime": 1301575536.3640611, + "st_uid": 1000, + "st_ino": 659621, + "st_mode": 33277 + }, + "earlyPrintingCollection.py": { + "st_ctime": 1304607671.3867805, + "st_rdev": 0, + "st_mtime": 1283865770.0, + "st_blocks": 56, + "st_nlink": 1, + "st_gid": 33, + "st_blksize": 4096, + "st_dev": 2049, + "st_size": 24885, + "st_atime": 1301575536.5714226, + "st_uid": 1000, + "st_ino": 659028, + "st_mode": 33277 + }, + "blockbooksCollection.py": { + "st_ctime": 1304607671.3867805, + "st_rdev": 0, + "st_mtime": 1283960952.0, + "st_blocks": 72, + "st_nlink": 1, + "st_gid": 33, + "st_blksize": 4096, + "st_dev": 2049, + "st_size": 34171, + "st_atime": 1301575536.4673808, + "st_uid": 1000, + "st_ino": 659008, + "st_mode": 33277 + }, + "main2.py": { + "st_ctime": 1304607671.3867805, + "st_rdev": 0, + "st_mtime": 1285688296.0, + "st_blocks": 8, + "st_nlink": 1, + "st_gid": 33, + "st_blksize": 4096, + "st_dev": 2049, + "st_size": 712, + "st_atime": 1301575536.164046, + "st_uid": 1000, + "st_ino": 659049, + "st_mode": 33277 + }, + "medievalCollection.py": { + "st_ctime": 1304607671.3867805, + "st_rdev": 0, + "st_mtime": 1283870624.0, + "st_blocks": 16, + "st_nlink": 1, + "st_gid": 33, + "st_blksize": 4096, + "st_dev": 2049, + "st_size": 6993, + "st_atime": 1301575535.8940632, + "st_uid": 1000, + "st_ino": 659050, + "st_mode": 33277 + }, + "config": {}, + "sourceData": {}, + "greekCollection.py": { + "st_ctime": 1304607671.3867805, + "st_rdev": 0, + "st_mtime": 1283870624.0, + "st_blocks": 16, + "st_nlink": 1, + "st_gid": 33, + "st_blksize": 4096, + "st_dev": 2049, + "st_size": 6972, + "st_atime": 1301575536.4177606, + "st_uid": 1000, + "st_ino": 659034, + "st_mode": 33277 + } + } +} diff --git a/rdfdatabank/public/static/json_data/datasetSubdirInformation-version3.txt b/rdfdatabank/public/static/json_data/datasetSubdirInformation-version3.txt new file mode 100644 index 0000000..e4e95bc --- /dev/null +++ b/rdfdatabank/public/static/json_data/datasetSubdirInformation-version3.txt @@ -0,0 +1,180 @@ +Information obtained for version 3 of the sub-directory 'blockbooks_src2' in dataset 'blockbooks2' in silo 'sandbox' +(at http://databank.ora.ox.ac.uk/sandbox/datasets/blockbooks2/blockbooks_src2/version3) in text/plain or application/json + +data_returned = { + "readme_text": null, + "parts": { + "templates": {}, + "deleteSolrEntries.py": { + "st_ctime": 1304607671.3867805, + "st_rdev": 0, + "st_mtime": 1271430644.0, + "st_blocks": 8, + "st_nlink": 1, + "st_gid": 33, + "st_blksize": 4096, + "st_dev": 2049, + "st_size": 199, + "st_atime": 1301575535.9440448, + "st_uid": 1000, + "st_ino": 659027, + "st_mode": 33277 + }, + "main.py": { + "st_ctime": 1304607671.3867805, + "st_rdev": 0, + "st_mtime": 1274346250.0, + "st_blocks": 16, + "st_nlink": 1, + "st_gid": 33, + "st_blksize": 4096, + "st_dev": 2049, + "st_size": 5972, + "st_atime": 1301575536.2440608, + "st_uid": 1000, + "st_ino": 659048, + "st_mode": 33277 + }, + "lib": {}, + "blockbooks.py": { + "st_ctime": 1304607671.3867805, + "st_rdev": 0, + "st_mtime": 1288193466.0, + "st_blocks": 112, + "st_nlink": 1, + "st_gid": 33, + "st_blksize": 4096, + "st_dev": 2049, + "st_size": 56230, + "st_atime": 1301575535.9944112, + "st_uid": 1000, + "st_ino": 656963, + "st_mode": 33277 + }, + "landingPage.py": { + "st_ctime": 1304607671.3867805, + "st_rdev": 0, + "st_mtime": 1283938772.0, + "st_blocks": 40, + "st_nlink": 1, + "st_gid": 33, + "st_blksize": 4096, + "st_dev": 2049, + "st_size": 18892, + "st_atime": 1301575536.1140733, + "st_uid": 1000, + "st_ino": 659035, + "st_mode": 33277 + }, + "orientalCollection.py": { + "st_ctime": 1304607671.3867805, + "st_rdev": 0, + "st_mtime": 1283870624.0, + "st_blocks": 16, + "st_nlink": 1, + "st_gid": 33, + "st_blksize": 4096, + "st_dev": 2049, + "st_size": 8109, + "st_atime": 1301575536.2840626, + "st_uid": 1000, + "st_ino": 659051, + "st_mode": 33277 + }, + "scripts": {}, + "entityFiles": {}, + "__init__.py": { + "st_ctime": 1304607671.3867805, + "st_rdev": 0, + "st_mtime": 1264173670.0, + "st_blocks": 0, + "st_nlink": 1, + "st_gid": 33, + "st_blksize": 4096, + "st_dev": 2049, + "st_size": 0, + "st_atime": 1264173670.0, + "st_uid": 1000, + "st_ino": 659622, + "st_mode": 33277 + }, + "medievalCollection.py": { + "st_ctime": 1304607671.3867805, + "st_rdev": 0, + "st_mtime": 1283870624.0, + "st_blocks": 16, + "st_nlink": 1, + "st_gid": 33, + "st_blksize": 4096, + "st_dev": 2049, + "st_size": 6993, + "st_atime": 1301575535.8940632, + "st_uid": 1000, + "st_ino": 659050, + "st_mode": 33277 + }, + "static": {}, + "greekCollection.py": { + "st_ctime": 1304607671.3867805, + "st_rdev": 0, + "st_mtime": 1283870624.0, + "st_blocks": 16, + "st_nlink": 1, + "st_gid": 33, + "st_blksize": 4096, + "st_dev": 2049, + "st_size": 6972, + "st_atime": 1301575536.4177606, + "st_uid": 1000, + "st_ino": 659034, + "st_mode": 33277 + }, + "earlyPrintingCollection.py": { + "st_ctime": 1304607671.3867805, + "st_rdev": 0, + "st_mtime": 1283865770.0, + "st_blocks": 56, + "st_nlink": 1, + "st_gid": 33, + "st_blksize": 4096, + "st_dev": 2049, + "st_size": 24885, + "st_atime": 1301575536.5714226, + "st_uid": 1000, + "st_ino": 659028, + "st_mode": 33277 + }, + "blockbooksCollection.py": { + "st_ctime": 1304607671.3867805, + "st_rdev": 0, + "st_mtime": 1283960952.0, + "st_blocks": 72, + "st_nlink": 1, + "st_gid": 33, + "st_blksize": 4096, + "st_dev": 2049, + "st_size": 34171, + "st_atime": 1301575536.4673808, + "st_uid": 1000, + "st_ino": 659008, + "st_mode": 33277 + }, + "main2.py": { + "st_ctime": 1304607671.3867805, + "st_rdev": 0, + "st_mtime": 1285688296.0, + "st_blocks": 8, + "st_nlink": 1, + "st_gid": 33, + "st_blksize": 4096, + "st_dev": 2049, + "st_size": 712, + "st_atime": 1301575536.164046, + "st_uid": 1000, + "st_ino": 659049, + "st_mode": 33277 + }, + "config": {}, + "sourceData": {} + } +} diff --git a/rdfdatabank/public/static/json_data/datasetSubdirInformation.txt b/rdfdatabank/public/static/json_data/datasetSubdirInformation.txt new file mode 100644 index 0000000..9139305 --- /dev/null +++ b/rdfdatabank/public/static/json_data/datasetSubdirInformation.txt @@ -0,0 +1,195 @@ +Information obtained for the sub-directory 'blockbooks_src' in dataset 'blockbooks' in silo 'sandbox' +(at /sandbox/datasets/blockbooks/blockbooks_src) in text/plain or application/json + +data_returned = { + "readme_text": null + "parts": { + "templates": {}, + "deleteSolrEntries.py": {"st_ctime": 1304607671.3867805, + "st_rdev": 0, + "st_mtime": 1271430644.0, + "st_blocks": 8, + "st_nlink": 1, + "st_gid": 33, + "st_blksize": 4096, + "st_dev": 2049, + "st_size": 199, + "st_atime": 1301575706.0840549, + "st_uid": 1000, + "st_ino": 141218, + "st_mode": 33277 + }, + "main.py": { + "st_ctime": 1304607671.3867805, + "st_rdev": 0, + "st_mtime": 1274346250.0, + "st_blocks": 16, + "st_nlink": 1, + "st_gid": 33, + "st_blksize": 4096, + "st_dev": 2049, + "st_size": 5972, + "st_atime": 1301575706.6640556, + "st_uid": 1000, + "st_ino": 141222, + "st_mode": 33277 + }, + "lib": {}, + "blockbooks.py": { + "st_ctime": 1304607671.3867805, + "st_rdev": 0, + "st_mtime": 1288193466.0, + "st_blocks": 112, + "st_nlink": 1, + "st_gid": 33, + "st_blksize": 4096, + "st_dev": 2049, + "st_size": 56230, + "st_atime": 1301575706.1740596, + "st_uid": 1000, + "st_ino": 139420, + "st_mode": 33277 + }, + "landingPage.py": { + "st_ctime": 1304607671.3867805, + "st_rdev": 0, + "st_mtime": 1283938772.0, + "st_blocks": 40, + "st_nlink": 1, + "st_gid": 33, + "st_blksize": 4096, + "st_dev": 2049, + "st_size": 18892, + "st_atime": 1301575706.374059, + "st_uid": 1000, + "st_ino": 141221, + "st_mode": 33277 + }, + "orientalCollection.py": { + "st_ctime": 1304607671.3867805, + "st_rdev": 0, + "st_mtime": 1283870624.0, + "st_blocks": 16, + "st_nlink": 1, + "st_gid": 33, + "st_blksize": 4096, + "st_dev": 2049, + "st_size": 8109, + "st_atime": 1301575706.764082, + "st_uid": 1000, + "st_ino": 141225, + "st_mode": 33277 + }, + "scripts": {}, + "entityFiles": {}, + "__init__.py": { + "st_ctime": 1304607671.3867805, + "st_rdev": 0, + "st_mtime": 1264173670.0, + "st_blocks": 0, + "st_nlink": 1, + "st_gid": 33, + "st_blksize": 4096, + "st_dev": 2049, + "st_size": 0, + "st_atime": 1264173670.0, + "st_uid": 1000, + "st_ino": 141254, + "st_mode": 33277 + }, + "logFiles": {}, + "static": {}, + "updateEntente.py": { + "st_ctime": 1304607671.3867805, + "st_rdev": 0, + "st_mtime": 1271248754.0, + "st_blocks": 8, + "st_nlink": 1, + "st_gid": 33, + "st_blksize": 4096, + "st_dev": 2049, + "st_size": 1007, + "st_atime": 1301575706.8940661, + "st_uid": 1000, + "st_ino": 141253, + "st_mode": 33277 + }, + "earlyPrintingCollection.py": { + "st_ctime": 1304607671.3867805, + "st_rdev": 0, + "st_mtime": 1283865770.0, + "st_blocks": 56, + "st_nlink": 1, + "st_gid": 33, + "st_blksize": 4096, + "st_dev": 2049, + "st_size": 24885, + "st_atime": 1301575707.324084, + "st_uid": 1000, + "st_ino": 141219, + "st_mode": 33277 + }, + "blockbooksCollection.py": { + "st_ctime": 1304607671.3867805, + "st_rdev": 0, + "st_mtime": 1283960952.0, + "st_blocks": 72, + "st_nlink": 1, + "st_gid": 33, + "st_blksize": 4096, + "st_dev": 2049, + "st_size": 34171, + "st_atime": 1301575707.0940628, + "st_uid": 1000, + "st_ino": 139421, + "st_mode": 33277 + }, + "main2.py": { + "st_ctime": 1304607671.3867805, + "st_rdev": 0, + "st_mtime": 1285688296.0, + "st_blocks": 8, + "st_nlink": 1, + "st_gid": 33, + "st_blksize": 4096, + "st_dev": 2049, + "st_size": 712, + "st_atime": 1301575706.4840596, + "st_uid": 1000, + "st_ino": 141223, + "st_mode": 33277 + }, + "medievalCollection.py": { + "st_ctime": 1304607671.3867805, + "st_rdev": 0, + "st_mtime": 1283870624.0, + "st_blocks": 16, + "st_nlink": 1, + "st_gid": 33, + "st_blksize": 4096, + "st_dev": 2049, + "st_size": 6993, + "st_atime": 1301575705.9940662, + "st_uid": 1000, + "st_ino": 141224, + "st_mode": 33277 + }, + "config": {}, + "sourceData": {}, + "greekCollection.py": { + "st_ctime": 1304607671.3867805, + "st_rdev": 0, + "st_mtime": 1283870624.0, + "st_blocks": 16, + "st_nlink": 1, + "st_gid": 33, + "st_blksize": 4096, + "st_dev": 2049, + "st_size": 6972, + "st_atime": 1301575706.9840605, + "st_uid": 1000, + "st_ino": 141220, + "st_mode": 33277 + } + } +} \ No newline at end of file diff --git a/rdfdatabank/public/static/json_data/datasetsInSiloInformation.txt b/rdfdatabank/public/static/json_data/datasetsInSiloInformation.txt new file mode 100644 index 0000000..11181c1 --- /dev/null +++ b/rdfdatabank/public/static/json_data/datasetsInSiloInformation.txt @@ -0,0 +1,25 @@ +Information obtained for datasets in silo 'sandbox' (at /sandbox/datasets) in text/plain or application/json + +data_returned = { + "test": ["True", "2080-10-28T21:23:51.615033"], + "test-907365275": ["True", "2081-05-01T11:18:20.516356"], + "test2": ["True", "2080-06-03T10:11:00.313756"], + "test3": ["True", "2080-06-14T14:33:10.780119"], + "test4": ["True", "2081-04-23T12:15:17.633969"], + "test5": ["True", "2080-06-20T10:06:14.585849"], + "dataset1": [false, ""], + "dataset1-testdir": ["True", "2080-10-11T17:14:39.763539"], + "dataset1-test4": ["True", "2013-01-02T00:00:00"], + "dataset1-test5": ["True", "2012-01-02T00:00:00"], + "dataset1-test7": ["True", "2080-11-11T17:57:02.000026"], + "dataset1-test3": ["True", "2080-11-11T17:43:14.874758"], + "dataset1-test6": ["True", "2012-01-02T00:00:00"], + "dataset1-TestScanFilesSubDir": ["True", "2080-11-11T17:37:40.411597"], + "dataset1-testrdf": ["True", "2080-10-09T05:22:32.004597"], + "dataset2": ["True", "2080-10-03T11:33:03.136135"], + "book1": ["True", "2081-03-06T16:00:36.214577"], + "302739708": ["True", "2081-05-22T14:49:04.738700"], + "databanksrc2": ["True", "2081-01-23T15:29:58.120092"], + "english": ["True", "2081-01-24T12:11:29.910444"], + "databanksrc": ["True", "2081-01-23T13:22:15.369095"] +} \ No newline at end of file diff --git a/rdfdatabank/public/static/json_data/itemInformationForDataset-old.txt b/rdfdatabank/public/static/json_data/itemInformationForDataset-old.txt new file mode 100644 index 0000000..d43e340 --- /dev/null +++ b/rdfdatabank/public/static/json_data/itemInformationForDataset-old.txt @@ -0,0 +1,7 @@ +Information obtained for the dataset 'dataset2' in silo 'sandbox' (at /sandbox/items/dataset2) in text/plain or application/json + +data_returned = { + "auctm315.zip": "dataset2-auctm315", + "archgc14.zip": "dataset2-archgc14", + "test1.zip": "dataset2-test1" +} diff --git a/rdfdatabank/public/static/json_data/itemInformationForDataset.txt b/rdfdatabank/public/static/json_data/itemInformationForDataset.txt new file mode 100644 index 0000000..28f7bba --- /dev/null +++ b/rdfdatabank/public/static/json_data/itemInformationForDataset.txt @@ -0,0 +1,7 @@ +Information obtained for the dataset 'dataset2' in silo 'sandbox' (at /sandbox/items/dataset2) in text/plain or application/json + +data_returned = [ + "auctm315.zip", + "archgc14.zip", + "test1.zip" +] diff --git a/rdfdatabank/public/static/json_data/itemInformationForZipFileinDataset.txt b/rdfdatabank/public/static/json_data/itemInformationForZipFileinDataset.txt new file mode 100644 index 0000000..a33602b --- /dev/null +++ b/rdfdatabank/public/static/json_data/itemInformationForZipFileinDataset.txt @@ -0,0 +1,10 @@ +Information obtained for the file test1.zip in dataset 'dataset2' in silo 'sandbox' +(at /sandbox/items/dataset2/test1.zip) in text/plain or application/json + +data_returned = { + "admiral-dataset.txt": [43, [2010, 11, 29, 16, 30, 52]], + "TestScanFilesSubDir/": [0, [2010, 11, 29, 17, 34, 42]], + "TestScanFilesSubDir/TestScanFiles31.txt": [9, [2010, 10, 4, 15, 39, 54]], + "TestScanFilesSubDir/TestScanFiles32.txt": [9, [2010, 10, 4, 15, 39, 54]], + "TestScanFilesSubDir/manifest.rdf": [471, [2010, 11, 29, 17, 36, 26]] +} diff --git a/rdfdatabank/public/static/json_data/siloInformation.txt b/rdfdatabank/public/static/json_data/siloInformation.txt new file mode 100644 index 0000000..e82dc26 --- /dev/null +++ b/rdfdatabank/public/static/json_data/siloInformation.txt @@ -0,0 +1,21 @@ +Information obtained for silo 'sandbox' (at /sandbox) in text/plain or application/json + +data_returned = { + "test": ["True", "2080-10-28T21:23:51.615033"], + "test-907365275": ["True", "2081-05-01T11:18:20.516356"], + "test3": ["True", "2080-06-14T14:33:10.780119"], + "test2": ["True", "2080-06-03T10:11:00.313756"], + "test5": ["True", "2080-06-20T10:06:14.585849"], + "test4": ["True", "2081-04-23T12:15:17.633969"], + "dataset1": [false, ""], + "dataset1-testdir": ["True", "2080-10-11T17:14:39.763539"], + "dataset1-test7": ["True", "2080-11-11T17:57:02.000026"], + "dataset1-test3": ["True", "2080-11-11T17:43:14.874758"], + "dataset1-test6": ["True", "2012-01-02T00:00:00"], + "dataset1-testrdf": ["True", "2080-10-09T05:22:32.004597"], + "dataset2": ["True", "2080-10-03T11:33:03.136135"], + "book1": ["True", "2081-03-06T16:00:36.214577"], + "databanksrc2": ["True", "2081-01-23T15:29:58.120092"], + "english": ["True", "2081-01-24T12:11:29.910444"], + "databanksrc": ["True", "2081-01-23T13:22:15.369095"] +} \ No newline at end of file diff --git a/rdfdatabank/public/static/json_data/silos.txt b/rdfdatabank/public/static/json_data/silos.txt new file mode 100644 index 0000000..a0dae52 --- /dev/null +++ b/rdfdatabank/public/static/json_data/silos.txt @@ -0,0 +1,8 @@ +Information obtained at /silos in text/plain or application/json + +data_returned = [ + "admiral-test", + "general", + "sandbox", + "digitaltest" +] \ No newline at end of file diff --git a/rdfdatabank/public/static/style.css b/rdfdatabank/public/static/style.css new file mode 100644 index 0000000..3bd2402 --- /dev/null +++ b/rdfdatabank/public/static/style.css @@ -0,0 +1,524 @@ +body { + font-size: 80%; + font-family: droid, helvetica, verdana, sans-serif; +} + +/* Layout */ +#wrapper { + width: 72em; + max-width: 100%; + margin: 0 auto; + text-align: left; +} + +#header { + max-width: 100%; + height: 85px; + border-bottom: 2px ridge #CCCCCC; + margin-bottom: 10px; +} + +#leftcol { + width: 18em; + max-width: 23%; + float: left; + /* debug color + background: #ddd;*/ +} + +#content { + width: 52em; + max-width: 75%; + float: right; + /* debug color + background: #efefef;*/ + padding: 0.5em; +} + +#widecontent { + max-width: 100%; + padding: 0.5em; +} + +#footer { + clear: both; + max-width: 100%; + border-top: 2px ridge #CCCCCC; + margin-top: 10px; + /* debug color + background: #69d;*/ +} + +/* Float ruler */ +.clearfloats { + clear: both; + height: 0em; +} + +/* Header */ +#branding { +} + +#logo { + float: left; +} + +#search { + float: right; + margin-right: 10px +} + +#search_form { + text-align: right; +} + +#nav { + clear: both; + margin-bottom: 10px; +} + +h1 { + clear:both; +} + +#nav .lt ul { + padding: 10px 10px 10px 0px; + font-size: 2em; +} + +#nav .lt ul li { + display: inline; + background-image: url("blksquare.gif"); + background-position: 0px 10px; + background-repeat: no-repeat; + display: inline; + padding-left: 18px; +} + +.rt a{ + padding-right: 0px; +} + +.rt ul { + padding-right: 0px; +} + +.readme { + font-size: 0.9em; +} + +.unpack_endpoints li { + height: 2em; +} + +.List { + border-top: 1px solid #CCCCCC; + margin-top: 5px; + margin-bottom: 10px; +} + +.List-entry { + font-weight: bold; + letter-spacing: 1px; + margin-bottom: 5px; + margin-left: 20px; + margin-top: 15px; +} + +.List-contents { + margin-left: 30px; +} + +.Section { + background: none repeat scroll 0 0 #CCCCCC; + font-size: 1.3em; + font-weight: bold; + height: 23px; + width: 100%; +} + +.todo { + color: #ff0000; +} + +.lt { + float: left; +} + +.rt { + float: right; +} + +.menu { + display: inline; + margin-right: 5px; +} + +h1, h2, h3, h4, h5, h6 +{ +clear: both; +color: #045275; +font-style: normal; +font-weight: normal; +letter-spacing: 0.1em; +margin: 1.3em 0px 0.3em 0px; +border-bottom: 1px solid #b5d2df; +} +/* Results Page Specific CSS */ + +#results { +color: #002147; +font-size: 12px; +margin-bottom: 100px; +margin-left: 180px; +} + +#results a { +text-decoration: none; +} + +#results .stitle a:visited { +color: #7F007F; +} + +span.highlighted_page a { +font-size: 12px !important; +} + +.paginated_results { +float: left; +font-size: 12px !important; +} + +.paginated_results .pagination_label a { +padding: 4px 6px; +margin: 0 3px 0 0; +text-decoration: none; +color: #002147; +font-size: 12px; +background-color: #999; +} + +.paginated_results .highlighted_page a { +color: white !important; +background-color: #002147; +} + +.paginated_results .numbers { +margin-right: 50px; +} + +#sorting { +float: right; +font-size: 11px; +padding-top: 1px; +} + +#sorting span { +margin: 0 0 0 8px; +} + +#sorting span a { +color: #999; +} + +#sorting span.active a { +color: black; +} + +#response_wrapper { +margin-top: 10px; +} + +div.response_doc { +margin-top: 10px; +padding-bottom: 20px; +border-bottom: 1px solid #999; +} +.response_doc span.stitle a { +font-weight: bold; +color: #002147; +font-size: 14px; +} +.row { +width: 100%; +clear: both; +} + +.label { +font-weight: bold; +color: #002147; +vertical-align: top; +width: 150px; +float:left; +display: inline; +} + +.value, .value a, .value a:hover, .value a:active, .value a:visited { +display:inline; +padding-left:2px; +text-align:right; +color: #000000; +text-decoration: none; +font-weight: normal; +} + +.rt { +color: #002147; +vertical-align: top; +float:left; +padding-left: 5px; +display: inline; +} + +#facet_wrapper { +position: absolute; +width: 160px; +left: 10px; +margin-left: 10px; +top: 250px; +font-size: 11px; +} + +#facet_container { +width: 150px; +word-wrap: break-word; +} + +.facet_results .subheading { +background: #BBB; +padding: 4px 5px; +border-bottom: 1px solid #002147 !important; +} + +.facet_results .facetlist { +background: #EEE; +} +.facet_results .facetlist ul, .facet_results .facetlist p { +margin: 0; padding: 0; +} + +.facet_results .subheading a { +text-decoration: none; +color: black; +font-weight: normal; +background: url('fplus.png') no-repeat 0% 60%; +padding-left: 18px; +} + +.facet_results .facetlist li { +list-style: none; +} + +.facet_results .facetlist a { +text-decoration: none; +} + +.facet_chosen { +margin-bottom: 10px; +background: #002147; +color: white; +} + +.facet_chosen #remove { +float: left; +background: white; +color: #002147; +border: 0; +padding: 0px 2px; +border-radius: 10px; +font-size: 11px; +font-weight: bold; +} +#current_search { +font-weight: bold; +background: #002147; +color: white; +font-size: 1.2em; +border-bottom: 1px solid white; +padding: 4px 5px; +} + +div#link_to_this_search { +margin-top: 20px; +margin-bottom: -15px; +clear: both; +color: #000000; +} + +div#link_to_this_search a { +color: #002147; +} + +.nomatches { +background-color: rgba(255, 255, 255, 0.7); +color: #000000; +font-size: 1.2em; +margin: 30px 10px 10px 10px; +padding: 15px; +width: 100%; +} + +.searchbottom { +margin-bottom: 5px; +margin-top: 10px; +float: left; +font-size: 13px; +color: #002147; +background: white; +width: 100%; +} +.searchtop { +margin-top: 5px; +margin-bottom: 10px; +float: left; +font-size: 13px; +color: #002147; +background: white; +width: 100%; +} + +.searchleft { +float: left; +width: 150px; +} + +.searchright { +float: right; +width: 250px; +} + +.searchmid { +float: left; +text-align: center; +width: 250px; +} + +span#searchordering { +float: right; +font-size: 13px; +} + +span#searchordering a { +color: #002147; +} + +span#searchordering a.active { +color: black; +font-weight: bold; +} +span#itemsppt, span#itemsppb { +color: #002147; +float: right; +} + +span#itemsppt a, span#itemsppb a { +color: #002147; +padding: 2px 3px; +margin: 0px 2px; +text-decoration: none; +border: 1px solid #aaa; +} + +span#itemsppt a:hover, span#itemsppb a:hover { +color: black; +border: 1px solid #333; +} + +span#itemsppt a.active, span#itemsppb a.active { +color: black; +border: 1px solid #666; +background: #FFFF99; +} + +span#itemsppt a.active:hover, span#itemsppb a.active:hover { +color: black; +border: 1px solid #333; +background: #FFFF99; +} + +.itemsppform { +margin: 0px; +display:inline; +padding: 0px; +} + +.itemsppformtext { +margin: 0px; +display:inline; +padding: 0px; +width: 40px; +} + +.itemsppformchosen { + color: black; + border: 1px solid #666; + background: #FFFF99; +} + +.httperror h1{ + font-size: 2em; +} + +.create_silo label, .alter_silo label, .create_user label { +display: inline-block; +margin-bottom: 10px; +width: 300px; +} + +.create_silo div, .alter_silo div, .metadata div, .create_user div, .update_role div { +margin-left: 20px; +} + +.metadata label { +display: inline-block; +margin-top: 10px; +width: 150px; +} + +.metadata ul { +margin: -1.2em 0px -10px 150px; +padding: 0px; +} + +.metadata li { +margin: 0px 0px 10px 0px; +padding: 0px; +list-style-type: none; +} + +.metadata li.subtitle { +margin: 0px 0px 10px 0px; +padding: 0px; +list-style-type: none; +font-weight: bold; + +} + +.metadata li div { +display: inline; +margin-left: 10px; +} + +.update_role label { +display: inline-block; +margin-top: 0px; +width: 150px; +} + +.update_role div { +margin-bottom: 10px; +margin-left: 40px; +} + +.update_user label { +display: inline-block; +margin-bottom: 10px; +width: 150px; +} + +.update_user div { +margin-bottom: 10px; +margin-left: 40px; +} + diff --git a/rdfdatabank/public/static/styles/basic.css b/rdfdatabank/public/static/styles/basic.css new file mode 100644 index 0000000..fb3c6a8 --- /dev/null +++ b/rdfdatabank/public/static/styles/basic.css @@ -0,0 +1,1569 @@ +/** + * Copyright (c) 2012 University of Oxford + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, --INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + **/ + +body +{ + background-color: #fff; + font-family: helvetica, arial, sans-serif; + font-size: 0.82em; + padding: 0px; + + +} +/* use a serif font for headers, preferably Georgia */ +h1, h2, h3, h4, h5, h6 +{ + color: #002147; + font-weight: normal; + margin: 0.8em 0px 0.2em 0px; + + +} + + +h1 +{ +font-size: 1.8em; + +} + +h2 +{ + +font-size: 1.65em; +} + +h3 +{ + +font-size: 1.5em; +} + +h4 +{ + +font-size: 1.35em; +} + +h5 +{ + +font-size: 1.2em; +} + +h6 +{ + +font-size: 1.05em; +font-style: italic; +font-weight: normal; +margin: 0px; +margin-bottom: 0.2em; +padding: 0px; + +} + +a, +a:link, +a:visited, +a:active, +a:focus, +a:hover +{ + color: #0a4361; +} + + + +/* establish some consistant style for the main blocks */ +header.pageHeader, +.main, +footer.site +{ + background-color: #002147; + color: #fff; + margin-bottom: 10px; +} + + +.main +{ + background-color: #fff; + color: #444; +} + +label +{ + font-weight: bold; +} + +p +{ + margin: 0.7em 0px; +} + +.content footer +{ + margin: 10px 0px; +} + + + +/* START OF additional style changes applicable to both 'Bank and 'Stage, April 2012 */ + +blockquote { + font-size: 1em; +} + +code +{ + font-size: 1.3em; +} + +.siteAdmin { + +} + + +/* END OF additional style changes applicable to both 'Bank and 'Stage, April 2012 */ + + +/* hide h2's in mainnav */ +#mainnav h2, +#subnav h2 +{ + position: absolute; + top: -1000px; +} + + + +header.pageHeader { + + background: #002147; /* for non-css3 browsers */ + + /*filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#002147', endColorstr='#DDDDDD');*/ /* for IE */ + /*ckground: -webkit-gradient(linear, left top, left bottom, from(#002147), to(#DDDDDD));*/ /* for webkit browsers */ + /*ckground: -moz-linear-gradient(top, #002147, #DDDDDD);*/ /* for firefox 3.6+ */ + border-top: 1px solid #002147; + color:#fff; + margin: 0px; + padding: 0px; + position: relative; +} + + +header.pageHeader a +{ + color: #fff; +} + +header.pageHeader h1 +{ + background-color: transparent; + font-size: 5.5555em;; + font-weight: bold; + letter-spacing: -0.16em; + /* + margin: -10px 0px -10px -7px; + */ + margin: 0px; + padding: 0px; + text-transform: uppercase; + +} + +header.pageHeader h1 a, +header.pageHeader h1 a:link, +header.pageHeader h1 a:visited, +header.pageHeader h1 a:active + +{ + + color:#ffffff; +} + +header.pageHeader h1 a:hover, +header.pageHeader h1 a:focus +{ + color: #fff; +} + +header.pageHeader h1 a span.small { + font-size: 0.7em; + padding-left: 5px; + letter-spacing: -0.1em; +} + +header ul li, +footer ul li +{ +display: inline; +padding: 2px 10px; +font-size: 0.9em; + +} + + +li +{ +list-style: none; +} + + +.content ul, +.content ol +{ +padding-left: 20px; +} +.content li +{ +margin: 0.6em 0px 0px 0px; +} + + +.content ol li +{ +list-style: disc; +} + +.content ol li +{ +list-style: decimal; +} + + + + +/* breadcrumb styles */ + +.breadcrumb ul li +{ +display: inline; +} + +.breadcrumb .youarehere +{ +font-weight: bold; +} + + +a +{ +text-decoration: none; +} + + +.main a +{ +text-decoration: underline; +} + +.clear +{ +clear: both; +} + + + +.name +{ + +font-weight: bold; +} + +h1 .name +{ + + +font-style: italic; +} + + + + +/* currentActions */ + +.currentActions ul li +{ +display: inline; +} + + + +/* browse styles */ + + + + + +/* general table styles */ + + +table +{ +word-wrap: break-word; +margin: 10px 0px; +} + + + +table th, +table td +{ +background-color: #fff; +border: 1px solid #eee; +margin: 3px; +padding: 4px; + +word-wrap: break-word; + +} + + +table th +{ + background: #002147; /* for non-css3 browsers */ + /* filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#002147', endColorstr='#DDDDDD'); */ /* for IE */ + /* background: -webkit-gradient(linear, left top, left bottom, from(#002147), to(#DDDDDD)); */ /* for webkit browsers */ + /* background: -moz-linear-gradient(top, #002147, #DDDDDD); */ /* for firefox 3.6+ */ + + color: #fff; + font-weight: bold; +} + + +/* enforce max-width on the browse table cells */ +table.browse td +{ + + + +} + + + +/* system messgae styles */ + +.message { + +background-color: #666; +border-bottom: 10px solid #999; +color: #fff; +padding: 5px; +margin-bottom: 5px; +} +/* good (green is good) */ + +.success{ +background-color: #2a2; +border-color: #6c6; +} +/* bad (red is baaaaaad) */ +.error { +background-color: #a22; +border-color: #c66; +} + +/* are you sure? (blue is cool on the subject..) */ + +.confirm { +background-color: #22a; +border-color: #66c; +} + + +.message a +{ +color: #fff; +} + + +.message ul li +{ +list-style: none; +display: inline; +} + + +/* breadcrumb */ + +.breadcrumb +{ + +} + + +ul.breadcrumb +{ +display: inline; +} + +ul.breadcrumb li a +{ +background: transparent url('/static/styles/images/icons/breadcrumb-arrow.png') no-repeat center right; +padding-right: 12px; +} + +/* action links */ +.main a.action, +a.action +{ + background: #002147; /* for non-css3 browsers */ + /* filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#002147', endColorstr='#DDDDDD'); */ /* for IE */ + /* background: -webkit-gradient(linear, left top, left bottom, from(#002147), to(#DDDDDD)); */ /* for webkit browsers */ + /* background: -moz-linear-gradient(top, #002147, #DDDDDD); */ /* for firefox 3.6+ */ + + box-shadow: #ccc 0.1em 0.1em 0.1em; /* Opera 10.5, IE 9.0 */ + -webkit-box-shadow: #ccc 0.1em 0.1em 0.1em; /* Chrome Safari */ + -moz-box-shadow: #ccc 0.1em 0.1em 0.1em; /* Firefox F3.5+ */ + + color: #fff; + font-size: 0.9em; + line-height: 1.7em; + padding: 3px 5px; + text-decoration: none; + text-transform: uppercase; + + +} + + +.main a.action:hover, +.main a.action:focus, +a.action:hover, +a.action:focus +{ + background-color: #fff; + color: #002147; +} + + + +/* form styling */ + + +form +{ +margin: 1em; +} + +.main form label +{ +display: block; +width: 10em; +float: left; +text-align: right; +margin-right: 5px; +} + +#search form label +{ +font-weight: normal; +text-transform: none; +} + + +#search form fieldset +{ +border: none; +padding: 0px; + +} + + label +{ +padding: 0px 2px 15px 5px; +text-transform: uppercase; +} + +form div, +form fieldset, +div.summary div +{ + +max-width: 25em; +clear: both; +margin: 5px; +} + + + +div.textarea label, +form div.textareaOutput, +div#receipt div.textareaOutput, +textarea +{ +border: 1px solid #ccc; +display: block; +margin: 0px; +max-width: none; +padding: 3px; +text-align: left; +width: 100%; +} + + div.textarea label +{ +background-color: #eee; +border-bottom-style: none; +text-align: center; +} + +fieldset +{ +padding: 10px; +margin: 10px; +border: 1px dotted #ccc; + +} + +legend +{ +font-weight: bold; +} + + +form .radio label +{ +display: inline !important; +width: auto; +float: none; + +} + + +/* submission */ + + +.confirmSubmit +{ +margin-top: 20px; +} + + + +/* navigation */ + + +nav { + background-color: #fff; + border: 1px solid #dfe2e4; + border-top-style: none; + padding-bottom: 7px; + position: relative; +} + +nav span.nonVis +{ +color: #000; +position: absolute; +top: -9000px; +} + +nav span +{ +color: #000; +} + +ul.breadcrumb .dataPackage a , +ul.breadcrumb .silo a { + +font-weight: bold; +} + + + +header.pageHeader nav a { + color: #002147; + +} + +#mainnav { + + margin-bottom: 0px; + padding: 3px 0px; +} + +#mainnav a, +#mainnav span + +{ + border: 1px solid #fff; + border-top: 3px solid #fff; + color: #002147; + font-size: 1.3em; + line-height: 2em; + padding: 6px 7px 2px 7px; + text-decoration: none; + + +} + + +#mainnav a:hover, +#mainnav a:focus, +#mainnav span +{ + border: 1px solid #eee; + border-top: 3px solid #002147; +} + + +#mainnav span +{ + -webkit-box-shadow: #ccc 0.2em 0.2em 0.2em; /* Chrome Safari */ + -moz-box-shadow: #ccc 0.2em 0.2em 0.2em; /* Firefox F3.5+ */ + box-shadow: #ccc 0.2em 0.2em 0.2em; /* Opera 10.5, IE 9.0 */ + border-top: 3px solid #002147; +} + +#subnav { + +border-top: 1px solid #eee; +margin-top: 5px; +padding: 0px; +} + + +#subnav ul { +padding-top: 7px; +} + + +.nav_rt { + + background: #FDFEFE; + /* filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fdfefe', endColorstr='#f4f7fa'); + background: -webkit-gradient(linear, left top, left bottom, from(#FDFEFE), to(#F4F7FA)); + background: -moz-linear-gradient(top, #FDFEFE, #F4F7FA); */ + border: 1px solid #DFE2E4; + border-bottom-width: 3px; + -moz-border-radius: 5px; + -webkit-border-radius: 5px; + border-radius: 5px; + clear: both; + color: #000;margin: 0px 0px 15px 0px; + padding: 5px; + position: absolute; + right: 5px; + top: 5px; + text-align: right; +} + + +.nav_rt p{ + display: inline; + margin: 0px 5px 0px 0px; +} + +footer.site +{ + background: #002147; /* for non-css3 browsers */ + /* filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#002147', endColorstr='#DDDDDD'); */ /* for IE */ + /* background: -webkit-gradient(linear, left top, left bottom, from(#002147), to(#DDDDDD)); */ /* for webkit browsers */ + /* background: -moz-linear-gradient(top, #002147, #DDDDDD); */ /* for firefox 3.6+ */ + + font-size: 0.9em; + padding: 10px; + text-align: center; +} + +footer.site a +{ +color: #fff; + +} + + +.intro, +.pleaseNote, +.message, +.about{ + background: #fdfefe; /* for non-css3 browsers */ + /* filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fdfefe', endColorstr='#f4f7fa'); */ /* for IE */ + /* background: -webkit-gradient(linear, left top, left bottom, from(#fdfefe), to(#f4f7fa)); */ /* for webkit browsers */ + /* background: -moz-linear-gradient(top, #fdfefe, #f4f7fa); */ /* for firefox 3.6+ */ + border: 1px solid #dfe2e4; + -moz-border-radius: 5px; /* Firefox */ + -webkit-border-radius: 5px; /* Safari, Chrome */ + border-radius: 5px; /* CSS3 */ + clear: both; + margin: 0px 0px 15px 0px; + padding: 5px; + +} + +.message { + + +} + +.intro { + +font-weight: bold; +} + +.about { +margin: 0px 20px; +padding: 10px 30px; +} + +.about p { + margin-bottom: 20px; +} +.create-silo form { +border: 1px solid #eee; +padding: 5px; +} + +.create-silo form label { + + + +} + +.create-silo form li { +clear: both; +line-height: 2em; + +} + +.bigUp { + + font-size: 2em; + text-align: center; + margin-bottom: 25px; + margin-top: 20px; +} + +a:link.callToAction, +a:visited.callToAction, +a:active.callToAction, +a:hover.callToAction, +a:focus.callToAction{ + +background: #002147; /* for non-css3 browsers */ + /* filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#002147', endColorstr='#DDDDDD');*/ /* for IE */ + /* background: -webkit-gradient(linear, left top, left bottom, from(#002147), to(#DDDDDD)); */ /* for webkit browsers */ + /* background: -moz-linear-gradient(top, #002147, #DDDDDD); */ /* for firefox 3.6+ */ + border-top: 1px solid #002147; + box-shadow: #cccccc 0.3em 0.3em 0.2em; /* Opera 10.5, IE 9.0 */ + -webkit-box-shadow: #cccccc 0.3em 0.3em 0.2em; /* Chrome Safari */ + -moz-box-shadow: #cccccc 0.3em 0.3em 0.2em; /* Firefox F3.5+ */ + clear: both; + color:#fff; + margin: 10px 0px; + padding: 5px; + text-decoration: none; + + +} + +a:hover.callToAction, +a:focus.callToAction { + color:#99cdd1; +} + +/* Results Page Specific CSS */ + +#results { + color: #002147; + font-size: 12px; + margin-bottom: 100px; + margin-left: 180px; +} + +#results a { + text-decoration: none; +} + +#results .stitle a:visited { + color: #7F007F; +} + +span.highlighted_page a { + font-size: 12px !important; +} + +.paginated_results { + float: left; + font-size: 12px !important; +} + +.paginated_results .pagination_label a { + padding: 4px 6px; + margin: 0 3px 0 0; + text-decoration: none; + color: #002147; + font-size: 12px; + background-color: #999; +} + +.paginated_results .highlighted_page a { + color: white !important; + background-color: #002147; +} + +.paginated_results .numbers { + margin-right: 50px; +} + +#sorting { + float: right; + font-size: 11px; + padding-top: 1px; +} + +#sorting span { + margin: 0 0 0 8px; +} + +#sorting span a { + color: #999; +} + +#sorting span.active a { + color: black; +} + +#response_wrapper { + margin-top: 10px; +} + +div.response_doc { + margin-top: 10px; + padding-bottom: 20px; + border-bottom: 1px solid #999; +} + +.response_doc .resultTitle { + font-weight: bold; + font-size: 1.2em; + margin-bottom: 5px; +} + +.response_doc .resultTitle a { + font-weight: bold; + font-size: 1.2em; + + background: #002147; /* for non-css3 browsers */ + /* filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#7e94a5', endColorstr='#90b6b7'); /* for IE */ + /* background: -webkit-gradient(linear, left top, left bottom, from(#7e94a5), to(#90b6b7)); */ /* for webkit browsers */ + /* background: -moz-linear-gradient(top, #7e94a5, #90b6b7); */ /* for firefox 3.6+ */ + + color: #fff; + display: block; + padding: 5px; + text-decoration: none; + +} + +#results .response_doc .silotitle a:focus, +#results .response_doc .silotitle a:hover { + +text-decoration: underline; + +} + + +.response_doc .silotitle a:visited { + + + background: #002147; /* for non-css3 browsers */ + /* filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#002147', endColorstr='#DDDDDD'); */ /* for IE */ + /* background: -webkit-gradient(linear, left top, left bottom, from(#002147), to(#DDDDDD)); */ /* for webkit browsers */ + /* background: -moz-linear-gradient(top, #002147, #DDDDDD); */ /* for firefox 3.6+ */ + + +} + + + + +.response_doc dt { + font-weight: bold; + font-size: 1em; + vertical-align: top; + width: 150px; + float:left; + display: inline; +} + +.response_doc dd { + padding-left:3px; + text-align:left; + font-weight: normal; + font-size: 1em; + margin-bottom: 5px; +} + +.rt { + color: #002147; + vertical-align: top; + float:right; + padding-left: 5px; + display: inline; +} + +/* facets in results */ +#facet_wrapper { + position: absolute; + width: 160px; + margin-left: 10px; + top: 150px; + font-size: 11px; +} + +#facet_container { + width: 150px; + word-wrap: break-word; +} + +.facet_title { + /* + background: #BBB; + padding: 10px 5px; + border-bottom: 1px solid #002147 !important; + font-size: 1.3em; + font-weight: bold; + text-align: center; + */ + + background: #002147; + color: #fff; + padding: 10px 5px; + border-bottom: 1px solid #fff !important; + font-size: 1.3em; + font-weight: bold; + text-align: center; + +} + +.facet_results .subheading { + /* + background: #BBB; + padding: 4px 5px; + border-bottom: 1px solid #002147 !important; + */ + background: #002147; /* for non-css3 browsers */ + /* filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#002147', endColorstr='#DDDDDD'); */ /* for IE */ + /* background: -webkit-gradient(linear, left top, left bottom, from(#002147), to(#DDDDDD)); */ /* for webkit browsers */ + /* background: -moz-linear-gradient(top, #002147, #DDDDDD); */ /* for firefox 3.6+ */ + color: #fff; + padding: 4px 5px; + border-bottom: 1px solid #fff !important; +} + +.facet_results .facetlist { + background: #EEE; +} +.facet_results .facetlist ul, .facet_results .facetlist p { + margin: 0; padding: 0; +} + +.facet_results .subheading a { + text-decoration: none; + color: #fff; + font-weight: normal; + background: url('/static/styles/images/fplus.png') no-repeat 0% 60%; + padding-left: 18px; +} + +.facet_results .facetlist li { + list-style: none; +} + +.facet_results .facetlist a { + text-decoration: none; +} + +.facet_chosen { + margin-bottom: 10px; + background: #002147; + color: white; +} + +.facet_chosen #remove { + float: left; + background: white; + color: #002147; + border: 0; + padding: 0px 2px; + border-radius: 10px; + font-size: 11px; + font-weight: bold; +} + +/*Current search */ +.current_search { + + background: #FDFEFE; + /* filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fdfefe', endColorstr='#f4f7fa'); + background: -webkit-gradient(linear, left top, left bottom, from(#FDFEFE), to(#F4F7FA)); + background: -moz-linear-gradient(top, #FDFEFE, #F4F7FA); */ + border: 1px solid #DFE2E4; + -moz-border-radius: 5px; + -webkit-border-radius: 5px; + border-radius: 5px; + padding: 5px; +} + +h2.current_search_header { + + border-bottom: 1px solid #DFE2E4; + font-size: 1.3em; + margin: 0px; + padding: 0px; + +} + +.numRecords { + font-size: 1.2em; + font-weight: bold; +} + + +.current_facet { + margin: 10px; + border-bottom: 1px dotted #cccccc; + clear: both; + height: 25px; +} + +.current_facet .search_remove { + float:left; + position:inline; + font-weight: bold; + width: 50px; + text-align: left; + padding-right: 10px; +} + +.current_facet .search_remove form { + margin: 0px; +} + +.current_facet .search_remove form input{ + margin: 0px; + width: 20px; + font-size: 1em; + font-weight: bold; +} + +.current_facet .label { + float:left; + position:inline; + font-weight: bold; + width: 200px; + text-align: left; + padding: 5px 10px 0px 0px; +} + +.current_facet .value { + float:left; + position:inline; + text-align: left; + margin-bottom: 5px; + padding: 5px 10px 0px 0px; +} + +div#link_to_this_search { + margin-top: 20px; + margin-bottom: -15px; + clear: both; + color: #000000; + padding-bottom: 10px; + border-bottom: 1px solid #999999 +} + +div#link_to_this_search a { + color: #002147; +} + +.nomatches { + background-color: rgba(255, 255, 255, 0.7); + color: #000000; + font-size: 1.2em; + margin: 30px 10px 10px 10px; + padding: 15px; + width: 100%; +} + +.searchbottom, .browsebottom { + margin-bottom: 5px; + margin-top: 10px; + float: left; + font-size: 13px; + color: #002147; + background: white; + width: 100%; +} +.searchtop, .browsetop { + margin-top: 5px; + margin-bottom: 10px; + float: left; + font-size: 13px; + color: #002147; + background: white; + clear:left; +} + +.searchtop { + width: 100%; +} + +.browsetop { + width: 50%; +} + +.searchleft { + float: left; + width: 150px; +} + +.searchright { + float: right; + width: 250px; +} + +.searchmid { + float: left; + text-align: center; + width: 250px; +} + +span#searchordering { + float: right; + font-size: 13px; +} + +span#searchordering a { + color: #002147; +} + +span#searchordering a.active { + color: black; + font-weight: bold; +} +span#itemsppt, span#itemsppb { + color: #002147; + float: right; +} + +span#itemsppt a, span#itemsppb a { + color: #002147; + padding: 2px 3px; + margin: 0px 2px; + text-decoration: none; + border: 1px solid #aaa; +} + +span#itemsppt a:hover, span#itemsppb a:hover { + color: black; + border: 1px solid #333; +} + +span#itemsppt a.active, span#itemsppb a.active { + color: black; + border: 1px solid #666; + background: #FFFF99; +} + +span#itemsppt a.active:hover, span#itemsppb a.active:hover { + color: black; + border: 1px solid #333; + background: #FFFF99; +} + +.itemsppform { + margin: 0px; + display:inline; + padding: 0px; +} + +.itemsppformtext { + margin: 0px; + display:inline; + padding: 0px; + width: 40px; +} + +.itemsppformchosen { + color: black; + border: 1px solid #666; + background: #FFFF99; +} + + +ul.versions li{ +display: inline; +list-style: none; +} + + +/* Used in the api */ +.List-contents { + padding-bottom: 5px; + padding-left: 15px; + line-height: 150%; + /* background-image: url('/static/styles/images/blksquare.gif'); + background-repeat: no-repeat; + background-position: 0 .2em; */ +} + +.List-entry { + padding-bottom: 5px; + padding-left: 30px; + line-height: 150%; + /* background-image: url('/static/styles/images/blkdiamond.gif'); + background-repeat: no-repeat; + background-position: 0.8em 0.4em; + background-size: 12px; */ +} + +.Section { + font-size: 1.5em; + font-weight: bold; + margin-top: 10px; + padding: 10px 0px; +} + +/*List of Datsets */ +.dataset_list { + margin: 0px 0px 20px 0px; + clear: left;} + +.dataset_list tr { + margin: 0px 10px 10px 10px; +} + +.dataset_list td { + padding: 0px 0px 10px 10px; +} + +.dataset_list li { + list-style-type: square; + list-style-image: url('/static/styles/images/blksquare.gif'); + list-style-position: outside; +} + +/*List of silos */ +.silo_list { + margin: 10px; + padding: 5px; +} + +.silo_list ul { + margin-left: 1em; + padding-left: 0; + margin-top: -10px; +} + +.silo_list li { + list-style-type: square; + list-style-image: url('/static/styles/images/blksquare.gif'); + list-style-position: outside; + padding-top: 10px; +} + +.silo_list li a { + padding: 10px; +} + +/* .silo_list ul li:before { + content: "\00BB \0020"; +} */ + +/* Data package */ +#editor_view { + float: right; + background: #FDFEFE; + /* filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fdfefe', endColorstr='#f4f7fa'); + background: -webkit-gradient(linear, left top, left bottom, from(#FDFEFE), to(#F4F7FA)); + background: -moz-linear-gradient(top, #FDFEFE, #F4F7FA); */ + border: 1px solid #DFE2E4; + -moz-border-radius: 5px; + -webkit-border-radius: 5px; + border-radius: 5px; + padding: 0px; + clear: both; + margin: 0px 20px 10px 10px; + text-decoration: none; + +} + +#editor_view ul { + margin: 0px; + padding: 5px 2px; +} + +#editor_view h2{ + + background: #002147; +/* filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#002147', endColorstr='#DDDDDD'); +background: -webkit-gradient(linear, left top, left bottom, from(#002147), to(#DDDDDD)); +background: -moz-linear-gradient(top, #002147, #DDDDDD); */ +color: white; +font-size: 1.3em; + margin: 0px; + padding: 3px; + text-align: center; + +} + +#editor_view a { + font-weight: normal; + margin-left: 5px; +} + +#editor_view a.current { + font-weight: bold; +} + +#editor_view img { + vertical-align: middle; + margin-right: 5px; +} + +.dataset_info { + margin: 10px 10px 10px 20px; + padding-bottom: 10px; +} + +.dataset_info a { + font-weight: normal; + margin-left: 5px; +} + +.dataset_info a.current { + font-weight: bold; +} + +.dataset_info img { + vertical-align: middle; +} + +.edit_section { + clear: both; + margin: 20px; + padding: 10px 20px; + border: 2px outset #333; +} + +.edit_section div.top{ + float: right; + margin-right: 0px; + margin-top: -20px; + position: relative; +} + +.edit_section img{ + vertical-align: bottom; +} + +.form_helper { + margin-left: 20px; + margin-bottom: 10px; + max-width: 100%; + background-image: url('/static/styles/images/help-icon-16.png'); + background-repeat: no-repeat; + background-position: 0 0; + padding-left: 20px; + min-height: 20px; +} + +.form_info { + margin-left: 20px; + margin-bottom: 10px; + max-width: 100%; + background-image: url('/static/styles/images/info-icon-16.png'); + background-repeat: no-repeat; + background-position: 0 0; + padding-left: 20px; + min-height: 20px; +} + +.metadata td div { + /*max-height: 150px; + overflow: auto;*/ +} + +div.metadata { + margin-bottom: 10px; + line-height: 1.5em; +} + +ul.metadata li { + margin-bottom: 5px; + line-height: 1.5em; + display: block; +} + +pre { + white-space: pre-wrap; /* css-3 */ + white-space: -moz-pre-wrap !important; /* Mozilla, since 1999 */ + white-space: -pre-wrap; /* Opera 4-6 */ + white-space: -o-pre-wrap; /* Opera 7 */ + word-wrap: break-word; /* Internet Explorer 5.5+ */ +} + +.httperrorcode { + float: left; + margin-left: 10px; + width: 200px; +} + +.httperrorcode h1{ + font-size: 5em; + font-weight: bold; + margin-top: 0px; +} + +.httperrortitle { + font-family: courier; + font-size: 2em; + font-weight: bold; + margin: 0px; +} + +.httperror { + float: left; + margin-left: 10px; + margin-top: 10px; +} + +.httperror h3{ + font-weight: bold; + margin-top: 0px; +} + +/* Admin section */ +.admin_list h4 { + display:inline; + margin-right: 5px; +} + +.admin_list a { + margin-right: 10px; +} + +form div.role { + width: 100%; + max-width: 100%; +} + +form.newitem label{ + width:100%; + text-align:left; + padding-bottom: 5px; +} + + +div.newPackage { +background: #FDFEFE;filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fdfefe', endColorstr='#f4f7fa'); +background: -webkit-gradient(linear, left top, left bottom, from(#FDFEFE), to(#F4F7FA)); +background: -moz-linear-gradient(top, #FDFEFE, #F4F7FA); +border: 1px solid #DFE2E4; +border-bottom-width: 3px; +-moz-border-radius: 5px; +-webkit-border-radius: 5px; +border-radius: 5px; +clear: both; +color: black; +margin: 0px 0px; +padding: 5px; + + +} + +div.newPackage h2{ + +margin: 0px; +} + +/* hide skipLinks off-screen */ +ul.skipLinks a{ + +font-weight: bold; +position: absolute; +top: -9000px; + +} + +/* show skipLinks link on focus */ +ul.skipLinks a:focus{ +position: relative; +left: 300px; +top: 0px; +} + +#accessStatusForm fieldset{ + max-width: 90%; +} + +#emu { + width: 250px; +} + +.unpack_endpoints form label { + width: 25em; +} + +.unpackForm fieldset { + max-width: 90%; +} + +.manifest-form form label { + width: 90%; + text-align: left; +} + +.displayLabel { + font-weight: bold; + display: inline; + width: 10em; + float: left; + text-align: right; + margin-right: 5px; + padding: 0px 5px; + text-transform: none; +} + +.displayValues { + margin-left: 10em; +} + +.siloList tr { + border-bottom: 0.12em solid #ccc; +} + +.siloList td { + padding: 10px; +} + +.siloList a { + font-weight: bold; + font-size: 1.2em; + margin-bottom: 5px; +} diff --git a/rdfdatabank/public/static/styles/chimpanzee.css b/rdfdatabank/public/static/styles/chimpanzee.css new file mode 100644 index 0000000..69fd288 --- /dev/null +++ b/rdfdatabank/public/static/styles/chimpanzee.css @@ -0,0 +1,52 @@ + + + +/* +### this is styles/chimpanzee.css ### + + + +This stylesheet is part of a set of stylesheets intended to provide progressive enhancement of css styles based on device capabilities. + +Here is an outline of each stylesheet: + +### basic.css ### This stylesheet establishes some basic style rules that work on all devices. + +### smartphone.css ### styles for more advanced handheld devices. + +### full.css ### styles for 'full screen' devices (desktops, laptops, etc.). + +### ie.css ### IE specific styles (ie.css also imports full.css) + +### mainnav.css ### styles for the main nav in full mode (mainnav.css is imported by full.css) + +### homepage.css ### styles for the homepage in full mode (homepage.css is imported by full.css) + +### print.css ### styles for printing + +All stylesheets files are in the /styles directory. + + + + +*/ +div { +} + + + +/* for tesing media queries */ +ul.mqtest li.bonobo +{ +color: #333; +} + + + + +#page { + + width: 1000px; + +} + diff --git a/rdfdatabank/public/static/styles/ie.css b/rdfdatabank/public/static/styles/ie.css new file mode 100644 index 0000000..2016e76 --- /dev/null +++ b/rdfdatabank/public/static/styles/ie.css @@ -0,0 +1,41 @@ + + + +/* +### this is styles/ie.css ### + + + +This stylesheet is part of a set of stylesheets intended to provide progressive enhancement of css styles based on device capabilities. + +Here is an outline of each stylesheet: + +### basic.css ### This stylesheet establishes some basic style rules that work on all devices. + +### smartphone.css ### styles for more advanced handheld devices. + +### full.css ### styles for 'full screen' devices (desktops, laptops, etc.). + +### ie.css ### IE specific styles (ie.css also imports full.css) + +### mainnav.css ### styles for the main nav in full mode (mainnav.css is imported by full.css) + +### homepage.css ### styles for the homepage in full mode (homepage.css is imported by full.css) + +### print.css ### styles for printing + +All stylesheets files are in the /styles directory. + + + + +*/ + +@import url("squirrelMonkey.css"); +@import url("chimpanzee.css"); +/* for tesing media queries */ +ul.mqtest li.ie +{ +color: #333; +} + diff --git a/rdfdatabank/public/static/styles/images/blkdiamond.gif b/rdfdatabank/public/static/styles/images/blkdiamond.gif new file mode 100644 index 0000000..749fc02 Binary files /dev/null and b/rdfdatabank/public/static/styles/images/blkdiamond.gif differ diff --git a/rdfdatabank/public/static/styles/images/blksquare.gif b/rdfdatabank/public/static/styles/images/blksquare.gif new file mode 100644 index 0000000..39446dc Binary files /dev/null and b/rdfdatabank/public/static/styles/images/blksquare.gif differ diff --git a/rdfdatabank/public/static/styles/images/csv.png b/rdfdatabank/public/static/styles/images/csv.png new file mode 100644 index 0000000..c56ca8f Binary files /dev/null and b/rdfdatabank/public/static/styles/images/csv.png differ diff --git a/rdfdatabank/public/static/styles/images/datapackage-24.png b/rdfdatabank/public/static/styles/images/datapackage-24.png new file mode 100644 index 0000000..1fbb455 Binary files /dev/null and b/rdfdatabank/public/static/styles/images/datapackage-24.png differ diff --git a/rdfdatabank/public/static/styles/images/delete-icon-16.png b/rdfdatabank/public/static/styles/images/delete-icon-16.png new file mode 100644 index 0000000..2534823 Binary files /dev/null and b/rdfdatabank/public/static/styles/images/delete-icon-16.png differ diff --git a/rdfdatabank/public/static/styles/images/delete-icon-24.png b/rdfdatabank/public/static/styles/images/delete-icon-24.png new file mode 100644 index 0000000..66b8b4c Binary files /dev/null and b/rdfdatabank/public/static/styles/images/delete-icon-24.png differ diff --git a/rdfdatabank/public/static/styles/images/down_arrow.png b/rdfdatabank/public/static/styles/images/down_arrow.png new file mode 100644 index 0000000..24b2113 Binary files /dev/null and b/rdfdatabank/public/static/styles/images/down_arrow.png differ diff --git a/rdfdatabank/public/static/styles/images/down_arrow_black.png b/rdfdatabank/public/static/styles/images/down_arrow_black.png new file mode 100644 index 0000000..267e838 Binary files /dev/null and b/rdfdatabank/public/static/styles/images/down_arrow_black.png differ diff --git a/rdfdatabank/public/static/styles/images/down_arrow_blue.png b/rdfdatabank/public/static/styles/images/down_arrow_blue.png new file mode 100644 index 0000000..33806a4 Binary files /dev/null and b/rdfdatabank/public/static/styles/images/down_arrow_blue.png differ diff --git a/rdfdatabank/public/static/styles/images/download_icon_24.png b/rdfdatabank/public/static/styles/images/download_icon_24.png new file mode 100644 index 0000000..204b73d Binary files /dev/null and b/rdfdatabank/public/static/styles/images/download_icon_24.png differ diff --git a/rdfdatabank/public/static/styles/images/download_icon_32.png b/rdfdatabank/public/static/styles/images/download_icon_32.png new file mode 100644 index 0000000..1ee8d36 Binary files /dev/null and b/rdfdatabank/public/static/styles/images/download_icon_32.png differ diff --git a/rdfdatabank/public/static/styles/images/download_icon_48.png b/rdfdatabank/public/static/styles/images/download_icon_48.png new file mode 100644 index 0000000..f8dd10c Binary files /dev/null and b/rdfdatabank/public/static/styles/images/download_icon_48.png differ diff --git a/rdfdatabank/public/static/styles/images/download_zip_icon_24.png b/rdfdatabank/public/static/styles/images/download_zip_icon_24.png new file mode 100755 index 0000000..9ce7ad0 Binary files /dev/null and b/rdfdatabank/public/static/styles/images/download_zip_icon_24.png differ diff --git a/rdfdatabank/public/static/styles/images/download_zip_icon_32.png b/rdfdatabank/public/static/styles/images/download_zip_icon_32.png new file mode 100755 index 0000000..60acde7 Binary files /dev/null and b/rdfdatabank/public/static/styles/images/download_zip_icon_32.png differ diff --git a/rdfdatabank/public/static/styles/images/download_zip_icon_48.png b/rdfdatabank/public/static/styles/images/download_zip_icon_48.png new file mode 100755 index 0000000..063c0d8 Binary files /dev/null and b/rdfdatabank/public/static/styles/images/download_zip_icon_48.png differ diff --git a/rdfdatabank/public/static/styles/images/file-add-icon-24.png b/rdfdatabank/public/static/styles/images/file-add-icon-24.png new file mode 100644 index 0000000..1f77aee Binary files /dev/null and b/rdfdatabank/public/static/styles/images/file-add-icon-24.png differ diff --git a/rdfdatabank/public/static/styles/images/file-new-icon-24.png b/rdfdatabank/public/static/styles/images/file-new-icon-24.png new file mode 100644 index 0000000..64f18c7 Binary files /dev/null and b/rdfdatabank/public/static/styles/images/file-new-icon-24.png differ diff --git a/rdfdatabank/public/static/styles/images/fminus.png b/rdfdatabank/public/static/styles/images/fminus.png new file mode 100644 index 0000000..c41cbae Binary files /dev/null and b/rdfdatabank/public/static/styles/images/fminus.png differ diff --git a/rdfdatabank/public/static/styles/images/fplus.png b/rdfdatabank/public/static/styles/images/fplus.png new file mode 100644 index 0000000..8201a63 Binary files /dev/null and b/rdfdatabank/public/static/styles/images/fplus.png differ diff --git a/rdfdatabank/public/static/styles/images/go-up-icon-24.png b/rdfdatabank/public/static/styles/images/go-up-icon-24.png new file mode 100644 index 0000000..ad51a4d Binary files /dev/null and b/rdfdatabank/public/static/styles/images/go-up-icon-24.png differ diff --git a/rdfdatabank/public/static/styles/images/help-icon-16.png b/rdfdatabank/public/static/styles/images/help-icon-16.png new file mode 100644 index 0000000..0aa5179 Binary files /dev/null and b/rdfdatabank/public/static/styles/images/help-icon-16.png differ diff --git a/rdfdatabank/public/static/styles/images/help-icon-24.png b/rdfdatabank/public/static/styles/images/help-icon-24.png new file mode 100644 index 0000000..be80963 Binary files /dev/null and b/rdfdatabank/public/static/styles/images/help-icon-24.png differ diff --git a/rdfdatabank/public/static/styles/images/icons/breadcrumb-arrow.png b/rdfdatabank/public/static/styles/images/icons/breadcrumb-arrow.png new file mode 100644 index 0000000..d88189b Binary files /dev/null and b/rdfdatabank/public/static/styles/images/icons/breadcrumb-arrow.png differ diff --git a/rdfdatabank/public/static/styles/images/info-icon-16.png b/rdfdatabank/public/static/styles/images/info-icon-16.png new file mode 100644 index 0000000..64f28b5 Binary files /dev/null and b/rdfdatabank/public/static/styles/images/info-icon-16.png differ diff --git a/rdfdatabank/public/static/styles/images/json.png b/rdfdatabank/public/static/styles/images/json.png new file mode 100644 index 0000000..a62a4c0 Binary files /dev/null and b/rdfdatabank/public/static/styles/images/json.png differ diff --git a/rdfdatabank/public/static/styles/images/link.png b/rdfdatabank/public/static/styles/images/link.png new file mode 100644 index 0000000..7501de1 Binary files /dev/null and b/rdfdatabank/public/static/styles/images/link.png differ diff --git a/rdfdatabank/public/static/styles/images/page-edit-icon-24.png b/rdfdatabank/public/static/styles/images/page-edit-icon-24.png new file mode 100644 index 0000000..8c8fca7 Binary files /dev/null and b/rdfdatabank/public/static/styles/images/page-edit-icon-24.png differ diff --git a/rdfdatabank/public/static/styles/images/silo-add-icon-24.png b/rdfdatabank/public/static/styles/images/silo-add-icon-24.png new file mode 100644 index 0000000..94fa435 Binary files /dev/null and b/rdfdatabank/public/static/styles/images/silo-add-icon-24.png differ diff --git a/rdfdatabank/public/static/styles/images/silo-delete-icon-24.png b/rdfdatabank/public/static/styles/images/silo-delete-icon-24.png new file mode 100644 index 0000000..56866db Binary files /dev/null and b/rdfdatabank/public/static/styles/images/silo-delete-icon-24.png differ diff --git a/rdfdatabank/public/static/styles/images/silo-edit-icon-16.png b/rdfdatabank/public/static/styles/images/silo-edit-icon-16.png new file mode 100644 index 0000000..c2913ce Binary files /dev/null and b/rdfdatabank/public/static/styles/images/silo-edit-icon-16.png differ diff --git a/rdfdatabank/public/static/styles/images/silo-edit-icon-2-24.png b/rdfdatabank/public/static/styles/images/silo-edit-icon-2-24.png new file mode 100644 index 0000000..f825b8a Binary files /dev/null and b/rdfdatabank/public/static/styles/images/silo-edit-icon-2-24.png differ diff --git a/rdfdatabank/public/static/styles/images/silo-edit-icon-24.png b/rdfdatabank/public/static/styles/images/silo-edit-icon-24.png new file mode 100644 index 0000000..63452f6 Binary files /dev/null and b/rdfdatabank/public/static/styles/images/silo-edit-icon-24.png differ diff --git a/rdfdatabank/public/static/styles/images/silo-view-icon-16.png b/rdfdatabank/public/static/styles/images/silo-view-icon-16.png new file mode 100644 index 0000000..e3cf4a4 Binary files /dev/null and b/rdfdatabank/public/static/styles/images/silo-view-icon-16.png differ diff --git a/rdfdatabank/public/static/styles/images/silo-view-icon-24.png b/rdfdatabank/public/static/styles/images/silo-view-icon-24.png new file mode 100644 index 0000000..a0e3e0c Binary files /dev/null and b/rdfdatabank/public/static/styles/images/silo-view-icon-24.png differ diff --git a/rdfdatabank/public/static/styles/images/state-icon-24.png b/rdfdatabank/public/static/styles/images/state-icon-24.png new file mode 100644 index 0000000..07838e3 Binary files /dev/null and b/rdfdatabank/public/static/styles/images/state-icon-24.png differ diff --git a/rdfdatabank/public/static/styles/images/unzip-icon-24.png b/rdfdatabank/public/static/styles/images/unzip-icon-24.png new file mode 100644 index 0000000..ed26eb3 Binary files /dev/null and b/rdfdatabank/public/static/styles/images/unzip-icon-24.png differ diff --git a/rdfdatabank/public/static/styles/images/unzip-icon-32.png b/rdfdatabank/public/static/styles/images/unzip-icon-32.png new file mode 100644 index 0000000..1208634 Binary files /dev/null and b/rdfdatabank/public/static/styles/images/unzip-icon-32.png differ diff --git a/rdfdatabank/public/static/styles/images/up_arrow.png b/rdfdatabank/public/static/styles/images/up_arrow.png new file mode 100644 index 0000000..c1700eb Binary files /dev/null and b/rdfdatabank/public/static/styles/images/up_arrow.png differ diff --git a/rdfdatabank/public/static/styles/images/up_arrow_black.png b/rdfdatabank/public/static/styles/images/up_arrow_black.png new file mode 100644 index 0000000..bd1aca1 Binary files /dev/null and b/rdfdatabank/public/static/styles/images/up_arrow_black.png differ diff --git a/rdfdatabank/public/static/styles/images/up_arrow_blue.png b/rdfdatabank/public/static/styles/images/up_arrow_blue.png new file mode 100644 index 0000000..7dd2b80 Binary files /dev/null and b/rdfdatabank/public/static/styles/images/up_arrow_blue.png differ diff --git a/rdfdatabank/public/static/styles/images/user-add-icon-24.png b/rdfdatabank/public/static/styles/images/user-add-icon-24.png new file mode 100644 index 0000000..47aa4a3 Binary files /dev/null and b/rdfdatabank/public/static/styles/images/user-add-icon-24.png differ diff --git a/rdfdatabank/public/static/styles/images/user-delete-icon-16.png b/rdfdatabank/public/static/styles/images/user-delete-icon-16.png new file mode 100644 index 0000000..6020d8e Binary files /dev/null and b/rdfdatabank/public/static/styles/images/user-delete-icon-16.png differ diff --git a/rdfdatabank/public/static/styles/images/user-delete-icon-24.png b/rdfdatabank/public/static/styles/images/user-delete-icon-24.png new file mode 100644 index 0000000..6bc9e91 Binary files /dev/null and b/rdfdatabank/public/static/styles/images/user-delete-icon-24.png differ diff --git a/rdfdatabank/public/static/styles/images/user-edit-icon-16.png b/rdfdatabank/public/static/styles/images/user-edit-icon-16.png new file mode 100644 index 0000000..25ac358 Binary files /dev/null and b/rdfdatabank/public/static/styles/images/user-edit-icon-16.png differ diff --git a/rdfdatabank/public/static/styles/images/user-edit-icon-24.png b/rdfdatabank/public/static/styles/images/user-edit-icon-24.png new file mode 100644 index 0000000..6629350 Binary files /dev/null and b/rdfdatabank/public/static/styles/images/user-edit-icon-24.png differ diff --git a/rdfdatabank/public/static/styles/images/user-group-add-24.png b/rdfdatabank/public/static/styles/images/user-group-add-24.png new file mode 100644 index 0000000..62a98a1 Binary files /dev/null and b/rdfdatabank/public/static/styles/images/user-group-add-24.png differ diff --git a/rdfdatabank/public/static/styles/images/user-group-edit-24.png b/rdfdatabank/public/static/styles/images/user-group-edit-24.png new file mode 100644 index 0000000..23bdbba Binary files /dev/null and b/rdfdatabank/public/static/styles/images/user-group-edit-24.png differ diff --git a/rdfdatabank/public/static/styles/images/user-group-icon-2-24.png b/rdfdatabank/public/static/styles/images/user-group-icon-2-24.png new file mode 100644 index 0000000..5541f2c Binary files /dev/null and b/rdfdatabank/public/static/styles/images/user-group-icon-2-24.png differ diff --git a/rdfdatabank/public/static/styles/images/user-group-icon-24.png b/rdfdatabank/public/static/styles/images/user-group-icon-24.png new file mode 100644 index 0000000..87ac666 Binary files /dev/null and b/rdfdatabank/public/static/styles/images/user-group-icon-24.png differ diff --git a/rdfdatabank/public/static/styles/images/user-group-rdelete-24.png b/rdfdatabank/public/static/styles/images/user-group-rdelete-24.png new file mode 100644 index 0000000..855657d Binary files /dev/null and b/rdfdatabank/public/static/styles/images/user-group-rdelete-24.png differ diff --git a/rdfdatabank/public/static/styles/images/user-icon-16.png b/rdfdatabank/public/static/styles/images/user-icon-16.png new file mode 100644 index 0000000..9809e00 Binary files /dev/null and b/rdfdatabank/public/static/styles/images/user-icon-16.png differ diff --git a/rdfdatabank/public/static/styles/images/user-icon-24.png b/rdfdatabank/public/static/styles/images/user-icon-24.png new file mode 100644 index 0000000..0f1a929 Binary files /dev/null and b/rdfdatabank/public/static/styles/images/user-icon-24.png differ diff --git a/rdfdatabank/public/static/styles/images/view_icon_24.png b/rdfdatabank/public/static/styles/images/view_icon_24.png new file mode 100644 index 0000000..9d555f9 Binary files /dev/null and b/rdfdatabank/public/static/styles/images/view_icon_24.png differ diff --git a/rdfdatabank/public/static/styles/images/view_icon_32.png b/rdfdatabank/public/static/styles/images/view_icon_32.png new file mode 100644 index 0000000..343da14 Binary files /dev/null and b/rdfdatabank/public/static/styles/images/view_icon_32.png differ diff --git a/rdfdatabank/public/static/styles/images/view_icon_48.png b/rdfdatabank/public/static/styles/images/view_icon_48.png new file mode 100644 index 0000000..a274fce Binary files /dev/null and b/rdfdatabank/public/static/styles/images/view_icon_48.png differ diff --git a/rdfdatabank/public/static/styles/images/view_zip_icon_24.png b/rdfdatabank/public/static/styles/images/view_zip_icon_24.png new file mode 100755 index 0000000..867c43d Binary files /dev/null and b/rdfdatabank/public/static/styles/images/view_zip_icon_24.png differ diff --git a/rdfdatabank/public/static/styles/images/view_zip_icon_32.png b/rdfdatabank/public/static/styles/images/view_zip_icon_32.png new file mode 100755 index 0000000..0d3aca8 Binary files /dev/null and b/rdfdatabank/public/static/styles/images/view_zip_icon_32.png differ diff --git a/rdfdatabank/public/static/styles/images/view_zip_icon_48.png b/rdfdatabank/public/static/styles/images/view_zip_icon_48.png new file mode 100755 index 0000000..4641903 Binary files /dev/null and b/rdfdatabank/public/static/styles/images/view_zip_icon_48.png differ diff --git a/rdfdatabank/public/static/styles/marmoset.css b/rdfdatabank/public/static/styles/marmoset.css new file mode 100644 index 0000000..15d641a --- /dev/null +++ b/rdfdatabank/public/static/styles/marmoset.css @@ -0,0 +1,88 @@ + + + +/* +### this is styles/marmoset.css ### + + + +This stylesheet is part of a set of stylesheets intended to provide progressive enhancement of css styles based on device capabilities. + +Here is an outline of each stylesheet: + +### basic.css ### This stylesheet establishes some basic style rules that work on all devices. + +### smartphone.css ### styles for more advanced handheld devices. + +### full.css ### styles for 'full screen' devices (desktops, laptops, etc.). + +### ie.css ### IE specific styles (ie.css also imports full.css) + +### mainnav.css ### styles for the main nav in full mode (mainnav.css is imported by full.css) + +### homepage.css ### styles for the homepage in full mode (homepage.css is imported by full.css) + +### print.css ### styles for printing + +All stylesheets files are in the /styles directory. + + + + +*/ +/* for tesing media queries */ + + + + + +header.pageHeader +{ +margin-bottom: 0px; + +} + + + + + +ul.mqtest li +{ +color: #eee; +} +ul.mqtest li.marmoset +{ +color: #333; +} + +header.pageHeader +{ +margin-top: 0px; +} + + +/* give some padding to content containers */ +.main, +footer.site +{ +padding: 3px; +} + + +.main +{ +background-color: #fff; +padding-top: 10px; +padding-bottom: 10px; +margin-bottom: 0px; +} + + +footer.site +{ + +margin-top: 0px; +} + + + diff --git a/rdfdatabank/public/static/styles/print.css b/rdfdatabank/public/static/styles/print.css new file mode 100644 index 0000000..c6e92bc --- /dev/null +++ b/rdfdatabank/public/static/styles/print.css @@ -0,0 +1,39 @@ + + + +/* +### this is styles/print.css ### + + + +This stylesheet is part of a set of stylesheets intended to provide progressive enhancement of css styles based on device capabilities. + +Here is an outline of each stylesheet: + +### basic.css ### This stylesheet establishes some basic style rules that work on all devices. + +### smartphone.css ### styles for more advanced handheld devices. + +### full.css ### styles for 'full screen' devices (desktops, laptops, etc.). + +### ie.css ### IE specific styles (ie.css also imports full.css) + +### mainnav.css ### styles for the main nav in full mode (mainnav.css is imported by full.css) + +### homepage.css ### styles for the homepage in full mode (homepage.css is imported by full.css) + +### print.css ### styles for printing + +All stylesheets files are in the /styles directory. + + + + +*/ +/* for tesing media queries */ + +ul.mqtest li.print +{ +color: #333; +} + diff --git a/rdfdatabank/public/static/styles/reset.css b/rdfdatabank/public/static/styles/reset.css new file mode 100644 index 0000000..4ea402f --- /dev/null +++ b/rdfdatabank/public/static/styles/reset.css @@ -0,0 +1,153 @@ + + + +/* +### this is styles/reset.css ### + + + +This stylesheet is part of a set of stylesheets intended to provide progressive enhancement of css styles based on device capabilities. + +Here is an outline of each stylesheet: + +### basic.css ### This stylesheet establishes some basic style rules that work on all devices. + +### smartphone.css ### styles for more advanced handheld devices. + +### full.css ### styles for 'full screen' devices (desktops, laptops, etc.). + +### ie.css ### IE specific styles (ie.css also imports full.css) + +### mainnav.css ### styles for the main nav in full mode (mainnav.css is imported by full.css) + +### homepage.css ### styles for the homepage in full mode (homepage.css is imported by full.css) + +### print.css ### styles for printing + +All stylesheets files are in the /styles directory. + + + + +*/ + + +/* for tesing media queries */ + + + + + +/* +html5doctor.com Reset Stylesheet +v1.6.1 +Last Updated: 2010-09-17 +Author: Richard Clark - http://richclarkdesign.com +Twitter: @rich_clark +*/ + +html, body, div, span, object, iframe, +h1, h2, h3, h4, h5, h6, p, blockquote, pre, +abbr, address, cite, code, +del, dfn, em, img, ins, kbd, q, samp, +small, strong, sub, sup, var, +b, i, +dl, dt, dd, ol, ul, li, +fieldset, form, label, legend, +table, caption, tbody, tfoot, thead, tr, th, td, +article, aside, canvas, details, figcaption, figure, +footer, header, hgroup, menu, nav, section, summary, +time, mark, audio, video { + margin:0px; + padding:0; + border:0; + outline:0; + font-size:100%; + vertical-align:baseline; + background:transparent; + +} + + +body { + line-height:1; + +} + +article,aside,details,figcaption,figure, +footer,header,hgroup,menu,nav,section { + display:block; +} + +nav ul { + list-style:none; +} + +blockquote, q { + quotes:none; +} + +blockquote:before, blockquote:after, +q:before, q:after { + content:''; + content:none; +} + +a { + margin:0; + padding:0; + font-size:100%; + vertical-align:baseline; + background:transparent; +} + +/* change colours to suit your needs */ +ins { + background-color:#ff9; + color:#000; + text-decoration:none; +} + +/* change colours to suit your needs */ +mark { + background-color:#ff9; + color:#000; + font-style:italic; + font-weight:bold; +} + +del { + text-decoration: line-through; +} + +abbr[title], dfn[title] { + border-bottom:1px dotted; + cursor:help; +} + +table { + border-collapse:collapse; + border-spacing:0; +} + +/* change border colour to suit your needs */ +hr { + display:block; + height:1px; + border:0; + border-top:1px solid #cccccc; + margin:1em 0; + padding:0; +} + +input, select { + vertical-align:middle; +} + + + +ul.mqtest li.reset +{ +color: #333; +} + diff --git a/rdfdatabank/public/static/styles/silverback.css b/rdfdatabank/public/static/styles/silverback.css new file mode 100644 index 0000000..621b4b9 --- /dev/null +++ b/rdfdatabank/public/static/styles/silverback.css @@ -0,0 +1,46 @@ + + + +/* +### this is styles/silverback.css ### + + + +This stylesheet is part of a set of stylesheets intended to provide progressive enhancement of css styles based on device capabilities. + +Here is an outline of each stylesheet: + +### basic.css ### This stylesheet establishes some basic style rules that work on all devices. + +### smartphone.css ### styles for more advanced handheld devices. + +### full.css ### styles for 'full screen' devices (desktops, laptops, etc.). + +### ie.css ### IE specific styles (ie.css also imports full.css) + +### mainnav.css ### styles for the main nav in full mode (mainnav.css is imported by full.css) + +### homepage.css ### styles for the homepage in full mode (homepage.css is imported by full.css) + +### print.css ### styles for printing + +All stylesheets files are in the /styles directory. + + + + +*/ +/* for tesing media queries */ +ul.mqtest li.silverback +{ +color: #333; +} + + +#page { + + width: 1500px; + +} + + diff --git a/rdfdatabank/public/static/styles/squirrelMonkey.css b/rdfdatabank/public/static/styles/squirrelMonkey.css new file mode 100644 index 0000000..a3ec6be --- /dev/null +++ b/rdfdatabank/public/static/styles/squirrelMonkey.css @@ -0,0 +1,144 @@ + +/* +### this is styles/squirrelMonkey.css ### + +*/ + + + +#page { + + margin: 0px auto; + +} + +#search { + +padding: 0px; +position: absolute; +right: 6px; +text-align: right; +top: -3px; +} + +#search div +{ +display: inline; +padding: 0px; +margin: 0px; +} + +#search label.searchterm +{ + +position: absolute; +top: -9000px; +} + + +/* drop shadow on main page blocks */ +header.pageHeader, +.main, +footer.site { + + + -webkit-box-shadow: #cccccc 0.5em 0.5em 0.5em; /* Chrome Safari */ + -moz-box-shadow: #cccccc 0.5em 0.5em 0.5em; /* Firefox F3.5+ */ + box-shadow: #cccccc 0.5em 0.5em 0.5em; /* Opera 10.5, IE 9.0 */ + + +} + + + + + +.content .wrapper { + background-color: #fff; + +} + +.content footer { + + clear: both; +} + +.content a +{ +text-decoration: underline; +} + + +.main +{ + border: 1px solid #dfe2e4; + margin: 10px 0px; + padding: 10px; +} + +.content footer section +{ + + + float: right; + width: 49%; +} + +footer.site +{ +margin: 20px 0px; + +clear: both; + + +} + + + +footer.site +{ +padding: 10px; +} + + + +footer li +{ +display: inline; +} + + + + +footer .copyright +{ +text-align: center; +padding: 10px; +} + +.contentMeta +{ + float: left; + width: 300px; +} + +div.newPackage { + +width: 280px; +float: right; + +} + +div.newPackage h2{ + +text-align: center; + +} + + +div.newPackage .form_helper { +margin: 0px 0px 10px 0px; + +} + + diff --git a/rdfdatabank/templates/about.html b/rdfdatabank/templates/about.html new file mode 100644 index 0000000..b57d65e --- /dev/null +++ b/rdfdatabank/templates/about.html @@ -0,0 +1,15 @@ +# -*- coding: utf-8 -*- +<%inherit file="/base.html" /> +<%def name="head_tags()"> + About DataBank + +

About Databank

+
+

Databank is being developed by the Bodleian Digital Library Systems and Services of the University of Oxford as a part of the Dataflow project

+ +

DataFlow is creating a two-stage data management infrastructure that makes it easy for you and your research group to work with, annotate, publish, and permanently store your research data. You manage this locally using your own instance of DataStage, while allowing your institution to deploy DataBank easily to preserve and publish your most valuable data packages.

+ +

Published data packages have assigned DOIs to make them citable and to gain you academic credit.

+ +

Want to test-drive the system? Help develop it? Learn more and Join us!

+
diff --git a/rdfdatabank/templates/add_users_to_silo.html b/rdfdatabank/templates/add_users_to_silo.html new file mode 100644 index 0000000..a60783a --- /dev/null +++ b/rdfdatabank/templates/add_users_to_silo.html @@ -0,0 +1,23 @@ +# -*- coding: utf-8 -*- +
+
+ + +
+% if c.roles and "admin" in c.roles: +
+ + +
+% endif +
+ + +
+
+ + +
+
+
+

diff --git a/rdfdatabank/templates/admin_api.html b/rdfdatabank/templates/admin_api.html new file mode 100644 index 0000000..2c8697e --- /dev/null +++ b/rdfdatabank/templates/admin_api.html @@ -0,0 +1,488 @@ +# -*- coding: utf-8 -*- +
+

Admin

+

API call to

    +
  • Obtain a list silos that are owned by user
  • +
  • Modify the silo metadata
  • +
  • Delete the silo
  • +
  • Create usernames and assign passwords
  • +
  • update passwords
  • +
  • Add/remove users from a silo
  • +
+

Sections

+
    +
  1. /admin
  2. +
  3. /{silo name}/admin
  4. +
  5. /{silo name}/register
  6. +
+ +
+
+
/admin
+ +
+
+

API call to obtain a list of silos owned by the user, create new silos, delete silo, manage users and alter silo metadata

+

Controller: datasets     action: siloview

+
+

GET: Obtain a list of datasets in a silo

+
Returns
+
+
401 if not a valid user
+
403 if not authorized
+
Accept:text/html
+
Returns the ids of each dataset in the silo, along with a form for changing the embargo information and deleting the dataset. A form for dataset creation is also available.
+
Accept: text/plain, application/json
+
200 OK
+
Returns a JSON-encoded list of dataset ids in that silo along with the embargo information for each dataset
+
The file datasetsInSiloInformation.txt contains an example of the data returned (data_returned)
+
Accept:*/*, default
+
Returns text/HTML listing the ids of each dataset, along with a form for changing the embargo information and deleting the dataset. A form for dataset creation is also available.
+
+
+
+

POST: Create new dataset

+
Parameters
+
+ + + + + +
id {id to create}
embargoed {true|false} (optional).
If the parameter is not supplied, a default value of false will be used.
embargoed_until {ISO8601 date} (optional).
If embargoed = true and no date has been supplied, a default time delta of 70 years will be used
title{(Optional)}
+
+
Returns
+
+
+ 409 if dataset already exists
+ 401 If not a valid user
+ 403 if not authorized
+ 403 if the name of the dataset does not confirm to the naming rule (name can contain only the followin characters 0-9a-zA-Z-_:
+
+
Accept: text/html
+
302 to splash page for newly created dataset
+
Accept: text/plain, application/json
+
201 created
+
Accept: */*, default
+
Returns text/plain, 201 created
+
+
+
+

PUT, DELETE: NOOP

+
+ + +
+
+
/{silo name}/datasets/{id}
+ +
+
+

API call to obtain information about the dataset, create a dataset, modify the datasets embargo information, post files to the dataset and delete the dataset. All of this is performed on the latest version of the dataset.

+

Controller: datasets     action: datasetview

+
+

GET: Obtain information about the dataset

+
Returns
+
+
404 if dataset is not available
+
401 if not authenticated and dataset is under embargo
+
403 if not authorized and dataset is under embargo
+
Accept: text/html
+
returns HTML splash page for dataset id. The page contains the following: +
    +
  • Readme text
  • +
  • List of files / folders at the top level
  • +
  • Dataset's embargo state
  • +
  • Manifest data
  • +
  • If authenticated and authorized user:
    +
      +
    • View options: Choice to view page as anonymous user or as editor
    • +
    • Form to modify manifest
    • +
    • Form to upload file
    • +
    • List of dataset unpack endpoints, to unpack file to new dataset
    • +
    +
  • +
+
+
Accept: application/json
+
200 OK
+
Returns a JSON-encoded hash/dict. The keys map to the following : +
    +
  • Readme text - data_returned["readme_text"]
  • +
  • Dataset's embargo information data_returned["embargos"]
  • +
  • If user is authenticated and authorized (editor = true) / anonymous (editor = false) - data_returned["editor"]
  • +
  • List of zipfiles - data_returned["zipfiles"]
  • +
  • If user is authenticated and authorized, the view chosen by the user - data_returned["view"]
  • +
  • Manifest data in two forms - data_returned["manifest"] and data_returned["manifest_pretty"]
  • +
  • Hash dict of files / folders at the top level - data_returned["parts"][file_or_directory_name"]
    + If it is a file, the file stat information is also available. See http://docs.python.org/library/os.html#os.stat for more information.
    + If it is a directory, the dictionary value is empty as there is no stat information
  • +
+
+
The file datasetInformation.txt contains an example of the data returned (data_returned)
+
Accept: application/rdf+xml, text/xml
+
returns dataset's RDF manifest as RDF/XML
+
Accept: text/rdf+n3
+
returns dataset's RDF manifest as N3
+
Accept: application/x-turtle
+
Returns dataset's RDF manifest as Turtle
+
Accept: text/rdf+ntriples, text/rdf+nt
+
returns dataset's RDF manifest as ntriples
+
Accept: */*, default
+
returns text/HTML of splash page for dataset id, as described above
+
+
+
+

POST: Create new dataset. Dataset id doesn't exist. (As POST /{silo}/datasets id=...)

+
Parameters
+
+ + + + +
embargoed {true|false} (optional).
If the parameter is not supplied, a default value of false will be used.
embargoed_until {ISO8601 date} (optional).
If embargoed = true and no date has been supplied, a default time delta of 70 years will be used
title{(Optional)}
+
+
Returns
+
+
+ 401 If not a valid user
+ 403 if not authorized
+ 403 if the name of the dataset does not confirm to the naming rule (name can contain only the followin characters 0-9a-zA-Z-_:
+
+
Accept: text/html
+
302 to splash page for newly created dataset
+
Accept: text/plain, application/json
+
201 created
+
Accept: */*, default
+
Returns text/plain, 201 created
+
+
+
+

POST : change embargo information in dataset. On success, version is incremented.

+
+
TODO:
+
Redo this bit of code, removing the parameter embargo_change and just using embargoed and embargoed_until
+
+
Parameters
+
+ + + + +
embargo_changetrue (used as a convenience parameter for html forms)
embargoed{true|false}
embargoed_until{ISO8601 date} (optional).
If embargoed = true and no date has been supplied, a default time delta of 70 years will be used
+
+
Returns
+
+
401 if not a valid user
+
403 if not authorized
+
Accept: text/html
+
302 to splash page of dataset
+
Accept: text/plain, application/json
+
204 updated
+
Accept: */*, default
+
204 updated
+
+
+
+
+

POST: Upload file to root directory. On success, version is incremented.

+
Parameters
+
+ + + +
file Multipart-encoded (HTML) file upload
filename {Optional filename for upload}
+
+
Returns
+
+
401 if not a valid user
+
403 if not authorized
+
400 if filename contains ..
+
403 if filename is an existing directory in dataset. Cannot post a file to update a directory with a file.
+
400 if filename is manifest.rdf and cannot read / parse rdf in manifest
+
Accept: text/html
+
302 to splash page of dataset
+
Accept: text/plain
+
204 on update, if filename exists.
+
201 on creation, if filename does not exist
+
Accept: */*, default
+
Returns text/plain
+
204 on update, if filename exists.
+
201 on creation, if filename does not exist
+
+
+
+

POST: Text upload. Convenience function for HTML to update/create text files. On success, version is incremented.

+
Parameters
+
+ + + +
text {UTF text to store}
filename {desired filename}
+
+
Returns
+
+
401 if not a valid user
+
403 if not authorized
+
400 if filename contains ..
+
406 if the parameter filename is missing
+
403 if filename is an existing directory in dataset. Cannot post a file to update a directory with a file.
+
406 if the filename is manifet.rdf and cannot parse rdf
+
Accept: text/html
+
302 to splash page of dataset
+
Accept: text/plain
+
204 on update, if filename exists.
+
201 on creation, if filename does not exist
+
Accept: */*, default
+
Returns text/plain
+
204 on update, if filename exists.
+
201 on creation, if filename does not exist
+
+
+
+

DELETE Deletes the dataset id

+
+
Be Aware!
+
Delete currently deletes the dataset. This action cannot be undone and your data is lost following this action.
+
+
+
TODO:
+
Delete currently deletes the dataset. This SHOULD NOT happen. On delete, create a new version with just a stub in the manifest saying deleted. Also, this dataset should not be returned in the list of datasets and dataset create should not return a 409.
+
+
Returns
+
+
401 if not a valid user
+
403 if not authorized
+
404 if id doesn't exist
+
Accept: */*, default
+
Returns text/plain, 200 on success
+
+
+
+

PUT NOOP

+
+ + +
+
+
/{silo name}/datasets/{id}/version{#}
+ +
+
+

API call to obtain information about a particular version of the dataset

+

Controller: datasets     action: datasetview_vnum

+
+

GET: Obtain information about a particular version of the dataset

+
Returns
+
+
404 if dataset is not available
+
404 if version number # of dataset is not available
+
401 if not authenticated and dataset is under embargo
+
403 if not authorized and dataset is under embargo
+
Accept: text/html
+
returns HTML splash page for dataset id. The page contains the following: +
    +
  • Readme text
  • +
  • List of files / folders at the top level
  • +
  • Dataset's embargo state
  • +
  • Manifest data
  • +
+
+
Accept: application/json
+
200 OK
+
Returns a JSON-encoded hash/dict. The keys map to the following : +
    +
  • Readme text - data_returned["readme_text"]
  • +
  • Dataset's embargo information data_returned["embargos"]
  • +
  • If user is authenticated and authorized (editor = true) / anonymous (editor = false) - data_returned["editor"]
  • +
  • If user is authenticated and authorized, the view chosen by the user - data_returned["view"]
    (Note : this will always return empty as there is no edit possible for previous versions of the dataset)
  • +
  • Manifest data - data_returned["manifest_pretty"]
  • +
  • Hash dict of files / folders at the top level - data_returned["parts"][file_or_directory_name"]
    + If it is a file, the file stat information is also available. See http://docs.python.org/library/os.html#os.stat for more information.
    + If it is a directory, the dictionary value is empty as there is no stat information
  • +
+
+
The files + datasetInformation-version0.txt and + datasetInformation-version1.txt + contain an example of the data returned (data_returned) +
+
Accept: application/rdf+xml, text/xml
+
returns dataset's RDF manifest as RDF/XML
+
Accept: text/rdf+n3
+
returns dataset's RDF manifest as N3
+
Accept: application/x-turtle
+
Returns dataset's RDF manifest as Turtle
+
Accept: text/rdf+ntriples, text/rdf+nt
+
returns dataset's RDF manifest as ntriples
+
Accept: */*, default
+
returns text/HTML of splash page for dataset id, as described above
+
+
+ + +
+
+
/{silo name}/datasets/{id}/{subpath}
+ +
+
+

API call to view files / contents of a folder and add, update or delete a file. All of these actions are performed on the latest version of the dataset. +

+

Controller: datasets     action: itemview

+
+

GET: view the contents of subpath (subpath could point to a file or a folder)

+
Returns
+
+
404 if dataset is not available
+
401 if not authenticated and dataset is under embargo
+
403 if not authorized and dataset is under embargo
+
404 if subpath is not available
+
subpath is a file
+
+
Accept: */*, default
+
The file is served
+
+
subpath is a directory
+
+
Accept: text/html
+
HTML page listing the files within the given subpath and readme_text
+
Accept: text/plain, application/json
+
200 OK
+
Returns a JSON-encoded hash/dict. The keys map to the following : +
    +
  • Readme text - data_returned["readme_text"]
  • +
  • Hash dict of files / folders at subpath (just that level) - data_returned["parts"]["file_or_directory_name"]
    + If it is a file, the file stat information is also available. + See http://docs.python.org/library/os.html#os.stat for more information.
    + If it is a directory, the dictionary value is empty as there is no stat information
  • +
+
+
The file + datasetSubdirInformation.txt + contains an example of the data returned (data_returned) +
+
Accept: */*, default
+
HTML page listing the files within the given subpath and readme_text
+
+
+
+
+

PUT: used to add content to a file.
+ If the subpath points to manifest.rdf, the content is munged with exisitng metadata.
+ If the subpath does not exist, a file is created in the same name as the filename in path including all the intermediate directories in the subpath (using os.makedirs) and the contents are added to that file.
+ On success, version is incremented. +

+
Returns
+
+
401 if not authenticated
+
403 if not authorized
+
403 if subpath points to a directory - cannot put content onto an existing directory
+
400 if subpath contains ..
+
Accept: text/html
+
302 to the subpath, listing the files within the given subpath and readme_text
+
Accept: text/plain, application/json
+
204 Updated, if subpath did exist
+
201 created, if subpath did not exist
+
Accept: */*, default
+
Return text/plain. 201 on creation and 204 on update.
+
+
+
+

POST Upload a file within the subpath
+ If the subpath + filename parameter points to manifest.rdf, the content is munged with exisitng metadata.
+ If the subpath + filename parameter does not exist, the file is added as filename into subpath. The intermediate directories in the subpath (using os.makedirs) are created if they do not exist.
+ On success, version is incremented. +

+
Parameters
+
+ + + +
file Multipart-encoded (HTML) file upload
filename {Optional filename for upload}.
+ When provided, it is only used if subpath points to an existing directory.
+
+
Returns
+
+
401 if not authenticated
+
403 if not authorized
+
400 if subpath contains ..
+
403 if subpath + filename points to an existing directory
+
Accept: text/html
+
302 to the subpath, listing the file filename within the given subpath and readme_text
+
Accept: text/plain, application/json
+
204 Updated, if subpath did exist
+
201 created, if subpath did not exist
+
Accept: */*, default
+
Returns text/plain. 201 on creation and 204 on update.
+
+
+
+

DELETE: deletes files or directories within the dataset. It also performs recursive deletes (directories with sub-directories within them).
+ On success, version is incremented. +

+
Returns
+
+
401 if not authenticated
+
403 if not authorized
+
403 if subpath is manifest.rdf
+
Accept: */*, default
+
Returns text/plain. 200 OK on successful completion.
+
+
+ + +
+
+
/{silo name}/datasets/{id}[/{subpath}]/version{#}
+ +
+
+

API call to obtain a file or view the contents of a folder, for a particular version of the dataset

+

Controller: datasets     action: itemview

+
+

GET: Obtain files or view the contents of a directory for a particular version of the dataset.

+
Returns
+
+
404 if dataset, subpath or version number (#) is not available
+
401 if not authenticated and dataset is under embargo
+
403 if not authorized and dataset is under embargo
+
subpath is a file
+
+
Accept: */*, default
+
The file is served
+
+
subpath is a directory
+
+
Accept: text/html
+
HTML page listing the files within the given subpath and readme_text (pertaining to that version)
+
+
TODO:
+
The html display is not showing the files. Fix bug!
+
+
Accept: text/plain, application/json
+
200 OK
+
Returns a JSON-encoded hash/dict. The keys map to the following : +
    +
  • Readme text - data_returned["readme_text"]
  • +
  • Hash dict of files / folders at subpath (just that level) - data_returned["parts"]["file_or_directory_name"]
    + If it is a file, the file stat information is also available. + See http://docs.python.org/library/os.html#os.stat for more information.
    + If it is a directory, the dictionary value is empty as there is no stat information
  • +
+
+
The files + datasetSubdirInformation-version3.txt and + datasetSubdirInformation-version1.txt + contains an example of the data returned (data_returned) +
+
Accept: */*, default
+
HTML page listing the files within the given subpath and readme_text
+
+
+
+
+

POST, PUT, DELETE: NOOP

+
+ +
diff --git a/rdfdatabank/templates/admin_silos.html b/rdfdatabank/templates/admin_silos.html new file mode 100644 index 0000000..fbe80d8 --- /dev/null +++ b/rdfdatabank/templates/admin_silos.html @@ -0,0 +1,68 @@ +# -*- coding: utf-8 -*- +<%inherit file="/base.html" /> +<%def name="head_tags()"> + Admin Interface - List of Silos + + + +

Admin Interface

+ +
+

Edit options

+ +
+ +% if c.granary_list: +
+

List of silos managed by you

+ +
+% endif + +% if 'administrator' in c.ident['permissions']: + +% endif + + + diff --git a/rdfdatabank/templates/admin_siloview.html b/rdfdatabank/templates/admin_siloview.html index 5542d86..45f3bcf 100644 --- a/rdfdatabank/templates/admin_siloview.html +++ b/rdfdatabank/templates/admin_siloview.html @@ -1,15 +1,68 @@ # -*- coding: utf-8 -*- <%inherit file="/base.html" /> <%def name="head_tags()"> - List of Data Archives + Admin Interface - ${c.silo} + + -% if c.silo_name: -

Admin Archive ${c.silo_name}

+

Admin Interface - ${c.silo}

% if c.message:

${c.message}

% endif -
-

Delete this silo - NO UNDO!

-
-% endif + + +

Silo Metadata

+<%include file="/silo_metadata.html"/> + + + + + + + diff --git a/rdfdatabank/templates/admin_user.html b/rdfdatabank/templates/admin_user.html new file mode 100644 index 0000000..a9bda0f --- /dev/null +++ b/rdfdatabank/templates/admin_user.html @@ -0,0 +1,82 @@ +# -*- coding: utf-8 -*- +<%inherit file="/base.html" /> +<%def name="head_tags()"> + User Info - ${c.username} + + + +

Details for user ${c.username}

+% if c.message: +

${c.message}

+% endif + +% if c.user: +
+

Edit options

+ +
+ + +% endif + + +% if c.ident['user'].user_name == c.username: + +% endif + + + diff --git a/rdfdatabank/templates/admin_users.html b/rdfdatabank/templates/admin_users.html new file mode 100644 index 0000000..8d47c16 --- /dev/null +++ b/rdfdatabank/templates/admin_users.html @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +<%inherit file="/base.html" /> +<%def name="head_tags()"> + Users - ${c.silo_name} + +

Users of ${c.silo_name}

+% if c.message: +

${c.message}

+% endif +
+% if c.users: +
    + % for o in c.users: +
  • ${o}
  • + % endfor +
+% endif +
+

Create new user

+<%include file="/register_new_user.html"/> diff --git a/rdfdatabank/templates/alter_silo.html b/rdfdatabank/templates/alter_silo.html index 31a174a..6d5c0c6 100644 --- a/rdfdatabank/templates/alter_silo.html +++ b/rdfdatabank/templates/alter_silo.html @@ -1,20 +1,72 @@ # -*- coding: utf-8 -*- -

Alter Silo

-

-

-
    -
  • ${c.silo_name}
  • + +
    + + +
    % if c.kw: -
  • -
  • -
  • -
  • +
    + + +
    +
    + + +
    +
    + + +
    + % if c.roles and "admin" in c.roles: +
    + + +
    + % endif +
    + + +
    +
    + + +
    +
    + + +
    % else: -
  • -
  • -
  • -
  • +
    + + +
    +
    + + +
    +
    + + +
    + % if c.roles and "admin" in c.roles: +
    + + +
    + % endif +
    + + +
    +
    + + +
    +
    + + +
    % endif -
  • +

    diff --git a/rdfdatabank/templates/api.html b/rdfdatabank/templates/api.html new file mode 100644 index 0000000..b8da929 --- /dev/null +++ b/rdfdatabank/templates/api.html @@ -0,0 +1,15 @@ +# -*- coding: utf-8 -*- +<%inherit file="/base.html" /> +<%def name="head_tags()"> + Databank API + +

    Databank API

    +

    + +<%include file="${c.api_file}"/> diff --git a/rdfdatabank/templates/atom_results.html b/rdfdatabank/templates/atom_results.html new file mode 100644 index 0000000..ebedd08 --- /dev/null +++ b/rdfdatabank/templates/atom_results.html @@ -0,0 +1,2 @@ + +${c.atom} diff --git a/rdfdatabank/templates/base.html b/rdfdatabank/templates/base.html index bf78c86..429c429 100644 --- a/rdfdatabank/templates/base.html +++ b/rdfdatabank/templates/base.html @@ -1,63 +1,198 @@ -# -*- coding: utf-8 -*- - - - - - - - ${self.head_tags()} - - -% if c.silo_name: -

    Archives - Search API: ${c.silo_name} - ${c.silo_name} package upload

    -% else: -

    Archives - Search API

    -% endif -% if c.ident and c.ident['role'] == "admin": -
    -

    Admin pages -% if c.silo_name: -Admin ${c.silo_name} -% endif -

    -
    -% endif -% if c.id: -% if c.path: -

    Subdirectory ${c.id}/${c.path}

    -

    Root directory: ${c.id}

    -% else: -

    Root directory of ${c.id}

    -% endif -% endif - ${self.body()} -
    -

    /objects API list

    - - - +# -*- coding: utf-8 -*- + + + + + + + + + + + + + + + ${ h.javascript_link( '/static/jquery.js' ) } + ${self.head_tags()} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + + + +
    + +
    + +
    + ${self.body()} +
    + +
    +
    + +
    + +
    + <%include file="/footer.html"/> +
    +
    + + diff --git a/rdfdatabank/templates/cookies.html b/rdfdatabank/templates/cookies.html new file mode 100755 index 0000000..3c147fd --- /dev/null +++ b/rdfdatabank/templates/cookies.html @@ -0,0 +1,18 @@ +# -*- coding: utf-8 -*- +<%inherit file="/base.html" /> +<%def name="head_tags()"> + About DataBank + +

    Cookies in Databank

    +
    +
    +

    What are cookies

    +

    Cookies are small text files that can be written and read by websites and stored by the browser on your computer, tablet or smartphone. They do a number of things including allowing access to some types of content and functionality to users, and collecting anonymous user information so that site owners can monitor the performance of their sites. They are a kind of .memory. for a website that can help it respond appropriately to users and user behaviour.

    +
    +
    +

    Cookies on this website

    +

    This website stores cookies for use in website analytics and other cookies that are striclty necessary to provide certain features or content, you have requested based on the authentication information provided by you.

    +

    The analytics cookies anonymously tracks individual visitor behaviour on the website so that we can see how the site is being used. We only use this information for monitoring and improving our website and content for the benefit of our users (you).

    +

    More information about controlling these cookies can be found at http://www.bodleian.ox.ac.uk/cookies. +

    +
    diff --git a/rdfdatabank/templates/create_doi.html b/rdfdatabank/templates/create_doi.html new file mode 100644 index 0000000..f98db20 --- /dev/null +++ b/rdfdatabank/templates/create_doi.html @@ -0,0 +1,4 @@ +# -*- coding: utf-8 -*- +
    + +
    diff --git a/rdfdatabank/templates/create_new_item.html b/rdfdatabank/templates/create_new_item.html index 3c1a694..c69f55c 100644 --- a/rdfdatabank/templates/create_new_item.html +++ b/rdfdatabank/templates/create_new_item.html @@ -1,5 +1,7 @@ -# -*- coding: utf-8 -*- -
    - - -
    +# -*- coding: utf-8 -*- +
    + + +
    The name can only contain numbers, letters, '-' and ':', must be more than one character long and must not contain any spaces.
    + +
    diff --git a/rdfdatabank/templates/create_new_silo.html b/rdfdatabank/templates/create_new_silo.html index db05b37..7c40cd0 100644 --- a/rdfdatabank/templates/create_new_silo.html +++ b/rdfdatabank/templates/create_new_silo.html @@ -1,13 +1,25 @@ # -*- coding: utf-8 -*- -

    Create new Silo

    -

    -

    -
      -
    • -
    • -
    • -
    • -
    • -
    • + +
      +
      +
      The machine readable name of the silo, used by the system and in the urls.
      +
      For example the name siloa will appear in the url as http://databank/siloa/...
      +
      +
      +
      +
      +
      +
      +
      +
      +
      Separate multiple usernames with a comma
      +
      +
      +
      Separate multiple usernames with a comma
      +
      +
      +
      Separate multiple usernames with a comma
      +
      +
      +
      -

      diff --git a/rdfdatabank/templates/datasets_api.html b/rdfdatabank/templates/datasets_api.html new file mode 100644 index 0000000..01cb71c --- /dev/null +++ b/rdfdatabank/templates/datasets_api.html @@ -0,0 +1,493 @@ +# -*- coding: utf-8 -*- +
      +

      Datasets

      +

      API call to

        +
      • Obtain a list of datasets in a silo
      • +
      • Obtain information about the latest version of the dataset or information about a particular vesion of the dataset
      • +
      • View the contents of the dataset - view / download files and view contents of sub-directories (latest verison or a particular version)
      • +
      • Create a dataset
      • +
      • Modify the datasets embargo information
      • +
      • Add new files to the dataset (including metadata files)
      • +
      • Update existing files in the dataset (including metadata files)
      • +
      • Delete files in the dataset
      • +
      • Delete the dataset
      • +
      +

      Sections

      +
        +
      1. /{silo name}/datasets
      2. +
      3. /{silo name}/datasets/{id}
      4. +
      5. /{silo name}/datasets/{id}/version{#}
      6. +
      7. /{silo name}/datasets/{id}/{subpath}
      8. +
      9. /{silo name}/datasets/{id}[/{subpath}]/version{#}
      10. +
      + +
      +
      +
      /{silo name}/datasets
      + +
      +
      +

      API call to obtain a list of datasets in a silo and create a new dataset

      +

      Controller: datasets     action: siloview

      +
      +

      GET: Obtain a list of datasets in a silo

      +
      Returns
      +
      +
      401 if not a valid user
      +
      403 if not authorized
      +
      Accept:text/html
      +
      Returns the ids of each dataset in the silo, along with a form for changing the embargo information and deleting the dataset. A form for dataset creation is also available.
      +
      Accept: text/plain, application/json
      +
      200 OK
      +
      Returns a JSON-encoded list of dataset ids in that silo along with the embargo information for each dataset
      +
      The file datasetsInSiloInformation.txt contains an example of the data returned (data_returned)
      +
      Accept:*/*, default
      +
      Returns text/HTML listing the ids of each dataset, along with a form for changing the embargo information and deleting the dataset. A form for dataset creation is also available.
      +
      +
      +
      +

      POST: Create new dataset

      +
      Parameters
      +
      + + + + + +
      id {id to create}
      embargoed {true|false} (optional).
      If the parameter is not supplied, a default value of true will be used.
      embargoed_until {ISO8601 date} (optional).
      If embargoed = true and no date has been supplied, a default time delta of 70 years will be used
      title{(Optional)}
      +
      +
      Returns
      +
      +
      + 409 if dataset already exists
      + 401 If not a valid user
      + 403 if not authorized
      + 403 if the name of the dataset does not confirm to the naming rule (name can contain only the followin characters 0-9a-zA-Z-_:
      +
      +
      Accept: text/html
      +
      302 to splash page for newly created dataset
      +
      Accept: text/plain, application/json
      +
      201 created
      +
      Accept: */*, default
      +
      Returns text/plain, 201 created
      +
      +
      +
      +

      PUT, DELETE: NOOP

      +
      + + +
      +
      +
      /{silo name}/datasets/{id}
      + +
      +
      +

      API call to obtain information about the dataset, create a dataset, modify the datasets embargo information, post files to the dataset and delete the dataset. All of this is performed on the latest version of the dataset.

      +

      Controller: datasets     action: datasetview

      +
      +

      GET: Obtain information about the dataset

      +
      Returns
      +
      +
      404 if dataset is not available
      +
      401 if not authenticated and dataset is under embargo
      +
      403 if not authorized and dataset is under embargo
      +
      Accept: text/html
      +
      returns HTML splash page for dataset id. The page contains the following: +
        +
      • Readme text
      • +
      • List of files / folders at the top level
      • +
      • Dataset's embargo state
      • +
      • Manifest data
      • +
      • If authenticated and authorized user:
        +
          +
        • View options: Choice to view page as anonymous user or as editor
        • +
        • Form to modify manifest
        • +
        • Form to upload file
        • +
        • List of dataset unpack endpoints, to unpack file to new dataset
        • +
        +
      • +
      +
      +
      Accept: application/json
      +
      200 OK
      +
      Returns a JSON-encoded hash/dict. The keys map to the following : +
        +
      • Readme text - data_returned["readme_text"]
      • +
      • Dataset's embargo information data_returned["embargos"]
      • +
      • If user is authenticated and authorized (editor = true) / anonymous (editor = false) - data_returned["editor"]
      • +
      • List of zipfiles - data_returned["zipfiles"]
      • +
      • If user is authenticated and authorized, the view chosen by the user - data_returned["view"]
      • +
      • Manifest data in two forms - data_returned["manifest"] and data_returned["manifest_pretty"]
      • +
      • Hash dict of files / folders at the top level - data_returned["parts"][file_or_directory_name"]
        + If it is a file, the file stat information is also available. See http://docs.python.org/library/os.html#os.stat for more information.
        + If it is a directory, the dictionary value is empty as there is no stat information
      • +
      +
      +
      The file datasetInformation.txt contains an example of the data returned (data_returned)
      +
      Accept: application/rdf+xml, text/xml
      +
      returns dataset's RDF manifest as RDF/XML
      +
      Accept: text/rdf+n3
      +
      returns dataset's RDF manifest as N3
      +
      Accept: application/x-turtle
      +
      Returns dataset's RDF manifest as Turtle
      +
      Accept: text/rdf+ntriples, text/rdf+nt
      +
      returns dataset's RDF manifest as ntriples
      +
      Accept: */*, default
      +
      returns text/HTML of splash page for dataset id, as described above
      +
      +
      +
      +

      POST: Create new dataset. Dataset id doesn't exist. (As POST /{silo}/datasets id=...)

      +
      Parameters
      +
      + + + + +
      embargoed {true|false} (optional).
      If the parameter is not supplied, a default value of true will be used.
      embargoed_until {ISO8601 date} (optional).
      If embargoed = true and no date has been supplied, a default time delta of 70 years will be used
      title{(Optional)}
      +
      +
      Returns
      +
      +
      + 401 If not a valid user
      + 403 if not authorized
      + 403 if the name of the dataset does not confirm to the naming rule (name can contain only the followin characters 0-9a-zA-Z-_:
      +
      +
      Accept: text/html
      +
      302 to splash page for newly created dataset
      +
      Accept: text/plain, application/json
      +
      201 created
      +
      Accept: */*, default
      +
      Returns text/plain, 201 created
      +
      +
      +
      +

      POST : change embargo information in dataset. On success, version is incremented.

      +
      +
      TODO:
      +
      Redo this bit of code, removing the parameter embargo_change and just using embargoed and embargoed_until
      +
      +
      Parameters
      +
      + + + + +
      embargo_changetrue (used as a convenience parameter for html forms)
      embargoed{true|false}
      embargoed_until{ISO8601 date} (optional).
      If embargoed = true and no date has been supplied, a default time delta of 70 years will be used
      +
      +
      Returns
      +
      +
      401 if not a valid user
      +
      403 if not authorized
      +
      Accept: text/html
      +
      302 to splash page of dataset
      +
      Accept: text/plain, application/json
      +
      204 updated
      +
      Accept: */*, default
      +
      204 updated
      +
      +
      +
      +
      +

      POST: Upload file to root directory. On success, version is incremented.

      +
      Parameters
      +
      + + + +
      file Multipart-encoded (HTML) file upload
      filename {Optional filename for upload}
      +
      +
      Returns
      +
      +
      401 if not a valid user
      +
      403 if not authorized
      +
      400 if filename contains ..
      +
      403 if filename is an existing directory in dataset. Cannot post a file to update a directory with a file.
      +
      400 if filename is manifest.rdf and cannot read / parse rdf in manifest
      +
      Accept: text/html
      +
      302 to splash page of dataset
      +
      Accept: text/plain
      +
      204 on update, if filename exists.
      +
      201 on creation, if filename does not exist
      +
      Accept: */*, default
      +
      Returns text/plain
      +
      204 on update, if filename exists.
      +
      201 on creation, if filename does not exist
      +
      +
      +
      +

      POST: Text upload. Convenience function for HTML to update/create text files. On success, version is incremented.

      +
      Parameters
      +
      + + + +
      text {UTF text to store}
      filename {desired filename}
      +
      +
      Returns
      +
      +
      401 if not a valid user
      +
      403 if not authorized
      +
      400 if filename contains ..
      +
      406 if the parameter filename is missing
      +
      403 if filename is an existing directory in dataset. Cannot post a file to update a directory with a file.
      +
      406 if the filename is manifet.rdf and cannot parse rdf
      +
      Accept: text/html
      +
      302 to splash page of dataset
      +
      Accept: text/plain
      +
      204 on update, if filename exists.
      +
      201 on creation, if filename does not exist
      +
      Accept: */*, default
      +
      Returns text/plain
      +
      204 on update, if filename exists.
      +
      201 on creation, if filename does not exist
      +
      +
      +
      +

      DELETE Deletes the dataset id

      +
      +
      Be Aware!
      +
      Delete currently deletes the dataset. This action cannot be undone and your data is lost following this action.
      +
      +
      +
      TODO:
      +
      Delete currently deletes the dataset. This SHOULD NOT happen. On delete, create a new version with just a stub in the manifest saying deleted. Also, this dataset should not be returned in the list of datasets and dataset create should not return a 409.
      +
      +
      Returns
      +
      +
      401 if not a valid user
      +
      403 if not authorized
      +
      404 if id doesn't exist
      +
      Accept: */*, default
      +
      Returns text/plain, 200 on success
      +
      +
      +
      +

      PUT NOOP

      +
      + + +
      +
      +
      /{silo name}/datasets/{id}/version{#}
      + +
      +
      +

      API call to obtain information about a particular version of the dataset

      +

      Controller: datasets     action: datasetview_vnum

      +
      +

      GET: Obtain information about a particular version of the dataset

      +
      Returns
      +
      +
      404 if dataset is not available
      +
      404 if version number # of dataset is not available
      +
      401 if not authenticated and dataset is under embargo
      +
      403 if not authorized and dataset is under embargo
      +
      Accept: text/html
      +
      returns HTML splash page for dataset id. The page contains the following: +
        +
      • Readme text
      • +
      • List of files / folders at the top level
      • +
      • Dataset's embargo state
      • +
      • Manifest data
      • +
      +
      +
      Accept: application/json
      +
      200 OK
      +
      Returns a JSON-encoded hash/dict. The keys map to the following : +
        +
      • Readme text - data_returned["readme_text"]
      • +
      • Dataset's embargo information data_returned["embargos"]
      • +
      • If user is authenticated and authorized (editor = true) / anonymous (editor = false) - data_returned["editor"]
      • +
      • If user is authenticated and authorized, the view chosen by the user - data_returned["view"]
        (Note : this will always return empty as there is no edit possible for previous versions of the dataset)
      • +
      • Manifest data - data_returned["manifest_pretty"]
      • +
      • Hash dict of files / folders at the top level - data_returned["parts"][file_or_directory_name"]
        + If it is a file, the file stat information is also available. See http://docs.python.org/library/os.html#os.stat for more information.
        + If it is a directory, the dictionary value is empty as there is no stat information
      • +
      +
      +
      The files + datasetInformation-version0.txt and + datasetInformation-version1.txt + contain an example of the data returned (data_returned) +
      +
      Accept: application/rdf+xml, text/xml
      +
      returns dataset's RDF manifest as RDF/XML
      +
      Accept: text/rdf+n3
      +
      returns dataset's RDF manifest as N3
      +
      Accept: application/x-turtle
      +
      Returns dataset's RDF manifest as Turtle
      +
      Accept: text/rdf+ntriples, text/rdf+nt
      +
      returns dataset's RDF manifest as ntriples
      +
      Accept: */*, default
      +
      returns text/HTML of splash page for dataset id, as described above
      +
      +
      + + +
      +
      +
      /{silo name}/datasets/{id}/{subpath}
      + +
      +
      +

      API call to view files / contents of a folder and add, update or delete a file. All of these actions are performed on the latest version of the dataset. +

      +

      Controller: datasets     action: itemview

      +
      +

      GET: view the contents of subpath (subpath could point to a file or a folder)

      +
      Returns
      +
      +
      404 if dataset is not available
      +
      401 if not authenticated and dataset is under embargo
      +
      403 if not authorized and dataset is under embargo
      +
      404 if subpath is not available
      +
      subpath is a file
      +
      +
      Accept: */*, default
      +
      The file is served
      +
      +
      subpath is a directory
      +
      +
      Accept: text/html
      +
      HTML page listing the files within the given subpath and readme_text
      +
      Accept: text/plain, application/json
      +
      200 OK
      +
      Returns a JSON-encoded hash/dict. The keys map to the following : +
        +
      • Readme text - data_returned["readme_text"]
      • +
      • Hash dict of files / folders at subpath (just that level) - data_returned["parts"]["file_or_directory_name"]
        + If it is a file, the file stat information is also available. + See http://docs.python.org/library/os.html#os.stat for more information.
        + If it is a directory, the dictionary value is empty as there is no stat information
      • +
      +
      +
      The file + datasetSubdirInformation.txt + contains an example of the data returned (data_returned) +
      +
      Accept: */*, default
      +
      HTML page listing the files within the given subpath and readme_text
      +
      +
      +
      +
      +

      PUT: used to add content to a file.
      + If the subpath points to manifest.rdf, the content is munged with exisitng metadata.
      + If the subpath does not exist, a file is created in the same name as the filename in path including all the intermediate directories in the subpath (using os.makedirs) and the contents are added to that file.
      + On success, version is incremented. +

      +
      Returns
      +
      +
      401 if not authenticated
      +
      403 if not authorized
      +
      403 if subpath points to a directory - cannot put content onto an existing directory
      +
      400 if subpath contains ..
      +
      Accept: text/html
      +
      302 to the subpath, listing the files within the given subpath and readme_text
      +
      Accept: text/plain, application/json
      +
      204 Updated, if subpath did exist
      +
      201 created, if subpath did not exist
      +
      Accept: */*, default
      +
      Return text/plain. 201 on creation and 204 on update.
      +
      +
      +
      +

      POST Upload a file within the subpath
      + If the subpath + filename parameter points to manifest.rdf, the content is munged with exisitng metadata.
      + If the subpath + filename parameter does not exist, the file is added as filename into subpath. The intermediate directories in the subpath (using os.makedirs) are created if they do not exist.
      + On success, version is incremented. +

      +
      Parameters
      +
      + + + +
      file Multipart-encoded (HTML) file upload
      filename {Optional filename for upload}.
      + When provided, it is only used if subpath points to an existing directory.
      +
      +
      Returns
      +
      +
      401 if not authenticated
      +
      403 if not authorized
      +
      400 if subpath contains ..
      +
      403 if subpath + filename points to an existing directory
      +
      Accept: text/html
      +
      302 to the subpath, listing the file filename within the given subpath and readme_text
      +
      Accept: text/plain, application/json
      +
      204 Updated, if subpath did exist
      +
      201 created, if subpath did not exist
      +
      Accept: */*, default
      +
      Returns text/plain. 201 on creation and 204 on update.
      +
      +
      +
      +

      DELETE: deletes files or directories within the dataset. It also performs recursive deletes (directories with sub-directories within them).
      + On success, version is incremented. +

      +
      Returns
      +
      +
      401 if not authenticated
      +
      403 if not authorized
      +
      403 if subpath is manifest.rdf
      +
      Accept: */*, default
      +
      Returns text/plain. 200 OK on successful completion.
      +
      +
      + + +
      +
      +
      /{silo name}/datasets/{id}[/{subpath}]/version{#}
      + +
      +
      +

      API call to obtain a file or view the contents of a folder, for a particular version of the dataset

      +

      Controller: datasets     action: itemview

      +
      +

      GET: Obtain files or view the contents of a directory for a particular version of the dataset.

      +
      Returns
      +
      +
      404 if dataset, subpath or version number (#) is not available
      +
      401 if not authenticated and dataset is under embargo
      +
      403 if not authorized and dataset is under embargo
      +
      subpath is a file
      +
      +
      Accept: */*, default
      +
      The file is served
      +
      +
      subpath is a directory
      +
      +
      Accept: text/html
      +
      HTML page listing the files within the given subpath and readme_text (pertaining to that version)
      +
      +
      TODO:
      +
      The html display is not showing the files. Fix bug!
      +
      +
      Accept: text/plain, application/json
      +
      200 OK
      +
      Returns a JSON-encoded hash/dict. The keys map to the following : +
        +
      • Readme text - data_returned["readme_text"]
      • +
      • Hash dict of files / folders at subpath (just that level) - data_returned["parts"]["file_or_directory_name"]
        + If it is a file, the file stat information is also available. + See http://docs.python.org/library/os.html#os.stat for more information.
        + If it is a directory, the dictionary value is empty as there is no stat information
      • +
      +
      +
      The files + datasetSubdirInformation-version3.txt and + datasetSubdirInformation-version1.txt + contains an example of the data returned (data_returned) +
      +
      Accept: */*, default
      +
      HTML page listing the files within the given subpath and readme_text
      +
      +
      +
      +
      +

      POST, PUT, DELETE: NOOP

      +
      + + diff --git a/rdfdatabank/templates/datasetview.html b/rdfdatabank/templates/datasetview.html new file mode 100644 index 0000000..d5b94b5 --- /dev/null +++ b/rdfdatabank/templates/datasetview.html @@ -0,0 +1,212 @@ +# -*- coding: utf-8 -*- +<%inherit file="/base.html" /> +<%def name="head_tags()"> + + Data package ${c.id} + + + + +<% +c.current = c.id +%> +

      Data package ${c.id}

      +% if 'title' in c.metadata and c.metadata['title'][0]: +

      ${c.metadata['title'][0]}

      +% endif +% if c.version: +

      Version ${c.version}

      +% elif c.versions: +

      Version ${c.versions[-1]}

      +% endif + +% if c.editor: + +% endif + + +
      +% if c.version and c.versions and c.version == c.versions[-1]: +

      You are currently viewing the latest version (${c.version}) of the data package

      +% elif c.version: +

      You are currently viewing version ${c.version} of the data package

      +% elif c.versions: +

      You are currently viewing the latest version (${c.versions[-1]}) of the data package

      +% else: +

      You are currently viewing the latest version of th data package

      +% endif + +% if c.versions: +

      View other versions:

        + % for v in c.versions: + % if c.version and v == c.version: +
      • ${v}
      • + % elif (not c.version) and (v == c.versions[-1]): +
      • ${v}
      • + % else: +
      • ${v}
      • + % endif + % endfor +
      +% endif +
      + +
      + + +% if c.embargos: +

      Access Information

      +
      +% if c.embargos and c.current in c.embargos and c.embargos[c.current]: +<% +from rdfdatabank.lib.utils import formatDate +c.emb = c.embargos[c.current] +dt_human = c.emb[1] +if dt_human and dt_human.strip(): + dt_human = formatDate(dt_human) +%> + % if c.emb[0] == True or c.emb[0] == 1 or (isinstance(c.emb[0], basestring) and c.emb[0].strip().lower() in ['true', '1']): + % if (isinstance(c.emb[1], basestring) and c.emb[1].strip()): + Data package is embargoed until ${dt_human}. Only the metadata is openly accessible. + % else: + Data package is embargoed indefinitely. Only the metadata is openly accessible. + % endif + % else: + Data package is openly accessible + % endif +% else: + Data package is openly accessible +% endif +% endif +
      + +
      + +% if c.show_files: +

      Data package contents

      +
      + % if c.editor: + <%include file="/part_list.html"/> + % else: + <%include file="/part_list_display.html"/> + % endif +
      +% endif + + +% if c.readme_text: +

      README text

      +
      +<%include file="/readme_section.html"/> +
      +% endif + + +

      Metadata

      +
      +<%include file="/rdf_manifest.html"/> +
      + +
      + + +% if c.editor: +
      + % if c.view == 'editor': +
      + + + + + + + + % if c.zipfiles: + + % endif + % endif +
      +% endif + +
      diff --git a/rdfdatabank/templates/delete_item.html b/rdfdatabank/templates/delete_item.html index 3c1a694..c8cd06a 100644 --- a/rdfdatabank/templates/delete_item.html +++ b/rdfdatabank/templates/delete_item.html @@ -1,5 +1,5 @@ -# -*- coding: utf-8 -*- -
      - - -
      +# -*- coding: utf-8 -*- +
      + + +
      diff --git a/rdfdatabank/templates/doiview.html b/rdfdatabank/templates/doiview.html new file mode 100644 index 0000000..b5486cf --- /dev/null +++ b/rdfdatabank/templates/doiview.html @@ -0,0 +1,43 @@ +# -*- coding: utf-8 -*- +<%inherit file="/base.html" /> +<%def name="head_tags()"> + DOI view + +

      DOI Information for ${c.silo_name}/${c.id}/version${c.version}

      +% if c.heading: +

      ${c.heading}

      +%endif +
      +% if c.message: +

      ${c.message}

      +% endif +% if c.resp_status: +

      Response code from Datacite: ${c.resp_status}

      +% endif +% if c.resp_reason: +

      Response message from Datacite: ${c.resp_reason}

      +% endif +
      + +% if c.dois: +

      DOIs available for this data package

      +
        + % for v, d in c.dois.iteritems(): +
      • Verison ${v}: ${d}
      • + % endfor +
      +% endif + +% if c.version_doi: +

      DOI: ${c.version_doi}

      +% endif + +% if c.editor: + <%include file="/create_doi.html"/> +% endif + +%if c.metadata: +

      Metadata associated with the DOI

      +
      ${c.metadata}
      +% endif + diff --git a/rdfdatabank/templates/embargo_form.html b/rdfdatabank/templates/embargo_form.html index acd9982..a764b60 100644 --- a/rdfdatabank/templates/embargo_form.html +++ b/rdfdatabank/templates/embargo_form.html @@ -1,17 +1,46 @@ -# -*- coding: utf-8 -*- -
      - -% if c.embargos: -% if c.embargos[c.current][0] not in ['False', 'false', 0, False]: - -% else: - -% endif - -% else: - - -% endif - - -
      +# -*- coding: utf-8 -*- +
      +
      Access status +% if c.embargos and c.current in c.embargos and c.embargos[c.current]: + % if c.embargos[c.current][0] == True or (isinstance(c.embargos[c.current][0], basestring) and c.embargos[c.current][0].lower() in ['true', '1', 1]): + + +
      + % else: + + +

      + % endif + % if c.embargos[c.current][1]: + <% + from rdfdatabank.lib.utils import formatDate + dt_human = c.embargos[c.current][1] + if dt_human and dt_human.strip(): + dt_human = formatDate(dt_human) + %> + + +

      + % else: + + +

      + % endif +% else: + + +

      + +

      +

      +% endif +
      +
      Embargoed: only authorized users of the silo can access the data package
      +Open access: all users can access the data package.
      +
      Embargo date: Date until which a data package should be emargoed. +By default, this is set to 70 years from now. If no time is included, the current time is used.

      +The date and time can be in one of several formats.
      For example: January 16 2020; 16-01-2020; 16/Jan/2020; 16/01/2020; Jan 16 2020, 1:00 AM; 16-Jan-2020 13:00 +
      + +
      + diff --git a/rdfdatabank/templates/error.html b/rdfdatabank/templates/error.html new file mode 100644 index 0000000..1fab39c --- /dev/null +++ b/rdfdatabank/templates/error.html @@ -0,0 +1,15 @@ +# -*- coding: utf-8 -*- +<%inherit file="/base.html" /> +<%def name="head_tags()"> + Error + +
      +

      Oops!

      +
      +
      +

      ${c.code}

      +
      +
      +

      ${c.status}

      +

      ${c.message}

      +
      diff --git a/rdfdatabank/templates/file_upload.html b/rdfdatabank/templates/file_upload.html index 017f7be..0e34ccc 100644 --- a/rdfdatabank/templates/file_upload.html +++ b/rdfdatabank/templates/file_upload.html @@ -1,9 +1,10 @@ -# -*- coding: utf-8 -*- -

      Add File to ${c.id}

      -

      -

      - - - -
      -

      +# -*- coding: utf-8 -*- +
      + + +

      + + +
      Filename is optional. Use it to specify a new filename for the file being uploaded.
      + +
      diff --git a/rdfdatabank/templates/files_unpack.html b/rdfdatabank/templates/files_unpack.html new file mode 100644 index 0000000..87d4b45 --- /dev/null +++ b/rdfdatabank/templates/files_unpack.html @@ -0,0 +1,21 @@ +# -*- coding: utf-8 -*- +% if c.zipfile: +
      +<% +c.disp = c.zipfile.strip().strip('/').split('/')[-1] +if len(c.disp) > 50: + c.disp = "...%s"%c.disp[-50:] +%> +
      ${c.disp} + +% if c.newid: + + +% else: + + +%endif + +
      +
      +%endif diff --git a/rdfdatabank/templates/footer.html b/rdfdatabank/templates/footer.html new file mode 100644 index 0000000..4f00d3c --- /dev/null +++ b/rdfdatabank/templates/footer.html @@ -0,0 +1,11 @@ +# -*- coding: utf-8 -*- +
      + +
      + diff --git a/rdfdatabank/templates/header.html b/rdfdatabank/templates/header.html new file mode 100644 index 0000000..79bcaa8 --- /dev/null +++ b/rdfdatabank/templates/header.html @@ -0,0 +1,11 @@ +# -*- coding: utf-8 -*- +<% +from pylons import app_globals as ag +%> +

      DataBank + %if ag.api_version: + ${ag.api_version} + % endif +

      +<%include file="/search_form.html"/> + diff --git a/rdfdatabank/templates/home.html b/rdfdatabank/templates/home.html new file mode 100644 index 0000000..35d3ff1 --- /dev/null +++ b/rdfdatabank/templates/home.html @@ -0,0 +1,47 @@ +# -*- coding: utf-8 -*- +<%inherit file="/base.html" /> +<%def name="head_tags()"> + Databank + + +

      Welcome to DataBank

      +
      +

      Store, archive, publish and access your data from the web using Databank.

      +
      + +
      +

      DataBank is a repository that will keep data safe in the long term.

      +

      You can use Databank to store, retrieve and publish data at any time, from anywhere on the web. You can accomplish these tasks using

      +
        +
      • The simple web interface of a hosted instance of Databank; as in this website you are viewing
      • +
      • Form another service, using the Databank API
      • +
      • Run your own instance of Databank
      • +
      +
      + + + +
      +

      Using the web interface

      +

      To get started, read this document on the Databank wiki

      +

      You should be able to browse and search for all of the bibliographic information in this instance of Databank and where allowed, view the data packages.

      +

      A demo instance of databank is available at http://databank-vm1.oerc.ox.ac.uk/ where you will be to create data pacakges, upload files and manage users.

      +
      + +
      +

      Using the Databank API

      +

      The API provides a restful web interface to all of Databank's features. Documentation on the API

      + +
      + +
      +

      Running your own instance of Databank

      +

      Databank is written in Python. The Databank code is licensed under MIT and can be checked out from https://github.com/dataflow/RDFDatabank

      +

      For instructions on installing Databank, follow this document

      +

      Get in touch with the developers of Databank

      +
      + diff --git a/rdfdatabank/templates/item_file_upload.html b/rdfdatabank/templates/item_file_upload.html new file mode 100644 index 0000000..7439b61 --- /dev/null +++ b/rdfdatabank/templates/item_file_upload.html @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +
      + + +

      + + +

      + +
      diff --git a/rdfdatabank/templates/items_api.html b/rdfdatabank/templates/items_api.html new file mode 100644 index 0000000..7505239 --- /dev/null +++ b/rdfdatabank/templates/items_api.html @@ -0,0 +1,160 @@ +# -*- coding: utf-8 -*- +
      +

      Items

      +

      Sections

      +
        + +
      1. /{silo name}/items/{id}
      2. +
      3. /{silo name}/items/{id}/{path}
      4. +
      5. /{silo name}/items/{id}/{path}/{subpath}
      6. +
      + + + +
      +
      +
      /{silo name}/items/{id}
      + +
      +
      +

      API call to obtain a list of unpack end-points, read the contents of a zip-file (without unpacking) and unpack a zip file into a new / existing dataset

      +

      Controller: items     action: datasetview

      +
      +

      GET

      +
      Returns
      +
      +
      401 if not a valid user
      +
      403 if not authorized
      +
      404 if dataset id does not exist
      +
      Accept: text/html
      +
      Returns an HTML page with list of zipfiles in {id} with form to unpack the zip file
      +
      Accept: text/plain, application/json
      +
      200 OK
      +
      Returns a JSON-encoded list of zipfiles in dataset
      +
      The file + itemInformationForDataset.txt + contains an example of the data returned (data_returned) +
      +
      Accept: */*, default
      +
      Returns text/html - an HTML page with list of zipfiles in {id} with form to unpack the zip file
      +
      +
      +
      +

      POST Unpack file. Create new dataset/update existing dataset with contents of upacked file

      +
      Parameters
      +
      + + + +
      filename{name of file to be unpacked, including the path}
      Id{(Optional)}. if Id is not supplied, the default dataset id for the new dataset will be {dataset_id}-{file_name}
      +
      +
      Returns
      +
      +
      401 if not a valid user
      +
      403 if not authorized
      +
      404 if file to unpack (as given by filename) was not found
      +
      415 if file is not of type application/zip
      +
      400 if there was an error reading the zipfile (BadZipFile)
      +
      Accept:text/html
      +
      302 to splash page for newly created dataset / updated dataset
      +
      Accept: text/plain, application/json
      +
      201/204 on creation/update of dataset
      +
      Accept: */*, default
      +
      Returns text/plain - 201/204 on creation/update of dataset
      +
      +
      +
      +

      PUT, DELETE NOOP

      +
      + + +
      +
      +
      /{silo name}/items/{id}/{path}
      + +
      +
      +

      API call to read the contents of a zip-file (without having to unpack) and unpack a zip file into a new / existing dataset

      +

      Controller: items     action: itemview

      +
      +

      GET

      +
      Returns
      +
      +
      401 if not a valid user
      +
      403 if not authorized
      +
      404 if path does not exist, or if path does not point to a file
      +
      415 if file is not of type application/zip
      +
      400 if there was an error reading the zipfile (BadZipFile)
      +
      Accept: text/html
      +
      Returns a HTML page listing the contents of the zipfile
      +
      Accept: text/plain, application/json
      +
      200 OK
      +
      Returns a JSON-encoded hash dict listing the contents of the ziipfile (files / directories in archive), along with the size of the uncompressed member and and the date of last modification of the member as a tuple of 6 values (Year, Month, Day of month, Hours, Minutes, Seconds).
      +
      The file + itemInformationForZipFileinDataset.txt + contains an example of the data returned (data_returned) +
      +
      Accept: */*, default
      +
      Returns text/html - an HTML page listing the contents of the zipfile
      +
      +
      +
      +

      POST Unpack file. Create new dataset/update existing dataset with contents of upacked file

      +
      Parameters
      +
      + + + +
      filename{name of file to be unpacked, including the path}
      Id{(Optional)}. if Id is not supplied, the default dataset id for the new dataset will be {dataset_id}-{file_name}
      +
      +
      Returns
      +
      +
      401 if not a valid user
      +
      403 if not authorized
      +
      404 if path does not exist, or if path does not point to a file
      +
      415 if file is not of type application/zip
      +
      400 if there was an error reading the zipfile (BadZipFile)
      +
      Accept:text/html
      +
      302 to splash page for newly created dataset / updated dataset
      +
      Accept: text/plain, application/json
      +
      201/204 on creation/update of dataset
      +
      Accept: */*, default
      +
      Returns text/plain - 201/204 on creation/update of dataset
      +
      +
      +
      +

      PUT, DELETE NOOP

      +
      + + +
      +
      +
      /{silo name}/items/{id}/{path}/{subpath}
      + +
      +
      +

      API call to retreive a file from the zip file

      +

      Controller: items     action: subitemview

      +
      +
      TODO:
      +
      This is yet to be implemented
      +
      + +
      diff --git a/rdfdatabank/templates/itemview.html b/rdfdatabank/templates/itemview.html index a818026..a26ffa8 100644 --- a/rdfdatabank/templates/itemview.html +++ b/rdfdatabank/templates/itemview.html @@ -1,36 +1,118 @@ -# -*- coding: utf-8 -*- -<%inherit file="/base.html" /> -<%def name="head_tags()"> - - View of item ${c.item.uri} - -% if c.editor: -

      View as Editor , or User -% endif -% if c.readme_text: -<%include file="/readme_section.html"/> -% endif -<%include file="/part_list.html"/> -% if c.item and c.view == "editor": -

      -

      Detailed view (As Editor) -

      -
      Item's JSON state (Accept: application/json):
      -
      ${repr(c.item)}
      -
      Item's Embargo state
      -
      -<% -c.current = c.id -%> -

      <%include file="/embargo_form.html"/>

      -

      Embargo state: True - only those logged in and with edit rights can see item. False - Anyone can GET the item and it's files.

      -

      Embargo date: Aim is for ISO8601 dates to provide embargo trigger events. Currently unused, unvalidated and unparsed.

      -
      Item's RDF Manifest (Accept: most RDF mimetypes):
      -
      ${c.item.rdf_to_string(format="pretty-xml")}
      -
      Change RDF Manifest:
      -
      <%include file="/rdf_manifest_form.html"/>
      -
      -
      -<%include file="/file_upload.html"/> -% endif +# -*- coding: utf-8 -*- +<%inherit file="/base.html" /> +<%def name="head_tags()"> + Item View + + + + +

      Data package ${c.id}

      +% if c.version: +

      Version ${c.version}

      +% elif c.versions: +

      Version ${c.versions[-1]}

      +% endif + + +% if c.editor: +
      +

      Edit options

      + + +
      +% endif + + +
      +% if c.version and c.versions and c.version == c.versions[-1]: +

      You are currently viewing the latest version (${c.versions[-1]}) of the data package

      +% elif c.version: +

      You are currently viewing version ${c.version} of the data package

      +% elif c.versions: +

      You are currently viewing the latest version (${c.versions[-1]}) of the data package

      +% else: +

      You are currently viewing the latest version of th data package

      +% endif + +% if c.versions: +

      View other versions:

        + % for v in c.versions: + % if c.version and v == c.version: +
      • ${v}
      • + % elif (not c.version) and (v == c.versions[-1]): +
      • ${v}
      • + % else: +
      • ${v}
      • + % endif + % endfor +
      +% endif +
      + +
      + +% if c.parts: +

      Data package contents within ${c.path}

      +
      + % if c.show_files: + % if c.editor: + <%include file="/part_list.html"/> + % else: + <%include file="/part_list_display.html"/> + % endif + % else: +

      You do not have permission to view these files

      + % endif +
      +% else: +

      There are no files or folders in this path

      +% endif +
      + + +% if c.readme_text: +

      README text

      +
      +<%include file="/readme_section.html"/> +
      +% endif + + +% if c.editor and c.view == 'editor': + + +% endif + diff --git a/rdfdatabank/templates/keywords.html b/rdfdatabank/templates/keywords.html new file mode 100644 index 0000000..f13a756 --- /dev/null +++ b/rdfdatabank/templates/keywords.html @@ -0,0 +1,7 @@ +# -*- coding: utf-8 -*- +<%inherit file="/base.html" /> +<%def name="head_tags()"> + Keywords + + +

      List of keywords in the site goes here...

      diff --git a/rdfdatabank/templates/list_of_archives.html b/rdfdatabank/templates/list_of_archives.html deleted file mode 100644 index d152d2f..0000000 --- a/rdfdatabank/templates/list_of_archives.html +++ /dev/null @@ -1,13 +0,0 @@ -# -*- coding: utf-8 -*- -<%inherit file="/base.html" /> -<%def name="head_tags()"> - List of Data Archives that accept zipped packages - -% if c.silos: -

      List of Archives: -
        -% for silo in c.silos: -
      • ${silo} (Package upload: ${silo})
      • -% endfor -
      -% endif diff --git a/rdfdatabank/templates/list_of_datasets.html b/rdfdatabank/templates/list_of_datasets.html new file mode 100644 index 0000000..41c388e --- /dev/null +++ b/rdfdatabank/templates/list_of_datasets.html @@ -0,0 +1,151 @@ +# -*- coding: utf-8 -*- +##============================================================================= +## Pagination fucntion +<%def name="pagination()"> + % if len(c.permissible_offsets) > 0: +
      + <% + jump = c.rows*10 + lowerJump = c.start-jump + if lowerJump < 0: + lowerJump = 0; + higherJump = c.start+jump + if higherJump > c.lastPage: + higherJump = c.lastPage + %> + + ## Goto to first page + % if c.start == 0: + + % else: + + % endif +« + + % if c.start != 0: + + % endif + <%! import math %> + <% pagcount = 0.0 %> + % for offset in c.permissible_offsets: + % if c.start == offset: + + % else: + + % endif + <% + offset_start = offset+1 + offset_end = offset+c.rows + if offset_end > c.numFound: + offset_end = c.numFound + pagcount = float(offset_end) / float(c.rows) + pagcount= math.ceil(pagcount) + pagcount= int(pagcount) + %> +${h.link_to(pagcount, "%s?start=%d&rows=%d"%(c.silo_name, offset, c.rows))} + + % endfor + % if c.start != c.lastPage: + + % endif + ## Goto to last page + % if c.start == c.lastPage: + + % else: + + % endif +» + + +
      + % endif +## end pagination() + +##============================================================================= + +% if c.silo_name: +<% +n = len(c.items) +%> +% if n > 0: + +## Pagination and sort options +% if c.numFound and c.numFound > 0: +
      + ${pagination()} +
      +% endif + +## Number of records, current span of records and items per page +
      +
      +${ c.numFound} records found. +
      +% if c.numFound > 0: +
      Showing results ${c.start+1} to +% if (c.start+c.rows) > c.numFound: +${c.numFound} +% else: +${c.start+c.rows} +% endif +
      +% endif +
      + +% for item in sorted(c.items): + + + + +% endfor +
      +% if c.embargos and item in c.embargos and c.embargos[item]: +<% +from rdfdatabank.lib.utils import formatDate +c.emb = c.embargos[item] +dt_human = c.emb[1] +if dt_human and dt_human.strip(): + dt_human = formatDate(dt_human) +%> + % if c.emb[0] == True or c.emb[0] == 1 or (isinstance(c.emb[0], basestring) and c.emb[0].strip().lower() in ['true', '1']): + % if (isinstance(c.emb[1], basestring) and c.emb[1].strip()): + Data package is embargoed until ${dt_human} + % else: + Data package is embargoed indefinitely + % endif + % else: + Data package is openly accessible + % endif +% else: + Data package is openly accessible +% endif +
      + +## Pagination and sort options +%if c.numFound and c.numFound > 0: +
      + ${pagination()} +
      +% endif + +## Number of records, current record start and end and items per page +%if c.numFound and c.numFound > 0: +
      +
      +${ c.numFound} records found. +
      +% if c.numFound > 0: +
      Showing results ${c.start+1} to +% if (c.start+c.rows) > c.numFound: +${c.numFound} +% else: +${c.start+c.rows} +% endif +
      +% endif +
      +% endif +
      +% endif +% endif + diff --git a/rdfdatabank/templates/list_of_silos.html b/rdfdatabank/templates/list_of_silos.html new file mode 100644 index 0000000..738d52a --- /dev/null +++ b/rdfdatabank/templates/list_of_silos.html @@ -0,0 +1,30 @@ +# -*- coding: utf-8 -*- +<%inherit file="/base.html" /> +<%def name="head_tags()"> + List of Silos + +<% +n = len(c.silo_infos) +%> +

      List of Silos

      +% if c.silo_infos and n > 0: +

      Number of silos: ${n}

      +
      + + + + + + + % for silo in c.silo_infos: + + + + + + % endfor +
      Silo titleNumber of data packages in siloDate silo last modified
      ${c.silo_infos[silo][0]}${c.silo_infos[silo][1]}${c.silo_infos[silo][2]}
      +
      +% else: +

      Number of silos: ${n}

      +% endif diff --git a/rdfdatabank/templates/list_of_zipfile_archives.html b/rdfdatabank/templates/list_of_zipfile_archives.html deleted file mode 100644 index c1dc151..0000000 --- a/rdfdatabank/templates/list_of_zipfile_archives.html +++ /dev/null @@ -1,18 +0,0 @@ -# -*- coding: utf-8 -*- -<%inherit file="/base.html" /> -<%def name="head_tags()"> - List of Data Archives that accept zipped packages - -% if c.silos: -

      List of package upload endpoints: -
        -% for silo in c.silos: -
      • ${silo}

        -<% -c.silo_name = silo -%> -

        <%include file="/upload_package.html"/>

        -
      • -% endfor -
      -% endif diff --git a/rdfdatabank/templates/list_of_zipfiles.html b/rdfdatabank/templates/list_of_zipfiles.html new file mode 100644 index 0000000..2aa7be4 --- /dev/null +++ b/rdfdatabank/templates/list_of_zipfiles.html @@ -0,0 +1,23 @@ +# -*- coding: utf-8 -*- +<%inherit file="/base.html" /> +<%def name="head_tags()"> + List of zipped packages + +% if c.silo_name and c.id and c.zipfiles: +

      List of package unpack endpoints

      +
        +<% c.zipFileCount = 0 %> +% for zf in c.zipfiles: +<% +c.zipFileCount += 1 +c.zipfile = zf +c.newid = c.zipfiles[zf] +%> +
      • ${zf} : +View    |    +Download    +<%include file="/files_unpack.html"/>

        +
      • +% endfor +
      +% endif diff --git a/rdfdatabank/templates/login.html b/rdfdatabank/templates/login.html new file mode 100644 index 0000000..328dccd --- /dev/null +++ b/rdfdatabank/templates/login.html @@ -0,0 +1,53 @@ +# -*- coding: utf-8 -*- +<%def name="head_tags()"> + Login to Databank + + +<%def name="flash()"> + % if session.has_key('login_flash'): +
      +

      ${session.get('login_flash') | n}

       

      +

       

      + <% + del session['login_flash'] + session.save() + %> + % endif + + +<%inherit file="/base.html" /> +

      Login

      +${self.flash()} + +<% + user_logged_in = request.environ.get("repoze.who.identity") + if user_logged_in: + c.user_logged_in_name = user_logged_in['repoze.who.userid'] +%> +% if user_logged_in: + % if c.user_logged_in_name: +

      You are already logged in as ${c.user_logged_in_name}. +Please logout before logging in again.

      + % else: +

      You are already logged in. +Please logout before logging in again.

      + % endif +% else: +

      +

      +
        +
      • + + +
      • +
      • + + +
      • +
      • + +
      • +
      +
      +

      +% endif diff --git a/rdfdatabank/templates/logout.html b/rdfdatabank/templates/logout.html new file mode 100644 index 0000000..7b71542 --- /dev/null +++ b/rdfdatabank/templates/logout.html @@ -0,0 +1,14 @@ +# -*- coding: utf-8 -*- +<%def name="head_tags()"> + Login to Databank + +<%inherit file="/base.html" /> +

      Loggin out

      +

       

      +% if c.message: +
      ${c.message | n}
      +% endif +

      You have successfully logged out.

      +

      To login again click here

      +

       

      + diff --git a/rdfdatabank/templates/package_form_upload.html b/rdfdatabank/templates/package_form_upload.html deleted file mode 100644 index f1de8f4..0000000 --- a/rdfdatabank/templates/package_form_upload.html +++ /dev/null @@ -1,7 +0,0 @@ -# -*- coding: utf-8 -*- -<%inherit file="/base.html" /> -<%def name="head_tags()"> - Package upload for "${c.silo_name}" - -

      Package upload

      -

      <%include file="/upload_package.html"/>

      diff --git a/rdfdatabank/templates/part_list.html b/rdfdatabank/templates/part_list.html index 16971a7..63b5dce 100644 --- a/rdfdatabank/templates/part_list.html +++ b/rdfdatabank/templates/part_list.html @@ -1,17 +1,45 @@ -% if c.parts: -
        -<% -if c.path: - subpath = "/%s" % c.path -else: - subpath = "" -%> -% for part in c.parts: -% if c.parts[part]: -
      • ${part} ${h.bytes_to_english(c.parts[part].st_size)} - (remove)
      • -% else: -
      • ${part} - (remove)
      • -% endif -% endfor -
      -% endif +% if c.parts: +<% +if c.path: + subpath = "/%s" % c.path +else: + subpath = "" + +if c.version and c.versions and c.version == c.versions[-1]: + c.latest = True + urlext = "" +else: + c.latest= False + urlext = "?version=%s"%c.version +%> +
        +% for part in c.parts: +% if not part.startswith('3=') and not part.startswith('4='): +<% +p1 = "_".join(c.silo_name.split(".")) +p2 = "_".join(c.id.split(":")) +p3 = "_".join( "_".join(part.split(".")).split(" ") ) +lid = "%s_%s_%s"%(p1,p2, p3) +if type(c.parts).__name__ == 'dict' and c.parts[part]: + sz = h.bytes_to_english(c.parts[part].st_size) + ext = "(%s)"%sz +else: + ext = "" +%> +
      • ${part} ${ext} + +% if part.endswith('.zip'): + +% endif +% if c.latest: + + + +% endif +
      • +% endif +% endfor + +
      +% endif diff --git a/rdfdatabank/templates/part_list_display.html b/rdfdatabank/templates/part_list_display.html new file mode 100644 index 0000000..6bf8e08 --- /dev/null +++ b/rdfdatabank/templates/part_list_display.html @@ -0,0 +1,32 @@ +% if c.parts: +
        + <% + if c.path: + subpath = "/%s" % c.path + else: + subpath = "" + if c.version: + urlext = "?version=%s" % c.version + else: + urlext = "" + %> + + % for part in c.parts: +<% +if type(c.parts).__name__ == 'dict' and c.parts[part]: + sz = h.bytes_to_english(c.parts[part].st_size) + ext = "(%s)"%sz +else: + ext = "" +%> + % if not part.startswith('3=') and not part.startswith('4='): +
      • ${part} ${ext} + +% if part.endswith('.zip'): + +% endif +
      • + % endif + % endfor +
      +% endif diff --git a/rdfdatabank/templates/raw_search.html b/rdfdatabank/templates/raw_search.html index b254f5a..18dbed0 100644 --- a/rdfdatabank/templates/raw_search.html +++ b/rdfdatabank/templates/raw_search.html @@ -1,16 +1,16 @@ -# -*- coding: utf-8 -*- -<%inherit file="/base.html" /> -<%def name="head_tags()"> - Raw Solr search - -
      - - - - - - - -
      +# -*- coding: utf-8 -*- +<%inherit file="/base.html" /> +<%def name="head_tags()"> + Raw Solr search + +
      + + + + + + + +
      diff --git a/rdfdatabank/templates/rdf_manifest.html b/rdfdatabank/templates/rdf_manifest.html new file mode 100644 index 0000000..f2eb63d --- /dev/null +++ b/rdfdatabank/templates/rdf_manifest.html @@ -0,0 +1,246 @@ +<% +from rdflib import URIRef +%> +% if c.metadata: + + + % if 'title' in c.metadata: + + % endif + + % if 'identifier' in c.metadata: + + % endif + + % if 'description' in c.metadata: + + % endif + + % if 'creator' in c.metadata: + + % endif + + % if 'subject' in c.metadata: + + % endif + + % if 'abstract' in c.metadata: + + % endif + + % if 'created' in c.metadata: + + % endif + + % if 'hasVersion' in c.metadata: + + % endif + + % if 'isVersionOf' in c.metadata: + + % endif + + % if 'license' in c.metadata: + + % endif + + % if 'mediator' in c.metadata: + + % endif + + % if 'modified' in c.metadata: + + % endif + + % if 'publisher' in c.metadata: + + % endif + + % if 'rights' in c.metadata: + + % endif + + % if 'isEmbargoed' in c.metadata: + + % endif + + % if 'embargoedUntil' in c.metadata: + + % endif + + % if 'currentVersion' in c.metadata: + + % endif + + % if 'doi' in c.metadata: + + % endif + + % if 'aggregates' in c.metadata: + + % endif + + ##${c.metadata} + +% endif diff --git a/rdfdatabank/templates/rdf_manifest_form.html b/rdfdatabank/templates/rdf_manifest_form.html index c08a788..70f919e 100644 --- a/rdfdatabank/templates/rdf_manifest_form.html +++ b/rdfdatabank/templates/rdf_manifest_form.html @@ -1,10 +1,12 @@ -# -*- coding: utf-8 -*- -

      -

      - - - -
      -

      +# -*- coding: utf-8 -*- +
      +
      +
      + +
      +
      At the moment, it is only possible to add metadata, but not replace or delete the metadata.
      + +
      +
      diff --git a/rdfdatabank/templates/readme_section.html b/rdfdatabank/templates/readme_section.html index b32dd78..63d299a 100644 --- a/rdfdatabank/templates/readme_section.html +++ b/rdfdatabank/templates/readme_section.html @@ -1,6 +1,6 @@ -# -*- coding: utf-8 -*- -
      -
      -${c.readme_text}
      -
      -
      +# -*- coding: utf-8 -*- +
      +
      +${c.readme_text}
      +
      +
      diff --git a/rdfdatabank/templates/register_new_user.html b/rdfdatabank/templates/register_new_user.html new file mode 100644 index 0000000..f6ae769 --- /dev/null +++ b/rdfdatabank/templates/register_new_user.html @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +
      +
      +
      +
      +
      +
      +
      +
      +
      diff --git a/rdfdatabank/templates/search.html b/rdfdatabank/templates/search.html new file mode 100644 index 0000000..5d7567d --- /dev/null +++ b/rdfdatabank/templates/search.html @@ -0,0 +1,159 @@ +# -*- coding: utf-8 -*- +<%inherit file="/base.html" /> + +<%def name="head_tags()"> + Search + +% if c.search and c.q: + +% endif + + +<%def name="header()"> + +<%def name="footer()"> + + +
      +<%include file="/search_response_display.html"/> +
      + +
      +<% count = 0 %> +
      +% if c.returned_facets: +
      Filter results
      +% for facet in c.returned_facets: +<% count = count + 1 %> +% if c.returned_facets[facet] and len(c.returned_facets[facet]) > 1: +
      + +
      +
        +% for result,value in c.returned_facets[facet]: +
      • +<% +from urllib import quote +try: + res = quote(result) +except: + res = result +if len(result) > 40: + res_label = result[:40] + '...' +else: + res_label = result +%> +${res_label} +(${value})
      • +% endfor +
      +
      +
      +% endif +% endfor +
      +% endif +
      + + + + + + + + diff --git a/rdfdatabank/templates/search_advanced.html b/rdfdatabank/templates/search_advanced.html new file mode 100644 index 0000000..80447ea --- /dev/null +++ b/rdfdatabank/templates/search_advanced.html @@ -0,0 +1,153 @@ +# -*- coding: utf-8 -*- +<%inherit file="/base.html" /> + +<%def name="head_tags()"> + Advanced Search + +% if c.search and c.q: + +% endif + + +<%def name="header()"> + +<%def name="footer()"> + + +
      +<% count = 0 %> +
      +% if c.returned_facets: +% for facet in c.returned_facets: +<% count = count + 1 %> +% if c.returned_facets[facet] and len(c.returned_facets[facet]) > 1: +
      + +
      +
        +% for result,value in c.returned_facets[facet]: +
      • +<% +from urllib import quote +try: + res = quote(result) +except: + res = result +if len(result) > 40: + res_label = result[:40] + '...' +else: + res_label = result +%> +${res_label} +(${value})
      • +% endfor +
      +
      +
      +% endif +% endfor +
      +% endif +
      + + + + + + + diff --git a/rdfdatabank/templates/search_form.html b/rdfdatabank/templates/search_form.html new file mode 100644 index 0000000..8be3e3d --- /dev/null +++ b/rdfdatabank/templates/search_form.html @@ -0,0 +1,44 @@ +# -*- coding: utf-8 -*- + diff --git a/rdfdatabank/templates/search_response_display.html b/rdfdatabank/templates/search_response_display.html new file mode 100644 index 0000000..369cc3b --- /dev/null +++ b/rdfdatabank/templates/search_response_display.html @@ -0,0 +1,195 @@ +<%namespace name="options" file="/search_response_options.html" /> +##============================================================================= +## Current search options +${options.currentsearch()} +##============================================================================= +## Pagination and sort options +% if c.numFound and c.numFound > 0: +
      + ${options.pagination()} + ${options.sortoptions()} +
      +% endif +##============================================================================= +## Number of records, current span of records and items per page +
      + +
      +${ c.numFound} records found. +
      + +% if c.numFound > 0: +
      Showing results ${c.start+1} to +% if (c.start+c.rows) > c.numFound: +${c.numFound} +% else: +${c.start+c.rows} +% endif +
      +% endif + +% if c.numFound > 0: +${options.numresultspp("itemsppt")} +% endif + +
      +##============================================================================= +##Link to these results +% if c.docs and c.add_facet: +
      +% endif +##============================================================================= +##Search results +% if c.docs: +
       
      +
      +<% cnt = 0 %> +% for doc_index in xrange(len(c.docs)): +<% +cnt += 1 +isSilo = False +if 'type' in c.docs[doc_index] and c.docs[doc_index]['type']: + if not isinstance(c.docs[doc_index]['type'], list): + c.docs[doc_index]['type'] = [c.docs[doc_index]['type']] + for typ in c.docs[doc_index]['type']: + if typ.lower() == 'silo': + isSilo = True +%> +

      +% if isSilo == True: +Silo +% else: + +Data package +% endif +% if 'title' in c.docs[doc_index] and c.docs[doc_index]['title'] and c.docs[doc_index]['title'][0]: +${c.docs[doc_index]['title'][0]} +% else: +${c.docs[doc_index]['id']} +% endif +

      + +% if isSilo == False: + + +% for field in c.chosen_fields: +% if field in c.docs[doc_index] and field not in ['silo', 'id', 'title']: +<% +lbl = field +if field in c.field_names: + lbl = c.field_names[field] +%> + +% endif +% endfor +% else: + +% if 'description' in c.docs[doc_index] and c.docs[doc_index]['description'] and c.docs[doc_index]['description']: + + +% endif +% endif +% endif +
      Identifier ${c.docs[doc_index]['id']}
      Silo ${c.docs[doc_index]['silo']}
      ${lbl} +% if isinstance(c.docs[doc_index][field], list): +% for txt in c.docs[doc_index][field]: +% if isinstance(txt, basestring): +<% +todisplay = txt +if c.truncate and len(txt) > c.truncate: + todisplay = txt[:c.truncate] + ' ...' +%> +% if txt.startswith('http'): +${todisplay |n} +% else: +${ todisplay |n} +% endif +% elif isinstance(txt, int) or isinstance(txt, bool): +${str(txt)} +% else: +${txt |n} +% endif +% endfor +% elif isinstance(c.docs[doc_index][field], basestring): +<% + todisplay = c.docs[doc_index][field] + if c.truncate and len(c.docs[doc_index][field]) > c.truncate: + todisplay = c.docs[doc_index][field][:c.truncate] + ' ...' +%> +% if c.docs[doc_index][field].startswith('http'): +${todisplay |n} +% else: +${ todisplay |n} +% endif +% elif isinstance(c.docs[doc_index][field], int) or isinstance(c.docs[doc_index][field], bool): +${str(c.docs[doc_index][field])} +% else: +${c.docs[doc_index][field] |n} +% endif + +
      Silo identifier${c.docs[doc_index]['silo']}
      Description +% if isinstance(c.docs[doc_index]['description'], list): +% for txt in c.docs[doc_index]['description']: +<% +todisplay = txt +if c.truncate and len(txt) > c.truncate: + todisplay = txt[:c.truncate] + ' ...' +%> +${ todisplay |n} +% endfor +% else: +<% +todisplay = c.docs[doc_index]['description'] +if c.truncate and len(todisplay) > c.truncate: + todisplay = todisplay[:c.truncate] + ' ...' +%> +${ todisplay |n} +
      +% endfor +
      +
      +% endif +##============================================================================= +## Pagination and sort options +%if c.numFound and c.numFound > 0: +
      + ${options.pagination()} + ${options.sortoptions()} +
      +% endif +##============================================================================= +## Number of records, current record start and end and items per page +%if c.numFound and c.numFound > 0: +
      + +
      +${ c.numFound} records found. +
      + +% if c.numFound > 0: +
      Showing results ${c.start+1} to +% if (c.start+c.rows) > c.numFound: +${c.numFound} +% else: +${c.start+c.rows} +% endif +
      +% endif + +% if c.numFound > 0: +${options.numresultspp("itemsppb")} +% endif + +
      +% endif +##============================================================================= +
      + + diff --git a/rdfdatabank/templates/search_response_options.html b/rdfdatabank/templates/search_response_options.html new file mode 100644 index 0000000..0ddc157 --- /dev/null +++ b/rdfdatabank/templates/search_response_options.html @@ -0,0 +1,409 @@ +##============================================================================= +<%def name="pagination()"> + % if len(c.permissible_offsets) > 0: +
      + <% + jump = c.rows*10 + lowerJump = c.start-jump + if lowerJump < 0: + lowerJump = 0; + higherJump = c.start+jump + if higherJump > c.lastPage: + higherJump = c.lastPage + %> + + + ## Goto to first page + % if c.start == 0: + + % else: + + % endif +« + + % if c.start != 0: + + % endif + <%! import math %> + <% pagcount = 0.0 %> + % for offset in c.permissible_offsets: + % if c.start == offset: + + % else: + + % endif + <% + offset_start = offset+1 + offset_end = offset+c.rows + if offset_end > c.numFound: + offset_end = c.numFound + %> + <% pagcount = float(offset_end) / float(c.rows) %> + <% pagcount= math.ceil(pagcount) %> + <% pagcount= int(pagcount) %> +${h.link_to(pagcount, "%s&start=%d&rows=%d&sort=%s"%(c.add_facet, offset, c.rows, c.sort))} + + % endfor + % if c.start != c.lastPage: + + % endif + ## Goto to last page + % if c.start == c.lastPage: + + % else: + + % endif +» + + + +
      + % endif +## end pagination() + +##============================================================================= +<%def name="sortoptions()"> +% if c.docs and c.add_facet: +
      +Sort by: + +% if (c.sort == "score desc"): +<% + relstring = c.add_facet + "&start=%d&sort=score+desc&rows=%d"%(c.start, c.rows) + datestring = c.add_facet + "&sort=publicationDate+desc&rows=%d&start=%d"%(c.rows, c.start) + titlestring = c.add_facet + "&sort=silo+desc&rows=%d&start=%d"%(c.rows, c.start) +%> +Relevance | +Date Descending | +Silo Descending + +% elif (c.sort == "publicationDate desc"): +<% + relstring = c.add_facet + "&sort=score+desc&rows=%d&start=%d"%(c.rows, c.start) + datestring = c.add_facet + "&sort=publicationDate+asc&rows=%d&start=%d"%(c.rows, c.start) + titlestring = c.add_facet + "&sort=silo+desc&rows=%d&start=%d"%(c.rows, c.start) +%> +Relevance | +Date Ascending | +Silo Descending + +% elif (c.sort == "publicationDate asc"): +<% + relstring = c.add_facet + "&sort=score+desc&rows=%d&start=%d"%(c.rows, c.start) + datestring = c.add_facet + "&sort=publicationDate+desc&rows=%d&start=%d"%(c.rows, c.start) + titlestring = c.add_facet + "&sort=silo+desc&rows=%d&start=%d"%(c.rows, c.start) +%> +Relevance | +Date Descending | +Silo Descending + +% elif (c.sort == "silo desc"): +<% + relstring = c.add_facet + "&sort=score+desc&rows=%d&start=%d"%(c.rows, c.start) + datestring = c.add_facet + "&sort=publicationDate+desc&rows=%d&start=%d"%(c.rows, c.start) + titlestring = c.add_facet + "&sort=silo+asc&rows=%d&start=%d"%(c.rows, c.start) +%> +Relevance | +Date Descending | +Silo Ascending + +% elif (c.sort == "silo asc"): +<% + relstring = c.add_facet + "&sort=score+desc&rows=%d&start=%d"%(c.rows, c.start) + datestring = c.add_facet + "&sort=publicationDate+desc&rows=%d&start=%d"%(c.rows, c.start) + titlestring = c.add_facet + "&sort=silo+desc&rows=%d&start=%d"%(c.rows, c.start) +%> +Relevance | +Date Descending | +Silo Descending + +% else: +<% + relstring = c.add_facet + "&sort=score+desc&rows=%d&start=%d"%(c.rows,c.start) + datestring = c.add_facet + "&sort=publicationDate+desc&rows=%d&start=%d"%(c.rows, c.start) + titlestring = c.add_facet + "&sort=silo+desc&rows=%d&start=%d"%(c.rows ,c.start) +%> +Relevance | +Date Descecding | +Silo Descecding + +% endif + +
      +% endif +## end sortoptions() + +##============================================================================= +<%def name="numresultspp(idname)"> +% if c.docs and c.add_facet: +
      +
      +Items per page: +<% + twentyfstring = c.add_facet + "&rows=25&sort=%s"%c.sort + fiftystring = c.add_facet + "&rows=50&sort=%s"%c.sort + hundredstring = c.add_facet + "&rows=100&sort=%s"%c.sort +%> +% if (c.rows == 25): +25 +50 +100 +
      + + +% if c.chosen_facets: +% for chosen_facet in c.chosen_facets: + <% + current_items = [] + if isinstance(c.chosen_facets[chosen_facet], list): + current_items = c.chosen_facets[chosen_facet] + else: + current_items = [c.chosen_facets[chosen_facet]] + %> +% for chosen_item in current_items: + <% + from urllib import quote + try: + res = '"%s"'%quote(chosen_item.strip()) + except: + res = '"%s"'%chosen_item.strip() + %> + +% endfor +% endfor +% endif + +
      +% elif (c.rows == 50): +25 +50 +100 +
      + + +% if c.chosen_facets: +% for chosen_facet in c.chosen_facets: + <% + current_items = [] + if isinstance(c.chosen_facets[chosen_facet], list): + current_items = c.chosen_facets[chosen_facet] + else: + current_items = [c.chosen_facets[chosen_facet]] + %> +% for chosen_item in current_items: + <% + from urllib import quote + try: + res = '"%s"'%quote(chosen_item.strip()) + except: + res = '"%s"'%chosen_item.strip() + %> + +% endfor +% endfor +% endif + +
      +% elif (c.rows == 100): +25 +50 +100 +
      + + +% if c.chosen_facets: +% for chosen_facet in c.chosen_facets: + <% + current_items = [] + if isinstance(c.chosen_facets[chosen_facet], list): + current_items = c.chosen_facets[chosen_facet] + else: + current_items = [c.chosen_facets[chosen_facet]] + %> +% for chosen_item in current_items: + <% + from urllib import quote + try: + res = '"%s"'%quote(chosen_item.strip()) + except: + res = '"%s"'%chosen_item.strip() + %> + +% endfor +% endfor +% endif + +
      +% else: +25 +50 +100 +
      + + +% if c.chosen_facets: +% for chosen_facet in c.chosen_facets: + <% + current_items = [] + if isinstance(c.chosen_facets[chosen_facet], list): + current_items = c.chosen_facets[chosen_facet] + else: + current_items = [c.chosen_facets[chosen_facet]] + %> +% for chosen_item in current_items: + <% + from urllib import quote + try: + res = '"%s"'%quote(chosen_item.strip()) + except: + res = '"%s"'%chosen_item.strip() + %> + +% endfor +% endfor +% endif + +
      +% endif +
      +
      +% endif + +##============================================================================= +<%def name="currentsearch()"> +## Current search + + +##============================================================================= diff --git a/rdfdatabank/templates/searching.html b/rdfdatabank/templates/searching.html new file mode 100644 index 0000000..03a66e5 --- /dev/null +++ b/rdfdatabank/templates/searching.html @@ -0,0 +1,7 @@ +# -*- coding: utf-8 -*- +<%inherit file="/base.html" /> +<%def name="head_tags()"> + Search + + +

      Search interface goes here...

      \ No newline at end of file diff --git a/rdfdatabank/templates/silo_admin.html b/rdfdatabank/templates/silo_admin.html index 186e1fb..0b25a9f 100644 --- a/rdfdatabank/templates/silo_admin.html +++ b/rdfdatabank/templates/silo_admin.html @@ -1,14 +1,17 @@ -# -*- coding: utf-8 -*- -<%inherit file="/base.html" /> -<%def name="head_tags()"> - List of Data Archives - -% if c.granary_list: -

      Admin Archives: - -% endif -<%include file="/create_new_silo.html"/> +# -*- coding: utf-8 -*- +<%inherit file="/base.html" /> +<%def name="head_tags()"> + Admin Interface - List of Silos + +

      Admin Interface - List of Silos

      +% if c.granary_list: +

      Admin Archives:

      + +% endif +<%include file="/create_new_silo.html"/> diff --git a/rdfdatabank/templates/silo_metadata.html b/rdfdatabank/templates/silo_metadata.html new file mode 100644 index 0000000..3b0766b --- /dev/null +++ b/rdfdatabank/templates/silo_metadata.html @@ -0,0 +1,57 @@ +# -*- coding: utf-8 -*- + +

      diff --git a/rdfdatabank/templates/silo_user.html b/rdfdatabank/templates/silo_user.html new file mode 100644 index 0000000..3c9fa4e --- /dev/null +++ b/rdfdatabank/templates/silo_user.html @@ -0,0 +1,96 @@ +# -*- coding: utf-8 -*- +<%inherit file="/base.html" /> +<%def name="head_tags()"> + User Info - ${c.username} + + + + +

      User info for ${c.username} in silo ${c.silo}

      +% if c.message: +

      ${c.message}

      +% endif + +% if c.user: + +
      +

      Edit options

      + +
      + + + + + +% if 'administrator' in c.ident['permissions'] or 'manager' in c.ident['permissions']: + +% endif + + +% if c.ident['user'].user_name == c.username: + +% endif +% endif + diff --git a/rdfdatabank/templates/silo_users.html b/rdfdatabank/templates/silo_users.html new file mode 100644 index 0000000..60c0a33 --- /dev/null +++ b/rdfdatabank/templates/silo_users.html @@ -0,0 +1,71 @@ +# -*- coding: utf-8 -*- +<%inherit file="/base.html" /> +<%def name="head_tags()"> + Users - ${c.silo} + + + +

      Users in ${c.silo}

      +% if c.message: +

      ${c.message}

      +% endif + + + + +%if c.users: + +% endif + + + + + diff --git a/rdfdatabank/templates/silos_api.html b/rdfdatabank/templates/silos_api.html new file mode 100644 index 0000000..3b1b6db --- /dev/null +++ b/rdfdatabank/templates/silos_api.html @@ -0,0 +1,61 @@ +# -*- coding: utf-8 -*- +
      +

      Silos

      +

      Obtain a list of all silos visible to your account or a list of datasets in a silo

      +

      Sections

      +
        +
      1. /silos
      2. +
      3. /{silo name}
      4. +
      + +
      +
      +
      /silos
      + +
      +
      +

      Returns a list of silos visible to your account

      +

      Controller: silos     action: index

      +
      +

      GET

      +
      Returns 401 if not a valid user
      +
      Accept: text/html (with auth)
      +
      Returns text/HTML listing the 'silos' your account can see, with links to dataset view
      +
      Accept: text/plain, application/json (with auth)
      +
      Returns a JSON-encoded list of exising silos your account can see.
      +
      The file silos.txt contains an example of the data returned (data_returned)
      +
      Accept: */*, default (with auth)
      Returns text/HTML listing the ’silos’ your account can see, with links to dataset view
      +
      +
      +

      POST, PUT, DELETE NOOP

      +
      + + +
      +
      +
      /{silo name}
      + +
      +
      +

      Returns a list of datasets in the silo

      +

      Controller: silos     action: siloview

      +
      +

      GET

      +
      Returns 401 if not a valid user and 403 if not authorized
      +
      Accept:text/html (with auth)
      +
      Returns text/HTML +listing the ids of each dataset, along with a form for changing the embargo information and deleting the dataset. +A form for dataset creation is also available.
      +
      Accept: text/plain, application/json (with auth)
      +
      Returns a JSON-encoded list of dataset ids in that silo, along with the embargo information for each dataset
      +
      The file siloInformation.txt contains an example of the data returned (data_returned)
      +
      Accept:*/*, default (with auth)
      +
      Returns text/HTML +listing the ids of each dataset, along with a form for changing the embargo information and deleting the dataset. +A form for dataset creation is also available.
      +
      +
      +

      POST, PUT, DELETE NOOP

      +
      + +
      diff --git a/rdfdatabank/templates/siloview.html b/rdfdatabank/templates/siloview.html index 438ba5a..c7388d8 100644 --- a/rdfdatabank/templates/siloview.html +++ b/rdfdatabank/templates/siloview.html @@ -1,32 +1,24 @@ -# -*- coding: utf-8 -*- -<%inherit file="/base.html" /> -<%def name="head_tags()"> - List of Data Archives - -% if c.silo_name: -
        -% for key in ['title', 'description','owner']: -% if c.silo.state.has_key(key): -
      • ${key.capitalize()} - ${c.silo.state[key]}
      • -% endif -% endfor -
      • Items:
          -% for item in sorted(c.items): -
        • - ${item} -
          • Delete item
          • -<% -c.current = item -%> -
          • <%include file="/embargo_form.html"/>
          • -
          -
        • -% endfor -
        -
      • -
          -% endif -

          Create new empty object:

          -

          -<%include file="create_new_item.html"/> -

          +# -*- coding: utf-8 -*- +<%inherit file="/base.html" /> +<%def name="head_tags()"> + List of Data packages + +% if c.title: +

          Silo ${c.title}

          +% elif c.silo_name: +

          Silo ${c.silo_name}

          +% endif +% if c.editor: +
          +

          Create new data package

          + <%include file="create_new_item.html"/> +
          +% endif +% if c.title: +

          List of Data packages in ${c.title}

          +% elif c.silo_name: +

          List of Data packages in ${c.silo_name}

          +% else: +

          List of Data packages

          +% endif +<%include file="list_of_datasets.html"/> diff --git a/rdfdatabank/templates/states_api.html b/rdfdatabank/templates/states_api.html new file mode 100644 index 0000000..2fcd3e7 --- /dev/null +++ b/rdfdatabank/templates/states_api.html @@ -0,0 +1,126 @@ +# -*- coding: utf-8 -*- +
          +

          States

          +

          API call to obtain information regarding the state of a silo, the state of the latest version of a dataset or the state of a particular version of a dataset.

          + +

          Sections

          +
            + +
          1. /{silo name}/states
          2. +
          3. /{silo name}/states/{id}
          4. +
          5. /{silo name}/states/{id}/version#
          6. +
          + + +
          +
          +
          /{silo name}/states
          + +
          +
          +

          Returns the state information of a silo. To view an example of the state information returned for the silo Sandbox (ans["silo"]) see SiloStateInfo.txt

          +

          The state information for a silo contains the following:

          +
          +Name of the silo (machine name, used in uris) - ans["silo"]
          +Base URI for the silo - ans["uri_base"]
          +Users who can access the silo (silo owners) - ans["owners"]
          +Silo description - ans["description"]
          +Title of the silo (human readable) - ans["title"]
          +Disk allocation for the silo (in kB) - ans["disk_allocation"]
          +List of datasets in the silo with embargo information for each of the datasets - ans["datasets"], ans["datasets"]["dataset_name"]["embargo_info"] +
          +
          +
          Note:
          +
          Disk allocation information is not used at the moment. In the future, this will be used to calculate if current levels of disk usage is within the allocation limit and warn or prevent users from submitting data if beyond this limit. Also, the unit will change from kB to MB.
          +
          +

          Controller: states     action: siloview

          +
          +

          GET

          +
          Returns 401 if not a valid user and 403 if not authorized
          +
          Accept: */*, default (with auth)
          Returns a JSON-encoded hash/dict, keys map +with the silo name, base uri and embargo information for each of the datasets in the silo {silo name} as text/plain
          +
          +
          +

          POST, PUT, DELETE NOOP

          +
          + + +
          +
          +
          /{silo name}/states/{id}
          + +
          +
          +

          Returns the state information of the latest version of a dataset. To view an example of the state information returned for the dataset dataset2 see DatasetStateInfo-dataset2.txt and dataset1 see DatasetStateInfo-dataset1-version1.txt

          +

          The state information for a dataset contains the following:

          +

          Information about the dataset

          +
          +list of files in each version - ans["state"]["files"]
          +list of sub-directories in each version - ans["state"]["subdir"]
          +List of available versions - ans["state"]["versions]
          +manifest file format - ans["state"]["rdffileformat"]
          +manifest file name - ans["state"]["rdffilename"]
          +Metadata for the dataset - ans["state"]["metadata"].
          +
          + createdby - ans["state"]["metadata"]["createdby"]
          + uuid - ans["state"]["metadata"]["uuid"]
          + embargo date - ans["state"]["metadata"]["embargoed_until"]
          + State of embargo (true | false) - ans["state"]["metadata"]["embargoed"] +
          +Id of the dataset - ans["state"]["item_id"]
          +Current version of the dataset - ans["state"]["currentversion"]
          +Metadata files for each version - ans["state"]["metadata_files"] - This feature is not used at the moment
          +Dates when each version was created - ans["state"]["version_dates"]
          +Date last modified - ans["state"]["date"]

          +
          +

          Information about each file in the latest version of the dataset

          +
          +ans["parts"]["file_name"] contains file information for each of the files listed in ans["state"]["files"]["#"] (# is the current version number), including the Namaste files that are generated and the databank metadata file manifest.rdf

          +The Nameaste file generated in Databank are
          +
          + 3=when - Date last modified (example: 3=2011-02-09T14+15+05,064235)
          + 4=where - name of dataset (example: 4=dataset1)
          +

          +The file information returns the equivalent of a stat() system call on the given path. See http://docs.python.org/library/os.html#os.stat for more information. +
          +
          +
          TODO:
          +
          Currently the state information returns information regarding all versions for some. Modify to return information peraining only to the relevant version.
          +
          The name of metadata files are not included in the state information. Need to add files mentioned in 'seeAlso' to ans["state"]["metadata_files"]
          +
          +

          Controller: states     action: datasetview

          +
          +

          GET

          +
          Returns 401 if not a valid user and 403 if not authorized
          +
          Returns 404 if dataset id does not exist
          +
          Accept: */*, default (with auth)
          Returns a JSON-encoded hash/dict, keys map with the detailed state information of the latest version of the dataset id as text/plain
          +
          +
          +

          POST, PUT, DELETE NOOP

          +
          + +
          +
          +
          /{silo name}/states/{id}/version#
          + +
          +
          +

          Returns the state of a particular version of a dataset. To view an example of the state information returned for verison 0 of the dataset dataset1 see DatasetStateInfo-dataset1-version0.txt. For details pertaining to the information returned, see above

          +
          +
          TODO:
          +
          Currently the state information returns information regarding all versions for some. Modify to return information peraining only to the relevant version.
          +
          The name of metadata files are not included in the state information. Need to add files mentioned in 'seeAlso' to ans["state"]["metadata_files"]
          +
          +

          Controller: states     action: datasetview_vnum

          +
          +

          GET

          +
          Returns 401 if not a valid user and 403 if not authorized
          +
          Returns 404 if dataset id does not exist
          +
          Returns 404 if dataset version number # does not exist
          +
          Accept: */*, default (with auth)
          Returns a JSON-encoded hash/dict, keys map with the detailed state information of the version # of the dataset id as text/plain
          +
          +
          +

          POST, PUT, DELETE NOOP

          +
          + +
          diff --git a/rdfdatabank/templates/subitemview.html b/rdfdatabank/templates/subitemview.html deleted file mode 100644 index 3a41b60..0000000 --- a/rdfdatabank/templates/subitemview.html +++ /dev/null @@ -1,11 +0,0 @@ -# -*- coding: utf-8 -*- -<%inherit file="/base.html" /> -<%def name="head_tags()"> - View of item ${c.item.uri} - -% if c.readme_text: -<%include file="/readme_section.html"/> -% endif -% if c.item: -<%include file="/part_list.html"/> -% endif diff --git a/rdfdatabank/templates/successful_package_upload.html b/rdfdatabank/templates/successful_package_upload.html deleted file mode 100644 index 08aa094..0000000 --- a/rdfdatabank/templates/successful_package_upload.html +++ /dev/null @@ -1,10 +0,0 @@ -# -*- coding: utf-8 -*- -<%inherit file="/base.html" /> -<%def name="head_tags()"> - Package uploaded to "${c.silo_name}" - -

          SUCCESS!

          -

          Package uploaded successfully

          -

          Zipfile stored in object: ${c.info['zip_id']}

          -

          Uploaded zipfile size: ${h.bytes_to_english(c.info['zip_file_size'])} -

          Resultant unpacked object: ${c.info['zip_target']}

          diff --git a/rdfdatabank/templates/update_user.html b/rdfdatabank/templates/update_user.html new file mode 100644 index 0000000..b9b815d --- /dev/null +++ b/rdfdatabank/templates/update_user.html @@ -0,0 +1,34 @@ +# -*- coding: utf-8 -*- +
          +
          +
          + +% if c.user and 'firstname' in c.user: +
          +
          +% else: +
          +% endif + +% if c.user and 'lastname' in c.user: +
          +
          +% else: +
          +% endif + +% if c.user and 'name' in c.user: +
          +% else: +
          +% endif + +% if c.user and 'email' in c.user: +
          +% else: +
          +% endif + +
          +
          +
          diff --git a/rdfdatabank/templates/update_user_role.html b/rdfdatabank/templates/update_user_role.html new file mode 100644 index 0000000..bed65a0 --- /dev/null +++ b/rdfdatabank/templates/update_user_role.html @@ -0,0 +1,30 @@ +# -*- coding: utf-8 -*- +<% + roles = [] + if c.user and 'groups' in c.user and c.user['groups']: + for g, p in c.user['groups']: + roles.append(p) +%> +
          +
          + +% else: + Administrator +% endif + +% else: + Manager +% endif + +% else: + Submitter +% endif +
          +
          +
          diff --git a/rdfdatabank/templates/upload_package.html b/rdfdatabank/templates/upload_package.html deleted file mode 100644 index 7d63c45..0000000 --- a/rdfdatabank/templates/upload_package.html +++ /dev/null @@ -1,6 +0,0 @@ -# -*- coding: utf-8 -*- -
          - - - -
          diff --git a/rdfdatabank/templates/users.html b/rdfdatabank/templates/users.html new file mode 100644 index 0000000..72ed275 --- /dev/null +++ b/rdfdatabank/templates/users.html @@ -0,0 +1,79 @@ +# -*- coding: utf-8 -*- +<%inherit file="/base.html" /> +<%def name="head_tags()"> + Users + + + +

          Users

          +% if c.message: +

          ${c.message}

          +% endif + +
          +

          Edit options

          + +
          + +% if c.users: + % for user in c.users: +

          ${user['user_name']} + % if not ('groups' in user and user['groups']): + + Delete user + + % endif +

          + + % endfor +% endif + + + diff --git a/rdfdatabank/templates/zipfilesubitemview.html b/rdfdatabank/templates/zipfilesubitemview.html new file mode 100644 index 0000000..208574b --- /dev/null +++ b/rdfdatabank/templates/zipfilesubitemview.html @@ -0,0 +1,9 @@ +# -*- coding: utf-8 -*- +<%inherit file="/base.html" /> +<%def name="head_tags()"> + Contents of zipfile ${c.path}/${c.subpath} + +% if c.silo_name and c.id and c.path and c.subpath: +

          Contents of zipfile ${c.path}/${c.subpath}

          +

          TO BE DONE

          +% endif diff --git a/rdfdatabank/templates/zipfileview.html b/rdfdatabank/templates/zipfileview.html new file mode 100644 index 0000000..5ef4987 --- /dev/null +++ b/rdfdatabank/templates/zipfileview.html @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +<%inherit file="/base.html" /> +<%def name="head_tags()"> + Contents of zipfile ${c.path} + +% if c.silo_name and c.id and c.path: +

          Contents of zipfile ${c.path}

          + % if c.zipfile_contents: +
            + % for zf in c.zipfile_contents: +
          • + ##

            ${zf} ( ${h.bytes_to_english(c.zipfile_contents[zf][0])} )

            +

            ${zf} ( ${h.bytes_to_english(c.zipfile_contents[zf][0])} )

            +
          • + % endfor +
          + % else: +

          There are no contents avaialble

          + % endif +% endif diff --git a/rdfdatabank/tests/RDFDatabankConfig-Jenkins.py b/rdfdatabank/tests/RDFDatabankConfig-Jenkins.py new file mode 100644 index 0000000..7e9be8a --- /dev/null +++ b/rdfdatabank/tests/RDFDatabankConfig-Jenkins.py @@ -0,0 +1,82 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +""" +Copyright (c) 2012 University of Oxford + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, --INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +""" + +""" +Define configuration for RDFDatabank testing +""" + +class RDFDatabankConfig: + + granary_uri_root="http://dataflow-jenkins.bodleian.ox.ac.uk" + + # Access to local VM + endpointhost="localhost:5000" + + endpointpath="/sandbox/" + endpointpath2="/sandbox2/" + + # Access credentials for testing from local dev VM as user + endpointuser="sandbox_user" + endpointpass="sandbox" + + # Access credentials for testing from local dev VM as admin + #endpointadminuser="admin" + #endpointadminpass="test" + + #Admin1 of silo1 + endpointadminuser="admin" + endpointadminpass="test" + #Admin2 of silo1 + endpointadminuser2="admin2" + endpointadminpass2="test2" + #Admin3 of silo2 + endpointadminuser3="admin3" + endpointadminpass3="test3" + + #Manager1 of silo1 + endpointmanageruser="sandbox_manager" + endpointmanagerpass="managertest" + #Manager2 of silo1 + endpointmanageruser2="sandbox_manager2" + endpointmanagerpass2="managertest2" + #Manager3 of silo2 + endpointmanageruser3="sandbox_manager3" + endpointmanagerpass3="managertest3" + + #Submitter1 of silo1 + endpointsubmitteruser="sandbox_user" + endpointsubmitterpass="sandbox" + #Submitter2 of silo1 + endpointsubmitteruser2="sandbox_user2" + endpointsubmitterpass2="sandbox2" + #Submitter3 of silo2 + endpointsubmitteruser3="sandbox_user3" + endpointsubmitterpass3="sandbox3" + + #Access credentials for generic user + endpointgeneraluser = "" + endpointgeneralpass = "" + +# End. diff --git a/rdfdatabank/tests/RDFDatabankConfig.py b/rdfdatabank/tests/RDFDatabankConfig.py new file mode 100644 index 0000000..1d6c5e9 --- /dev/null +++ b/rdfdatabank/tests/RDFDatabankConfig.py @@ -0,0 +1,78 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +""" +Copyright (c) 2012 University of Oxford + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, --INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +""" + +""" +Define configuration for RDFDatabank testing +""" + +class RDFDatabankConfig: + + granary_uri_root="http://databank" + + # Access via IP address + endpointhost="localhost" + endpointpath="/sandbox/" + endpointpath2="/sandbox2/" + + endpointuser="sandbox_user" + endpointpass="sandbox" + + #Admin1 of silo1 + endpointadminuser="admin" + endpointadminpass="test" + #Admin2 of silo1 + endpointadminuser2="admin2" + endpointadminpass2="test2" + #Admin3 of silo2 + endpointadminuser3="admin3" + endpointadminpass3="test3" + + #Manager1 of silo1 + endpointmanageruser="sandbox_manager" + endpointmanagerpass="managertest" + #Manager2 of silo1 + endpointmanageruser2="sandbox_manager2" + endpointmanagerpass2="managertest2" + #Manager3 of silo2 + endpointmanageruser3="sandbox_manager3" + endpointmanagerpass3="managertest3" + + #Submitter1 of silo1 + endpointsubmitteruser="sandbox_user" + endpointsubmitterpass="sandbox" + #Submitter2 of silo1 + endpointsubmitteruser2="sandbox_user2" + endpointsubmitterpass2="sandbox2" + #Submitter3 of silo2 + endpointsubmitteruser3="sandbox_user3" + endpointsubmitterpass3="sandbox3" + + #Access credentials for generic user + endpointgeneraluser = "" + endpointgeneralpass = "" + + # Later, may define methods to override these defaults, e.g. from a configuration file + +# End. diff --git a/rdfdatabank/tests/TestAdmin.py b/rdfdatabank/tests/TestAdmin.py new file mode 100644 index 0000000..88e03aa --- /dev/null +++ b/rdfdatabank/tests/TestAdmin.py @@ -0,0 +1,234 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +""" +Copyright (c) 2012 University of Oxford + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, --INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +""" + +""" +Databank submission test cases +""" + +import os, os.path +from datetime import datetime, timedelta +import sys +import unittest +import logging +import httplib +import urllib +try: + # Running Python 2.5 with simplejson? + import simplejson as json +except ImportError: + import json + +from StringIO import StringIO + +from rdflib import RDF, URIRef, Literal +#from rdflib.Graph import ConjunctiveGraph as Graph +from rdflib import ConjunctiveGraph as Graph + +if __name__ == "__main__": + # For testing: + # add main library directory to python path if running stand-alone + sys.path.append("..") + +#from MiscLib import TestUtils +from testlib import TestUtils +from testlib import SparqlQueryTestCase + +from RDFDatabankConfig import RDFDatabankConfig + +logger = logging.getLogger('testAdmin') + +class TestAdmin(SparqlQueryTestCase.SparqlQueryTestCase): + """ + Test simple dataset submissions to RDFDatabank + """ + def setUp(self): + #super(TestAdmin, self).__init__() + self.setRequestEndPoint( + endpointhost=RDFDatabankConfig.endpointhost, + endpointpath=RDFDatabankConfig.endpointpath) + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointadminuser, + endpointpass=RDFDatabankConfig.endpointadminpass) + self.setRequestUriRoot( + manifesturiroot=RDFDatabankConfig.granary_uri_root) + self.testsilo = 'tessst' + return + + def tearDown(self): + return + + # Actual tests follow + def test1CreateSilo(self): + """List all silos your account has access to - GET /silo""" + #Write a test to list all the silos. Test to see if it returns 200 OK and the list of silos is not empty + # Access list silos, check response + (resp, data) = self.doHTTP_GET( + endpointpath="/", + resource="admin/", + expect_status=200, expect_reason="OK", expect_type="application/JSON") + #Create new silo + silolist = data + fields = \ + [ ("silo", self.testsilo), + ("title", "Sandbox silo"), + ("description", "Sandbox silo for testing"), + ("notes", "Created by test"), + ("owners", RDFDatabankConfig.endpointadminuser), + ("disk_allocation", "100000") + ] + files =[] + (reqtype, reqdata) = SparqlQueryTestCase.encode_multipart_formdata(fields, files) + (resp,respdata) = self.doHTTP_POST( + reqdata, reqtype, resource="admin/", endpointpath="/", + expect_status=201, expect_reason="Created") + LHobtained = resp.getheader('Content-Location', None) + LHexpected = "/%s"%self.testsilo + self.assertEquals(LHobtained, LHexpected, 'Content-Location not correct') + # Access list silos, check response + (resp, data) = self.doHTTP_GET( + endpointpath="/", + resource="admin/", + expect_status=200, expect_reason="OK", expect_type="application/json") + #newsilolist = [] + #for k in data: + # newsilolist.append(k) + newsilolist = data + self.failUnless(len(newsilolist)>0, "No silos returned") + self.assertEquals(len(newsilolist), len(silolist)+1, "One additional silo should have been returned") + for s in silolist: self.failUnless(s in newsilolist, "Silo "+s+" in original list, not in new list") + self.failUnless(self.testsilo in newsilolist, "Silo "+self.testsilo+" not in new list") + return + + def test2SiloInfo(self): + """Get admin informaton of a silo - GET /silo_name/admin""" + # Access silo information in admin, check response + (resp, data) = self.doHTTP_GET( + endpointpath="/%s/"%self.testsilo, + resource="admin/", + expect_status=200, expect_reason="OK", expect_type="application/json") + # check silo name and base_uri + self.assertEqual(data['title'], "Sandbox silo", "Silo title is %s not 'Sandbox silo'" %data['title']) + self.assertEqual(data['description'], "Sandbox silo for testing", "Silo title is '%s' not 'Sandbox silo for testing'" %data['description']) + self.assertEqual(data['notes'], "Created by test", "Silo notes is '%s' not 'Created by test'" %data['notes']) + self.assertEqual(data['owners'], RDFDatabankConfig.endpointadminuser, "Silo owner is '%s' not '%s'" %(data['owners'], RDFDatabankConfig.endpointadminuser)) + self.assertEqual(data['disk_allocation'], "100000", "Silo title is '%s' not '100000'" %data['disk_allocation']) + return + + def test3UpdateSiloInfo(self): + """Update silo metadata""" + fields = \ + [ ("silo", self.testsilo), + ("title", "Sandbox silo"), + ("description", "Sandbox silo for testing"), + ("notes", "Created by test"), + ("owners", "%s,%s"%(RDFDatabankConfig.endpointadminuser,RDFDatabankConfig.endpointuser)), + ("disk_allocation", "200000") + ] + files =[] + (reqtype, reqdata) = SparqlQueryTestCase.encode_multipart_formdata(fields, files) + (resp,respdata) = self.doHTTP_POST(reqdata, reqtype, + endpointpath="/%s/"%self.testsilo, resource="admin/", + expect_status=204, expect_reason="Updated", expect_type="text/plain") + # Access silo information in admin, check response + (resp, data) = self.doHTTP_GET( + endpointpath="/%s/"%self.testsilo, + resource="admin/", + expect_status=200, expect_reason="OK", expect_type="application/json") + # check silo name and base_uri + self.assertEqual(data['title'], "Sandbox silo", "Silo title is %s not 'Sandbox silo'" %data['title']) + self.assertEqual(data['description'], "Sandbox silo for testing", "Silo title is '%s' not 'Sandbox silo for testing'" %data['description']) + self.assertEqual(data['notes'], "Created by test", "Silo notes is '%s' not 'Created by test'" %data['notes']) + self.assertEqual(data['owners'], "%s,%s"%(RDFDatabankConfig.endpointadminuser,RDFDatabankConfig.endpointuser), "Silo owner is '%s' not '%s,%s'" %(data['owners'], RDFDatabankConfig.endpointadminuser,RDFDatabankConfig.endpointuser)) + self.assertEqual(data['disk_allocation'], "200000", "Silo title is '%s' not '200000'" %data['disk_allocation']) + return + + def test4DeleteSilo(self): + """Delete silo test""" + #Delete Silo + resp = self.doHTTP_DELETE( + endpointpath="/%s/"%self.testsilo, + resource="admin/", + expect_status=200, expect_reason="OK") + # Access silo, check response + (resp, data) = self.doHTTP_GET( + endpointpath="/%s/"%self.testsilo, + resource="admin/", + expect_status=403, expect_reason="Forbidden") + return + + # Sentinel/placeholder tests + + def testUnits(self): + assert (True) + + def testComponents(self): + assert (True) + + def testIntegration(self): + assert (True) + + def testPending(self): + #Need to have performance tests and analyse performance + #Need to set the permission of file being uploaded + #assert (False), "Pending tests follow" + assert (True) + +# Assemble test suite + +def getTestSuite(select="unit"): + """ + Get test suite + + select is one of the following: + "unit" return suite of unit tests only + "component" return suite of unit and component tests + "all" return suite of unit, component and integration tests + "pending" return suite of pending tests + name a single named test to be run + """ + testdict = { + "unit": + [ "testUnits" + , "test1CreateSilo" + , "test2SiloInfo" + , "test3UpdateSiloInfo" + , "test4DeleteSilo" + ], + "component": + [ "testComponents" + ], + "integration": + [ "testIntegration" + ], + "pending": + [ "testPending" + ] + } + return TestUtils.getTestSuite(TestAdmin, testdict, select=select) + +if __name__ == "__main__": + TestUtils.runTests("TestAdmin.log", getTestSuite, sys.argv) + +# End. diff --git a/rdfdatabank/tests/TestSubmission.py b/rdfdatabank/tests/TestSubmission.py new file mode 100644 index 0000000..496adc5 --- /dev/null +++ b/rdfdatabank/tests/TestSubmission.py @@ -0,0 +1,4517 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +""" +Copyright (c) 2012 University of Oxford + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, --INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +""" + +""" +Databank submission test cases +""" + +import os, os.path +from datetime import datetime, timedelta +from dateutil.relativedelta import * +import sys +import unittest +import logging +import httplib +import urllib +import codecs +try: + # Running Python 2.5 with simplejson? + import simplejson as json +except ImportError: + import json + +#My system is running rdflib version 2.4.2. So adding rdflib v3.0 to sys path +#rdflib_path = os.path.join(os.getcwd(), 'rdflib') +#sys.path.insert(0, rdflib_path) +#import rdflib +#from rdflib.namespace import RDF +#from rdflib.graph import Graph +#from rdflib.plugins.memory import Memory +#from rdflib import URIRef +#from rdflib import Literal +#rdflib.plugin.register('sparql',rdflib.query.Processor,'rdfextras.sparql.processor','Processor') +#rdflib.plugin.register('sparql', rdflib.query.Result, +# 'rdfextras.sparql.query', 'SPARQLQueryResult') + +from StringIO import StringIO + +from rdflib import RDF, URIRef, Literal +from rdflib.Graph import ConjunctiveGraph as Graph + +#from time import sleep +#import subprocess + +if __name__ == "__main__": + # For testing: + # add main library directory to python path if running stand-alone + sys.path.append("..") + +#from MiscLib import TestUtils +from testlib import TestUtils +from testlib import SparqlQueryTestCase + +from RDFDatabankConfig import RDFDatabankConfig as RC +RDFDatabankConfig = RC() + +logger = logging.getLogger('TestSubmission') +#hdlr = logging.FileHandler('TestSubmission.log') +#fmt = logging.Formatter('%(asctime)s %(levelname)s %(message)s') +#hdlr.setFormatter(fmt) +#logger.addHandler(hdlr) +#logger.setLevel(logging.DEBUG) + +class TestSubmission(SparqlQueryTestCase.SparqlQueryTestCase): + """ + Test simple dataset submissions to RDFDatabank + """ + def setUp(self): + self.setRequestEndPoint( + endpointhost=RDFDatabankConfig.endpointhost, # Via SSH tunnel + endpointpath=RDFDatabankConfig.endpointpath) + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointuser, + endpointpass=RDFDatabankConfig.endpointpass) + self.setRequestUriRoot( + manifesturiroot=RDFDatabankConfig.granary_uri_root) + resp = self.doHTTP_DELETE( + resource="datasets/TestSubmission", + expect_status="*", expect_reason="*") + return + + def tearDown(self): + return + + # Create empty test submission dataset + def createSubmissionDataset(self, embargoed=None, embargoed_until=None): + # Create a new dataset, check response + fields = \ + [ ("id", "TestSubmission") + ] + if embargoed != None: + if embargoed: + fields.append(('embargoed', 'True')) + else: + fields.append(('embargoed', 'False')) + if embargoed_until != None: + if embargoed_until == True: + fields.append(('embargoed_until', 'True')) + elif embargoed_until == False: + fields.append(('embargoed_until', 'False')) + else: + fields.append(('embargoed_until', embargoed_until)) + files =[] + (reqtype, reqdata) = SparqlQueryTestCase.encode_multipart_formdata(fields, files) + (resp,respdata) = self.doHTTP_POST( + reqdata, reqtype, + resource="datasets/", + expect_status=201, expect_reason="Created") + LHobtained = resp.getheader('Content-Location', None) + LHexpected = "%sdatasets/TestSubmission"%self._endpointpath + self.assertEquals(LHobtained, LHexpected, 'Content-Location not correct') + return + + def uploadSubmissionZipfile(self, file_to_upload="testdir.zip"): + # Submit ZIP file, check response + fields = [] + zipdata = open("testdata/%s"%file_to_upload).read() + files = \ + [ ("file", file_to_upload, zipdata, "application/zip") + ] + (reqtype, reqdata) = SparqlQueryTestCase.encode_multipart_formdata(fields, files) + (resp,respdata) = self.doHTTP_POST( + reqdata, reqtype, + resource="datasets/TestSubmission/", + expect_status=201, expect_reason="Created") + LHobtained = resp.getheader('Content-Location', None) + LHexpected = "%sdatasets/TestSubmission/%s"%(self._endpointpath, file_to_upload) + self.assertEquals(LHobtained, LHexpected, 'Content-Location not correct') + return zipdata + + def updateSubmissionZipfile(self, file_to_upload="testdir.zip", filename=None): + # Submit ZIP file, check response + fields = [] + if filename: + fields = \ + [ ("filename", filename) + ] + zipdata = open("testdata/%s"%file_to_upload).read() + files = \ + [ ("file", file_to_upload, zipdata, "application/zip") + ] + (reqtype, reqdata) = SparqlQueryTestCase.encode_multipart_formdata(fields, files) + (resp,respdata)= self.doHTTP_POST( + reqdata, reqtype, + resource="datasets/TestSubmission/", + expect_status=204, expect_reason="No Content") + return zipdata + + # Actual tests follow + def test01CreateUser(self): + """Create user sandbox_user""" + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointadminuser, + endpointpass=RDFDatabankConfig.endpointadminpass) + # Access list silos, check response + (resp, data) = self.doHTTP_GET( + endpointpath="/", + resource="users/", + expect_status=200, expect_reason="OK", expect_type="application/JSON") + userExists = False + for user in data: + if RDFDatabankConfig.endpointuser in user['user_name']: + userExists = True + + fields = [ + ('username',RDFDatabankConfig.endpointuser), + ('password',RDFDatabankConfig.endpointpass), + ('name',RDFDatabankConfig.endpointuser), + ] + files = [] + (reqtype, reqdata) = SparqlQueryTestCase.encode_multipart_formdata(fields, files) + if userExists: + (resp,respdata)= self.doHTTP_POST( + reqdata, reqtype, + endpointpath="/", + resource="users/", + expect_status=403, expect_reason="Forbidden") + else: + (resp,respdata)= self.doHTTP_POST( + reqdata, reqtype, + endpointpath="/", + resource="users/", + expect_status=201, expect_reason="Created") + LHobtained = resp.getheader('Content-Location', None) + LHexpected = "/users/%s"%RDFDatabankConfig.endpointuser + self.assertEquals(LHobtained, LHexpected, 'Content-Location not correct') + #Access user details + (resp, data) = self.doHTTP_GET( + endpointpath="/", + resource="users/%s"%RDFDatabankConfig.endpointuser, + expect_status=200, expect_reason="OK", expect_type="application/JSON") + return + + def test02CreateSilo(self): + """List all silos your account has access to - GET /admin. If the silo 'sandbox' does not exist, create it""" + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointadminuser, + endpointpass=RDFDatabankConfig.endpointadminpass) + # Access list silos, check response + (resp, data) = self.doHTTP_GET( + endpointpath="/", + resource="admin/", + expect_status=200, expect_reason="OK", expect_type="application/JSON") + silo_name = RDFDatabankConfig.endpointpath.strip('/') + silolist = data + if not silo_name in silolist: + #Create new silo + owner_list = [RDFDatabankConfig.endpointadminuser] + if not RDFDatabankConfig.endpointuser in owner_list: + owner_list.append(RDFDatabankConfig.endpointuser) + owner_list = ",".join(owner_list) + fields = \ + [ ("silo", silo_name), + ("title", "Sandbox silo"), + ("description", "Sandbox silo for testing"), + ("notes", "Created by test"), + ("administrators", RDFDatabankConfig.endpointadminuser), + ("disk_allocation", "100000") + ] + files =[] + (reqtype, reqdata) = SparqlQueryTestCase.encode_multipart_formdata(fields, files) + (resp,respdata) = self.doHTTP_POST( + reqdata, reqtype, resource="admin/", endpointpath="/", + expect_status=201, expect_reason="Created") + LHobtained = resp.getheader('Content-Location', None) + LHexpected = "/%s"%silo_name + self.assertEquals(LHobtained, LHexpected, 'Content-Location not correct') + # Access list silos, check response + (resp, data) = self.doHTTP_GET( + endpointpath="/", + resource="admin/", + expect_status=200, expect_reason="OK", expect_type="application/json") + newsilolist = data + self.failUnless(len(newsilolist)>0, "No silos returned") + self.assertEquals(len(newsilolist), len(silolist)+1, "One additional silo should have been returned") + for s in silolist: self.failUnless(s in newsilolist, "Silo "+s+" in original list, not in new list") + self.failUnless(silo_name in newsilolist, "Silo '%s' not in new list"%silo_name) + return + + def test03AddUserMembership(self): + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointadminuser, + endpointpass=RDFDatabankConfig.endpointadminpass) + # Access user details, check response + (resp, data) = self.doHTTP_GET( + endpointpath="/", + resource="users/%s"%RDFDatabankConfig.endpointuser, + expect_status=200, expect_reason="OK", expect_type="application/JSON") + membershipExists = False + silo_name = RDFDatabankConfig.endpointpath.strip('/') + if [silo_name, 'submitter'] in data['groups']: + membershipExists = True + fields = [ + ('role','submitter') + ] + files = [] + (reqtype, reqdata) = SparqlQueryTestCase.encode_multipart_formdata(fields, files) + if membershipExists: + (resp,respdata)= self.doHTTP_POST( + reqdata, reqtype, + endpointpath=RDFDatabankConfig.endpointpath, + resource="users/%s"%RDFDatabankConfig.endpointuser, + expect_status=400, expect_reason="Bad Request") + else: + (resp,respdata)= self.doHTTP_POST( + reqdata, reqtype, + endpointpath=RDFDatabankConfig.endpointpath, + resource="users/%s"%RDFDatabankConfig.endpointuser, + expect_status=201, expect_reason="Created") + LHobtained = resp.getheader('Content-Location', None) + LHexpected = "/%s/users/%s"%(silo_name, RDFDatabankConfig.endpointuser) + self.assertEquals(LHobtained, LHexpected, 'Content-Location not correct') + #Access user details + (resp, data) = self.doHTTP_GET( + endpointpath="/", + resource="users/%s"%RDFDatabankConfig.endpointuser, + expect_status=200, expect_reason="OK", expect_type="application/JSON") + self.assertEquals(data['user_name'], RDFDatabankConfig.endpointuser, "user info: username") + try: + self.assertEquals(data['name'], RDFDatabankConfig.endpointuser, "user info: name") + except: + pass + #self.assertEquals(data['firstname'], 'Sandbox', "user info: firstname") + #self.assertEquals(data['lastname'], 'User', "user info: lastname") + self.failUnless([silo_name, 'submitter'] in data['groups'], "user info: membership") + return + + def testListSilos(self): + """List all silos your account has access to - GET /silo""" + #Write a test to list all the silos. Test to see if it returns 200 OK and the list of silos is not empty + # Access list silos, check response + (resp, data) = self.doHTTP_GET( + endpointpath=None, + resource="/silos/", + expect_status=200, expect_reason="OK", expect_type="application/JSON") + # check list of silos is not empty + self.failUnless(len(data)>0, "No silos returned") + + def testListDatasets(self): + """List all datasets in a silo - GET /silo_name/datasets""" + # Access list of datasets in the silo, check response + (resp, data) = self.doHTTP_GET( + resource="datasets/", + expect_status=200, expect_reason="OK", expect_type="application/json") + # Save initial list of datasets + datasetlist = [] + for k in data: + datasetlist.append(k) + # Create a new dataset + self.createSubmissionDataset() + # Read list of datasets, check that new list is original + new dataset + (resp, data) = self.doHTTP_GET( + resource="datasets/", + expect_status=200, expect_reason="OK", expect_type="application/json") + newlist = [] + for k in data: + newlist.append(k) + logger.debug("Orig. length "+str(len(datasetlist))+", new length "+str(len(newlist))) + self.assertEquals(len(newlist), len(datasetlist)+1, "One additional dataset") + for ds in datasetlist: self.failUnless(ds in newlist, "Dataset "+ds+" in original list, not in new list") + for ds in newlist: self.failUnless((ds in datasetlist) or (ds == "TestSubmission"), "Datset "+ds+" in new list, not in original list") + self.failUnless("TestSubmission" in newlist, "testSubmission in new list") + # Delete new dataset + resp = self.doHTTP_DELETE( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK") + # read list of datasets, check result is same as original list + (resp, data) = self.doHTTP_GET( + resource="datasets/", + expect_status=200, expect_reason="OK", expect_type="application/json") + newlist = [] + for k in data: + newlist.append(k) + logger.debug("Orig. length "+str(len(datasetlist))+", new length "+str(len(newlist))) + self.assertEquals(len(newlist), len(datasetlist), "Back to original content in silo") + for ds in datasetlist: self.failUnless(ds in newlist, "Datset "+ds+" in original list, not in new list") + for ds in newlist: self.failUnless(ds in datasetlist, "Datset "+ds+" in new list, not in original list") + + def testSiloState(self): + """Get state informaton of a silo - GET /silo_name/states""" + # Access state information of silo, check response + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointadminuser, + endpointpass=RDFDatabankConfig.endpointadminpass) + (resp, data) = self.doHTTP_GET( + resource="states/", + expect_status=200, expect_reason="OK", expect_type="application/json") + # check silo name and base_uri + silo_name = RDFDatabankConfig.endpointpath.strip('/') + silo_base = URIRef(self.getManifestUri("datasets/")) + self.assertEqual(data['silo'], silo_name, 'Silo name is %s not %s' %(data['silo'], silo_name)) + self.assertEqual(data['uri_base'].strip(), silo_base.strip(), 'Silo uri_base is %s not %s' %(data['uri_base'], silo_base)) + #self.failUnless(len(data['datasets'])>0, "No datasets returned") + self.failUnless('datasets' in data, "datasets info for silo not returned") + self.failUnless((type(data['datasets']).__name__ == 'dict'), "Datasets with embargo info not returned for silo") + # Save initial list of datasets + datasetlist = data['datasets'] + # Create a new dataset + self.createSubmissionDataset() + # Read list of datasets, check that new list is original + new dataset + (resp, data) = self.doHTTP_GET( + resource="states/", + expect_status=200, expect_reason="OK", expect_type="application/json") + newlist = data['datasets'] + logger.debug("Orig. length "+str(len(datasetlist))+", new length "+str(len(newlist))) + self.assertEquals(len(newlist), len(datasetlist)+1, "One additional dataset") + for ds in datasetlist: self.failUnless(ds in newlist, "Dataset "+ds+" in original list, not in new list") + for ds in newlist: self.failUnless((ds in datasetlist) or (ds == "TestSubmission"), "Datset "+ds+" in new list, not in original list") + self.failUnless("TestSubmission" in newlist, "testSubmission in new list") + # Delete new dataset + resp = self.doHTTP_DELETE( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK") + # read list of datasets, check result is same as original list + (resp, data) = self.doHTTP_GET( + resource="states/", + expect_status=200, expect_reason="OK", expect_type="application/json") + newlist = data['datasets'] + logger.debug("Orig. length "+str(len(datasetlist))+", new length "+str(len(newlist))) + self.assertEquals(len(newlist), len(datasetlist), "Back to original content in silo") + for ds in datasetlist: self.failUnless(ds in newlist, "Datset "+ds+" in original list, not in new list") + for ds in newlist: self.failUnless(ds in datasetlist, "Datset "+ds+" in new list, not in original list") + + def testDatasetNotPresent(self): + """Verify dataset is not present - GET /silo_name/dataset_name""" + (resp, respdata) = self.doHTTP_GET(resource="TestSubmission", expect_status=404, expect_reason="Not Found") + + def testDatasetCreation(self): + """Create dataset - POST id to /silo_name""" + # Create a new dataset, check response + self.createSubmissionDataset() + # Access dataset, check response + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + self.assertEqual(len(rdfgraph),10,'Graph length %i' %len(rdfgraph)) + oxds = "http://vocab.ox.ac.uk/dataset/schema#" + dcterms = "http://purl.org/dc/terms/" + subj = URIRef(self.getManifestUri("datasets/TestSubmission")) + stype = URIRef(oxds+"DataSet") + self.failUnless((subj,RDF.type,stype) in rdfgraph, 'Testing submission type: '+subj+", "+stype) + self.failUnless((subj,URIRef(dcterms+"created"),None) in rdfgraph, 'dcterms:created') + self.failUnless((subj,URIRef(dcterms+"identifier"),None) in rdfgraph, 'dcterms:identifier') + self.failUnless((subj,URIRef(dcterms+"mediator"),None) in rdfgraph, 'dcterms:mediator') + self.failUnless((subj,URIRef(dcterms+"rights"),None) in rdfgraph, 'dcterms:rights') + self.failUnless((subj,URIRef(dcterms+"license"),None) in rdfgraph, 'dcterms:license') + self.failUnless((subj,URIRef(dcterms+"publisher"),None) in rdfgraph, 'dcterms:publisher') + self.failUnless((subj,URIRef(oxds+"isEmbargoed"),None) in rdfgraph, 'oxds:isEmbargoed') + self.failUnless((subj,URIRef(oxds+"embargoedUntil"),None) in rdfgraph, 'oxds:embargoedUntil') + self.failUnless((subj,URIRef(oxds+"currentVersion"),'0') in rdfgraph, 'oxds:currentVersion') + + def testDatasetRecreation(self): + """Create dataset - POST existing id to /silo_name""" + # Create a new dataset, check response + self.createSubmissionDataset() + # Access dataset, check response + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + self.assertEqual(len(rdfgraph),10,'Graph length %i' %len(rdfgraph)) + #Recreate the dataset, check response + fields = \ + [ ("id", "TestSubmission") + ] + files =[] + (reqtype, reqdata) = SparqlQueryTestCase.encode_multipart_formdata(fields, files) + (resp,respdata) = self.doHTTP_POST( + reqdata, reqtype, + resource="datasets", + expect_status=409, expect_reason="Conflict: Data package already exists") + #Recreate the dataset, check response + fields = [] + files =[] + (reqtype, reqdata) = SparqlQueryTestCase.encode_multipart_formdata(fields, files) + (resp,respdata) = self.doHTTP_POST( + reqdata, reqtype, + resource="datasets/TestSubmission", + expect_status=400, expect_reason="Bad request") + # Access dataset, check response + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + self.assertEqual(len(rdfgraph),10,'Graph length %i' %len(rdfgraph)) + + def testDeleteDataset(self): + """Delete dataset - DELETE /silo_name/dataset_name""" + # Create a new dataset, check response + self.createSubmissionDataset() + # Access dataset, check response + (resp, respdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/json") + # Delete dataset, check response + resp = self.doHTTP_DELETE( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK") + # Access dataset, test response indicating non-existent + (resp, respdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=404, expect_reason="Not Found") + + def testDatasetNaming(self): + """Create dataset - POST id to /silo_name/datasets. If name is valid, return 201, else return 403""" + names = [("TestSubmission-1", 201, "Created") + ,("TestSubmission_2", 201, "Created") + ,("TestSubmission:3", 201, "Created") + ,("TestSubmission*4", 400, "Bad request. Data package name not valid") + ,("TestSubmission/5", 400, "Bad request. Data package name not valid") + ,("TestSubmission\6", 400, "Bad request. Data package name not valid") + ,("TestSubmission,7", 400, "Bad request. Data package name not valid") + ,("TestSubmission&8", 400, "Bad request. Data package name not valid") + ,("TestSubmission.9", 400, "Bad request. Data package name not valid") + ,("""Test"Submission""", 400, "Bad request. Data package name not valid") + ,("Test'Submission", 400, "Bad request. Data package name not valid") + #,("""Test Submission""", 400, "Bad request. Dataset name not valid") #The name is truncated to Test and dataset is created. This does not happen when using the form + ,("TestSubmission$", 400, "Bad request. Data package name not valid") + ,("T", 400, "Bad request. Data package name not valid") + ] + files =[] + for name, status, reason in names: + fields = [("id", name)] + (reqtype, reqdata) = SparqlQueryTestCase.encode_multipart_formdata(fields, files) + #Create a new dataset, check response + (resp,respdata) = self.doHTTP_POST( + reqdata, reqtype, + resource="datasets/", expect_status=status, expect_reason=reason) + # Access dataset, check response + if status == 201: + LHobtained = urllib.unquote(resp.getheader('Content-Location', None)) + LHexpected = "%sdatasets/%s"%(self._endpointpath, name) + self.assertEquals(LHobtained, LHexpected, 'Content-Location not correct') + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/%s"%name, + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + self.assertEqual(len(rdfgraph),10,'Graph length %i' %len(rdfgraph)) + elif status == 403: + (resp, respdata) = self.doHTTP_GET( + resource="datasets/%s"%name, + expect_status=404, expect_reason="Not Found") + #Delete Datasets + for name, status, reason in names: + if not status == 201: + continue + resp = self.doHTTP_DELETE( + resource="datasets/%s"%name, + expect_status=200, expect_reason="OK") + + def testDatasetStateInformation(self): + """Get state information of dataset - GET /silo_name/states/dataset_name.""" + # Create a new dataset, check response + self.createSubmissionDataset() + # Access state info + (resp, data) = self.doHTTP_GET( + resource="states/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/json") + state = data['state'] + parts = data['parts'] + self.assertEqual(len(state.keys()), 12, "States") + self.assertEqual(state['item_id'], "TestSubmission", "Submission item identifier") + self.assertEqual(len(state['versions']), 1, "Initially one version") + self.assertEqual(state['versions'][0], '0', "Version 0") + self.assertEqual(state['currentversion'], '0', "Current version == 0") + self.assertEqual(state['rdffileformat'], 'xml', "RDF file type") + self.assertEqual(state['rdffilename'], 'manifest.rdf', "RDF file name") + self.assertEqual(state['files']['0'], ['manifest.rdf'], "List should contain just manifest.rdf") + self.assertEqual(len(state['metadata_files']['0']), 0, "metadata_files of version 0") + self.assertEqual(len(state['subdir']['0']), 0, "Subdirectory count for version 0") + self.assertEqual(state['metadata']['createdby'], RDFDatabankConfig.endpointuser, "Created by") + self.assertEqual(state['metadata']['embargoed'], True, "Embargoed?") + # date + # version_dates + self.assertEqual(len(parts.keys()), 3, "Parts") + self.assertEqual(len(parts['4=TestSubmission'].keys()), 13, "File stats for 4=TestSubmission") + self.assertEqual(len(parts['manifest.rdf'].keys()), 13, "File stats for manifest.rdf") + + def testEmbargoOnCreation(self): + """Create dataset - POST id to /silo_name""" + #--------------------------------------------------------------- + # Create a new dataset, check response. No embargo information is passed. + self.createSubmissionDataset() + # Access dataset, check response + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + self.assertEqual(len(rdfgraph),10,'Graph length %i' %len(rdfgraph)) + oxds = "http://vocab.ox.ac.uk/dataset/schema#" + dcterms = "http://purl.org/dc/terms/" + subj = URIRef(self.getManifestUri("datasets/TestSubmission")) + stype = URIRef(oxds+"DataSet") + self.failUnless((subj,RDF.type,stype) in rdfgraph, 'Testing submission type: '+subj+", "+stype) + self.failUnless((subj,URIRef(oxds+"isEmbargoed"),'True') in rdfgraph, 'oxds:isEmbargoed') + self.assertTrue((subj,URIRef(oxds+"embargoedUntil"),None) in rdfgraph, 'oxds:embargoedUntil') + self.failUnless((subj,URIRef(oxds+"currentVersion"),'0') in rdfgraph, 'oxds:currentVersion') + # Access state info + (resp, data) = self.doHTTP_GET( + resource="states/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/json") + state = data['state'] + d = (datetime.now() + relativedelta(years=+70)).isoformat() + d = d.split('T')[0] + self.assertEqual(state['metadata']['embargoed'], True, "Embargoed?") + self.assertTrue(d in state['metadata']['embargoed_until'], "embargoed_until %s?"%d) + #--------------------------------------------------------------- + # Delete dataset, check response + resp = self.doHTTP_DELETE( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK") + # Access dataset, test response indicating non-existent + (resp, respdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=404, expect_reason="Not Found") + #--------------------------------------------------------------- + # Create a new dataset, check response. embargoed=None, embargo_until=True. + self.createSubmissionDataset(embargoed_until=True) + # Access dataset, check response + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + self.assertEqual(len(rdfgraph),10,'Graph length %i' %len(rdfgraph)) + oxds = "http://vocab.ox.ac.uk/dataset/schema#" + subj = URIRef(self.getManifestUri("datasets/TestSubmission")) + stype = URIRef(oxds+"DataSet") + self.failUnless((subj,RDF.type,stype) in rdfgraph, 'Testing submission type: '+subj+", "+stype) + self.failUnless((subj,URIRef(oxds+"isEmbargoed"),'True') in rdfgraph, 'oxds:isEmbargoed') + self.assertTrue((subj,URIRef(oxds+"embargoedUntil"),None) in rdfgraph, 'oxds:embargoedUntil') + self.failUnless((subj,URIRef(oxds+"currentVersion"),'0') in rdfgraph, 'oxds:currentVersion') + # Access state info + (resp, data) = self.doHTTP_GET( + resource="states/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/json") + state = data['state'] + d = (datetime.now() + relativedelta(years=+70)).isoformat() + d = d.split('T')[0] + self.assertEqual(state['metadata']['embargoed'], True, "Embargoed?") + self.assertTrue(d in state['metadata']['embargoed_until'], "embargoed_until %s?"%d) + #--------------------------------------------------------------- + # Delete dataset, check response + resp = self.doHTTP_DELETE( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK") + # Access dataset, test response indicating non-existent + (resp, respdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=404, expect_reason="Not Found") + #--------------------------------------------------------------- + # Create a new dataset, check response. embargoed=None, embargo_until=2012-08-12 + d = '2012-08-12' + self.createSubmissionDataset(embargoed_until=d) + # Access dataset, check response + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + self.assertEqual(len(rdfgraph),10,'Graph length %i' %len(rdfgraph)) + oxds = "http://vocab.ox.ac.uk/dataset/schema#" + subj = URIRef(self.getManifestUri("datasets/TestSubmission")) + stype = URIRef(oxds+"DataSet") + self.failUnless((subj,RDF.type,stype) in rdfgraph, 'Testing submission type: '+subj+", "+stype) + self.failUnless((subj,URIRef(oxds+"isEmbargoed"),'True') in rdfgraph, 'oxds:isEmbargoed') + self.assertTrue((subj,URIRef(oxds+"embargoedUntil"),None) in rdfgraph, 'oxds:embargoedUntil') + self.failUnless((subj,URIRef(oxds+"currentVersion"),'0') in rdfgraph, 'oxds:currentVersion') + # Access state info + (resp, data) = self.doHTTP_GET( + resource="states/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/json") + state = data['state'] + d = (datetime.now() + relativedelta(years=+70)).isoformat() + d = d.split('T')[0] + self.assertEqual(state['metadata']['embargoed'], True, "Embargoed?") + self.assertTrue(d in state['metadata']['embargoed_until'], "embargoed_until %s?"%d) + #--------------------------------------------------------------- + # Delete dataset, check response + resp = self.doHTTP_DELETE( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK") + # Access dataset, test response indicating non-existent + (resp, respdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=404, expect_reason="Not Found") + #--------------------------------------------------------------- + # Create a new dataset, check response. embargoed=True, embargo_until=None. + self.createSubmissionDataset(embargoed=True) + # Access dataset, check response + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + self.assertEqual(len(rdfgraph),9,'Graph length %i' %len(rdfgraph)) + oxds = "http://vocab.ox.ac.uk/dataset/schema#" + subj = URIRef(self.getManifestUri("datasets/TestSubmission")) + stype = URIRef(oxds+"DataSet") + self.failUnless((subj,RDF.type,stype) in rdfgraph, 'Testing submission type: '+subj+", "+stype) + self.failUnless((subj,URIRef(oxds+"isEmbargoed"),'True') in rdfgraph, 'oxds:isEmbargoed') + self.assertFalse((subj,URIRef(oxds+"embargoedUntil"),None) in rdfgraph, 'oxds:embargoedUntil') + self.failUnless((subj,URIRef(oxds+"currentVersion"),'0') in rdfgraph, 'oxds:currentVersion') + # Access state info + (resp, data) = self.doHTTP_GET( + resource="states/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/json") + state = data['state'] + self.assertEqual(state['metadata']['embargoed'], True, "Embargoed?") + self.assertEqual(state['metadata']['embargoed_until'], '', "Embargoed?") + #--------------------------------------------------------------- + # Delete dataset, check response + resp = self.doHTTP_DELETE( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK") + # Access dataset, test response indicating non-existent + (resp, respdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=404, expect_reason="Not Found") + #--------------------------------------------------------------- + # Create a new dataset, check response. embargoed=True, embargo_until=True + self.createSubmissionDataset(embargoed=True, embargoed_until=True) + # Access dataset, check response + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + self.assertEqual(len(rdfgraph),10,'Graph length %i' %len(rdfgraph)) + oxds = "http://vocab.ox.ac.uk/dataset/schema#" + dcterms = "http://purl.org/dc/terms/" + subj = URIRef(self.getManifestUri("datasets/TestSubmission")) + stype = URIRef(oxds+"DataSet") + self.failUnless((subj,RDF.type,stype) in rdfgraph, 'Testing submission type: '+subj+", "+stype) + self.failUnless((subj,URIRef(oxds+"isEmbargoed"),'True') in rdfgraph, 'oxds:isEmbargoed') + self.failUnless((subj,URIRef(oxds+"embargoedUntil"),None) in rdfgraph, 'oxds:embargoedUntil') + self.failUnless((subj,URIRef(oxds+"currentVersion"),'0') in rdfgraph, 'oxds:currentVersion') + # Access state info + (resp, data) = self.doHTTP_GET( + resource="states/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/json") + state = data['state'] + d = (datetime.now() + relativedelta(years=+70)).isoformat() + d = d.split('T')[0] + self.assertEqual(state['metadata']['embargoed'], True, "Embargoed?") + self.assertTrue(d in state['metadata']['embargoed_until'], "embargoed_until %s?"%d) + #--------------------------------------------------------------- + # Delete dataset, check response + resp = self.doHTTP_DELETE( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK") + # Access dataset, test response indicating non-existent + (resp, respdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=404, expect_reason="Not Found") + #--------------------------------------------------------------- + # Create a new dataset, check response. embargoed=True, embargo_until=09-08-2012 + d = '09-08-2012' + self.createSubmissionDataset(embargoed=True, embargoed_until=d) + # Access dataset, check response + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + self.assertEqual(len(rdfgraph),10,'Graph length %i' %len(rdfgraph)) + oxds = "http://vocab.ox.ac.uk/dataset/schema#" + dcterms = "http://purl.org/dc/terms/" + subj = URIRef(self.getManifestUri("datasets/TestSubmission")) + stype = URIRef(oxds+"DataSet") + self.failUnless((subj,RDF.type,stype) in rdfgraph, 'Testing submission type: '+subj+", "+stype) + self.failUnless((subj,URIRef(oxds+"isEmbargoed"),'True') in rdfgraph, 'oxds:isEmbargoed') + self.failUnless((subj,URIRef(oxds+"embargoedUntil"),None) in rdfgraph, 'oxds:embargoedUntil') + self.failUnless((subj,URIRef(oxds+"currentVersion"),'0') in rdfgraph, 'oxds:currentVersion') + # Access state info + (resp, data) = self.doHTTP_GET( + resource="states/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/json") + state = data['state'] + self.assertEqual(state['metadata']['embargoed'], True, "Embargoed?") + self.assertTrue('2012-08-09' in state['metadata']['embargoed_until'], "embargoed_until 2012-08-09?") + #--------------------------------------------------------------- + # Delete dataset, check response + resp = self.doHTTP_DELETE( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK") + # Access dataset, test response indicating non-existent + (resp, respdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=404, expect_reason="Not Found") + #--------------------------------------------------------------- + # Create a new dataset, check response. embargoed = False. + self.createSubmissionDataset(embargoed=False) + # Access dataset, check response + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + self.assertEqual(len(rdfgraph),9,'Graph length %i' %len(rdfgraph)) + oxds = "http://vocab.ox.ac.uk/dataset/schema#" + dcterms = "http://purl.org/dc/terms/" + subj = URIRef(self.getManifestUri("datasets/TestSubmission")) + stype = URIRef(oxds+"DataSet") + self.failUnless((subj,RDF.type,stype) in rdfgraph, 'Testing submission type: '+subj+", "+stype) + self.failUnless((subj,URIRef(oxds+"isEmbargoed"),'False') in rdfgraph, 'oxds:isEmbargoed') + self.assertFalse((subj,URIRef(oxds+"embargoedUntil"),None) in rdfgraph, 'oxds:embargoedUntil') + self.failUnless((subj,URIRef(oxds+"currentVersion"),'0') in rdfgraph, 'oxds:currentVersion') + # Access state info + (resp, data) = self.doHTTP_GET( + resource="states/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/json") + state = data['state'] + self.assertEqual(state['metadata']['embargoed'], False, "Embargoed?") + self.assertEqual(state['metadata']['embargoed_until'], '', "Embargoed?") + #--------------------------------------------------------------- + # Delete dataset, check response + resp = self.doHTTP_DELETE( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK") + # Access dataset, test response indicating non-existent + (resp, respdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=404, expect_reason="Not Found") + #--------------------------------------------------------------- + # Create a new dataset, check response. embargoed = False, embargoed_until = True + self.createSubmissionDataset(embargoed=False, embargoed_until=True) + # Access dataset, check response + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + self.assertEqual(len(rdfgraph),9,'Graph length %i' %len(rdfgraph)) + oxds = "http://vocab.ox.ac.uk/dataset/schema#" + dcterms = "http://purl.org/dc/terms/" + subj = URIRef(self.getManifestUri("datasets/TestSubmission")) + stype = URIRef(oxds+"DataSet") + self.failUnless((subj,RDF.type,stype) in rdfgraph, 'Testing submission type: '+subj+", "+stype) + self.failUnless((subj,URIRef(oxds+"isEmbargoed"),'False') in rdfgraph, 'oxds:isEmbargoed') + self.assertFalse((subj,URIRef(oxds+"embargoedUntil"),None) in rdfgraph, 'oxds:embargoedUntil') + self.failUnless((subj,URIRef(oxds+"currentVersion"),'0') in rdfgraph, 'oxds:currentVersion') + # Access state info + (resp, data) = self.doHTTP_GET( + resource="states/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/json") + state = data['state'] + self.assertEqual(state['metadata']['embargoed'], False, "Embargoed?") + self.assertEqual(state['metadata']['embargoed_until'], '', "Embargoed?") + #--------------------------------------------------------------- + # Delete dataset, check response + resp = self.doHTTP_DELETE( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK") + # Access dataset, test response indicating non-existent + (resp, respdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=404, expect_reason="Not Found") + #--------------------------------------------------------------- + # Create a new dataset, check response. embargoed = False, embargoed_until = 12 sep 2013 + d = '12 Sep 2013' + self.createSubmissionDataset(embargoed=False, embargoed_until=d) + # Access dataset, check response + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + self.assertEqual(len(rdfgraph),9,'Graph length %i' %len(rdfgraph)) + oxds = "http://vocab.ox.ac.uk/dataset/schema#" + dcterms = "http://purl.org/dc/terms/" + subj = URIRef(self.getManifestUri("datasets/TestSubmission")) + stype = URIRef(oxds+"DataSet") + self.failUnless((subj,RDF.type,stype) in rdfgraph, 'Testing submission type: '+subj+", "+stype) + self.failUnless((subj,URIRef(oxds+"isEmbargoed"),'False') in rdfgraph, 'oxds:isEmbargoed') + self.assertFalse((subj,URIRef(oxds+"embargoedUntil"),None) in rdfgraph, 'oxds:embargoedUntil') + self.failUnless((subj,URIRef(oxds+"currentVersion"),'0') in rdfgraph, 'oxds:currentVersion') + # Access state info + (resp, data) = self.doHTTP_GET( + resource="states/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/json") + state = data['state'] + self.assertEqual(state['metadata']['embargoed'], False, "Embargoed?") + self.assertEqual(state['metadata']['embargoed_until'], '', "Embargoed?") + + + def testFileUpload(self): + """Upload file to dataset - POST file to /silo_name/datasets/dataset_name""" + # Create a new dataset, check response + self.createSubmissionDataset() + #Access state information + (resp, respdata) = self.doHTTP_GET( + resource="states/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/json") + # Upload zip file, check response + zipdata = self.uploadSubmissionZipfile() + # Access and check list of contents + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + subj = URIRef(self.getManifestUri("datasets/TestSubmission")) + base = self.getManifestUri("datasets/TestSubmission/") + dcterms = "http://purl.org/dc/terms/" + ore = "http://www.openarchives.org/ore/terms/" + oxds = "http://vocab.ox.ac.uk/dataset/schema#" + stype = URIRef(oxds+"DataSet") + self.assertEqual(len(rdfgraph),12,'Graph length %i' %len(rdfgraph)) + self.failUnless((subj,RDF.type,stype) in rdfgraph, 'Testing submission type: '+subj+", "+stype) + self.failUnless((subj,URIRef(dcterms+"created"),None) in rdfgraph, 'dcterms:created') + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"testdir.zip")) in rdfgraph) + self.failUnless((subj,URIRef(dcterms+"identifier"),None) in rdfgraph, 'dcterms:identifier') + self.failUnless((subj,URIRef(dcterms+"mediator"),None) in rdfgraph, 'dcterms:mediator') + self.failUnless((subj,URIRef(dcterms+"rights"),None) in rdfgraph, 'dcterms:rights') + self.failUnless((subj,URIRef(dcterms+"license"),None) in rdfgraph, 'dcterms:license') + self.failUnless((subj,URIRef(dcterms+"publisher"),None) in rdfgraph, 'dcterms:publisher') + self.failUnless((subj,URIRef(oxds+"isEmbargoed"),None) in rdfgraph, 'oxds:isEmbargoed') + self.failUnless((subj,URIRef(oxds+"embargoedUntil"),None) in rdfgraph, 'oxds:embargoedUntil') + self.failUnless((subj,URIRef(oxds+"currentVersion"),'1') in rdfgraph, 'oxds:currentVersion') + self.failUnless((subj,URIRef(dcterms+"modified"),None) in rdfgraph, 'dcterms:modified') + # Access and check zip file content + (resp, zipfile) = self.doHTTP_GET( + resource="datasets/TestSubmission/testdir.zip", + expect_status=200, expect_reason="OK", expect_type="application/zip") + self.assertEqual(zipdata, zipfile, "Difference between local and remote zipfile!") + #Access state information and check + (resp, data) = self.doHTTP_GET( + resource="states/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/json") + state = data['state'] + parts = data['parts'] + self.assertEqual(len(state.keys()), 12, "States") + self.assertEqual(state['item_id'], "TestSubmission", "Submission item identifier") + self.assertEqual(len(state['versions']), 2, "Two versions") + self.assertEqual(state['versions'][0], '0', "Version 0") + self.assertEqual(state['versions'][1], '1', "Version 1") + self.assertEqual(state['currentversion'], '1', "Current version == 1") + self.assertEqual(state['rdffileformat'], 'xml', "RDF file type") + self.assertEqual(state['rdffilename'], 'manifest.rdf', "RDF file name") + self.assertEqual(state['files']['0'], ['manifest.rdf'], "List should contain just manifest.rdf") + self.assertEqual(len(state['files']['1']), 2, "List should contain manifest.rdf and testdir.zip") + self.assertEqual(len(state['metadata_files']['0']), 0, "metadata_files of version 0") + self.assertEqual(len(state['metadata_files']['1']), 0, "metadata_files of version 1") + self.assertEqual(len(state['subdir']['0']), 0, "Subdirectory count for version 0") + self.assertEqual(len(state['subdir']['1']), 0, "Subdirectory count for version 1") + self.failUnless('Created new data package' in state['versionlog']['0'], "Version 0 log") + self.failUnless('Added or updated file testdir.zip' in state['versionlog']['1'], "Version 1 log") + self.assertEqual(state['metadata']['createdby'], RDFDatabankConfig.endpointuser, "Created by") + self.assertEqual(state['metadata']['embargoed'], True, "Embargoed?") + self.assertEqual(len(parts.keys()), 4, "Parts") + self.assertEqual(len(parts['4=TestSubmission'].keys()), 13, "File stats for 4=TestSubmission") + self.assertEqual(len(parts['manifest.rdf'].keys()), 13, "File stats for manifest.rdf") + self.assertEqual(len(parts['testdir.zip'].keys()), 13, "File stats for testdir.zip") + + def testFileUploadtoNewDataset(self): + """Upload file to a new dataset - POST file to /silo_name/datasets/dataset_name""" + #Access state information + (resp, respdata) = self.doHTTP_GET( + resource="states/TestSubmission", + expect_status=404, expect_reason="Not Found") + # Upload zip file, check response + d = datetime.now() + delta = timedelta(days=365*4) + d2 = d + delta + d2 = d2.isoformat() + fields = [ + ("filename", "testdir.zip") + ,("embargoed", 'true') + ,("embargoed_until", d2) + ] + zipdata = open("testdata/testdir.zip").read() + files = \ + [ ("file", "testdir.zip", zipdata, "application/zip") + ] + (reqtype, reqdata) = SparqlQueryTestCase.encode_multipart_formdata(fields, files) + (resp,respdata)= self.doHTTP_POST( + reqdata, reqtype, + resource="datasets/TestSubmission/", + expect_status=201, expect_reason="Created") + LHobtained = resp.getheader('Content-Location', None) + LHexpected = "%sdatasets/TestSubmission/testdir.zip"%self._endpointpath + self.assertEquals(LHobtained, LHexpected, 'Content-Location not correct') + #Access dataset and check content of version 1 - embargo update + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission?version=1", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + self.assertEqual(len(rdfgraph),11,'Graph length %i' %len(rdfgraph)) + oxds = "http://vocab.ox.ac.uk/dataset/schema#" + dcterms = "http://purl.org/dc/terms/" + subj = URIRef(self.getManifestUri("datasets/TestSubmission")) + stype = URIRef(oxds+"DataSet") + self.failUnless((subj,RDF.type,stype) in rdfgraph, 'Testing submission type: '+subj+", "+stype) + self.failUnless((subj,URIRef(oxds+"isEmbargoed"),'True') in rdfgraph, 'oxds:isEmbargoed') + self.failUnless((subj,URIRef(oxds+"embargoedUntil"),d2) in rdfgraph, 'oxds:embargoedUntil') + self.failUnless((subj,URIRef(dcterms+"identifier"),None) in rdfgraph, 'dcterms:identifier') + self.failUnless((subj,URIRef(dcterms+"created"),None) in rdfgraph, 'dcterms:created') + self.failUnless((subj,URIRef(dcterms+"mediator"),None) in rdfgraph, 'dcterms:mediator') + self.failUnless((subj,URIRef(dcterms+"rights"),None) in rdfgraph, 'dcterms:rights') + self.failUnless((subj,URIRef(dcterms+"license"),None) in rdfgraph, 'dcterms:license') + self.failUnless((subj,URIRef(dcterms+"publisher"),None) in rdfgraph, 'dcterms:publisher') + self.failUnless((subj,URIRef(oxds+"currentVersion"),'1') in rdfgraph, 'oxds:currentVersion') + self.failUnless((subj,URIRef(dcterms+"modified"),None) in rdfgraph, 'dcterms:modified') + # Access and check list of contents of version 2 - file upload + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + subj = URIRef(self.getManifestUri("datasets/TestSubmission")) + base = self.getManifestUri("datasets/TestSubmission/") + dcterms = "http://purl.org/dc/terms/" + ore = "http://www.openarchives.org/ore/terms/" + oxds = "http://vocab.ox.ac.uk/dataset/schema#" + stype = URIRef(oxds+"DataSet") + self.assertEqual(len(rdfgraph),12,'Graph length %i' %len(rdfgraph)) + self.failUnless((subj,RDF.type,stype) in rdfgraph, 'Testing submission type: '+subj+", "+stype) + self.failUnless((subj,URIRef(dcterms+"created"),None) in rdfgraph, 'dcterms:created') + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"testdir.zip")) in rdfgraph) + self.failUnless((subj,URIRef(dcterms+"identifier"),None) in rdfgraph, 'dcterms:identifier') + self.failUnless((subj,URIRef(dcterms+"mediator"),None) in rdfgraph, 'dcterms:mediator') + self.failUnless((subj,URIRef(dcterms+"rights"),None) in rdfgraph, 'dcterms:rights') + self.failUnless((subj,URIRef(dcterms+"license"),None) in rdfgraph, 'dcterms:license') + self.failUnless((subj,URIRef(dcterms+"publisher"),None) in rdfgraph, 'dcterms:publisher') + self.failUnless((subj,URIRef(oxds+"isEmbargoed"),'True') in rdfgraph, 'oxds:isEmbargoed') + self.failUnless((subj,URIRef(oxds+"embargoedUntil"),None) in rdfgraph, 'oxds:embargoedUntil') + self.failUnless((subj,URIRef(oxds+"currentVersion"),'2') in rdfgraph, 'oxds:currentVersion') + self.failUnless((subj,URIRef(dcterms+"modified"),None) in rdfgraph, 'dcterms:modified') + # Access and check zip file content + (resp, zipfile) = self.doHTTP_GET( + resource="datasets/TestSubmission/testdir.zip", + expect_status=200, expect_reason="OK", expect_type="application/zip") + self.assertEqual(zipdata, zipfile, "Difference between local and remote zipfile!") + #Access state information and check + (resp, data) = self.doHTTP_GET( + resource="states/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/json") + state = data['state'] + parts = data['parts'] + self.assertEqual(len(state.keys()), 12, "States") + self.assertEqual(state['item_id'], "TestSubmission", "Submission item identifier") + self.assertEqual(len(state['versions']), 3, "Three versions") + self.assertEqual(state['versions'][0], '0', "Version 0") + self.assertEqual(state['versions'][1], '1', "Version 1") + self.assertEqual(state['versions'][2], '2', "Version 2") + self.assertEqual(state['currentversion'], '2', "Current version == 2") + self.assertEqual(state['rdffileformat'], 'xml', "RDF file type") + self.assertEqual(state['rdffilename'], 'manifest.rdf', "RDF file name") + self.assertEqual(state['files']['0'], ['manifest.rdf'], "List should contain just manifest.rdf") + self.assertEqual(state['files']['1'], ['manifest.rdf'], "List should contain just manifest.rdf") + self.assertEqual(len(state['files']['2']), 2, "List should contain manifest.rdf and testdir.zip") + self.assertEqual(len(state['metadata_files']['0']), 0, "metadata_files of version 0") + self.assertEqual(len(state['metadata_files']['1']), 0, "metadata_files of version 1") + self.assertEqual(len(state['metadata_files']['2']), 0, "metadata_files of version 2") + self.assertEqual(len(state['subdir']['0']), 0, "Subdirectory count for version 0") + self.assertEqual(len(state['subdir']['1']), 0, "Subdirectory count for version 1") + self.assertEqual(len(state['subdir']['2']), 0, "Subdirectory count for version 2") + #logger.debug("Versionlog "+str(state['versionlog'])) + self.failUnless('Created new data package' in state['versionlog']['0'], "Version 0 log") + #self.failUnless('Added or updated file manifest.rdf' in state['versionlog']['1'], "Version 1 log") + self.failUnless('Added or updated file testdir.zip' in state['versionlog']['2'], "Version 2 log") + self.assertEqual(state['metadata']['createdby'], RDFDatabankConfig.endpointuser, "Created by") + self.assertEqual(state['metadata']['embargoed'], True, "Embargoed?") + self.assertEqual(state['metadata']['embargoed_until'], d2, "embargoed_until?") + self.assertEqual(len(parts.keys()), 4, "Parts") + self.assertEqual(len(parts['4=TestSubmission'].keys()), 13, "File stats for 4=TestSubmission") + self.assertEqual(len(parts['manifest.rdf'].keys()), 13, "File stats for manifest.rdf") + self.assertEqual(len(parts['testdir.zip'].keys()), 13, "File stats for testdir.zip") + + def testFileDelete(self): + """Delete file in dataset - DELETE /silo_name/datasets/dataset_name/file_name""" + # Create a new dataset, check response + self.createSubmissionDataset() + # Upload zip file, check response + zipdata = self.uploadSubmissionZipfile() + # Access and check list of contents + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + subj = URIRef(self.getManifestUri("datasets/TestSubmission")) + oxds = "http://vocab.ox.ac.uk/dataset/schema#" + self.assertEqual(len(rdfgraph),12,'Graph length %i' %len(rdfgraph)) + self.failUnless((subj,URIRef(oxds+"currentVersion"),"1") in rdfgraph, 'oxds:currentVersion') + # Access and check zip file content and version + (resp, zipfile) = self.doHTTP_GET( + resource="datasets/TestSubmission/testdir.zip", + expect_status=200, expect_reason="OK", expect_type="application/zip") + self.assertEqual(zipdata, zipfile, "Difference between local and remote zipfile!") + # Delete file, check response + resp = self.doHTTP_DELETE( + resource="datasets/TestSubmission/testdir.zip", + expect_status=200, expect_reason="OK") + # Access and check zip file does not exist + (resp, zipfile) = self.doHTTP_GET( + resource="datasets/TestSubmission/testdir.zip", + expect_status=404, expect_reason="Not Found") + # Access and check list of contents + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + subj = URIRef(self.getManifestUri("datasets/TestSubmission")) + dcterms = "http://purl.org/dc/terms/" + ore = "http://www.openarchives.org/ore/terms/" + oxds = "http://vocab.ox.ac.uk/dataset/schema#" + stype = URIRef(oxds+"DataSet") + self.assertEqual(len(rdfgraph),11,'Graph length %i' %len(rdfgraph)) + self.failUnless((subj,RDF.type,stype) in rdfgraph, 'Testing submission type: '+subj+", "+stype) + self.failUnless((subj,URIRef(dcterms+"created"),None) in rdfgraph, 'dcterms:created') + self.failUnless((subj,URIRef(dcterms+"identifier"),None) in rdfgraph, 'dcterms:identifier') + self.failUnless((subj,URIRef(dcterms+"mediator"),None) in rdfgraph, 'dcterms:mediator') + self.failUnless((subj,URIRef(dcterms+"rights"),None) in rdfgraph, 'dcterms:rights') + self.failUnless((subj,URIRef(dcterms+"license"),None) in rdfgraph, 'dcterms:license') + self.failUnless((subj,URIRef(dcterms+"publisher"),None) in rdfgraph, 'dcterms:publisher') + self.failUnless((subj,URIRef(oxds+"isEmbargoed"),None) in rdfgraph, 'oxds:isEmbargoed') + self.failUnless((subj,URIRef(oxds+"embargoedUntil"),None) in rdfgraph, 'oxds:embargoedUntil') + self.failUnless((subj,URIRef(oxds+"currentVersion"),'2') in rdfgraph, 'oxds:currentVersion') + self.failUnless((subj,URIRef(dcterms+"modified"),None) in rdfgraph, 'dcterms:modified') + #Access state information and check + (resp, data) = self.doHTTP_GET( + resource="states/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/json") + state = data['state'] + parts = data['parts'] + self.assertEqual(len(state.keys()), 12, "States") + self.assertEqual(state['item_id'], "TestSubmission", "Submission item identifier") + self.assertEqual(len(state['versions']), 3, "Three versions") + self.assertEqual(state['versions'][0], '0', "Version 0") + self.assertEqual(state['versions'][1], '1', "Version 1") + self.assertEqual(state['versions'][2], '2', "Version 2") + self.assertEqual(state['currentversion'], '2', "Current version == 2") + self.assertEqual(state['rdffileformat'], 'xml', "RDF file type") + self.assertEqual(state['rdffilename'], 'manifest.rdf', "RDF file name") + self.assertEqual(len(state['files']['0']), 1, "List should contain just manifest.rdf") + self.assertEqual(len(state['files']['1']), 2, "List should contain manifest.rdf and testdir.zip") + self.assertEqual(len(state['files']['2']), 1, "List should contain just manifest.rdf") + self.assertEqual(len(state['metadata_files']['0']), 0, "metadata_files of version 0") + self.assertEqual(len(state['metadata_files']['1']), 0, "metadata_files of version 1") + self.assertEqual(len(state['metadata_files']['2']), 0, "metadata_files of version 2") + self.assertEqual(len(state['subdir']['0']), 0, "Subdirectory count for version 0") + self.assertEqual(len(state['subdir']['1']), 0, "Subdirectory count for version 1") + self.assertEqual(len(state['subdir']['2']), 0, "Subdirectory count for version 2") + self.assertEqual(state['metadata']['createdby'], RDFDatabankConfig.endpointuser, "Created by") + self.assertEqual(state['metadata']['embargoed'], True, "Embargoed?") + self.failUnless('Created new data package' in state['versionlog']['0'], "Version 0 log") + self.failUnless('Added or updated file testdir.zip' in state['versionlog']['1'], "Version 1 log") + self.failUnless("Deleted file testdir.zip" in state['versionlog']['2'], "Version 2 log") + self.assertEqual(len(parts.keys()), 3, "Parts") + self.assertEqual(len(parts['4=TestSubmission'].keys()), 13, "File stats for 4=TestSubmission") + self.assertEqual(len(parts['manifest.rdf'].keys()), 13, "File stats for manifest.rdf") + + def testFileUpdate(self): + """Update file in dataset - POST file to /silo_name/datasets/dataset_name (x 2)""" + # Create a new dataset, check response + self.createSubmissionDataset() + # Upload zip file, check response (uploads the file testdir.zip) + zipdata = self.uploadSubmissionZipfile() + # Access and check list of contents + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + subj = URIRef(self.getManifestUri("datasets/TestSubmission")) + oxds = "http://vocab.ox.ac.uk/dataset/schema#" + self.assertEqual(len(rdfgraph),12,'Graph length %i' %len(rdfgraph)) + self.failUnless((subj,URIRef(oxds+"currentVersion"),'1') in rdfgraph, 'oxds:currentVersion') + # Access and check zip file content and version + (resp, zipfile) = self.doHTTP_GET( + resource="datasets/TestSubmission/testdir.zip", + expect_status=200, expect_reason="OK", expect_type="application/zip") + self.assertEqual(zipdata, zipfile, "Difference between local and remote zipfile!") + # Upload zip file again, check response + zipdata = self.updateSubmissionZipfile(file_to_upload="testdir2.zip", filename="testdir.zip") + # Access and check list of contents + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + subj = URIRef(self.getManifestUri("datasets/TestSubmission")) + base = self.getManifestUri("datasets/TestSubmission/") + dcterms = "http://purl.org/dc/terms/" + ore = "http://www.openarchives.org/ore/terms/" + oxds = "http://vocab.ox.ac.uk/dataset/schema#" + stype = URIRef(oxds+"DataSet") + self.assertEqual(len(rdfgraph),12,'Graph length %i' %len(rdfgraph)) + self.failUnless((subj,RDF.type,stype) in rdfgraph, 'Testing submission type: '+subj+", "+stype) + self.failUnless((subj,URIRef(dcterms+"created"),None) in rdfgraph, 'dcterms:created') + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"testdir.zip")) in rdfgraph) + self.failUnless((subj,URIRef(dcterms+"identifier"),None) in rdfgraph, 'dcterms:identifier') + self.failUnless((subj,URIRef(dcterms+"mediator"),None) in rdfgraph, 'dcterms:mediator') + self.failUnless((subj,URIRef(dcterms+"rights"),None) in rdfgraph, 'dcterms:rights') + self.failUnless((subj,URIRef(dcterms+"license"),None) in rdfgraph, 'dcterms:license') + self.failUnless((subj,URIRef(dcterms+"publisher"),None) in rdfgraph, 'dcterms:publisher') + self.failUnless((subj,URIRef(oxds+"isEmbargoed"),None) in rdfgraph, 'oxds:isEmbargoed') + self.failUnless((subj,URIRef(oxds+"embargoedUntil"),None) in rdfgraph, 'oxds:embargoedUntil') + self.failUnless((subj,URIRef(oxds+"currentVersion"),'2') in rdfgraph, 'oxds:currentVersion') + self.failUnless((subj,URIRef(dcterms+"modified"),None) in rdfgraph, 'dcterms:modified') + # Access and check zip file content + (resp, zipfile) = self.doHTTP_GET( + resource="datasets/TestSubmission/testdir.zip", + expect_status=200, expect_reason="OK", expect_type="application/zip") + self.assertEqual(zipdata, zipfile, "Difference between local and remote zipfile!") + #Access state information and check + (resp, data) = self.doHTTP_GET( + resource="states/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/json") + state = data['state'] + parts = data['parts'] + self.assertEqual(len(state.keys()), 12, "States") + self.assertEqual(state['item_id'], "TestSubmission", "Submission item identifier") + self.assertEqual(len(state['versions']), 3, "Three versions") + self.assertEqual(state['versions'][0], '0', "Version 0") + self.assertEqual(state['versions'][1], '1', "Version 1") + self.assertEqual(state['versions'][2], '2', "Version 2") + self.assertEqual(state['currentversion'], '2', "Current version == 2") + self.assertEqual(state['rdffileformat'], 'xml', "RDF file type") + self.assertEqual(state['rdffilename'], 'manifest.rdf', "RDF file name") + self.assertEqual(len(state['files']['0']), 1, "List should contain just manifest.rdf") + self.assertEqual(len(state['files']['1']), 2, "List should contain manifest.rdf and testdir.zip") + self.assertEqual(len(state['files']['2']), 2, "List should contain manifest.rdf and testdir.zip") + self.assertEqual(len(state['metadata_files']['0']), 0, "metadata_files of version 0") + self.assertEqual(len(state['metadata_files']['1']), 0, "metadata_files of version 1") + self.assertEqual(len(state['metadata_files']['2']), 0, "metadata_files of version 2") + self.assertEqual(len(state['subdir']['0']), 0, "Subdirectory count for version 0") + self.assertEqual(len(state['subdir']['1']), 0, "Subdirectory count for version 1") + self.assertEqual(len(state['subdir']['2']), 0, "Subdirectory count for version 2") + self.assertEqual(state['metadata']['createdby'], RDFDatabankConfig.endpointuser, "Created by") + self.assertEqual(state['metadata']['embargoed'], True, "Embargoed?") + self.failUnless('Created new data package' in state['versionlog']['0'], "Version 0 log") + self.failUnless("Added or updated file testdir.zip" in state['versionlog']['1'], "Version 1 log") + self.failUnless("Added or updated file testdir.zip" in state['versionlog']['2'], "Version 2 log") + self.assertEqual(len(parts.keys()), 4, "Parts") + self.assertEqual(len(parts['4=TestSubmission'].keys()), 13, "File stats for 4=TestSubmission") + self.assertEqual(len(parts['manifest.rdf'].keys()), 13, "File stats for manifest.rdf") + self.assertEqual(len(parts['testdir.zip'].keys()), 13, "File stats for testdir.zip") + + def testGetDatasetByVersion(self): + """Upload files to a dataset - POST file to /silo_name/datasets/dataset_name. Access each of the versions and the files in that version""" + #Definitions + subj = URIRef(self.getManifestUri("datasets/TestSubmission")) + base = self.getManifestUri("datasets/TestSubmission/") + dcterms = "http://purl.org/dc/terms/" + ore = "http://www.openarchives.org/ore/terms/" + oxds = "http://vocab.ox.ac.uk/dataset/schema#" + stype = URIRef(oxds+"DataSet") + #---------Version 0 + # Create a new dataset, check response + self.createSubmissionDataset() + # Access and check list of contents + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + self.assertEqual(len(rdfgraph),10,'Graph length %i' %len(rdfgraph)) + #Access state information and check + (resp, data) = self.doHTTP_GET( + resource="states/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/json") + state = data['state'] + self.failUnless('Created new data package' in state['versionlog']['0'], "Version 0 log") + parts = data['parts'] + self.assertEqual(len(state.keys()), 12, "States") + self.assertEqual(len(parts.keys()), 3, "Parts") + #---------Version 1 + # Upload zip file, check response + zipdata = self.uploadSubmissionZipfile() + # Access and check list of contents + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + self.assertEqual(len(rdfgraph),12,'Graph length %i' %len(rdfgraph)) + # Access and check zip file content + (resp, zipfile) = self.doHTTP_GET( + resource="datasets/TestSubmission/testdir.zip", + expect_status=200, expect_reason="OK", expect_type="application/zip") + self.assertEqual(zipdata, zipfile, "Difference between local and remote zipfile!") + #Access state information and check + (resp, data) = self.doHTTP_GET( + resource="states/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/json") + state = data['state'] + parts = data['parts'] + self.assertEqual(len(state.keys()), 12, "States") + self.assertEqual(len(parts.keys()), 4, "Parts") + self.failUnless("Added or updated file testdir.zip" in state['versionlog']['1'], "Version 1 log") + # Access and check list of contents of version 0 + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission?version=0", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + self.assertEqual(len(rdfgraph),10,'Graph length %i' %len(rdfgraph)) + #---------Version 2 + # Upload zip file, check response + zipdata2 = self.uploadSubmissionZipfile(file_to_upload="testdir2.zip") + # Access and check list of contents + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + self.assertEqual(len(rdfgraph),13,'Graph length %i' %len(rdfgraph)) + # Access and check zip file content + (resp, zipfile) = self.doHTTP_GET( + resource="datasets/TestSubmission/testdir.zip", + expect_status=200, expect_reason="OK", expect_type="application/zip") + self.assertEqual(zipdata, zipfile, "Difference between local and remote zipfile - testdir.zip!") + (resp, zipfile2) = self.doHTTP_GET( + resource="datasets/TestSubmission/testdir2.zip", + expect_status=200, expect_reason="OK", expect_type="application/zip") + self.assertEqual(zipdata2, zipfile2, "Difference between local and remote zipfile - testdir2.zip!") + #Access state information and check + (resp, data) = self.doHTTP_GET( + resource="states/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/json") + state = data['state'] + parts = data['parts'] + self.assertEqual(len(state.keys()), 12, "States") + self.assertEqual(len(parts.keys()), 5, "Parts") + self.failUnless("Added or updated file testdir2.zip" in state['versionlog']['2'], "Version 2 log") + #---------Version 3 + # Delete file, check response + resp = self.doHTTP_DELETE( + resource="datasets/TestSubmission/testdir.zip", + expect_status=200, expect_reason="OK") + # Access and check list of contents + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + self.assertEqual(len(rdfgraph),12,'Graph length %i' %len(rdfgraph)) + # Access and check zip file content + (resp, zipfile) = self.doHTTP_GET( + resource="datasets/TestSubmission/testdir.zip", + expect_status=404, expect_reason="Not Found") + (resp, zipfile2) = self.doHTTP_GET( + resource="datasets/TestSubmission/testdir2.zip", + expect_status=200, expect_reason="OK", expect_type="application/zip") + self.assertEqual(zipdata2, zipfile2, "Difference between local and remote zipfile - testdir2.zip!") + #Access state information and check + (resp, data) = self.doHTTP_GET( + resource="states/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/json") + state = data['state'] + parts = data['parts'] + self.assertEqual(len(state.keys()), 12, "States") + self.assertEqual(len(parts.keys()), 4, "Parts") + self.failUnless("Deleted file testdir.zip" in state['versionlog']['3'], "Version 3 log") + #---------Version 4 + # Update zip file, check response + zipdata3 = self.updateSubmissionZipfile(file_to_upload="testrdf4.zip", filename="testdir2.zip") + # Access and check list of contents + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + self.assertEqual(len(rdfgraph),12,'Graph length %i' %len(rdfgraph)) + # Access and check zip file content + (resp, zipfile) = self.doHTTP_GET( + resource="datasets/TestSubmission/testdir.zip", + expect_status=404, expect_reason="Not Found") + (resp, zipfile2) = self.doHTTP_GET( + resource="datasets/TestSubmission/testdir2.zip", + expect_status=200, expect_reason="OK", expect_type="application/zip") + self.assertEqual(zipdata3, zipfile2, "Difference between local and remote zipfile - testdir2.zip!") + #Access state information and check + (resp, data) = self.doHTTP_GET( + resource="states/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/json") + state = data['state'] + parts = data['parts'] + self.assertEqual(len(state.keys()), 12, "States") + self.assertEqual(len(parts.keys()), 4, "Parts") + self.failUnless("Added or updated file testdir2.zip" in state['versionlog']['4'], "Version 4 log") + #=========Access each of the versions + #---------Version 0 + # Access and check list of contents of version 0 + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission?version=0", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + self.assertEqual(len(rdfgraph),10,'Graph length %i' %len(rdfgraph)) + self.failUnless((subj,RDF.type,stype) in rdfgraph, 'Testing submission type: '+subj+", "+stype) + self.failUnless((subj,URIRef(dcterms+"created"),None) in rdfgraph, 'dcterms:created') + self.failUnless((subj,URIRef(dcterms+"identifier"),None) in rdfgraph, 'dcterms:identifier') + self.failUnless((subj,URIRef(dcterms+"mediator"),None) in rdfgraph, 'dcterms:mediator') + self.failUnless((subj,URIRef(dcterms+"rights"),None) in rdfgraph, 'dcterms:rights') + self.failUnless((subj,URIRef(dcterms+"license"),None) in rdfgraph, 'dcterms:license') + self.failUnless((subj,URIRef(dcterms+"publisher"),None) in rdfgraph, 'dcterms:publisher') + self.failUnless((subj,URIRef(oxds+"isEmbargoed"),None) in rdfgraph, 'oxds:isEmbargoed') + self.failUnless((subj,URIRef(oxds+"embargoedUntil"),None) in rdfgraph, 'oxds:embargoedUntil') + self.failUnless((subj,URIRef(oxds+"currentVersion"),'0') in rdfgraph, 'oxds:currentVersion') + #Access state information and check + (resp, data) = self.doHTTP_GET( + resource="states/TestSubmission?version=0", + expect_status=200, expect_reason="OK", expect_type="application/json") + state = data['state'] + parts = data['parts'] + self.assertEqual(len(state.keys()), 12, "States") + self.failUnless('Created new data package' in state['versionlog']['0'], "Version 0 log") + self.assertEqual(len(parts.keys()), 3, "Parts") + self.assertEqual(len(parts['4=TestSubmission'].keys()), 13, "File stats for 4=TestSubmission") + self.assertEqual(len(parts['manifest.rdf'].keys()), 13, "File stats for manifest.rdf") + #---------Version 1 + # Access and check list of contents of version 1 + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission?version=1", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + self.assertEqual(len(rdfgraph),12,'Graph length %i' %len(rdfgraph)) + self.failUnless((subj,RDF.type,stype) in rdfgraph, 'Testing submission type: '+subj+", "+stype) + self.failUnless((subj,URIRef(dcterms+"created"),None) in rdfgraph, 'dcterms:created') + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"testdir.zip")) in rdfgraph) + self.failUnless((subj,URIRef(dcterms+"identifier"),None) in rdfgraph, 'dcterms:identifier') + self.failUnless((subj,URIRef(dcterms+"mediator"),None) in rdfgraph, 'dcterms:mediator') + self.failUnless((subj,URIRef(dcterms+"rights"),None) in rdfgraph, 'dcterms:rights') + self.failUnless((subj,URIRef(dcterms+"license"),None) in rdfgraph, 'dcterms:license') + self.failUnless((subj,URIRef(dcterms+"publisher"),None) in rdfgraph, 'dcterms:publisher') + self.failUnless((subj,URIRef(oxds+"isEmbargoed"),None) in rdfgraph, 'oxds:isEmbargoed') + self.failUnless((subj,URIRef(oxds+"embargoedUntil"),None) in rdfgraph, 'oxds:embargoedUntil') + self.failUnless((subj,URIRef(oxds+"currentVersion"),'1') in rdfgraph, 'oxds:currentVersion') + self.failUnless((subj,URIRef(dcterms+"modified"),None) in rdfgraph, 'dcterms:modified') + # Access and check zip file content + (resp, zipfile) = self.doHTTP_GET( + resource="datasets/TestSubmission/testdir.zip?version=1", + expect_status=200, expect_reason="OK", expect_type="application/zip") + self.assertEqual(zipdata, zipfile, "Difference between local and remote zipfile - Version 1!") + #Access state information and check + (resp, data) = self.doHTTP_GET( + resource="states/TestSubmission?version=1", + expect_status=200, expect_reason="OK", expect_type="application/json") + state = data['state'] + parts = data['parts'] + self.assertEqual(len(state.keys()), 12, "States") + self.assertEqual(len(parts.keys()), 4, "Parts") + self.assertEqual(len(parts['4=TestSubmission'].keys()), 13, "File stats for 4=TestSubmission") + self.assertEqual(len(parts['manifest.rdf'].keys()), 13, "File stats for manifest.rdf") + self.assertEqual(len(parts['testdir.zip'].keys()), 13, "File stats for testdir.zip") + #---------Version 2 + # Access and check list of contents of version 2 + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission?version=2", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + self.assertEqual(len(rdfgraph),13,'Graph length %i' %len(rdfgraph)) + self.failUnless((subj,RDF.type,stype) in rdfgraph, 'Testing submission type: '+subj+", "+stype) + self.failUnless((subj,URIRef(dcterms+"created"),None) in rdfgraph, 'dcterms:created') + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"testdir.zip")) in rdfgraph) + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"testdir2.zip")) in rdfgraph) + self.failUnless((subj,URIRef(dcterms+"identifier"),None) in rdfgraph, 'dcterms:identifier') + self.failUnless((subj,URIRef(dcterms+"mediator"),None) in rdfgraph, 'dcterms:mediator') + self.failUnless((subj,URIRef(dcterms+"rights"),None) in rdfgraph, 'dcterms:rights') + self.failUnless((subj,URIRef(dcterms+"license"),None) in rdfgraph, 'dcterms:license') + self.failUnless((subj,URIRef(dcterms+"publisher"),None) in rdfgraph, 'dcterms:publisher') + self.failUnless((subj,URIRef(oxds+"isEmbargoed"),None) in rdfgraph, 'oxds:isEmbargoed') + self.failUnless((subj,URIRef(oxds+"embargoedUntil"),None) in rdfgraph, 'oxds:embargoedUntil') + self.failUnless((subj,URIRef(oxds+"currentVersion"),'2') in rdfgraph, 'oxds:currentVersion') + self.failUnless((subj,URIRef(dcterms+"modified"),None) in rdfgraph, 'dcterms:modified') + # Access and check zip file content + (resp, zipfile) = self.doHTTP_GET( + resource="datasets/TestSubmission/testdir.zip?version=2", + expect_status=200, expect_reason="OK", expect_type="application/zip") + self.assertEqual(zipdata, zipfile, "Difference between local and remote zipfile - Version 2!") + (resp, zipfile2) = self.doHTTP_GET( + resource="datasets/TestSubmission/testdir2.zip?version=2", + expect_status=200, expect_reason="OK", expect_type="application/zip") + self.assertEqual(zipdata2, zipfile2, "Difference between local and remote zipfile - Version 2!") + #Access state information and check + (resp, data) = self.doHTTP_GET( + resource="states/TestSubmission?version=2", + expect_status=200, expect_reason="OK", expect_type="application/json") + state = data['state'] + parts = data['parts'] + self.assertEqual(len(state.keys()), 12, "States") + self.assertEqual(len(parts.keys()), 5, "Parts") + self.assertEqual(len(parts['4=TestSubmission'].keys()), 13, "File stats for 4=TestSubmission") + self.assertEqual(len(parts['manifest.rdf'].keys()), 13, "File stats for manifest.rdf") + self.assertEqual(len(parts['testdir.zip'].keys()), 13, "File stats for testdir.zip") + self.assertEqual(len(parts['testdir2.zip'].keys()), 13, "File stats for testdir2.zip") + #---------Version 3 + # Access and check list of contents of version 3 + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission?version=3", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + self.assertEqual(len(rdfgraph),12,'Graph length %i' %len(rdfgraph)) + self.failUnless((subj,RDF.type,stype) in rdfgraph, 'Testing submission type: '+subj+", "+stype) + self.failUnless((subj,URIRef(dcterms+"created"),None) in rdfgraph, 'dcterms:created') + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"testdir2.zip")) in rdfgraph) + self.failUnless((subj,URIRef(dcterms+"identifier"),None) in rdfgraph, 'dcterms:identifier') + self.failUnless((subj,URIRef(dcterms+"mediator"),None) in rdfgraph, 'dcterms:mediator') + self.failUnless((subj,URIRef(dcterms+"rights"),None) in rdfgraph, 'dcterms:rights') + self.failUnless((subj,URIRef(dcterms+"license"),None) in rdfgraph, 'dcterms:license') + self.failUnless((subj,URIRef(dcterms+"publisher"),None) in rdfgraph, 'dcterms:publisher') + self.failUnless((subj,URIRef(oxds+"isEmbargoed"),None) in rdfgraph, 'oxds:isEmbargoed') + self.failUnless((subj,URIRef(oxds+"embargoedUntil"),None) in rdfgraph, 'oxds:embargoedUntil') + self.failUnless((subj,URIRef(oxds+"currentVersion"),'3') in rdfgraph, 'oxds:currentVersion') + self.failUnless((subj,URIRef(dcterms+"modified"),None) in rdfgraph, 'dcterms:modified') + # Access and check zip file content + (resp, zipfile) = self.doHTTP_GET( + resource="datasets/TestSubmission/testdir2.zip?version=3", + expect_status=200, expect_reason="OK", expect_type="application/zip") + self.assertEqual(zipdata2, zipfile, "Difference between local and remote zipfile - Version 3!") + (resp, zipfile) = self.doHTTP_GET( + resource="datasets/TestSubmission/testdir.zip?version=3", + expect_status=404, expect_reason="Not Found") + #Access state information and check + (resp, data) = self.doHTTP_GET( + resource="states/TestSubmission?version=3", + expect_status=200, expect_reason="OK", expect_type="application/json") + state = data['state'] + parts = data['parts'] + self.assertEqual(len(state.keys()), 12, "States") + self.assertEqual(len(parts.keys()), 4, "Parts") + self.assertEqual(len(parts['4=TestSubmission'].keys()), 13, "File stats for 4=TestSubmission") + self.assertEqual(len(parts['manifest.rdf'].keys()), 13, "File stats for manifest.rdf") + self.assertEqual(len(parts['testdir2.zip'].keys()), 13, "File stats for testdir2.zip") + #---------Version 4 + # Access and check list of contents of version 4 + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission?version=4", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + self.assertEqual(len(rdfgraph),12,'Graph length %i' %len(rdfgraph)) + self.failUnless((subj,RDF.type,stype) in rdfgraph, 'Testing submission type: '+subj+", "+stype) + self.failUnless((subj,URIRef(dcterms+"created"),None) in rdfgraph, 'dcterms:created') + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"testdir2.zip")) in rdfgraph) + self.failUnless((subj,URIRef(dcterms+"identifier"),None) in rdfgraph, 'dcterms:identifier') + self.failUnless((subj,URIRef(dcterms+"mediator"),None) in rdfgraph, 'dcterms:mediator') + self.failUnless((subj,URIRef(dcterms+"rights"),None) in rdfgraph, 'dcterms:rights') + self.failUnless((subj,URIRef(dcterms+"license"),None) in rdfgraph, 'dcterms:license') + self.failUnless((subj,URIRef(dcterms+"publisher"),None) in rdfgraph, 'dcterms:publisher') + self.failUnless((subj,URIRef(oxds+"isEmbargoed"),None) in rdfgraph, 'oxds:isEmbargoed') + self.failUnless((subj,URIRef(oxds+"embargoedUntil"),None) in rdfgraph, 'oxds:embargoedUntil') + self.failUnless((subj,URIRef(oxds+"currentVersion"),'4') in rdfgraph, 'oxds:currentVersion') + self.failUnless((subj,URIRef(dcterms+"modified"),None) in rdfgraph, 'dcterms:modified') + # Access and check zip file content + (resp, zipfile) = self.doHTTP_GET( + resource="datasets/TestSubmission/testdir2.zip?version=4", + expect_status=200, expect_reason="OK", expect_type="application/zip") + self.assertEqual(zipdata3, zipfile, "Difference between local and remote zipfile - Version 4!") + (resp, zipfile) = self.doHTTP_GET( + resource="datasets/TestSubmission/testdir.zip?version=4", + expect_status=404, expect_reason="Not Found") + #Access state information and check + (resp, data) = self.doHTTP_GET( + resource="states/TestSubmission?version=4", + expect_status=200, expect_reason="OK", expect_type="application/json") + state = data['state'] + parts = data['parts'] + self.assertEqual(len(state.keys()), 12, "States") + self.assertEqual(len(parts.keys()), 4, "Parts") + self.assertEqual(state['item_id'], "TestSubmission", "Submission item identifier") + self.assertEqual(len(state['versions']), 5, "Five versions") + self.assertEqual(state['versions'],['0', '1', '2', '3', '4'], "Versions") + self.assertEqual(state['currentversion'], '4', "Current version == 4") + self.assertEqual(state['rdffileformat'], 'xml', "RDF file type") + self.assertEqual(state['rdffilename'], 'manifest.rdf', "RDF file name") + self.assertEqual(state['files']['0'], ['manifest.rdf'], "List should contain just manifest.rdf") + self.assertEqual(len(state['files']['1']), 2, "List should contain manifest.rdf and testdir.zip") + self.assertEqual(len(state['files']['2']), 3, "List should contain manifest.rdf, testdir.zip and testdir2.zip") + self.assertEqual(len(state['files']['3']), 2, "List should contain manifest.rdf and testdir2.zip") + self.assertEqual(len(state['files']['4']), 2, "List should contain manifest.rdf and testdir2.zip") + self.assertEqual(len(state['metadata_files']['0']), 0, "metadata_files of version 0") + self.assertEqual(len(state['metadata_files']['1']), 0, "metadata_files of version 1") + self.assertEqual(len(state['metadata_files']['2']), 0, "metadata_files of version 2") + self.assertEqual(len(state['metadata_files']['3']), 0, "metadata_files of version 3") + self.assertEqual(len(state['metadata_files']['4']), 0, "metadata_files of version 4") + self.assertEqual(len(state['subdir']['0']), 0, "Subdirectory count for version 0") + self.assertEqual(len(state['subdir']['1']), 0, "Subdirectory count for version 1") + self.assertEqual(len(state['subdir']['2']), 0, "Subdirectory count for version 2") + self.assertEqual(len(state['subdir']['3']), 0, "Subdirectory count for version 3") + self.assertEqual(len(state['subdir']['4']), 0, "Subdirectory count for version 4") + self.assertEqual(state['metadata']['createdby'], RDFDatabankConfig.endpointuser, "Created by") + self.assertEqual(state['metadata']['embargoed'], True, "Embargoed?") + self.assertEqual(len(state['versionlog']), 5, "Version log for 5 version") + self.failUnless("Added or updated file testdir.zip" in state['versionlog']['1'], "Version 1 log") + self.failUnless("Added or updated file testdir2.zip" in state['versionlog']['2'], "Version 2 log") + self.failUnless("Deleted file testdir.zip" in state['versionlog']['3'], "Version 3 log") + self.failUnless("Added or updated file testdir2.zip" in state['versionlog']['4'], "Version 4 log") + self.assertEqual(len(parts['4=TestSubmission'].keys()), 13, "File stats for 4=TestSubmission") + self.assertEqual(len(parts['manifest.rdf'].keys()), 13, "File stats for manifest.rdf") + self.assertEqual(len(parts['testdir2.zip'].keys()), 13, "File stats for testdir2.zip") + + # Access and check list of contents of version 5 + (resp, respdata) = self.doHTTP_GET( + resource="datasets/TestSubmission?version=5", + expect_status=404, expect_reason="Not Found") + (resp, zipfile) = self.doHTTP_GET( + resource="datasets/TestSubmission/testdir2.zip?version=5", + expect_status=404, expect_reason="Not Found") + + def testPostMetadataFile(self): + """POST manifest to dataset - POST manifest.rdf to /silo_name/datasets/dataset_name""" + # Create a new dataset, check response + self.createSubmissionDataset() + # Upload metadata file, check response + zipdata = self.updateSubmissionZipfile(file_to_upload="ww1-860b-manifest.xml", filename="manifest.rdf") + # Access and check list of contents + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + subj = URIRef(self.getManifestUri("datasets/TestSubmission")) + base = self.getManifestUri("datasets/TestSubmission/") + dcterms = "http://purl.org/dc/terms/" + ore = "http://www.openarchives.org/ore/terms/" + oxds = "http://vocab.ox.ac.uk/dataset/schema#" + owl = "http://www.w3.org/2002/07/owl#" + bibo = "http://purl.org/ontology/bibo/" + geo = "http://www.w3.org/2003/01/geo/wgs84_pos#" + foaf = "http://xmlns.com/foaf/0.1/" + address = "http://schemas.talis.com/2005/address/schema#" + stype = URIRef(oxds+"DataSet") + stype2 = URIRef(bibo+"DocumentPart") + self.assertEqual(len(rdfgraph),42,'Graph length %i' %len(rdfgraph)) + self.failUnless((subj,RDF.type,stype) in rdfgraph, 'Testing submission type: '+subj+", "+stype) + self.failUnless((subj,URIRef(dcterms+"created"),None) in rdfgraph, 'dcterms:created') + self.failUnless((subj,URIRef(dcterms+"identifier"),None) in rdfgraph, 'dcterms:identifier') + self.failUnless((subj,URIRef(dcterms+"mediator"),None) in rdfgraph, 'dcterms:mediator') + self.failUnless((subj,URIRef(dcterms+"rights"),None) in rdfgraph, 'dcterms:rights') + self.failUnless((subj,URIRef(dcterms+"license"),None) in rdfgraph, 'dcterms:license') + self.failUnless((subj,URIRef(dcterms+"publisher"),None) in rdfgraph, 'dcterms:piblisher') + self.failUnless((subj,URIRef(oxds+"isEmbargoed"),None) in rdfgraph, 'oxds:isEmbargoed') + self.failUnless((subj,URIRef(oxds+"embargoedUntil"),None) in rdfgraph, 'oxds:embargoedUntil') + self.failUnless((subj,URIRef(oxds+"currentVersion"),'1') in rdfgraph, 'oxds:currentVersion') + self.failUnless((subj,URIRef(dcterms+"modified"),None) in rdfgraph, 'dcterms:modified') + self.failUnless((subj,RDF.type,stype2) in rdfgraph, 'Testing submission type: '+subj+", "+stype2) + self.failUnless((subj,URIRef(dcterms+"isPartOf"),URIRef("https://databank.ora.ox.ac.uk/ww1archives/datasets/ww1")) in rdfgraph, 'dcterms:isPartOf') + self.failUnless((subj,URIRef(dcterms+"isPartOf"),URIRef("https://databank.ora.ox.ac.uk/ww1archives/datasets/ww1-1123")) in rdfgraph, 'dcterms:isPartOf') + self.failUnless((subj,URIRef(dcterms+"creator"),"Thomas, Edward") in rdfgraph, 'dcterms:creator') + self.failUnless((subj,URIRef(dcterms+"title"),"Two Houses") in rdfgraph, 'dcterms:title') + self.failUnless((subj,URIRef(dcterms+"description"),"This manuscript is untitled but entitled 'Two Houses' in Edward Thomas Collected Poems") in rdfgraph, 'dcterms:description') + self.failUnless((subj,URIRef(dcterms+"spatial"),"London") in rdfgraph, 'dcterms:spatial') + self.failUnless((subj,URIRef(dcterms+"format"),"Notebook") in rdfgraph, 'dcterms:format') + self.failUnless((subj,URIRef(dcterms+"medium"),"Paper") in rdfgraph, 'dcterms:medium') + self.failUnless((subj,URIRef(dcterms+"type"),"Poem") in rdfgraph, 'dcterms:type') + self.failUnless((subj,URIRef(bibo+"number"),"13") in rdfgraph, 'bibo:number') + self.failUnless((subj,URIRef(dcterms+"source"),"MS Don d.28 f.13") in rdfgraph, 'dcterms:source') + self.failUnless((subj,URIRef(dcterms+"contributor"),"Everett Sharp") in rdfgraph, 'dcterms:contributor') + self.failUnless((subj,URIRef(dcterms+"identifier"),"ETBODDOND28-13.jpg") in rdfgraph, 'dcterms:identifier') + self.failUnless((subj,URIRef(dcterms+"rightsHolder"),"Copyright of The Bodleian Library, Oxford University / The Edward Thomas Literary Estate") in rdfgraph, 'dcterms:rightsHolder') + self.failUnless((subj,RDF.value,"51.501") in rdfgraph, 'rdf:value') + #self.failUnless((subj,URIRef(dcterms+"created"),"1915-07-22") in rdfgraph, 'dcterms:created') + self.failUnless((subj,URIRef(dcterms+"type"),URIRef("http://purl.org/dc/dcmitype/StillImage")) in rdfgraph, 'dcterms:type') + self.failUnless((subj,URIRef(dcterms+"spatial"),None) in rdfgraph, 'dcterms:spatial') + self.failUnless((None,URIRef(geo+"lat"),"51.501") in rdfgraph, 'geo:lat') + self.failUnless((None,URIRef(geo+"long"),"-0.1254") in rdfgraph, 'geo:long') + self.failUnless((subj,URIRef(bibo+"owner"),None) in rdfgraph, 'bibo:owner') + self.failUnless((None,RDF.type,URIRef(foaf+"Organization")) in rdfgraph, 'rdf:type') + self.failUnless((None,URIRef(foaf+"name"),"Bodleian Library, University of Oxford") in rdfgraph, 'foaf:name') + self.failUnless((None,URIRef(address+"streetAddress"),"Western Manuscripts Collections") in rdfgraph, 'address:streetAddress') + self.failUnless((None,URIRef(address+"streetAddress"),"Broad Street") in rdfgraph, 'address:streetAddress') + self.failUnless((None,URIRef(address+"localityName"),"Oxford") in rdfgraph, 'address:localityName') + self.failUnless((None,URIRef(address+"regionName"),"Oxfordshire") in rdfgraph, 'address:regionName') + self.failUnless((None,URIRef(address+"postalCode"),"OX13BG") in rdfgraph, 'address:postalCode') + self.failUnless((None,URIRef(address+"countryName"),"United Kingdom") in rdfgraph, 'address:countryName') + self.failUnless((None,URIRef(foaf+"homepage"),"http://www.bodley.ox.ac.uk/") in rdfgraph, 'foaf:homepage') + #Access state information and check + (resp, data) = self.doHTTP_GET( + resource="states/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/json") + state = data['state'] + parts = data['parts'] + self.assertEqual(len(state.keys()), 12, "States") + self.assertEqual(state['item_id'], "TestSubmission", "Submission item identifier") + self.assertEqual(len(state['versions']), 2, "Two versions") + self.assertEqual(state['versions'][0], '0', "Version 0") + self.assertEqual(state['versions'][1], '1', "Version 1") + self.assertEqual(state['currentversion'], '1', "Current version == 1") + self.assertEqual(state['rdffileformat'], 'xml', "RDF file type") + self.assertEqual(state['rdffilename'], 'manifest.rdf', "RDF file name") + self.assertEqual(len(state['files']['0']), 1, "List should contain just manifest.rdf") + self.assertEqual(len(state['files']['1']), 1, "List should contain just manifest.rdf") + self.assertEqual(len(state['metadata_files']['0']), 0, "metadata_files of version 0") + self.assertEqual(len(state['metadata_files']['1']), 0, "metadata_files of version 1") + self.assertEqual(len(state['subdir']['0']), 0, "Subdirectory count for version 0") + self.assertEqual(len(state['subdir']['1']), 0, "Subdirectory count for version 1") + self.assertEqual(state['metadata']['createdby'], RDFDatabankConfig.endpointuser, "Created by") + self.assertEqual(state['metadata']['embargoed'], True, "Embargoed?") + self.assertEqual(len(parts.keys()), 3, "Parts") + self.assertEqual(len(parts['4=TestSubmission'].keys()), 13, "File stats for 4=TestSubmission") + self.assertEqual(len(parts['manifest.rdf'].keys()), 13, "File stats for manifest.rdf") + + def testMetadataFileUpdate(self): + """POST manifest to dataset - POST manifest.rdf to /silo_name/datasets/dataset_name""" + # Create a new dataset, check response + self.createSubmissionDataset() + # Upload metadata file, check response + zipdata = self.updateSubmissionZipfile(file_to_upload="manifest.rdf") + # Access and check list of contents + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + subj = URIRef(self.getManifestUri("datasets/TestSubmission")) + base = self.getManifestUri("datasets/TestSubmission/") + dcterms = "http://purl.org/dc/terms/" + ore = "http://www.openarchives.org/ore/terms/" + oxds = "http://vocab.ox.ac.uk/dataset/schema#" + owl = "http://www.w3.org/2002/07/owl#" + bibo = "http://purl.org/ontology/bibo/" + geo = "http://www.w3.org/2003/01/geo/wgs84_pos#" + foaf = "http://xmlns.com/foaf/0.1/" + address = "http://schemas.talis.com/2005/address/schema#" + stype = URIRef(oxds+"DataSet") + self.assertEqual(len(rdfgraph),13,'Graph length %i' %len(rdfgraph)) + self.failUnless((subj,RDF.type,stype) in rdfgraph, 'Testing submission type: '+subj+", "+stype) + self.failUnless((subj,URIRef(dcterms+"created"),None) in rdfgraph, 'dcterms:created') + self.failUnless((subj,URIRef(dcterms+"identifier"),None) in rdfgraph, 'dcterms:identifier') + self.failUnless((subj,URIRef(dcterms+"mediator"),None) in rdfgraph, 'dcterms:mediator') + self.failUnless((subj,URIRef(dcterms+"rights"),None) in rdfgraph, 'dcterms:rights') + self.failUnless((subj,URIRef(dcterms+"license"),None) in rdfgraph, 'dcterms:license') + self.failUnless((subj,URIRef(dcterms+"publisher"),None) in rdfgraph, 'dcterms:piblisher') + self.failUnless((subj,URIRef(oxds+"isEmbargoed"),None) in rdfgraph, 'oxds:isEmbargoed') + self.failUnless((subj,URIRef(oxds+"embargoedUntil"),None) in rdfgraph, 'oxds:embargoedUntil') + self.failUnless((subj,URIRef(oxds+"currentVersion"),'1') in rdfgraph, 'oxds:currentVersion') + self.failUnless((subj,URIRef(dcterms+"modified"),None) in rdfgraph, 'dcterms:modified') + self.failUnless((subj,URIRef(dcterms+"title"),"Test dataset with merged metadata") in rdfgraph, 'dcterms:title') + self.failUnless((subj,URIRef(owl+"sameAs"),URIRef("http://example.org/testrdf/")) in rdfgraph, 'owl:sameAs') + # Update metadata file, check response + zipdata = self.updateSubmissionZipfile(file_to_upload="ww1-2862-manifest.xml", filename="manifest.rdf") + # Access and check list of contents + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + stype2 = URIRef(bibo+"Document") + doctext = """
          She had a name among the children; +
          But no one loved though someone owned +
          Her, locked her out of doors at bedtime +
          And had her kittens duly drowned. +
          In Spring, nevertheless, this cat +
          Ate blackbirds, thrushes, nightingales, +
          And birds of bright voice and plume and flight, +
          As well as scraps from neighbours' pails. +
          I loathed and hated her for this; +
          One speckle on a thrush's breast +
          Was worth a million such; and yet +
          She lived long, till God gave her rest. +

          """ + self.assertEqual(len(rdfgraph),32,'Graph length %i' %len(rdfgraph)) + self.failUnless((subj,RDF.type,stype) in rdfgraph, 'Testing submission type: '+subj+", "+stype) + self.failUnless((subj,URIRef(dcterms+"created"),None) in rdfgraph, 'dcterms:created') + self.failUnless((subj,URIRef(dcterms+"identifier"),None) in rdfgraph, 'dcterms:identifier') + self.failUnless((subj,URIRef(dcterms+"mediator"),None) in rdfgraph, 'dcterms:mediator') + self.failUnless((subj,URIRef(dcterms+"rights"),None) in rdfgraph, 'dcterms:rights') + self.failUnless((subj,URIRef(dcterms+"license"),None) in rdfgraph, 'dcterms:license') + self.failUnless((subj,URIRef(dcterms+"publisher"),None) in rdfgraph, 'dcterms:publisher') + self.failUnless((subj,URIRef(oxds+"isEmbargoed"),None) in rdfgraph, 'oxds:isEmbargoed') + self.failUnless((subj,URIRef(oxds+"embargoedUntil"),None) in rdfgraph, 'oxds:embargoedUntil') + self.failUnless((subj,URIRef(oxds+"currentVersion"),'2') in rdfgraph, 'oxds:currentVersion') + self.failUnless((subj,URIRef(dcterms+"modified"),None) in rdfgraph, 'dcterms:modified') + self.failUnless((subj,URIRef(dcterms+"title"),'A Cat') in rdfgraph, 'dcterms:title') + self.failUnless((subj,URIRef(owl+"sameAs"),URIRef("http://example.org/testrdf/")) in rdfgraph, 'owl:sameAs') + self.failUnless((subj,URIRef(owl+"sameAs"),URIRef("http://databank.ora.ox.ac.uk/ww1archives/datasets/ww1-2862")) in rdfgraph, 'owl:sameAs') + self.failUnless((subj,RDF.type,stype2) in rdfgraph, 'Testing submission type: '+subj+", "+stype2) + self.failUnless((subj,URIRef(dcterms+"isPartOf"),URIRef("https://databank.ora.ox.ac.uk/ww1archives/datasets/ww1")) in rdfgraph, 'dcterms:isPartOf') + self.failUnless((subj,URIRef(dcterms+"creator"),"Thomas, Edward") in rdfgraph, 'dcterms:creator') + self.failUnless((subj,URIRef(dcterms+"type"),"Poem") in rdfgraph, 'dcterms:type') + self.failUnless((subj,URIRef(dcterms+"type"),URIRef("http://purl.org/dc/dcmitype/Text")) in rdfgraph, 'dcterms:type') + self.failUnless((subj,URIRef(dcterms+"rightsHolder"),"Copyright Edward Thomas, 1979, reproduced under licence from Faber and Faber Ltd.") in rdfgraph, 'dcterms:rightsHolder') + self.failUnless((subj,RDF.value,Literal(doctext)) in rdfgraph, 'rdf:value') + self.failUnless((subj,URIRef(dcterms+"source"),"Edward Thomas Collected Poems") in rdfgraph, 'dcterms:source') + #self.failUnless((subj,URIRef(dcterms+"created"),"1979-01-01/1979-12-31") in rdfgraph, 'dcterms:created') + self.failUnless((subj,URIRef(bibo+"editor"),"Thomas, George") in rdfgraph, 'bibo:editor') + self.failUnless((subj,URIRef(bibo+"owner"),None) in rdfgraph, 'bibo:owner') + self.failUnless((None,RDF.type,URIRef(foaf+"Organization")) in rdfgraph, 'rdf:type') + self.failUnless((None,URIRef(foaf+"name"),"ProQuest") in rdfgraph, 'foaf:name') + self.failUnless((None,URIRef(foaf+"homepage"),"http://lion.chadwyck.co.uk/") in rdfgraph, 'foaf:homepage') + self.failUnless((subj,URIRef(dcterms+"publisher"),None) in rdfgraph, 'dcterms:publisher') + self.failUnless((None,URIRef(foaf+"name"),"Faber and Faber") in rdfgraph, 'foaf:name') + self.failUnless((None,URIRef(address+"localityName"),"London") in rdfgraph, 'address:localityName') + + #Access state information and check + (resp, data) = self.doHTTP_GET( + resource="states/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/json") + state = data['state'] + parts = data['parts'] + self.assertEqual(len(state.keys()), 12, "States") + self.assertEqual(state['item_id'], "TestSubmission", "Submission item identifier") + self.assertEqual(len(state['versions']), 3, "Three versions") + self.assertEqual(state['versions'][0], '0', "Version 0") + self.assertEqual(state['versions'][1], '1', "Version 1") + self.assertEqual(state['versions'][2], '2', "Version 2") + self.assertEqual(state['currentversion'], '2', "Current version == 2") + self.assertEqual(state['rdffileformat'], 'xml', "RDF file type") + self.assertEqual(state['rdffilename'], 'manifest.rdf', "RDF file name") + self.assertEqual(len(state['files']['0']), 1, "List should contain just manifest.rdf") + self.assertEqual(len(state['files']['1']), 1, "List should contain just manifest.rdf") + self.assertEqual(len(state['files']['2']), 1, "List should contain just manifest.rdf") + self.assertEqual(len(state['metadata_files']['0']), 0, "metadata_files of version 0") + self.assertEqual(len(state['metadata_files']['1']), 0, "metadata_files of version 1") + self.assertEqual(len(state['metadata_files']['2']), 0, "metadata_files of version 2") + self.assertEqual(len(state['subdir']['0']), 0, "Subdirectory count for version 0") + self.assertEqual(len(state['subdir']['1']), 0, "Subdirectory count for version 1") + self.assertEqual(len(state['subdir']['2']), 0, "Subdirectory count for version 2") + self.assertEqual(state['metadata']['createdby'], RDFDatabankConfig.endpointuser, "Created by") + self.assertEqual(state['metadata']['embargoed'], True, "Embargoed?") + #logger.debug("Versionlog "+str(state['versionlog'])) + self.failUnless("Created new data package" in state['versionlog']['0'], "Version 0 log") + self.failUnless("Updated file manifest.rdf" in state['versionlog']['1'], "Version 1 log") + self.failUnless("Updated file manifest.rdf" in state['versionlog']['2'], "Version 2 log") + self.assertEqual(len(parts.keys()), 3, "Parts") + self.assertEqual(len(parts['4=TestSubmission'].keys()), 13, "File stats for 4=TestSubmission") + self.assertEqual(len(parts['manifest.rdf'].keys()), 13, "File stats for manifest.rdf") + + def testMetadataFileDelete(self): + """Delete manifest in dataset - DELETE /silo_name/datasets/dataset_name/manifest.rdf""" + # Create a new dataset, check response + self.createSubmissionDataset() + # Delete metadata file, check response + resp = self.doHTTP_DELETE( + resource="datasets/TestSubmission/manifest.rdf", + expect_status=403, expect_reason="Forbidden") + # Access and check list of contents + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + subj = URIRef(self.getManifestUri("datasets/TestSubmission")) + dcterms = "http://purl.org/dc/terms/" + oxds = "http://vocab.ox.ac.uk/dataset/schema#" + stype = URIRef(oxds+"DataSet") + self.assertEqual(len(rdfgraph),10,'Graph length %i' %len(rdfgraph)) + self.failUnless((subj,RDF.type,stype) in rdfgraph, 'Testing submission type: '+subj+", "+stype) + self.failUnless((subj,URIRef(dcterms+"created"),None) in rdfgraph, 'dcterms:created') + self.failUnless((subj,URIRef(dcterms+"identifier"),None) in rdfgraph, 'dcterms:identifier') + self.failUnless((subj,URIRef(dcterms+"mediator"),None) in rdfgraph, 'dcterms:mediator') + self.failUnless((subj,URIRef(dcterms+"rights"),None) in rdfgraph, 'dcterms:rights') + self.failUnless((subj,URIRef(dcterms+"license"),None) in rdfgraph, 'dcterms:license') + self.failUnless((subj,URIRef(dcterms+"publisher"),None) in rdfgraph, 'dcterms:publisher') + self.failUnless((subj,URIRef(oxds+"isEmbargoed"),None) in rdfgraph, 'oxds:isEmbargoed') + self.failUnless((subj,URIRef(oxds+"embargoedUntil"),None) in rdfgraph, 'oxds:embargoedUntil') + self.failUnless((subj,URIRef(oxds+"currentVersion"),'0') in rdfgraph, 'oxds:currentVersion') + + def testPutCreateFile(self): + """PUT file contents to new filename - PUT file contents to /silo_name/datasets/dataset_name/file_name""" + # Create a new dataset, check response + self.createSubmissionDataset() + # Put zip file, check response + zipdata = open("testdata/testdir.zip").read() + (resp, respdata) = self.doHTTP_PUT(zipdata, resource="datasets/TestSubmission/testdir.zip", + expect_status=201, expect_reason="Created", expect_type="text/plain") + LHobtained = resp.getheader('Content-Location', None) + LHexpected = "%sdatasets/TestSubmission/testdir.zip"%self._endpointpath + self.assertEquals(LHobtained, LHexpected, 'Content-Location not correct') + # Access and check list of contents + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + subj = URIRef(self.getManifestUri("datasets/TestSubmission")) + base = self.getManifestUri("datasets/TestSubmission/") + dcterms = "http://purl.org/dc/terms/" + ore = "http://www.openarchives.org/ore/terms/" + oxds = "http://vocab.ox.ac.uk/dataset/schema#" + stype = URIRef(oxds+"DataSet") + self.assertEqual(len(rdfgraph),12,'Graph length %i' %len(rdfgraph)) + self.failUnless((subj,RDF.type,stype) in rdfgraph, 'Testing submission type: '+subj+", "+stype) + self.failUnless((subj,URIRef(dcterms+"created"),None) in rdfgraph, 'dcterms:created') + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"testdir.zip")) in rdfgraph) + self.failUnless((subj,URIRef(dcterms+"identifier"),None) in rdfgraph, 'dcterms:identifier') + self.failUnless((subj,URIRef(dcterms+"mediator"),None) in rdfgraph, 'dcterms:mediator') + self.failUnless((subj,URIRef(dcterms+"rights"),None) in rdfgraph, 'dcterms:rights') + self.failUnless((subj,URIRef(dcterms+"license"),None) in rdfgraph, 'dcterms:license') + self.failUnless((subj,URIRef(dcterms+"publisher"),None) in rdfgraph, 'dcterms:publisher') + self.failUnless((subj,URIRef(oxds+"isEmbargoed"),None) in rdfgraph, 'oxds:isEmbargoed') + self.failUnless((subj,URIRef(oxds+"embargoedUntil"),None) in rdfgraph, 'oxds:embargoedUntil') + self.failUnless((subj,URIRef(oxds+"currentVersion"),'1') in rdfgraph, 'oxds:currentVersion') + self.failUnless((subj,URIRef(dcterms+"modified"),None) in rdfgraph, 'dcterms:modified') + # Access and check zip file content + (resp, zipfile) = self.doHTTP_GET( + resource="datasets/TestSubmission/testdir.zip", + expect_status=200, expect_reason="OK", expect_type="application/zip") + self.assertEqual(zipdata, zipfile, "Difference between local and remote zipfile!") + #Access state information and check + (resp, data) = self.doHTTP_GET( + resource="states/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/json") + state = data['state'] + parts = data['parts'] + self.assertEqual(len(state.keys()), 12, "States") + self.assertEqual(state['item_id'], "TestSubmission", "Submission item identifier") + self.assertEqual(len(state['versions']), 2, "Two versions") + self.assertEqual(state['versions'][0], '0', "Version 0") + self.assertEqual(state['versions'][1], '1', "Version 1") + self.assertEqual(state['currentversion'], '1', "Current version == 1") + self.assertEqual(state['rdffileformat'], 'xml', "RDF file type") + self.assertEqual(state['rdffilename'], 'manifest.rdf', "RDF file name") + self.assertEqual(state['files']['0'], ['manifest.rdf'], "List should contain just manifest.rdf") + self.assertEqual(len(state['files']['1']), 2, "List should contain manifest.rdf and testdir.zip") + self.assertEqual(len(state['metadata_files']['0']), 0, "metadata_files of version 0") + self.assertEqual(len(state['metadata_files']['1']), 0, "metadata_files of version 1") + self.assertEqual(len(state['subdir']['0']), 0, "Subdirectory count for version 0") + self.assertEqual(len(state['subdir']['1']), 0, "Subdirectory count for version 1") + self.assertEqual(state['metadata']['createdby'], RDFDatabankConfig.endpointuser, "Created by") + self.assertEqual(state['metadata']['embargoed'], True, "Embargoed?") + self.failUnless("Created new data package" in state['versionlog']['0'], "Version 0 log") + self.failUnless("Added or updated file testdir.zip" in state['versionlog']['1'], "Version 1 log") + self.assertEqual(len(parts.keys()), 4, "Parts") + self.assertEqual(len(parts['4=TestSubmission'].keys()), 13, "File stats for 4=TestSubmission") + self.assertEqual(len(parts['manifest.rdf'].keys()), 13, "File stats for manifest.rdf") + self.assertEqual(len(parts['testdir.zip'].keys()), 13, "File stats for testdir.zip") + + def testPutUpdateFile(self): + """PUT file contents to existing filename - PUT file contents to /silo_name/datasets/dataset_name/file_name""" + # Create a new dataset, check response + self.createSubmissionDataset() + # Upload zip file, check response + zipdata = self.uploadSubmissionZipfile(file_to_upload="testdir.zip") + # Access content + (resp, respdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + # Put zip file, check response + zipdata2 = open("testdata/testrdf3.zip").read() + (resp, respdata) = self.doHTTP_PUT(zipdata2, resource="datasets/TestSubmission/testrdf3.zip", + expect_status=201, expect_reason="Created", expect_type="text/plain") + LHobtained = resp.getheader('Content-Location', None) + LHexpected = "%sdatasets/TestSubmission/testrdf3.zip"%self._endpointpath + self.assertEquals(LHobtained, LHexpected, 'Content-Location not correct') + # Access and check list of contents + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + subj = URIRef(self.getManifestUri("datasets/TestSubmission")) + oxds = "http://vocab.ox.ac.uk/dataset/schema#" + self.assertEqual(len(rdfgraph),13,'Graph length %i' %len(rdfgraph)) + self.failUnless((subj,URIRef(oxds+"currentVersion"),'2') in rdfgraph, 'oxds:currentVersion') + # Access and check zip file content and version + (resp, zipfile) = self.doHTTP_GET( + resource="datasets/TestSubmission/testdir.zip", + expect_status=200, expect_reason="OK", expect_type="application/zip") + self.assertEqual(zipdata, zipfile, "Difference between local and remote zipfile!") + (resp, zipfile2) = self.doHTTP_GET( + resource="datasets/TestSubmission/testrdf3.zip", + expect_status=200, expect_reason="OK", expect_type="application/zip") + self.assertEqual(zipdata2, zipfile2, "Difference between local and remote zipfile!") + # Put zip file again, check response + zipdata3 = open("testdata/testdir2.zip").read() + (resp, respdata) = self.doHTTP_PUT(zipdata3, resource="datasets/TestSubmission/testdir.zip", + expect_status=204, expect_reason="No Content", expect_type="text/plain") + # Access and check list of contents + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + subj = URIRef(self.getManifestUri("datasets/TestSubmission")) + base = self.getManifestUri("datasets/TestSubmission/") + dcterms = "http://purl.org/dc/terms/" + ore = "http://www.openarchives.org/ore/terms/" + oxds = "http://vocab.ox.ac.uk/dataset/schema#" + stype = URIRef(oxds+"DataSet") + self.assertEqual(len(rdfgraph),13,'Graph length %i' %len(rdfgraph)) + self.failUnless((subj,RDF.type,stype) in rdfgraph, 'Testing submission type: '+subj+", "+stype) + self.failUnless((subj,URIRef(dcterms+"created"),None) in rdfgraph, 'dcterms:created') + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"testdir.zip")) in rdfgraph) + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"testrdf3.zip")) in rdfgraph) + self.failUnless((subj,URIRef(dcterms+"identifier"),None) in rdfgraph, 'dcterms:identifier') + self.failUnless((subj,URIRef(dcterms+"mediator"),None) in rdfgraph, 'dcterms:mediator') + self.failUnless((subj,URIRef(dcterms+"rights"),None) in rdfgraph, 'dcterms:rights') + self.failUnless((subj,URIRef(dcterms+"license"),None) in rdfgraph, 'dcterms:license') + self.failUnless((subj,URIRef(dcterms+"publisher"),None) in rdfgraph, 'dcterms:publisher') + self.failUnless((subj,URIRef(oxds+"isEmbargoed"),None) in rdfgraph, 'oxds:isEmbargoed') + self.failUnless((subj,URIRef(oxds+"embargoedUntil"),None) in rdfgraph, 'oxds:embargoedUntil') + self.failUnless((subj,URIRef(oxds+"currentVersion"),'3') in rdfgraph, 'oxds:currentVersion') + self.failUnless((subj,URIRef(dcterms+"modified"),None) in rdfgraph, 'dcterms:modified') + # Access and check zip file content + (resp, zipfile) = self.doHTTP_GET( + resource="datasets/TestSubmission/testdir.zip", + expect_status=200, expect_reason="OK", expect_type="application/zip") + self.assertEqual(zipdata3, zipfile, "Difference between local and remote zipfile!") + (resp, zipfile2) = self.doHTTP_GET( + resource="datasets/TestSubmission/testrdf3.zip", + expect_status=200, expect_reason="OK", expect_type="application/zip") + self.assertEqual(zipdata2, zipfile2, "Difference between local and remote zipfile!") + #Access state information and check + (resp, data) = self.doHTTP_GET( + resource="states/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/json") + state = data['state'] + parts = data['parts'] + self.assertEqual(len(state.keys()), 12, "States") + self.assertEqual(state['item_id'], "TestSubmission", "Submission item identifier") + self.assertEqual(len(state['versions']), 4, "Four versions") + self.assertEqual(state['versions'][0], '0', "Version 0") + self.assertEqual(state['versions'][1], '1', "Version 1") + self.assertEqual(state['versions'][2], '2', "Version 2") + self.assertEqual(state['versions'][3], '3', "Version 3") + self.assertEqual(state['currentversion'], '3', "Current version == 3") + self.assertEqual(state['rdffileformat'], 'xml', "RDF file type") + self.assertEqual(state['rdffilename'], 'manifest.rdf', "RDF file name") + self.assertEqual(state['files']['0'], ['manifest.rdf'], "List should contain just manifest.rdf") + self.assertEqual(len(state['files']['1']), 2, "List should contain manifest.rdf and testdir.zip") + self.assertEqual(len(state['files']['2']), 3, "List should contain manifest.rdf, testdir.zip and testrdf3.zip") + self.assertEqual(len(state['files']['3']), 3, "List should contain manifest.rdf, testdir.zip and testrdf3.zip") + self.assertEqual(len(state['metadata_files']['0']), 0, "metadata_files of version 0") + self.assertEqual(len(state['metadata_files']['1']), 0, "metadata_files of version 1") + self.assertEqual(len(state['metadata_files']['2']), 0, "metadata_files of version 2") + self.assertEqual(len(state['metadata_files']['3']), 0, "metadata_files of version 3") + self.assertEqual(len(state['subdir']['0']), 0, "Subdirectory count for version 0") + self.assertEqual(len(state['subdir']['1']), 0, "Subdirectory count for version 1") + self.assertEqual(len(state['subdir']['2']), 0, "Subdirectory count for version 2") + self.assertEqual(len(state['subdir']['3']), 0, "Subdirectory count for version 3") + self.assertEqual(state['metadata']['createdby'], RDFDatabankConfig.endpointuser, "Created by") + self.assertEqual(state['metadata']['embargoed'], True, "Embargoed?") + self.failUnless("Created new data package" in state['versionlog']['0'], "Version 0 log") + self.failUnless("Added or updated file testdir.zip" in state['versionlog']['1'], "Version 1 log") + self.failUnless("Added or updated file testrdf3.zip" in state['versionlog']['2'], "Version 2 log") + self.failUnless("Added or updated file testdir.zip" in state['versionlog']['3'], "Version 3 log") + self.assertEqual(len(parts.keys()), 5, "Parts") + self.assertEqual(len(parts['4=TestSubmission'].keys()), 13, "File stats for 4=TestSubmission") + self.assertEqual(len(parts['manifest.rdf'].keys()), 13, "File stats for manifest.rdf") + self.assertEqual(len(parts['testdir.zip'].keys()), 13, "File stats for testdir.zip") + self.assertEqual(len(parts['testrdf3.zip'].keys()), 13, "File stats for testrdf3.zip") + # Access and check zip file content of version 1 + (resp, zipfile) = self.doHTTP_GET( + resource="datasets/TestSubmission/testdir.zip?version=1", + expect_status=200, expect_reason="OK", expect_type="application/zip") + self.assertEqual(zipdata, zipfile, "Difference between local and remote zipfile!") + + def testPutMetadataFile(self): + """Add metadata to manifest - PUT metadata to /silo_name/datasets/dataset_name/manifest.rdf""" + # Create a new dataset, check response + self.createSubmissionDataset() + # Put manifest file, check response + zipdata = open("testdata/manifest.rdf").read() + (resp, respdata) = self.doHTTP_PUT(zipdata, resource="datasets/TestSubmission/manifest.rdf", + expect_status=204, expect_reason="No Content", expect_type="text/plain") + # Access and check list of contents + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + subj = URIRef(self.getManifestUri("datasets/TestSubmission")) + base = self.getManifestUri("datasets/TestSubmission/") + dcterms = "http://purl.org/dc/terms/" + ore = "http://www.openarchives.org/ore/terms/" + oxds = "http://vocab.ox.ac.uk/dataset/schema#" + owl = "http://www.w3.org/2002/07/owl#" + stype = URIRef(oxds+"DataSet") + self.assertEqual(len(rdfgraph),13,'Graph length %i' %len(rdfgraph)) + self.failUnless((subj,RDF.type,stype) in rdfgraph, 'Testing submission type: '+subj+", "+stype) + self.failUnless((subj,URIRef(dcterms+"created"),None) in rdfgraph, 'dcterms:created') + self.failUnless((subj,URIRef(dcterms+"identifier"),None) in rdfgraph, 'dcterms:identifier') + self.failUnless((subj,URIRef(dcterms+"mediator"),None) in rdfgraph, 'dcterms:mediator') + self.failUnless((subj,URIRef(dcterms+"rights"),None) in rdfgraph, 'dcterms:rights') + self.failUnless((subj,URIRef(dcterms+"license"),None) in rdfgraph, 'dcterms:license') + self.failUnless((subj,URIRef(dcterms+"publisher"),None) in rdfgraph, 'dcterms:publisher') + self.failUnless((subj,URIRef(oxds+"isEmbargoed"),None) in rdfgraph, 'oxds:isEmbargoed') + self.failUnless((subj,URIRef(oxds+"embargoedUntil"),None) in rdfgraph, 'oxds:embargoedUntil') + self.failUnless((subj,URIRef(oxds+"currentVersion"),'1') in rdfgraph, 'oxds:currentVersion') + self.failUnless((subj,URIRef(dcterms+"modified"),None) in rdfgraph, 'dcterms:modified') + self.failUnless((subj,URIRef(dcterms+"title"),"Test dataset with merged metadata") in rdfgraph, 'dcterms:title') + self.failUnless((subj,URIRef(owl+"sameAs"),URIRef("http://example.org/testrdf/")) in rdfgraph, 'owl:sameAs') + # Update metadata file, check response + zipdata = open("testdata/manifest2.rdf").read() + (resp, respdata) = self.doHTTP_PUT(zipdata, resource="datasets/TestSubmission/manifest.rdf", + expect_status=204, expect_reason="No Content", expect_type="text/plain") + # Access and check list of contents + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + self.assertEqual(len(rdfgraph),13,'Graph length %i' %len(rdfgraph)) + self.failUnless((subj,RDF.type,stype) in rdfgraph, 'Testing submission type: '+subj+", "+stype) + self.failUnless((subj,URIRef(dcterms+"created"),None) in rdfgraph, 'dcterms:created') + self.failUnless((subj,URIRef(dcterms+"identifier"),None) in rdfgraph, 'dcterms:identifier') + self.failUnless((subj,URIRef(dcterms+"mediator"),None) in rdfgraph, 'dcterms:mediator') + self.failUnless((subj,URIRef(dcterms+"rights"),None) in rdfgraph, 'dcterms:rights') + self.failUnless((subj,URIRef(dcterms+"license"),None) in rdfgraph, 'dcterms:license') + self.failUnless((subj,URIRef(dcterms+"publisher"),None) in rdfgraph, 'dcterms:publisher') + self.failUnless((subj,URIRef(oxds+"isEmbargoed"),None) in rdfgraph, 'oxds:isEmbargoed') + self.failUnless((subj,URIRef(oxds+"embargoedUntil"),None) in rdfgraph, 'oxds:embargoedUntil') + self.failUnless((subj,URIRef(oxds+"currentVersion"),'2') in rdfgraph, 'oxds:currentVersion') + self.failUnless((subj,URIRef(dcterms+"modified"),None) in rdfgraph, 'dcterms:modified') + self.failUnless((subj,URIRef(owl+"sameAs"),URIRef("http://example.org/testrdf/")) in rdfgraph, 'owl:sameAs') + self.failUnless((subj,URIRef(dcterms+"title"),'Test dataset with updated and merged metadata') in rdfgraph, 'dcterms:title') + #Access state information and check + (resp, data) = self.doHTTP_GET( + resource="states/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/json") + state = data['state'] + parts = data['parts'] + self.assertEqual(len(state.keys()), 12, "States") + self.assertEqual(state['item_id'], "TestSubmission", "Submission item identifier") + self.assertEqual(len(state['versions']), 3, "Three versions") + self.assertEqual(state['versions'][0], '0', "Version 0") + self.assertEqual(state['versions'][1], '1', "Version 1") + self.assertEqual(state['versions'][2], '2', "Version 2") + self.assertEqual(state['currentversion'], '2', "Current version == 2") + self.assertEqual(state['rdffileformat'], 'xml', "RDF file type") + self.assertEqual(state['rdffilename'], 'manifest.rdf', "RDF file name") + self.assertEqual(len(state['files']['0']), 1, "List should contain just manifest.rdf") + self.assertEqual(len(state['files']['1']), 1, "List should contain just manifest.rdf") + self.assertEqual(len(state['files']['2']), 1, "List should contain just manifest.rdf") + self.assertEqual(len(state['metadata_files']['0']), 0, "metadata_files of version 0") + self.assertEqual(len(state['metadata_files']['1']), 0, "metadata_files of version 1") + self.assertEqual(len(state['metadata_files']['2']), 0, "metadata_files of version 2") + self.assertEqual(len(state['subdir']['0']), 0, "Subdirectory count for version 0") + self.assertEqual(len(state['subdir']['1']), 0, "Subdirectory count for version 1") + self.assertEqual(len(state['subdir']['2']), 0, "Subdirectory count for version 2") + self.assertEqual(state['metadata']['createdby'], RDFDatabankConfig.endpointuser, "Created by") + self.assertEqual(state['metadata']['embargoed'], True, "Embargoed?") + #logger.debug("Versionlog "+str(state['versionlog'])) + self.failUnless("Updated file manifest.rdf" in state['versionlog']['2'], "Version 2 log") + self.assertEqual(len(parts.keys()), 3, "Parts") + self.assertEqual(len(parts['4=TestSubmission'].keys()), 13, "File stats for 4=TestSubmission") + self.assertEqual(len(parts['manifest.rdf'].keys()), 13, "File stats for manifest.rdf") + + def testUnicodeMetadataFileUpdate(self): + """POST/PUT manifest to dataset - /silo_name/datasets/dataset_name""" + # Create a new dataset, check response + self.createSubmissionDataset() + + subj = URIRef(self.getManifestUri("datasets/TestSubmission")) + base = self.getManifestUri("datasets/TestSubmission/") + dcterms = "http://purl.org/dc/terms/" + ore = "http://www.openarchives.org/ore/terms/" + oxds = "http://vocab.ox.ac.uk/dataset/schema#" + owl = "http://www.w3.org/2002/07/owl#" + stype = URIRef(oxds+"DataSet") + # POST unicode file 01, check response + zipdata = self.updateSubmissionZipfile(file_to_upload="unicodedata/unicode01.xml", filename="manifest.rdf") + # Access and check list of contents + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + fr = open('response.xml', 'w') + fr.write(rdfdata) + fr.close() + rdfgraph = Graph() + rdfgraph.parse('response.xml', format='xml') + doctext1 = None + f = codecs.open('testdata/unicodedata/unicode01.txt', 'r', 'utf-8') + doctext1 = f.read() + f.close() + self.assertEqual(len(rdfgraph),14,'Graph length %i' %len(rdfgraph)) + self.failUnless((subj,RDF.type,stype) in rdfgraph, 'Testing submission type: '+subj+", "+stype) + self.failUnless((subj,URIRef(dcterms+"created"),None) in rdfgraph, 'dcterms:created') + self.failUnless((subj,URIRef(dcterms+"identifier"),None) in rdfgraph, 'dcterms:identifier') + self.failUnless((subj,URIRef(dcterms+"mediator"),None) in rdfgraph, 'dcterms:mediator') + self.failUnless((subj,URIRef(dcterms+"rights"),None) in rdfgraph, 'dcterms:rights') + self.failUnless((subj,URIRef(dcterms+"license"),None) in rdfgraph, 'dcterms:license') + self.failUnless((subj,URIRef(dcterms+"publisher"),None) in rdfgraph, 'dcterms:piblisher') + self.failUnless((subj,URIRef(oxds+"isEmbargoed"),None) in rdfgraph, 'oxds:isEmbargoed') + self.failUnless((subj,URIRef(oxds+"embargoedUntil"),None) in rdfgraph, 'oxds:embargoedUntil') + self.failUnless((subj,URIRef(oxds+"currentVersion"),'1') in rdfgraph, 'oxds:currentVersion') + self.failUnless((subj,URIRef(dcterms+"modified"),None) in rdfgraph, 'dcterms:modified') + self.failUnless((subj,URIRef(dcterms+"title"),"General punctuation") in rdfgraph, 'dcterms:title') + #for s, p, o in rdfgraph.triples((None, RDF.value, None)): + # print s, p, type(o), '\n', o + #print '\n', '-'*50 + #print Literal(doctext1) + #print '\n', '-'*50 + self.failUnless((subj,RDF.value,Literal(doctext1)) in rdfgraph, 'rdf:value') + self.failUnless((subj,URIRef(dcterms+"source"),"http://www.madore.org/~david/misc/unitest/") in rdfgraph, 'dcterms:source') + + # PUT unicode file 02, check response + manidata = open("testdata/unicodedata/unicode02.xml").read() + (resp, respdata) = self.doHTTP_PUT(manidata, resource="datasets/TestSubmission/manifest.rdf", + expect_status=204, expect_reason="No Content", expect_type="text/plain") + #Access state information and check + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + fr = open('response.xml', 'w') + fr.write(rdfdata) + fr.close() + rdfgraph = Graph() + rdfgraph.parse('response.xml', format='xml') + doctext2 = None + f = codecs.open('testdata/unicodedata/unicode02.txt', 'r', 'utf-8') + doctext2 = f.read() + f.close() + self.assertEqual(len(rdfgraph),15,'Graph length %i' %len(rdfgraph)) + self.failUnless((subj,URIRef(oxds+"currentVersion"),'2') in rdfgraph, 'oxds:currentVersion') + self.failUnless((subj,URIRef(dcterms+"title"),"A table of (some) accents") in rdfgraph, 'dcterms:title') + self.failUnless((subj,RDF.value,Literal(doctext2)) in rdfgraph, 'rdf:value') + + # PUT unicode file 03, check response + manidata = open("testdata/unicodedata/unicode03.xml").read() + (resp, respdata) = self.doHTTP_PUT(manidata, resource="datasets/TestSubmission/manifest.rdf", + expect_status=204, expect_reason="No Content", expect_type="text/plain") + #Access state information and check + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + fr = open('response.xml', 'w') + fr.write(rdfdata) + fr.close() + rdfgraph = Graph() + rdfgraph.parse('response.xml', format='xml') + doctext3 = None + f = codecs.open('testdata/unicodedata/unicode03.txt', 'r', 'utf-8') + doctext3 = f.read() + f.close() + self.assertEqual(len(rdfgraph),16,'Graph length %i' %len(rdfgraph)) + self.failUnless((subj,URIRef(oxds+"currentVersion"),'3') in rdfgraph, 'oxds:currentVersion') + self.failUnless((subj,URIRef(dcterms+"title"),"Combining diacritics") in rdfgraph, 'dcterms:title') + self.failUnless((subj,RDF.value,Literal(doctext3)) in rdfgraph, 'rdf:value') + + # POST unicode file 04, check response + zipdata = self.updateSubmissionZipfile(file_to_upload="unicodedata/unicode04.xml", filename="manifest.rdf") + # Access and check list of contents + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + fr = open('response.xml', 'w') + fr.write(rdfdata) + fr.close() + rdfgraph = Graph() + rdfgraph.parse('response.xml', format='xml') + doctext4 = None + f = codecs.open('testdata/unicodedata/unicode04.txt', 'r', 'utf-8') + doctext4 = f.read() + f.close() + self.assertEqual(len(rdfgraph),17,'Graph length %i' %len(rdfgraph)) + self.failUnless((subj,URIRef(oxds+"currentVersion"),'4') in rdfgraph, 'oxds:currentVersion') + self.failUnless((subj,URIRef(dcterms+"title"),"Various symbols") in rdfgraph, 'dcterms:title') + self.failUnless((subj,RDF.value,Literal(doctext4)) in rdfgraph, 'rdf:value') + + # POST unicode file 05, check response + zipdata = self.updateSubmissionZipfile(file_to_upload="unicodedata/unicode05.xml", filename="manifest.rdf") + # Access and check list of contents + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + fr = open('response.xml', 'w') + fr.write(rdfdata) + fr.close() + rdfgraph = Graph() + rdfgraph.parse('response.xml', format='xml') + doctext5 = None + f = codecs.open('testdata/unicodedata/unicode05.txt', 'r', 'utf-8') + doctext5 = f.read() + f.close() + self.assertEqual(len(rdfgraph),18,'Graph length %i' %len(rdfgraph)) + self.failUnless((subj,URIRef(oxds+"currentVersion"),'5') in rdfgraph, 'oxds:currentVersion') + self.failUnless((subj,URIRef(dcterms+"title"),"Some verses in Russian") in rdfgraph, 'dcterms:title') + self.failUnless((subj,RDF.value,Literal(doctext5)) in rdfgraph, 'rdf:value') + + # PUT unicode file 06, check response + manidata = open("testdata/unicodedata/unicode06.xml").read() + (resp, respdata) = self.doHTTP_PUT(manidata, resource="datasets/TestSubmission/manifest.rdf", + expect_status=204, expect_reason="No Content", expect_type="text/plain") + #Access state information and check + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + fr = open('response.xml', 'w') + fr.write(rdfdata) + fr.close() + rdfgraph = Graph() + rdfgraph.parse('response.xml', format='xml') + doctext6 = None + f = codecs.open('testdata/unicodedata/unicode06.txt', 'r', 'utf-8') + doctext6 = f.read() + f.close() + self.assertEqual(len(rdfgraph),19,'Graph length %i' %len(rdfgraph)) + self.failUnless((subj,URIRef(oxds+"currentVersion"),'6') in rdfgraph, 'oxds:currentVersion') + self.failUnless((subj,URIRef(dcterms+"title"),"Some verses in ancient Greek") in rdfgraph, 'dcterms:title') + self.failUnless((subj,RDF.value,Literal(doctext6)) in rdfgraph, 'rdf:value') + + # POST unicode file 07, check response + zipdata = self.updateSubmissionZipfile(file_to_upload="unicodedata/unicode07.xml", filename="manifest.rdf") + # Access and check list of contents + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + fr = open('response.xml', 'w') + fr.write(rdfdata) + fr.close() + rdfgraph = Graph() + rdfgraph.parse('response.xml', format='xml') + doctext7 = None + f = codecs.open('testdata/unicodedata/unicode07.txt', 'r', 'utf-8') + doctext7 = f.read() + f.close() + self.assertEqual(len(rdfgraph),20,'Graph length %i' %len(rdfgraph)) + self.failUnless((subj,URIRef(oxds+"currentVersion"),'7') in rdfgraph, 'oxds:currentVersion') + self.failUnless((subj,URIRef(dcterms+"title"),"Some verses in Sanskrit") in rdfgraph, 'dcterms:title') + self.failUnless((subj,RDF.value,Literal(doctext7)) in rdfgraph, 'rdf:value') + + # PUT unicode file 08, check response + manidata = open("testdata/unicodedata/unicode08.xml").read() + (resp, respdata) = self.doHTTP_PUT(manidata, resource="datasets/TestSubmission/manifest.rdf", + expect_status=204, expect_reason="No Content", expect_type="text/plain") + #Access state information and check + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + fr = open('response.xml', 'w') + fr.write(rdfdata) + fr.close() + rdfgraph = Graph() + rdfgraph.parse('response.xml', format='xml') + doctext8 = None + f = codecs.open('testdata/unicodedata/unicode08.txt', 'r', 'utf-8') + doctext8= f.read() + f.close() + self.assertEqual(len(rdfgraph),21,'Graph length %i' %len(rdfgraph)) + self.failUnless((subj,URIRef(oxds+"currentVersion"),'8') in rdfgraph, 'oxds:currentVersion') + self.failUnless((subj,URIRef(dcterms+"title"),"Some Chinese") in rdfgraph, 'dcterms:title') + self.failUnless((subj,RDF.value,Literal(doctext8)) in rdfgraph, 'rdf:value') + + # POST unicode file 09, check response + zipdata = self.updateSubmissionZipfile(file_to_upload="unicodedata/unicode09.xml", filename="manifest.rdf") + # Access and check list of contents + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + fr = open('response.xml', 'w') + fr.write(rdfdata) + fr.close() + rdfgraph = Graph() + rdfgraph.parse('response.xml', format='xml') + doctext9 = None + f = codecs.open('testdata/unicodedata/unicode09.txt', 'r', 'utf-8') + doctext9 = f.read() + f.close() + self.assertEqual(len(rdfgraph),22,'Graph length %i' %len(rdfgraph)) + self.failUnless((subj,URIRef(oxds+"currentVersion"),'9') in rdfgraph, 'oxds:currentVersion') + self.failUnless((subj,URIRef(dcterms+"title"),"A Tamil name") in rdfgraph, 'dcterms:title') + self.failUnless((subj,RDF.value,Literal(doctext9)) in rdfgraph, 'rdf:value') + + # PUT unicode file 10, check response + manidata = open("testdata/unicodedata/unicode10.xml").read() + (resp, respdata) = self.doHTTP_PUT(manidata, resource="datasets/TestSubmission/manifest.rdf", + expect_status=204, expect_reason="No Content", expect_type="text/plain") + #Access state information and check + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + fr = open('response.xml', 'w') + fr.write(rdfdata) + fr.close() + rdfgraph = Graph() + rdfgraph.parse('response.xml', format='xml') + doctext10 = None + f = codecs.open('testdata/unicodedata/unicode10.txt', 'r', 'utf-8') + doctext10= f.read() + f.close() + self.assertEqual(len(rdfgraph),23,'Graph length %i' %len(rdfgraph)) + self.failUnless((subj,URIRef(oxds+"currentVersion"),'10') in rdfgraph, 'oxds:currentVersion') + self.failUnless((subj,URIRef(dcterms+"title"),"Some Arabic") in rdfgraph, 'dcterms:title') + self.failUnless((subj,RDF.value,Literal(doctext10)) in rdfgraph, 'rdf:value') + os.remove('response.xml') + + def testDeleteEmbargo(self): + """Delete embargo information - POST embargo_change to /silo_name/datasets/dataset_name""" + # Create a new dataset, check response + self.createSubmissionDataset() + #Access dataset and check content + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + self.assertEqual(len(rdfgraph),10,'Graph length %i' %len(rdfgraph)) + oxds = "http://vocab.ox.ac.uk/dataset/schema#" + dcterms = "http://purl.org/dc/terms/" + subj = URIRef(self.getManifestUri("datasets/TestSubmission")) + stype = URIRef(oxds+"DataSet") + self.failUnless((subj,RDF.type,stype) in rdfgraph, 'Testing submission type: '+subj+", "+stype) + self.failUnless((subj,URIRef(oxds+"isEmbargoed"),'True') in rdfgraph, 'oxds:isEmbargoed') + self.failUnless((subj,URIRef(oxds+"embargoedUntil"),None) in rdfgraph, 'oxds:embargoedUntil') + # Delete embargo, check response + fields = \ + [ ("embargoed", 'false') + ] + files =[] + (reqtype, reqdata) = SparqlQueryTestCase.encode_multipart_formdata(fields, files) + (resp,respdata) = self.doHTTP_POST( + reqdata, reqtype, + resource="datasets/TestSubmission", + expect_status=204, expect_reason="Updated") + #Access dataset and check content + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + self.assertEqual(len(rdfgraph),10,'Graph length %i' %len(rdfgraph)) + oxds = "http://vocab.ox.ac.uk/dataset/schema#" + dcterms = "http://purl.org/dc/terms/" + subj = URIRef(self.getManifestUri("datasets/TestSubmission")) + stype = URIRef(oxds+"DataSet") + self.failUnless((subj,RDF.type,stype) in rdfgraph, 'Testing submission type: '+subj+", "+stype) + self.failUnless((subj,URIRef(oxds+"isEmbargoed"),'False') in rdfgraph, 'oxds:isEmbargoed') + self.assertFalse((subj,URIRef(oxds+"embargoedUntil"),None) in rdfgraph, 'oxds:embargoedUntil') + self.failUnless((subj,URIRef(dcterms+"identifier"),None) in rdfgraph, 'dcterms:identifier') + self.failUnless((subj,URIRef(dcterms+"created"),None) in rdfgraph, 'dcterms:created') + self.failUnless((subj,URIRef(dcterms+"mediator"),None) in rdfgraph, 'dcterms:mediator') + self.failUnless((subj,URIRef(dcterms+"rights"),None) in rdfgraph, 'dcterms:rights') + self.failUnless((subj,URIRef(dcterms+"license"),None) in rdfgraph, 'dcterms:license') + self.failUnless((subj,URIRef(dcterms+"publisher"),None) in rdfgraph, 'dcterms:publisher') + self.failUnless((subj,URIRef(oxds+"currentVersion"),'1') in rdfgraph, 'oxds:currentVersion') + self.failUnless((subj,URIRef(dcterms+"modified"),None) in rdfgraph, 'dcterms:modified') + #Access state information and check + (resp, data) = self.doHTTP_GET( + resource="states/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/json") + state = data['state'] + self.assertEqual(state['metadata']['embargoed'], False, "Embargoed?") + self.assertEqual(state['metadata']['embargoed_until'], "", "Should have no date for embargoed_until") + + def testChangeEmbargo(self): + """Modify embargo information - POST embargo_change, embargo, embargo_until to /silo_name/datasets/dataset_name""" + # Create a new dataset, check response + self.createSubmissionDataset() + #Access dataset and check content + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + self.assertEqual(len(rdfgraph),10,'Graph length %i' %len(rdfgraph)) + oxds = "http://vocab.ox.ac.uk/dataset/schema#" + dcterms = "http://purl.org/dc/terms/" + subj = URIRef(self.getManifestUri("datasets/TestSubmission")) + stype = URIRef(oxds+"DataSet") + self.failUnless((subj,RDF.type,stype) in rdfgraph, 'Testing submission type: '+subj+", "+stype) + self.failUnless((subj,URIRef(oxds+"isEmbargoed"),'True') in rdfgraph, 'oxds:isEmbargoed') + self.assertTrue((subj,URIRef(oxds+"embargoedUntil"),None) in rdfgraph, 'oxds:embargoedUntil') + #Access state information and check + (resp, data) = self.doHTTP_GET( + resource="states/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/json") + state = data['state'] + d = (datetime.now() + relativedelta(years=+70)).isoformat() + d = d.split('T')[0] + self.assertEqual(state['metadata']['embargoed'], True, "Embargoed?") + self.assertTrue(d in state['metadata']['embargoed_until'], "embargoed_until %s?"%d) + #------------------------------------------------- + # Change embargo without embargo_until date - embargoed = True, check response + fields = \ + [ ("embargoed", 'true') + ] + files =[] + (reqtype, reqdata) = SparqlQueryTestCase.encode_multipart_formdata(fields, files) + (resp,respdata) = self.doHTTP_POST( + reqdata, reqtype, + resource="datasets/TestSubmission", + expect_status=204, expect_reason="Updated") + #Access dataset and check content + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + self.assertEqual(len(rdfgraph),10,'Graph length %i' %len(rdfgraph)) + oxds = "http://vocab.ox.ac.uk/dataset/schema#" + dcterms = "http://purl.org/dc/terms/" + subj = URIRef(self.getManifestUri("datasets/TestSubmission")) + stype = URIRef(oxds+"DataSet") + self.failUnless((subj,RDF.type,stype) in rdfgraph, 'Testing submission type: '+subj+", "+stype) + self.failUnless((subj,URIRef(oxds+"isEmbargoed"),'True') in rdfgraph, 'oxds:isEmbargoed') + self.assertFalse((subj,URIRef(oxds+"embargoedUntil"),None) in rdfgraph, 'oxds:embargoedUntil') + self.failUnless((subj,URIRef(dcterms+"identifier"),None) in rdfgraph, 'dcterms:identifier') + self.failUnless((subj,URIRef(dcterms+"created"),None) in rdfgraph, 'dcterms:created') + self.failUnless((subj,URIRef(dcterms+"mediator"),None) in rdfgraph, 'dcterms:mediator') + self.failUnless((subj,URIRef(dcterms+"rights"),None) in rdfgraph, 'dcterms:rights') + self.failUnless((subj,URIRef(dcterms+"license"),None) in rdfgraph, 'dcterms:license') + self.failUnless((subj,URIRef(dcterms+"publisher"),None) in rdfgraph, 'dcterms:publisher') + self.failUnless((subj,URIRef(oxds+"currentVersion"),'1') in rdfgraph, 'oxds:currentVersion') + self.failUnless((subj,URIRef(dcterms+"modified"),None) in rdfgraph, 'dcterms:modified') + #Access state information and check + (resp, data) = self.doHTTP_GET( + resource="states/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/json") + state = data['state'] + self.assertEqual(state['metadata']['embargoed'], True, "Embargoed?") + self.assertEqual(state['metadata']['embargoed_until'], '', "embargoed_until?") + #------------------------------------------------- + #Change embargo - embargoed = true, embargoed_until = True + d = datetime.now().isoformat() + fields = \ + [ ("embargoed", 'true') + ,("embargoed_until", 'true') + ] + files =[] + (reqtype, reqdata) = SparqlQueryTestCase.encode_multipart_formdata(fields, files) + (resp,respdata) = self.doHTTP_POST( + reqdata, reqtype, + resource="datasets/TestSubmission", + expect_status=204, expect_reason="Updated") + #Access dataset and check content + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + self.assertEqual(len(rdfgraph),11,'Graph length %i' %len(rdfgraph)) + oxds = "http://vocab.ox.ac.uk/dataset/schema#" + dcterms = "http://purl.org/dc/terms/" + subj = URIRef(self.getManifestUri("datasets/TestSubmission")) + stype = URIRef(oxds+"DataSet") + self.failUnless((subj,RDF.type,stype) in rdfgraph, 'Testing submission type: '+subj+", "+stype) + self.failUnless((subj,URIRef(oxds+"isEmbargoed"),'True') in rdfgraph, 'oxds:isEmbargoed') + self.failUnless((subj,URIRef(oxds+"embargoedUntil"),None) in rdfgraph, 'oxds:embargoedUntil') + self.failUnless((subj,URIRef(dcterms+"identifier"),None) in rdfgraph, 'dcterms:identifier') + self.failUnless((subj,URIRef(dcterms+"created"),None) in rdfgraph, 'dcterms:created') + self.failUnless((subj,URIRef(dcterms+"mediator"),None) in rdfgraph, 'dcterms:mediator') + self.failUnless((subj,URIRef(dcterms+"rights"),None) in rdfgraph, 'dcterms:rights') + self.failUnless((subj,URIRef(dcterms+"license"),None) in rdfgraph, 'dcterms:license') + self.failUnless((subj,URIRef(dcterms+"publisher"),None) in rdfgraph, 'dcterms:publisher') + self.failUnless((subj,URIRef(oxds+"currentVersion"),'2') in rdfgraph, 'oxds:currentVersion') + self.failUnless((subj,URIRef(dcterms+"modified"),None) in rdfgraph, 'dcterms:modified') + #Access state information and check + (resp, data) = self.doHTTP_GET( + resource="states/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/json") + state = data['state'] + d = (datetime.now() + relativedelta(years=+70)).isoformat() + d = d.split('T')[0] + self.assertEqual(state['metadata']['embargoed'], True, "Embargoed?") + self.assertTrue(d in state['metadata']['embargoed_until'], "embargoed_until %s?"%d) + #------------------------------------------------- + #Change embargo - embargoed = true, embargoed_until = datetime + d = datetime.now() + delta = timedelta(days=365*4) + d2 = d + delta + d2 = d2.isoformat() + fields = \ + [ ("embargoed", 'true') + ,("embargoed_until", d2) + ] + files =[] + (reqtype, reqdata) = SparqlQueryTestCase.encode_multipart_formdata(fields, files) + (resp,respdata) = self.doHTTP_POST( + reqdata, reqtype, + resource="datasets/TestSubmission", + expect_status=204, expect_reason="Updated") + #Access dataset and check content + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + self.assertEqual(len(rdfgraph),11,'Graph length %i' %len(rdfgraph)) + oxds = "http://vocab.ox.ac.uk/dataset/schema#" + dcterms = "http://purl.org/dc/terms/" + subj = URIRef(self.getManifestUri("datasets/TestSubmission")) + stype = URIRef(oxds+"DataSet") + self.failUnless((subj,RDF.type,stype) in rdfgraph, 'Testing submission type: '+subj+", "+stype) + self.failUnless((subj,URIRef(oxds+"isEmbargoed"),'True') in rdfgraph, 'oxds:isEmbargoed') + self.failUnless((subj,URIRef(oxds+"embargoedUntil"),d2) in rdfgraph, 'oxds:embargoedUntil') + self.failUnless((subj,URIRef(dcterms+"identifier"),None) in rdfgraph, 'dcterms:identifier') + self.failUnless((subj,URIRef(dcterms+"created"),None) in rdfgraph, 'dcterms:created') + self.failUnless((subj,URIRef(dcterms+"mediator"),None) in rdfgraph, 'dcterms:mediator') + self.failUnless((subj,URIRef(dcterms+"rights"),None) in rdfgraph, 'dcterms:rights') + self.failUnless((subj,URIRef(dcterms+"license"),None) in rdfgraph, 'dcterms:license') + self.failUnless((subj,URIRef(dcterms+"publisher"),None) in rdfgraph, 'dcterms:publisher') + self.failUnless((subj,URIRef(oxds+"currentVersion"),'3') in rdfgraph, 'oxds:currentVersion') + self.failUnless((subj,URIRef(dcterms+"modified"),None) in rdfgraph, 'dcterms:modified') + #Access state information and check + (resp, data) = self.doHTTP_GET( + resource="states/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/json") + state = data['state'] + self.assertEqual(state['metadata']['embargoed'], True, "Embargoed?") + self.assertEqual(state['metadata']['embargoed_until'], d2, "embargoed_until?") + #------------------------------------------------- + #Change embargo - embargoed = true, embargoed_until = datetime + d2 = '09 August 2013' + fields = \ + [ ("embargoed", 'true') + ,("embargoed_until", d2) + ] + files =[] + (reqtype, reqdata) = SparqlQueryTestCase.encode_multipart_formdata(fields, files) + (resp,respdata) = self.doHTTP_POST( + reqdata, reqtype, + resource="datasets/TestSubmission", + expect_status=204, expect_reason="Updated") + #Access dataset and check content + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + self.assertEqual(len(rdfgraph),11,'Graph length %i' %len(rdfgraph)) + oxds = "http://vocab.ox.ac.uk/dataset/schema#" + dcterms = "http://purl.org/dc/terms/" + subj = URIRef(self.getManifestUri("datasets/TestSubmission")) + stype = URIRef(oxds+"DataSet") + self.failUnless((subj,RDF.type,stype) in rdfgraph, 'Testing submission type: '+subj+", "+stype) + self.failUnless((subj,URIRef(oxds+"isEmbargoed"),'True') in rdfgraph, 'oxds:isEmbargoed') + self.failUnless((subj,URIRef(oxds+"embargoedUntil"),None) in rdfgraph, 'oxds:embargoedUntil') + self.failUnless((subj,URIRef(dcterms+"identifier"),None) in rdfgraph, 'dcterms:identifier') + self.failUnless((subj,URIRef(dcterms+"created"),None) in rdfgraph, 'dcterms:created') + self.failUnless((subj,URIRef(dcterms+"mediator"),None) in rdfgraph, 'dcterms:mediator') + self.failUnless((subj,URIRef(dcterms+"rights"),None) in rdfgraph, 'dcterms:rights') + self.failUnless((subj,URIRef(dcterms+"license"),None) in rdfgraph, 'dcterms:license') + self.failUnless((subj,URIRef(dcterms+"publisher"),None) in rdfgraph, 'dcterms:publisher') + self.failUnless((subj,URIRef(oxds+"currentVersion"),'4') in rdfgraph, 'oxds:currentVersion') + self.failUnless((subj,URIRef(dcterms+"modified"),None) in rdfgraph, 'dcterms:modified') + #Access state information and check + (resp, data) = self.doHTTP_GET( + resource="states/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/json") + state = data['state'] + self.assertEqual(state['metadata']['embargoed'], True, "Embargoed?") + self.assertTrue('2013-08-09' in state['metadata']['embargoed_until'], "embargoed_until 2013-08-09?") + #------------------------------------------------- + # Change embargo - embargoed_until = true and check response + fields = \ + [ ("embargoed_until", 'true') + ] + files =[] + (reqtype, reqdata) = SparqlQueryTestCase.encode_multipart_formdata(fields, files) + (resp,respdata) = self.doHTTP_POST( + reqdata, reqtype, + resource="datasets/TestSubmission", + expect_status=400, expect_reason="Bad request") + #Access dataset and check content + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + self.assertEqual(len(rdfgraph),11,'Graph length %i' %len(rdfgraph)) + oxds = "http://vocab.ox.ac.uk/dataset/schema#" + dcterms = "http://purl.org/dc/terms/" + subj = URIRef(self.getManifestUri("datasets/TestSubmission")) + stype = URIRef(oxds+"DataSet") + self.failUnless((subj,RDF.type,stype) in rdfgraph, 'Testing submission type: '+subj+", "+stype) + self.failUnless((subj,URIRef(oxds+"isEmbargoed"),'True') in rdfgraph, 'oxds:isEmbargoed') + self.failUnless((subj,URIRef(oxds+"embargoedUntil"),None) in rdfgraph, 'oxds:embargoedUntil') + self.failUnless((subj,URIRef(dcterms+"identifier"),None) in rdfgraph, 'dcterms:identifier') + self.failUnless((subj,URIRef(dcterms+"created"),None) in rdfgraph, 'dcterms:created') + self.failUnless((subj,URIRef(dcterms+"mediator"),None) in rdfgraph, 'dcterms:mediator') + self.failUnless((subj,URIRef(dcterms+"rights"),None) in rdfgraph, 'dcterms:rights') + self.failUnless((subj,URIRef(dcterms+"license"),None) in rdfgraph, 'dcterms:license') + self.failUnless((subj,URIRef(dcterms+"publisher"),None) in rdfgraph, 'dcterms:publisher') + self.failUnless((subj,URIRef(oxds+"currentVersion"),'4') in rdfgraph, 'oxds:currentVersion') + self.failUnless((subj,URIRef(dcterms+"modified"),None) in rdfgraph, 'dcterms:modified') + #Access state information and check + (resp, data) = self.doHTTP_GET( + resource="states/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/json") + state = data['state'] + self.assertEqual(state['metadata']['embargoed'], True, "Embargoed?") + self.assertTrue('2013-08-09' in state['metadata']['embargoed_until'], "embargoed_until 2013-08-09?") + #------------------------------------------------- + # Change embargo - embargoed_until = date and check response + d5 = datetime.now() + delta = timedelta(days=3) + d5 = d5 + delta + d5 = d5.isoformat() + fields = \ + [ ("embargoed_until", d5) + ] + files =[] + (reqtype, reqdata) = SparqlQueryTestCase.encode_multipart_formdata(fields, files) + (resp,respdata) = self.doHTTP_POST( + reqdata, reqtype, + resource="datasets/TestSubmission", + expect_status=400, expect_reason="Bad request") + #Access dataset and check content + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + self.assertEqual(len(rdfgraph),11,'Graph length %i' %len(rdfgraph)) + oxds = "http://vocab.ox.ac.uk/dataset/schema#" + dcterms = "http://purl.org/dc/terms/" + subj = URIRef(self.getManifestUri("datasets/TestSubmission")) + stype = URIRef(oxds+"DataSet") + self.failUnless((subj,RDF.type,stype) in rdfgraph, 'Testing submission type: '+subj+", "+stype) + self.failUnless((subj,URIRef(oxds+"isEmbargoed"),'True') in rdfgraph, 'oxds:isEmbargoed') + self.failUnless((subj,URIRef(oxds+"embargoedUntil"),None) in rdfgraph, 'oxds:embargoedUntil') + self.failUnless((subj,URIRef(dcterms+"identifier"),None) in rdfgraph, 'dcterms:identifier') + self.failUnless((subj,URIRef(dcterms+"created"),None) in rdfgraph, 'dcterms:created') + self.failUnless((subj,URIRef(dcterms+"mediator"),None) in rdfgraph, 'dcterms:mediator') + self.failUnless((subj,URIRef(dcterms+"rights"),None) in rdfgraph, 'dcterms:rights') + self.failUnless((subj,URIRef(dcterms+"license"),None) in rdfgraph, 'dcterms:license') + self.failUnless((subj,URIRef(dcterms+"publisher"),None) in rdfgraph, 'dcterms:publisher') + self.failUnless((subj,URIRef(oxds+"currentVersion"),'4') in rdfgraph, 'oxds:currentVersion') + self.failUnless((subj,URIRef(dcterms+"modified"),None) in rdfgraph, 'dcterms:modified') + #Access state information and check + (resp, data) = self.doHTTP_GET( + resource="states/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/json") + state = data['state'] + self.assertEqual(state['metadata']['embargoed'], True, "Embargoed?") + self.assertTrue('2013-08-09' in state['metadata']['embargoed_until'], "embargoed_until 2013-08-09?") + #------------------------------------------------- + #Delete embargo + fields = \ + [ ("embargoed", 'false') + ] + files =[] + (reqtype, reqdata) = SparqlQueryTestCase.encode_multipart_formdata(fields, files) + (resp,respdata) = self.doHTTP_POST( + reqdata, reqtype, + resource="datasets/TestSubmission", + expect_status=204, expect_reason="Updated") + #Access dataset and check content + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + self.assertEqual(len(rdfgraph),10,'Graph length %i' %len(rdfgraph)) + oxds = "http://vocab.ox.ac.uk/dataset/schema#" + dcterms = "http://purl.org/dc/terms/" + subj = URIRef(self.getManifestUri("datasets/TestSubmission")) + stype = URIRef(oxds+"DataSet") + self.failUnless((subj,RDF.type,stype) in rdfgraph, 'Testing submission type: '+subj+", "+stype) + self.failUnless((subj,URIRef(oxds+"isEmbargoed"),'False') in rdfgraph, 'oxds:isEmbargoed') + self.assertFalse((subj,URIRef(oxds+"embargoedUntil"),None) in rdfgraph, 'oxds:embargoedUntil') + self.failUnless((subj,URIRef(dcterms+"identifier"),None) in rdfgraph, 'dcterms:identifier') + self.failUnless((subj,URIRef(dcterms+"created"),None) in rdfgraph, 'dcterms:created') + self.failUnless((subj,URIRef(dcterms+"mediator"),None) in rdfgraph, 'dcterms:mediator') + self.failUnless((subj,URIRef(dcterms+"rights"),None) in rdfgraph, 'dcterms:rights') + self.failUnless((subj,URIRef(dcterms+"license"),None) in rdfgraph, 'dcterms:license') + self.failUnless((subj,URIRef(dcterms+"publisher"),None) in rdfgraph, 'dcterms:publisher') + self.failUnless((subj,URIRef(oxds+"currentVersion"),'5') in rdfgraph, 'oxds:currentVersion') + self.failUnless((subj,URIRef(dcterms+"modified"),None) in rdfgraph, 'dcterms:modified') + #Access state information and check + (resp, data) = self.doHTTP_GET( + resource="states/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/json") + state = data['state'] + self.assertEqual(state['metadata']['embargoed'], False, "Embargoed?") + self.assertEqual(state['metadata']['embargoed_until'], '', "Embargoed until?") + #------------------------------------------------- + #Delete embargo + fields = \ + [ ("embargoed", 'false') + ,("embargoed_until", 'true') + ] + files =[] + (reqtype, reqdata) = SparqlQueryTestCase.encode_multipart_formdata(fields, files) + (resp,respdata) = self.doHTTP_POST( + reqdata, reqtype, + resource="datasets/TestSubmission", + expect_status=204, expect_reason="Updated") + #Access dataset and check content + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + self.assertEqual(len(rdfgraph),10,'Graph length %i' %len(rdfgraph)) + oxds = "http://vocab.ox.ac.uk/dataset/schema#" + dcterms = "http://purl.org/dc/terms/" + subj = URIRef(self.getManifestUri("datasets/TestSubmission")) + stype = URIRef(oxds+"DataSet") + self.failUnless((subj,RDF.type,stype) in rdfgraph, 'Testing submission type: '+subj+", "+stype) + self.failUnless((subj,URIRef(oxds+"isEmbargoed"),'False') in rdfgraph, 'oxds:isEmbargoed') + self.assertFalse((subj,URIRef(oxds+"embargoedUntil"),None) in rdfgraph, 'oxds:embargoedUntil') + self.failUnless((subj,URIRef(dcterms+"identifier"),None) in rdfgraph, 'dcterms:identifier') + self.failUnless((subj,URIRef(dcterms+"created"),None) in rdfgraph, 'dcterms:created') + self.failUnless((subj,URIRef(dcterms+"mediator"),None) in rdfgraph, 'dcterms:mediator') + self.failUnless((subj,URIRef(dcterms+"rights"),None) in rdfgraph, 'dcterms:rights') + self.failUnless((subj,URIRef(dcterms+"license"),None) in rdfgraph, 'dcterms:license') + self.failUnless((subj,URIRef(dcterms+"publisher"),None) in rdfgraph, 'dcterms:publisher') + self.failUnless((subj,URIRef(oxds+"currentVersion"),'6') in rdfgraph, 'oxds:currentVersion') + self.failUnless((subj,URIRef(dcterms+"modified"),None) in rdfgraph, 'dcterms:modified') + #Access state information and check + (resp, data) = self.doHTTP_GET( + resource="states/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/json") + state = data['state'] + self.assertEqual(state['metadata']['embargoed'], False, "Embargoed?") + self.assertEqual(state['metadata']['embargoed_until'], '', "Embargoed until?") + #------------------------------------------------- + #Delete embargo + d = datetime.now() + delta = timedelta(days=4) + d3 = d + delta + d3 = d3.isoformat() + fields = \ + [ ("embargoed", 'false') + ,("embargoed_until", d3) + ] + files =[] + (reqtype, reqdata) = SparqlQueryTestCase.encode_multipart_formdata(fields, files) + (resp,respdata) = self.doHTTP_POST( + reqdata, reqtype, + resource="datasets/TestSubmission", + expect_status=204, expect_reason="Updated") + #Access dataset and check content + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + self.assertEqual(len(rdfgraph),10,'Graph length %i' %len(rdfgraph)) + oxds = "http://vocab.ox.ac.uk/dataset/schema#" + dcterms = "http://purl.org/dc/terms/" + subj = URIRef(self.getManifestUri("datasets/TestSubmission")) + stype = URIRef(oxds+"DataSet") + self.failUnless((subj,RDF.type,stype) in rdfgraph, 'Testing submission type: '+subj+", "+stype) + self.failUnless((subj,URIRef(oxds+"isEmbargoed"),'False') in rdfgraph, 'oxds:isEmbargoed') + self.assertFalse((subj,URIRef(oxds+"embargoedUntil"),None) in rdfgraph, 'oxds:embargoedUntil') + self.failUnless((subj,URIRef(dcterms+"identifier"),None) in rdfgraph, 'dcterms:identifier') + self.failUnless((subj,URIRef(dcterms+"created"),None) in rdfgraph, 'dcterms:created') + self.failUnless((subj,URIRef(dcterms+"mediator"),None) in rdfgraph, 'dcterms:mediator') + self.failUnless((subj,URIRef(dcterms+"rights"),None) in rdfgraph, 'dcterms:rights') + self.failUnless((subj,URIRef(dcterms+"license"),None) in rdfgraph, 'dcterms:license') + self.failUnless((subj,URIRef(dcterms+"publisher"),None) in rdfgraph, 'dcterms:publisher') + self.failUnless((subj,URIRef(oxds+"currentVersion"),'7') in rdfgraph, 'oxds:currentVersion') + self.failUnless((subj,URIRef(dcterms+"modified"),None) in rdfgraph, 'dcterms:modified') + #Access state information and check + (resp, data) = self.doHTTP_GET( + resource="states/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/json") + state = data['state'] + self.assertEqual(state['metadata']['embargoed'], False, "Embargoed?") + self.assertEqual(state['metadata']['embargoed_until'], '', "Embargoed until?") + #------------------------------------------------- + #Change embargo - embargoed = true, embargoed_until = '09 Aug 2013' + d2 = '09 Aug 2013' + fields = \ + [ ("embargoed", 'true') + ,("embargoed_until", d2) + ] + files =[] + (reqtype, reqdata) = SparqlQueryTestCase.encode_multipart_formdata(fields, files) + (resp,respdata) = self.doHTTP_POST( + reqdata, reqtype, + resource="datasets/TestSubmission", + expect_status=204, expect_reason="Updated") + #Access dataset and check content + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + self.assertEqual(len(rdfgraph),11,'Graph length %i' %len(rdfgraph)) + oxds = "http://vocab.ox.ac.uk/dataset/schema#" + dcterms = "http://purl.org/dc/terms/" + subj = URIRef(self.getManifestUri("datasets/TestSubmission")) + stype = URIRef(oxds+"DataSet") + self.failUnless((subj,RDF.type,stype) in rdfgraph, 'Testing submission type: '+subj+", "+stype) + self.failUnless((subj,URIRef(oxds+"isEmbargoed"),'True') in rdfgraph, 'oxds:isEmbargoed') + self.failUnless((subj,URIRef(oxds+"embargoedUntil"),None) in rdfgraph, 'oxds:embargoedUntil') + self.failUnless((subj,URIRef(dcterms+"identifier"),None) in rdfgraph, 'dcterms:identifier') + self.failUnless((subj,URIRef(dcterms+"created"),None) in rdfgraph, 'dcterms:created') + self.failUnless((subj,URIRef(dcterms+"mediator"),None) in rdfgraph, 'dcterms:mediator') + self.failUnless((subj,URIRef(dcterms+"rights"),None) in rdfgraph, 'dcterms:rights') + self.failUnless((subj,URIRef(dcterms+"license"),None) in rdfgraph, 'dcterms:license') + self.failUnless((subj,URIRef(dcterms+"publisher"),None) in rdfgraph, 'dcterms:publisher') + self.failUnless((subj,URIRef(oxds+"currentVersion"),'8') in rdfgraph, 'oxds:currentVersion') + self.failUnless((subj,URIRef(dcterms+"modified"),None) in rdfgraph, 'dcterms:modified') + #Access state information and check + (resp, data) = self.doHTTP_GET( + resource="states/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/json") + state = data['state'] + self.assertEqual(state['metadata']['embargoed'], True, "Embargoed?") + self.assertTrue('2013-08-09' in state['metadata']['embargoed_until'], "embargoed_until 2013-08-09?") + #------------------------------------------------- + #Change embargo - embargoed = true, embargoed_until = '09 08 2015' + d2 = '09 08 2015' + fields = \ + [ ("embargoed", 'true') + ,("embargoed_until", d2) + ] + files =[] + (reqtype, reqdata) = SparqlQueryTestCase.encode_multipart_formdata(fields, files) + (resp,respdata) = self.doHTTP_POST( + reqdata, reqtype, + resource="datasets/TestSubmission", + expect_status=204, expect_reason="Updated") + #Access dataset and check content + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + self.assertEqual(len(rdfgraph),11,'Graph length %i' %len(rdfgraph)) + oxds = "http://vocab.ox.ac.uk/dataset/schema#" + dcterms = "http://purl.org/dc/terms/" + subj = URIRef(self.getManifestUri("datasets/TestSubmission")) + stype = URIRef(oxds+"DataSet") + self.failUnless((subj,RDF.type,stype) in rdfgraph, 'Testing submission type: '+subj+", "+stype) + self.failUnless((subj,URIRef(oxds+"isEmbargoed"),'True') in rdfgraph, 'oxds:isEmbargoed') + self.failUnless((subj,URIRef(oxds+"embargoedUntil"),None) in rdfgraph, 'oxds:embargoedUntil') + self.failUnless((subj,URIRef(dcterms+"identifier"),None) in rdfgraph, 'dcterms:identifier') + self.failUnless((subj,URIRef(dcterms+"created"),None) in rdfgraph, 'dcterms:created') + self.failUnless((subj,URIRef(dcterms+"mediator"),None) in rdfgraph, 'dcterms:mediator') + self.failUnless((subj,URIRef(dcterms+"rights"),None) in rdfgraph, 'dcterms:rights') + self.failUnless((subj,URIRef(dcterms+"license"),None) in rdfgraph, 'dcterms:license') + self.failUnless((subj,URIRef(dcterms+"publisher"),None) in rdfgraph, 'dcterms:publisher') + self.failUnless((subj,URIRef(oxds+"currentVersion"),'9') in rdfgraph, 'oxds:currentVersion') + self.failUnless((subj,URIRef(dcterms+"modified"),None) in rdfgraph, 'dcterms:modified') + #Access state information and check + (resp, data) = self.doHTTP_GET( + resource="states/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/json") + state = data['state'] + self.assertEqual(state['metadata']['embargoed'], True, "Embargoed?") + self.assertTrue('2015-08-09' in state['metadata']['embargoed_until'], "embargoed_until 2015-08-09?") + #------------------------------------------------- + #Change embargo - embargoed = true, embargoed_until = '09-08-16' + d2 = '09-08-16' + fields = \ + [ ("embargoed", 'true') + ,("embargoed_until", d2) + ] + files =[] + (reqtype, reqdata) = SparqlQueryTestCase.encode_multipart_formdata(fields, files) + (resp,respdata) = self.doHTTP_POST( + reqdata, reqtype, + resource="datasets/TestSubmission", + expect_status=204, expect_reason="Updated") + #Access dataset and check content + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + self.assertEqual(len(rdfgraph),11,'Graph length %i' %len(rdfgraph)) + oxds = "http://vocab.ox.ac.uk/dataset/schema#" + dcterms = "http://purl.org/dc/terms/" + subj = URIRef(self.getManifestUri("datasets/TestSubmission")) + stype = URIRef(oxds+"DataSet") + self.failUnless((subj,RDF.type,stype) in rdfgraph, 'Testing submission type: '+subj+", "+stype) + self.failUnless((subj,URIRef(oxds+"isEmbargoed"),'True') in rdfgraph, 'oxds:isEmbargoed') + self.failUnless((subj,URIRef(oxds+"embargoedUntil"),None) in rdfgraph, 'oxds:embargoedUntil') + self.failUnless((subj,URIRef(dcterms+"identifier"),None) in rdfgraph, 'dcterms:identifier') + self.failUnless((subj,URIRef(dcterms+"created"),None) in rdfgraph, 'dcterms:created') + self.failUnless((subj,URIRef(dcterms+"mediator"),None) in rdfgraph, 'dcterms:mediator') + self.failUnless((subj,URIRef(dcterms+"license"),None) in rdfgraph, 'dcterms:license') + self.failUnless((subj,URIRef(dcterms+"rights"),None) in rdfgraph, 'dcterms:rights') + self.failUnless((subj,URIRef(dcterms+"license"),None) in rdfgraph, 'dcterms:license') + self.failUnless((subj,URIRef(dcterms+"publisher"),None) in rdfgraph, 'dcterms:publisher') + self.failUnless((subj,URIRef(oxds+"currentVersion"),'10') in rdfgraph, 'oxds:currentVersion') + self.failUnless((subj,URIRef(dcterms+"modified"),None) in rdfgraph, 'dcterms:modified') + #Access state information and check + (resp, data) = self.doHTTP_GET( + resource="states/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/json") + state = data['state'] + self.assertEqual(state['metadata']['embargoed'], True, "Embargoed?") + self.assertTrue('2016-08-09' in state['metadata']['embargoed_until'], "embargoed_until 2016-08-09?") + #------------------------------------------------- + #Change embargo - embargoed = true, embargoed_until = '09/08/17' + d2 = '09/08/17' + fields = \ + [ ("embargoed", 'true') + ,("embargoed_until", d2) + ] + files =[] + (reqtype, reqdata) = SparqlQueryTestCase.encode_multipart_formdata(fields, files) + (resp,respdata) = self.doHTTP_POST( + reqdata, reqtype, + resource="datasets/TestSubmission", + expect_status=204, expect_reason="Updated") + #Access dataset and check content + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + self.assertEqual(len(rdfgraph),11,'Graph length %i' %len(rdfgraph)) + oxds = "http://vocab.ox.ac.uk/dataset/schema#" + dcterms = "http://purl.org/dc/terms/" + subj = URIRef(self.getManifestUri("datasets/TestSubmission")) + stype = URIRef(oxds+"DataSet") + self.failUnless((subj,RDF.type,stype) in rdfgraph, 'Testing submission type: '+subj+", "+stype) + self.failUnless((subj,URIRef(oxds+"isEmbargoed"),'True') in rdfgraph, 'oxds:isEmbargoed') + self.failUnless((subj,URIRef(oxds+"embargoedUntil"),None) in rdfgraph, 'oxds:embargoedUntil') + self.failUnless((subj,URIRef(dcterms+"identifier"),None) in rdfgraph, 'dcterms:identifier') + self.failUnless((subj,URIRef(dcterms+"created"),None) in rdfgraph, 'dcterms:created') + self.failUnless((subj,URIRef(dcterms+"mediator"),None) in rdfgraph, 'dcterms:mediator') + self.failUnless((subj,URIRef(dcterms+"rights"),None) in rdfgraph, 'dcterms:rights') + self.failUnless((subj,URIRef(dcterms+"license"),None) in rdfgraph, 'dcterms:license') + self.failUnless((subj,URIRef(dcterms+"publisher"),None) in rdfgraph, 'dcterms:publisher') + self.failUnless((subj,URIRef(oxds+"currentVersion"),'11') in rdfgraph, 'oxds:currentVersion') + self.failUnless((subj,URIRef(dcterms+"modified"),None) in rdfgraph, 'dcterms:modified') + #Access state information and check + (resp, data) = self.doHTTP_GET( + resource="states/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/json") + state = data['state'] + self.assertEqual(state['metadata']['embargoed'], True, "Embargoed?") + self.assertTrue('2017-08-09' in state['metadata']['embargoed_until'], "embargoed_until 2017-08-09?") + #------------------------------------------------- + #Change embargo - embargoed = true, embargoed_until = 'Aug 09, 2018' + d2 = 'Aug 09, 2018' + fields = \ + [ ("embargoed", 'true') + ,("embargoed_until", d2) + ] + files =[] + (reqtype, reqdata) = SparqlQueryTestCase.encode_multipart_formdata(fields, files) + (resp,respdata) = self.doHTTP_POST( + reqdata, reqtype, + resource="datasets/TestSubmission", + expect_status=204, expect_reason="Updated") + #Access dataset and check content + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + self.assertEqual(len(rdfgraph),11,'Graph length %i' %len(rdfgraph)) + oxds = "http://vocab.ox.ac.uk/dataset/schema#" + dcterms = "http://purl.org/dc/terms/" + subj = URIRef(self.getManifestUri("datasets/TestSubmission")) + stype = URIRef(oxds+"DataSet") + self.failUnless((subj,RDF.type,stype) in rdfgraph, 'Testing submission type: '+subj+", "+stype) + self.failUnless((subj,URIRef(oxds+"isEmbargoed"),'True') in rdfgraph, 'oxds:isEmbargoed') + self.failUnless((subj,URIRef(oxds+"embargoedUntil"),None) in rdfgraph, 'oxds:embargoedUntil') + self.failUnless((subj,URIRef(dcterms+"identifier"),None) in rdfgraph, 'dcterms:identifier') + self.failUnless((subj,URIRef(dcterms+"created"),None) in rdfgraph, 'dcterms:created') + self.failUnless((subj,URIRef(dcterms+"mediator"),None) in rdfgraph, 'dcterms:mediator') + self.failUnless((subj,URIRef(dcterms+"rights"),None) in rdfgraph, 'dcterms:rights') + self.failUnless((subj,URIRef(dcterms+"license"),None) in rdfgraph, 'dcterms:license') + self.failUnless((subj,URIRef(dcterms+"publisher"),None) in rdfgraph, 'dcterms:publisher') + self.failUnless((subj,URIRef(oxds+"currentVersion"),'12') in rdfgraph, 'oxds:currentVersion') + self.failUnless((subj,URIRef(dcterms+"modified"),None) in rdfgraph, 'dcterms:modified') + #Access state information and check + (resp, data) = self.doHTTP_GET( + resource="states/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/json") + state = data['state'] + self.assertEqual(state['metadata']['embargoed'], True, "Embargoed?") + self.assertTrue('2018-08-09' in state['metadata']['embargoed_until'], "embargoed_until 2018-08-09?") + #------------------------------------------------- + + def testFileUnpack(self): + """Unpack zip file within dataset - POST zip filename to /silo_name/items/dataset_name""" + # Create a new dataset, check response + self.createSubmissionDataset() + # Upload zip file, check response + zipdata = self.uploadSubmissionZipfile() + # Unpack ZIP file into a new dataset, check response + fields = \ + [ ("filename", "testdir.zip") + ] + files = [] + (reqtype, reqdata) = SparqlQueryTestCase.encode_multipart_formdata(fields, files) + (resp,respdata) = self.doHTTP_POST( + reqdata, reqtype, + resource="items/TestSubmission", + expect_status=204, expect_reason="No Content") + # Access new dataset, check response + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + subj = URIRef(self.getManifestUri("datasets/TestSubmission")) + dcterms = "http://purl.org/dc/terms/" + ore = "http://www.openarchives.org/ore/terms/" + oxds = "http://vocab.ox.ac.uk/dataset/schema#" + stype = URIRef("http://vocab.ox.ac.uk/dataset/schema#Grouping") + base = self.getManifestUri("datasets/TestSubmission/") + self.assertEqual(len(rdfgraph),17,'Graph length %i' %len(rdfgraph)) + self.failUnless((subj,RDF.type,stype) in rdfgraph, 'Testing submission type: '+subj+", "+stype) + self.failUnless((subj,URIRef(dcterms+"identifier"),None) in rdfgraph, 'dcterms:identifier') + self.failUnless((subj,URIRef(dcterms+"mediator"),None) in rdfgraph, 'dcterms:mediator') + self.failUnless((subj,URIRef(dcterms+"rights"),None) in rdfgraph, 'dcterms:rights') + self.failUnless((subj,URIRef(dcterms+"license"),None) in rdfgraph, 'dcterms:locense') + self.failUnless((subj,URIRef(dcterms+"publisher"),None) in rdfgraph, 'dcterms:publisher') + self.failUnless((subj,URIRef(oxds+"isEmbargoed"),None) in rdfgraph, 'oxds:isEmbargoed') + self.failUnless((subj,URIRef(oxds+"embargoedUntil"),None) in rdfgraph, 'oxds:embargoedUntil') + self.failUnless((subj,URIRef(dcterms+"created"),None) in rdfgraph, 'dcterms:created') + self.failUnless((subj,URIRef(dcterms+"modified"),None) in rdfgraph, 'dcterms:modified') + self.failUnless((subj,URIRef(dcterms+"isVersionOf"),None) in rdfgraph, 'dcterms:isVersionOf') + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"directory")) in rdfgraph) + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"directory/file1.a")) in rdfgraph) + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"directory/file1.b")) in rdfgraph) + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"directory/file2.a")) in rdfgraph) + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"test-csv.csv")) in rdfgraph) + self.failUnless((subj,URIRef(oxds+"currentVersion"),"2") in rdfgraph, 'oxds:currentVersion') + # Access and check content of a resource + (resp, filedata) = self.doHTTP_GET( + resource="datasets/TestSubmission/directory/file1.b", + expect_status=200, expect_reason="OK", expect_type="text/plain") + checkdata = open("testdata/testdir/directory/file1.b").read() + self.assertEqual(filedata, checkdata, "Difference between local and remote data!") + #Access state information of TestSubmission-testdir + (resp, data) = self.doHTTP_GET( + resource="states/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/json") + state = data['state'] + parts = data['parts'] + self.assertEqual(len(state.keys()), 12, "States") + self.assertEqual(state['item_id'], "TestSubmission", "Submission item identifier") + self.assertEqual(len(state['versions']), 3, "Two versions") + self.assertEqual(state['versions'][0], '0', "Version 0") + self.assertEqual(state['versions'][1], '1', "Version 1") + self.assertEqual(state['versions'][2], '2', "Version 2") + self.assertEqual(state['currentversion'], '2', "Current version == 2") + self.assertEqual(state['rdffileformat'], 'xml', "RDF file type") + self.assertEqual(state['rdffilename'], 'manifest.rdf', "RDF file name") + self.assertEqual(len(state['files']['0']), 1, "List should contain just manifest.rdf") + self.assertEqual(len(state['files']['1']), 2, "List should contain manifest.rdf and testdir.zip") + self.assertEqual(len(state['files']['2']), 3, "List should contain manifest.rdf, testdir and test-csv.csv") + self.assertEqual(len(state['metadata_files']['0']), 0, "metadata_files of version 0") + self.assertEqual(len(state['metadata_files']['1']), 0, "metadata_files of version 1") + self.assertEqual(len(state['metadata_files']['2']), 0, "metadata_files of version 2") + self.assertEqual(state['subdir']['0'], [], "Subdirectory for version 0") + self.assertEqual(state['subdir']['1'], [], "Subdirectory for version 1") + self.assertEqual(state['subdir']['2'], ['directory'], "Subdirectory for version 2") + self.assertEqual(state['metadata']['createdby'], RDFDatabankConfig.endpointuser, "Created by") + self.assertEqual(state['metadata']['embargoed'], True, "Embargoed?") + self.failUnless("Unpacked file testdir.zip. Contents added as version 2" in state['versionlog']['2'], "Version 2 log") + self.assertEqual(len(parts.keys()), 5, "Parts") + self.assertEqual(len(parts['4=TestSubmission'].keys()), 13, "File stats for 4=TestSubmission-testdir") + self.assertEqual(len(parts['manifest.rdf'].keys()), 13, "File stats for manifest.rdf") + self.assertEqual(len(parts['directory'].keys()), 0, "File stats for directory") + self.assertEqual(len(parts['test-csv.csv'].keys()), 13, "File stats for test-csv.csv") + + # Delete the dataset TestSubmission-testdir + resp = self.doHTTP_DELETE( + resource="datasets/TestSubmission-testdir", + expect_status="*", expect_reason="*") + + def testSymlinkFileUnpack(self): + """Unpack zip file uploaded in a previous version to a new dataset - POST zip filename to /silo_name/items/dataset_name""" + # Create a new dataset, check response + self.createSubmissionDataset() + # Upload zip file testdir.zip, check response + zipdata = self.uploadSubmissionZipfile(file_to_upload="testdir2.zip") + # Upload zip file test, check response + zipdata = self.uploadSubmissionZipfile() + # Unpack ZIP file into a new dataset, check response + fields = [ + ("filename", "testdir.zip"), + ("id", "TestSubmission-testdir"), + ] + files = [] + (reqtype, reqdata) = SparqlQueryTestCase.encode_multipart_formdata(fields, files) + (resp,respdata) = self.doHTTP_POST( + reqdata, reqtype, + resource="items/TestSubmission", + expect_status=201, expect_reason="Created") + LHobtained = resp.getheader('Content-Location', None) + LHexpected = "%sdatasets/TestSubmission-testdir"%self._endpointpath + self.assertEquals(LHobtained, LHexpected, 'Content-Location not correct') + # Access parent dataset, check response + (resp, respdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/json") + # Access and check list of contents in TestSubmission + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + subj = URIRef(self.getManifestUri("datasets/TestSubmission")) + base = self.getManifestUri("datasets/TestSubmission/") + dcterms = "http://purl.org/dc/terms/" + ore = "http://www.openarchives.org/ore/terms/" + oxds = "http://vocab.ox.ac.uk/dataset/schema#" + stype = URIRef(oxds+"DataSet") + self.assertEqual(len(rdfgraph),13,'Graph length %i' %len(rdfgraph)) + self.failUnless((subj,RDF.type,stype) in rdfgraph, 'Testing submission type: '+subj+", "+stype) + self.failUnless((subj,URIRef(dcterms+"created"),None) in rdfgraph, 'dcterms:created') + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"testdir.zip")) in rdfgraph) + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"testdir2.zip")) in rdfgraph) + #self.failUnless((URIRef(base+"testdir.zip"),URIRef(dcterms+"hasVersion"),None) in rdfgraph, 'dcterms:hasVersion') + self.failUnless((subj,URIRef(dcterms+"identifier"),None) in rdfgraph, 'dcterms:identifier') + self.failUnless((subj,URIRef(dcterms+"mediator"),None) in rdfgraph, 'dcterms:mediator') + self.failUnless((subj,URIRef(dcterms+"rights"),None) in rdfgraph, 'dcterms:rights') + self.failUnless((subj,URIRef(dcterms+"license"),None) in rdfgraph, 'dcterms:license') + self.failUnless((subj,URIRef(dcterms+"publisher"),None) in rdfgraph, 'dcterms:publisher') + self.failUnless((subj,URIRef(oxds+"isEmbargoed"),None) in rdfgraph, 'oxds:isEmbargoed') + self.failUnless((subj,URIRef(oxds+"embargoedUntil"),None) in rdfgraph, 'oxds:embargoedUntil') + self.failUnless((subj,URIRef(oxds+"currentVersion"),"2") in rdfgraph, 'oxds:currentVersion') + self.failUnless((subj,URIRef(dcterms+"modified"),None) in rdfgraph, 'dcterms:modified') + # Access new dataset, check response + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission-testdir", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + subj = URIRef(self.getManifestUri("datasets/TestSubmission-testdir")) + stype = URIRef("http://vocab.ox.ac.uk/dataset/schema#Grouping") + base = self.getManifestUri("datasets/TestSubmission-testdir/") + self.assertEqual(len(rdfgraph),17,'Graph length %i' %len(rdfgraph)) + self.failUnless((subj,RDF.type,stype) in rdfgraph, 'Testing submission type: '+subj+", "+stype) + self.failUnless((subj,URIRef(dcterms+"identifier"),None) in rdfgraph, 'dcterms:identifier') + self.failUnless((subj,URIRef(dcterms+"mediator"),None) in rdfgraph, 'dcterms:mediator') + self.failUnless((subj,URIRef(dcterms+"rights"),None) in rdfgraph, 'dcterms:rights') + self.failUnless((subj,URIRef(dcterms+"license"),None) in rdfgraph, 'dcterms:license') + self.failUnless((subj,URIRef(dcterms+"publisher"),None) in rdfgraph, 'dcterms:publisher') + self.failUnless((subj,URIRef(oxds+"isEmbargoed"),None) in rdfgraph, 'oxds:isEmbargoed') + self.failUnless((subj,URIRef(oxds+"embargoedUntil"),None) in rdfgraph, 'oxds:embargoedUntil') + self.failUnless((subj,URIRef(dcterms+"created"),None) in rdfgraph, 'dcterms:created') + self.failUnless((subj,URIRef(dcterms+"modified"),None) in rdfgraph, 'dcterms:modified') + self.failUnless((subj,URIRef(dcterms+"isVersionOf"),None) in rdfgraph, 'dcterms:isVersionOf') + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"directory")) in rdfgraph) + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"directory/file1.a")) in rdfgraph) + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"directory/file1.b")) in rdfgraph) + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"directory/file2.a")) in rdfgraph) + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"test-csv.csv")) in rdfgraph) + self.failUnless((subj,URIRef(oxds+"currentVersion"),"1") in rdfgraph, 'oxds:currentVersion') + # Access and check content of a resource + (resp, filedata) = self.doHTTP_GET( + resource="datasets/TestSubmission-testdir/directory/file1.b", + expect_status=200, expect_reason="OK", expect_type="text/plain") + checkdata = open("testdata/testdir/directory/file1.b").read() + self.assertEqual(filedata, checkdata, "Difference between local and remote data!") + #Access state information of TestSubmission-testdir + (resp, data) = self.doHTTP_GET( + resource="states/TestSubmission-testdir", + expect_status=200, expect_reason="OK", expect_type="application/json") + state = data['state'] + parts = data['parts'] + self.assertEqual(len(state.keys()), 12, "States") + self.assertEqual(state['item_id'], "TestSubmission-testdir", "Submission item identifier") + self.assertEqual(len(state['versions']), 2, "Two versions") + self.assertEqual(state['versions'][0], '0', "Version 0") + self.assertEqual(state['versions'][1], '1', "Version 1") + self.assertEqual(state['currentversion'], '1', "Current version == 1") + self.assertEqual(state['rdffileformat'], 'xml', "RDF file type") + self.assertEqual(state['rdffilename'], 'manifest.rdf', "RDF file name") + self.assertEqual(len(state['files']['0']), 1, "List should contain just manifest.rdf") + self.assertEqual(len(state['files']['1']), 3, "List should contain manifest.rdf, test-csv.csv and testdir") + self.assertEqual(len(state['metadata_files']['0']), 0, "metadata_files of version 0") + self.assertEqual(len(state['metadata_files']['1']), 0, "metadata_files of version 1") + self.assertEqual(state['subdir']['0'], [], "Subdirectory for version 0") + self.assertEqual(state['subdir']['1'], ['directory'], "Subdirectory for version 1") + self.assertEqual(state['metadata']['createdby'], RDFDatabankConfig.endpointuser, "Created by") + self.assertEqual(state['metadata']['embargoed'], True, "Embargoed?") + self.failUnless("Created new data package" in state['versionlog']['0'], "Version 0 log") + self.failUnless("Unpacked file testdir.zip. Contents added as version 1" in state['versionlog']['1'], "Version 1 log") + self.assertEqual(len(parts.keys()), 5, "Parts") + self.assertEqual(len(parts['4=TestSubmission-testdir'].keys()), 13, "File stats for 4=TestSubmission-testdir") + self.assertEqual(len(parts['manifest.rdf'].keys()), 13, "File stats for manifest.rdf") + self.assertEqual(len(parts['directory'].keys()), 0, "File stats for directory") + self.assertEqual(len(parts['test-csv.csv'].keys()), 13, "File stats for test-csv.csv") + # Delete the dataset TestSubmission-testdir + resp = self.doHTTP_DELETE( + resource="datasets/TestSubmission-testdir", + expect_status="*", expect_reason="*") + + def testFileUploadToUnpackedDataset(self): + """Upload a file to an unpacked dataset - POST filename to /silo_name/datasets/dataset_name""" + # Create a new dataset, check response + self.createSubmissionDataset() + # Upload zip file, check response + zipdata = self.uploadSubmissionZipfile() + # Unpack ZIP file into a new dataset, check response + fields = [("filename", "testdir.zip")] + files = [] + (reqtype, reqdata) = SparqlQueryTestCase.encode_multipart_formdata(fields, files) + (resp,respdata) = self.doHTTP_POST( + reqdata, reqtype, + resource="items/TestSubmission", + expect_status=204, expect_reason="No Content") + # Access dataset TestSubmission, check response + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + subj = URIRef(self.getManifestUri("datasets/TestSubmission")) + stype = URIRef("http://vocab.ox.ac.uk/dataset/schema#Grouping") + base = self.getManifestUri("datasets/TestSubmission/") + dcterms = "http://purl.org/dc/terms/" + ore = "http://www.openarchives.org/ore/terms/" + oxds = "http://vocab.ox.ac.uk/dataset/schema#" + self.assertEqual(len(rdfgraph),17,'Graph length %i' %len(rdfgraph)) + self.failUnless((subj,RDF.type,stype) in rdfgraph, 'Testing submission type: '+subj+", "+stype) + self.failUnless((subj,URIRef(dcterms+"identifier"),None) in rdfgraph, 'dcterms:identifier') + self.failUnless((subj,URIRef(dcterms+"mediator"),None) in rdfgraph, 'dcterms:mediator') + self.failUnless((subj,URIRef(dcterms+"rights"),None) in rdfgraph, 'dcterms:rights') + self.failUnless((subj,URIRef(dcterms+"license"),None) in rdfgraph, 'dcterms:license') + self.failUnless((subj,URIRef(dcterms+"publisher"),None) in rdfgraph, 'dcterms:publisher') + self.failUnless((subj,URIRef(oxds+"isEmbargoed"),None) in rdfgraph, 'oxds:isEmbargoed') + self.failUnless((subj,URIRef(oxds+"embargoedUntil"),None) in rdfgraph, 'oxds:embargoedUntil') + self.failUnless((subj,URIRef(dcterms+"created"),None) in rdfgraph, 'dcterms:created') + self.failUnless((subj,URIRef(dcterms+"modified"),None) in rdfgraph, 'dcterms:modified') + self.failUnless((subj,URIRef(dcterms+"isVersionOf"),None) in rdfgraph, 'dcterms:isVersionOf') + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"directory")) in rdfgraph) + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"directory/file1.a")) in rdfgraph) + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"directory/file1.b")) in rdfgraph) + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"directory/file2.a")) in rdfgraph) + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"test-csv.csv")) in rdfgraph) + self.failUnless((subj,URIRef(oxds+"currentVersion"),"2") in rdfgraph, 'oxds:currentVersion') + # Upload zip file to dataset TestSubmission-testdir + fields = \ + [ ("filename", "testdir2.zip") + ] + zipdata = open("testdata/testdir2.zip").read() + files = \ + [ ("file", "testdir2.zip", zipdata, "application/zip") + ] + (reqtype, reqdata) = SparqlQueryTestCase.encode_multipart_formdata(fields, files) + (resp,respdata) = self.doHTTP_POST( + reqdata, reqtype, + resource="datasets/TestSubmission/", + expect_status=201, expect_reason="Created") + LHobtained = resp.getheader('Content-Location', None) + LHexpected = "%sdatasets/TestSubmission/testdir2.zip"%self._endpointpath + self.assertEquals(LHobtained, LHexpected, 'Content-Location not correct') + # Access dataset TestSubmission-testdir, check response + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + subj = URIRef(self.getManifestUri("datasets/TestSubmission")) + stype = URIRef("http://vocab.ox.ac.uk/dataset/schema#Grouping") + base = self.getManifestUri("datasets/TestSubmission/") + self.assertEqual(len(rdfgraph),18,'Graph length %i' %len(rdfgraph)) + self.failUnless((subj,RDF.type,stype) in rdfgraph, 'Testing submission type: '+subj+", "+stype) + self.failUnless((subj,URIRef(dcterms+"identifier"),None) in rdfgraph, 'dcterms:identifier') + self.failUnless((subj,URIRef(dcterms+"mediator"),None) in rdfgraph, 'dcterms:mediator') + self.failUnless((subj,URIRef(dcterms+"rights"),None) in rdfgraph, 'dcterms:rights') + self.failUnless((subj,URIRef(dcterms+"license"),None) in rdfgraph, 'dcterms:license') + self.failUnless((subj,URIRef(dcterms+"publisher"),None) in rdfgraph, 'dcterms:publisher') + self.failUnless((subj,URIRef(oxds+"isEmbargoed"),None) in rdfgraph, 'oxds:isEmbargoed') + self.failUnless((subj,URIRef(oxds+"embargoedUntil"),None) in rdfgraph, 'oxds:embargoedUntil') + self.failUnless((subj,URIRef(dcterms+"created"),None) in rdfgraph, 'dcterms:created') + self.failUnless((subj,URIRef(dcterms+"modified"),None) in rdfgraph, 'dcterms:modified') + self.failUnless((subj,URIRef(dcterms+"isVersionOf"),None) in rdfgraph, 'dcterms:isVersionOf') + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"directory")) in rdfgraph) + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"directory/file1.a")) in rdfgraph) + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"directory/file1.b")) in rdfgraph) + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"directory/file2.a")) in rdfgraph) + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"test-csv.csv")) in rdfgraph) + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"testdir2.zip")) in rdfgraph) + self.failUnless((subj,URIRef(oxds+"currentVersion"),"3") in rdfgraph, 'oxds:currentVersion') + #Access state information of TestSubmission-testdir + (resp, data) = self.doHTTP_GET( + resource="states/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/json") + state = data['state'] + parts = data['parts'] + self.assertEqual(len(state.keys()), 12, "States") + self.assertEqual(state['item_id'], "TestSubmission", "Submission item identifier") + self.assertEqual(len(state['versions']), 4, "Four versions") + self.assertEqual(state['versions'][0], '0', "Version 0") + self.assertEqual(state['versions'][1], '1', "Version 1") + self.assertEqual(state['versions'][2], '2', "Version 2") + self.assertEqual(state['versions'][3], '3', "Version 3") + self.assertEqual(state['currentversion'], '3', "Current version == 3") + self.assertEqual(state['rdffileformat'], 'xml', "RDF file type") + self.assertEqual(state['rdffilename'], 'manifest.rdf', "RDF file name") + self.assertEqual(len(state['files']['0']), 1, "List should contain just manifest.rdf") + self.assertEqual(len(state['files']['1']), 2, "List should contain manifest.rdf and testrdf.zip") + self.assertEqual(len(state['files']['2']), 3, "List should contain manifest.rdf, test-csv.csv and directory") + self.assertEqual(len(state['files']['3']), 4, "List should contain manifest.rdf, test-csv.csv, directory and testdir2.zip") + self.assertEqual(len(state['metadata_files']['0']), 0, "metadata_files of version 0") + self.assertEqual(len(state['metadata_files']['1']), 0, "metadata_files of version 1") + self.assertEqual(len(state['metadata_files']['2']), 0, "metadata_files of version 2") + self.assertEqual(len(state['metadata_files']['3']), 0, "metadata_files of version 3") + self.assertEqual(state['subdir']['0'], [], "Subdirectory for version 0") + self.assertEqual(state['subdir']['1'], [], "Subdirectory for version 1") + self.assertEqual(state['subdir']['2'], ['directory'], "Subdirectory for version 2") + self.assertEqual(state['subdir']['3'], ['directory'], "Subdirectory for version 3") + self.assertEqual(state['metadata']['createdby'], RDFDatabankConfig.endpointuser, "Created by") + self.assertEqual(state['metadata']['embargoed'], True, "Embargoed?") + self.failUnless("Created new data package" in state['versionlog']['0'], "Version 0 log") + self.failUnless("Added or updated file testdir.zip" in state['versionlog']['1'], "Version 1 log") + self.failUnless("Unpacked file testdir.zip. Contents added as version 2" in state['versionlog']['2'], "Version 2 log") + self.failUnless("Added or updated file testdir2.zip" in state['versionlog']['3'], "Version 3 log") + self.assertEqual(len(parts.keys()), 6, "Parts") + self.assertEqual(len(parts['4=TestSubmission'].keys()), 13, "File stats for 4=TestSubmission-testdir") + self.assertEqual(len(parts['manifest.rdf'].keys()), 13, "File stats for manifest.rdf") + self.assertEqual(len(parts['directory'].keys()), 0, "File stats for directory") + self.assertEqual(len(parts['test-csv.csv'].keys()), 13, "File stats for test-csv.csv") + self.assertEqual(len(parts['testdir2.zip'].keys()), 13, "File stats for testdir2.zip") + # Delete the dataset TestSubmission + resp = self.doHTTP_DELETE( + resource="datasets/TestSubmission", + expect_status="*", expect_reason="*") + + def testUpdateUnpackedDataset(self): + """Unpack zip file to an existing dataset - POST zip filename to /silo_name/items/dataset_name""" + # Create a new dataset, check response + self.createSubmissionDataset() + # Upload zip file, check response + zipdata = self.uploadSubmissionZipfile() + # Upload second zip file, check response + zipdata = self.uploadSubmissionZipfile(file_to_upload="testdir2.zip") + # Unpack ZIP file into a new dataset, check response + fields = [ + ("filename", "testdir.zip"), + ("id", "TestSubmission-testdir") + ] + files = [] + (reqtype, reqdata) = SparqlQueryTestCase.encode_multipart_formdata(fields, files) + (resp,respdata) = self.doHTTP_POST( + reqdata, reqtype, + resource="items/TestSubmission", + expect_status=201, expect_reason="Created") + LHobtained = resp.getheader('Content-Location', None) + LHexpected = "%sdatasets/TestSubmission-testdir"%self._endpointpath + self.assertEquals(LHobtained, LHexpected, 'Content-Location not correct') + # Access and check response for TestSubmission + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + self.assertEqual(len(rdfgraph),13,'Graph length %i' %len(rdfgraph)) + # Access and check list of contents in TestSubmission-testdir + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission-testdir", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + subj = URIRef(self.getManifestUri("datasets/TestSubmission-testdir")) + stype = URIRef("http://vocab.ox.ac.uk/dataset/schema#Grouping") + base = self.getManifestUri("datasets/TestSubmission-testdir/") + dcterms = "http://purl.org/dc/terms/" + ore = "http://www.openarchives.org/ore/terms/" + oxds = "http://vocab.ox.ac.uk/dataset/schema#" + self.assertEqual(len(rdfgraph),17,'Graph length %i' %len(rdfgraph)) + oxds = "http://vocab.ox.ac.uk/dataset/schema#" + dcterms = "http://purl.org/dc/terms/" + subj = URIRef(self.getManifestUri("datasets/TestSubmission-testdir")) + stype = URIRef(oxds+"Grouping") + self.failUnless((subj,RDF.type,stype) in rdfgraph, 'Testing submission type: '+subj+", "+stype) + self.failUnless((subj,URIRef(dcterms+"identifier"),None) in rdfgraph, 'dcterms:identifier') + self.failUnless((subj,URIRef(dcterms+"mediator"),None) in rdfgraph, 'dcterms:mediator') + self.failUnless((subj,URIRef(dcterms+"rights"),None) in rdfgraph, 'dcterms:rights') + self.failUnless((subj,URIRef(dcterms+"license"),None) in rdfgraph, 'dcterms:license') + self.failUnless((subj,URIRef(dcterms+"publisher"),None) in rdfgraph, 'dcterms:publisher') + self.failUnless((subj,URIRef(oxds+"isEmbargoed"),None) in rdfgraph, 'oxds:isEmbargoed') + self.failUnless((subj,URIRef(oxds+"embargoedUntil"),None) in rdfgraph, 'oxds:embargoedUntil') + self.failUnless((subj,URIRef(dcterms+"created"),None) in rdfgraph, 'dcterms:created') + self.failUnless((subj,URIRef(dcterms+"modified"),None) in rdfgraph, 'dcterms:modified') + self.failUnless((subj,URIRef(dcterms+"isVersionOf"),None) in rdfgraph, 'dcterms:isVersionOf') + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"directory")) in rdfgraph) + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"directory/file1.a")) in rdfgraph) + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"directory/file1.b")) in rdfgraph) + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"directory/file2.a")) in rdfgraph) + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"test-csv.csv")) in rdfgraph) + self.failUnless((subj,URIRef(oxds+"currentVersion"),"1") in rdfgraph, 'oxds:currentVersion') + # Unpack second ZIP file into dataset TestSubmission-testdir, check response + fields = \ + [ ("filename", "testdir2.zip"), + ("id", "TestSubmission-testdir") + ] + files = [] + (reqtype, reqdata) = SparqlQueryTestCase.encode_multipart_formdata(fields, files) + (resp,respdata) = self.doHTTP_POST( + reqdata, reqtype, + resource="items/TestSubmission", + expect_status=204, expect_reason="No Content") + # Access and check list of contents in TestSubmission + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + subj = URIRef(self.getManifestUri("datasets/TestSubmission")) + base = self.getManifestUri("datasets/TestSubmission/") + dcterms = "http://purl.org/dc/terms/" + ore = "http://www.openarchives.org/ore/terms/" + oxds = "http://vocab.ox.ac.uk/dataset/schema#" + stype = URIRef(oxds+"DataSet") + self.assertEqual(len(rdfgraph),13,'Graph length %i' %len(rdfgraph)) + self.failUnless((subj,RDF.type,stype) in rdfgraph, 'Testing submission type: '+subj+", "+stype) + self.failUnless((subj,URIRef(dcterms+"created"),None) in rdfgraph, 'dcterms:created') + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"testdir.zip")) in rdfgraph) + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"testdir2.zip")) in rdfgraph) + #self.failUnless((URIRef(base+"testdir.zip"),URIRef(dcterms+"hasVersion"),None) in rdfgraph, 'dcterms:hasVersion') + #self.failUnless((URIRef(base+"testdir2.zip"),URIRef(dcterms+"hasVersion"),None) in rdfgraph, 'dcterms:hasVersion') + self.failUnless((subj,URIRef(dcterms+"identifier"),None) in rdfgraph, 'dcterms:identifier') + self.failUnless((subj,URIRef(dcterms+"mediator"),None) in rdfgraph, 'dcterms:mediator') + self.failUnless((subj,URIRef(dcterms+"rights"),None) in rdfgraph, 'dcterms:rights') + self.failUnless((subj,URIRef(dcterms+"license"),None) in rdfgraph, 'dcterms:license') + self.failUnless((subj,URIRef(dcterms+"publisher"),None) in rdfgraph, 'dcterms:publisher') + self.failUnless((subj,URIRef(oxds+"isEmbargoed"),None) in rdfgraph, 'oxds:isEmbargoed') + self.failUnless((subj,URIRef(oxds+"embargoedUntil"),None) in rdfgraph, 'oxds:embargoedUntil') + self.failUnless((subj,URIRef(oxds+"currentVersion"),"2") in rdfgraph, 'oxds:currentVersion') + self.failUnless((subj,URIRef(dcterms+"modified"),None) in rdfgraph, 'dcterms:modified') + # Access dataset TestSubmission-testdir, check response + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission-testdir", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + subj = URIRef(self.getManifestUri("datasets/TestSubmission-testdir")) + stype1 = URIRef("http://vocab.ox.ac.uk/dataset/schema#DataSet") + stype = URIRef("http://vocab.ox.ac.uk/dataset/schema#Grouping") + base = self.getManifestUri("datasets/TestSubmission-testdir/") + owl = "http://www.w3.org/2002/07/owl#" + self.assertEqual(len(rdfgraph),22,'Graph length %i' %len(rdfgraph)) + self.failUnless((subj,RDF.type,stype) in rdfgraph, 'Testing submission type: '+subj+", "+stype) + self.failUnless((subj,URIRef(dcterms+"identifier"),None) in rdfgraph, 'dcterms:identifier') + self.failUnless((subj,URIRef(dcterms+"mediator"),None) in rdfgraph, 'dcterms:mediator') + self.failUnless((subj,URIRef(dcterms+"rights"),None) in rdfgraph, 'dcterms:rights') + self.failUnless((subj,URIRef(dcterms+"license"),None) in rdfgraph, 'dcterms:license') + self.failUnless((subj,URIRef(dcterms+"publisher"),None) in rdfgraph, 'dcterms:publisher') + self.failUnless((subj,URIRef(oxds+"isEmbargoed"),None) in rdfgraph, 'oxds:isEmbargoed') + self.failUnless((subj,URIRef(oxds+"embargoedUntil"),None) in rdfgraph, 'oxds:embargoedUntil') + self.failUnless((subj,URIRef(dcterms+"created"),None) in rdfgraph, 'dcterms:created') + self.failUnless((subj,URIRef(dcterms+"title"),"Test dataset with merged metadata") in rdfgraph, 'dcterms:title') + self.failUnless((subj,URIRef(dcterms+"modified"),None) in rdfgraph, 'dcterms:modified') + self.failUnless((subj,URIRef(dcterms+"isVersionOf"),None) in rdfgraph, 'dcterms:isVersionOf') + self.failUnless((subj,URIRef(owl+"sameAs"),URIRef("http://example.org/testdir2/")) in rdfgraph, 'owl:sameAs') + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"directory1")) in rdfgraph) + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"directory1/file1.a")) in rdfgraph) + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"directory1/file1.b")) in rdfgraph) + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"directory1/file1.c")) in rdfgraph) + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"directory2")) in rdfgraph) + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"directory2/file2.a")) in rdfgraph) + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"directory2/file2.b")) in rdfgraph) + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"test-csv.csv")) in rdfgraph) + self.failUnless((subj,URIRef(oxds+"currentVersion"),"2") in rdfgraph, 'oxds:currentVersion') + #Access state information of TestSubmission-testdir + (resp, data) = self.doHTTP_GET( + resource="states/TestSubmission-testdir", + expect_status=200, expect_reason="OK", expect_type="application/json") + state = data['state'] + parts = data['parts'] + self.assertEqual(len(state.keys()), 12, "States") + self.assertEqual(state['item_id'], "TestSubmission-testdir", "Submission item identifier") + self.assertEqual(len(state['versions']), 3, "Three versions") + self.assertEqual(state['versions'][0], '0', "Version 0") + self.assertEqual(state['versions'][1], '1', "Version 1") + self.assertEqual(state['versions'][2], '2', "Version 2") + self.assertEqual(state['currentversion'], '2', "Current version == 2") + self.assertEqual(state['rdffileformat'], 'xml', "RDF file type") + self.assertEqual(state['rdffilename'], 'manifest.rdf', "RDF file name") + self.assertEqual(len(state['files']['0']), 1, "List should contain just manifest.rdf") + self.assertEqual(len(state['files']['1']), 3, "List should contain manifest.rdf, directory and test-csv.csv") + self.assertEqual(len(state['files']['2']), 4, "List should contain manifest.rdf, directory1, directory2 and test-csv.csv") + self.assertEqual(len(state['metadata_files']['0']), 0, "metadata_files of version 0") + self.assertEqual(len(state['metadata_files']['1']), 0, "metadata_files of version 1") + self.assertEqual(len(state['metadata_files']['2']), 0, "metadata_files of version 2") + self.assertEqual(state['subdir']['0'], [], "Subdirectory count for version 0") + self.assertEqual(state['subdir']['1'], ['directory'], "Subdirectory for version 1") + self.assertEqual(len(state['subdir']['2']), 2, "Subdirectory for version 2 should be directory1 and directory2") + self.assertEqual(state['metadata']['createdby'], RDFDatabankConfig.endpointuser, "Created by") + self.assertEqual(state['metadata']['embargoed'], True, "Embargoed?") + self.assertEqual(len(state['versionlog']), 3, "Three version logs") + #logger.debug("Versionlog "+str(state['versionlog'])) + self.failUnless("Created new data package" in state['versionlog']['0'], "Version 0 log") + self.failUnless("Unpacked file testdir.zip. Contents added as version 1" in state['versionlog']['1'], "Version 1 log") + self.failUnless("Unpacked file testdir2.zip. Contents added as version 2" in state['versionlog']['2'], "Version 2 log") + self.failUnless("Updated file manifest.rdf" in state['versionlog']['2'], "Version 2 log") + self.assertEqual(len(parts.keys()), 6, "Parts") + self.assertEqual(len(parts['4=TestSubmission-testdir'].keys()), 13, "File stats for 4=TestSubmission-testdir") + self.assertEqual(len(parts['manifest.rdf'].keys()), 13, "File stats for manifest.rdf") + self.assertEqual(len(parts['test-csv.csv'].keys()), 13, "File stats for test-csv.csv") + self.assertEqual(len(parts['directory1'].keys()), 0, "File stats for directory1") + self.assertEqual(len(parts['directory2'].keys()), 0, "File stats for directory2") + # Access dataset TestSubmission-testdir version 1 + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission-testdir?version=1", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + self.assertEqual(len(rdfgraph),17,'Graph length %i' %len(rdfgraph)) + self.failUnless((subj,RDF.type,stype) in rdfgraph, 'Testing submission type: '+subj+", "+stype) + self.failUnless((subj,URIRef(dcterms+"identifier"),None) in rdfgraph, 'dcterms:identifier') + self.failUnless((subj,URIRef(dcterms+"mediator"),None) in rdfgraph, 'dcterms:mediator') + self.failUnless((subj,URIRef(dcterms+"rights"),None) in rdfgraph, 'dcterms:rights') + self.failUnless((subj,URIRef(dcterms+"license"),None) in rdfgraph, 'dcterms:license') + self.failUnless((subj,URIRef(dcterms+"publisher"),None) in rdfgraph, 'dcterms:publisher') + self.failUnless((subj,URIRef(oxds+"isEmbargoed"),None) in rdfgraph, 'oxds:isEmbargoed') + self.failUnless((subj,URIRef(oxds+"embargoedUntil"),None) in rdfgraph, 'oxds:embargoedUntil') + self.failUnless((subj,URIRef(dcterms+"created"),None) in rdfgraph, 'dcterms:created') + self.failUnless((subj,URIRef(dcterms+"modified"),None) in rdfgraph, 'dcterms:modified') + self.failUnless((subj,URIRef(dcterms+"isVersionOf"),None) in rdfgraph, 'dcterms:isVersionOf') + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"directory")) in rdfgraph) + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"directory/file1.a")) in rdfgraph) + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"directory/file1.b")) in rdfgraph) + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"directory/file2.a")) in rdfgraph) + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"test-csv.csv")) in rdfgraph) + self.failUnless((subj,URIRef(oxds+"currentVersion"),"1") in rdfgraph, 'oxds:currentVersion') + #Access state information of TestSubmission-testdir version 1 + (resp, data) = self.doHTTP_GET( + resource="states/TestSubmission-testdir?version=1", + expect_status=200, expect_reason="OK", expect_type="application/json") + state = data['state'] + parts = data['parts'] + self.assertEqual(len(state.keys()), 12, "States") + self.assertEqual(len(parts.keys()), 5, "Parts") + self.assertEqual(len(parts['4=TestSubmission-testdir'].keys()), 13, "File stats for 4=TestSubmission-testdir") + self.assertEqual(len(parts['manifest.rdf'].keys()), 13, "File stats for manifest.rdf") + self.assertEqual(len(parts['test-csv.csv'].keys()), 13, "File stats for test-csv.csv") + self.assertEqual(len(parts['directory'].keys()), 0, "File stats for directory") + # Access and check list of contents in TestSubmission-testdir version 0 + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission-testdir?version=0", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + self.assertEqual(len(rdfgraph),10,'Graph length %i' %len(rdfgraph)) + self.failUnless((subj,RDF.type,stype1) in rdfgraph, 'Testing submission type: '+subj+", "+stype1) + self.failUnless((subj,URIRef(dcterms+"identifier"),None) in rdfgraph, 'dcterms:identifier') + self.failUnless((subj,URIRef(dcterms+"mediator"),None) in rdfgraph, 'dcterms:mediator') + self.failUnless((subj,URIRef(dcterms+"rights"),None) in rdfgraph, 'dcterms:rights') + self.failUnless((subj,URIRef(dcterms+"license"),None) in rdfgraph, 'dcterms:license') + self.failUnless((subj,URIRef(dcterms+"publisher"),None) in rdfgraph, 'dcterms:publisher') + self.failUnless((subj,URIRef(oxds+"isEmbargoed"),None) in rdfgraph, 'oxds:isEmbargoed') + self.failUnless((subj,URIRef(oxds+"embargoedUntil"),None) in rdfgraph, 'oxds:embargoedUntil') + self.failUnless((subj,URIRef(dcterms+"created"),None) in rdfgraph, 'dcterms:created') + self.failUnless((subj,URIRef(oxds+"currentVersion"),"0") in rdfgraph, 'oxds:currentVersion') + #Access state information of TestSubmission-testdir version 0 + (resp, data) = self.doHTTP_GET( + resource="states/TestSubmission-testdir?version=0", + expect_status=200, expect_reason="OK", expect_type="application/json") + state = data['state'] + parts = data['parts'] + self.assertEqual(len(state.keys()), 12, "States") + self.assertEqual(len(parts.keys()), 3, "Parts") + self.assertEqual(len(parts['4=TestSubmission-testdir'].keys()), 13, "File stats for 4=TestSubmission-testdir") + self.assertEqual(len(parts['manifest.rdf'].keys()), 13, "File stats for manifest.rdf") + # Access dataset TestSubmission-testdir version 2 + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission-testdir?version=2", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + self.assertEqual(len(rdfgraph),22,'Graph length %i' %len(rdfgraph)) + self.failUnless((subj,RDF.type,stype) in rdfgraph, 'Testing submission type: '+subj+", "+stype) + self.failUnless((subj,URIRef(dcterms+"identifier"),None) in rdfgraph, 'dcterms:identifier') + self.failUnless((subj,URIRef(dcterms+"mediator"),None) in rdfgraph, 'dcterms:mediator') + self.failUnless((subj,URIRef(dcterms+"rights"),None) in rdfgraph, 'dcterms:rights') + self.failUnless((subj,URIRef(dcterms+"license"),None) in rdfgraph, 'dcterms:license') + self.failUnless((subj,URIRef(dcterms+"publisher"),None) in rdfgraph, 'dcterms:publisher') + self.failUnless((subj,URIRef(oxds+"isEmbargoed"),None) in rdfgraph, 'oxds:isEmbargoed') + self.failUnless((subj,URIRef(oxds+"embargoedUntil"),None) in rdfgraph, 'oxds:embargoedUntil') + self.failUnless((subj,URIRef(dcterms+"created"),None) in rdfgraph, 'dcterms:created') + self.failUnless((subj,URIRef(dcterms+"title"),"Test dataset with merged metadata") in rdfgraph, 'dcterms:title') + self.failUnless((subj,URIRef(dcterms+"modified"),None) in rdfgraph, 'dcterms:modified') + self.failUnless((subj,URIRef(dcterms+"isVersionOf"),None) in rdfgraph, 'dcterms:isVersionOf') + self.failUnless((subj,URIRef(owl+"sameAs"),URIRef("http://example.org/testdir2/")) in rdfgraph, 'owl:sameAs') + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"directory1")) in rdfgraph) + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"directory1/file1.a")) in rdfgraph) + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"directory1/file1.b")) in rdfgraph) + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"directory1/file1.c")) in rdfgraph) + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"directory2")) in rdfgraph) + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"directory2/file2.a")) in rdfgraph) + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"directory2/file2.b")) in rdfgraph) + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"test-csv.csv")) in rdfgraph) + self.failUnless((subj,URIRef(oxds+"currentVersion"),"2") in rdfgraph, 'oxds:currentVersion') + #Access state information of TestSubmission-testdir version 2 + (resp, data) = self.doHTTP_GET( + resource="states/TestSubmission-testdir?version=2", + expect_status=200, expect_reason="OK", expect_type="application/json") + state = data['state'] + parts = data['parts'] + self.assertEqual(len(state.keys()), 12, "States") + self.assertEqual(state['item_id'], "TestSubmission-testdir", "Submission item identifier") + self.assertEqual(len(state['versions']), 3, "Three versions") + self.assertEqual(state['versions'][0], '0', "Version 0") + self.assertEqual(state['versions'][1], '1', "Version 1") + self.assertEqual(state['versions'][2], '2', "Version 2") + self.assertEqual(state['currentversion'], '2', "Current version == 2") + self.assertEqual(state['rdffileformat'], 'xml', "RDF file type") + self.assertEqual(state['rdffilename'], 'manifest.rdf', "RDF file name") + self.assertEqual(len(state['files']['0']), 1, "List should contain just manifest.rdf") + self.assertEqual(len(state['files']['1']), 3, "List should contain manifest.rdf, directory and test-csv.csv") + self.assertEqual(len(state['files']['2']), 4, "List should contain manifest.rdf, directory1, directory2 and test-csv.csv") + self.assertEqual(len(state['metadata_files']['0']), 0, "metadata_files of version 0") + self.assertEqual(len(state['metadata_files']['1']), 0, "metadata_files of version 1") + self.assertEqual(len(state['metadata_files']['2']), 0, "metadata_files of version 2") + self.assertEqual(state['subdir']['0'], [], "Subdirectory count for version 0") + self.assertEqual(state['subdir']['1'], ['directory'], "Subdirectory for version 1") + self.assertEqual(len(state['subdir']['2']), 2, "Subdirectory for version 2 should be directory1 and directory2") + self.assertEqual(state['metadata']['createdby'], RDFDatabankConfig.endpointuser, "Created by") + self.assertEqual(state['metadata']['embargoed'], True, "Embargoed?") + self.assertEqual(len(parts.keys()), 6, "Parts") + self.assertEqual(len(parts['4=TestSubmission-testdir'].keys()), 13, "File stats for 4=TestSubmission-testdir") + self.assertEqual(len(parts['manifest.rdf'].keys()), 13, "File stats for manifest.rdf") + self.assertEqual(len(parts['test-csv.csv'].keys()), 13, "File stats for test-csv.csv") + self.assertEqual(len(parts['directory1'].keys()), 0, "File stats for directory1") + self.assertEqual(len(parts['directory2'].keys()), 0, "File stats for directory2") + # Delete the dataset TestSubmission-testdir + resp = self.doHTTP_DELETE( + resource="datasets/TestSubmission-testdir", + expect_status="*", expect_reason="*") + # Delete the dataset TestSubmission + resp = self.doHTTP_DELETE( + resource="datasets/TestSubmission", + expect_status="*", expect_reason="*") + + def testFileUploadAndUnpack(self): + """PUT file contents to existing filename - PUT file contents to /silo_name/datasets/dataset_name/file_name""" + # Create a new dataset, check response + self.createSubmissionDataset() + # Upload zip file, check response + zipdata = self.uploadSubmissionZipfile(file_to_upload="testrdf3.zip") + # Access content + (resp, respdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + # Put zip file, check response + zipdata2 = open("testdata/testdir.zip").read() + (resp, respdata) = self.doHTTP_PUT(zipdata2, resource="items/TestSubmission/testrdf3.zip", + expect_status=204, expect_reason="No Content", expect_type="text/plain") + # Access and check manifest for version 2 + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission?version=2", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + base = self.getManifestUri("datasets/TestSubmission/") + subj = URIRef(self.getManifestUri("datasets/TestSubmission")) + subj2 = URIRef(self.getManifestUri("datasets/TestSubmission/testrdf3.zip")) + subj3 = URIRef(self.getManifestUri("datasets/TestSubmission?version=3")) + dcterms = "http://purl.org/dc/terms/" + ore = "http://www.openarchives.org/ore/terms/" + oxds = "http://vocab.ox.ac.uk/dataset/schema#" + stype = URIRef(oxds+"DataSet") + self.assertEqual(len(rdfgraph),12,'Graph length %i' %len(rdfgraph)) + self.failUnless((subj,URIRef(oxds+"currentVersion"),'2') in rdfgraph, 'oxds:currentVersion') + self.failUnless((subj,RDF.type,stype) in rdfgraph, 'Testing submission type: '+subj+", "+stype) + self.failUnless((subj,URIRef(dcterms+"created"),None) in rdfgraph, 'dcterms:created') + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"testrdf3.zip")) in rdfgraph) + self.failUnless((subj,URIRef(dcterms+"identifier"),None) in rdfgraph, 'dcterms:identifier') + self.failUnless((subj,URIRef(dcterms+"mediator"),None) in rdfgraph, 'dcterms:mediator') + self.failUnless((subj,URIRef(dcterms+"rights"),None) in rdfgraph, 'dcterms:rights') + self.failUnless((subj,URIRef(dcterms+"license"),None) in rdfgraph, 'dcterms:license') + self.failUnless((subj,URIRef(dcterms+"publisher"),None) in rdfgraph, 'dcterms:publisher') + self.failUnless((subj,URIRef(oxds+"isEmbargoed"),None) in rdfgraph, 'oxds:isEmbargoed') + self.failUnless((subj,URIRef(oxds+"embargoedUntil"),None) in rdfgraph, 'oxds:embargoedUntil') + self.failUnless((subj,URIRef(dcterms+"modified"),None) in rdfgraph, 'dcterms:modified') + #self.failUnless((subj2,URIRef(dcterms+"hasVersion"),subj3) in rdfgraph, 'dcterms:isVersionOf') + # Access dataset version 3, check response + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + stype = URIRef("http://vocab.ox.ac.uk/dataset/schema#Grouping") + self.assertEqual(len(rdfgraph),17,'Graph length %i' %len(rdfgraph)) + self.failUnless((subj,RDF.type,stype) in rdfgraph, 'Testing submission type: '+subj+", "+stype) + self.failUnless((subj,URIRef(dcterms+"identifier"),None) in rdfgraph, 'dcterms:identifier') + self.failUnless((subj,URIRef(dcterms+"mediator"),None) in rdfgraph, 'dcterms:mediator') + self.failUnless((subj,URIRef(dcterms+"rights"),None) in rdfgraph, 'dcterms:rights') + self.failUnless((subj,URIRef(dcterms+"license"),None) in rdfgraph, 'dcterms:locense') + self.failUnless((subj,URIRef(dcterms+"publisher"),None) in rdfgraph, 'dcterms:publisher') + self.failUnless((subj,URIRef(oxds+"isEmbargoed"),None) in rdfgraph, 'oxds:isEmbargoed') + self.failUnless((subj,URIRef(oxds+"embargoedUntil"),None) in rdfgraph, 'oxds:embargoedUntil') + self.failUnless((subj,URIRef(dcterms+"created"),None) in rdfgraph, 'dcterms:created') + self.failUnless((subj,URIRef(dcterms+"modified"),None) in rdfgraph, 'dcterms:modified') + self.failUnless((subj,URIRef(dcterms+"isVersionOf"),None) in rdfgraph, 'dcterms:isVersionOf') + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"directory")) in rdfgraph) + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"directory/file1.a")) in rdfgraph) + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"directory/file1.b")) in rdfgraph) + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"directory/file2.a")) in rdfgraph) + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"test-csv.csv")) in rdfgraph) + self.failUnless((subj,URIRef(oxds+"currentVersion"),"3") in rdfgraph, 'oxds:currentVersion') + #Access state information and check + (resp, data) = self.doHTTP_GET( + resource="states/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/json") + state = data['state'] + parts = data['parts'] + self.assertEqual(len(state.keys()), 12, "States") + self.assertEqual(state['item_id'], "TestSubmission", "Submission item identifier") + self.assertEqual(len(state['versions']), 4, "Four versions") + self.assertEqual(state['versions'][0], '0', "Version 0") + self.assertEqual(state['versions'][1], '1', "Version 1") + self.assertEqual(state['versions'][2], '2', "Version 2") + self.assertEqual(state['versions'][3], '3', "Version 2") + self.assertEqual(state['currentversion'], '3', "Current version == 3") + self.assertEqual(state['rdffileformat'], 'xml', "RDF file type") + self.assertEqual(state['rdffilename'], 'manifest.rdf', "RDF file name") + self.assertEqual(state['files']['0'], ['manifest.rdf'], "List should contain just manifest.rdf") + self.assertEqual(len(state['files']['1']), 2, "List should contain manifest.rdf and testrdf3.zip") + self.assertEqual(len(state['files']['2']), 2, "List should contain manifest.rdf, testrdf3.zip") + self.assertEqual(len(state['files']['3']), 3, "List should contain manifest.rdf, testdir and test-csv.csv") + self.assertEqual(len(state['metadata_files']['0']), 0, "metadata_files of version 0") + self.assertEqual(len(state['metadata_files']['1']), 0, "metadata_files of version 1") + self.assertEqual(len(state['metadata_files']['2']), 0, "metadata_files of version 2") + self.assertEqual(len(state['metadata_files']['3']), 0, "metadata_files of version 3") + self.assertEqual(len(state['subdir']['0']), 0, "Subdirectory count for version 0") + self.assertEqual(len(state['subdir']['1']), 0, "Subdirectory count for version 1") + self.assertEqual(len(state['subdir']['2']), 0, "Subdirectory count for version 2") + self.assertEqual(state['subdir']['3'], ['directory'], "Subdirectory for version 3") + self.assertEqual(state['metadata']['createdby'], RDFDatabankConfig.endpointuser, "Created by") + self.assertEqual(state['metadata']['embargoed'], True, "Embargoed?") + self.failUnless("Created new data package" in state['versionlog']['0'], "Version 0 log") + self.failUnless("Added or updated file testrdf3.zip" in state['versionlog']['1'], "Version 1 log") + self.failUnless("Added or updated file testrdf3.zip" in state['versionlog']['2'], "Version 2 log") + self.failUnless("Unpacked file testrdf3.zip. Contents added as version 3" in state['versionlog']['3'], "Version 3 log") + self.assertEqual(len(parts.keys()), 5, "Parts") + self.assertEqual(len(parts['4=TestSubmission'].keys()), 13, "File stats for 4=TestSubmission-testdir") + self.assertEqual(len(parts['manifest.rdf'].keys()), 13, "File stats for manifest.rdf") + self.assertEqual(len(parts['directory'].keys()), 0, "File stats for directory") + self.assertEqual(len(parts['test-csv.csv'].keys()), 13, "File stats for test-csv.csv") + # Access and check zipfile content of version 1 + (resp, zipfile) = self.doHTTP_GET( + resource="datasets/TestSubmission/testrdf3.zip?version=1", + expect_status=200, expect_reason="OK", expect_type="application/zip") + self.assertEqual(zipdata, zipfile, "Difference between local and remote zipfile!") + # Access and check zipfile content of version 2 + (resp, zipfile2) = self.doHTTP_GET( + resource="datasets/TestSubmission/testrdf3.zip?version=2", + expect_status=200, expect_reason="OK", expect_type="application/zip") + self.assertEqual(zipdata2, zipfile2, "Difference between local and remote zipfile!") + # Access and check file content of a resource in version 3 + (resp, filedata) = self.doHTTP_GET( + resource="datasets/TestSubmission/directory/file1.b", + expect_status=200, expect_reason="OK", expect_type="text/plain") + checkdata = open("testdata/testdir/directory/file1.b").read() + self.assertEqual(filedata, checkdata, "Difference between local and remote data!") + + def testMetadataMerging(self): + """POST zipfile to /silo_name/items/dataset_name. Unpack zip file to a dataset. + manifest.rdf located in unpacked zipfile is munged with existing manifest of the dataset.""" + # Create a new dataset, check response + self.createSubmissionDataset() + # Submit ZIP file testdata/testrdf.zip, check response + fields = [] + zipdata = open("testdata/testrdf.zip").read() + files = \ + [ ("file", "testrdf.zip", zipdata, "application/zip") + ] + (reqtype, reqdata) = SparqlQueryTestCase.encode_multipart_formdata(fields, files) + (resp,respdata) = self.doHTTP_POST( + reqdata, reqtype, + resource="datasets/TestSubmission/", + expect_status=201, expect_reason="Created") + LHobtained = resp.getheader('Content-Location', None) + LHexpected = "%sdatasets/TestSubmission/testrdf.zip"%self._endpointpath + self.assertEquals(LHobtained, LHexpected, 'Content-Location not correct') + # Unpack ZIP file into a new dataset, check response + fields = [ + ("filename", "testrdf.zip"), + ("id", "TestSubmission-testrdf") + ] + files = [] + (reqtype, reqdata) = SparqlQueryTestCase.encode_multipart_formdata(fields, files) + (resp,respdata) = self.doHTTP_POST( + reqdata, reqtype, + resource="items/TestSubmission", + expect_status=201, expect_reason="Created") + LHobtained = resp.getheader('Content-Location', None) + LHexpected = "%sdatasets/TestSubmission-testrdf"%self._endpointpath + self.assertEquals(LHobtained, LHexpected, 'Content-Location not correct') + # Access parent dataset, check response + (resp, respdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/json") + # Access and check list of contents in parent dataset - TestSubmission + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + subj = URIRef(self.getManifestUri("datasets/TestSubmission")) + base = self.getManifestUri("datasets/TestSubmission/") + dcterms = "http://purl.org/dc/terms/" + ore = "http://www.openarchives.org/ore/terms/" + oxds = "http://vocab.ox.ac.uk/dataset/schema#" + stype = URIRef(oxds+"DataSet") + self.assertEqual(len(rdfgraph),12,'Graph length %i' %len(rdfgraph)) + self.failUnless((subj,RDF.type,stype) in rdfgraph, 'Testing submission type: '+subj+", "+stype) + self.failUnless((subj,URIRef(dcterms+"created"),None) in rdfgraph, 'dcterms:created') + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"testrdf.zip")) in rdfgraph) + #self.failUnless((URIRef(base+"testrdf.zip"),URIRef(dcterms+"hasVersion"),None) in rdfgraph, 'dcterms:hasVersion') + self.failUnless((subj,URIRef(dcterms+"identifier"),None) in rdfgraph, 'dcterms:identifier') + self.failUnless((subj,URIRef(dcterms+"mediator"),None) in rdfgraph, 'dcterms:mediator') + self.failUnless((subj,URIRef(dcterms+"rights"),None) in rdfgraph, 'dcterms:rights') + self.failUnless((subj,URIRef(dcterms+"license"),None) in rdfgraph, 'dcterms:license') + self.failUnless((subj,URIRef(dcterms+"publisher"),None) in rdfgraph, 'dcterms:publisher') + self.failUnless((subj,URIRef(oxds+"isEmbargoed"),None) in rdfgraph, 'oxds:isEmbargoed') + self.failUnless((subj,URIRef(oxds+"embargoedUntil"),None) in rdfgraph, 'oxds:embargoedUntil') + self.failUnless((subj,URIRef(oxds+"currentVersion"),'1') in rdfgraph, 'oxds:currentVersion') + self.failUnless((subj,URIRef(dcterms+"modified"),None) in rdfgraph, 'dcterms:modified') + # Access and check list of contents in child dataset - TestSubmission-testrdf + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission-testrdf", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + fr = open('response.xml', 'w') + fr.write(rdfdata) + fr.close() + rdfgraph = Graph() + rdfgraph.parse('response.xml', format='xml') + subj = URIRef(self.getManifestUri("datasets/TestSubmission-testrdf")) + base = self.getManifestUri("datasets/TestSubmission-testrdf/") + owl = "http://www.w3.org/2002/07/owl#" + stype = URIRef(oxds+"Grouping") + self.assertEqual(len(rdfgraph),21,'Graph length %i' %len(rdfgraph)) + self.failUnless((subj,RDF.type,stype) in rdfgraph, 'Testing submission type: '+subj+", "+stype) + self.failUnless((subj,URIRef(owl+"sameAs"),URIRef("http://example.org/testrdf/")) in rdfgraph, 'owl:sameAs') + self.failUnless((subj,URIRef(dcterms+"modified"),None) in rdfgraph, 'dcterms:modified') + self.failUnless((subj,URIRef(dcterms+"isVersionOf"),None) in rdfgraph, 'dcterms:isVersionOf') + self.failUnless((subj,URIRef(dcterms+"title"),"Test dataset with merged metadata") in rdfgraph, 'dcterms:title') + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"directory")) in rdfgraph) + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"directory/file1.a")) in rdfgraph) + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"directory/file1.b")) in rdfgraph) + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"directory/file2.a")) in rdfgraph) + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"arabic.txt")) in rdfgraph) + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"test-csv.csv")) in rdfgraph) + self.failUnless((subj,URIRef(dcterms+"identifier"),None) in rdfgraph, 'dcterms:identifier') + self.failUnless((subj,URIRef(dcterms+"mediator"),None) in rdfgraph, 'dcterms:mediator') + self.failUnless((subj,URIRef(dcterms+"rights"),None) in rdfgraph, 'dcterms:rights') + self.failUnless((subj,URIRef(dcterms+"license"),None) in rdfgraph, 'dcterms:license') + self.failUnless((subj,URIRef(dcterms+"publisher"),None) in rdfgraph, 'dcterms:publisher') + self.failUnless((subj,URIRef(oxds+"isEmbargoed"),None) in rdfgraph, 'oxds:isEmbargoed') + self.failUnless((subj,URIRef(oxds+"embargoedUntil"),None) in rdfgraph, 'oxds:embargoedUntil') + self.failUnless((subj,URIRef(dcterms+"created"),None) in rdfgraph, 'dcterms:created') + self.failUnless((subj,URIRef(oxds+"currentVersion"),'1') in rdfgraph, 'oxds:currentVersion') + #Get the file arabic.txt + (resp, arabic_data) = self.doHTTP_GET( + resource="datasets/TestSubmission-testrdf/arabic.txt", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + #print type(arabic_data) + #for s, p, o in rdfgraph.triples((None, URIRef(dcterms+"description"), None)): + # print s, p, type(o), '\n', o + #print '\n', '-'*50 + #print Literal(arabic_data) + #print '\n', '-'*50 + #fr = open('testdata/testrdf/arabic.txt', 'r') + #arabic_data = fr.read() + #fr.close() + #print '\n', '-'*50 + #print Literal(arabic_data) + #print '\n', '-'*50 + #self.failUnless((subj,URIRef(dcterms+"description"),Literal(arabic_data)) in rdfgraph, 'dcterms:description') + # Delete the dataset TestSubmission-testrdf + resp = self.doHTTP_DELETE( + resource="datasets/TestSubmission-testrdf", + expect_status="*", expect_reason="*") + os.remove('response.xml') + + def testMetadataInDirectoryMerging(self): + """POST zipfile to /silo_name/items/dataset_name. Unpack zip file to a dataset. + manifest.rdf located within a folder in unpacked zipfile is munged with datsets existing manifest""" + + # Create a new dataset, check response + self.createSubmissionDataset() + # Submit ZIP file testdata/testrdf2.zip, check response + fields = [] + zipdata = open("testdata/testrdf2.zip").read() + files = \ + [ ("file", "testrdf2.zip", zipdata, "application/zip") + ] + (reqtype, reqdata) = SparqlQueryTestCase.encode_multipart_formdata(fields, files) + (resp,respdata) = self.doHTTP_POST( + reqdata, reqtype, + resource="datasets/TestSubmission/", + expect_status=201, expect_reason="Created") + LHobtained = resp.getheader('Content-Location', None) + LHexpected = "%sdatasets/TestSubmission/testrdf2.zip"%self._endpointpath + self.assertEquals(LHobtained, LHexpected, 'Content-Location not correct') + # Unpack ZIP file into a new dataset, check response + fields = \ + [ ("filename", "testrdf2.zip") + ] + files = [] + (reqtype, reqdata) = SparqlQueryTestCase.encode_multipart_formdata(fields, files) + (resp,respdata) = self.doHTTP_POST( + reqdata, reqtype, + resource="items/TestSubmission", + expect_status=204, expect_reason="No Content") + # Access parent dataset, check response + (resp, respdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/json") + # Access and check list of contents in dataset + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + subj = URIRef(self.getManifestUri("datasets/TestSubmission")) + base = self.getManifestUri("datasets/TestSubmission/") + dcterms = "http://purl.org/dc/terms/" + ore = "http://www.openarchives.org/ore/terms/" + oxds = "http://vocab.ox.ac.uk/dataset/schema#" + owl = "http://www.w3.org/2002/07/owl#" + stype = URIRef(oxds+"Grouping") + self.assertEqual(len(rdfgraph),20,'Graph length %i' %len(rdfgraph)) + self.failUnless((subj,RDF.type,stype) in rdfgraph, 'Testing submission type: '+subj+", "+stype) + self.failUnless((subj,URIRef(owl+"sameAs"),URIRef("http://example.org/testrdf/")) in rdfgraph, 'owl:sameAs') + self.failUnless((subj,URIRef(dcterms+"modified"),None) in rdfgraph, 'dcterms:modified') + self.failUnless((subj,URIRef(dcterms+"isVersionOf"),None) in rdfgraph, 'dcterms:isVersionOf') + self.failUnless((subj,URIRef(dcterms+"title"),"Test dataset with merged metadata") in rdfgraph, 'dcterms:title') + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"testrdf2")) in rdfgraph) + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"testrdf2/directory")) in rdfgraph) + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"testrdf2/directory/file1.a")) in rdfgraph) + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"testrdf2/directory/file1.b")) in rdfgraph) + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"testrdf2/directory/file2.a")) in rdfgraph) + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"testrdf2/test-csv.csv")) in rdfgraph) + self.failUnless((subj,URIRef(dcterms+"identifier"),None) in rdfgraph, 'dcterms:identifier') + self.failUnless((subj,URIRef(dcterms+"mediator"),None) in rdfgraph, 'dcterms:mediator') + self.failUnless((subj,URIRef(dcterms+"rights"),None) in rdfgraph, 'dcterms:rights') + self.failUnless((subj,URIRef(dcterms+"license"),None) in rdfgraph, 'dcterms:license') + self.failUnless((subj,URIRef(dcterms+"publisher"),None) in rdfgraph, 'dcterms:publisher') + self.failUnless((subj,URIRef(oxds+"isEmbargoed"),None) in rdfgraph, 'oxds:isEmbargoed') + self.failUnless((subj,URIRef(oxds+"embargoedUntil"),None) in rdfgraph, 'oxds:embargoedUntil') + self.failUnless((subj,URIRef(dcterms+"created"),None) in rdfgraph, 'dcterms:created') + self.failUnless((subj,URIRef(oxds+"currentVersion"),'2') in rdfgraph, 'oxds:currentVersion') + + def testReferencedMetadataMerging(self): + """POST zipfile to /silo_name/items/dataset_name. Unpack zip file to a dataset. + manifest.rdf located within the unpacked zipfile is munged with datsets existing manifest. + Also, manifest.rdf in the unpacked zipfile, references other metadata files, using the property rdfs:seeAlso. + The metadata from these files are munged.""" + + # Create a new dataset, check response + self.createSubmissionDataset() + # Submit ZIP file testdata/testrdf3.zip, check response + fields = [] + zipdata = open("testdata/testrdf3.zip").read() + files = \ + [ ("file", "testrdf3.zip", zipdata, "application/zip") + ] + (reqtype, reqdata) = SparqlQueryTestCase.encode_multipart_formdata(fields, files) + (resp,respdata) = self.doHTTP_POST( + reqdata, reqtype, + resource="datasets/TestSubmission/", + expect_status=201, expect_reason="Created") + LHobtained = resp.getheader('Content-Location', None) + LHexpected = "%sdatasets/TestSubmission/testrdf3.zip"%self._endpointpath + self.assertEquals(LHobtained, LHexpected, 'Content-Location not correct') + # Unpack ZIP file into a new dataset, check response + fields = [ + ("filename", "testrdf3.zip"), + ("id", "TestSubmission-testrdf3") + ] + files = [] + (reqtype, reqdata) = SparqlQueryTestCase.encode_multipart_formdata(fields, files) + (resp,respdata) = self.doHTTP_POST( + reqdata, reqtype, + resource="items/TestSubmission", + expect_status=201, expect_reason="Created") + LHobtained = resp.getheader('Content-Location', None) + LHexpected = "%sdatasets/TestSubmission-testrdf3"%self._endpointpath + self.assertEquals(LHobtained, LHexpected, 'Content-Location not correct') + # Access parent dataset, check response + (resp, respdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/json") + # Access and check list of contents in parent dataset - TestSubmission + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + subj = URIRef(self.getManifestUri("datasets/TestSubmission")) + base = self.getManifestUri("datasets/TestSubmission/") + dcterms = "http://purl.org/dc/terms/" + ore = "http://www.openarchives.org/ore/terms/" + oxds = "http://vocab.ox.ac.uk/dataset/schema#" + owl = "http://www.w3.org/2002/07/owl#" + dc = "http://purl.org/dc/elements/1.1/" + stype = URIRef(oxds+"DataSet") + self.assertEqual(len(rdfgraph),12,'Graph length %i' %len(rdfgraph)) + self.failUnless((subj,RDF.type,stype) in rdfgraph, 'Testing submission type: '+subj+", "+stype) + self.failUnless((subj,URIRef(dcterms+"created"),None) in rdfgraph, 'dcterms:created') + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"testrdf3.zip")) in rdfgraph) + #self.failUnless((URIRef(base+"testrdf3.zip"),URIRef(dcterms+"hasVersion"),None) in rdfgraph, 'dcterms:hasVersion') + self.failUnless((subj,URIRef(dcterms+"identifier"),None) in rdfgraph, 'dcterms:identifier') + self.failUnless((subj,URIRef(dcterms+"mediator"),None) in rdfgraph, 'dcterms:mediator') + self.failUnless((subj,URIRef(dcterms+"rights"),None) in rdfgraph, 'dcterms:rights') + self.failUnless((subj,URIRef(dcterms+"license"),None) in rdfgraph, 'dcterms:license') + self.failUnless((subj,URIRef(dcterms+"publisher"),None) in rdfgraph, 'dcterms:publisher') + self.failUnless((subj,URIRef(oxds+"isEmbargoed"),None) in rdfgraph, 'oxds:isEmbargoed') + self.failUnless((subj,URIRef(oxds+"embargoedUntil"),None) in rdfgraph, 'oxds:embargoedUntil') + self.failUnless((subj,URIRef(oxds+"currentVersion"),'1') in rdfgraph, 'oxds:currentVersion') + self.failUnless((subj,URIRef(dcterms+"modified"),None) in rdfgraph, 'dcterms:modified') + # Access and check list of contents in child dataset - TestSubmission-testrdf3 + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission-testrdf3", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + fr = open('response.xml', 'w') + fr.write(rdfdata) + fr.close() + rdfgraph = Graph() + rdfgraph.parse('response.xml', format='xml') + subj = URIRef(self.getManifestUri("datasets/TestSubmission-testrdf3")) + subj2 = URIRef(self.getManifestUri("datasets/TestSubmission-testrdf3/testrdf3/directory/hebrew.txt")) + base = self.getManifestUri("datasets/TestSubmission-testrdf3/") + stype = URIRef(oxds+"Grouping") + stype2 = URIRef(oxds+"item") + self.assertEqual(len(rdfgraph),32,'Graph length %i' %len(rdfgraph)) + self.failUnless((subj,RDF.type,stype) in rdfgraph, 'Testing submission type: '+subj+", "+stype) + self.failUnless((subj,URIRef(dcterms+"modified"),None) in rdfgraph, 'dcterms:modified') + self.failUnless((subj,URIRef(dcterms+"isVersionOf"),None) in rdfgraph, 'dcterms:isVersionOf') + self.failUnless((subj,URIRef(dcterms+"title"),"Test dataset 3 with updated and merged metadata") in rdfgraph, 'dcterms:title') + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"testrdf3")) in rdfgraph) + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"testrdf3/directory")) in rdfgraph) + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"testrdf3/directory/file1.a")) in rdfgraph) + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"testrdf3/directory/file1.b")) in rdfgraph) + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"testrdf3/directory/file2.a")) in rdfgraph) + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"testrdf3/directory/1a.rdf")) in rdfgraph) + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"testrdf3/directory/1b.rdf")) in rdfgraph) + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"testrdf3/directory/2a.rdf")) in rdfgraph) + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"testrdf3/directory/3a.rdf")) in rdfgraph) + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"testrdf3/directory/hebrew.txt")) in rdfgraph) + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"testrdf3/test-csv.csv")) in rdfgraph) + self.failUnless((subj,URIRef(dcterms+"identifier"),None) in rdfgraph, 'dcterms:identifier') + self.failUnless((subj,URIRef(dcterms+"mediator"),None) in rdfgraph, 'dcterms:mediator') + self.failUnless((subj,URIRef(dcterms+"rights"),None) in rdfgraph, 'dcterms:rights') + self.failUnless((subj,URIRef(dcterms+"license"),None) in rdfgraph, 'dcterms:license') + self.failUnless((subj,URIRef(dcterms+"publisher"),None) in rdfgraph, 'dcterms:publisher') + self.failUnless((subj,URIRef(oxds+"isEmbargoed"),None) in rdfgraph, 'oxds:isEmbargoed') + self.failUnless((subj,URIRef(oxds+"embargoedUntil"),None) in rdfgraph, 'oxds:embargoedUntil') + self.failUnless((subj,URIRef(dcterms+"created"),None) in rdfgraph, 'dcterms:created') + self.failUnless((subj,URIRef(oxds+"currentVersion"),'1') in rdfgraph, 'oxds:currentVersion') + self.failUnless((subj,URIRef(owl+"sameAs"),URIRef("http://example.org/2aFiles/")) in rdfgraph, 'owl:sameAs') + self.failUnless((subj,URIRef(dc+"description"),"file1.a is another file") in rdfgraph, 'dc:description') + self.failUnless((subj,URIRef(dc+"description"),"file1.b is another file") in rdfgraph, 'dc:description') + self.failUnless((subj,URIRef(dc+"description"),"This is a archived test item 2a ") in rdfgraph, 'dc:description') + self.failUnless((subj2,RDF.type,stype2) in rdfgraph, 'Testing submission type: '+subj2+", "+stype2) + self.failUnless((subj2,URIRef(dcterms+"title"),"Hebrew text") in rdfgraph, 'dcterms:title') + self.failUnless((subj2,URIRef(dcterms+"source"),"http://genizah.bodleian.ox.ac.uk/") in rdfgraph, 'dcterms:source') + #Get the file hebrew.txt + (resp, hebrew_data) = self.doHTTP_GET( + resource="datasets/TestSubmission-testrdf3/testrdf3/directory/hebrew.txt", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + #self.failUnless((subj2,RDF.value,Literal(hebrew_data)) in rdfgraph, 'rdf:value') + self.failUnless((subj2,RDF.value,None) in rdfgraph, 'rdf:value') + # Delete the dataset TestSubmission-testrdf3 + resp = self.doHTTP_DELETE( + resource="datasets/TestSubmission-testrdf3", + expect_status="*", expect_reason="*") + + def testReferencedMetadataMerging2(self): + """POST zipfile to /silo_name/items/dataset_name. Unpack zip file to a dataset. + manifest.rdf located within the unpacked zipfile is munged with datsets existing manifest. + Also, manifest.rdf in the unpacked zipfile, references other metadata files, using the property rdfs:seeAlso. + The metadata from these files are munged. One of the referenced files, references other files, which if they exists are also munged.""" + + # Create a new dataset, check response + self.createSubmissionDataset() + # Submit ZIP file testdata/testrdf4.zip, check response + fields = [] + zipdata = open("testdata/testrdf4.zip").read() + files = \ + [ ("file", "testrdf4.zip", zipdata, "application/zip") + ] + (reqtype, reqdata) = SparqlQueryTestCase.encode_multipart_formdata(fields, files) + (resp,respdata) = self.doHTTP_POST( + reqdata, reqtype, + resource="datasets/TestSubmission/", + expect_status=201, expect_reason="Created") + LHobtained = resp.getheader('Content-Location', None) + LHexpected = "%sdatasets/TestSubmission/testrdf4.zip"%self._endpointpath + self.assertEquals(LHobtained, LHexpected, 'Content-Location not correct') + # Unpack ZIP file into a new dataset, check response + fields = \ + [ ("filename", "testrdf4.zip") + ] + files = [] + (reqtype, reqdata) = SparqlQueryTestCase.encode_multipart_formdata(fields, files) + (resp,respdata) = self.doHTTP_POST( + reqdata, reqtype, + resource="items/TestSubmission", + expect_status=204, expect_reason="No Content") + # Access dataset, check response + (resp, respdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/json") + # Access and check list of contents in dataset - TestSubmission + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + subj = URIRef(self.getManifestUri("datasets/TestSubmission")) + subj2 = URIRef(self.getManifestUri("datasets/TestSubmission/testrdf4/directory/file1.a")) + subj3 = URIRef(self.getManifestUri("datasets/TestSubmission/testrdf4/directory/file1.b")) + base = self.getManifestUri("datasets/TestSubmission/") + dcterms = "http://purl.org/dc/terms/" + ore = "http://www.openarchives.org/ore/terms/" + oxds = "http://vocab.ox.ac.uk/dataset/schema#" + owl = "http://www.w3.org/2002/07/owl#" + dc = "http://purl.org/dc/elements/1.1/" + stype = URIRef(oxds+"Grouping") + stype2 = URIRef(oxds+"item") + self.assertEqual(len(rdfgraph),29,'Graph length %i' %len(rdfgraph)) + self.failUnless((subj,RDF.type,stype) in rdfgraph, 'Testing submission type: '+subj+", "+stype) + self.failUnless((subj,URIRef(dcterms+"modified"),None) in rdfgraph, 'dcterms:modified') + self.failUnless((subj,URIRef(dcterms+"isVersionOf"),None) in rdfgraph, 'dcterms:isVersionOf') + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"testrdf4")) in rdfgraph) + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"testrdf4/directory")) in rdfgraph) + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"testrdf4/directory/file1.a")) in rdfgraph) + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"testrdf4/directory/file1.b")) in rdfgraph) + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"testrdf4/directory/file2.a")) in rdfgraph) + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"testrdf4/directory/1a.rdf")) in rdfgraph) + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"testrdf4/directory/1b.rdf")) in rdfgraph) + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"testrdf4/directory/2a.rdf")) in rdfgraph) + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"testrdf4/test-csv.csv")) in rdfgraph) + self.failUnless((subj,URIRef(dcterms+"identifier"),None) in rdfgraph, 'dcterms:identifier') + self.failUnless((subj,URIRef(dcterms+"mediator"),None) in rdfgraph, 'dcterms:mediator') + self.failUnless((subj,URIRef(dcterms+"rights"),None) in rdfgraph, 'dcterms:rights') + self.failUnless((subj,URIRef(dcterms+"license"),None) in rdfgraph, 'dcterms:license') + self.failUnless((subj,URIRef(dcterms+"publisher"),None) in rdfgraph, 'dcterms:publisher') + self.failUnless((subj,URIRef(oxds+"isEmbargoed"),None) in rdfgraph, 'oxds:isEmbargoed') + self.failUnless((subj,URIRef(oxds+"embargoedUntil"),None) in rdfgraph, 'oxds:embargoedUntil') + self.failUnless((subj,URIRef(dcterms+"created"),None) in rdfgraph, 'dcterms:created') + self.failUnless((subj,URIRef(oxds+"currentVersion"),'2') in rdfgraph, 'oxds:currentVersion') + self.failUnless((subj,URIRef(dc+"description"),"This is a archived test item 2a ") in rdfgraph, 'dc:description') + #self.failUnless((subj,URIRef(dcterms+"title"),"Test dataset 4 with updated and merged metadata") in rdfgraph, 'dcterms:title') + self.failUnless((subj,URIRef(dcterms+"title"),"Test item 2a") in rdfgraph, 'dcterms:title') + #self.failUnless((subj,URIRef(owl+"sameAs"),URIRef("2aFiles")) in rdfgraph, 'dcterms:title') + #for s, p, o in rdfgraph.triples((None, RDF.type, None)): + # print s, p, o, type(o) + self.failUnless((subj2,RDF.type,stype2) in rdfgraph, 'Testing submission type: %s, %s'%(str(subj2), str(stype2))) + self.failUnless((subj2,URIRef(dc+"description"),"This is a archived test item 1a ") in rdfgraph, 'dc:description') + self.failUnless((subj2,URIRef(dcterms+"title"),"Test item 1a") in rdfgraph, 'dcterms:title') + self.failUnless((subj3,RDF.type,stype2) in rdfgraph, 'Testing submission type: '+subj3+", "+stype2) + self.failUnless((subj3,URIRef(dc+"description"),"This is test item 1b of type file") in rdfgraph, 'dc:description') + self.failUnless((subj3,URIRef(dcterms+"title"),"Test item 1b") in rdfgraph, 'dcterms:title') + # Delete the dataset TestSubmission + resp = self.doHTTP_DELETE( + resource="datasets/TestSubmission", + expect_status="*", expect_reason="*") + + def testZ1DeleteUserMembership(self): + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointadminuser, + endpointpass=RDFDatabankConfig.endpointadminpass) + # Access user details, check response + (resp, data) = self.doHTTP_GET( + endpointpath="/", + resource="users/%s"%RDFDatabankConfig.endpointuser, + expect_status=200, expect_reason="OK", expect_type="application/JSON") + membershipExists = False + silo_name = RDFDatabankConfig.endpointpath.strip('/') + if [silo_name, 'submitter'] in data['groups']: + membershipExists = True + if membershipExists: + resp = self.doHTTP_DELETE( + endpointpath=RDFDatabankConfig.endpointpath, + resource="users/%s"%RDFDatabankConfig.endpointuser, + expect_status=200, expect_reason="OK") + else: + resp = self.doHTTP_DELETE( + endpointpath=RDFDatabankConfig.endpointpath, + resource="users/%s"%RDFDatabankConfig.endpointuser, + expect_status=400, expect_reason="Bad Request") + #Access user details + (resp, data) = self.doHTTP_GET( + endpointpath=RDFDatabankConfig.endpointpath, + resource="users/%s"%RDFDatabankConfig.endpointuser, + expect_status=404, expect_reason="Not Found") + # Access user details, check response + (resp, data) = self.doHTTP_GET( + endpointpath="/", + resource="users/%s"%RDFDatabankConfig.endpointuser, + expect_status=200, expect_reason="OK", expect_type="application/JSON") + self.failUnless([silo_name, 'submitter'] not in data['groups'], "User membership to silo not deleted") + return + + def testZ2DeleteSilo(self): + """List all silos your account has access to - GET /admin. If the silo 'sandbox' does not exist, create it""" + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointadminuser, + endpointpass=RDFDatabankConfig.endpointadminpass) + # Access list silos, check response + (resp, data) = self.doHTTP_GET( + endpointpath="/", + resource="admin/", + expect_status=200, expect_reason="OK", expect_type="application/JSON") + silo_name = RDFDatabankConfig.endpointpath.strip('/') + silolist = data + oldlen = len(silolist) + if silo_name in silolist: + resp = self.doHTTP_DELETE( + endpointpath="/", + resource="%s/admin"%silo_name, + expect_status=200, expect_reason="OK") + lenexpected = oldlen - 1 + else: + resp = self.doHTTP_DELETE( + endpointpath="/", + resource="%s/admin"%silo_name, + expect_status=404, expect_reason="Not Found") + lenexpected = oldlen + # Access list silos, check response + (resp, data) = self.doHTTP_GET( + endpointpath="/", + resource="silos/", + expect_status=200, expect_reason="OK", expect_type="application/json") + newsilolist = data + self.assertEquals(len(newsilolist), lenexpected, "number fo silos do not match") + for s in newsilolist: self.failUnless(s in silolist, "Silo "+s+" in new list, not in original list") + self.failUnless(silo_name not in newsilolist, "Silo '%s' is in new list"%silo_name) + return + + def testZ3DeleteUser(self): + """Create user sandbox_user""" + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointadminuser, + endpointpass=RDFDatabankConfig.endpointadminpass) + # Access list silos, check response + (resp, data) = self.doHTTP_GET( + endpointpath="/", + resource="users/", + expect_status=200, expect_reason="OK", expect_type="application/JSON") + silo_name = RDFDatabankConfig.endpointpath.strip('/') + userExists = False + membershipExists = False + for user in data: + if RDFDatabankConfig.endpointuser in user['user_name']: + userExists = True + if userExists: + # Access user details, check response + (resp, data) = self.doHTTP_GET( + endpointpath="/", + resource="users/%s"%RDFDatabankConfig.endpointuser, + expect_status=200, expect_reason="OK", expect_type="application/JSON") + if len(data['groups']) > 0: + membershipExists = True + if membershipExists: + resp = self.doHTTP_DELETE( + endpointpath="/", + resource="users/%s"%RDFDatabankConfig.endpointuser, + expect_status=403, expect_reason="Forbidden") + #Access user details + (resp, data) = self.doHTTP_GET( + endpointpath="/", + resource="users/%s"%RDFDatabankConfig.endpointuser, + expect_status=200, expect_reason="OK") + else: + resp = self.doHTTP_DELETE( + endpointpath="/", + resource="users/%s"%RDFDatabankConfig.endpointuser, + expect_status=200, expect_reason="OK") + #Access user details + (resp, data) = self.doHTTP_GET( + endpointpath="/", + resource="users/%s"%RDFDatabankConfig.endpointuser, + expect_status=404, expect_reason="Not Found") + else: + resp = self.doHTTP_DELETE( + endpointpath="/", + resource="users/%s"%RDFDatabankConfig.endpointuser, + expect_status=400, expect_reason="Bad Request") + #Access user details + (resp, data) = self.doHTTP_GET( + endpointpath="/", + resource="users/%s"%RDFDatabankConfig.endpointuser, + expect_status=404, expect_reason="Not Found") + return + + # Sentinel/placeholder tests + + def testUnits(self): + assert (True) + + def testComponents(self): + assert (True) + + def testIntegration(self): + assert (True) + + def testPending(self): + #Need to have performance tests and analyse performance + #Need to set the permission of file being uploaded + #assert (False), "Pending tests follow" + #TODO: Write tests to POST 'text' to existing dataset + #TODO: Write tests to POST 'text' to new dataset and set embargo info + assert (True) + +# Assemble test suite + +def getTestSuite(select="unit"): + """ + Get test suite + + select is one of the following: + "unit" return suite of unit tests only + "component" return suite of unit and component tests + "all" return suite of unit, component and integration tests + "pending" return suite of pending tests + name a single named test to be run + """ + testdict = { + "unit": + [ "testUnits" + , "test01CreateUser" + , "test02CreateSilo" + , "test03AddUserMembership" + , "testListSilos" + , "testListDatasets" + , "testSiloState" + , "testDatasetNotPresent" + , "testDatasetCreation" + , "testDatasetRecreation" + , "testDeleteDataset" + , "testDatasetNaming" + , "testDatasetStateInformation" + , "testEmbargoOnCreation" + , "testFileUpload" + , "testFileUploadtoNewDataset" + , "testFileDelete" + , "testFileUpdate" + , "testGetDatasetByVersion" + , "testPostMetadataFile" + , "testMetadataFileUpdate" + , "testMetadataFileDelete" + , "testPutCreateFile" + , "testPutUpdateFile" + , "testPutMetadataFile" + , "testUnicodeMetadataFileUpdate" + , "testDeleteEmbargo" + , "testChangeEmbargo" + , "testFileUnpack" + , "testSymlinkFileUnpack" + , "testFileUploadAndUnpack" + , "testMetadataMerging" + , "testMetadataInDirectoryMerging" + , "testFileUploadToUnpackedDataset" + , "testUpdateUnpackedDataset" + , "testReferencedMetadataMerging" + , "testReferencedMetadataMerging2" + , "testZ1DeleteUserMembership" + , "testZ2DeleteSilo" + , "testZ3DeleteUser" + ], + "component": + [ "testComponents" + ], + "integration": + [ "testIntegration" + ], + "pending": + [ "testPending" + ] + } + return TestUtils.getTestSuite(TestSubmission, testdict, select=select) + +if __name__ == "__main__": + TestUtils.runTests("TestSubmission.log", getTestSuite, sys.argv) + +# End. diff --git a/rdfdatabank/tests/TestSubmission_load.py b/rdfdatabank/tests/TestSubmission_load.py new file mode 100644 index 0000000..25a3f31 --- /dev/null +++ b/rdfdatabank/tests/TestSubmission_load.py @@ -0,0 +1,348 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +""" +Copyright (c) 2012 University of Oxford + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, --INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +""" + +""" +Databank submission test cases +""" + +import os, os.path +from datetime import datetime, timedelta +from dateutil.relativedelta import * +from time import sleep +import sys +import unittest +import logging +import httplib +import urllib +import codecs +from uuid import uuid4 +try: + # Running Python 2.5 with simplejson? + import simplejson as json +except ImportError: + import json + +#My system is running rdflib version 2.4.2. So adding rdflib v3.0 to sys path +#rdflib_path = os.path.join(os.getcwd(), 'rdflib') +#sys.path.insert(0, rdflib_path) +#import rdflib +#from rdflib.namespace import RDF +#from rdflib.graph import Graph +#from rdflib.plugins.memory import Memory +#from rdflib import URIRef +#from rdflib import Literal +#rdflib.plugin.register('sparql',rdflib.query.Processor,'rdfextras.sparql.processor','Processor') +#rdflib.plugin.register('sparql', rdflib.query.Result, +# 'rdfextras.sparql.query', 'SPARQLQueryResult') + +from StringIO import StringIO + +from rdflib import RDF, URIRef, Literal +from rdflib.Graph import ConjunctiveGraph as Graph + +#from time import sleep +#import subprocess + +if __name__ == "__main__": + # For testing: + # add main library directory to python path if running stand-alone + sys.path.append("..") + +#from MiscLib import TestUtils +from testlib import TestUtils +from testlib import SparqlQueryTestCase + +#from RDFDatabankConfigProd import RDFDatabankConfig as RC +from RDFDatabankConfig import RDFDatabankConfig as RC + +RDFDatabankConfig = RC() +logger = logging.getLogger('TestSubmission') + +class TestSubmission(SparqlQueryTestCase.SparqlQueryTestCase): + """ + Test simple dataset submissions to RDFDatabank + """ + def setUp(self): + self.setRequestEndPoint( + endpointhost=RDFDatabankConfig.endpointhost, # Via SSH tunnel + endpointpath=RDFDatabankConfig.endpointpathl) + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointuser, + endpointpass=RDFDatabankConfig.endpointpass) + self.setRequestUriRoot( + manifesturiroot=RDFDatabankConfig.granary_uri_root) + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointuser, + endpointpass=RDFDatabankConfig.endpointpass) + resp = self.doHTTP_DELETE( + resource="datasets/TestSubmission", + expect_status="*", expect_reason="*") + return + + def tearDown(self): + return + + # Create empty test submission dataset + def createSubmissionDataset(self, dataset_id='TestSubmission'): + # Create a new dataset, check response + fields = \ + [ ("id", dataset_id) + ] + files =[] + (reqtype, reqdata) = SparqlQueryTestCase.encode_multipart_formdata(fields, files) + (resp,respdata) = self.doHTTP_POST( + reqdata, reqtype, + resource="datasets/", + expect_status=201, expect_reason="Created") + LHobtained = resp.getheader('Content-Location', None) + LHexpected = "%sdatasets/%s"%(self._endpointpath, dataset_id) + self.assertEquals(LHobtained, LHexpected, 'Content-Location not correct') + return + + def uploadSubmissionZipfile(self, dataset_id='TestSubmission', file_to_upload="testdir.zip", filename=None): + # Submit ZIP file, check response + fields = [] + if filename: + fields = \ + [ ("filename", filename) + ] + else: + filename = file_to_upload + zipdata = open("testdata/%s"%file_to_upload).read() + files = \ + [ ("file", file_to_upload, zipdata, "application/zip") + ] + (reqtype, reqdata) = SparqlQueryTestCase.encode_multipart_formdata(fields, files) + (resp,respdata) = self.doHTTP_POST( + reqdata, reqtype, + resource="datasets/%s/"%dataset_id, + expect_status=201, expect_reason="Created") + LHobtained = resp.getheader('Content-Location', None) + LHexpected = "%sdatasets/%s/%s"%(self._endpointpath, dataset_id, filename) + self.assertEquals(LHobtained, LHexpected, 'Content-Location not correct') + return zipdata + + def updateSubmissionZipfile(self, dataset_id='TestSubmission', file_to_upload="testdir.zip", filename=None): + # Submit ZIP file, check response + fields = [] + if filename: + fields = \ + [ ("filename", filename) + ] + zipdata = open("testdata/%s"%file_to_upload).read() + files = \ + [ ("file", file_to_upload, zipdata, "application/zip") + ] + (reqtype, reqdata) = SparqlQueryTestCase.encode_multipart_formdata(fields, files) + (resp,respdata)= self.doHTTP_POST( + reqdata, reqtype, + resource="datasets/%s/"%dataset_id, + expect_status=204, expect_reason="No Content") + return zipdata + + # Actual tests follow + def test01CreateSilo(self): + """List all silos your account has access to - GET /admin. If the silo 'sandbox' does not exist, create it""" + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointadminuser, + endpointpass=RDFDatabankConfig.endpointadminpass) + # Access list silos, check response + (resp, data) = self.doHTTP_GET( + endpointpath="/", + resource="admin/", + expect_status=200, expect_reason="OK", expect_type="application/JSON") + silo_name = RDFDatabankConfig.endpointpathl.strip('/') + silolist = data + if not silo_name in silolist: + #Create new silo + owner_list = [RDFDatabankConfig.endpointadminuser] + if not RDFDatabankConfig.endpointuser in owner_list: + owner_list.append(RDFDatabankConfig.endpointuser) + owner_list = ",".join(owner_list) + fields = \ + [ ("silo", silo_name), + ("title", "Sandbox silo"), + ("description", "Sandbox silo for testing"), + ("notes", "Created by test"), + ("owners", owner_list), + ("disk_allocation", "100000") + ] + files =[] + (reqtype, reqdata) = SparqlQueryTestCase.encode_multipart_formdata(fields, files) + (resp,respdata) = self.doHTTP_POST( + reqdata, reqtype, resource="admin/", endpointpath="/", + expect_status=201, expect_reason="Created") + LHobtained = resp.getheader('Content-Location', None) + LHexpected = "/%s"%silo_name + self.assertEquals(LHobtained, LHexpected, 'Content-Location not correct') + # Access list silos, check response + (resp, data) = self.doHTTP_GET( + endpointpath="/", + resource="admin/", + expect_status=200, expect_reason="OK", expect_type="application/json") + newsilolist = data + self.failUnless(len(newsilolist)>0, "No silos returned") + self.assertEquals(len(newsilolist), len(silolist)+1, "One additional silo should have been returned") + for s in silolist: self.failUnless(s in newsilolist, "Silo "+s+" in original list, not in new list") + self.failUnless(silo_name in newsilolist, "Silo '%s' not in new list"%silo_name) + return + + def testFileUploadBulk(self): + for i in range(0, 50000): + """Upload file to dataset - POST file to /silo_name/datasets/dataset_name""" + # Create a new dataset, check response + start = datetime.now() + dataset_id= uuid4().hex + f = open('test_times.log', 'a') + f.write('%s: Creating and uploading file to dataset %s \n'%(start.isoformat(), dataset_id)) + f.close() + self.createSubmissionDataset(dataset_id=dataset_id) + #Access state information + (resp, respdata) = self.doHTTP_GET( + resource="states/%s"%dataset_id, + expect_status=200, expect_reason="OK", expect_type="application/json") + # Upload zip file, check response + zipdata = self.uploadSubmissionZipfile(dataset_id=dataset_id, file_to_upload='images.zip', filename='images.zip') + end = datetime.now() + delta = end - start + time_used = delta.days * 86400 + delta.seconds + f = open('test_times.log', 'a') + f.write(' Time taken: %s \n\n'%str(time_used)) + f.close() + # Access and check list of contents + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/%s"%dataset_id, + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + subj = URIRef(self.getManifestUri("datasets/%s"%dataset_id)) + base = self.getManifestUri("datasets/%s/"%dataset_id) + dcterms = "http://purl.org/dc/terms/" + ore = "http://www.openarchives.org/ore/terms/" + oxds = "http://vocab.ox.ac.uk/dataset/schema#" + stype = URIRef(oxds+"DataSet") + self.assertEqual(len(rdfgraph),12,'Graph length %i' %len(rdfgraph)) + self.failUnless((subj,RDF.type,stype) in rdfgraph, 'Testing submission type: '+subj+", "+stype) + self.failUnless((subj,URIRef(dcterms+"created"),None) in rdfgraph, 'dcterms:created') + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"images.zip")) in rdfgraph) + self.failUnless((subj,URIRef(dcterms+"identifier"),None) in rdfgraph, 'dcterms:identifier') + self.failUnless((subj,URIRef(dcterms+"mediator"),None) in rdfgraph, 'dcterms:mediator') + self.failUnless((subj,URIRef(dcterms+"rights"),None) in rdfgraph, 'dcterms:rights') + self.failUnless((subj,URIRef(dcterms+"license"),None) in rdfgraph, 'dcterms:license') + self.failUnless((subj,URIRef(dcterms+"publisher"),None) in rdfgraph, 'dcterms:publisher') + self.failUnless((subj,URIRef(oxds+"isEmbargoed"),None) in rdfgraph, 'oxds:isEmbargoed') + self.failUnless((subj,URIRef(oxds+"currentVersion"),'1') in rdfgraph, 'oxds:currentVersion') + self.failUnless((subj,URIRef(dcterms+"modified"),None) in rdfgraph, 'dcterms:modified') + # Access and check zip file content + (resp, zipfile) = self.doHTTP_GET( + resource="datasets/%s/images.zip"%dataset_id, + expect_status=200, expect_reason="OK", expect_type="application/zip") + self.assertEqual(zipdata, zipfile, "Difference between local and remote zipfile!") + #Access state information and check + (resp, data) = self.doHTTP_GET( + resource="states/%s"%dataset_id, + expect_status=200, expect_reason="OK", expect_type="application/json") + state = data['state'] + parts = data['parts'] + self.assertEqual(len(state.keys()), 12, "States") + self.assertEqual(state['item_id'], dataset_id, "Submission item identifier") + self.assertEqual(len(state['versions']), 2, "Two versions") + self.assertEqual(state['versions'][0], '0', "Version 0") + self.assertEqual(state['versions'][1], '1', "Version 1") + self.assertEqual(state['currentversion'], '1', "Current version == 1") + self.assertEqual(state['rdffileformat'], 'xml', "RDF file type") + self.assertEqual(state['rdffilename'], 'manifest.rdf', "RDF file name") + self.assertEqual(state['files']['0'], ['manifest.rdf'], "List should contain just manifest.rdf") + self.assertEqual(len(state['files']['1']), 2, "List should contain manifest.rdf and images.zip") + self.assertEqual(len(state['metadata_files']['0']), 0, "metadata_files of version 0") + self.assertEqual(len(state['metadata_files']['1']), 0, "metadata_files of version 1") + self.assertEqual(len(state['subdir']['0']), 0, "Subdirectory count for version 0") + self.assertEqual(len(state['subdir']['1']), 0, "Subdirectory count for version 1") + self.assertEqual(state['metadata']['createdby'], RDFDatabankConfig.endpointuser, "Created by") + d = (datetime.now() + relativedelta(years=+70)).isoformat() + d = d.split('T')[0] + self.assertEqual(state['metadata']['embargoed'], True, "Embargoed?") + self.assertTrue(d in state['metadata']['embargoed_until'], "embargoed_until %s?"%d) + self.failUnless('Created new data package' in state['versionlog']['0'], "Version 0 log") + self.failUnless('Added or updated file images.zip' in state['versionlog']['1'], "Version 1 log") + self.assertEqual(len(parts.keys()), 4, "Parts") + self.assertEqual(len(parts['4=%s'%dataset_id].keys()), 13, "File stats for 4=%s"%dataset_id) + self.assertEqual(len(parts['manifest.rdf'].keys()), 13, "File stats for manifest.rdf") + self.assertEqual(len(parts['images.zip'].keys()), 13, "File stats for images.zip") + #sleep(1) + + # Sentinel/placeholder tests + + def testUnits(self): + assert (True) + + def testComponents(self): + assert (True) + + def testIntegration(self): + assert (True) + + def testPending(self): + #Need to have performance tests and analyse performance + #Need to set the permission of file being uploaded + #assert (False), "Pending tests follow" + assert (True) + +# Assemble test suite + +def getTestSuite(select="unit"): + """ + Get test suite + + select is one of the following: + "unit" return suite of unit tests only + "component" return suite of unit and component tests + "all" return suite of unit, component and integration tests + "pending" return suite of pending tests + name a single named test to be run + """ + testdict = { + "unit": + [ "testUnits" + , "test01CreateSilo" + , "testFileUploadBulk" + ], + "component": + [ "testComponents" + ], + "integration": + [ "testIntegration" + ], + "pending": + [ "testPending" + ] + } + return TestUtils.getTestSuite(TestSubmission, testdict, select=select) + +if __name__ == "__main__": + TestUtils.runTests("TestSubmission.log", getTestSuite, sys.argv) + +# End. diff --git a/rdfdatabank/tests/TestSubmission_submitter.py b/rdfdatabank/tests/TestSubmission_submitter.py new file mode 100644 index 0000000..98053cc --- /dev/null +++ b/rdfdatabank/tests/TestSubmission_submitter.py @@ -0,0 +1,8347 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +""" +Copyright (c) 2012 University of Oxford + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, --INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +""" + +""" +Databank submission test cases for user with role submitter. +These tests are for a databank instance with visible metadata +""" +import os, os.path +import datetime +from dateutil.relativedelta import * +import sys +import unittest +import logging +import httplib +import urllib +import codecs +try: + # Running Python 2.5 with simplejson? + import simplejson as json +except ImportError: + import json + +#My system is running rdflib version 2.4.2. So adding rdflib v3.0 to sys path +#rdflib_path = os.path.join(os.getcwd(), 'rdflib') +#sys.path.insert(0, rdflib_path) +#import rdflib + +from StringIO import StringIO + +from rdflib import RDF, URIRef, Literal +from rdflib.Graph import ConjunctiveGraph as Graph + +if __name__ == "__main__": + # For testing: + # add main library directory to python path if running stand-alone + sys.path.append("..") + +#from MiscLib import TestUtils +from testlib import TestUtils +from testlib import SparqlQueryTestCase + +from RDFDatabankConfig import RDFDatabankConfig as RC +RDFDatabankConfig = RC() + +logger = logging.getLogger('TestSubmission') + +class TestSubmission(SparqlQueryTestCase.SparqlQueryTestCase): + """ + Test simple dataset submissions to RDFDatabank + """ + def setUp(self): + self.setRequestEndPoint( + endpointhost=RDFDatabankConfig.endpointhost, # Via SSH tunnel + endpointpath=RDFDatabankConfig.endpointpath) + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointadminuser, + endpointpass=RDFDatabankConfig.endpointadminpass) + self.setRequestUriRoot( + manifesturiroot=RDFDatabankConfig.granary_uri_root) + resp = self.doHTTP_DELETE( + resource="datasets/TestSubmission", + expect_status="*", expect_reason="*") + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointsubmitteruser, + endpointpass=RDFDatabankConfig.endpointsubmitterpass) + return + + def tearDown(self): + return + + # Create empty test submission dataset + def createSubmissionDataset(self): + d = (datetime.datetime.now() + datetime.timedelta(days=365*4)).isoformat() + # Create a new dataset, check response + fields = \ + [ ("id", "TestSubmission"), + ('embargoed', 'True'), + ('embargoed_until', d) + ] + files =[] + (reqtype, reqdata) = SparqlQueryTestCase.encode_multipart_formdata(fields, files) + (resp,respdata) = self.doHTTP_POST( + reqdata, reqtype, + resource="datasets/", + expect_status=201, expect_reason="Created") + LHobtained = resp.getheader('Content-Location', None) + LHexpected = "%sdatasets/TestSubmission"%self._endpointpath + self.assertEquals(LHobtained, LHexpected, 'Content-Location not correct') + return d + + def uploadSubmissionZipfile(self, file_to_upload="testdir.zip"): + # Submit ZIP file, check response + fields = [] + zipdata = open("testdata/%s"%file_to_upload).read() + files = \ + [ ("file", file_to_upload, zipdata, "application/zip") + ] + (reqtype, reqdata) = SparqlQueryTestCase.encode_multipart_formdata(fields, files) + (resp,respdata) = self.doHTTP_POST( + reqdata, reqtype, + resource="datasets/TestSubmission/", + expect_status=201, expect_reason="Created") + LHobtained = resp.getheader('Content-Location', None) + LHexpected = "%sdatasets/TestSubmission/%s"%(self._endpointpath, file_to_upload) + self.assertEquals(LHobtained, LHexpected, 'Content-Location not correct') + return zipdata + + def updateSubmissionZipfile(self, file_to_upload="testdir.zip", filename=None): + # Submit ZIP file, check response + fields = [] + if filename: + fields = \ + [ ("filename", filename) + ] + zipdata = open("testdata/%s"%file_to_upload).read() + files = \ + [ ("file", file_to_upload, zipdata, "application/zip") + ] + (reqtype, reqdata) = SparqlQueryTestCase.encode_multipart_formdata(fields, files) + (resp,respdata)= self.doHTTP_POST( + reqdata, reqtype, + resource="datasets/TestSubmission/", + expect_status=204, expect_reason="No Content") + return zipdata + + + + # Actual tests follow + def test01CreateSilo(self): + """This test is a part of the testcase setup. So not testing for different users""" + """List all silos your account has access to - GET /admin. If the silo 'sandbox' does not exist, create it""" + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointadminuser, + endpointpass=RDFDatabankConfig.endpointadminpass) + # Access list silos, check response + (resp, data) = self.doHTTP_GET( + endpointpath="/", + resource="admin/", + expect_status=200, expect_reason="OK", expect_type="application/JSON") + silo_name = RDFDatabankConfig.endpointpath.strip('/') + silolist = data + if not silo_name in silolist: + #Create new silo + #Add owners + owner_list = [ + RDFDatabankConfig.endpointadminuser + ,RDFDatabankConfig.endpointmanageruser + ,RDFDatabankConfig.endpointsubmitteruser + ,RDFDatabankConfig.endpointadminuser2 + ,RDFDatabankConfig.endpointmanageruser2 + ,RDFDatabankConfig.endpointsubmitteruser2 + ] + owner_list = ",".join(owner_list) + fields = \ + [ ("silo", silo_name), + ("title", "Sandbox silo"), + ("description", "Sandbox silo for testing"), + ("notes", "Created by test"), + ("owners", owner_list), + ("disk_allocation", "100000") + ] + files =[] + (reqtype, reqdata) = SparqlQueryTestCase.encode_multipart_formdata(fields, files) + (resp,respdata) = self.doHTTP_POST( + reqdata, reqtype, resource="admin/", endpointpath="/", + expect_status=201, expect_reason="Created") + LHobtained = resp.getheader('Content-Location', None) + LHexpected = "/%s"%silo_name + self.assertEquals(LHobtained, LHexpected, 'Content-Location not correct') + # Access list silos, check response + (resp, data) = self.doHTTP_GET( + endpointpath="/", + resource="admin/", + expect_status=200, expect_reason="OK", expect_type="application/json") + newsilolist = data + self.failUnless(len(newsilolist)>0, "No silos returned") + self.assertEquals(len(newsilolist), len(silolist)+1, "One additional silo should have been returned") + for s in silolist: self.failUnless(s in newsilolist, "Silo "+s+" in original list, not in new list") + self.failUnless(silo_name in newsilolist, "Silo '%s' not in new list"%silo_name) + + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointadminuser3, + endpointpass=RDFDatabankConfig.endpointadminpass3) + # Access list silos, check response + (resp, data) = self.doHTTP_GET( + endpointpath="/", + resource="admin/", + expect_status=200, expect_reason="OK", expect_type="application/JSON") + silo_name2 = RDFDatabankConfig.endpointpath2.strip('/') + silolist = data + if not silo_name2 in silolist: + #Create new silo + #Add owners + owner_list = [ + RDFDatabankConfig.endpointadminuser3 + ,RDFDatabankConfig.endpointmanageruser3 + ,RDFDatabankConfig.endpointsubmitteruser3 + ] + owner_list = ",".join(owner_list) + fields = \ + [ ("silo", silo_name2), + ("title", "Sandbox silo 2"), + ("description", "Sandbox silo 2 for testing"), + ("notes", "Created by test"), + ("owners", owner_list), + ("disk_allocation", "100000") + ] + files =[] + (reqtype, reqdata) = SparqlQueryTestCase.encode_multipart_formdata(fields, files) + (resp,respdata) = self.doHTTP_POST( + reqdata, reqtype, resource="admin/", endpointpath="/", + expect_status=201, expect_reason="Created") + LHobtained = resp.getheader('Content-Location', None) + LHexpected = "/%s"%silo_name2 + self.assertEquals(LHobtained, LHexpected, 'Content-Location not correct') + # Access list silos, check response + (resp, data) = self.doHTTP_GET( + endpointpath="/", + resource="admin/", + expect_status=200, expect_reason="OK", expect_type="application/json") + newsilolist = data + self.failUnless(len(newsilolist)>0, "No silos returned") + self.assertEquals(len(newsilolist), len(silolist)+1, "One additional silo should have been returned") + for s in silolist: self.failUnless(s in newsilolist, "Silo "+s+" in original list, not in new list") + self.failUnless(silo_name2 in newsilolist, "Silo '%s' not in new list"%silo_name2) + return + + + + def testListSilos(self): + """List all silos your account has access to - GET /silo""" + # Access list of silos, check response + (resp, data) = self.doHTTP_GET( + endpointpath=None, + resource="/silos/", + expect_status=200, expect_reason="OK", expect_type="application/JSON") + # check list of silos is not empty + self.failUnless(len(data)>0, "No silos returned") + self.failUnless(RDFDatabankConfig.endpointpath.strip('/') in data, "%s not in silo list"%RDFDatabankConfig.endpointpath.strip('/')) + silolist = data + + # Access list of silos as admin, check response + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointadminuser, + endpointpass=RDFDatabankConfig.endpointadminpass) + (resp, data) = self.doHTTP_GET( + endpointpath=None, + resource="/silos/", + expect_status=200, expect_reason="OK", expect_type="application/JSON") + self.assertEqual(len(data), len(silolist), "Silos returned for submitter and admin are different") + for s in data: self.failUnless(s in silolist, "Silo "+s+" in submitter list, not in admin list") + + # Access list of silos as manager, check response + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointmanageruser, + endpointpass=RDFDatabankConfig.endpointmanagerpass) + (resp, data) = self.doHTTP_GET( + endpointpath=None, + resource="/silos/", + expect_status=200, expect_reason="OK", expect_type="application/JSON") + self.assertEqual(len(data), len(silolist), "Silos returned for submitter and admin are different") + for s in data: self.failUnless(s in silolist, "Silo "+s+" in submitter list, not in manager list") + + # Access list of silos as submitter2, check response + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointsubmitteruser2, + endpointpass=RDFDatabankConfig.endpointsubmitterpass2) + (resp, data) = self.doHTTP_GET( + endpointpath=None, + resource="/silos/", + expect_status=200, expect_reason="OK", expect_type="application/JSON") + self.assertEqual(len(data), len(silolist), "Silos returned for submitter and admin are different") + for s in data: self.failUnless(s in silolist, "Silo "+s+" in submitter list, not in submitter 2 list") + + # Access list of silos as general user, check response + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointgeneraluser, + endpointpass=RDFDatabankConfig.endpointgeneralpass) + (resp, data) = self.doHTTP_GET( + endpointpath=None, + resource="/silos/", + expect_status=200, expect_reason="OK", expect_type="application/JSON") + self.assertEqual(len(data), len(silolist), "Silos returned for submitter and admin are different") + for s in data: self.failUnless(s in silolist, "Silo "+s+" in submitter list, not in general list") + + # Access list of silos as admin3, check response + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointadminuser3, + endpointpass=RDFDatabankConfig.endpointadminpass3) + (resp, data) = self.doHTTP_GET( + endpointpath=None, + resource="/silos/", + expect_status=200, expect_reason="OK", expect_type="application/JSON") + self.assertEqual(len(data), len(silolist), "Silos returned for submitter and admin are different") + for s in data: self.failUnless(s in silolist, "Silo "+s+" in submitter list, not in admin3 list") + + # Access list of silos as manager3, check response + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointmanageruser3, + endpointpass=RDFDatabankConfig.endpointmanagerpass3) + (resp, data) = self.doHTTP_GET( + endpointpath=None, + resource="/silos/", + expect_status=200, expect_reason="OK", expect_type="application/JSON") + self.assertEqual(len(data), len(silolist), "Silos returned for submitter and admin are different") + for s in data: self.failUnless(s in silolist, "Silo "+s+" in submitter list, not in manager 3 list") + + # Access list of silos as submitter3, check response + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointsubmitteruser3, + endpointpass=RDFDatabankConfig.endpointsubmitterpass3) + (resp, data) = self.doHTTP_GET( + endpointpath=None, + resource="/silos/", + expect_status=200, expect_reason="OK", expect_type="application/JSON") + self.assertEqual(len(data), len(silolist), "Silos returned for submitter and admin are different") + for s in data: self.failUnless(s in silolist, "Silo "+s+" in submitter list, not in submitter 3 list") + + + + def testListDatasets(self): + """List all datasets in a silo - GET /silo_name/datasets""" + # Access list of datasets in the silo as submitter, check response + (resp, data) = self.doHTTP_GET( + resource="datasets/", + expect_status=200, expect_reason="OK", expect_type="application/json") + # Save initial list of datasets + datasetlist = data.keys() + #datasetlist = [] + #for k in data: + # datasetlist.append(k) + #Access as other users and check it is the same as data + #Admin user of this silo + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointadminuser, + endpointpass=RDFDatabankConfig.endpointadminpass) + (resp, data2) = self.doHTTP_GET( + resource="datasets/", + expect_status=200, expect_reason="OK", expect_type="application/json") + self.assertEqual(data2, data, 'List of silos as admin not equal to list as submitter') + #manager user of this silo + data2 = None + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointmanageruser, + endpointpass=RDFDatabankConfig.endpointmanagerpass) + (resp, data2) = self.doHTTP_GET( + resource="datasets/", + expect_status=200, expect_reason="OK", expect_type="application/json") + self.assertEqual(data2, data, 'List of silos as manager not equal to list as submitter') + #submitter user of this silo + data2 = None + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointsubmitteruser2, + endpointpass=RDFDatabankConfig.endpointsubmitterpass2) + (resp, data2) = self.doHTTP_GET( + resource="datasets/", + expect_status=200, expect_reason="OK", expect_type="application/json") + self.assertEqual(data2, data, 'List of silos as submitter not equal to list as submitter') + #General user + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointgeneraluser, + endpointpass=RDFDatabankConfig.endpointgeneralpass) + (resp, data2) = self.doHTTP_GET( + resource="datasets/", + expect_status=200, expect_reason="OK", expect_type="application/json") + self.assertEqual(data2, data, 'List of silos as general not equal to list as submitter') + #Admin user of another silo + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointadminuser3, + endpointpass=RDFDatabankConfig.endpointadminpass3) + (resp, data2) = self.doHTTP_GET( + resource="datasets/", + expect_status=200, expect_reason="OK", expect_type="application/json") + self.assertEqual(data2, data, 'List of silos as admin3 not equal to list as submitter') + #manager user of another silo + data2 = None + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointmanageruser3, + endpointpass=RDFDatabankConfig.endpointmanagerpass3) + (resp, data2) = self.doHTTP_GET( + resource="datasets/", + expect_status=200, expect_reason="OK", expect_type="application/json") + self.assertEqual(data2, data, 'List of silos as manager3 not equal to list as submitter') + #submitter user of another silo + data2 = None + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointsubmitteruser3, + endpointpass=RDFDatabankConfig.endpointsubmitterpass3) + (resp, data2) = self.doHTTP_GET( + resource="datasets/", + expect_status=200, expect_reason="OK", expect_type="application/json") + self.assertEqual(data2, data, 'List of silos as submitter not equal to list as submitter') + + # Create a new dataset + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointsubmitteruser, + endpointpass=RDFDatabankConfig.endpointsubmitterpass) + self.createSubmissionDataset() + + # Read list of datasets, check that new list is original + new dataset + (resp, data) = self.doHTTP_GET( + resource="datasets/", + expect_status=200, expect_reason="OK", expect_type="application/json") + newlist = [] + for k in data: + newlist.append(k) + logger.debug("Orig. length "+str(len(datasetlist))+", new length "+str(len(newlist))) + self.assertEquals(len(newlist), len(datasetlist)+1, "One additional dataset") + for ds in datasetlist: self.failUnless(ds in newlist, "Dataset "+ds+" in original list, not in new list") + for ds in newlist: self.failUnless((ds in datasetlist) or (ds == "TestSubmission"), "Datset "+ds+" in new list, not in original list") + self.failUnless("TestSubmission" in newlist, "testSubmission in new list") + #Access as other users and check it is the same as data + #Admin user of this silo + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointadminuser, + endpointpass=RDFDatabankConfig.endpointadminpass) + (resp, data2) = self.doHTTP_GET( + resource="datasets/", + expect_status=200, expect_reason="OK", expect_type="application/json") + self.assertEqual(data2, data, 'List of silos as admin not equal to list as submitter') + #manager user of this silo + data2 = None + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointmanageruser, + endpointpass=RDFDatabankConfig.endpointmanagerpass) + (resp, data2) = self.doHTTP_GET( + resource="datasets/", + expect_status=200, expect_reason="OK", expect_type="application/json") + self.assertEqual(data2, data, 'List of silos as manager not equal to list as submitter') + #submitter user of this silo + data2 = None + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointsubmitteruser2, + endpointpass=RDFDatabankConfig.endpointsubmitterpass2) + (resp, data2) = self.doHTTP_GET( + resource="datasets/", + expect_status=200, expect_reason="OK", expect_type="application/json") + self.assertEqual(data2, data, 'List of silos as submitter not equal to list as submitter') + #General user + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointgeneraluser, + endpointpass=RDFDatabankConfig.endpointgeneralpass) + (resp, data2) = self.doHTTP_GET( + resource="datasets/", + expect_status=200, expect_reason="OK", expect_type="application/json") + self.assertEqual(data2, data, 'List of silos as general not equal to list as submitter') + #Admin user of another silo + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointadminuser3, + endpointpass=RDFDatabankConfig.endpointadminpass3) + (resp, data2) = self.doHTTP_GET( + resource="datasets/", + expect_status=200, expect_reason="OK", expect_type="application/json") + self.assertEqual(data2, data, 'List of silos as admin3 not equal to list as submitter') + #manager user of another silo + data2 = None + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointmanageruser3, + endpointpass=RDFDatabankConfig.endpointmanagerpass3) + (resp, data2) = self.doHTTP_GET( + resource="datasets/", + expect_status=200, expect_reason="OK", expect_type="application/json") + self.assertEqual(data2, data, 'List of silos as manager3 not equal to list as submitter') + #submitter user of another silo + data2 = None + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointsubmitteruser3, + endpointpass=RDFDatabankConfig.endpointsubmitterpass3) + (resp, data2) = self.doHTTP_GET( + resource="datasets/", + expect_status=200, expect_reason="OK", expect_type="application/json") + self.assertEqual(data2, data, 'List of silos as submitter not equal to list as submitter') + + # Delete new dataset + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointsubmitteruser, + endpointpass=RDFDatabankConfig.endpointsubmitterpass) + resp = self.doHTTP_DELETE( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK") + # read list of datasets, check result is same as original list + (resp, data) = self.doHTTP_GET( + resource="datasets/", + expect_status=200, expect_reason="OK", expect_type="application/json") + newlist = [] + for k in data: + newlist.append(k) + logger.debug("Orig. length "+str(len(datasetlist))+", new length "+str(len(newlist))) + self.assertEquals(len(newlist), len(datasetlist), "Back to original content in silo") + for ds in datasetlist: self.failUnless(ds in newlist, "Datset "+ds+" in original list, not in new list") + for ds in newlist: self.failUnless(ds in datasetlist, "Datset "+ds+" in new list, not in original list") + #Access as other users and check it is the same as data + #Admin user of this silo + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointadminuser, + endpointpass=RDFDatabankConfig.endpointadminpass) + (resp, data2) = self.doHTTP_GET( + resource="datasets/", + expect_status=200, expect_reason="OK", expect_type="application/json") + self.assertEqual(data2, data, 'List of silos as admin not equal to list as submitter') + #manager user of this silo + data2 = None + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointmanageruser, + endpointpass=RDFDatabankConfig.endpointmanagerpass) + (resp, data2) = self.doHTTP_GET( + resource="datasets/", + expect_status=200, expect_reason="OK", expect_type="application/json") + self.assertEqual(data2, data, 'List of silos as manager not equal to list as submitter') + #submitter user of this silo + data2 = None + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointsubmitteruser2, + endpointpass=RDFDatabankConfig.endpointsubmitterpass2) + (resp, data2) = self.doHTTP_GET( + resource="datasets/", + expect_status=200, expect_reason="OK", expect_type="application/json") + self.assertEqual(data2, data, 'List of silos as submitter not equal to list as submitter') + #General user + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointgeneraluser, + endpointpass=RDFDatabankConfig.endpointgeneralpass) + (resp, data2) = self.doHTTP_GET( + resource="datasets/", + expect_status=200, expect_reason="OK", expect_type="application/json") + self.assertEqual(data2, data, 'List of silos as general not equal to list as submitter') + #Admin user of another silo + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointadminuser3, + endpointpass=RDFDatabankConfig.endpointadminpass3) + (resp, data2) = self.doHTTP_GET( + resource="datasets/", + expect_status=200, expect_reason="OK", expect_type="application/json") + self.assertEqual(data2, data, 'List of silos as admin3 not equal to list as submitter') + #manager user of another silo + data2 = None + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointmanageruser3, + endpointpass=RDFDatabankConfig.endpointmanagerpass3) + (resp, data2) = self.doHTTP_GET( + resource="datasets/", + expect_status=200, expect_reason="OK", expect_type="application/json") + self.assertEqual(data2, data, 'List of silos as manager3 not equal to list as submitter') + #submitter user of another silo + data2 = None + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointsubmitteruser3, + endpointpass=RDFDatabankConfig.endpointsubmitterpass3) + (resp, data2) = self.doHTTP_GET( + resource="datasets/", + expect_status=200, expect_reason="OK", expect_type="application/json") + self.assertEqual(data2, data, 'List of silos as submitter not equal to list as submitter') + + + + def testSiloState(self): + """Get state informaton of a silo - GET /silo_name/states""" + # Access state information of silo for submitter, check response + (resp, data) = self.doHTTP_GET(resource="states/", + expect_status=403, expect_reason="Forbidden", expect_type="text/plain") + + #Access state info as other users + #Admin user of this silo + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointadminuser, + endpointpass=RDFDatabankConfig.endpointadminpass) + (resp, data2) = self.doHTTP_GET( + resource="states/", + expect_status=200, expect_reason="OK", expect_type="application/json") + + #manager user of this silo + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointmanageruser, + endpointpass=RDFDatabankConfig.endpointmanagerpass) + (resp, data2) = self.doHTTP_GET( + resource="states/", + expect_status=200, expect_reason="OK", expect_type="application/json") + + #submitter user of this silo + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointsubmitteruser2, + endpointpass=RDFDatabankConfig.endpointsubmitterpass2) + (resp, data2) = self.doHTTP_GET( + resource="states/", + expect_status=403, expect_reason="Forbidden", expect_type="text/plain") + + #General user + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointgeneraluser, + endpointpass=RDFDatabankConfig.endpointgeneralpass) + (resp, data2) = self.doHTTP_GET( + resource="states/", + expect_status=401, expect_reason="Unauthorized", expect_type="text/plain") + + #Admin user of another silo + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointadminuser3, + endpointpass=RDFDatabankConfig.endpointadminpass3) + (resp, data2) = self.doHTTP_GET( + resource="states/", + expect_status=403, expect_reason="Forbidden", expect_type="text/plain") + + #manager user of another silo + data2 = None + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointmanageruser3, + endpointpass=RDFDatabankConfig.endpointmanagerpass3) + (resp, data2) = self.doHTTP_GET( + resource="states/", + expect_status=403, expect_reason="Forbidden", expect_type="text/plain") + + #submitter user of another silo + data2 = None + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointsubmitteruser3, + endpointpass=RDFDatabankConfig.endpointsubmitterpass3) + (resp, data2) = self.doHTTP_GET( + resource="states/", + expect_status=403, expect_reason="Forbidden", expect_type="text/plain") + + + + def testDatasetNotPresent(self): + """Verify dataset is not present - GET /silo_name/dataset_name""" + (resp, respdata) = self.doHTTP_GET(resource="TestSubmission", + expect_status=404, expect_reason="Not Found") + + #Access dataset as other users + #Admin user of this silo + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointadminuser, + endpointpass=RDFDatabankConfig.endpointadminpass) + (resp, respdata) = self.doHTTP_GET(resource="TestSubmission", + expect_status=404, expect_reason="Not Found") + + #manager user of this silo + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointmanageruser, + endpointpass=RDFDatabankConfig.endpointmanagerpass) + (resp, respdata) = self.doHTTP_GET(resource="TestSubmission", + expect_status=404, expect_reason="Not Found") + + #submitter user of this silo + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointsubmitteruser2, + endpointpass=RDFDatabankConfig.endpointsubmitterpass2) + (resp, respdata) = self.doHTTP_GET(resource="TestSubmission", + expect_status=404, expect_reason="Not Found") + + #General user + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointgeneraluser, + endpointpass=RDFDatabankConfig.endpointgeneralpass) + (resp, respdata) = self.doHTTP_GET(resource="TestSubmission", + expect_status=404, expect_reason="Not Found") + + #Admin user of another silo + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointadminuser3, + endpointpass=RDFDatabankConfig.endpointadminpass3) + (resp, respdata) = self.doHTTP_GET(resource="TestSubmission", + expect_status=404, expect_reason="Not Found") + + #manager user of another silo + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointmanageruser3, + endpointpass=RDFDatabankConfig.endpointmanagerpass3) + (resp, respdata) = self.doHTTP_GET(resource="TestSubmission", + expect_status=404, expect_reason="Not Found") + + #submitter user of another silo + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointsubmitteruser3, + endpointpass=RDFDatabankConfig.endpointsubmitterpass3) + (resp, respdata) = self.doHTTP_GET(resource="TestSubmission", + expect_status=404, expect_reason="Not Found") + + + + def testDatasetCreation(self): + """Create dataset - POST id to /silo_name""" + # Create a new dataset as submitter, check response + d = self.createSubmissionDataset() + # Access dataset, check response + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + self.assertEqual(len(rdfgraph),10,'Graph length %i' %len(rdfgraph)) + oxds = "http://vocab.ox.ac.uk/dataset/schema#" + dcterms = "http://purl.org/dc/terms/" + subj = URIRef(self.getManifestUri("datasets/TestSubmission")) + stype = URIRef(oxds+"DataSet") + self.failUnless((subj,RDF.type,stype) in rdfgraph, 'Testing submission type: '+subj+", "+stype) + self.failUnless((subj,URIRef(dcterms+"created"),None) in rdfgraph, 'dcterms:created') + self.failUnless((subj,URIRef(dcterms+"identifier"),None) in rdfgraph, 'dcterms:identifier') + self.failUnless((subj,URIRef(dcterms+"mediator"),RDFDatabankConfig.endpointsubmitteruser) in rdfgraph, 'dcterms:mediator') + self.failUnless((subj,URIRef(dcterms+"rights"),None) in rdfgraph, 'dcterms:rights') + self.failUnless((subj,URIRef(dcterms+"license"),None) in rdfgraph, 'dcterms:license') + self.failUnless((subj,URIRef(dcterms+"publisher"),None) in rdfgraph, 'dcterms:publisher') + self.failUnless((subj,URIRef(oxds+"isEmbargoed"),'True') in rdfgraph, 'oxds:isEmbargoed') + self.failUnless((subj,URIRef(oxds+"embargoedUntil"),d) in rdfgraph, 'oxds:embargoedUntil') + self.failUnless((subj,URIRef(oxds+"currentVersion"),'0') in rdfgraph, 'oxds:currentVersion') + + #Admin user of this silo - Create a new dataset, check response + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointadminuser, + endpointpass=RDFDatabankConfig.endpointadminpass) + fields = \ + [ ("id", "TestSubmission2") + ] + files =[] + (reqtype, reqdata) = SparqlQueryTestCase.encode_multipart_formdata(fields, files) + (resp,respdata) = self.doHTTP_POST( + reqdata, reqtype, + resource="datasets/", + expect_status=201, expect_reason="Created") + LHobtained = resp.getheader('Content-Location', None) + LHexpected = "%sdatasets/TestSubmission2"%self._endpointpath + self.assertEquals(LHobtained, LHexpected, 'Content-Location not correct') + # Access dataset, check response + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointsubmitteruser, + endpointpass=RDFDatabankConfig.endpointsubmitterpass) + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission2", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + subj = URIRef(self.getManifestUri("datasets/TestSubmission2")) + self.assertEqual(len(rdfgraph), 10,'Graph length %i' %len(rdfgraph)) + self.failUnless((subj,URIRef(dcterms+"mediator"),RDFDatabankConfig.endpointadminuser) in rdfgraph, 'dcterms:mediator') + + #manager user of this silo - Create a new dataset, check response + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointmanageruser, + endpointpass=RDFDatabankConfig.endpointmanagerpass) + fields = \ + [ ("id", "TestSubmission3") + ] + files =[] + (reqtype, reqdata) = SparqlQueryTestCase.encode_multipart_formdata(fields, files) + (resp,respdata) = self.doHTTP_POST( + reqdata, reqtype, + resource="datasets/", + expect_status=201, expect_reason="Created") + LHobtained = resp.getheader('Content-Location', None) + LHexpected = "%sdatasets/TestSubmission3"%self._endpointpath + self.assertEquals(LHobtained, LHexpected, 'Content-Location not correct') + # Access dataset, check response + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointsubmitteruser, + endpointpass=RDFDatabankConfig.endpointsubmitterpass) + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission3", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + subj = URIRef(self.getManifestUri("datasets/TestSubmission3")) + self.assertEqual(len(rdfgraph),10,'Graph length %i' %len(rdfgraph)) + self.failUnless((subj,URIRef(dcterms+"mediator"),RDFDatabankConfig.endpointmanageruser) in rdfgraph, 'dcterms:mediator') + + #General user - Create a new dataset, check response + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointgeneraluser, + endpointpass=RDFDatabankConfig.endpointgeneralpass) + fields = \ + [ ("id", "TestSubmission4") + ] + files =[] + (reqtype, reqdata) = SparqlQueryTestCase.encode_multipart_formdata(fields, files) + (resp,respdata) = self.doHTTP_POST( + reqdata, reqtype, + resource="datasets/", + expect_status=401, expect_reason="Unauthorized", expect_type="text/plain") + # Access dataset, check response + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointsubmitteruser, + endpointpass=RDFDatabankConfig.endpointsubmitterpass) + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission4", + expect_status=404, expect_reason="Not Found", expect_type="application/rdf+xml") + + #Admin user of another silo - Create a new dataset, check response + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointadminuser3, + endpointpass=RDFDatabankConfig.endpointadminpass3) + fields = \ + [ ("id", "TestSubmission4") + ] + files =[] + (reqtype, reqdata) = SparqlQueryTestCase.encode_multipart_formdata(fields, files) + (resp,respdata) = self.doHTTP_POST( + reqdata, reqtype, + resource="datasets/", + expect_status=403, expect_reason="Forbidden") + # Access dataset, check response + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointsubmitteruser, + endpointpass=RDFDatabankConfig.endpointsubmitterpass) + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission4", + expect_status=404, expect_reason="Not Found", expect_type="application/rdf+xml") + + #manager user of another silo - Create a new dataset, check response + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointmanageruser3, + endpointpass=RDFDatabankConfig.endpointmanagerpass3) + fields = \ + [ ("id", "TestSubmission4") + ] + files =[] + (reqtype, reqdata) = SparqlQueryTestCase.encode_multipart_formdata(fields, files) + (resp,respdata) = self.doHTTP_POST( + reqdata, reqtype, + resource="datasets/", + expect_status=403, expect_reason="Forbidden") + # Access dataset, check response + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointsubmitteruser, + endpointpass=RDFDatabankConfig.endpointsubmitterpass) + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission4", + expect_status=404, expect_reason="Not Found", expect_type="application/rdf+xml") + + #submitter user of another silo - Create a new dataset, check response + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointsubmitteruser3, + endpointpass=RDFDatabankConfig.endpointsubmitterpass3) + fields = \ + [ ("id", "TestSubmission4") + ] + files =[] + (reqtype, reqdata) = SparqlQueryTestCase.encode_multipart_formdata(fields, files) + (resp,respdata) = self.doHTTP_POST( + reqdata, reqtype, + resource="datasets/", + expect_status=403, expect_reason="Forbidden") + # Access dataset, check response + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointsubmitteruser, + endpointpass=RDFDatabankConfig.endpointsubmitterpass) + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission4", + expect_status=404, expect_reason="Not Found", expect_type="application/rdf+xml") + + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointadminuser, + endpointpass=RDFDatabankConfig.endpointadminpass) + resp = self.doHTTP_DELETE( + resource="datasets/TestSubmission2", + expect_status=200, expect_reason="*") + resp = self.doHTTP_DELETE( + resource="datasets/TestSubmission3", + expect_status=200, expect_reason="*") + + + + def testDatasetCreation2(self): + """Create dataset - POST to /silo_name/dataset_name""" + # Create a new dataset, check response + fields = [] + files =[] + (reqtype, reqdata) = SparqlQueryTestCase.encode_multipart_formdata(fields, files) + (resp,respdata)= self.doHTTP_POST( + reqdata, reqtype, + resource="datasets/TestSubmission", + expect_status=201, expect_reason="Created") + LHobtained = resp.getheader('Content-Location', None) + LHexpected = "%sdatasets/TestSubmission"%self._endpointpath + self.assertEquals(LHobtained, LHexpected, 'Content-Location not correct') + # Access dataset, check response + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + self.assertEqual(len(rdfgraph),10,'Graph length %i' %len(rdfgraph)) + oxds = "http://vocab.ox.ac.uk/dataset/schema#" + dcterms = "http://purl.org/dc/terms/" + subj = URIRef(self.getManifestUri("datasets/TestSubmission")) + stype = URIRef(oxds+"DataSet") + self.failUnless((subj,RDF.type,stype) in rdfgraph, 'Testing submission type: '+subj+", "+stype) + self.failUnless((subj,URIRef(dcterms+"created"),None) in rdfgraph, 'dcterms:created') + self.failUnless((subj,URIRef(dcterms+"identifier"),None) in rdfgraph, 'dcterms:identifier') + self.failUnless((subj,URIRef(dcterms+"mediator"),RDFDatabankConfig.endpointsubmitteruser) in rdfgraph, 'dcterms:mediator') + self.failUnless((subj,URIRef(dcterms+"rights"),None) in rdfgraph, 'dcterms:rights') + self.failUnless((subj,URIRef(dcterms+"license"),None) in rdfgraph, 'dcterms:license') + self.failUnless((subj,URIRef(dcterms+"publisher"),None) in rdfgraph, 'dcterms:publisher') + self.failUnless((subj,URIRef(oxds+"isEmbargoed"),'True') in rdfgraph, 'oxds:isEmbargoed') + self.failUnless((subj,URIRef(oxds+"embargoedUntil"),None) in rdfgraph, 'oxds:isEmbargoed') + self.failUnless((subj,URIRef(oxds+"currentVersion"),'0') in rdfgraph, 'oxds:currentVersion') + #Admin user of this silo - Create a new dataset, check response + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointadminuser, + endpointpass=RDFDatabankConfig.endpointadminpass) + d = (datetime.datetime.now() + datetime.timedelta(days=365*4)).isoformat() + fields = \ + [ ("id", "TestSubmission2"), + ('embargoed', 'True'), + ('embargoed_until', d) + ] + files =[] + (reqtype, reqdata) = SparqlQueryTestCase.encode_multipart_formdata(fields, files) + (resp,respdata) = self.doHTTP_POST( + reqdata, reqtype, + resource="datasets/TestSubmission2", + expect_status=201, expect_reason="Created") + LHobtained = resp.getheader('Content-Location', None) + LHexpected = "%sdatasets/TestSubmission2"%self._endpointpath + self.assertEquals(LHobtained, LHexpected, 'Content-Location not correct') + # Access dataset, check response + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointsubmitteruser, + endpointpass=RDFDatabankConfig.endpointsubmitterpass) + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission2", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + subj = URIRef(self.getManifestUri("datasets/TestSubmission2")) + self.assertEqual(len(rdfgraph),10,'Graph length %i' %len(rdfgraph)) + self.failUnless((subj,URIRef(dcterms+"mediator"),RDFDatabankConfig.endpointadminuser) in rdfgraph, 'dcterms:mediator') + + #manager user of this silo - Create a new dataset, check response + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointmanageruser, + endpointpass=RDFDatabankConfig.endpointmanagerpass) + fields = \ + [ ("id", "TestSubmission3"), + ('embargoed', 'True'), + ('embargoed_until', d) + ] + files =[] + (reqtype, reqdata) = SparqlQueryTestCase.encode_multipart_formdata(fields, files) + (resp,respdata) = self.doHTTP_POST( + reqdata, reqtype, + resource="datasets/TestSubmission3", + expect_status=201, expect_reason="Created") + LHobtained = resp.getheader('Content-Location', None) + LHexpected = "%sdatasets/TestSubmission3"%self._endpointpath + self.assertEquals(LHobtained, LHexpected, 'Content-Location not correct') + # Access dataset, check response + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointsubmitteruser, + endpointpass=RDFDatabankConfig.endpointsubmitterpass) + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission3", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + subj = URIRef(self.getManifestUri("datasets/TestSubmission3")) + self.assertEqual(len(rdfgraph),10,'Graph length %i' %len(rdfgraph)) + self.failUnless((subj,URIRef(dcterms+"mediator"),RDFDatabankConfig.endpointmanageruser) in rdfgraph, 'dcterms:mediator') + + #General user - Create a new dataset, check response + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointgeneraluser, + endpointpass=RDFDatabankConfig.endpointgeneralpass) + fields = \ + [ ("id", "TestSubmission4") + ] + files =[] + (reqtype, reqdata) = SparqlQueryTestCase.encode_multipart_formdata(fields, files) + (resp,respdata) = self.doHTTP_POST( + reqdata, reqtype, + resource="datasets/TestSubmission4", + expect_status=401, expect_reason="Unauthorized", expect_type="text/plain") + # Access dataset, check response + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointsubmitteruser, + endpointpass=RDFDatabankConfig.endpointsubmitterpass) + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission4", + expect_status=404, expect_reason="Not Found", expect_type="application/rdf+xml") + + #Admin user of another silo - Create a new dataset, check response + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointadminuser3, + endpointpass=RDFDatabankConfig.endpointadminpass3) + fields = \ + [ ("id", "TestSubmission4") + ] + files =[] + (reqtype, reqdata) = SparqlQueryTestCase.encode_multipart_formdata(fields, files) + (resp,respdata) = self.doHTTP_POST( + reqdata, reqtype, + resource="datasets/TestSubmission4", + expect_status=403, expect_reason="Forbidden") + # Access dataset, check response + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointsubmitteruser, + endpointpass=RDFDatabankConfig.endpointsubmitterpass) + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission4", + expect_status=404, expect_reason="Not Found", expect_type="application/rdf+xml") + + #manager user of another silo - Create a new dataset, check response + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointmanageruser3, + endpointpass=RDFDatabankConfig.endpointmanagerpass3) + fields = \ + [ ("id", "TestSubmission4") + ] + files =[] + (reqtype, reqdata) = SparqlQueryTestCase.encode_multipart_formdata(fields, files) + (resp,respdata) = self.doHTTP_POST( + reqdata, reqtype, + resource="datasets/TestSubmission4", + expect_status=403, expect_reason="Forbidden") + # Access dataset, check response + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointsubmitteruser, + endpointpass=RDFDatabankConfig.endpointsubmitterpass) + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission4", + expect_status=404, expect_reason="Not Found", expect_type="application/rdf+xml") + + #submitter user of another silo - Create a new dataset, check response + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointsubmitteruser3, + endpointpass=RDFDatabankConfig.endpointsubmitterpass3) + fields = \ + [ ("id", "TestSubmission4") + ] + files =[] + (reqtype, reqdata) = SparqlQueryTestCase.encode_multipart_formdata(fields, files) + (resp,respdata) = self.doHTTP_POST( + reqdata, reqtype, + resource="datasets/TestSubmission4", + expect_status=403, expect_reason="Forbidden") + # Access dataset, check response + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointsubmitteruser, + endpointpass=RDFDatabankConfig.endpointsubmitterpass) + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission4", + expect_status=404, expect_reason="Not Found", expect_type="application/rdf+xml") + + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointadminuser, + endpointpass=RDFDatabankConfig.endpointadminpass) + resp = self.doHTTP_DELETE( + resource="datasets/TestSubmission2", + expect_status=200, expect_reason="*") + resp = self.doHTTP_DELETE( + resource="datasets/TestSubmission3", + expect_status=200, expect_reason="*") + + + + def testDatasetRecreation(self): + """Create dataset - POST existing id to /silo_name""" + # Create a new dataset, check response + self.createSubmissionDataset() + # Access dataset, check response + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + self.assertEqual(len(rdfgraph),10,'Graph length %i' %len(rdfgraph)) + #Recreate the dataset, check response + fields = \ + [ ("id", "TestSubmission") + ] + files =[] + (reqtype, reqdata) = SparqlQueryTestCase.encode_multipart_formdata(fields, files) + (resp,respdata) = self.doHTTP_POST( + reqdata, reqtype, + resource="datasets", + expect_status=409, expect_reason="Conflict: Dataset Already Exists") + #Recreate the dataset, check response + fields = [] + files =[] + (reqtype, reqdata) = SparqlQueryTestCase.encode_multipart_formdata(fields, files) + (resp,respdata) = self.doHTTP_POST( + reqdata, reqtype, + resource="datasets/TestSubmission", + expect_status=400, expect_reason="Bad request") + + #Admin user of this silo + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointadminuser, + endpointpass=RDFDatabankConfig.endpointadminpass) + #Recreate the dataset, check response + fields = \ + [ ("id", "TestSubmission") + ] + files =[] + (reqtype, reqdata) = SparqlQueryTestCase.encode_multipart_formdata(fields, files) + (resp,respdata) = self.doHTTP_POST( + reqdata, reqtype, + resource="datasets", + expect_status=409, expect_reason="Conflict: Dataset Already Exists") + #Recreate the dataset, check response + fields = [] + files =[] + (reqtype, reqdata) = SparqlQueryTestCase.encode_multipart_formdata(fields, files) + (resp,respdata) = self.doHTTP_POST( + reqdata, reqtype, + resource="datasets/TestSubmission", + expect_status=400, expect_reason="Bad request") + + #manager user of this silo + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointmanageruser, + endpointpass=RDFDatabankConfig.endpointmanagerpass) + #Recreate the dataset, check response + fields = \ + [ ("id", "TestSubmission") + ] + files =[] + (reqtype, reqdata) = SparqlQueryTestCase.encode_multipart_formdata(fields, files) + (resp,respdata) = self.doHTTP_POST( + reqdata, reqtype, + resource="datasets", + expect_status=409, expect_reason="Conflict: Dataset Already Exists") + #Recreate the dataset, check response + fields = [] + files =[] + (reqtype, reqdata) = SparqlQueryTestCase.encode_multipart_formdata(fields, files) + (resp,respdata) = self.doHTTP_POST( + reqdata, reqtype, + resource="datasets/TestSubmission", + expect_status=400, expect_reason="Bad request") + + #submitter user of this silo + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointsubmitteruser2, + endpointpass=RDFDatabankConfig.endpointsubmitterpass2) + #Recreate the dataset, check response + fields = \ + [ ("id", "TestSubmission") + ] + files =[] + (reqtype, reqdata) = SparqlQueryTestCase.encode_multipart_formdata(fields, files) + (resp,respdata) = self.doHTTP_POST( + reqdata, reqtype, + resource="datasets", + expect_status=409, expect_reason="Conflict: Dataset Already Exists") + #Recreate the dataset, check response + fields = [] + files =[] + (reqtype, reqdata) = SparqlQueryTestCase.encode_multipart_formdata(fields, files) + (resp,respdata) = self.doHTTP_POST( + reqdata, reqtype, + resource="datasets/TestSubmission", + expect_status=400, expect_reason="Bad request") + + #General user + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointgeneraluser, + endpointpass=RDFDatabankConfig.endpointgeneralpass) + #Recreate the dataset, check response + fields = \ + [ ("id", "TestSubmission") + ] + files =[] + (reqtype, reqdata) = SparqlQueryTestCase.encode_multipart_formdata(fields, files) + (resp,respdata) = self.doHTTP_POST( + reqdata, reqtype, + resource="datasets", + expect_status=401, expect_reason="Unauthorized", expect_type="text/plain") + #Recreate the dataset, check response + fields = [] + files =[] + (reqtype, reqdata) = SparqlQueryTestCase.encode_multipart_formdata(fields, files) + (resp,respdata) = self.doHTTP_POST( + reqdata, reqtype, + resource="datasets/TestSubmission", + expect_status=401, expect_reason="Unauthorized", expect_type="text/plain") + + #Admin user of another silo + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointadminuser3, + endpointpass=RDFDatabankConfig.endpointadminpass3) + #Recreate the dataset, check response + fields = \ + [ ("id", "TestSubmission") + ] + files =[] + (reqtype, reqdata) = SparqlQueryTestCase.encode_multipart_formdata(fields, files) + (resp,respdata) = self.doHTTP_POST( + reqdata, reqtype, + resource="datasets", + expect_status=403, expect_reason="Forbidden") + #Recreate the dataset, check response + fields = [] + files =[] + (reqtype, reqdata) = SparqlQueryTestCase.encode_multipart_formdata(fields, files) + (resp,respdata) = self.doHTTP_POST( + reqdata, reqtype, + resource="datasets/TestSubmission", + expect_status=403, expect_reason="Forbidden") + + #manager user of another silo + data2 = None + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointmanageruser3, + endpointpass=RDFDatabankConfig.endpointmanagerpass3) + #Recreate the dataset, check response + fields = \ + [ ("id", "TestSubmission") + ] + files =[] + (reqtype, reqdata) = SparqlQueryTestCase.encode_multipart_formdata(fields, files) + (resp,respdata) = self.doHTTP_POST( + reqdata, reqtype, + resource="datasets", + expect_status=403, expect_reason="Forbidden") + #Recreate the dataset, check response + fields = [] + files =[] + (reqtype, reqdata) = SparqlQueryTestCase.encode_multipart_formdata(fields, files) + (resp,respdata) = self.doHTTP_POST( + reqdata, reqtype, + resource="datasets/TestSubmission", + expect_status=403, expect_reason="Forbidden") + + #submitter user of another silo + data2 = None + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointsubmitteruser3, + endpointpass=RDFDatabankConfig.endpointsubmitterpass3) + #Recreate the dataset, check response + fields = \ + [ ("id", "TestSubmission") + ] + files =[] + (reqtype, reqdata) = SparqlQueryTestCase.encode_multipart_formdata(fields, files) + (resp,respdata) = self.doHTTP_POST( + reqdata, reqtype, + resource="datasets", + expect_status=403, expect_reason="Forbidden") + #Recreate the dataset, check response + fields = [] + files =[] + (reqtype, reqdata) = SparqlQueryTestCase.encode_multipart_formdata(fields, files) + (resp,respdata) = self.doHTTP_POST( + reqdata, reqtype, + resource="datasets/TestSubmission", + expect_status=403, expect_reason="Forbidden") + + # Access dataset, check response + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + subj = URIRef(self.getManifestUri("datasets/TestSubmission")) + dcterms = "http://purl.org/dc/terms/" + self.assertEqual(len(rdfgraph),10,'Graph length %i' %len(rdfgraph)) + self.failUnless((subj,URIRef(dcterms+"mediator"),RDFDatabankConfig.endpointsubmitteruser) in rdfgraph, 'dcterms:mediator') + + + + def testDeleteDataset(self): + """Delete dataset - DELETE /silo_name/dataset_name""" + # Create a new dataset, check response + self.createSubmissionDataset() + # Access dataset, check response + (resp, respdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/json") + #Delete dataset by users not permitted to + #General user + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointgeneraluser, + endpointpass=RDFDatabankConfig.endpointgeneralpass) + resp = self.doHTTP_DELETE( + resource="datasets/TestSubmission", + expect_status=401, expect_reason="Unauthorized") + #Admin user of another silo + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointadminuser3, + endpointpass=RDFDatabankConfig.endpointadminpass3) + resp = self.doHTTP_DELETE( + resource="datasets/TestSubmission", + expect_status=403, expect_reason="Forbidden") + #manager user of another silo + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointmanageruser3, + endpointpass=RDFDatabankConfig.endpointmanagerpass3) + resp = self.doHTTP_DELETE( + resource="datasets/TestSubmission", + expect_status=403, expect_reason="Forbidden") + #submitter user of another silo + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointsubmitteruser3, + endpointpass=RDFDatabankConfig.endpointsubmitterpass3) + resp = self.doHTTP_DELETE( + resource="datasets/TestSubmission", + expect_status=403, expect_reason="Forbidden") + #Another submitter user of same silo + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointsubmitteruser2, + endpointpass=RDFDatabankConfig.endpointsubmitterpass2) + resp = self.doHTTP_DELETE( + resource="datasets/TestSubmission", + expect_status=403, expect_reason="Forbidden") + + # Delete dataset by submitter, check response + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointsubmitteruser, + endpointpass=RDFDatabankConfig.endpointsubmitterpass) + resp = self.doHTTP_DELETE( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK") + # Access dataset, test response indicating non-existent + (resp, respdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=404, expect_reason="Not Found") + + # Create a new dataset, check response + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointsubmitteruser, + endpointpass=RDFDatabankConfig.endpointsubmitterpass) + self.createSubmissionDataset() + # Access dataset, check response + (resp, respdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/json") + # Delete dataset by admin, check response + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointadminuser, + endpointpass=RDFDatabankConfig.endpointadminpass) + resp = self.doHTTP_DELETE( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK") + # Access dataset, test response indicating non-existent + (resp, respdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=404, expect_reason="Not Found") + + # Create a new dataset, check response + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointsubmitteruser, + endpointpass=RDFDatabankConfig.endpointsubmitterpass) + self.createSubmissionDataset() + # Access dataset, check response + (resp, respdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/json") + # Delete dataset by manager, check response + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointmanageruser, + endpointpass=RDFDatabankConfig.endpointmanagerpass) + resp = self.doHTTP_DELETE( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK") + # Access dataset, test response indicating non-existent + (resp, respdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=404, expect_reason="Not Found") + + + + def testDatasetNaming(self): + """Create dataset - POST to /silo_name/dataset_name. If name is valid, return 201, else return 403""" + names = [("TestSubmission-1", 201, "Created") + ,("TestSubmission_2", 201, "Created") + ,("TestSubmission:3", 201, "Created") + ,("TestSubmission*4", 403, "Forbidden") + ,("TestSubmission/5", 404, "Not Found") + ,("TestSubmission\6", 403, "Forbidden") + ,("TestSubmission,7", 403, "Forbidden") + ,("TestSubmission&8", 403, "Forbidden") + ,("TestSubmission.9", 403, "Forbidden") + ,("""Test"Submission""", 403, "Forbidden") + ,("Test'Submission", 403, "Forbidden") + #,("""Test Submission""", 403, "Forbidden") #The name is truncated to Test and dataset is created. This does not happen when using the form + ,("TestSubmission$", 403, "Forbidden") + ,("T", 403, "Forbidden") + ] + names = [("TestSubmission-1", 201, "Created") + ,("TestSubmission_2", 201, "Created") + ,("TestSubmission:3", 201, "Created") + ,("TestSubmission*4", 400, "Bad request. Dataset name not valid") + ,("TestSubmission/5", 404, "Not Found") + ,("TestSubmission\6", 400, "Bad request. Dataset name not valid") + ,("TestSubmission,7", 400, "Bad request. Dataset name not valid") + ,("TestSubmission&8", 400, "Bad request. Dataset name not valid") + ,("TestSubmission.9", 400, "Bad request. Dataset name not valid") + ,("""Test"Submission""", 400, "Bad request. Dataset name not valid") + ,("Test'Submission", 400, "Bad request. Dataset name not valid") + #,("""Test Submission""", 403, "Forbidden") #The name is truncated to Test and dataset is created. This does not happen when using the form + ,("TestSubmission$", 400, "Bad request. Dataset name not valid") + ,("T", 400, "Bad request. Dataset name not valid") + ] + fields = [] + files =[] + (reqtype, reqdata) = SparqlQueryTestCase.encode_multipart_formdata(fields, files) + for name, status, reason in names: + #Create a new dataset, check response + (resp,respdata) = self.doHTTP_POST( + reqdata, reqtype, + resource="datasets/%s"%name, + expect_status=status, expect_reason=reason) + # Access dataset, check response + if status == 201: + LHobtained = urllib.unquote(resp.getheader('Content-Location', None)) + LHexpected = "%sdatasets/%s"%(self._endpointpath, name) + self.assertEquals(LHobtained, LHexpected, 'Content-Location not correct') + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/%s"%name, + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + self.assertEqual(len(rdfgraph),10,'Graph length %i' %len(rdfgraph)) + elif status == 403 or status == 400: + (resp, respdata) = self.doHTTP_GET( + resource="datasets/%s"%name, + expect_status=404, expect_reason="Not Found") + #Delete Datasets + for name, status, reason in names: + if not status == 201: + continue + resp = self.doHTTP_DELETE( + resource="datasets/%s"%name, + expect_status=200, expect_reason="OK") + + + + def testDatasetStateInformation(self): + """Get state information of dataset - GET /silo_name/states/dataset_name.""" + # Create a new dataset by submitter, check response + d = self.createSubmissionDataset() + # Access state info + (resp, data) = self.doHTTP_GET( + resource="states/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/json") + state = data['state'] + parts = data['parts'] + self.assertEqual(len(state.keys()), 11, "States") + self.assertEqual(state['item_id'], "TestSubmission", "Submission item identifier") + self.assertEqual(len(state['versions']), 1, "Initially one version") + self.assertEqual(state['versions'][0], '0', "Version 0") + self.assertEqual(state['currentversion'], '0', "Current version == 0") + self.assertEqual(state['rdffileformat'], 'xml', "RDF file type") + self.assertEqual(state['rdffilename'], 'manifest.rdf', "RDF file name") + self.assertEqual(state['files']['0'], ['manifest.rdf'], "List should contain just manifest.rdf") + self.assertEqual(len(state['metadata_files']['0']), 0, "metadata_files of version 0") + self.assertEqual(len(state['subdir']['0']), 0, "Subdirectory count for version 0") + self.assertEqual(state['metadata']['createdby'], RDFDatabankConfig.endpointsubmitteruser, "Created by") + self.assertEqual(state['metadata']['embargoed'], True, "Embargoed?") + self.assertEqual(state['metadata']['embargoed_until'], d, "Embargoed until?") + # date + # version_dates + self.assertEqual(len(parts.keys()), 3, "Parts") + self.assertEqual(len(parts['4=TestSubmission'].keys()), 13, "File stats for 4=TestSubmission") + self.assertEqual(len(parts['manifest.rdf'].keys()), 13, "File stats for manifest.rdf") + + # Access state info by admin + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointadminuser, + endpointpass=RDFDatabankConfig.endpointadminpass) + (resp, data2) = self.doHTTP_GET( + resource="states/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/json") + self.assertEqual(data, data2, "State info accesses by admin is different from submitter") + + # Access state info by manager + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointmanageruser, + endpointpass=RDFDatabankConfig.endpointmanagerpass) + (resp, data2) = self.doHTTP_GET( + resource="states/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/json") + self.assertEqual(data, data2, "State info accesses by manager is different from submitter") + + # Access state info by general user + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointgeneraluser, + endpointpass=RDFDatabankConfig.endpointgeneralpass) + (resp, data2) = self.doHTTP_GET( + resource="states/TestSubmission", + expect_status=401, expect_reason="Unauthorized", expect_type="text/plain") + + # Access state info by submitter2 + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointsubmitteruser2, + endpointpass=RDFDatabankConfig.endpointsubmitterpass2) + (resp, data2) = self.doHTTP_GET( + resource="states/TestSubmission", + expect_status=403, expect_reason="Forbidden", expect_type="text/plain") + + # Access state info by admin3 + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointadminuser3, + endpointpass=RDFDatabankConfig.endpointadminpass3) + (resp, data2) = self.doHTTP_GET( + resource="states/TestSubmission", + expect_status=403, expect_reason="Forbidden", expect_type="text/plain") + + # Access state info by manager3 + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointmanageruser3, + endpointpass=RDFDatabankConfig.endpointmanagerpass3) + (resp, data2) = self.doHTTP_GET( + resource="states/TestSubmission", + expect_status=403, expect_reason="Forbidden", expect_type="text/plain") + + # Access state info by submitter3 + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointsubmitteruser3, + endpointpass=RDFDatabankConfig.endpointsubmitterpass3) + (resp, data2) = self.doHTTP_GET( + resource="states/TestSubmission", + expect_status=403, expect_reason="Forbidden", expect_type="text/plain") + + + + def testFileUpload(self): + """Upload file to dataset - POST file to /silo_name/datasets/dataset_name""" + # Create a new dataset, check response + d = self.createSubmissionDataset() + #Access state information + (resp, respdata) = self.doHTTP_GET( + resource="states/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/json") + # Upload zip file, check response + zipdata = self.uploadSubmissionZipfile() + # Access and check list of contents + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + subj = URIRef(self.getManifestUri("datasets/TestSubmission")) + base = self.getManifestUri("datasets/TestSubmission/") + dcterms = "http://purl.org/dc/terms/" + ore = "http://www.openarchives.org/ore/terms/" + oxds = "http://vocab.ox.ac.uk/dataset/schema#" + stype = URIRef(oxds+"DataSet") + self.assertEqual(len(rdfgraph),12,'Graph length %i' %len(rdfgraph)) + self.failUnless((subj,RDF.type,stype) in rdfgraph, 'Testing submission type: '+subj+", "+stype) + self.failUnless((subj,URIRef(dcterms+"created"),None) in rdfgraph, 'dcterms:created') + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"testdir.zip")) in rdfgraph, 'ore:aggregates testdir.zip') + self.failUnless((subj,URIRef(dcterms+"identifier"),None) in rdfgraph, 'dcterms:identifier') + self.failUnless((subj,URIRef(dcterms+"mediator"),None) in rdfgraph, 'dcterms:mediator') + self.failUnless((subj,URIRef(dcterms+"rights"),None) in rdfgraph, 'dcterms:rights') + self.failUnless((subj,URIRef(dcterms+"license"),None) in rdfgraph, 'dcterms:license') + self.failUnless((subj,URIRef(dcterms+"publisher"),None) in rdfgraph, 'dcterms:publisher') + self.failUnless((subj,URIRef(oxds+"isEmbargoed"),'True') in rdfgraph, 'oxds:isEmbargoed') + self.failUnless((subj,URIRef(oxds+"embargoedUntil"),d) in rdfgraph, 'oxds:embargoedUntil') + self.failUnless((subj,URIRef(oxds+"currentVersion"),'1') in rdfgraph, 'oxds:currentVersion') + self.failUnless((subj,URIRef(dcterms+"modified"),None) in rdfgraph, 'dcterms:modified') + # Access and check zip file content + (resp, zipfile) = self.doHTTP_GET( + resource="datasets/TestSubmission/testdir.zip", + expect_status=200, expect_reason="OK", expect_type="application/zip") + self.assertEqual(zipdata, zipfile, "Difference between local and remote zipfile!") + #Access state information and check + (resp, data) = self.doHTTP_GET( + resource="states/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/json") + state = data['state'] + parts = data['parts'] + self.assertEqual(len(state.keys()), 11, "States") + self.assertEqual(state['item_id'], "TestSubmission", "Submission item identifier") + self.assertEqual(len(state['versions']), 2, "Two versions") + self.assertEqual(state['versions'][0], '0', "Version 0") + self.assertEqual(state['versions'][1], '1', "Version 1") + self.assertEqual(state['currentversion'], '1', "Current version == 1") + self.assertEqual(state['rdffileformat'], 'xml', "RDF file type") + self.assertEqual(state['rdffilename'], 'manifest.rdf', "RDF file name") + self.assertEqual(state['files']['0'], ['manifest.rdf'], "List should contain just manifest.rdf") + self.assertEqual(len(state['files']['1']), 2, "List should contain manifest.rdf and testdir.zip") + self.assertEqual(len(state['metadata_files']['0']), 0, "metadata_files of version 0") + self.assertEqual(len(state['metadata_files']['1']), 0, "metadata_files of version 1") + self.assertEqual(len(state['subdir']['0']), 0, "Subdirectory count for version 0") + self.assertEqual(len(state['subdir']['1']), 0, "Subdirectory count for version 1") + self.assertEqual(state['metadata']['createdby'], RDFDatabankConfig.endpointsubmitteruser, "Created by") + self.assertEqual(state['metadata']['embargoed'], True, "Embargoed?") + self.assertEqual(len(parts.keys()), 4, "Parts") + self.assertEqual(len(parts['4=TestSubmission'].keys()), 13, "File stats for 4=TestSubmission") + self.assertEqual(len(parts['manifest.rdf'].keys()), 13, "File stats for manifest.rdf") + self.assertEqual(len(parts['testdir.zip'].keys()), 13, "File stats for testdir.zip") + + # Upload zip file by admin, check response + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointadminuser, + endpointpass=RDFDatabankConfig.endpointadminpass) + fields = [] + zipdata = open("testdata/testdir2.zip").read() + files = \ + [ ("file", 'testdir2.zip', zipdata, "application/zip") + ] + (reqtype, reqdata) = SparqlQueryTestCase.encode_multipart_formdata(fields, files) + (resp,respdata) = self.doHTTP_POST( + reqdata, reqtype, + resource="datasets/TestSubmission/", + expect_status=201, expect_reason="Created") + LHobtained = resp.getheader('Content-Location', None) + LHexpected = "%sdatasets/TestSubmission/testdir2.zip"%self._endpointpath + self.assertEquals(LHobtained, LHexpected, 'Content-Location not correct') + # Access and check zip file content + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointsubmitteruser, + endpointpass=RDFDatabankConfig.endpointsubmitterpass) + (resp, zipfile) = self.doHTTP_GET( + resource="datasets/TestSubmission/testdir2.zip", + expect_status=200, expect_reason="OK", expect_type="application/zip") + self.assertEqual(zipdata, zipfile, "Difference between local and remote zipfile!") + + # Upload zip file by manager, check response + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointmanageruser, + endpointpass=RDFDatabankConfig.endpointmanagerpass) + fields = [] + zipdata = open("testdata/testrdf.zip").read() + files = \ + [ ("file", 'testrdf.zip', zipdata, "application/zip") + ] + (reqtype, reqdata) = SparqlQueryTestCase.encode_multipart_formdata(fields, files) + (resp,respdata) = self.doHTTP_POST( + reqdata, reqtype, + resource="datasets/TestSubmission/", + expect_status=201, expect_reason="Created") + LHobtained = resp.getheader('Content-Location', None) + LHexpected = "%sdatasets/TestSubmission/testrdf.zip"%self._endpointpath + self.assertEquals(LHobtained, LHexpected, 'Content-Location not correct') + # Access and check zip file content + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointsubmitteruser, + endpointpass=RDFDatabankConfig.endpointsubmitterpass) + (resp, zipfile) = self.doHTTP_GET( + resource="datasets/TestSubmission/testrdf.zip", + expect_status=200, expect_reason="OK", expect_type="application/zip") + self.assertEqual(zipdata, zipfile, "Difference between local and remote zipfile!") + + #Prepare data to upload + fields = [] + zipdata = open("testdata/testrdf2.zip").read() + files = \ + [ ("file", 'testrdf2.zip', zipdata, "application/zip") + ] + (reqtype, reqdata) = SparqlQueryTestCase.encode_multipart_formdata(fields, files) + + # Upload zip file by submitter2, check response + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointsubmitteruser2, + endpointpass=RDFDatabankConfig.endpointsubmitterpass2) + (resp,respdata) = self.doHTTP_POST( + reqdata, reqtype, + resource="datasets/TestSubmission/", + expect_status=403, expect_reason="Forbidden") + # Access and check zip file content + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointsubmitteruser, + endpointpass=RDFDatabankConfig.endpointsubmitterpass) + (resp, zipfile) = self.doHTTP_GET( + resource="datasets/TestSubmission/testrdf2.zip", + expect_status=404, expect_reason="Not Found", expect_type="application/zip") + + # Upload zip file by general user, check response + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointgeneraluser, + endpointpass=RDFDatabankConfig.endpointgeneralpass) + (resp,respdata) = self.doHTTP_POST( + reqdata, reqtype, + resource="datasets/TestSubmission/", + expect_status=401, expect_reason="Unauthorized", expect_type="text/plain") + # Access and check zip file content + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointsubmitteruser, + endpointpass=RDFDatabankConfig.endpointsubmitterpass) + (resp, zipfile) = self.doHTTP_GET( + resource="datasets/TestSubmission/testrdf2.zip", + expect_status=404, expect_reason="Not Found", expect_type="application/zip") + + # Upload zip file by admin user 3, check response + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointadminuser3, + endpointpass=RDFDatabankConfig.endpointadminpass3) + (resp,respdata) = self.doHTTP_POST( + reqdata, reqtype, + resource="datasets/TestSubmission/", + expect_status=403, expect_reason="Forbidden") + # Access and check zip file content + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointsubmitteruser, + endpointpass=RDFDatabankConfig.endpointsubmitterpass) + (resp, zipfile) = self.doHTTP_GET( + resource="datasets/TestSubmission/testrdf2.zip", + expect_status=404, expect_reason="Not Found", expect_type="application/zip") + + # Upload zip file by manager user 3, check response + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointmanageruser3, + endpointpass=RDFDatabankConfig.endpointmanagerpass3) + (resp,respdata) = self.doHTTP_POST( + reqdata, reqtype, + resource="datasets/TestSubmission/", + expect_status=403, expect_reason="Forbidden") + # Access and check zip file content + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointsubmitteruser, + endpointpass=RDFDatabankConfig.endpointsubmitterpass) + (resp, zipfile) = self.doHTTP_GET( + resource="datasets/TestSubmission/testrdf2.zip", + expect_status=404, expect_reason="Not Found", expect_type="application/zip") + + # Upload zip file by submitter user 3, check response + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointsubmitteruser3, + endpointpass=RDFDatabankConfig.endpointsubmitterpass3) + (resp,respdata) = self.doHTTP_POST( + reqdata, reqtype, + resource="datasets/TestSubmission/", + expect_status=403, expect_reason="Forbidden") + # Access and check zip file content + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointsubmitteruser, + endpointpass=RDFDatabankConfig.endpointsubmitterpass) + (resp, zipfile) = self.doHTTP_GET( + resource="datasets/TestSubmission/testrdf2.zip", + expect_status=404, expect_reason="Not Found", expect_type="application/zip") + + + + def testFileDelete(self): + """Delete file in dataset - DELETE /silo_name/datasets/dataset_name/file_name""" + # Create a new dataset, check response + d = self.createSubmissionDataset() + # Upload zip file, check response + zipdata = self.uploadSubmissionZipfile() + # Access and check list of contents + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + subj = URIRef(self.getManifestUri("datasets/TestSubmission")) + oxds = "http://vocab.ox.ac.uk/dataset/schema#" + self.assertEqual(len(rdfgraph),12,'Graph length %i' %len(rdfgraph)) + self.failUnless((subj,URIRef(oxds+"currentVersion"),"1") in rdfgraph, 'oxds:currentVersion') + # Access and check zip file content and version + (resp, zipfile) = self.doHTTP_GET( + resource="datasets/TestSubmission/testdir.zip", + expect_status=200, expect_reason="OK", expect_type="application/zip") + self.assertEqual(zipdata, zipfile, "Difference between local and remote zipfile!") + # Delete file, check response + resp = self.doHTTP_DELETE( + resource="datasets/TestSubmission/testdir.zip", + expect_status=200, expect_reason="OK") + # Access and check zip file does not exist + (resp, zipfile) = self.doHTTP_GET( + resource="datasets/TestSubmission/testdir.zip", + expect_status=404, expect_reason="Not Found") + # Access and check list of contents + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + subj = URIRef(self.getManifestUri("datasets/TestSubmission")) + dcterms = "http://purl.org/dc/terms/" + ore = "http://www.openarchives.org/ore/terms/" + oxds = "http://vocab.ox.ac.uk/dataset/schema#" + stype = URIRef(oxds+"DataSet") + self.assertEqual(len(rdfgraph),11,'Graph length %i' %len(rdfgraph)) + self.failUnless((subj,RDF.type,stype) in rdfgraph, 'Testing submission type: '+subj+", "+stype) + self.failUnless((subj,URIRef(dcterms+"created"),None) in rdfgraph, 'dcterms:created') + self.failUnless((subj,URIRef(dcterms+"identifier"),None) in rdfgraph, 'dcterms:identifier') + self.failUnless((subj,URIRef(dcterms+"mediator"),None) in rdfgraph, 'dcterms:mediator') + self.failUnless((subj,URIRef(dcterms+"rights"),None) in rdfgraph, 'dcterms:rights') + self.failUnless((subj,URIRef(dcterms+"license"),None) in rdfgraph, 'dcterms:license') + self.failUnless((subj,URIRef(dcterms+"publisher"),None) in rdfgraph, 'dcterms:publisher') + self.failUnless((subj,URIRef(oxds+"isEmbargoed"),'True') in rdfgraph, 'oxds:isEmbargoed') + self.failUnless((subj,URIRef(oxds+"embargoedUntil"),d) in rdfgraph, 'oxds:embargoedUntil') + self.failUnless((subj,URIRef(oxds+"currentVersion"),'2') in rdfgraph, 'oxds:currentVersion') + self.failUnless((subj,URIRef(dcterms+"modified"),None) in rdfgraph, 'dcterms:modified') + #Access state information and check + (resp, data) = self.doHTTP_GET( + resource="states/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/json") + state = data['state'] + parts = data['parts'] + self.assertEqual(len(state.keys()), 11, "States") + self.assertEqual(state['item_id'], "TestSubmission", "Submission item identifier") + self.assertEqual(len(state['versions']), 3, "Three versions") + self.assertEqual(state['versions'][0], '0', "Version 0") + self.assertEqual(state['versions'][1], '1', "Version 1") + self.assertEqual(state['versions'][2], '2', "Version 2") + self.assertEqual(state['currentversion'], '2', "Current version == 2") + self.assertEqual(state['rdffileformat'], 'xml', "RDF file type") + self.assertEqual(state['rdffilename'], 'manifest.rdf', "RDF file name") + self.assertEqual(len(state['files']['0']), 1, "List should contain just manifest.rdf") + self.assertEqual(len(state['files']['1']), 2, "List should contain manifest.rdf and testdir.zip") + self.assertEqual(len(state['files']['2']), 1, "List should contain just manifest.rdf") + self.assertEqual(len(state['metadata_files']['0']), 0, "metadata_files of version 0") + self.assertEqual(len(state['metadata_files']['1']), 0, "metadata_files of version 1") + self.assertEqual(len(state['metadata_files']['2']), 0, "metadata_files of version 2") + self.assertEqual(len(state['subdir']['0']), 0, "Subdirectory count for version 0") + self.assertEqual(len(state['subdir']['1']), 0, "Subdirectory count for version 1") + self.assertEqual(len(state['subdir']['2']), 0, "Subdirectory count for version 2") + self.assertEqual(state['metadata']['createdby'], RDFDatabankConfig.endpointsubmitteruser, "Created by") + self.assertEqual(state['metadata']['embargoed'], True, "Embargoed?") + self.assertEqual(len(parts.keys()), 3, "Parts") + self.assertEqual(len(parts['4=TestSubmission'].keys()), 13, "File stats for 4=TestSubmission") + self.assertEqual(len(parts['manifest.rdf'].keys()), 13, "File stats for manifest.rdf") + + # Upload zip file, check response + zipdata = self.uploadSubmissionZipfile() + # Access and check zip file content and version + (resp, zipfile) = self.doHTTP_GET( + resource="datasets/TestSubmission/testdir.zip", + expect_status=200, expect_reason="OK", expect_type="application/zip") + self.assertEqual(zipdata, zipfile, "Difference between local and remote zipfile!") + # Delete file by admin, check response + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointadminuser, + endpointpass=RDFDatabankConfig.endpointadminpass) + resp = self.doHTTP_DELETE( + resource="datasets/TestSubmission/testdir.zip", + expect_status=200, expect_reason="OK") + # Access and check zip file does not exist + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointsubmitteruser, + endpointpass=RDFDatabankConfig.endpointsubmitterpass) + (resp, zipfile) = self.doHTTP_GET( + resource="datasets/TestSubmission/testdir.zip", + expect_status=404, expect_reason="Not Found") + + # Upload zip file, check response + zipdata = self.uploadSubmissionZipfile() + # Access and check zip file content and version + (resp, zipfile) = self.doHTTP_GET( + resource="datasets/TestSubmission/testdir.zip", + expect_status=200, expect_reason="OK", expect_type="application/zip") + self.assertEqual(zipdata, zipfile, "Difference between local and remote zipfile!") + # Delete file by manager, check response + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointmanageruser, + endpointpass=RDFDatabankConfig.endpointmanagerpass) + resp = self.doHTTP_DELETE( + resource="datasets/TestSubmission/testdir.zip", + expect_status=200, expect_reason="OK") + # Access and check zip file does not exist + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointsubmitteruser, + endpointpass=RDFDatabankConfig.endpointsubmitterpass) + (resp, zipfile) = self.doHTTP_GET( + resource="datasets/TestSubmission/testdir.zip", + expect_status=404, expect_reason="Not Found") + + # Upload zip file, check response + zipdata = self.uploadSubmissionZipfile() + # Access and check zip file content and version + (resp, zipfile) = self.doHTTP_GET( + resource="datasets/TestSubmission/testdir.zip", + expect_status=200, expect_reason="OK", expect_type="application/zip") + self.assertEqual(zipdata, zipfile, "Difference between local and remote zipfile!") + + # Delete file by submitter2, check response + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointsubmitteruser2, + endpointpass=RDFDatabankConfig.endpointsubmitterpass2) + resp = self.doHTTP_DELETE( + resource="datasets/TestSubmission/testdir.zip", + expect_status=403, expect_reason="Forbidden") + # Access and check zip file does not exist + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointsubmitteruser, + endpointpass=RDFDatabankConfig.endpointsubmitterpass) + (resp, zipfile) = self.doHTTP_GET( + resource="datasets/TestSubmission/testdir.zip", + expect_status=200, expect_reason="OK") + + # Delete file by general user, check response + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointgeneraluser, + endpointpass=RDFDatabankConfig.endpointgeneralpass) + resp = self.doHTTP_DELETE( + resource="datasets/TestSubmission/testdir.zip", + expect_status=401, expect_reason="Unauthorized") + # Access and check zip file does not exist + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointsubmitteruser, + endpointpass=RDFDatabankConfig.endpointsubmitterpass) + (resp, zipfile) = self.doHTTP_GET( + resource="datasets/TestSubmission/testdir.zip", + expect_status=200, expect_reason="OK") + + # Delete file by admin user 3, check response + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointadminuser3, + endpointpass=RDFDatabankConfig.endpointadminpass3) + resp = self.doHTTP_DELETE( + resource="datasets/TestSubmission/testdir.zip", + expect_status=403, expect_reason="Forbidden") + # Access and check zip file does not exist + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointsubmitteruser, + endpointpass=RDFDatabankConfig.endpointsubmitterpass) + (resp, zipfile) = self.doHTTP_GET( + resource="datasets/TestSubmission/testdir.zip", + expect_status=200, expect_reason="OK") + + # Delete file by manager user 3, check response + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointmanageruser3, + endpointpass=RDFDatabankConfig.endpointmanagerpass3) + resp = self.doHTTP_DELETE( + resource="datasets/TestSubmission/testdir.zip", + expect_status=403, expect_reason="Forbidden") + # Access and check zip file does not exist + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointsubmitteruser, + endpointpass=RDFDatabankConfig.endpointsubmitterpass) + (resp, zipfile) = self.doHTTP_GET( + resource="datasets/TestSubmission/testdir.zip", + expect_status=200, expect_reason="OK") + + # Delete file by submitter user 3, check response + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointsubmitteruser3, + endpointpass=RDFDatabankConfig.endpointsubmitterpass3) + resp = self.doHTTP_DELETE( + resource="datasets/TestSubmission/testdir.zip", + expect_status=403, expect_reason="Forbidden") + # Access and check zip file does not exist + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointsubmitteruser, + endpointpass=RDFDatabankConfig.endpointsubmitterpass) + (resp, zipfile) = self.doHTTP_GET( + resource="datasets/TestSubmission/testdir.zip", + expect_status=200, expect_reason="OK") + + + + def testFileUpdate(self): + """Update file in dataset - POST file to /silo_name/datasets/dataset_name (x 2)""" + # Create a new dataset, check response + d = self.createSubmissionDataset() + # Upload zip file, check response (uploads the file testdir.zip) + zipdata = self.uploadSubmissionZipfile() + # Access and check list of contents + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + subj = URIRef(self.getManifestUri("datasets/TestSubmission")) + oxds = "http://vocab.ox.ac.uk/dataset/schema#" + self.assertEqual(len(rdfgraph),12,'Graph length %i' %len(rdfgraph)) + self.failUnless((subj,URIRef(oxds+"currentVersion"),'1') in rdfgraph, 'oxds:currentVersion') + # Access and check zip file content and version + (resp, zipfile) = self.doHTTP_GET( + resource="datasets/TestSubmission/testdir.zip", + expect_status=200, expect_reason="OK", expect_type="application/zip") + self.assertEqual(zipdata, zipfile, "Difference between local and remote zipfile!") + # Upload zip file again, check response + zipdata = self.updateSubmissionZipfile(file_to_upload="testdir2.zip", filename="testdir.zip") + # Access and check list of contents + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + subj = URIRef(self.getManifestUri("datasets/TestSubmission")) + base = self.getManifestUri("datasets/TestSubmission/") + dcterms = "http://purl.org/dc/terms/" + ore = "http://www.openarchives.org/ore/terms/" + oxds = "http://vocab.ox.ac.uk/dataset/schema#" + stype = URIRef(oxds+"DataSet") + self.assertEqual(len(rdfgraph),12,'Graph length %i' %len(rdfgraph)) + self.failUnless((subj,RDF.type,stype) in rdfgraph, 'Testing submission type: '+subj+", "+stype) + self.failUnless((subj,URIRef(dcterms+"created"),None) in rdfgraph, 'dcterms:created') + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"testdir.zip")) in rdfgraph, 'ore:aggregates testdir.zip') + self.failUnless((subj,URIRef(dcterms+"identifier"),None) in rdfgraph, 'dcterms:identifier') + self.failUnless((subj,URIRef(dcterms+"mediator"),None) in rdfgraph, 'dcterms:mediator') + self.failUnless((subj,URIRef(dcterms+"rights"),None) in rdfgraph, 'dcterms:rights') + self.failUnless((subj,URIRef(dcterms+"license"),None) in rdfgraph, 'dcterms:license') + self.failUnless((subj,URIRef(dcterms+"publisher"),None) in rdfgraph, 'dcterms:publisher') + self.failUnless((subj,URIRef(oxds+"isEmbargoed"),'True') in rdfgraph, 'oxds:isEmbargoed') + self.failUnless((subj,URIRef(oxds+"embargoedUntil"),d) in rdfgraph, 'oxds:embargoedUntil') + self.failUnless((subj,URIRef(oxds+"currentVersion"),'2') in rdfgraph, 'oxds:currentVersion') + self.failUnless((subj,URIRef(dcterms+"modified"),None) in rdfgraph, 'dcterms:modified') + # Access and check zip file content + (resp, zipfile) = self.doHTTP_GET( + resource="datasets/TestSubmission/testdir.zip", + expect_status=200, expect_reason="OK", expect_type="application/zip") + self.assertEqual(zipdata, zipfile, "Difference between local and remote zipfile!") + #Access state information and check + (resp, data) = self.doHTTP_GET( + resource="states/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/json") + state = data['state'] + parts = data['parts'] + self.assertEqual(len(state.keys()), 11, "States") + self.assertEqual(state['item_id'], "TestSubmission", "Submission item identifier") + self.assertEqual(len(state['versions']), 3, "Three versions") + self.assertEqual(state['versions'][0], '0', "Version 0") + self.assertEqual(state['versions'][1], '1', "Version 1") + self.assertEqual(state['versions'][2], '2', "Version 2") + self.assertEqual(state['currentversion'], '2', "Current version == 2") + self.assertEqual(state['rdffileformat'], 'xml', "RDF file type") + self.assertEqual(state['rdffilename'], 'manifest.rdf', "RDF file name") + self.assertEqual(len(state['files']['0']), 1, "List should contain just manifest.rdf") + self.assertEqual(len(state['files']['1']), 2, "List should contain manifest.rdf and testdir.zip") + self.assertEqual(len(state['files']['2']), 2, "List should contain manifest.rdf and testdir.zip") + self.assertEqual(len(state['metadata_files']['0']), 0, "metadata_files of version 0") + self.assertEqual(len(state['metadata_files']['1']), 0, "metadata_files of version 1") + self.assertEqual(len(state['metadata_files']['2']), 0, "metadata_files of version 2") + self.assertEqual(len(state['subdir']['0']), 0, "Subdirectory count for version 0") + self.assertEqual(len(state['subdir']['1']), 0, "Subdirectory count for version 1") + self.assertEqual(len(state['subdir']['2']), 0, "Subdirectory count for version 2") + self.assertEqual(state['metadata']['createdby'], RDFDatabankConfig.endpointsubmitteruser, "Created by") + self.assertEqual(state['metadata']['embargoed'], True, "Embargoed?") + self.assertEqual(len(parts.keys()), 4, "Parts") + self.assertEqual(len(parts['4=TestSubmission'].keys()), 13, "File stats for 4=TestSubmission") + self.assertEqual(len(parts['manifest.rdf'].keys()), 13, "File stats for manifest.rdf") + self.assertEqual(len(parts['testdir.zip'].keys()), 13, "File stats for testdir.zip") + + # Update zip file by admin, check response + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointadminuser, + endpointpass=RDFDatabankConfig.endpointadminpass) + fields = [] + zipdata = open("testdata/testrdf.zip").read() + files = \ + [ ("file", 'testdir.zip', zipdata, "application/zip") + ] + (reqtype, reqdata) = SparqlQueryTestCase.encode_multipart_formdata(fields, files) + (resp,respdata) = self.doHTTP_POST( + reqdata, reqtype, + resource="datasets/TestSubmission/", + expect_status=204, expect_reason="No Content") + # Access and check zip file content + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointsubmitteruser, + endpointpass=RDFDatabankConfig.endpointsubmitterpass) + (resp, zipfile) = self.doHTTP_GET( + resource="datasets/TestSubmission/testdir.zip", + expect_status=200, expect_reason="OK", expect_type="application/zip") + self.assertEqual(zipdata, zipfile, "zipfile not updated") + + # Update zip file by manager, check response + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointmanageruser, + endpointpass=RDFDatabankConfig.endpointmanagerpass) + fields = [] + zipdata = open("testdata/testrdf2.zip").read() + files = \ + [ ("file", 'testdir.zip', zipdata, "application/zip") + ] + (reqtype, reqdata) = SparqlQueryTestCase.encode_multipart_formdata(fields, files) + (resp,respdata) = self.doHTTP_POST( + reqdata, reqtype, + resource="datasets/TestSubmission/", + expect_status=204, expect_reason="No Content") + # Access and check zip file content + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointsubmitteruser, + endpointpass=RDFDatabankConfig.endpointsubmitterpass) + (resp, zipfile) = self.doHTTP_GET( + resource="datasets/TestSubmission/testdir.zip", + expect_status=200, expect_reason="OK", expect_type="application/zip") + self.assertEqual(zipdata, zipfile, "zipfile not updated!") + + #Prepare data to upload + fields = [] + zipdata2 = open("testdata/testrdf3.zip").read() + files = \ + [ ("file", 'testdir.zip', zipdata, "application/zip") + ] + (reqtype, reqdata) = SparqlQueryTestCase.encode_multipart_formdata(fields, files) + # Update zip file by submitter2, check response + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointsubmitteruser2, + endpointpass=RDFDatabankConfig.endpointsubmitterpass2) + (resp,respdata) = self.doHTTP_POST( + reqdata, reqtype, + resource="datasets/TestSubmission/", + expect_status=403, expect_reason="Forbidden") + # Access and check zip file content + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointsubmitteruser, + endpointpass=RDFDatabankConfig.endpointsubmitterpass) + (resp, zipfile) = self.doHTTP_GET( + resource="datasets/TestSubmission/testdir.zip", + expect_status=200, expect_reason="OK", expect_type="application/zip") + self.assertEqual(zipdata, zipfile, "zipfile not updated!") + + # Update zip file by general user, check response + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointgeneraluser, + endpointpass=RDFDatabankConfig.endpointgeneralpass) + (resp,respdata) = self.doHTTP_POST( + reqdata, reqtype, + resource="datasets/TestSubmission/", + expect_status=401, expect_reason="Unauthorized", expect_type="text/plain") + # Access and check zip file content + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointsubmitteruser, + endpointpass=RDFDatabankConfig.endpointsubmitterpass) + (resp, zipfile) = self.doHTTP_GET( + resource="datasets/TestSubmission/testdir.zip", + expect_status=200, expect_reason="OK", expect_type="application/zip") + self.assertEqual(zipdata, zipfile, "zipfile not updated!") + + # Update zip file by admin user 3, check response + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointadminuser3, + endpointpass=RDFDatabankConfig.endpointadminpass3) + (resp,respdata) = self.doHTTP_POST( + reqdata, reqtype, + resource="datasets/TestSubmission/", + expect_status=403, expect_reason="Forbidden") + # Access and check zip file content + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointsubmitteruser, + endpointpass=RDFDatabankConfig.endpointsubmitterpass) + (resp, zipfile) = self.doHTTP_GET( + resource="datasets/TestSubmission/testdir.zip", + expect_status=200, expect_reason="OK", expect_type="application/zip") + self.assertEqual(zipdata, zipfile, "zipfile not updated!") + + # Update zip file by manager user 3, check response + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointmanageruser3, + endpointpass=RDFDatabankConfig.endpointmanagerpass3) + (resp,respdata) = self.doHTTP_POST( + reqdata, reqtype, + resource="datasets/TestSubmission/", + expect_status=403, expect_reason="Forbidden") + # Access and check zip file content + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointsubmitteruser, + endpointpass=RDFDatabankConfig.endpointsubmitterpass) + (resp, zipfile) = self.doHTTP_GET( + resource="datasets/TestSubmission/testdir.zip", + expect_status=200, expect_reason="OK", expect_type="application/zip") + self.assertEqual(zipdata, zipfile, "zipfile not updated!") + + # Update zip file by submitter user 3, check response + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointsubmitteruser3, + endpointpass=RDFDatabankConfig.endpointsubmitterpass3) + (resp,respdata) = self.doHTTP_POST( + reqdata, reqtype, + resource="datasets/TestSubmission/", + expect_status=403, expect_reason="Forbidden") + # Access and check zip file content + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointsubmitteruser, + endpointpass=RDFDatabankConfig.endpointsubmitterpass) + (resp, zipfile) = self.doHTTP_GET( + resource="datasets/TestSubmission/testdir.zip", + expect_status=200, expect_reason="OK", expect_type="application/zip") + self.assertEqual(zipdata, zipfile, "zipfile not updated!") + + + + def testGetDatasetByVersionByURI(self): + """Upload files to a dataset - POST file to /silo_name/datasets/dataset_name. + Access each of the versions and the files in that version""" + #Definitions + subj = URIRef(self.getManifestUri("datasets/TestSubmission")) + base = self.getManifestUri("datasets/TestSubmission/") + dcterms = "http://purl.org/dc/terms/" + ore = "http://www.openarchives.org/ore/terms/" + oxds = "http://vocab.ox.ac.uk/dataset/schema#" + stype = URIRef(oxds+"DataSet") + #---------Version 0 + # Create a new dataset, check response + d = self.createSubmissionDataset() + # Access and check list of contents + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + self.assertEqual(len(rdfgraph),10,'Graph length %i' %len(rdfgraph)) + #Access state information and check + (resp, data) = self.doHTTP_GET( + resource="states/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/json") + state = data['state'] + parts = data['parts'] + self.assertEqual(len(state.keys()), 11, "States") + self.assertEqual(len(parts.keys()), 3, "Parts") + #---------Version 1 + # Upload zip file, check response + zipdata = self.uploadSubmissionZipfile() + # Access and check list of contents + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + self.assertEqual(len(rdfgraph),12,'Graph length %i' %len(rdfgraph)) + # Access and check zip file content + (resp, zipfile) = self.doHTTP_GET( + resource="datasets/TestSubmission/testdir.zip", + expect_status=200, expect_reason="OK", expect_type="application/zip") + self.assertEqual(zipdata, zipfile, "Difference between local and remote zipfile!") + #Access state information and check + (resp, data) = self.doHTTP_GET( + resource="states/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/json") + state = data['state'] + parts = data['parts'] + self.assertEqual(len(state.keys()), 11, "States") + self.assertEqual(len(parts.keys()), 4, "Parts") + # Access and check list of contents of version 0 + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission/version0", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + self.assertEqual(len(rdfgraph),10,'Graph length %i' %len(rdfgraph)) + #---------Version 2 + # Upload zip file, check response + zipdata2 = self.uploadSubmissionZipfile(file_to_upload="testdir2.zip") + # Access and check list of contents + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + self.assertEqual(len(rdfgraph),13,'Graph length %i' %len(rdfgraph)) + # Access and check zip file content + (resp, zipfile) = self.doHTTP_GET( + resource="datasets/TestSubmission/testdir.zip", + expect_status=200, expect_reason="OK", expect_type="application/zip") + self.assertEqual(zipdata, zipfile, "Difference between local and remote zipfile - testdir.zip!") + (resp, zipfile2) = self.doHTTP_GET( + resource="datasets/TestSubmission/testdir2.zip", + expect_status=200, expect_reason="OK", expect_type="application/zip") + self.assertEqual(zipdata2, zipfile2, "Difference between local and remote zipfile - testdir2.zip!") + #Access state information and check + (resp, data) = self.doHTTP_GET( + resource="states/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/json") + state = data['state'] + parts = data['parts'] + self.assertEqual(len(state.keys()), 11, "States") + self.assertEqual(len(parts.keys()), 5, "Parts") + #---------Version 3 + # Delete file, check response + resp = self.doHTTP_DELETE( + resource="datasets/TestSubmission/testdir.zip", + expect_status=200, expect_reason="OK") + # Access and check list of contents + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + self.assertEqual(len(rdfgraph),12,'Graph length %i' %len(rdfgraph)) + # Access and check zip file content + (resp, zipfile) = self.doHTTP_GET( + resource="datasets/TestSubmission/testdir.zip", + expect_status=404, expect_reason="Not Found") + (resp, zipfile2) = self.doHTTP_GET( + resource="datasets/TestSubmission/testdir2.zip", + expect_status=200, expect_reason="OK", expect_type="application/zip") + self.assertEqual(zipdata2, zipfile2, "Difference between local and remote zipfile - testdir2.zip!") + #Access state information and check + (resp, data) = self.doHTTP_GET( + resource="states/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/json") + state = data['state'] + parts = data['parts'] + self.assertEqual(len(state.keys()), 11, "States") + self.assertEqual(len(parts.keys()), 4, "Parts") + #---------Version 4 + # Update zip file, check response + zipdata3 = self.updateSubmissionZipfile(file_to_upload="testrdf4.zip", filename="testdir2.zip") + # Access and check list of contents + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + self.assertEqual(len(rdfgraph),12,'Graph length %i' %len(rdfgraph)) + # Access and check zip file content + (resp, zipfile) = self.doHTTP_GET( + resource="datasets/TestSubmission/testdir.zip", + expect_status=404, expect_reason="Not Found") + (resp, zipfile2) = self.doHTTP_GET( + resource="datasets/TestSubmission/testdir2.zip", + expect_status=200, expect_reason="OK", expect_type="application/zip") + self.assertEqual(zipdata3, zipfile2, "Difference between local and remote zipfile - testdir2.zip!") + #Access state information and check + (resp, data) = self.doHTTP_GET( + resource="states/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/json") + state = data['state'] + parts = data['parts'] + self.assertEqual(len(state.keys()), 11, "States") + self.assertEqual(len(parts.keys()), 4, "Parts") + #=========Access each of the versions + #---------Version 0 + # Access and check list of contents of version 0 + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission/version0", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + self.assertEqual(len(rdfgraph),10,'Graph length %i' %len(rdfgraph)) + self.failUnless((subj,RDF.type,stype) in rdfgraph, 'Testing submission type: '+subj+", "+stype) + self.failUnless((subj,URIRef(dcterms+"created"),None) in rdfgraph, 'dcterms:created') + self.failUnless((subj,URIRef(dcterms+"identifier"),None) in rdfgraph, 'dcterms:identifier') + self.failUnless((subj,URIRef(dcterms+"mediator"),None) in rdfgraph, 'dcterms:mediator') + self.failUnless((subj,URIRef(dcterms+"rights"),None) in rdfgraph, 'dcterms:rights') + self.failUnless((subj,URIRef(dcterms+"license"),None) in rdfgraph, 'dcterms:license') + self.failUnless((subj,URIRef(dcterms+"publisher"),None) in rdfgraph, 'dcterms:publisher') + self.failUnless((subj,URIRef(oxds+"isEmbargoed"),'True') in rdfgraph, 'oxds:isEmbargoed') + self.failUnless((subj,URIRef(oxds+"embargoedUntil"),d) in rdfgraph, 'oxds:embargoedUntil') + self.failUnless((subj,URIRef(oxds+"currentVersion"),'0') in rdfgraph, 'oxds:currentVersion') + #Access state information and check + (resp, data) = self.doHTTP_GET( + resource="states/TestSubmission/version0", + expect_status=200, expect_reason="OK", expect_type="application/json") + state = data['state'] + parts = data['parts'] + self.assertEqual(len(state.keys()), 11, "States") + self.assertEqual(len(parts.keys()), 3, "Parts") + self.assertEqual(len(parts['4=TestSubmission'].keys()), 13, "File stats for 4=TestSubmission") + self.assertEqual(len(parts['manifest.rdf'].keys()), 13, "File stats for manifest.rdf") + #---------Version 1 + # Access and check list of contents of version 1 + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission/version1", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + self.assertEqual(len(rdfgraph),12,'Graph length %i' %len(rdfgraph)) + self.failUnless((subj,RDF.type,stype) in rdfgraph, 'Testing submission type: '+subj+", "+stype) + self.failUnless((subj,URIRef(dcterms+"created"),None) in rdfgraph, 'dcterms:created') + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"testdir.zip")) in rdfgraph, 'ore:aggregates testdir.zip') + self.failUnless((subj,URIRef(dcterms+"identifier"),None) in rdfgraph, 'dcterms:identifier') + self.failUnless((subj,URIRef(dcterms+"mediator"),None) in rdfgraph, 'dcterms:mediator') + self.failUnless((subj,URIRef(dcterms+"rights"),None) in rdfgraph, 'dcterms:rights') + self.failUnless((subj,URIRef(dcterms+"license"),None) in rdfgraph, 'dcterms:license') + self.failUnless((subj,URIRef(dcterms+"publisher"),None) in rdfgraph, 'dcterms:publisher') + self.failUnless((subj,URIRef(oxds+"isEmbargoed"),'True') in rdfgraph, 'oxds:isEmbargoed') + self.failUnless((subj,URIRef(oxds+"embargoedUntil"),d) in rdfgraph, 'oxds:embargoedUntil') + self.failUnless((subj,URIRef(oxds+"currentVersion"),'1') in rdfgraph, 'oxds:currentVersion') + self.failUnless((subj,URIRef(dcterms+"modified"),None) in rdfgraph, 'dcterms:modified') + # Access and check zip file content + (resp, zipfile) = self.doHTTP_GET( + resource="datasets/TestSubmission/testdir.zip/version1", + expect_status=200, expect_reason="OK", expect_type="application/zip") + self.assertEqual(zipdata, zipfile, "Difference between local and remote zipfile - Version 1!") + #Access state information and check + (resp, data) = self.doHTTP_GET( + resource="states/TestSubmission/version1", + expect_status=200, expect_reason="OK", expect_type="application/json") + state = data['state'] + parts = data['parts'] + self.assertEqual(len(state.keys()), 11, "States") + self.assertEqual(len(parts.keys()), 4, "Parts") + self.assertEqual(len(parts['4=TestSubmission'].keys()), 13, "File stats for 4=TestSubmission") + self.assertEqual(len(parts['manifest.rdf'].keys()), 13, "File stats for manifest.rdf") + self.assertEqual(len(parts['testdir.zip'].keys()), 13, "File stats for testdir.zip") + #---------Version 2 + # Access and check list of contents of version 2 + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission/version2", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + self.assertEqual(len(rdfgraph),13,'Graph length %i' %len(rdfgraph)) + self.failUnless((subj,RDF.type,stype) in rdfgraph, 'Testing submission type: '+subj+", "+stype) + self.failUnless((subj,URIRef(dcterms+"created"),None) in rdfgraph, 'dcterms:created') + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"testdir.zip")) in rdfgraph, 'ore:aggregates testdir.zip') + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"testdir2.zip")) in rdfgraph, 'ore:aggregates testdir2.zip') + self.failUnless((subj,URIRef(dcterms+"identifier"),None) in rdfgraph, 'dcterms:identifier') + self.failUnless((subj,URIRef(dcterms+"mediator"),None) in rdfgraph, 'dcterms:mediator') + self.failUnless((subj,URIRef(dcterms+"rights"),None) in rdfgraph, 'dcterms:rights') + self.failUnless((subj,URIRef(dcterms+"license"),None) in rdfgraph, 'dcterms:license') + self.failUnless((subj,URIRef(dcterms+"publisher"),None) in rdfgraph, 'dcterms:publisher') + self.failUnless((subj,URIRef(oxds+"isEmbargoed"),'True') in rdfgraph, 'oxds:isEmbargoed') + self.failUnless((subj,URIRef(oxds+"embargoedUntil"),d) in rdfgraph, 'oxds:embargoedUntil') + self.failUnless((subj,URIRef(oxds+"currentVersion"),'2') in rdfgraph, 'oxds:currentVersion') + self.failUnless((subj,URIRef(dcterms+"modified"),None) in rdfgraph, 'dcterms:modified') + # Access and check zip file content + (resp, zipfile) = self.doHTTP_GET( + resource="datasets/TestSubmission/testdir.zip/version2", + expect_status=200, expect_reason="OK", expect_type="application/zip") + self.assertEqual(zipdata, zipfile, "Difference between local and remote zipfile - Version 2!") + (resp, zipfile2) = self.doHTTP_GET( + resource="datasets/TestSubmission/testdir2.zip/version2", + expect_status=200, expect_reason="OK", expect_type="application/zip") + self.assertEqual(zipdata2, zipfile2, "Difference between local and remote zipfile - Version 2!") + #Access state information and check + (resp, data) = self.doHTTP_GET( + resource="states/TestSubmission/version2", + expect_status=200, expect_reason="OK", expect_type="application/json") + state = data['state'] + parts = data['parts'] + self.assertEqual(len(state.keys()), 11, "States") + self.assertEqual(len(parts.keys()), 5, "Parts") + self.assertEqual(len(parts['4=TestSubmission'].keys()), 13, "File stats for 4=TestSubmission") + self.assertEqual(len(parts['manifest.rdf'].keys()), 13, "File stats for manifest.rdf") + self.assertEqual(len(parts['testdir.zip'].keys()), 13, "File stats for testdir.zip") + self.assertEqual(len(parts['testdir2.zip'].keys()), 13, "File stats for testdir2.zip") + #---------Version 3 + # Access and check list of contents of version 3 + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission/version3", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + self.assertEqual(len(rdfgraph),12,'Graph length %i' %len(rdfgraph)) + self.failUnless((subj,RDF.type,stype) in rdfgraph, 'Testing submission type: '+subj+", "+stype) + self.failUnless((subj,URIRef(dcterms+"created"),None) in rdfgraph, 'dcterms:created') + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"testdir2.zip")) in rdfgraph, 'ore:aggregates testdir2.zip') + self.failUnless((subj,URIRef(dcterms+"identifier"),None) in rdfgraph, 'dcterms:identifier') + self.failUnless((subj,URIRef(dcterms+"mediator"),None) in rdfgraph, 'dcterms:mediator') + self.failUnless((subj,URIRef(dcterms+"rights"),None) in rdfgraph, 'dcterms:rights') + self.failUnless((subj,URIRef(dcterms+"license"),None) in rdfgraph, 'dcterms:license') + self.failUnless((subj,URIRef(dcterms+"publisher"),None) in rdfgraph, 'dcterms:publisher') + self.failUnless((subj,URIRef(oxds+"isEmbargoed"),None) in rdfgraph, 'oxds:isEmbargoed') + self.failUnless((subj,URIRef(oxds+"embargoedUntil"),None) in rdfgraph, 'oxds:embargoedUntil') + self.failUnless((subj,URIRef(oxds+"currentVersion"),'3') in rdfgraph, 'oxds:currentVersion') + self.failUnless((subj,URIRef(dcterms+"modified"),None) in rdfgraph, 'dcterms:modified') + # Access and check zip file content + (resp, zipfile) = self.doHTTP_GET( + resource="datasets/TestSubmission/testdir2.zip/version3", + expect_status=200, expect_reason="OK", expect_type="application/zip") + self.assertEqual(zipdata2, zipfile, "Difference between local and remote zipfile - Version 3!") + (resp, zipfile) = self.doHTTP_GET( + resource="datasets/TestSubmission/testdir.zip/version3", + expect_status=404, expect_reason="Not Found") + #Access state information and check + (resp, data) = self.doHTTP_GET( + resource="states/TestSubmission/version3", + expect_status=200, expect_reason="OK", expect_type="application/json") + state = data['state'] + parts = data['parts'] + self.assertEqual(len(state.keys()), 11, "States") + self.assertEqual(len(parts.keys()), 4, "Parts") + self.assertEqual(len(parts['4=TestSubmission'].keys()), 13, "File stats for 4=TestSubmission") + self.assertEqual(len(parts['manifest.rdf'].keys()), 13, "File stats for manifest.rdf") + self.assertEqual(len(parts['testdir2.zip'].keys()), 13, "File stats for testdir2.zip") + #---------Version 4 + # Access and check list of contents of version 4 + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission/version4", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + self.assertEqual(len(rdfgraph),12,'Graph length %i' %len(rdfgraph)) + self.failUnless((subj,RDF.type,stype) in rdfgraph, 'Testing submission type: '+subj+", "+stype) + self.failUnless((subj,URIRef(dcterms+"created"),None) in rdfgraph, 'dcterms:created') + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"testdir2.zip")) in rdfgraph, 'ore:aggregates testdir2.zip') + self.failUnless((subj,URIRef(dcterms+"identifier"),None) in rdfgraph, 'dcterms:identifier') + self.failUnless((subj,URIRef(dcterms+"mediator"),None) in rdfgraph, 'dcterms:mediator') + self.failUnless((subj,URIRef(dcterms+"rights"),None) in rdfgraph, 'dcterms:rights') + self.failUnless((subj,URIRef(dcterms+"license"),None) in rdfgraph, 'dcterms:license') + self.failUnless((subj,URIRef(dcterms+"publisher"),None) in rdfgraph, 'dcterms:publisher') + self.failUnless((subj,URIRef(oxds+"isEmbargoed"),None) in rdfgraph, 'oxds:isEmbargoed') + self.failUnless((subj,URIRef(oxds+"embargoedUntil"),None) in rdfgraph, 'oxds:embargoedUntil') + self.failUnless((subj,URIRef(oxds+"currentVersion"),'4') in rdfgraph, 'oxds:currentVersion') + self.failUnless((subj,URIRef(dcterms+"modified"),None) in rdfgraph, 'dcterms:modified') + # Access and check zip file content + (resp, zipfile) = self.doHTTP_GET( + resource="datasets/TestSubmission/testdir2.zip/version4", + expect_status=200, expect_reason="OK", expect_type="application/zip") + self.assertEqual(zipdata3, zipfile, "Difference between local and remote zipfile - Version 4!") + (resp, zipfile) = self.doHTTP_GET( + resource="datasets/TestSubmission/testdir.zip/version4", + expect_status=404, expect_reason="Not Found") + #Access state information and check + (resp, data) = self.doHTTP_GET( + resource="states/TestSubmission/version4", + expect_status=200, expect_reason="OK", expect_type="application/json") + state = data['state'] + parts = data['parts'] + self.assertEqual(len(state.keys()), 11, "States") + self.assertEqual(len(parts.keys()), 4, "Parts") + self.assertEqual(state['item_id'], "TestSubmission", "Submission item identifier") + self.assertEqual(len(state['versions']), 5, "Five versions") + self.assertEqual(state['versions'],['0', '1', '2', '3', '4'], "Versions") + self.assertEqual(state['currentversion'], '4', "Current version == 4") + self.assertEqual(state['rdffileformat'], 'xml', "RDF file type") + self.assertEqual(state['rdffilename'], 'manifest.rdf', "RDF file name") + self.assertEqual(state['files']['0'], ['manifest.rdf'], "List should contain just manifest.rdf") + self.assertEqual(len(state['files']['1']), 2, "List should contain manifest.rdf and testdir.zip") + self.assertEqual(len(state['files']['2']), 3, "List should contain manifest.rdf, testdir.zip and testdir2.zip") + self.assertEqual(len(state['files']['3']), 2, "List should contain manifest.rdf and testdir2.zip") + self.assertEqual(len(state['files']['4']), 2, "List should contain manifest.rdf and testdir2.zip") + self.assertEqual(len(state['metadata_files']['0']), 0, "metadata_files of version 0") + self.assertEqual(len(state['metadata_files']['1']), 0, "metadata_files of version 1") + self.assertEqual(len(state['metadata_files']['2']), 0, "metadata_files of version 2") + self.assertEqual(len(state['metadata_files']['3']), 0, "metadata_files of version 3") + self.assertEqual(len(state['metadata_files']['4']), 0, "metadata_files of version 4") + self.assertEqual(len(state['subdir']['0']), 0, "Subdirectory count for version 0") + self.assertEqual(len(state['subdir']['1']), 0, "Subdirectory count for version 1") + self.assertEqual(len(state['subdir']['2']), 0, "Subdirectory count for version 2") + self.assertEqual(len(state['subdir']['3']), 0, "Subdirectory count for version 3") + self.assertEqual(len(state['subdir']['4']), 0, "Subdirectory count for version 4") + self.assertEqual(state['metadata']['createdby'], RDFDatabankConfig.endpointsubmitteruser, "Created by") + self.assertEqual(state['metadata']['embargoed'], True, "Embargoed?") + self.assertEqual(len(parts['4=TestSubmission'].keys()), 13, "File stats for 4=TestSubmission") + self.assertEqual(len(parts['manifest.rdf'].keys()), 13, "File stats for manifest.rdf") + self.assertEqual(len(parts['testdir2.zip'].keys()), 13, "File stats for testdir2.zip") + # Access and check list of contents of version 5 + (resp, respdata) = self.doHTTP_GET( + resource="datasets/TestSubmission/version5", + expect_status=404, expect_reason="Not Found") + (resp, zipfile) = self.doHTTP_GET( + resource="datasets/TestSubmission/testdir2.zip/version5", + expect_status=404, expect_reason="Not Found") + + #Access version as other users + #Admin user of this silo + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointadminuser, + endpointpass=RDFDatabankConfig.endpointadminpass) + #Access dataset for version 3 + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission/version3", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + # Access and check zip file content + (resp, zipfile) = self.doHTTP_GET( + resource="datasets/TestSubmission/testdir2.zip/version3", + expect_status=200, expect_reason="OK", expect_type="application/zip") + self.assertEqual(zipdata2, zipfile, "Difference between local and remote zipfile - Version 3!") + (resp, zipfile) = self.doHTTP_GET( + resource="datasets/TestSubmission/testdir.zip/version3", + expect_status=404, expect_reason="Not Found") + + #manager user of this silo + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointmanageruser, + endpointpass=RDFDatabankConfig.endpointmanagerpass) + #Access dataset for version 3 + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission/version3", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + # Access and check zip file content + (resp, zipfile) = self.doHTTP_GET( + resource="datasets/TestSubmission/testdir2.zip/version3", + expect_status=200, expect_reason="OK", expect_type="application/zip") + self.assertEqual(zipdata2, zipfile, "Difference between local and remote zipfile - Version 3!") + (resp, zipfile) = self.doHTTP_GET( + resource="datasets/TestSubmission/testdir.zip/version3", + expect_status=404, expect_reason="Not Found") + + #submitter user of this silo + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointsubmitteruser2, + endpointpass=RDFDatabankConfig.endpointsubmitterpass2) + #Access dataset for version 3 + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission/version3", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + # Access and check zip file content - under embargo + (resp, zipfile) = self.doHTTP_GET( + resource="datasets/TestSubmission/testdir2.zip/version3", + expect_status=403, expect_reason="Forbidden", expect_type="application/zip") + (resp, zipfile) = self.doHTTP_GET( + resource="datasets/TestSubmission/testdir.zip/version3", + expect_status=403, expect_reason="Forbidden") + + #General user + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointgeneraluser, + endpointpass=RDFDatabankConfig.endpointgeneralpass) + #Access dataset for version 3 + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission/version3", + expect_status=401, expect_reason="Unauthorized", expect_type="text/plain") + # Access and check zip file content - under embargo + (resp, zipfile) = self.doHTTP_GET( + resource="datasets/TestSubmission/testdir2.zip/version3", + expect_status=401, expect_reason="Unauthorized", expect_type="text/plain") + (resp, zipfile) = self.doHTTP_GET( + resource="datasets/TestSubmission/testdir.zip/version3", + expect_status=401, expect_reason="Unauthorized", expect_type="text/plain") + + #Admin user of another silo + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointadminuser3, + endpointpass=RDFDatabankConfig.endpointadminpass3) + #Access dataset for version 3 + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission/version3", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + # Access and check zip file content - under embargo + (resp, zipfile) = self.doHTTP_GET( + resource="datasets/TestSubmission/testdir2.zip/version3", + expect_status=403, expect_reason="Forbidden", expect_type="application/zip") + (resp, zipfile) = self.doHTTP_GET( + resource="datasets/TestSubmission/testdir.zip/version3", + expect_status=403, expect_reason="Forbidden") + + #manager user of another silo + data2 = None + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointmanageruser3, + endpointpass=RDFDatabankConfig.endpointmanagerpass3) + #Access dataset for version 3 + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission/version3", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + # Access and check zip file content - under embargo + (resp, zipfile) = self.doHTTP_GET( + resource="datasets/TestSubmission/testdir2.zip/version3", + expect_status=403, expect_reason="Forbidden", expect_type="application/zip") + (resp, zipfile) = self.doHTTP_GET( + resource="datasets/TestSubmission/testdir.zip/version3", + expect_status=403, expect_reason="Forbidden") + + #submitter user of another silo + data2 = None + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointsubmitteruser3, + endpointpass=RDFDatabankConfig.endpointsubmitterpass3) + #Access dataset for version 3 + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission/version3", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + # Access and check zip file content - under embargo + (resp, zipfile) = self.doHTTP_GET( + resource="datasets/TestSubmission/testdir2.zip/version3", + expect_status=403, expect_reason="Forbidden", expect_type="application/zip") + (resp, zipfile) = self.doHTTP_GET( + resource="datasets/TestSubmission/testdir.zip/version3", + expect_status=403, expect_reason="Forbidden") + + + + def testGetDatasetByVersionByParameter(self): + """Upload files to a dataset - POST file to /silo_name/datasets/dataset_name. + Access each of the versions and the files in that version""" + #Definitions + subj = URIRef(self.getManifestUri("datasets/TestSubmission")) + base = self.getManifestUri("datasets/TestSubmission/") + dcterms = "http://purl.org/dc/terms/" + ore = "http://www.openarchives.org/ore/terms/" + oxds = "http://vocab.ox.ac.uk/dataset/schema#" + stype = URIRef(oxds+"DataSet") + #---------Version 0 + # Create a new dataset, check response + self.createSubmissionDataset() + # Access and check list of contents + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + self.assertEqual(len(rdfgraph),10,'Graph length %i' %len(rdfgraph)) + #Access state information and check + (resp, data) = self.doHTTP_GET( + resource="states/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/json") + state = data['state'] + parts = data['parts'] + self.assertEqual(len(state.keys()), 11, "States") + self.assertEqual(len(parts.keys()), 3, "Parts") + #---------Version 1 + # Upload zip file, check response + zipdata = self.uploadSubmissionZipfile() + # Access and check list of contents + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + self.assertEqual(len(rdfgraph),12,'Graph length %i' %len(rdfgraph)) + # Access and check zip file content + (resp, zipfile) = self.doHTTP_GET( + resource="datasets/TestSubmission/testdir.zip", + expect_status=200, expect_reason="OK", expect_type="application/zip") + self.assertEqual(zipdata, zipfile, "Difference between local and remote zipfile!") + #Access state information and check + (resp, data) = self.doHTTP_GET( + resource="states/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/json") + state = data['state'] + parts = data['parts'] + self.assertEqual(len(state.keys()), 11, "States") + self.assertEqual(len(parts.keys()), 4, "Parts") + # Access and check list of contents of version 0 + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission?version=0", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + self.assertEqual(len(rdfgraph),10,'Graph length %i' %len(rdfgraph)) + #---------Version 2 + # Upload zip file, check response + zipdata2 = self.uploadSubmissionZipfile(file_to_upload="testdir2.zip") + # Access and check list of contents + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + self.assertEqual(len(rdfgraph),13,'Graph length %i' %len(rdfgraph)) + # Access and check zip file content + (resp, zipfile) = self.doHTTP_GET( + resource="datasets/TestSubmission/testdir.zip", + expect_status=200, expect_reason="OK", expect_type="application/zip") + self.assertEqual(zipdata, zipfile, "Difference between local and remote zipfile - testdir.zip!") + (resp, zipfile2) = self.doHTTP_GET( + resource="datasets/TestSubmission/testdir2.zip", + expect_status=200, expect_reason="OK", expect_type="application/zip") + self.assertEqual(zipdata2, zipfile2, "Difference between local and remote zipfile - testdir2.zip!") + #Access state information and check + (resp, data) = self.doHTTP_GET( + resource="states/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/json") + state = data['state'] + parts = data['parts'] + self.assertEqual(len(state.keys()), 11, "States") + self.assertEqual(len(parts.keys()), 5, "Parts") + #---------Version 3 + # Delete file, check response + resp = self.doHTTP_DELETE( + resource="datasets/TestSubmission/testdir.zip", + expect_status=200, expect_reason="OK") + # Access and check list of contents + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + self.assertEqual(len(rdfgraph),12,'Graph length %i' %len(rdfgraph)) + # Access and check zip file content + (resp, zipfile) = self.doHTTP_GET( + resource="datasets/TestSubmission/testdir.zip", + expect_status=404, expect_reason="Not Found") + (resp, zipfile2) = self.doHTTP_GET( + resource="datasets/TestSubmission/testdir2.zip", + expect_status=200, expect_reason="OK", expect_type="application/zip") + self.assertEqual(zipdata2, zipfile2, "Difference between local and remote zipfile - testdir2.zip!") + #Access state information and check + (resp, data) = self.doHTTP_GET( + resource="states/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/json") + state = data['state'] + parts = data['parts'] + self.assertEqual(len(state.keys()), 11, "States") + self.assertEqual(len(parts.keys()), 4, "Parts") + #---------Version 4 + # Update zip file, check response + zipdata3 = self.updateSubmissionZipfile(file_to_upload="testrdf4.zip", filename="testdir2.zip") + # Access and check list of contents + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + self.assertEqual(len(rdfgraph),12,'Graph length %i' %len(rdfgraph)) + # Access and check zip file content + (resp, zipfile) = self.doHTTP_GET( + resource="datasets/TestSubmission/testdir.zip", + expect_status=404, expect_reason="Not Found") + (resp, zipfile2) = self.doHTTP_GET( + resource="datasets/TestSubmission/testdir2.zip", + expect_status=200, expect_reason="OK", expect_type="application/zip") + self.assertEqual(zipdata3, zipfile2, "Difference between local and remote zipfile - testdir2.zip!") + #Access state information and check + (resp, data) = self.doHTTP_GET( + resource="states/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/json") + state = data['state'] + parts = data['parts'] + self.assertEqual(len(state.keys()), 11, "States") + self.assertEqual(len(parts.keys()), 4, "Parts") + #=========Access each of the versions + #---------Version 0 + # Access and check list of contents of version 0 + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission?version=0", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + self.assertEqual(len(rdfgraph),10,'Graph length %i' %len(rdfgraph)) + self.failUnless((subj,RDF.type,stype) in rdfgraph, 'Testing submission type: '+subj+", "+stype) + self.failUnless((subj,URIRef(dcterms+"created"),None) in rdfgraph, 'dcterms:created') + self.failUnless((subj,URIRef(dcterms+"identifier"),None) in rdfgraph, 'dcterms:identifier') + self.failUnless((subj,URIRef(dcterms+"mediator"),None) in rdfgraph, 'dcterms:mediator') + self.failUnless((subj,URIRef(dcterms+"rights"),None) in rdfgraph, 'dcterms:rights') + self.failUnless((subj,URIRef(dcterms+"license"),None) in rdfgraph, 'dcterms:license') + self.failUnless((subj,URIRef(dcterms+"publisher"),None) in rdfgraph, 'dcterms:publisher') + self.failUnless((subj,URIRef(oxds+"isEmbargoed"),None) in rdfgraph, 'oxds:isEmbargoed') + self.failUnless((subj,URIRef(oxds+"embargoedUntil"),None) in rdfgraph, 'oxds:embargoedUntil') + self.failUnless((subj,URIRef(oxds+"currentVersion"),'0') in rdfgraph, 'oxds:currentVersion') + #Access state information and check + (resp, data) = self.doHTTP_GET( + resource="states/TestSubmission?version=0", + expect_status=200, expect_reason="OK", expect_type="application/json") + state = data['state'] + parts = data['parts'] + self.assertEqual(len(state.keys()), 11, "States") + self.assertEqual(len(parts.keys()), 3, "Parts") + self.assertEqual(len(parts['4=TestSubmission'].keys()), 13, "File stats for 4=TestSubmission") + self.assertEqual(len(parts['manifest.rdf'].keys()), 13, "File stats for manifest.rdf") + #---------Version 1 + # Access and check list of contents of version 1 + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission?version=1", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + self.assertEqual(len(rdfgraph),12,'Graph length %i' %len(rdfgraph)) + self.failUnless((subj,RDF.type,stype) in rdfgraph, 'Testing submission type: '+subj+", "+stype) + self.failUnless((subj,URIRef(dcterms+"created"),None) in rdfgraph, 'dcterms:created') + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"testdir.zip")) in rdfgraph, 'ore:aggregates testdir.zip') + self.failUnless((subj,URIRef(dcterms+"identifier"),None) in rdfgraph, 'dcterms:identifier') + self.failUnless((subj,URIRef(dcterms+"mediator"),None) in rdfgraph, 'dcterms:mediator') + self.failUnless((subj,URIRef(dcterms+"rights"),None) in rdfgraph, 'dcterms:rights') + self.failUnless((subj,URIRef(dcterms+"license"),None) in rdfgraph, 'dcterms:license') + self.failUnless((subj,URIRef(dcterms+"publisher"),None) in rdfgraph, 'dcterms:publisher') + self.failUnless((subj,URIRef(oxds+"isEmbargoed"),None) in rdfgraph, 'oxds:isEmbargoed') + self.failUnless((subj,URIRef(oxds+"embargoedUntil"),None) in rdfgraph, 'oxds:embargoedUntil') + self.failUnless((subj,URIRef(oxds+"currentVersion"),'1') in rdfgraph, 'oxds:currentVersion') + self.failUnless((subj,URIRef(dcterms+"modified"),None) in rdfgraph, 'dcterms:modified') + # Access and check zip file content + (resp, zipfile) = self.doHTTP_GET( + resource="datasets/TestSubmission/testdir.zip?version=1", + expect_status=200, expect_reason="OK", expect_type="application/zip") + self.assertEqual(zipdata, zipfile, "Difference between local and remote zipfile - Version 1!") + #Access state information and check + (resp, data) = self.doHTTP_GET( + resource="states/TestSubmission?version=1", + expect_status=200, expect_reason="OK", expect_type="application/json") + state = data['state'] + parts = data['parts'] + self.assertEqual(len(state.keys()), 11, "States") + self.assertEqual(len(parts.keys()), 4, "Parts") + self.assertEqual(len(parts['4=TestSubmission'].keys()), 13, "File stats for 4=TestSubmission") + self.assertEqual(len(parts['manifest.rdf'].keys()), 13, "File stats for manifest.rdf") + self.assertEqual(len(parts['testdir.zip'].keys()), 13, "File stats for testdir.zip") + #---------Version 2 + # Access and check list of contents of version 2 + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission?version=2", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + self.assertEqual(len(rdfgraph),13,'Graph length %i' %len(rdfgraph)) + self.failUnless((subj,RDF.type,stype) in rdfgraph, 'Testing submission type: '+subj+", "+stype) + self.failUnless((subj,URIRef(dcterms+"created"),None) in rdfgraph, 'dcterms:created') + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"testdir.zip")) in rdfgraph, 'ore:aggregates testdir.zip') + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"testdir2.zip")) in rdfgraph, 'ore:aggregates testdir2.zip') + self.failUnless((subj,URIRef(dcterms+"identifier"),None) in rdfgraph, 'dcterms:identifier') + self.failUnless((subj,URIRef(dcterms+"mediator"),None) in rdfgraph, 'dcterms:mediator') + self.failUnless((subj,URIRef(dcterms+"rights"),None) in rdfgraph, 'dcterms:rights') + self.failUnless((subj,URIRef(dcterms+"license"),None) in rdfgraph, 'dcterms:license') + self.failUnless((subj,URIRef(dcterms+"publisher"),None) in rdfgraph, 'dcterms:publisher') + self.failUnless((subj,URIRef(oxds+"isEmbargoed"),None) in rdfgraph, 'oxds:isEmbargoed') + self.failUnless((subj,URIRef(oxds+"embargoedUntil"),None) in rdfgraph, 'oxds:embargoedUntil') + self.failUnless((subj,URIRef(oxds+"currentVersion"),'2') in rdfgraph, 'oxds:currentVersion') + self.failUnless((subj,URIRef(dcterms+"modified"),None) in rdfgraph, 'dcterms:modified') + # Access and check zip file content + (resp, zipfile) = self.doHTTP_GET( + resource="datasets/TestSubmission/testdir.zip?version=2", + expect_status=200, expect_reason="OK", expect_type="application/zip") + self.assertEqual(zipdata, zipfile, "Difference between local and remote zipfile - Version 2!") + (resp, zipfile2) = self.doHTTP_GET( + resource="datasets/TestSubmission/testdir2.zip?version=2", + expect_status=200, expect_reason="OK", expect_type="application/zip") + self.assertEqual(zipdata2, zipfile2, "Difference between local and remote zipfile - Version 2!") + #Access state information and check + (resp, data) = self.doHTTP_GET( + resource="states/TestSubmission?version=2", + expect_status=200, expect_reason="OK", expect_type="application/json") + state = data['state'] + parts = data['parts'] + self.assertEqual(len(state.keys()), 11, "States") + self.assertEqual(len(parts.keys()), 5, "Parts") + self.assertEqual(len(parts['4=TestSubmission'].keys()), 13, "File stats for 4=TestSubmission") + self.assertEqual(len(parts['manifest.rdf'].keys()), 13, "File stats for manifest.rdf") + self.assertEqual(len(parts['testdir.zip'].keys()), 13, "File stats for testdir.zip") + self.assertEqual(len(parts['testdir2.zip'].keys()), 13, "File stats for testdir2.zip") + #---------Version 3 + # Access and check list of contents of version 3 + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission?version=3", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + self.assertEqual(len(rdfgraph),12,'Graph length %i' %len(rdfgraph)) + self.failUnless((subj,RDF.type,stype) in rdfgraph, 'Testing submission type: '+subj+", "+stype) + self.failUnless((subj,URIRef(dcterms+"created"),None) in rdfgraph, 'dcterms:created') + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"testdir2.zip")) in rdfgraph, 'ore:aggregates testdir2.zip') + self.failUnless((subj,URIRef(dcterms+"identifier"),None) in rdfgraph, 'dcterms:identifier') + self.failUnless((subj,URIRef(dcterms+"mediator"),None) in rdfgraph, 'dcterms:mediator') + self.failUnless((subj,URIRef(dcterms+"rights"),None) in rdfgraph, 'dcterms:rights') + self.failUnless((subj,URIRef(dcterms+"license"),None) in rdfgraph, 'dcterms:license') + self.failUnless((subj,URIRef(dcterms+"publisher"),None) in rdfgraph, 'dcterms:publisher') + self.failUnless((subj,URIRef(oxds+"isEmbargoed"),None) in rdfgraph, 'oxds:isEmbargoed') + self.failUnless((subj,URIRef(oxds+"embargoedUntil"),None) in rdfgraph, 'oxds:embargoedUntil') + self.failUnless((subj,URIRef(oxds+"currentVersion"),'3') in rdfgraph, 'oxds:currentVersion') + self.failUnless((subj,URIRef(dcterms+"modified"),None) in rdfgraph, 'dcterms:modified') + # Access and check zip file content + (resp, zipfile) = self.doHTTP_GET( + resource="datasets/TestSubmission/testdir2.zip?version=3", + expect_status=200, expect_reason="OK", expect_type="application/zip") + self.assertEqual(zipdata2, zipfile, "Difference between local and remote zipfile - Version 3!") + (resp, zipfile) = self.doHTTP_GET( + resource="datasets/TestSubmission/testdir.zip?version=3", + expect_status=404, expect_reason="Not Found") + #Access state information and check + (resp, data) = self.doHTTP_GET( + resource="states/TestSubmission?version=3", + expect_status=200, expect_reason="OK", expect_type="application/json") + state = data['state'] + parts = data['parts'] + self.assertEqual(len(state.keys()), 11, "States") + self.assertEqual(len(parts.keys()), 4, "Parts") + self.assertEqual(len(parts['4=TestSubmission'].keys()), 13, "File stats for 4=TestSubmission") + self.assertEqual(len(parts['manifest.rdf'].keys()), 13, "File stats for manifest.rdf") + self.assertEqual(len(parts['testdir2.zip'].keys()), 13, "File stats for testdir2.zip") + #---------Version 4 + # Access and check list of contents of version 4 + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission?version=4", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + self.assertEqual(len(rdfgraph),12,'Graph length %i' %len(rdfgraph)) + self.failUnless((subj,RDF.type,stype) in rdfgraph, 'Testing submission type: '+subj+", "+stype) + self.failUnless((subj,URIRef(dcterms+"created"),None) in rdfgraph, 'dcterms:created') + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"testdir2.zip")) in rdfgraph, 'ore:aggregates testdir2.zip') + self.failUnless((subj,URIRef(dcterms+"identifier"),None) in rdfgraph, 'dcterms:identifier') + self.failUnless((subj,URIRef(dcterms+"mediator"),None) in rdfgraph, 'dcterms:mediator') + self.failUnless((subj,URIRef(dcterms+"rights"),None) in rdfgraph, 'dcterms:rights') + self.failUnless((subj,URIRef(dcterms+"license"),None) in rdfgraph, 'dcterms:license') + self.failUnless((subj,URIRef(dcterms+"publisher"),None) in rdfgraph, 'dcterms:publisher') + self.failUnless((subj,URIRef(oxds+"isEmbargoed"),None) in rdfgraph, 'oxds:isEmbargoed') + self.failUnless((subj,URIRef(oxds+"embargoedUntil"),None) in rdfgraph, 'oxds:embargoedUntil') + self.failUnless((subj,URIRef(oxds+"currentVersion"),'4') in rdfgraph, 'oxds:currentVersion') + self.failUnless((subj,URIRef(dcterms+"modified"),None) in rdfgraph, 'dcterms:modified') + # Access and check zip file content + (resp, zipfile) = self.doHTTP_GET( + resource="datasets/TestSubmission/testdir2.zip?version=4", + expect_status=200, expect_reason="OK", expect_type="application/zip") + self.assertEqual(zipdata3, zipfile, "Difference between local and remote zipfile - Version 4!") + (resp, zipfile) = self.doHTTP_GET( + resource="datasets/TestSubmission/testdir.zip?version=4", + expect_status=404, expect_reason="Not Found") + #Access state information and check + (resp, data) = self.doHTTP_GET( + resource="states/TestSubmission?version=4", + expect_status=200, expect_reason="OK", expect_type="application/json") + state = data['state'] + parts = data['parts'] + self.assertEqual(len(state.keys()), 11, "States") + self.assertEqual(len(parts.keys()), 4, "Parts") + self.assertEqual(state['item_id'], "TestSubmission", "Submission item identifier") + self.assertEqual(len(state['versions']), 5, "Five versions") + self.assertEqual(state['versions'],['0', '1', '2', '3', '4'], "Versions") + self.assertEqual(state['currentversion'], '4', "Current version == 4") + self.assertEqual(state['rdffileformat'], 'xml', "RDF file type") + self.assertEqual(state['rdffilename'], 'manifest.rdf', "RDF file name") + self.assertEqual(state['files']['0'], ['manifest.rdf'], "List should contain just manifest.rdf") + self.assertEqual(len(state['files']['1']), 2, "List should contain manifest.rdf and testdir.zip") + self.assertEqual(len(state['files']['2']), 3, "List should contain manifest.rdf, testdir.zip and testdir2.zip") + self.assertEqual(len(state['files']['3']), 2, "List should contain manifest.rdf and testdir2.zip") + self.assertEqual(len(state['files']['4']), 2, "List should contain manifest.rdf and testdir2.zip") + self.assertEqual(len(state['metadata_files']['0']), 0, "metadata_files of version 0") + self.assertEqual(len(state['metadata_files']['1']), 0, "metadata_files of version 1") + self.assertEqual(len(state['metadata_files']['2']), 0, "metadata_files of version 2") + self.assertEqual(len(state['metadata_files']['3']), 0, "metadata_files of version 3") + self.assertEqual(len(state['metadata_files']['4']), 0, "metadata_files of version 4") + self.assertEqual(len(state['subdir']['0']), 0, "Subdirectory count for version 0") + self.assertEqual(len(state['subdir']['1']), 0, "Subdirectory count for version 1") + self.assertEqual(len(state['subdir']['2']), 0, "Subdirectory count for version 2") + self.assertEqual(len(state['subdir']['3']), 0, "Subdirectory count for version 3") + self.assertEqual(len(state['subdir']['4']), 0, "Subdirectory count for version 4") + self.assertEqual(state['metadata']['createdby'], RDFDatabankConfig.endpointsubmitteruser, "Created by") + self.assertEqual(state['metadata']['embargoed'], True, "Embargoed?") + self.assertEqual(len(parts['4=TestSubmission'].keys()), 13, "File stats for 4=TestSubmission") + self.assertEqual(len(parts['manifest.rdf'].keys()), 13, "File stats for manifest.rdf") + self.assertEqual(len(parts['testdir2.zip'].keys()), 13, "File stats for testdir2.zip") + # Access and check list of contents of version 5 + (resp, respdata) = self.doHTTP_GET( + resource="datasets/TestSubmission?version=5", + expect_status=404, expect_reason="Not Found") + (resp, zipfile) = self.doHTTP_GET( + resource="datasets/TestSubmission/testdir2.zip?version=5", + expect_status=404, expect_reason="Not Found") + + #Access version as other users + #Admin user of this silo + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointadminuser, + endpointpass=RDFDatabankConfig.endpointadminpass) + #Access dataset for version 3 + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission?version=3", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + # Access and check zip file content + (resp, zipfile) = self.doHTTP_GET( + resource="datasets/TestSubmission/testdir2.zip?version=3", + expect_status=200, expect_reason="OK", expect_type="application/zip") + self.assertEqual(zipdata2, zipfile, "Difference between local and remote zipfile - Version 3!") + (resp, zipfile) = self.doHTTP_GET( + resource="datasets/TestSubmission/testdir.zip?version=3", + expect_status=404, expect_reason="Not Found") + + #manager user of this silo + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointmanageruser, + endpointpass=RDFDatabankConfig.endpointmanagerpass) + #Access dataset for version 3 + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission?version=3", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + # Access and check zip file content + (resp, zipfile) = self.doHTTP_GET( + resource="datasets/TestSubmission/testdir2.zip?version=3", + expect_status=200, expect_reason="OK", expect_type="application/zip") + self.assertEqual(zipdata2, zipfile, "Difference between local and remote zipfile - Version 3!") + (resp, zipfile) = self.doHTTP_GET( + resource="datasets/TestSubmission/testdir.zip?version=3", + expect_status=404, expect_reason="Not Found") + + #submitter user of this silo + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointsubmitteruser2, + endpointpass=RDFDatabankConfig.endpointsubmitterpass2) + #Access dataset for version 3 + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission?version=3", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + # Access and check zip file content - under embargo + (resp, zipfile) = self.doHTTP_GET( + resource="datasets/TestSubmission/testdir2.zip?version=3", + expect_status=403, expect_reason="Forbidden", expect_type="application/zip") + (resp, zipfile) = self.doHTTP_GET( + resource="datasets/TestSubmission/testdir.zip?version=3", + expect_status=403, expect_reason="Forbidden") + + #General user + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointgeneraluser, + endpointpass=RDFDatabankConfig.endpointgeneralpass) + #Access dataset for version 3 + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission?version=3", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + # Access and check zip file content - under embargo + (resp, zipfile) = self.doHTTP_GET( + resource="datasets/TestSubmission/testdir2.zip?version=3", + expect_status=401, expect_reason="Unauthorized", expect_type="text/plain") + (resp, zipfile) = self.doHTTP_GET( + resource="datasets/TestSubmission/testdir.zip?version=3", + expect_status=401, expect_reason="Unauthorized", expect_type="text/plain") + + #Admin user of another silo + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointadminuser3, + endpointpass=RDFDatabankConfig.endpointadminpass3) + #Access dataset for version 3 + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission?version=3", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + # Access and check zip file content - under embargo + (resp, zipfile) = self.doHTTP_GET( + resource="datasets/TestSubmission/testdir2.zip?version=3", + expect_status=403, expect_reason="Forbidden", expect_type="application/zip") + (resp, zipfile) = self.doHTTP_GET( + resource="datasets/TestSubmission/testdir.zip?version=3", + expect_status=403, expect_reason="Forbidden") + + #manager user of another silo + data2 = None + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointmanageruser3, + endpointpass=RDFDatabankConfig.endpointmanagerpass3) + #Access dataset for version 3 + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission?version=3", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + # Access and check zip file content - under embargo + (resp, zipfile) = self.doHTTP_GET( + resource="datasets/TestSubmission/testdir2.zip?version=3", + expect_status=403, expect_reason="Forbidden", expect_type="application/zip") + (resp, zipfile) = self.doHTTP_GET( + resource="datasets/TestSubmission/testdir.zip?version=3", + expect_status=403, expect_reason="Forbidden") + + #submitter user of another silo + data2 = None + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointsubmitteruser3, + endpointpass=RDFDatabankConfig.endpointsubmitterpass3) + #Access dataset for version 3 + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission?version=3", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + # Access and check zip file content - under embargo + (resp, zipfile) = self.doHTTP_GET( + resource="datasets/TestSubmission/testdir2.zip?version=3", + expect_status=403, expect_reason="Forbidden", expect_type="application/zip") + (resp, zipfile) = self.doHTTP_GET( + resource="datasets/TestSubmission/testdir.zip?version=3", + expect_status=403, expect_reason="Forbidden") + + + + def testPostMetadataFile(self): + """POST manifest to dataset - POST manifest.rdf to /silo_name/datasets/dataset_name""" + # Create a new dataset, check response + self.createSubmissionDataset() + # Upload metadata file, check response + zipdata = self.updateSubmissionZipfile(file_to_upload="ww1-860b-manifest.xml", filename="manifest.rdf") + # Access and check list of contents + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + subj = URIRef(self.getManifestUri("datasets/TestSubmission")) + base = self.getManifestUri("datasets/TestSubmission/") + dcterms = "http://purl.org/dc/terms/" + ore = "http://www.openarchives.org/ore/terms/" + oxds = "http://vocab.ox.ac.uk/dataset/schema#" + owl = "http://www.w3.org/2002/07/owl#" + bibo = "http://purl.org/ontology/bibo/" + geo = "http://www.w3.org/2003/01/geo/wgs84_pos#" + foaf = "http://xmlns.com/foaf/0.1/" + address = "http://schemas.talis.com/2005/address/schema#" + stype = URIRef(oxds+"DataSet") + stype2 = URIRef(bibo+"DocumentPart") + self.assertEqual(len(rdfgraph),42,'Graph length %i' %len(rdfgraph)) + self.failUnless((subj,RDF.type,stype) in rdfgraph, 'Testing submission type: '+subj+", "+stype) + self.failUnless((subj,URIRef(dcterms+"created"),None) in rdfgraph, 'dcterms:created') + self.failUnless((subj,URIRef(dcterms+"identifier"),None) in rdfgraph, 'dcterms:identifier') + self.failUnless((subj,URIRef(dcterms+"mediator"),None) in rdfgraph, 'dcterms:mediator') + self.failUnless((subj,URIRef(dcterms+"rights"),None) in rdfgraph, 'dcterms:rights') + self.failUnless((subj,URIRef(dcterms+"license"),None) in rdfgraph, 'dcterms:license') + self.failUnless((subj,URIRef(dcterms+"publisher"),None) in rdfgraph, 'dcterms:piblisher') + self.failUnless((subj,URIRef(oxds+"isEmbargoed"),None) in rdfgraph, 'oxds:isEmbargoed') + self.failUnless((subj,URIRef(oxds+"embargoedUntil"),None) in rdfgraph, 'oxds:embargoedUntil') + self.failUnless((subj,URIRef(oxds+"currentVersion"),'1') in rdfgraph, 'oxds:currentVersion') + self.failUnless((subj,URIRef(dcterms+"modified"),None) in rdfgraph, 'dcterms:modified') + self.failUnless((subj,RDF.type,stype2) in rdfgraph, 'Testing submission type: '+subj+", "+stype2) + self.failUnless((subj,URIRef(dcterms+"isPartOf"),URIRef("https://databank.ora.ox.ac.uk/ww1archives/datasets/ww1")) in rdfgraph, 'dcterms:isPartOf') + self.failUnless((subj,URIRef(dcterms+"isPartOf"),URIRef("https://databank.ora.ox.ac.uk/ww1archives/datasets/ww1-1123")) in rdfgraph, 'dcterms:isPartOf') + self.failUnless((subj,URIRef(dcterms+"creator"),"Thomas, Edward") in rdfgraph, 'dcterms:creator') + self.failUnless((subj,URIRef(dcterms+"title"),"Two Houses") in rdfgraph, 'dcterms:title') + self.failUnless((subj,URIRef(dcterms+"description"),"This manuscript is untitled but entitled 'Two Houses' in Edward Thomas Collected Poems") in rdfgraph, 'dcterms:description') + self.failUnless((subj,URIRef(dcterms+"spatial"),"London") in rdfgraph, 'dcterms:spatial') + self.failUnless((subj,URIRef(dcterms+"format"),"Notebook") in rdfgraph, 'dcterms:format') + self.failUnless((subj,URIRef(dcterms+"medium"),"Paper") in rdfgraph, 'dcterms:medium') + self.failUnless((subj,URIRef(dcterms+"type"),"Poem") in rdfgraph, 'dcterms:type') + self.failUnless((subj,URIRef(bibo+"number"),"13") in rdfgraph, 'bibo:number') + self.failUnless((subj,URIRef(dcterms+"source"),"MS Don d.28 f.13") in rdfgraph, 'dcterms:source') + self.failUnless((subj,URIRef(dcterms+"contributor"),"Everett Sharp") in rdfgraph, 'dcterms:contributor') + self.failUnless((subj,URIRef(dcterms+"identifier"),"ETBODDOND28-13.jpg") in rdfgraph, 'dcterms:identifier') + self.failUnless((subj,URIRef(dcterms+"rightsHolder"),"Copyright of The Bodleian Library, Oxford University / The Edward Thomas Literary Estate") in rdfgraph, 'dcterms:rightsHolder') + self.failUnless((subj,RDF.value,"51.501") in rdfgraph, 'rdf:value') + #self.failUnless((subj,URIRef(dcterms+"created"),"1915-07-22") in rdfgraph, 'dcterms:created') + self.failUnless((subj,URIRef(dcterms+"type"),URIRef("http://purl.org/dc/dcmitype/StillImage")) in rdfgraph, 'dcterms:type') + self.failUnless((subj,URIRef(dcterms+"spatial"),None) in rdfgraph, 'dcterms:spatial') + self.failUnless((None,URIRef(geo+"lat"),"51.501") in rdfgraph, 'geo:lat') + self.failUnless((None,URIRef(geo+"long"),"-0.1254") in rdfgraph, 'geo:long') + self.failUnless((subj,URIRef(bibo+"owner"),None) in rdfgraph, 'bibo:owner') + self.failUnless((None,RDF.type,URIRef(foaf+"Organization")) in rdfgraph, 'rdf:type') + self.failUnless((None,URIRef(foaf+"name"),"Bodleian Library, University of Oxford") in rdfgraph, 'foaf:name') + self.failUnless((None,URIRef(address+"streetAddress"),"Western Manuscripts Collections") in rdfgraph, 'address:streetAddress') + self.failUnless((None,URIRef(address+"streetAddress"),"Broad Street") in rdfgraph, 'address:streetAddress') + self.failUnless((None,URIRef(address+"localityName"),"Oxford") in rdfgraph, 'address:localityName') + self.failUnless((None,URIRef(address+"regionName"),"Oxfordshire") in rdfgraph, 'address:regionName') + self.failUnless((None,URIRef(address+"postalCode"),"OX13BG") in rdfgraph, 'address:postalCode') + self.failUnless((None,URIRef(address+"countryName"),"United Kingdom") in rdfgraph, 'address:countryName') + self.failUnless((None,URIRef(foaf+"homepage"),"http://www.bodley.ox.ac.uk/") in rdfgraph, 'foaf:homepage') + #Access state information and check + (resp, data) = self.doHTTP_GET( + resource="states/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/json") + state = data['state'] + parts = data['parts'] + self.assertEqual(len(state.keys()), 11, "States") + self.assertEqual(state['item_id'], "TestSubmission", "Submission item identifier") + self.assertEqual(len(state['versions']), 2, "Two versions") + self.assertEqual(state['versions'][0], '0', "Version 0") + self.assertEqual(state['versions'][1], '1', "Version 1") + self.assertEqual(state['currentversion'], '1', "Current version == 1") + self.assertEqual(state['rdffileformat'], 'xml', "RDF file type") + self.assertEqual(state['rdffilename'], 'manifest.rdf', "RDF file name") + self.assertEqual(len(state['files']['0']), 1, "List should contain just manifest.rdf") + self.assertEqual(len(state['files']['1']), 1, "List should contain just manifest.rdf") + self.assertEqual(len(state['metadata_files']['0']), 0, "metadata_files of version 0") + self.assertEqual(len(state['metadata_files']['1']), 0, "metadata_files of version 1") + self.assertEqual(len(state['subdir']['0']), 0, "Subdirectory count for version 0") + self.assertEqual(len(state['subdir']['1']), 0, "Subdirectory count for version 1") + self.assertEqual(state['metadata']['createdby'], RDFDatabankConfig.endpointsubmitteruser, "Created by") + self.assertEqual(state['metadata']['embargoed'], True, "Embargoed?") + self.assertEqual(len(parts.keys()), 3, "Parts") + self.assertEqual(len(parts['4=TestSubmission'].keys()), 13, "File stats for 4=TestSubmission") + self.assertEqual(len(parts['manifest.rdf'].keys()), 13, "File stats for manifest.rdf") + + # Upload metadata file by admin, check response + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointadminuser, + endpointpass=RDFDatabankConfig.endpointadminpass) + fields = [] + zipdata = open("testdata/manifest-admin.rdf").read() + files = \ + [ ("file", 'manifest.rdf', zipdata, "application/zip") + ] + (reqtype, reqdata) = SparqlQueryTestCase.encode_multipart_formdata(fields, files) + (resp,respdata) = self.doHTTP_POST( + reqdata, reqtype, + resource="datasets/TestSubmission/", + expect_status=204, expect_reason="No Content") + # Access and check list of contents + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointsubmitteruser, + endpointpass=RDFDatabankConfig.endpointsubmitterpass) + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + self.assertEqual(len(rdfgraph),44,'Graph length %i' %len(rdfgraph)) + self.failUnless((subj,URIRef(dcterms+"description"),"Changes made by admin") in rdfgraph, 'dcterms:rights') + self.failUnless((subj,URIRef(owl+"sameAs"),URIRef("http://example.org/testrdf/")) in rdfgraph, 'owl:sameAs') + + # Upload metadata file by manager, check response + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointmanageruser, + endpointpass=RDFDatabankConfig.endpointmanagerpass) + fields = [] + zipdata = open("testdata/manifest-manager.rdf").read() + files = \ + [ ("file", 'manifest.rdf', zipdata, "application/zip") + ] + (reqtype, reqdata) = SparqlQueryTestCase.encode_multipart_formdata(fields, files) + (resp,respdata) = self.doHTTP_POST( + reqdata, reqtype, + resource="datasets/TestSubmission/", + expect_status=204, expect_reason="No Content") + # Access and check list of contents + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointsubmitteruser, + endpointpass=RDFDatabankConfig.endpointsubmitterpass) + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + self.assertEqual(len(rdfgraph),45,'Graph length %i' %len(rdfgraph)) + self.failUnless((subj,URIRef(dcterms+"description"),"Changes made by manager") in rdfgraph, 'dcterms:rights') + + #Prepare data for upload + fields = [] + zipdata = open("testdata/manifest-general.rdf").read() + files = \ + [ ("file", 'manifest.rdf', zipdata, "application/zip") + ] + (reqtype, reqdata) = SparqlQueryTestCase.encode_multipart_formdata(fields, files) + # Update metadata file by submitter2, check response + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointsubmitteruser2, + endpointpass=RDFDatabankConfig.endpointsubmitterpass2) + (resp,respdata) = self.doHTTP_POST( + reqdata, reqtype, + resource="datasets/TestSubmission/", + expect_status=403, expect_reason="Forbidden") + # Access and check list of contents + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointsubmitteruser, + endpointpass=RDFDatabankConfig.endpointsubmitterpass) + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + self.assertEqual(len(rdfgraph),45,'Graph length %i' %len(rdfgraph)) + + # Update metadata file by general user, check response + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointgeneraluser, + endpointpass=RDFDatabankConfig.endpointgeneralpass) + (resp,respdata) = self.doHTTP_POST( + reqdata, reqtype, + resource="datasets/TestSubmission/", + expect_status=401, expect_reason="Unauthorized", expect_type="text/plain") + # Access and check list of contents + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointsubmitteruser, + endpointpass=RDFDatabankConfig.endpointsubmitterpass) + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + self.assertEqual(len(rdfgraph),45,'Graph length %i' %len(rdfgraph)) + + # Update metadata file by admin user 3, check response + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointadminuser3, + endpointpass=RDFDatabankConfig.endpointadminpass3) + (resp,respdata) = self.doHTTP_POST( + reqdata, reqtype, + resource="datasets/TestSubmission/", + expect_status=403, expect_reason="Forbidden") + # Access and check list of contents + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointsubmitteruser, + endpointpass=RDFDatabankConfig.endpointsubmitterpass) + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + self.assertEqual(len(rdfgraph),45,'Graph length %i' %len(rdfgraph)) + + # Update metadata file by manager user 3, check response + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointmanageruser3, + endpointpass=RDFDatabankConfig.endpointmanagerpass3) + (resp,respdata) = self.doHTTP_POST( + reqdata, reqtype, + resource="datasets/TestSubmission/", + expect_status=403, expect_reason="Forbidden") + # Access and check list of contents + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointsubmitteruser, + endpointpass=RDFDatabankConfig.endpointsubmitterpass) + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + self.assertEqual(len(rdfgraph),45,'Graph length %i' %len(rdfgraph)) + + # Update metadata file by submitter user 3, check response + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointsubmitteruser3, + endpointpass=RDFDatabankConfig.endpointsubmitterpass3) + (resp,respdata) = self.doHTTP_POST( + reqdata, reqtype, + resource="datasets/TestSubmission/", + expect_status=403, expect_reason="Forbidden") + # Access and check list of contents + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointsubmitteruser, + endpointpass=RDFDatabankConfig.endpointsubmitterpass) + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + self.assertEqual(len(rdfgraph),45,'Graph length %i' %len(rdfgraph)) + + + + def testMetadataFileUpdate(self): + """POST manifest to dataset - POST manifest.rdf to /silo_name/datasets/dataset_name""" + # Create a new dataset, check response + self.createSubmissionDataset() + # Upload metadata file, check response + zipdata = self.updateSubmissionZipfile(file_to_upload="manifest.rdf") + # Access and check list of contents + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + subj = URIRef(self.getManifestUri("datasets/TestSubmission")) + base = self.getManifestUri("datasets/TestSubmission/") + dcterms = "http://purl.org/dc/terms/" + ore = "http://www.openarchives.org/ore/terms/" + oxds = "http://vocab.ox.ac.uk/dataset/schema#" + owl = "http://www.w3.org/2002/07/owl#" + bibo = "http://purl.org/ontology/bibo/" + geo = "http://www.w3.org/2003/01/geo/wgs84_pos#" + foaf = "http://xmlns.com/foaf/0.1/" + address = "http://schemas.talis.com/2005/address/schema#" + stype = URIRef(oxds+"DataSet") + self.assertEqual(len(rdfgraph),13,'Graph length %i' %len(rdfgraph)) + self.failUnless((subj,RDF.type,stype) in rdfgraph, 'Testing submission type: '+subj+", "+stype) + self.failUnless((subj,URIRef(dcterms+"created"),None) in rdfgraph, 'dcterms:created') + self.failUnless((subj,URIRef(dcterms+"identifier"),None) in rdfgraph, 'dcterms:identifier') + self.failUnless((subj,URIRef(dcterms+"mediator"),None) in rdfgraph, 'dcterms:mediator') + self.failUnless((subj,URIRef(dcterms+"rights"),None) in rdfgraph, 'dcterms:rights') + self.failUnless((subj,URIRef(dcterms+"license"),None) in rdfgraph, 'dcterms:license') + self.failUnless((subj,URIRef(dcterms+"publisher"),None) in rdfgraph, 'dcterms:piblisher') + self.failUnless((subj,URIRef(oxds+"isEmbargoed"),None) in rdfgraph, 'oxds:isEmbargoed') + self.failUnless((subj,URIRef(oxds+"embargoedUntil"),None) in rdfgraph, 'oxds:embargoedUntil') + self.failUnless((subj,URIRef(oxds+"currentVersion"),'1') in rdfgraph, 'oxds:currentVersion') + self.failUnless((subj,URIRef(dcterms+"modified"),None) in rdfgraph, 'dcterms:modified') + self.failUnless((subj,URIRef(dcterms+"title"),"Test dataset with merged metadata") in rdfgraph, 'dcterms:title') + self.failUnless((subj,URIRef(owl+"sameAs"),URIRef("http://example.org/testrdf/")) in rdfgraph, 'owl:sameAs') + # Update metadata file, check response + zipdata = self.updateSubmissionZipfile(file_to_upload="ww1-2862-manifest.xml", filename="manifest.rdf") + # Access and check list of contents + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + stype2 = URIRef(bibo+"Document") + doctext = """
          She had a name among the children; +
          But no one loved though someone owned +
          Her, locked her out of doors at bedtime +
          And had her kittens duly drowned. +
          In Spring, nevertheless, this cat +
          Ate blackbirds, thrushes, nightingales, +
          And birds of bright voice and plume and flight, +
          As well as scraps from neighbours' pails. +
          I loathed and hated her for this; +
          One speckle on a thrush's breast +
          Was worth a million such; and yet +
          She lived long, till God gave her rest. +

          """ + self.assertEqual(len(rdfgraph),32,'Graph length %i' %len(rdfgraph)) + self.failUnless((subj,RDF.type,stype) in rdfgraph, 'Testing submission type: '+subj+", "+stype) + self.failUnless((subj,URIRef(dcterms+"created"),None) in rdfgraph, 'dcterms:created') + self.failUnless((subj,URIRef(dcterms+"identifier"),None) in rdfgraph, 'dcterms:identifier') + self.failUnless((subj,URIRef(dcterms+"mediator"),None) in rdfgraph, 'dcterms:mediator') + self.failUnless((subj,URIRef(dcterms+"rights"),None) in rdfgraph, 'dcterms:rights') + self.failUnless((subj,URIRef(dcterms+"license"),None) in rdfgraph, 'dcterms:license') + self.failUnless((subj,URIRef(dcterms+"publisher"),None) in rdfgraph, 'dcterms:publisher') + self.failUnless((subj,URIRef(oxds+"isEmbargoed"),None) in rdfgraph, 'oxds:isEmbargoed') + self.failUnless((subj,URIRef(oxds+"embargoedUntil"),None) in rdfgraph, 'oxds:embargoedUntil') + self.failUnless((subj,URIRef(oxds+"currentVersion"),'2') in rdfgraph, 'oxds:currentVersion') + self.failUnless((subj,URIRef(dcterms+"modified"),None) in rdfgraph, 'dcterms:modified') + self.failUnless((subj,URIRef(dcterms+"title"),'A Cat') in rdfgraph, 'dcterms:title') + self.failUnless((subj,URIRef(owl+"sameAs"),URIRef("http://example.org/testrdf/")) in rdfgraph, 'owl:sameAs') + self.failUnless((subj,URIRef(owl+"sameAs"),URIRef("http://databank.ora.ox.ac.uk/ww1archives/datasets/ww1-2862")) in rdfgraph, 'owl:sameAs') + self.failUnless((subj,RDF.type,stype2) in rdfgraph, 'Testing submission type: '+subj+", "+stype2) + self.failUnless((subj,URIRef(dcterms+"isPartOf"),URIRef("https://databank.ora.ox.ac.uk/ww1archives/datasets/ww1")) in rdfgraph, 'dcterms:isPartOf') + self.failUnless((subj,URIRef(dcterms+"creator"),"Thomas, Edward") in rdfgraph, 'dcterms:creator') + self.failUnless((subj,URIRef(dcterms+"type"),"Poem") in rdfgraph, 'dcterms:type') + self.failUnless((subj,URIRef(dcterms+"type"),URIRef("http://purl.org/dc/dcmitype/Text")) in rdfgraph, 'dcterms:type') + self.failUnless((subj,URIRef(dcterms+"rightsHolder"),"Copyright Edward Thomas, 1979, reproduced under licence from Faber and Faber Ltd.") in rdfgraph, 'dcterms:rightsHolder') + self.failUnless((subj,RDF.value,Literal(doctext)) in rdfgraph, 'rdf:value') + self.failUnless((subj,URIRef(dcterms+"source"),"Edward Thomas Collected Poems") in rdfgraph, 'dcterms:source') + #self.failUnless((subj,URIRef(dcterms+"created"),"1979-01-01/1979-12-31") in rdfgraph, 'dcterms:created') + self.failUnless((subj,URIRef(bibo+"editor"),"Thomas, George") in rdfgraph, 'bibo:editor') + self.failUnless((subj,URIRef(bibo+"owner"),None) in rdfgraph, 'bibo:owner') + self.failUnless((None,RDF.type,URIRef(foaf+"Organization")) in rdfgraph, 'rdf:type') + self.failUnless((None,URIRef(foaf+"name"),"ProQuest") in rdfgraph, 'foaf:name') + self.failUnless((None,URIRef(foaf+"homepage"),"http://lion.chadwyck.co.uk/") in rdfgraph, 'foaf:homepage') + self.failUnless((subj,URIRef(dcterms+"publisher"),None) in rdfgraph, 'dcterms:publisher') + self.failUnless((None,URIRef(foaf+"name"),"Faber and Faber") in rdfgraph, 'foaf:name') + self.failUnless((None,URIRef(address+"localityName"),"London") in rdfgraph, 'address:localityName') + + #Access state information and check + (resp, data) = self.doHTTP_GET( + resource="states/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/json") + state = data['state'] + parts = data['parts'] + self.assertEqual(len(state.keys()), 11, "States") + self.assertEqual(state['item_id'], "TestSubmission", "Submission item identifier") + self.assertEqual(len(state['versions']), 3, "Three versions") + self.assertEqual(state['versions'][0], '0', "Version 0") + self.assertEqual(state['versions'][1], '1', "Version 1") + self.assertEqual(state['versions'][2], '2', "Version 2") + self.assertEqual(state['currentversion'], '2', "Current version == 2") + self.assertEqual(state['rdffileformat'], 'xml', "RDF file type") + self.assertEqual(state['rdffilename'], 'manifest.rdf', "RDF file name") + self.assertEqual(len(state['files']['0']), 1, "List should contain just manifest.rdf") + self.assertEqual(len(state['files']['1']), 1, "List should contain just manifest.rdf") + self.assertEqual(len(state['files']['2']), 1, "List should contain just manifest.rdf") + self.assertEqual(len(state['metadata_files']['0']), 0, "metadata_files of version 0") + self.assertEqual(len(state['metadata_files']['1']), 0, "metadata_files of version 1") + self.assertEqual(len(state['metadata_files']['2']), 0, "metadata_files of version 2") + self.assertEqual(len(state['subdir']['0']), 0, "Subdirectory count for version 0") + self.assertEqual(len(state['subdir']['1']), 0, "Subdirectory count for version 1") + self.assertEqual(len(state['subdir']['2']), 0, "Subdirectory count for version 2") + self.assertEqual(state['metadata']['createdby'], RDFDatabankConfig.endpointsubmitteruser, "Created by") + self.assertEqual(state['metadata']['embargoed'], True, "Embargoed?") + self.assertEqual(len(parts.keys()), 3, "Parts") + self.assertEqual(len(parts['4=TestSubmission'].keys()), 13, "File stats for 4=TestSubmission") + self.assertEqual(len(parts['manifest.rdf'].keys()), 13, "File stats for manifest.rdf") + + + + def testMetadataFileDelete(self): + """Delete manifest in dataset - DELETE /silo_name/datasets/dataset_name/manifest.rdf""" + # Create a new dataset, check response + self.createSubmissionDataset() + # Delete metadata file, check response + resp = self.doHTTP_DELETE( + resource="datasets/TestSubmission/manifest.rdf", + expect_status=403, expect_reason="Forbidden") + # Access and check list of contents + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + subj = URIRef(self.getManifestUri("datasets/TestSubmission")) + dcterms = "http://purl.org/dc/terms/" + oxds = "http://vocab.ox.ac.uk/dataset/schema#" + stype = URIRef(oxds+"DataSet") + self.assertEqual(len(rdfgraph),10,'Graph length %i' %len(rdfgraph)) + self.failUnless((subj,RDF.type,stype) in rdfgraph, 'Testing submission type: '+subj+", "+stype) + self.failUnless((subj,URIRef(dcterms+"created"),None) in rdfgraph, 'dcterms:created') + self.failUnless((subj,URIRef(dcterms+"identifier"),None) in rdfgraph, 'dcterms:identifier') + self.failUnless((subj,URIRef(dcterms+"mediator"),None) in rdfgraph, 'dcterms:mediator') + self.failUnless((subj,URIRef(dcterms+"rights"),None) in rdfgraph, 'dcterms:rights') + self.failUnless((subj,URIRef(dcterms+"license"),None) in rdfgraph, 'dcterms:license') + self.failUnless((subj,URIRef(dcterms+"publisher"),None) in rdfgraph, 'dcterms:publisher') + self.failUnless((subj,URIRef(oxds+"isEmbargoed"),None) in rdfgraph, 'oxds:isEmbargoed') + self.failUnless((subj,URIRef(oxds+"embargoedUntil"),None) in rdfgraph, 'oxds:embargoedUntil') + self.failUnless((subj,URIRef(oxds+"currentVersion"),'0') in rdfgraph, 'oxds:currentVersion') + + # Delete metadata file by admin, check response + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointadminuser, + endpointpass=RDFDatabankConfig.endpointadminpass) + resp = self.doHTTP_DELETE( + resource="datasets/TestSubmission/manifest.rdf", + expect_status=403, expect_reason="Forbidden") + # Access and check list of contents + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointsubmitteruser, + endpointpass=RDFDatabankConfig.endpointsubmitterpass) + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + self.assertEqual(len(rdfgraph),10,'Graph length %i' %len(rdfgraph)) + self.failUnless((subj,URIRef(oxds+"currentVersion"),'0') in rdfgraph, 'oxds:currentVersion') + + # Delete metadata file by manager, check response + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointmanageruser, + endpointpass=RDFDatabankConfig.endpointmanagerpass) + resp = self.doHTTP_DELETE( + resource="datasets/TestSubmission/manifest.rdf", + expect_status=403, expect_reason="Forbidden") + # Access and check list of contents + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointsubmitteruser, + endpointpass=RDFDatabankConfig.endpointsubmitterpass) + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + self.assertEqual(len(rdfgraph),10,'Graph length %i' %len(rdfgraph)) + self.failUnless((subj,URIRef(oxds+"currentVersion"),'0') in rdfgraph, 'oxds:currentVersion') + + # Delete metadata file by submitter2, check response + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointsubmitteruser2, + endpointpass=RDFDatabankConfig.endpointsubmitterpass2) + resp = self.doHTTP_DELETE( + resource="datasets/TestSubmission/manifest.rdf", + expect_status=403, expect_reason="Forbidden") + # Access and check list of contents + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointsubmitteruser, + endpointpass=RDFDatabankConfig.endpointsubmitterpass) + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + self.assertEqual(len(rdfgraph),10,'Graph length %i' %len(rdfgraph)) + self.failUnless((subj,URIRef(oxds+"currentVersion"),'0') in rdfgraph, 'oxds:currentVersion') + + # Delete metadata file by general user, check response + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointgeneraluser, + endpointpass=RDFDatabankConfig.endpointgeneralpass) + resp = self.doHTTP_DELETE( + resource="datasets/TestSubmission/manifest.rdf", + expect_status=401, expect_reason="Unauthorized") + # Access and check list of contents + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointsubmitteruser, + endpointpass=RDFDatabankConfig.endpointsubmitterpass) + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + self.assertEqual(len(rdfgraph),10,'Graph length %i' %len(rdfgraph)) + self.failUnless((subj,URIRef(oxds+"currentVersion"),'0') in rdfgraph, 'oxds:currentVersion') + + # Delete metadata file by admin user 3, check response + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointadminuser3, + endpointpass=RDFDatabankConfig.endpointadminpass3) + resp = self.doHTTP_DELETE( + resource="datasets/TestSubmission/manifest.rdf", + expect_status=403, expect_reason="Forbidden") + # Access and check list of contents + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointsubmitteruser, + endpointpass=RDFDatabankConfig.endpointsubmitterpass) + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + self.assertEqual(len(rdfgraph),10,'Graph length %i' %len(rdfgraph)) + self.failUnless((subj,URIRef(oxds+"currentVersion"),'0') in rdfgraph, 'oxds:currentVersion') + + # Delete metadata file by manager user 3, check response + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointmanageruser3, + endpointpass=RDFDatabankConfig.endpointmanagerpass3) + resp = self.doHTTP_DELETE( + resource="datasets/TestSubmission/manifest.rdf", + expect_status=403, expect_reason="Forbidden") + # Access and check list of contents + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointsubmitteruser, + endpointpass=RDFDatabankConfig.endpointsubmitterpass) + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + self.assertEqual(len(rdfgraph),10,'Graph length %i' %len(rdfgraph)) + self.failUnless((subj,URIRef(oxds+"currentVersion"),'0') in rdfgraph, 'oxds:currentVersion') + + # Delete metadata file by submitter user 3, check response + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointsubmitteruser3, + endpointpass=RDFDatabankConfig.endpointsubmitterpass3) + resp = self.doHTTP_DELETE( + resource="datasets/TestSubmission/manifest.rdf", + expect_status=403, expect_reason="Forbidden") + # Access and check list of contents + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointsubmitteruser, + endpointpass=RDFDatabankConfig.endpointsubmitterpass) + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + self.assertEqual(len(rdfgraph),10,'Graph length %i' %len(rdfgraph)) + self.failUnless((subj,URIRef(oxds+"currentVersion"),'0') in rdfgraph, 'oxds:currentVersion') + + + + def testPutCreateFile(self): + """PUT file contents to new filename - PUT file contents to /silo_name/datasets/dataset_name/file_name""" + # Create a new dataset, check response + self.createSubmissionDataset() + # Put zip file, check response + zipdata = open("testdata/testdir.zip").read() + (resp, respdata) = self.doHTTP_PUT(zipdata, resource="datasets/TestSubmission/testdir.zip", + expect_status=201, expect_reason="Created", expect_type="text/plain") + LHobtained = resp.getheader('Content-Location', None) + LHexpected = "%sdatasets/TestSubmission/testdir.zip"%self._endpointpath + self.assertEquals(LHobtained, LHexpected, 'Content-Location not correct') + # Access and check list of contents + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + subj = URIRef(self.getManifestUri("datasets/TestSubmission")) + base = self.getManifestUri("datasets/TestSubmission/") + dcterms = "http://purl.org/dc/terms/" + ore = "http://www.openarchives.org/ore/terms/" + oxds = "http://vocab.ox.ac.uk/dataset/schema#" + stype = URIRef(oxds+"DataSet") + self.assertEqual(len(rdfgraph),12,'Graph length %i' %len(rdfgraph)) + self.failUnless((subj,RDF.type,stype) in rdfgraph, 'Testing submission type: '+subj+", "+stype) + self.failUnless((subj,URIRef(dcterms+"created"),None) in rdfgraph, 'dcterms:created') + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"testdir.zip")) in rdfgraph, 'ore:aggregates testdir.zip') + self.failUnless((subj,URIRef(dcterms+"identifier"),None) in rdfgraph, 'dcterms:identifier') + self.failUnless((subj,URIRef(dcterms+"mediator"),None) in rdfgraph, 'dcterms:mediator') + self.failUnless((subj,URIRef(dcterms+"rights"),None) in rdfgraph, 'dcterms:rights') + self.failUnless((subj,URIRef(dcterms+"license"),None) in rdfgraph, 'dcterms:license') + self.failUnless((subj,URIRef(dcterms+"publisher"),None) in rdfgraph, 'dcterms:publisher') + self.failUnless((subj,URIRef(oxds+"isEmbargoed"),None) in rdfgraph, 'oxds:isEmbargoed') + self.failUnless((subj,URIRef(oxds+"embargoedUntil"),None) in rdfgraph, 'oxds:embargoedUntil') + self.failUnless((subj,URIRef(oxds+"currentVersion"),'1') in rdfgraph, 'oxds:currentVersion') + self.failUnless((subj,URIRef(dcterms+"modified"),None) in rdfgraph, 'dcterms:modified') + # Access and check zip file content + (resp, zipfile) = self.doHTTP_GET( + resource="datasets/TestSubmission/testdir.zip", + expect_status=200, expect_reason="OK", expect_type="application/zip") + self.assertEqual(zipdata, zipfile, "Difference between local and remote zipfile!") + #Access state information and check + (resp, data) = self.doHTTP_GET( + resource="states/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/json") + state = data['state'] + parts = data['parts'] + self.assertEqual(len(state.keys()), 11, "States") + self.assertEqual(state['item_id'], "TestSubmission", "Submission item identifier") + self.assertEqual(len(state['versions']), 2, "Two versions") + self.assertEqual(state['versions'][0], '0', "Version 0") + self.assertEqual(state['versions'][1], '1', "Version 1") + self.assertEqual(state['currentversion'], '1', "Current version == 1") + self.assertEqual(state['rdffileformat'], 'xml', "RDF file type") + self.assertEqual(state['rdffilename'], 'manifest.rdf', "RDF file name") + self.assertEqual(state['files']['0'], ['manifest.rdf'], "List should contain just manifest.rdf") + self.assertEqual(len(state['files']['1']), 2, "List should contain manifest.rdf and testdir.zip") + self.assertEqual(len(state['metadata_files']['0']), 0, "metadata_files of version 0") + self.assertEqual(len(state['metadata_files']['1']), 0, "metadata_files of version 1") + self.assertEqual(len(state['subdir']['0']), 0, "Subdirectory count for version 0") + self.assertEqual(len(state['subdir']['1']), 0, "Subdirectory count for version 1") + self.assertEqual(state['metadata']['createdby'], RDFDatabankConfig.endpointsubmitteruser, "Created by") + self.assertEqual(state['metadata']['embargoed'], True, "Embargoed?") + self.assertEqual(len(parts.keys()), 4, "Parts") + self.assertEqual(len(parts['4=TestSubmission'].keys()), 13, "File stats for 4=TestSubmission") + self.assertEqual(len(parts['manifest.rdf'].keys()), 13, "File stats for manifest.rdf") + self.assertEqual(len(parts['testdir.zip'].keys()), 13, "File stats for testdir.zip") + + # Put zip file by admin, check response + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointadminuser, + endpointpass=RDFDatabankConfig.endpointadminpass) + zipdata = open("testdata/testdir2.zip").read() + (resp, respdata) = self.doHTTP_PUT(zipdata, resource="datasets/TestSubmission/testdir2.zip", + expect_status=201, expect_reason="Created", expect_type="text/plain") + LHobtained = resp.getheader('Content-Location', None) + LHexpected = "%sdatasets/TestSubmission/testdir2.zip"%self._endpointpath + self.assertEquals(LHobtained, LHexpected, 'Content-Location not correct') + # Access and check list of contents + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointsubmitteruser, + endpointpass=RDFDatabankConfig.endpointsubmitterpass) + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + self.assertEqual(len(rdfgraph),13,'Graph length %i' %len(rdfgraph)) + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"testdir2.zip")) in rdfgraph, 'ore:aggregates testdir2.zip') + self.failUnless((subj,URIRef(oxds+"currentVersion"),'2') in rdfgraph, 'oxds:currentVersion') + # Access and check zip file content + (resp, zipfile) = self.doHTTP_GET( + resource="datasets/TestSubmission/testdir2.zip", + expect_status=200, expect_reason="OK", expect_type="application/zip") + self.assertEqual(zipdata, zipfile, "Difference between local and remote zipfile!") + + # Put zip file by manager, check response + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointmanageruser, + endpointpass=RDFDatabankConfig.endpointmanagerpass) + zipdata = open("testdata/testrdf.zip").read() + (resp, respdata) = self.doHTTP_PUT(zipdata, resource="datasets/TestSubmission/testrdf.zip", + expect_status=201, expect_reason="Created", expect_type="text/plain") + LHobtained = resp.getheader('Content-Location', None) + LHexpected = "%sdatasets/TestSubmission/testrdf.zip"%self._endpointpath + self.assertEquals(LHobtained, LHexpected, 'Content-Location not correct') + # Access and check zip file content + (resp, zipfile) = self.doHTTP_GET( + resource="datasets/TestSubmission/testrdf.zip", + expect_status=200, expect_reason="OK", expect_type="application/zip") + self.assertEqual(zipdata, zipfile, "Difference between local and remote zipfile!") + # Access and check list of contents + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointsubmitteruser, + endpointpass=RDFDatabankConfig.endpointsubmitterpass) + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + self.assertEqual(len(rdfgraph),14,'Graph length %i' %len(rdfgraph)) + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"testrdf.zip")) in rdfgraph, 'ore:aggregates testrdf.zip') + self.failUnless((subj,URIRef(oxds+"currentVersion"),'3') in rdfgraph, 'oxds:currentVersion') + + zipdata = open("testdata/testrdf2.zip").read() + + # Put file by submitter2, check response + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointsubmitteruser2, + endpointpass=RDFDatabankConfig.endpointsubmitterpass2) + (resp, respdata) = self.doHTTP_PUT(zipdata, resource="datasets/TestSubmission/testrdf2.zip", + expect_status=403, expect_reason="Forbidden", expect_type="text/plain") + # Access and check zip file content + (resp, zipfile) = self.doHTTP_GET( + resource="datasets/TestSubmission/testrdf.zip", + expect_status=403, expect_reason="Forbidden", expect_type="application/zip") + # Access and check list of contents + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointsubmitteruser, + endpointpass=RDFDatabankConfig.endpointsubmitterpass) + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + self.assertEqual(len(rdfgraph),14,'Graph length %i' %len(rdfgraph)) + self.assertFalse((subj,URIRef(ore+"aggregates"),URIRef(base+"testrdf2.zip")) in rdfgraph, 'testrdf2.zip has been aggregated') + self.failUnless((subj,URIRef(oxds+"currentVersion"),'3') in rdfgraph, 'oxds:currentVersion') + + # Put file by general user, check response + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointgeneraluser, + endpointpass=RDFDatabankConfig.endpointgeneralpass) + (resp, respdata) = self.doHTTP_PUT(zipdata, resource="datasets/TestSubmission/testrdf2.zip", + expect_status=401, expect_reason="Unauthorized", expect_type="text/plain") + + # Access and check zip file content + (resp, zipfile) = self.doHTTP_GET( + resource="datasets/TestSubmission/testrdf2.zip", + expect_status=401, expect_reason="Unauthorized", expect_type="text/plain") + # Access and check list of contents + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointsubmitteruser, + endpointpass=RDFDatabankConfig.endpointsubmitterpass) + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + self.assertEqual(len(rdfgraph),14,'Graph length %i' %len(rdfgraph)) + self.assertFalse((subj,URIRef(ore+"aggregates"),URIRef(base+"testrdf2.zip")) in rdfgraph, 'testrdf2.zip has been aggregated') + self.failUnless((subj,URIRef(oxds+"currentVersion"),'3') in rdfgraph, 'oxds:currentVersion') + + # Put file by admin user3, check response + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointadminuser3, + endpointpass=RDFDatabankConfig.endpointadminpass3) + (resp, respdata) = self.doHTTP_PUT(zipdata, resource="datasets/TestSubmission/testrdf2.zip", + expect_status=403, expect_reason="Forbidden", expect_type="text/plain") + # Access and check zip file content + (resp, zipfile) = self.doHTTP_GET( + resource="datasets/TestSubmission/testrdf2.zip", + expect_status=403, expect_reason="Forbidden", expect_type="application/zip") + # Access and check list of contents + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointsubmitteruser, + endpointpass=RDFDatabankConfig.endpointsubmitterpass) + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + self.assertEqual(len(rdfgraph),14,'Graph length %i' %len(rdfgraph)) + self.assertFalse((subj,URIRef(ore+"aggregates"),URIRef(base+"testrdf2.zip")) in rdfgraph, 'testrdf2.zip has been aggregated') + self.failUnless((subj,URIRef(oxds+"currentVersion"),'3') in rdfgraph, 'oxds:currentVersion') + + # Put file by manager user 3, check response + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointmanageruser3, + endpointpass=RDFDatabankConfig.endpointmanagerpass3) + (resp, respdata) = self.doHTTP_PUT(zipdata, resource="datasets/TestSubmission/testrdf2.zip", + expect_status=403, expect_reason="Forbidden", expect_type="application/zip") + # Access and check zip file content + (resp, zipfile) = self.doHTTP_GET( + resource="datasets/TestSubmission/testrdf2.zip", + expect_status=403, expect_reason="Forbidden", expect_type="application/zip") + # Access and check list of contents + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointsubmitteruser, + endpointpass=RDFDatabankConfig.endpointsubmitterpass) + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + self.assertEqual(len(rdfgraph),14,'Graph length %i' %len(rdfgraph)) + self.assertFalse((subj,URIRef(ore+"aggregates"),URIRef(base+"testrdf2.zip")) in rdfgraph, 'testrdf2.zip has been aggregated') + self.failUnless((subj,URIRef(oxds+"currentVersion"),'3') in rdfgraph, 'oxds:currentVersion') + + # Put file by submitter user 3, check response + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointsubmitteruser3, + endpointpass=RDFDatabankConfig.endpointsubmitterpass3) + (resp, respdata) = self.doHTTP_PUT(zipdata, resource="datasets/TestSubmission/testrdf2.zip", + expect_status=403, expect_reason="Forbidden", expect_type="application/zip") + # Access and check zip file content + (resp, zipfile) = self.doHTTP_GET( + resource="datasets/TestSubmission/testrdf2.zip", + expect_status=403, expect_reason="Forbidden", expect_type="application/zip") + # Access and check list of contents + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointsubmitteruser, + endpointpass=RDFDatabankConfig.endpointsubmitterpass) + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + self.assertEqual(len(rdfgraph),14,'Graph length %i' %len(rdfgraph)) + self.assertFalse((subj,URIRef(ore+"aggregates"),URIRef(base+"testrdf2.zip")) in rdfgraph, 'testrdf2.zip has been aggregated') + self.failUnless((subj,URIRef(oxds+"currentVersion"),'3') in rdfgraph, 'oxds:currentVersion') + + + + def testPutUpdateFile(self): + """PUT file contents to existing filename - PUT file contents to /silo_name/datasets/dataset_name/file_name""" + # Create a new dataset, check response + self.createSubmissionDataset() + # Upload zip file, check response + zipdata = self.uploadSubmissionZipfile(file_to_upload="testdir.zip") + # Access content + (resp, respdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + # Put zip file - create, check response + zipdata2 = open("testdata/testrdf3.zip").read() + (resp, respdata) = self.doHTTP_PUT(zipdata2, resource="datasets/TestSubmission/testrdf3.zip", + expect_status=201, expect_reason="Created", expect_type="text/plain") + LHobtained = resp.getheader('Content-Location', None) + LHexpected = "%sdatasets/TestSubmission/testrdf3.zip"%self._endpointpath + self.assertEquals(LHobtained, LHexpected, 'Content-Location not correct') + # Access and check list of contents + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + subj = URIRef(self.getManifestUri("datasets/TestSubmission")) + oxds = "http://vocab.ox.ac.uk/dataset/schema#" + self.assertEqual(len(rdfgraph),13,'Graph length %i' %len(rdfgraph)) + self.failUnless((subj,URIRef(oxds+"currentVersion"),'2') in rdfgraph, 'oxds:currentVersion') + # Access and check zip file content + (resp, zipfile) = self.doHTTP_GET( + resource="datasets/TestSubmission/testdir.zip", + expect_status=200, expect_reason="OK", expect_type="application/zip") + self.assertEqual(zipdata, zipfile, "Difference between local and remote zipfile!") + (resp, zipfile2) = self.doHTTP_GET( + resource="datasets/TestSubmission/testrdf3.zip", + expect_status=200, expect_reason="OK", expect_type="application/zip") + self.assertEqual(zipdata2, zipfile2, "Difference between local and remote zipfile!") + + # Put zip file - update testrdf3, check response + zipdata3 = open("testdata/testrdf2.zip").read() + (resp, respdata) = self.doHTTP_PUT(zipdata3, resource="datasets/TestSubmission/testrdf3.zip", + expect_status=204, expect_reason="No Content", expect_type="text/plain") + # Access and check list of contents + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + base = self.getManifestUri("datasets/TestSubmission/") + dcterms = "http://purl.org/dc/terms/" + ore = "http://www.openarchives.org/ore/terms/" + stype = URIRef(oxds+"DataSet") + self.assertEqual(len(rdfgraph),13,'Graph length %i' %len(rdfgraph)) + self.failUnless((subj,RDF.type,stype) in rdfgraph, 'Testing submission type: '+subj+", "+stype) + self.failUnless((subj,URIRef(dcterms+"created"),None) in rdfgraph, 'dcterms:created') + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"testdir.zip")) in rdfgraph, 'ore:aggregates testdir.zip') + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"testrdf3.zip")) in rdfgraph, 'ore:aggregates testrdf3.zip') + self.failUnless((subj,URIRef(dcterms+"identifier"),None) in rdfgraph, 'dcterms:identifier') + self.failUnless((subj,URIRef(dcterms+"mediator"),None) in rdfgraph, 'dcterms:mediator') + self.failUnless((subj,URIRef(dcterms+"rights"),None) in rdfgraph, 'dcterms:rights') + self.failUnless((subj,URIRef(dcterms+"license"),None) in rdfgraph, 'dcterms:license') + self.failUnless((subj,URIRef(dcterms+"publisher"),None) in rdfgraph, 'dcterms:publisher') + self.failUnless((subj,URIRef(oxds+"isEmbargoed"),None) in rdfgraph, 'oxds:isEmbargoed') + self.failUnless((subj,URIRef(oxds+"embargoedUntil"),None) in rdfgraph, 'oxds:embargoedUntil') + self.failUnless((subj,URIRef(oxds+"currentVersion"),'3') in rdfgraph, 'oxds:currentVersion') + self.failUnless((subj,URIRef(dcterms+"modified"),None) in rdfgraph, 'dcterms:modified') + # Access and check zip file content + (resp, zipfile) = self.doHTTP_GET( + resource="datasets/TestSubmission/testdir.zip", + expect_status=200, expect_reason="OK", expect_type="application/zip") + self.assertEqual(zipdata, zipfile, "Difference between local and remote zipfile!") + (resp, zipfile2) = self.doHTTP_GET( + resource="datasets/TestSubmission/testrdf3.zip", + expect_status=200, expect_reason="OK", expect_type="application/zip") + f = open('/tmp/testrdf3.zip', 'wb') + f.write(zipfile2) + f.close() + self.assertEqual(zipdata3, zipfile2, "Difference between local and remote zipfile!") + #Access state information and check + (resp, data) = self.doHTTP_GET( + resource="states/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/json") + state = data['state'] + parts = data['parts'] + self.assertEqual(len(state.keys()), 11, "States") + self.assertEqual(state['item_id'], "TestSubmission", "Submission item identifier") + self.assertEqual(len(state['versions']), 4, "Four versions") + self.assertEqual(state['versions'][0], '0', "Version 0") + self.assertEqual(state['versions'][1], '1', "Version 1") + self.assertEqual(state['versions'][2], '2', "Version 2") + self.assertEqual(state['versions'][3], '3', "Version 3") + self.assertEqual(state['currentversion'], '3', "Current version == 3") + self.assertEqual(state['rdffileformat'], 'xml', "RDF file type") + self.assertEqual(state['rdffilename'], 'manifest.rdf', "RDF file name") + self.assertEqual(state['files']['0'], ['manifest.rdf'], "List should contain just manifest.rdf") + self.assertEqual(len(state['files']['1']), 2, "List should contain manifest.rdf and testdir.zip") + self.assertEqual(len(state['files']['2']), 3, "List should contain manifest.rdf, testdir.zip and testrdf3.zip") + self.assertEqual(len(state['files']['3']), 3, "List should contain manifest.rdf, testdir.zip and testrdf3.zip") + self.assertEqual(len(state['metadata_files']['0']), 0, "metadata_files of version 0") + self.assertEqual(len(state['metadata_files']['1']), 0, "metadata_files of version 1") + self.assertEqual(len(state['metadata_files']['2']), 0, "metadata_files of version 2") + self.assertEqual(len(state['metadata_files']['3']), 0, "metadata_files of version 3") + self.assertEqual(len(state['subdir']['0']), 0, "Subdirectory count for version 0") + self.assertEqual(len(state['subdir']['1']), 0, "Subdirectory count for version 1") + self.assertEqual(len(state['subdir']['2']), 0, "Subdirectory count for version 2") + self.assertEqual(len(state['subdir']['3']), 0, "Subdirectory count for version 3") + self.assertEqual(state['metadata']['createdby'], RDFDatabankConfig.endpointsubmitteruser, "Created by") + self.assertEqual(state['metadata']['embargoed'], True, "Embargoed?") + self.assertEqual(len(parts.keys()), 5, "Parts") + self.assertEqual(len(parts['4=TestSubmission'].keys()), 13, "File stats for 4=TestSubmission") + self.assertEqual(len(parts['manifest.rdf'].keys()), 13, "File stats for manifest.rdf") + self.assertEqual(len(parts['testdir.zip'].keys()), 13, "File stats for testdir.zip") + self.assertEqual(len(parts['testrdf3.zip'].keys()), 13, "File stats for testrdf3.zip") + + # Put zip file - update testdir, check response + zipdata4 = open("testdata/testdir2.zip").read() + (resp, respdata) = self.doHTTP_PUT(zipdata4, resource="datasets/TestSubmission/testdir.zip", + expect_status=204, expect_reason="No Content", expect_type="text/plain") + # Access and check list of contents + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + self.assertEqual(len(rdfgraph),13,'Graph length %i' %len(rdfgraph)) + self.failUnless((subj,RDF.type,stype) in rdfgraph, 'Testing submission type: '+subj+", "+stype) + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"testdir.zip")) in rdfgraph, 'ore:aggregates testdir.zip') + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"testrdf3.zip")) in rdfgraph, 'ore:aggregates testrdf3.zip') + self.failUnless((subj,URIRef(oxds+"currentVersion"),'4') in rdfgraph, 'oxds:currentVersion') + # Access and check zip file content + (resp, zipfile) = self.doHTTP_GET( + resource="datasets/TestSubmission/testdir.zip", + expect_status=200, expect_reason="OK", expect_type="application/zip") + self.assertEqual(zipdata4, zipfile, "Difference between local and remote zipfile!") + (resp, zipfile2) = self.doHTTP_GET( + resource="datasets/TestSubmission/testrdf3.zip", + expect_status=200, expect_reason="OK", expect_type="application/zip") + self.assertEqual(zipdata3, zipfile2, "Difference between local and remote zipfile!") + #Access state information and check + (resp, data) = self.doHTTP_GET( + resource="states/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/json") + state = data['state'] + parts = data['parts'] + self.assertEqual(len(state.keys()), 11, "States") + self.assertEqual(state['item_id'], "TestSubmission", "Submission item identifier") + self.assertEqual(len(state['versions']), 5, "Five versions") + self.assertEqual(state['versions'][0], '0', "Version 0") + self.assertEqual(state['versions'][1], '1', "Version 1") + self.assertEqual(state['versions'][2], '2', "Version 2") + self.assertEqual(state['versions'][3], '3', "Version 3") + self.assertEqual(state['versions'][4], '4', "Version 4") + self.assertEqual(state['currentversion'], '4', "Current version == 4") + self.assertEqual(state['rdffileformat'], 'xml', "RDF file type") + self.assertEqual(state['rdffilename'], 'manifest.rdf', "RDF file name") + self.assertEqual(state['files']['0'], ['manifest.rdf'], "List should contain just manifest.rdf") + self.assertEqual(len(state['files']['1']), 2, "List should contain manifest.rdf and testdir.zip") + self.assertEqual(len(state['files']['2']), 3, "List should contain manifest.rdf, testdir.zip and testrdf3.zip") + self.assertEqual(len(state['files']['3']), 3, "List should contain manifest.rdf, testdir.zip and testrdf3.zip") + self.assertEqual(len(state['files']['4']), 3, "List should contain manifest.rdf, testdir.zip and testrdf3.zip") + self.assertEqual(len(state['metadata_files']['0']), 0, "metadata_files of version 0") + self.assertEqual(len(state['metadata_files']['1']), 0, "metadata_files of version 1") + self.assertEqual(len(state['metadata_files']['2']), 0, "metadata_files of version 2") + self.assertEqual(len(state['metadata_files']['3']), 0, "metadata_files of version 3") + self.assertEqual(len(state['metadata_files']['4']), 0, "metadata_files of version 4") + self.assertEqual(len(state['subdir']['0']), 0, "Subdirectory count for version 0") + self.assertEqual(len(state['subdir']['1']), 0, "Subdirectory count for version 1") + self.assertEqual(len(state['subdir']['2']), 0, "Subdirectory count for version 2") + self.assertEqual(len(state['subdir']['3']), 0, "Subdirectory count for version 3") + self.assertEqual(len(state['subdir']['4']), 0, "Subdirectory count for version 4") + self.assertEqual(state['metadata']['createdby'], RDFDatabankConfig.endpointsubmitteruser, "Created by") + self.assertEqual(state['metadata']['embargoed'], True, "Embargoed?") + self.assertEqual(len(parts.keys()), 5, "Parts") + self.assertEqual(len(parts['4=TestSubmission'].keys()), 13, "File stats for 4=TestSubmission") + self.assertEqual(len(parts['manifest.rdf'].keys()), 13, "File stats for manifest.rdf") + self.assertEqual(len(parts['testdir.zip'].keys()), 13, "File stats for testdir.zip") + self.assertEqual(len(parts['testrdf3.zip'].keys()), 13, "File stats for testrdf3.zip") + + # Put zip file by admin - update testrdf3, check response + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointadminuser, + endpointpass=RDFDatabankConfig.endpointadminpass) + zipdata5 = open("testdata/testrdf.zip").read() + (resp, respdata) = self.doHTTP_PUT(zipdata5, resource="datasets/TestSubmission/testrdf3.zip", + expect_status=204, expect_reason="No Content", expect_type="text/plain") + # Access and check list of contents + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointsubmitteruser, + endpointpass=RDFDatabankConfig.endpointsubmitterpass) + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + self.assertEqual(len(rdfgraph),13,'Graph length %i' %len(rdfgraph)) + self.failUnless((subj,RDF.type,stype) in rdfgraph, 'Testing submission type: '+subj+", "+stype) + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"testdir.zip")) in rdfgraph, 'ore:aggregates testdir.zip') + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"testrdf3.zip")) in rdfgraph, 'ore:aggregates testrdf3.zip') + self.failUnless((subj,URIRef(oxds+"currentVersion"),'5') in rdfgraph, 'oxds:currentVersion') + # Access and check zip file content + (resp, zipfile) = self.doHTTP_GET( + resource="datasets/TestSubmission/testdir.zip", + expect_status=200, expect_reason="OK", expect_type="application/zip") + self.assertEqual(zipdata4, zipfile, "Difference between local and remote zipfile!") + (resp, zipfile2) = self.doHTTP_GET( + resource="datasets/TestSubmission/testrdf3.zip", + expect_status=200, expect_reason="OK", expect_type="application/zip") + self.assertEqual(zipdata5, zipfile2, "Difference between local and remote zipfile!") + + # Put zip file by manager - update testrdf3, check response + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointmanageruser, + endpointpass=RDFDatabankConfig.endpointmanagerpass) + (resp, respdata) = self.doHTTP_PUT(zipdata, resource="datasets/TestSubmission/testdir.zip", + expect_status=204, expect_reason="No Content", expect_type="text/plain") + # Access and check list of contents + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointsubmitteruser, + endpointpass=RDFDatabankConfig.endpointsubmitterpass) + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + self.assertEqual(len(rdfgraph),13,'Graph length %i' %len(rdfgraph)) + self.failUnless((subj,RDF.type,stype) in rdfgraph, 'Testing submission type: '+subj+", "+stype) + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"testdir.zip")) in rdfgraph, 'ore:aggregates testdir.zip') + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"testrdf3.zip")) in rdfgraph, 'ore:aggregates testrdf3.zip') + self.failUnless((subj,URIRef(oxds+"currentVersion"),'6') in rdfgraph, 'oxds:currentVersion') + # Access and check zip file content + (resp, zipfile) = self.doHTTP_GET( + resource="datasets/TestSubmission/testdir.zip", + expect_status=200, expect_reason="OK", expect_type="application/zip") + self.assertEqual(zipdata, zipfile, "Difference between local and remote zipfile!") + (resp, zipfile2) = self.doHTTP_GET( + resource="datasets/TestSubmission/testrdf3.zip", + expect_status=200, expect_reason="OK", expect_type="application/zip") + self.assertEqual(zipdata5, zipfile2, "Difference between local and remote zipfile!") + + # Put zip file by submitter2, check response + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointsubmitteruser2, + endpointpass=RDFDatabankConfig.endpointsubmitterpass2) + (resp, respdata) = self.doHTTP_PUT(zipdata2, resource="datasets/TestSubmission/testdir.zip", + expect_status=403, expect_reason="Forbidden", expect_type="text/plain") + # Access and check list of contents + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointsubmitteruser, + endpointpass=RDFDatabankConfig.endpointsubmitterpass) + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + self.assertEqual(len(rdfgraph),13,'Graph length %i' %len(rdfgraph)) + self.failUnless((subj,URIRef(oxds+"currentVersion"),'6') in rdfgraph, 'oxds:currentVersion') + # Access and check zip file content + (resp, zipfile) = self.doHTTP_GET( + resource="datasets/TestSubmission/testdir.zip", + expect_status=200, expect_reason="OK", expect_type="application/zip") + self.assertEqual(zipdata, zipfile, "Zipfile has been updated by submitter of another dataset!") + (resp, zipfile2) = self.doHTTP_GET( + resource="datasets/TestSubmission/testrdf3.zip", + expect_status=200, expect_reason="OK", expect_type="application/zip") + self.assertEqual(zipdata5, zipfile2, "Zipfile has been updated by submitter of another dataset!") + + # Put zip file by general user, check response + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointgeneraluser, + endpointpass=RDFDatabankConfig.endpointgeneralpass) + (resp, respdata) = self.doHTTP_PUT(zipdata3, resource="datasets/TestSubmission/testrdf3.zip", + expect_status=401, expect_reason="Unauthorized", expect_type="text/plain") + # Access and check list of contents + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointsubmitteruser, + endpointpass=RDFDatabankConfig.endpointsubmitterpass) + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + self.assertEqual(len(rdfgraph),13,'Graph length %i' %len(rdfgraph)) + self.failUnless((subj,URIRef(oxds+"currentVersion"),'6') in rdfgraph, 'oxds:currentVersion') + # Access and check zip file content + (resp, zipfile) = self.doHTTP_GET( + resource="datasets/TestSubmission/testdir.zip", + expect_status=200, expect_reason="OK", expect_type="application/zip") + self.assertEqual(zipdata, zipfile, "Zipfile has been updated by general user!") + (resp, zipfile2) = self.doHTTP_GET( + resource="datasets/TestSubmission/testrdf3.zip", + expect_status=200, expect_reason="OK", expect_type="application/zip") + self.assertEqual(zipdata5, zipfile2, "Zipfile has been updated by general user!") + + # Put zip file by admin 3, check response + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointadminuser3, + endpointpass=RDFDatabankConfig.endpointadminpass3) + (resp, respdata) = self.doHTTP_PUT(zipdata2, resource="datasets/TestSubmission/testdir.zip", + expect_status=403, expect_reason="Forbidden", expect_type="text/plain") + # Access and check list of contents + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointsubmitteruser, + endpointpass=RDFDatabankConfig.endpointsubmitterpass) + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + self.assertEqual(len(rdfgraph),13,'Graph length %i' %len(rdfgraph)) + self.failUnless((subj,URIRef(oxds+"currentVersion"),'6') in rdfgraph, 'oxds:currentVersion') + # Access and check zip file content + (resp, zipfile) = self.doHTTP_GET( + resource="datasets/TestSubmission/testdir.zip", + expect_status=200, expect_reason="OK", expect_type="application/zip") + self.assertEqual(zipdata, zipfile, "Zipfile has been updated by admin of another dataset!") + (resp, zipfile2) = self.doHTTP_GET( + resource="datasets/TestSubmission/testrdf3.zip", + expect_status=200, expect_reason="OK", expect_type="application/zip") + self.assertEqual(zipdata5, zipfile2, "Zipfile has been updated by admin of another dataset!") + + # Put zip file by manager 3, check response + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointmanageruser3, + endpointpass=RDFDatabankConfig.endpointmanagerpass3) + (resp, respdata) = self.doHTTP_PUT(zipdata3, resource="datasets/TestSubmission/testrdf3.zip", + expect_status=403, expect_reason="Forbidden", expect_type="text/plain") + # Access and check list of contents + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointsubmitteruser, + endpointpass=RDFDatabankConfig.endpointsubmitterpass) + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + self.assertEqual(len(rdfgraph),13,'Graph length %i' %len(rdfgraph)) + self.failUnless((subj,URIRef(oxds+"currentVersion"),'6') in rdfgraph, 'oxds:currentVersion') + # Access and check zip file content + (resp, zipfile) = self.doHTTP_GET( + resource="datasets/TestSubmission/testdir.zip", + expect_status=200, expect_reason="OK", expect_type="application/zip") + self.assertEqual(zipdata, zipfile, "Zipfile has been updated by manager of another dataset!") + (resp, zipfile2) = self.doHTTP_GET( + resource="datasets/TestSubmission/testrdf3.zip", + expect_status=200, expect_reason="OK", expect_type="application/zip") + self.assertEqual(zipdata5, zipfile2, "Zipfile has been updated by manager of another dataset!") + + # Put zip file by submitter 3, check response + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointsubmitteruser3, + endpointpass=RDFDatabankConfig.endpointsubmitterpass3) + (resp, respdata) = self.doHTTP_PUT(zipdata3, resource="datasets/TestSubmission/testrdf3.zip", + expect_status=403, expect_reason="Forbidden", expect_type="text/plain") + # Access and check list of contents + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointsubmitteruser, + endpointpass=RDFDatabankConfig.endpointsubmitterpass) + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + self.assertEqual(len(rdfgraph),13,'Graph length %i' %len(rdfgraph)) + self.failUnless((subj,URIRef(oxds+"currentVersion"),'6') in rdfgraph, 'oxds:currentVersion') + # Access and check zip file content + (resp, zipfile) = self.doHTTP_GET( + resource="datasets/TestSubmission/testdir.zip", + expect_status=200, expect_reason="OK", expect_type="application/zip") + self.assertEqual(zipdata, zipfile, "Zipfile has been updated by submitter of another dataset!") + (resp, zipfile2) = self.doHTTP_GET( + resource="datasets/TestSubmission/testrdf3.zip", + expect_status=200, expect_reason="OK", expect_type="application/zip") + self.assertEqual(zipdata5, zipfile2, "Zipfile has been updated by submitter of another dataset!") + + # Access and check zip file content of version 1 + (resp, zipfile) = self.doHTTP_GET( + resource="datasets/TestSubmission/testdir.zip?version=1", + expect_status=200, expect_reason="OK", expect_type="application/zip") + self.assertEqual(zipdata, zipfile, "Difference between local and remote zipfile!") + # Access and check zip file content of version 2 + (resp, zipfile) = self.doHTTP_GET( + resource="datasets/TestSubmission/testdir.zip?version=2", + expect_status=200, expect_reason="OK", expect_type="application/zip") + self.assertEqual(zipdata, zipfile, "Difference between local and remote zipfile!") + (resp, zipfile) = self.doHTTP_GET( + resource="datasets/TestSubmission/testrdf3.zip?version=2", + expect_status=200, expect_reason="OK", expect_type="application/zip") + self.assertEqual(zipdata2, zipfile, "Difference between local and remote zipfile!") + # Access and check zip file content of version 3 + (resp, zipfile) = self.doHTTP_GET( + resource="datasets/TestSubmission/testdir.zip?version=3", + expect_status=200, expect_reason="OK", expect_type="application/zip") + self.assertEqual(zipdata, zipfile, "Difference between local and remote zipfile!") + (resp, zipfile) = self.doHTTP_GET( + resource="datasets/TestSubmission/testrdf3.zip?version=3", + expect_status=200, expect_reason="OK", expect_type="application/zip") + self.assertEqual(zipdata3, zipfile, "Difference between local and remote zipfile!") + # Access and check zip file content of version 4 + (resp, zipfile) = self.doHTTP_GET( + resource="datasets/TestSubmission/testdir.zip?version=4", + expect_status=200, expect_reason="OK", expect_type="application/zip") + self.assertEqual(zipdata4, zipfile, "Difference between local and remote zipfile!") + (resp, zipfile) = self.doHTTP_GET( + resource="datasets/TestSubmission/testrdf3.zip?version=4", + expect_status=200, expect_reason="OK", expect_type="application/zip") + self.assertEqual(zipdata3, zipfile, "Difference between local and remote zipfile!") + # Access and check zip file content of version 5 + (resp, zipfile) = self.doHTTP_GET( + resource="datasets/TestSubmission/testdir.zip?version=5", + expect_status=200, expect_reason="OK", expect_type="application/zip") + self.assertEqual(zipdata4, zipfile, "Difference between local and remote zipfile!") + (resp, zipfile) = self.doHTTP_GET( + resource="datasets/TestSubmission/testrdf3.zip?version=5", + expect_status=200, expect_reason="OK", expect_type="application/zip") + self.assertEqual(zipdata5, zipfile, "Difference between local and remote zipfile!") + # Access and check zip file content of version 6 + (resp, zipfile) = self.doHTTP_GET( + resource="datasets/TestSubmission/testdir.zip?version=6", + expect_status=200, expect_reason="OK", expect_type="application/zip") + self.assertEqual(zipdata, zipfile, "Difference between local and remote zipfile!") + (resp, zipfile) = self.doHTTP_GET( + resource="datasets/TestSubmission/testrdf3.zip?version=6", + expect_status=200, expect_reason="OK", expect_type="application/zip") + self.assertEqual(zipdata5, zipfile, "Difference between local and remote zipfile!") + + + + def testPutMetadataFile(self): + """Add metadata to manifest - PUT metadata to /silo_name/datasets/dataset_name/manifest.rdf""" + # Create a new dataset, check response + self.createSubmissionDataset() + # Put manifest file, check response + zipdata = open("testdata/manifest.rdf").read() + (resp, respdata) = self.doHTTP_PUT(zipdata, resource="datasets/TestSubmission/manifest.rdf", + expect_status=204, expect_reason="No Content", expect_type="text/plain") + # Access and check list of contents + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + subj = URIRef(self.getManifestUri("datasets/TestSubmission")) + base = self.getManifestUri("datasets/TestSubmission/") + dcterms = "http://purl.org/dc/terms/" + ore = "http://www.openarchives.org/ore/terms/" + oxds = "http://vocab.ox.ac.uk/dataset/schema#" + owl = "http://www.w3.org/2002/07/owl#" + stype = URIRef(oxds+"DataSet") + self.assertEqual(len(rdfgraph),13,'Graph length %i' %len(rdfgraph)) + self.failUnless((subj,RDF.type,stype) in rdfgraph, 'Testing submission type: '+subj+", "+stype) + self.failUnless((subj,URIRef(dcterms+"created"),None) in rdfgraph, 'dcterms:created') + self.failUnless((subj,URIRef(dcterms+"identifier"),None) in rdfgraph, 'dcterms:identifier') + self.failUnless((subj,URIRef(dcterms+"mediator"),None) in rdfgraph, 'dcterms:mediator') + self.failUnless((subj,URIRef(dcterms+"rights"),None) in rdfgraph, 'dcterms:rights') + self.failUnless((subj,URIRef(dcterms+"license"),None) in rdfgraph, 'dcterms:license') + self.failUnless((subj,URIRef(dcterms+"publisher"),None) in rdfgraph, 'dcterms:publisher') + self.failUnless((subj,URIRef(oxds+"isEmbargoed"),None) in rdfgraph, 'oxds:isEmbargoed') + self.failUnless((subj,URIRef(oxds+"embargoedUntil"),None) in rdfgraph, 'oxds:embargoedUntil') + self.failUnless((subj,URIRef(oxds+"currentVersion"),'1') in rdfgraph, 'oxds:currentVersion') + self.failUnless((subj,URIRef(dcterms+"modified"),None) in rdfgraph, 'dcterms:modified') + self.failUnless((subj,URIRef(dcterms+"title"),"Test dataset with merged metadata") in rdfgraph, 'dcterms:title') + self.failUnless((subj,URIRef(owl+"sameAs"),URIRef("http://example.org/testrdf/")) in rdfgraph, 'owl:sameAs') + # Update metadata file, check response + zipdata = open("testdata/manifest2.rdf").read() + (resp, respdata) = self.doHTTP_PUT(zipdata, resource="datasets/TestSubmission/manifest.rdf", + expect_status=204, expect_reason="No Content", expect_type="text/plain") + # Access and check list of contents + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + self.assertEqual(len(rdfgraph),13,'Graph length %i' %len(rdfgraph)) + self.failUnless((subj,RDF.type,stype) in rdfgraph, 'Testing submission type: '+subj+", "+stype) + self.failUnless((subj,URIRef(dcterms+"created"),None) in rdfgraph, 'dcterms:created') + self.failUnless((subj,URIRef(dcterms+"identifier"),None) in rdfgraph, 'dcterms:identifier') + self.failUnless((subj,URIRef(dcterms+"mediator"),None) in rdfgraph, 'dcterms:mediator') + self.failUnless((subj,URIRef(dcterms+"rights"),None) in rdfgraph, 'dcterms:rights') + self.failUnless((subj,URIRef(dcterms+"license"),None) in rdfgraph, 'dcterms:license') + self.failUnless((subj,URIRef(dcterms+"publisher"),None) in rdfgraph, 'dcterms:publisher') + self.failUnless((subj,URIRef(oxds+"isEmbargoed"),None) in rdfgraph, 'oxds:isEmbargoed') + self.failUnless((subj,URIRef(oxds+"embargoedUntil"),None) in rdfgraph, 'oxds:embargoedUntil') + self.failUnless((subj,URIRef(oxds+"currentVersion"),'2') in rdfgraph, 'oxds:currentVersion') + self.failUnless((subj,URIRef(dcterms+"modified"),None) in rdfgraph, 'dcterms:modified') + self.failUnless((subj,URIRef(owl+"sameAs"),URIRef("http://example.org/testrdf/")) in rdfgraph, 'owl:sameAs') + self.failUnless((subj,URIRef(dcterms+"title"),'Test dataset with updated and merged metadata') in rdfgraph, 'dcterms:title') + #Access state information and check + (resp, data) = self.doHTTP_GET( + resource="states/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/json") + state = data['state'] + parts = data['parts'] + self.assertEqual(len(state.keys()), 11, "States") + self.assertEqual(state['item_id'], "TestSubmission", "Submission item identifier") + self.assertEqual(len(state['versions']), 3, "Three versions") + self.assertEqual(state['versions'][0], '0', "Version 0") + self.assertEqual(state['versions'][1], '1', "Version 1") + self.assertEqual(state['versions'][2], '2', "Version 2") + self.assertEqual(state['currentversion'], '2', "Current version == 2") + self.assertEqual(state['rdffileformat'], 'xml', "RDF file type") + self.assertEqual(state['rdffilename'], 'manifest.rdf', "RDF file name") + self.assertEqual(len(state['files']['0']), 1, "List should contain just manifest.rdf") + self.assertEqual(len(state['files']['1']), 1, "List should contain just manifest.rdf") + self.assertEqual(len(state['files']['2']), 1, "List should contain just manifest.rdf") + self.assertEqual(len(state['metadata_files']['0']), 0, "metadata_files of version 0") + self.assertEqual(len(state['metadata_files']['1']), 0, "metadata_files of version 1") + self.assertEqual(len(state['metadata_files']['2']), 0, "metadata_files of version 2") + self.assertEqual(len(state['subdir']['0']), 0, "Subdirectory count for version 0") + self.assertEqual(len(state['subdir']['1']), 0, "Subdirectory count for version 1") + self.assertEqual(len(state['subdir']['2']), 0, "Subdirectory count for version 2") + self.assertEqual(state['metadata']['createdby'], RDFDatabankConfig.endpointsubmitteruser, "Created by") + self.assertEqual(state['metadata']['embargoed'], True, "Embargoed?") + self.assertEqual(len(parts.keys()), 3, "Parts") + self.assertEqual(len(parts['4=TestSubmission'].keys()), 13, "File stats for 4=TestSubmission") + self.assertEqual(len(parts['manifest.rdf'].keys()), 13, "File stats for manifest.rdf") + + # Put metadata file by admin, check response + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointadminuser, + endpointpass=RDFDatabankConfig.endpointadminpass) + metadataa = open("testdata/manifest-admin.rdf").read() + (resp, respdata) = self.doHTTP_PUT(metadataa, resource="datasets/TestSubmission/manifest.rdf", + expect_status=204, expect_reason="No Content", expect_type="text/plain") + # Access and check list of contents + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointsubmitteruser, + endpointpass=RDFDatabankConfig.endpointsubmitterpass) + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + self.assertEqual(len(rdfgraph),14,'Graph length %i' %len(rdfgraph)) + self.failUnless((subj,URIRef(dcterms+"description"),"Changes made by admin") in rdfgraph, 'description by admin') + self.failUnless((subj,URIRef(oxds+"currentVersion"),'3') in rdfgraph, 'oxds:currentVersion') + + # Put metadata file by manager, check response + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointmanageruser, + endpointpass=RDFDatabankConfig.endpointmanagerpass) + metadatam = open("testdata/manifest-manager.rdf").read() + (resp, respdata) = self.doHTTP_PUT(metadatam, resource="datasets/TestSubmission/manifest.rdf", + expect_status=204, expect_reason="No Content", expect_type="text/plain") + # Access and check list of contents + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointsubmitteruser, + endpointpass=RDFDatabankConfig.endpointsubmitterpass) + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + self.assertEqual(len(rdfgraph),15,'Graph length %i' %len(rdfgraph)) + self.failUnless((subj,URIRef(dcterms+"description"),"Changes made by manager") in rdfgraph, 'description by manager') + self.failUnless((subj,URIRef(oxds+"currentVersion"),'4') in rdfgraph, 'oxds:currentVersion') + + # Put metadata file by submitter2, check response + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointsubmitteruser2, + endpointpass=RDFDatabankConfig.endpointsubmitterpass2) + metadatas = open("testdata/manifest-submitter.rdf").read() + (resp, respdata) = self.doHTTP_PUT(metadatas, resource="datasets/TestSubmission/manifest.rdf", + expect_status=403, expect_reason="Forbidden", expect_type="text/plain") + # Access and check list of contents + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointsubmitteruser, + endpointpass=RDFDatabankConfig.endpointsubmitterpass) + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + self.assertEqual(len(rdfgraph),15,'Graph length %i' %len(rdfgraph)) + self.failUnless((subj,URIRef(oxds+"currentVersion"),'4') in rdfgraph, 'oxds:currentVersion') + self.assertFalse((subj,URIRef(dcterms+"description"),'Changes made by submitter') in rdfgraph, 'description by submitter of another dataset') + + # Put metadata file by general user, check response + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointgeneraluser, + endpointpass=RDFDatabankConfig.endpointgeneralpass) + metadatag = open("testdata/manifest-general.rdf").read() + (resp, respdata) = self.doHTTP_PUT(metadatag, resource="datasets/TestSubmission/manifest.rdf", + expect_status=401, expect_reason="Unauthorized", expect_type="text/plain") + # Access and check list of contents + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointsubmitteruser, + endpointpass=RDFDatabankConfig.endpointsubmitterpass) + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + self.assertEqual(len(rdfgraph),15,'Graph length %i' %len(rdfgraph)) + self.failUnless((subj,URIRef(oxds+"currentVersion"),'4') in rdfgraph, 'oxds:currentVersion') + self.assertFalse((subj,URIRef(dcterms+"description"),'Changes made by general user') in rdfgraph, 'description by general user') + + # Put metadata file by admin 3, check response + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointadminuser3, + endpointpass=RDFDatabankConfig.endpointadminpass3) + metadatag = open("testdata/manifest-general.rdf").read() + (resp, respdata) = self.doHTTP_PUT(metadatag, resource="datasets/TestSubmission/manifest.rdf", + expect_status=403, expect_reason="Forbidden", expect_type="text/plain") + # Access and check list of contents + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointsubmitteruser, + endpointpass=RDFDatabankConfig.endpointsubmitterpass) + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + self.assertEqual(len(rdfgraph),15,'Graph length %i' %len(rdfgraph)) + self.failUnless((subj,URIRef(oxds+"currentVersion"),'4') in rdfgraph, 'oxds:currentVersion') + self.assertFalse((subj,URIRef(dcterms+"description"),'Changes made by general user') in rdfgraph, 'description by admin user') + + # Put metadata file by manager 3, check response + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointmanageruser3, + endpointpass=RDFDatabankConfig.endpointmanagerpass3) + metadatag = open("testdata/manifest-general.rdf").read() + (resp, respdata) = self.doHTTP_PUT(metadatag, resource="datasets/TestSubmission/manifest.rdf", + expect_status=403, expect_reason="Forbidden", expect_type="text/plain") + # Access and check list of contents + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointsubmitteruser, + endpointpass=RDFDatabankConfig.endpointsubmitterpass) + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + self.assertEqual(len(rdfgraph),15,'Graph length %i' %len(rdfgraph)) + self.failUnless((subj,URIRef(oxds+"currentVersion"),'4') in rdfgraph, 'oxds:currentVersion') + self.assertFalse((subj,URIRef(dcterms+"description"),'Changes made by general user') in rdfgraph, 'description by manager of another silo') + + # Put metadata file by submitter 3, check response + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointsubmitteruser3, + endpointpass=RDFDatabankConfig.endpointsubmitterpass3) + metadatag = open("testdata/manifest-general.rdf").read() + (resp, respdata) = self.doHTTP_PUT(metadatag, resource="datasets/TestSubmission/manifest.rdf", + expect_status=403, expect_reason="Forbidden", expect_type="text/plain") + # Access and check list of contents + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointsubmitteruser, + endpointpass=RDFDatabankConfig.endpointsubmitterpass) + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + self.assertEqual(len(rdfgraph),15,'Graph length %i' %len(rdfgraph)) + self.failUnless((subj,URIRef(oxds+"currentVersion"),'4') in rdfgraph, 'oxds:currentVersion') + self.assertFalse((subj,URIRef(dcterms+"description"),'Changes made by general user') in rdfgraph, 'description by submitter of another dataset') + + + + def testUnicodeMetadataFileUpdate(self): + """POST/PUT manifest to dataset - /silo_name/datasets/dataset_name""" + # Create a new dataset, check response + self.createSubmissionDataset() + + subj = URIRef(self.getManifestUri("datasets/TestSubmission")) + base = self.getManifestUri("datasets/TestSubmission/") + dcterms = "http://purl.org/dc/terms/" + ore = "http://www.openarchives.org/ore/terms/" + oxds = "http://vocab.ox.ac.uk/dataset/schema#" + owl = "http://www.w3.org/2002/07/owl#" + stype = URIRef(oxds+"DataSet") + # POST unicode file 01, check response + zipdata = self.updateSubmissionZipfile(file_to_upload="unicodedata/unicode01.xml", filename="manifest.rdf") + # Access and check list of contents + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + fr = open('response.xml', 'w') + fr.write(rdfdata) + fr.close() + rdfgraph = Graph() + rdfgraph.parse('response.xml', format='xml') + doctext1 = None + f = codecs.open('testdata/unicodedata/unicode01.txt', 'r', 'utf-8') + doctext1 = f.read() + f.close() + self.assertEqual(len(rdfgraph),14,'Graph length %i' %len(rdfgraph)) + self.failUnless((subj,RDF.type,stype) in rdfgraph, 'Testing submission type: '+subj+", "+stype) + self.failUnless((subj,URIRef(dcterms+"created"),None) in rdfgraph, 'dcterms:created') + self.failUnless((subj,URIRef(dcterms+"identifier"),None) in rdfgraph, 'dcterms:identifier') + self.failUnless((subj,URIRef(dcterms+"mediator"),None) in rdfgraph, 'dcterms:mediator') + self.failUnless((subj,URIRef(dcterms+"rights"),None) in rdfgraph, 'dcterms:rights') + self.failUnless((subj,URIRef(dcterms+"license"),None) in rdfgraph, 'dcterms:license') + self.failUnless((subj,URIRef(dcterms+"publisher"),None) in rdfgraph, 'dcterms:piblisher') + self.failUnless((subj,URIRef(oxds+"isEmbargoed"),None) in rdfgraph, 'oxds:isEmbargoed') + self.failUnless((subj,URIRef(oxds+"embargoedUntil"),None) in rdfgraph, 'oxds:embargoedUntil') + self.failUnless((subj,URIRef(oxds+"currentVersion"),'1') in rdfgraph, 'oxds:currentVersion') + self.failUnless((subj,URIRef(dcterms+"modified"),None) in rdfgraph, 'dcterms:modified') + self.failUnless((subj,URIRef(dcterms+"title"),"General punctuation") in rdfgraph, 'dcterms:title') + self.failUnless((subj,RDF.value,Literal(doctext1)) in rdfgraph, 'rdf:value') + self.failUnless((subj,URIRef(dcterms+"source"),"http://www.madore.org/~david/misc/unitest/") in rdfgraph, 'dcterms:source') + + # PUT unicode file 02, check response + manidata = open("testdata/unicodedata/unicode02.xml").read() + (resp, respdata) = self.doHTTP_PUT(manidata, resource="datasets/TestSubmission/manifest.rdf", + expect_status=204, expect_reason="No Content", expect_type="text/plain") + #Access state information and check + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + fr = open('response.xml', 'w') + fr.write(rdfdata) + fr.close() + rdfgraph = Graph() + rdfgraph.parse('response.xml', format='xml') + doctext2 = None + f = codecs.open('testdata/unicodedata/unicode02.txt', 'r', 'utf-8') + doctext2 = f.read() + f.close() + self.assertEqual(len(rdfgraph),15,'Graph length %i' %len(rdfgraph)) + self.failUnless((subj,URIRef(oxds+"currentVersion"),'2') in rdfgraph, 'oxds:currentVersion') + self.failUnless((subj,URIRef(dcterms+"title"),"A table of (some) accents") in rdfgraph, 'dcterms:title') + self.failUnless((subj,RDF.value,Literal(doctext2)) in rdfgraph, 'rdf:value') + + # PUT unicode file 03, check response + manidata = open("testdata/unicodedata/unicode03.xml").read() + (resp, respdata) = self.doHTTP_PUT(manidata, resource="datasets/TestSubmission/manifest.rdf", + expect_status=204, expect_reason="No Content", expect_type="text/plain") + #Access state information and check + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + fr = open('response.xml', 'w') + fr.write(rdfdata) + fr.close() + rdfgraph = Graph() + rdfgraph.parse('response.xml', format='xml') + doctext3 = None + f = codecs.open('testdata/unicodedata/unicode03.txt', 'r', 'utf-8') + doctext3 = f.read() + f.close() + self.assertEqual(len(rdfgraph),16,'Graph length %i' %len(rdfgraph)) + self.failUnless((subj,URIRef(oxds+"currentVersion"),'3') in rdfgraph, 'oxds:currentVersion') + self.failUnless((subj,URIRef(dcterms+"title"),"Combining diacritics") in rdfgraph, 'dcterms:title') + self.failUnless((subj,RDF.value,Literal(doctext3)) in rdfgraph, 'rdf:value') + + # POST unicode file 04, check response + zipdata = self.updateSubmissionZipfile(file_to_upload="unicodedata/unicode04.xml", filename="manifest.rdf") + # Access and check list of contents + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + fr = open('response.xml', 'w') + fr.write(rdfdata) + fr.close() + rdfgraph = Graph() + rdfgraph.parse('response.xml', format='xml') + doctext4 = None + f = codecs.open('testdata/unicodedata/unicode04.txt', 'r', 'utf-8') + doctext4 = f.read() + f.close() + self.assertEqual(len(rdfgraph),17,'Graph length %i' %len(rdfgraph)) + self.failUnless((subj,URIRef(oxds+"currentVersion"),'4') in rdfgraph, 'oxds:currentVersion') + self.failUnless((subj,URIRef(dcterms+"title"),"Various symbols") in rdfgraph, 'dcterms:title') + self.failUnless((subj,RDF.value,Literal(doctext4)) in rdfgraph, 'rdf:value') + + # POST unicode file 05, check response + zipdata = self.updateSubmissionZipfile(file_to_upload="unicodedata/unicode05.xml", filename="manifest.rdf") + # Access and check list of contents + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + fr = open('response.xml', 'w') + fr.write(rdfdata) + fr.close() + rdfgraph = Graph() + rdfgraph.parse('response.xml', format='xml') + doctext5 = None + f = codecs.open('testdata/unicodedata/unicode05.txt', 'r', 'utf-8') + doctext5 = f.read() + f.close() + self.assertEqual(len(rdfgraph),18,'Graph length %i' %len(rdfgraph)) + self.failUnless((subj,URIRef(oxds+"currentVersion"),'5') in rdfgraph, 'oxds:currentVersion') + self.failUnless((subj,URIRef(dcterms+"title"),"Some verses in Russian") in rdfgraph, 'dcterms:title') + self.failUnless((subj,RDF.value,Literal(doctext5)) in rdfgraph, 'rdf:value') + + # PUT unicode file 06, check response + manidata = open("testdata/unicodedata/unicode06.xml").read() + (resp, respdata) = self.doHTTP_PUT(manidata, resource="datasets/TestSubmission/manifest.rdf", + expect_status=204, expect_reason="No Content", expect_type="text/plain") + #Access state information and check + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + fr = open('response.xml', 'w') + fr.write(rdfdata) + fr.close() + rdfgraph = Graph() + rdfgraph.parse('response.xml', format='xml') + doctext6 = None + f = codecs.open('testdata/unicodedata/unicode06.txt', 'r', 'utf-8') + doctext6 = f.read() + f.close() + self.assertEqual(len(rdfgraph),19,'Graph length %i' %len(rdfgraph)) + self.failUnless((subj,URIRef(oxds+"currentVersion"),'6') in rdfgraph, 'oxds:currentVersion') + self.failUnless((subj,URIRef(dcterms+"title"),"Some verses in ancient Greek") in rdfgraph, 'dcterms:title') + self.failUnless((subj,RDF.value,Literal(doctext6)) in rdfgraph, 'rdf:value') + + # POST unicode file 07, check response + zipdata = self.updateSubmissionZipfile(file_to_upload="unicodedata/unicode07.xml", filename="manifest.rdf") + # Access and check list of contents + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + fr = open('response.xml', 'w') + fr.write(rdfdata) + fr.close() + rdfgraph = Graph() + rdfgraph.parse('response.xml', format='xml') + doctext7 = None + f = codecs.open('testdata/unicodedata/unicode07.txt', 'r', 'utf-8') + doctext7 = f.read() + f.close() + self.assertEqual(len(rdfgraph),20,'Graph length %i' %len(rdfgraph)) + self.failUnless((subj,URIRef(oxds+"currentVersion"),'7') in rdfgraph, 'oxds:currentVersion') + self.failUnless((subj,URIRef(dcterms+"title"),"Some verses in Sanskrit") in rdfgraph, 'dcterms:title') + self.failUnless((subj,RDF.value,Literal(doctext7)) in rdfgraph, 'rdf:value') + + # PUT unicode file 08, check response + manidata = open("testdata/unicodedata/unicode08.xml").read() + (resp, respdata) = self.doHTTP_PUT(manidata, resource="datasets/TestSubmission/manifest.rdf", + expect_status=204, expect_reason="No Content", expect_type="text/plain") + #Access state information and check + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + fr = open('response.xml', 'w') + fr.write(rdfdata) + fr.close() + rdfgraph = Graph() + rdfgraph.parse('response.xml', format='xml') + doctext8 = None + f = codecs.open('testdata/unicodedata/unicode08.txt', 'r', 'utf-8') + doctext8= f.read() + f.close() + self.assertEqual(len(rdfgraph),21,'Graph length %i' %len(rdfgraph)) + self.failUnless((subj,URIRef(oxds+"currentVersion"),'8') in rdfgraph, 'oxds:currentVersion') + self.failUnless((subj,URIRef(dcterms+"title"),"Some Chinese") in rdfgraph, 'dcterms:title') + self.failUnless((subj,RDF.value,Literal(doctext8)) in rdfgraph, 'rdf:value') + + # POST unicode file 09, check response + zipdata = self.updateSubmissionZipfile(file_to_upload="unicodedata/unicode09.xml", filename="manifest.rdf") + # Access and check list of contents + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + fr = open('response.xml', 'w') + fr.write(rdfdata) + fr.close() + rdfgraph = Graph() + rdfgraph.parse('response.xml', format='xml') + doctext9 = None + f = codecs.open('testdata/unicodedata/unicode09.txt', 'r', 'utf-8') + doctext9 = f.read() + f.close() + self.assertEqual(len(rdfgraph),22,'Graph length %i' %len(rdfgraph)) + self.failUnless((subj,URIRef(oxds+"currentVersion"),'9') in rdfgraph, 'oxds:currentVersion') + self.failUnless((subj,URIRef(dcterms+"title"),"A Tamil name") in rdfgraph, 'dcterms:title') + self.failUnless((subj,RDF.value,Literal(doctext9)) in rdfgraph, 'rdf:value') + + # PUT unicode file 10, check response + manidata = open("testdata/unicodedata/unicode10.xml").read() + (resp, respdata) = self.doHTTP_PUT(manidata, resource="datasets/TestSubmission/manifest.rdf", + expect_status=204, expect_reason="No Content", expect_type="text/plain") + #Access state information and check + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + fr = open('response.xml', 'w') + fr.write(rdfdata) + fr.close() + rdfgraph = Graph() + rdfgraph.parse('response.xml', format='xml') + doctext10 = None + f = codecs.open('testdata/unicodedata/unicode10.txt', 'r', 'utf-8') + doctext10= f.read() + f.close() + self.assertEqual(len(rdfgraph),23,'Graph length %i' %len(rdfgraph)) + self.failUnless((subj,URIRef(oxds+"currentVersion"),'10') in rdfgraph, 'oxds:currentVersion') + self.failUnless((subj,URIRef(dcterms+"title"),"Some Arabic") in rdfgraph, 'dcterms:title') + self.failUnless((subj,RDF.value,Literal(doctext10)) in rdfgraph, 'rdf:value') + os.remove('response.xml') + + + + def testChangeEmbargo(self): + """Change embargo information - POST embargo_change to /silo_name/datasets/dataset_name""" + # Create a new dataset, check response + #self.createSubmissionDataset() + fields = \ + [ ("id", "TestSubmission") + ] + files =[] + (reqtype, reqdata) = SparqlQueryTestCase.encode_multipart_formdata(fields, files) + (resp,respdata) = self.doHTTP_POST( + reqdata, reqtype, + resource="datasets/", + expect_status=201, expect_reason="Created") + LHobtained = resp.getheader('Content-Location', None) + LHexpected = "%sdatasets/TestSubmission"%self._endpointpath + self.assertEquals(LHobtained, LHexpected, 'Content-Location not correct') + #Access dataset and check content + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + self.assertEqual(len(rdfgraph),10,'Graph length %i' %len(rdfgraph)) + oxds = "http://vocab.ox.ac.uk/dataset/schema#" + dcterms = "http://purl.org/dc/terms/" + subj = URIRef(self.getManifestUri("datasets/TestSubmission")) + ore = "http://www.openarchives.org/ore/terms/" + stype = URIRef(oxds+"DataSet") + base = self.getManifestUri("datasets/TestSubmission/") + self.failUnless((subj,RDF.type,stype) in rdfgraph, 'Testing submission type: '+subj+", "+stype) + self.failUnless((subj,URIRef(oxds+"isEmbargoed"),'True') in rdfgraph, 'oxds:isEmbargoed') + #Access state information and check + (resp, data) = self.doHTTP_GET( + resource="states/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/json") + state = data['state'] + d = (datetime.datetime.now() + relativedelta(years=+70)).isoformat() + d = d.split('T')[0] + self.assertEqual(state['metadata']['embargoed'], True, "Embargoed?") + self.assertTrue(d in state['metadata']['embargoed_until'], "embargoed_until %s?"%d) + # Upload zip file, check response + zipdata = self.uploadSubmissionZipfile(file_to_upload="testdir.zip") + #Access dataset and check content + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + self.assertEqual(len(rdfgraph),12,'Graph length %i' %len(rdfgraph)) + self.failUnless((subj,RDF.type,stype) in rdfgraph, 'Testing submission type: '+subj+", "+stype) + self.failUnless((subj,URIRef(oxds+"isEmbargoed"),'True') in rdfgraph, 'oxds:isEmbargoed') + self.failUnless((subj,URIRef(oxds+"embargoedUntil"),None) in rdfgraph, 'oxds:isEmbargoed') + self.failUnless((subj,URIRef(dcterms+"identifier"),None) in rdfgraph, 'dcterms:identifier') + self.failUnless((subj,URIRef(dcterms+"created"),None) in rdfgraph, 'dcterms:created') + self.failUnless((subj,URIRef(dcterms+"mediator"),None) in rdfgraph, 'dcterms:mediator') + self.failUnless((subj,URIRef(dcterms+"rights"),None) in rdfgraph, 'dcterms:rights') + self.failUnless((subj,URIRef(dcterms+"license"),None) in rdfgraph, 'dcterms:license') + self.failUnless((subj,URIRef(dcterms+"publisher"),None) in rdfgraph, 'dcterms:publisher') + self.failUnless((subj,URIRef(oxds+"currentVersion"),'1') in rdfgraph, 'oxds:currentVersion') + self.failUnless((subj,URIRef(dcterms+"modified"),None) in rdfgraph, 'dcterms:modified') + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"testdir.zip")) in rdfgraph, 'ore:aggregates testdir.zip') + # Access and check zip file content by submitter + (resp, zipfile) = self.doHTTP_GET( + resource="datasets/TestSubmission/testdir.zip", + expect_status=200, expect_reason="OK", expect_type="application/zip") + self.assertEqual(zipdata, zipfile, "Difference between local and remote zipfile!") + # Access and check content by admin + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointadminuser, + endpointpass=RDFDatabankConfig.endpointadminpass) + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + self.assertEqual(len(rdfgraph),12,'Graph length %i' %len(rdfgraph)) + (resp, zipfile) = self.doHTTP_GET( + resource="datasets/TestSubmission/testdir.zip", + expect_status=200, expect_reason="OK", expect_type="application/zip") + self.assertEqual(zipdata, zipfile, "Difference between local and remote zipfile!") + # Access and check zip file content by manager + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointmanageruser, + endpointpass=RDFDatabankConfig.endpointmanagerpass) + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + self.assertEqual(len(rdfgraph),12,'Graph length %i' %len(rdfgraph)) + (resp, zipfile) = self.doHTTP_GET( + resource="datasets/TestSubmission/testdir.zip", + expect_status=200, expect_reason="OK", expect_type="application/zip") + self.assertEqual(zipdata, zipfile, "Difference between local and remote zipfile!") + # Access and check zip file content by submitter2 + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointsubmitteruser2, + endpointpass=RDFDatabankConfig.endpointsubmitterpass2) + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + self.assertEqual(len(rdfgraph),12,'Graph length %i' %len(rdfgraph)) + (resp, zipfile) = self.doHTTP_GET( + resource="datasets/TestSubmission/testdir.zip", + expect_status=403, expect_reason="Forbidden", expect_type="application/zip") + # Access and check zip file content by general user + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointgeneraluser, + endpointpass=RDFDatabankConfig.endpointgeneralpass) + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + self.assertEqual(len(rdfgraph),12,'Graph length %i' %len(rdfgraph)) + (resp, zipfile) = self.doHTTP_GET( + resource="datasets/TestSubmission/testdir.zip", + expect_status=401, expect_reason="Unauthorized", expect_type="text/plain") + # Access and check zip file content by admin user3 + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointadminuser3, + endpointpass=RDFDatabankConfig.endpointadminpass3) + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + self.assertEqual(len(rdfgraph),12,'Graph length %i' %len(rdfgraph)) + (resp, zipfile) = self.doHTTP_GET( + resource="datasets/TestSubmission/testdir.zip", + expect_status=403, expect_reason="Forbidden", expect_type="application/zip") + # Access and check zip file content by manager 3 + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointmanageruser3, + endpointpass=RDFDatabankConfig.endpointmanagerpass3) + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + self.assertEqual(len(rdfgraph),12,'Graph length %i' %len(rdfgraph)) + (resp, zipfile) = self.doHTTP_GET( + resource="datasets/TestSubmission/testdir.zip", + expect_status=403, expect_reason="Forbidden", expect_type="application/zip") + # Access and check zip file content by submitter3 + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointsubmitteruser3, + endpointpass=RDFDatabankConfig.endpointsubmitterpass3) + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + self.assertEqual(len(rdfgraph),12,'Graph length %i' %len(rdfgraph)) + (resp, zipfile) = self.doHTTP_GET( + resource="datasets/TestSubmission/testdir.zip", + expect_status=403, expect_reason="Forbidden", expect_type="application/zip") + + # Delete embargo, check response + embargoed_until_date = datetime.datetime.now().isoformat() + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointsubmitteruser, + endpointpass=RDFDatabankConfig.endpointsubmitterpass) + fields = \ + [ ("embargoed", 'false') + ,("embargoed_until", embargoed_until_date) + ] + files =[] + (reqtype, reqdata) = SparqlQueryTestCase.encode_multipart_formdata(fields, files) + (resp,respdata) = self.doHTTP_POST( + reqdata, reqtype, + resource="datasets/TestSubmission", + expect_status=204, expect_reason="Updated") + #Access dataset and check content + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + self.assertEqual(len(rdfgraph),11,'Graph length %i' %len(rdfgraph)) + oxds = "http://vocab.ox.ac.uk/dataset/schema#" + dcterms = "http://purl.org/dc/terms/" + subj = URIRef(self.getManifestUri("datasets/TestSubmission")) + stype = URIRef(oxds+"DataSet") + self.failUnless((subj,RDF.type,stype) in rdfgraph, 'Testing submission type: '+subj+", "+stype) + self.failUnless((subj,URIRef(oxds+"isEmbargoed"),'False') in rdfgraph, 'oxds:isEmbargoed') + self.assertFalse((subj,URIRef(oxds+"embargoedUntil"),None) in rdfgraph, 'oxds:embargoedUntil') + self.failUnless((subj,URIRef(dcterms+"identifier"),None) in rdfgraph, 'dcterms:identifier') + self.failUnless((subj,URIRef(dcterms+"created"),None) in rdfgraph, 'dcterms:created') + self.failUnless((subj,URIRef(dcterms+"mediator"),None) in rdfgraph, 'dcterms:mediator') + self.failUnless((subj,URIRef(dcterms+"rights"),None) in rdfgraph, 'dcterms:rights') + self.failUnless((subj,URIRef(dcterms+"license"),None) in rdfgraph, 'dcterms:license') + self.failUnless((subj,URIRef(dcterms+"publisher"),None) in rdfgraph, 'dcterms:publisher') + self.failUnless((subj,URIRef(oxds+"currentVersion"),'2') in rdfgraph, 'oxds:currentVersion') + self.failUnless((subj,URIRef(dcterms+"modified"),None) in rdfgraph, 'dcterms:modified') + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"testdir.zip")) in rdfgraph, 'ore:aggregates testdir.zip') + #Access state information and check + (resp, data) = self.doHTTP_GET( + resource="states/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/json") + state = data['state'] + self.assertEqual(state['metadata']['embargoed'], False, "Embargoed?") + self.assertEqual(state['metadata']['embargoed_until'], "", "Should have no date for embargoed_until") + # Access and check zip file content by submitter + (resp, zipfile) = self.doHTTP_GET( + resource="datasets/TestSubmission/testdir.zip", + expect_status=200, expect_reason="OK", expect_type="application/zip") + self.assertEqual(zipdata, zipfile, "Difference between local and remote zipfile!") + # Access and check content by admin + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointadminuser, + endpointpass=RDFDatabankConfig.endpointadminpass) + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + self.assertEqual(len(rdfgraph),11,'Graph length %i' %len(rdfgraph)) + (resp, zipfile) = self.doHTTP_GET( + resource="datasets/TestSubmission/testdir.zip", + expect_status=200, expect_reason="OK", expect_type="application/zip") + self.assertEqual(zipdata, zipfile, "Difference between local and remote zipfile!") + # Access and check zip file content by manager + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointmanageruser, + endpointpass=RDFDatabankConfig.endpointmanagerpass) + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + self.assertEqual(len(rdfgraph),11,'Graph length %i' %len(rdfgraph)) + (resp, zipfile) = self.doHTTP_GET( + resource="datasets/TestSubmission/testdir.zip", + expect_status=200, expect_reason="OK", expect_type="application/zip") + self.assertEqual(zipdata, zipfile, "Difference between local and remote zipfile!") + # Access and check zip file content by submitter2 + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointsubmitteruser2, + endpointpass=RDFDatabankConfig.endpointsubmitterpass2) + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + self.assertEqual(len(rdfgraph),11,'Graph length %i' %len(rdfgraph)) + (resp, zipfile) = self.doHTTP_GET( + resource="datasets/TestSubmission/testdir.zip", + expect_status=200, expect_reason="OK", expect_type="application/zip") + self.assertEqual(zipdata, zipfile, "Difference between local and remote zipfile!") + # Access and check zip file content by general user + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointgeneraluser, + endpointpass=RDFDatabankConfig.endpointgeneralpass) + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + self.assertEqual(len(rdfgraph),11,'Graph length %i' %len(rdfgraph)) + (resp, zipfile) = self.doHTTP_GET( + resource="datasets/TestSubmission/testdir.zip", + expect_status=200, expect_reason="OK", expect_type="application/zip") + self.assertEqual(zipdata, zipfile, "Difference between local and remote zipfile!") + # Access and check zip file content by admin user3 + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointadminuser3, + endpointpass=RDFDatabankConfig.endpointadminpass3) + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + self.assertEqual(len(rdfgraph),11,'Graph length %i' %len(rdfgraph)) + (resp, zipfile) = self.doHTTP_GET( + resource="datasets/TestSubmission/testdir.zip", + expect_status=200, expect_reason="OK", expect_type="application/zip") + self.assertEqual(zipdata, zipfile, "Difference between local and remote zipfile!") + # Access and check zip file content by manager 3 + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointmanageruser3, + endpointpass=RDFDatabankConfig.endpointmanagerpass3) + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + self.assertEqual(len(rdfgraph),11,'Graph length %i' %len(rdfgraph)) + (resp, zipfile) = self.doHTTP_GET( + resource="datasets/TestSubmission/testdir.zip", + expect_status=200, expect_reason="OK", expect_type="application/zip") + self.assertEqual(zipdata, zipfile, "Difference between local and remote zipfile!") + # Access and check zip file content by submitter3 + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointsubmitteruser3, + endpointpass=RDFDatabankConfig.endpointsubmitterpass3) + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + self.assertEqual(len(rdfgraph),11,'Graph length %i' %len(rdfgraph)) + (resp, zipfile) = self.doHTTP_GET( + resource="datasets/TestSubmission/testdir.zip", + expect_status=200, expect_reason="OK", expect_type="application/zip") + self.assertEqual(zipdata, zipfile, "Difference between local and remote zipfile!") + + # Change embargo by admin, check response + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointadminuser, + endpointpass=RDFDatabankConfig.endpointadminpass) + d1 = (datetime.datetime.now() + datetime.timedelta(days=365*10)).isoformat() + fields = \ + [ ("embargoed", 'true') + ,("embargoed_until", d1) + ] + files =[] + (reqtype, reqdata) = SparqlQueryTestCase.encode_multipart_formdata(fields, files) + (resp,respdata) = self.doHTTP_POST( + reqdata, reqtype, + resource="datasets/TestSubmission", + expect_status=204, expect_reason="Updated") + #Access dataset and check content + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointsubmitteruser, + endpointpass=RDFDatabankConfig.endpointsubmitterpass) + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + self.assertEqual(len(rdfgraph),12,'Graph length %i' %len(rdfgraph)) + self.failUnless((subj,RDF.type,stype) in rdfgraph, 'Testing submission type: '+subj+", "+stype) + self.failUnless((subj,URIRef(oxds+"isEmbargoed"),'True') in rdfgraph, 'oxds:isEmbargoed') + self.failUnless((subj,URIRef(oxds+"embargoedUntil"),None) in rdfgraph, 'oxds:isEmbargoed') + self.failUnless((subj,URIRef(oxds+"currentVersion"),'3') in rdfgraph, 'oxds:currentVersion') + #Access state information and check + (resp, data) = self.doHTTP_GET( + resource="states/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/json") + state = data['state'] + self.assertEqual(state['metadata']['embargoed'], True, "Embargoed?") + self.failUnless(d1 in state['metadata']['embargoed_until'], "embargoed_until date?") + # Access and check zip file content + (resp, zipfile) = self.doHTTP_GET( + resource="datasets/TestSubmission/testdir.zip", + expect_status=200, expect_reason="OK", expect_type="application/zip") + self.assertEqual(zipdata, zipfile, "Difference between local and remote zipfile!") + # Access and check content by admin + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointadminuser, + endpointpass=RDFDatabankConfig.endpointadminpass) + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + self.assertEqual(len(rdfgraph),12,'Graph length %i' %len(rdfgraph)) + (resp, zipfile) = self.doHTTP_GET( + resource="datasets/TestSubmission/testdir.zip", + expect_status=200, expect_reason="OK", expect_type="application/zip") + self.assertEqual(zipdata, zipfile, "Difference between local and remote zipfile!") + # Access and check content by manager + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointmanageruser, + endpointpass=RDFDatabankConfig.endpointmanagerpass) + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + self.assertEqual(len(rdfgraph),12,'Graph length %i' %len(rdfgraph)) + (resp, zipfile) = self.doHTTP_GET( + resource="datasets/TestSubmission/testdir.zip", + expect_status=200, expect_reason="OK", expect_type="application/zip") + self.assertEqual(zipdata, zipfile, "Difference between local and remote zipfile!") + # Access and check content by submitter2 + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointsubmitteruser2, + endpointpass=RDFDatabankConfig.endpointsubmitterpass2) + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + self.assertEqual(len(rdfgraph),12,'Graph length %i' %len(rdfgraph)) + (resp, zipfile) = self.doHTTP_GET( + resource="datasets/TestSubmission/testdir.zip", + expect_status=403, expect_reason="Forbidden", expect_type="application/zip") + # Access and check content by general user + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointgeneraluser, + endpointpass=RDFDatabankConfig.endpointgeneralpass) + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + self.assertEqual(len(rdfgraph),12,'Graph length %i' %len(rdfgraph)) + (resp, zipfile) = self.doHTTP_GET( + resource="datasets/TestSubmission/testdir.zip", + expect_status=401, expect_reason="Unauthorized", expect_type="text/plain") + # Access and check content by admin user3 + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointadminuser3, + endpointpass=RDFDatabankConfig.endpointadminpass3) + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + self.assertEqual(len(rdfgraph),12,'Graph length %i' %len(rdfgraph)) + (resp, zipfile) = self.doHTTP_GET( + resource="datasets/TestSubmission/testdir.zip", + expect_status=403, expect_reason="Forbidden", expect_type="application/zip") + # Access and check content by manager 3 + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointmanageruser3, + endpointpass=RDFDatabankConfig.endpointmanagerpass3) + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + self.assertEqual(len(rdfgraph),12,'Graph length %i' %len(rdfgraph)) + (resp, zipfile) = self.doHTTP_GET( + resource="datasets/TestSubmission/testdir.zip", + expect_status=403, expect_reason="Forbidden", expect_type="application/zip") + # Access and check content by submitter3 + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointsubmitteruser3, + endpointpass=RDFDatabankConfig.endpointsubmitterpass3) + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + self.assertEqual(len(rdfgraph),12,'Graph length %i' %len(rdfgraph)) + (resp, zipfile) = self.doHTTP_GET( + resource="datasets/TestSubmission/testdir.zip", + expect_status=403, expect_reason="Forbidden", expect_type="application/zip") + + # Change embargo by submitter, check response + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointsubmitteruser, + endpointpass=RDFDatabankConfig.endpointsubmitterpass) + d = (datetime.datetime.now() + datetime.timedelta(days=10)).isoformat() + fields = \ + [ ("embargoed", 'true') + ,("embargoed_until", d) + ] + files =[] + (reqtype, reqdata) = SparqlQueryTestCase.encode_multipart_formdata(fields, files) + (resp,respdata) = self.doHTTP_POST( + reqdata, reqtype, + resource="datasets/TestSubmission", + expect_status=204, expect_reason="Updated") + #Access dataset and check content + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointsubmitteruser, + endpointpass=RDFDatabankConfig.endpointsubmitterpass) + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + self.assertEqual(len(rdfgraph),12,'Graph length %i' %len(rdfgraph)) + self.failUnless((subj,RDF.type,stype) in rdfgraph, 'Testing submission type: '+subj+", "+stype) + self.failUnless((subj,URIRef(oxds+"isEmbargoed"),'True') in rdfgraph, 'oxds:isEmbargoed') + self.failUnless((subj,URIRef(oxds+"embargoedUntil"),None) in rdfgraph, 'oxds:isEmbargoed') + self.failUnless((subj,URIRef(oxds+"currentVersion"),'4') in rdfgraph, 'oxds:currentVersion') + #Access state information and check + (resp, data) = self.doHTTP_GET( + resource="states/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/json") + state = data['state'] + self.assertEqual(state['metadata']['embargoed'], True, "Embargoed?") + self.failUnless(d in state['metadata']['embargoed_until'], "embargoed_until date?") + # Access and check zip file content + (resp, zipfile) = self.doHTTP_GET( + resource="datasets/TestSubmission/testdir.zip", + expect_status=200, expect_reason="OK", expect_type="application/zip") + self.assertEqual(zipdata, zipfile, "Difference between local and remote zipfile!") + # Access and check content by admin + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointadminuser, + endpointpass=RDFDatabankConfig.endpointadminpass) + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + self.assertEqual(len(rdfgraph),12,'Graph length %i' %len(rdfgraph)) + (resp, zipfile) = self.doHTTP_GET( + resource="datasets/TestSubmission/testdir.zip", + expect_status=200, expect_reason="OK", expect_type="application/zip") + self.assertEqual(zipdata, zipfile, "Difference between local and remote zipfile!") + # Access and check content by manager + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointmanageruser, + endpointpass=RDFDatabankConfig.endpointmanagerpass) + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + self.assertEqual(len(rdfgraph),12,'Graph length %i' %len(rdfgraph)) + (resp, zipfile) = self.doHTTP_GET( + resource="datasets/TestSubmission/testdir.zip", + expect_status=200, expect_reason="OK", expect_type="application/zip") + self.assertEqual(zipdata, zipfile, "Difference between local and remote zipfile!") + # Access and check content by submitter2 + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointsubmitteruser2, + endpointpass=RDFDatabankConfig.endpointsubmitterpass2) + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + self.assertEqual(len(rdfgraph),12,'Graph length %i' %len(rdfgraph)) + (resp, zipfile) = self.doHTTP_GET( + resource="datasets/TestSubmission/testdir.zip", + expect_status=403, expect_reason="Forbidden", expect_type="application/zip") + # Access and check content by general user + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointgeneraluser, + endpointpass=RDFDatabankConfig.endpointgeneralpass) + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + self.assertEqual(len(rdfgraph),12,'Graph length %i' %len(rdfgraph)) + (resp, zipfile) = self.doHTTP_GET( + resource="datasets/TestSubmission/testdir.zip", + expect_status=401, expect_reason="Unauthorized", expect_type="text/plain") + # Access and check content by admin user3 + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointadminuser3, + endpointpass=RDFDatabankConfig.endpointadminpass3) + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + self.assertEqual(len(rdfgraph),12,'Graph length %i' %len(rdfgraph)) + (resp, zipfile) = self.doHTTP_GET( + resource="datasets/TestSubmission/testdir.zip", + expect_status=403, expect_reason="Forbidden", expect_type="application/zip") + # Access and check content by manager 3 + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointmanageruser3, + endpointpass=RDFDatabankConfig.endpointmanagerpass3) + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + self.assertEqual(len(rdfgraph),12,'Graph length %i' %len(rdfgraph)) + (resp, zipfile) = self.doHTTP_GET( + resource="datasets/TestSubmission/testdir.zip", + expect_status=403, expect_reason="Forbidden", expect_type="application/zip") + # Access and check content by submitter3 + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointsubmitteruser3, + endpointpass=RDFDatabankConfig.endpointsubmitterpass3) + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + self.assertEqual(len(rdfgraph),12,'Graph length %i' %len(rdfgraph)) + (resp, zipfile) = self.doHTTP_GET( + resource="datasets/TestSubmission/testdir.zip", + expect_status=403, expect_reason="Forbidden", expect_type="application/zip") + + # Change embargo by submitter 2, check response + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointsubmitteruser2, + endpointpass=RDFDatabankConfig.endpointsubmitterpass2) + fields = \ + [ ("embargoed", 'false') + ] + files =[] + (reqtype, reqdata) = SparqlQueryTestCase.encode_multipart_formdata(fields, files) + (resp,respdata) = self.doHTTP_POST( + reqdata, reqtype, + resource="datasets/TestSubmission", + expect_status=403, expect_reason="Forbidden") + #Access dataset and check content + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointsubmitteruser, + endpointpass=RDFDatabankConfig.endpointsubmitterpass) + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + self.assertEqual(len(rdfgraph),12,'Graph length %i' %len(rdfgraph)) + self.failUnless((subj,RDF.type,stype) in rdfgraph, 'Testing submission type: '+subj+", "+stype) + self.failUnless((subj,URIRef(oxds+"isEmbargoed"),'True') in rdfgraph, 'oxds:isEmbargoed') + self.failUnless((subj,URIRef(oxds+"embargoedUntil"),None) in rdfgraph, 'oxds:isEmbargoed') + self.failUnless((subj,URIRef(oxds+"currentVersion"),'4') in rdfgraph, 'oxds:currentVersion') + #Access state information and check + (resp, data) = self.doHTTP_GET( + resource="states/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/json") + state = data['state'] + self.assertEqual(state['metadata']['embargoed'], True, "Embargoed?") + self.failUnless(d in state['metadata']['embargoed_until'], "embargoed_until date?") + + # Change embargo by general user, check response + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointgeneraluser, + endpointpass=RDFDatabankConfig.endpointgeneralpass) + fields = \ + [ ("embargoed", 'false') + ] + files =[] + (reqtype, reqdata) = SparqlQueryTestCase.encode_multipart_formdata(fields, files) + (resp,respdata) = self.doHTTP_POST( + reqdata, reqtype, + resource="datasets/TestSubmission", + expect_status=401, expect_reason="Unauthorized", expect_type="text/plain") + #Access dataset and check content + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointsubmitteruser, + endpointpass=RDFDatabankConfig.endpointsubmitterpass) + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + self.assertEqual(len(rdfgraph),12,'Graph length %i' %len(rdfgraph)) + self.failUnless((subj,RDF.type,stype) in rdfgraph, 'Testing submission type: '+subj+", "+stype) + self.failUnless((subj,URIRef(oxds+"isEmbargoed"),'True') in rdfgraph, 'oxds:isEmbargoed') + self.failUnless((subj,URIRef(oxds+"embargoedUntil"),None) in rdfgraph, 'oxds:isEmbargoed') + self.failUnless((subj,URIRef(oxds+"currentVersion"),'4') in rdfgraph, 'oxds:currentVersion') + #Access state information and check + (resp, data) = self.doHTTP_GET( + resource="states/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/json") + state = data['state'] + self.assertEqual(state['metadata']['embargoed'], True, "Embargoed?") + self.failUnless(d in state['metadata']['embargoed_until'], "embargoed_until date?") + + # Change embargo by admin user3, check response + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointadminuser3, + endpointpass=RDFDatabankConfig.endpointadminpass3) + fields = \ + [ ("embargoed", 'false') + ] + files =[] + (reqtype, reqdata) = SparqlQueryTestCase.encode_multipart_formdata(fields, files) + (resp,respdata) = self.doHTTP_POST( + reqdata, reqtype, + resource="datasets/TestSubmission", + expect_status=403, expect_reason="Forbidden") + #Access dataset and check content + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointsubmitteruser, + endpointpass=RDFDatabankConfig.endpointsubmitterpass) + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + self.assertEqual(len(rdfgraph),12,'Graph length %i' %len(rdfgraph)) + self.failUnless((subj,RDF.type,stype) in rdfgraph, 'Testing submission type: '+subj+", "+stype) + self.failUnless((subj,URIRef(oxds+"isEmbargoed"),'True') in rdfgraph, 'oxds:isEmbargoed') + self.failUnless((subj,URIRef(oxds+"embargoedUntil"),None) in rdfgraph, 'oxds:isEmbargoed') + self.failUnless((subj,URIRef(oxds+"currentVersion"),'4') in rdfgraph, 'oxds:currentVersion') + #Access state information and check + (resp, data) = self.doHTTP_GET( + resource="states/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/json") + state = data['state'] + self.assertEqual(state['metadata']['embargoed'], True, "Embargoed?") + self.failUnless(d in state['metadata']['embargoed_until'], "embargoed_until date?") + + # Change embargo by maanger user3, check response + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointmanageruser3, + endpointpass=RDFDatabankConfig.endpointmanagerpass3) + fields = \ + [ ("embargoed", 'false') + ] + files =[] + (reqtype, reqdata) = SparqlQueryTestCase.encode_multipart_formdata(fields, files) + (resp,respdata) = self.doHTTP_POST( + reqdata, reqtype, + resource="datasets/TestSubmission", + expect_status=403, expect_reason="Forbidden") + #Access dataset and check content + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointsubmitteruser, + endpointpass=RDFDatabankConfig.endpointsubmitterpass) + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + self.assertEqual(len(rdfgraph),12,'Graph length %i' %len(rdfgraph)) + self.failUnless((subj,RDF.type,stype) in rdfgraph, 'Testing submission type: '+subj+", "+stype) + self.failUnless((subj,URIRef(oxds+"isEmbargoed"),'True') in rdfgraph, 'oxds:isEmbargoed') + self.failUnless((subj,URIRef(oxds+"embargoedUntil"),None) in rdfgraph, 'oxds:isEmbargoed') + self.failUnless((subj,URIRef(oxds+"currentVersion"),'4') in rdfgraph, 'oxds:currentVersion') + #Access state information and check + (resp, data) = self.doHTTP_GET( + resource="states/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/json") + state = data['state'] + self.assertEqual(state['metadata']['embargoed'], True, "Embargoed?") + self.failUnless(d in state['metadata']['embargoed_until'], "embargoed_until date?") + + # Change embargo by submitter user3, check response + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointsubmitteruser3, + endpointpass=RDFDatabankConfig.endpointsubmitterpass3) + fields = \ + [ ("embargoed", 'false') + ] + files =[] + (reqtype, reqdata) = SparqlQueryTestCase.encode_multipart_formdata(fields, files) + (resp,respdata) = self.doHTTP_POST( + reqdata, reqtype, + resource="datasets/TestSubmission", + expect_status=403, expect_reason="Forbidden") + #Access dataset and check content + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointsubmitteruser, + endpointpass=RDFDatabankConfig.endpointsubmitterpass) + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + self.assertEqual(len(rdfgraph),12,'Graph length %i' %len(rdfgraph)) + self.failUnless((subj,RDF.type,stype) in rdfgraph, 'Testing submission type: '+subj+", "+stype) + self.failUnless((subj,URIRef(oxds+"isEmbargoed"),'True') in rdfgraph, 'oxds:isEmbargoed') + self.failUnless((subj,URIRef(oxds+"embargoedUntil"),None) in rdfgraph, 'oxds:isEmbargoed') + self.failUnless((subj,URIRef(oxds+"currentVersion"),'4') in rdfgraph, 'oxds:currentVersion') + #Access state information and check + (resp, data) = self.doHTTP_GET( + resource="states/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/json") + state = data['state'] + self.assertEqual(state['metadata']['embargoed'], True, "Embargoed?") + self.failUnless(d in state['metadata']['embargoed_until'], "embargoed_until date?") + + # Delete embargo by manager, check response + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointmanageruser, + endpointpass=RDFDatabankConfig.endpointmanagerpass) + d = (datetime.datetime.now() + datetime.timedelta(days=365*70)).isoformat() + fields = \ + [ ("embargoed", 'false') + ] + files =[] + (reqtype, reqdata) = SparqlQueryTestCase.encode_multipart_formdata(fields, files) + (resp,respdata) = self.doHTTP_POST( + reqdata, reqtype, + resource="datasets/TestSubmission", + expect_status=204, expect_reason="Updated") + #Access dataset and check content + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointsubmitteruser, + endpointpass=RDFDatabankConfig.endpointsubmitterpass) + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + self.assertEqual(len(rdfgraph),11,'Graph length %i' %len(rdfgraph)) + oxds = "http://vocab.ox.ac.uk/dataset/schema#" + dcterms = "http://purl.org/dc/terms/" + subj = URIRef(self.getManifestUri("datasets/TestSubmission")) + stype = URIRef(oxds+"DataSet") + self.failUnless((subj,RDF.type,stype) in rdfgraph, 'Testing submission type: '+subj+", "+stype) + self.failUnless((subj,URIRef(oxds+"isEmbargoed"),'False') in rdfgraph, 'oxds:isEmbargoed') + self.failUnless((subj,URIRef(dcterms+"identifier"),None) in rdfgraph, 'dcterms:identifier') + self.failUnless((subj,URIRef(dcterms+"created"),None) in rdfgraph, 'dcterms:created') + self.failUnless((subj,URIRef(dcterms+"mediator"),None) in rdfgraph, 'dcterms:mediator') + self.failUnless((subj,URIRef(dcterms+"rights"),None) in rdfgraph, 'dcterms:rights') + self.failUnless((subj,URIRef(dcterms+"license"),None) in rdfgraph, 'dcterms:license') + self.failUnless((subj,URIRef(dcterms+"publisher"),None) in rdfgraph, 'dcterms:publisher') + self.failUnless((subj,URIRef(oxds+"currentVersion"),'5') in rdfgraph, 'oxds:currentVersion') + self.failUnless((subj,URIRef(dcterms+"modified"),None) in rdfgraph, 'dcterms:modified') + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"testdir.zip")) in rdfgraph, 'ore:aggregates testdir.zip') + #Access state information and check + (resp, data) = self.doHTTP_GET( + resource="states/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/json") + state = data['state'] + self.assertEqual(state['metadata']['embargoed'], False, "Embargoed?") + self.assertEqual(state['metadata']['embargoed_until'], "", "Should have no date for embargoed_until") + # Access and check zip file content by submitter + (resp, zipfile) = self.doHTTP_GET( + resource="datasets/TestSubmission/testdir.zip", + expect_status=200, expect_reason="OK", expect_type="application/zip") + self.assertEqual(zipdata, zipfile, "Difference between local and remote zipfile!") + # Access and check content by admin + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointadminuser, + endpointpass=RDFDatabankConfig.endpointadminpass) + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + self.assertEqual(len(rdfgraph),11,'Graph length %i' %len(rdfgraph)) + (resp, zipfile) = self.doHTTP_GET( + resource="datasets/TestSubmission/testdir.zip", + expect_status=200, expect_reason="OK", expect_type="application/zip") + self.assertEqual(zipdata, zipfile, "Difference between local and remote zipfile!") + # Access and check content by manager + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointmanageruser, + endpointpass=RDFDatabankConfig.endpointmanagerpass) + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + self.assertEqual(len(rdfgraph),11,'Graph length %i' %len(rdfgraph)) + (resp, zipfile) = self.doHTTP_GET( + resource="datasets/TestSubmission/testdir.zip", + expect_status=200, expect_reason="OK", expect_type="application/zip") + self.assertEqual(zipdata, zipfile, "Difference between local and remote zipfile!") + # Access and check content by submitter2 + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointsubmitteruser2, + endpointpass=RDFDatabankConfig.endpointsubmitterpass2) + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + self.assertEqual(len(rdfgraph),11,'Graph length %i' %len(rdfgraph)) + (resp, zipfile) = self.doHTTP_GET( + resource="datasets/TestSubmission/testdir.zip", + expect_status=200, expect_reason="OK", expect_type="application/zip") + self.assertEqual(zipdata, zipfile, "Difference between local and remote zipfile!") + # Access and check content by general user + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointgeneraluser, + endpointpass=RDFDatabankConfig.endpointgeneralpass) + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + self.assertEqual(len(rdfgraph),11,'Graph length %i' %len(rdfgraph)) + (resp, zipfile) = self.doHTTP_GET( + resource="datasets/TestSubmission/testdir.zip", + expect_status=200, expect_reason="OK", expect_type="application/zip") + self.assertEqual(zipdata, zipfile, "Difference between local and remote zipfile!") + # Access and check content by admin user3 + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointadminuser3, + endpointpass=RDFDatabankConfig.endpointadminpass3) + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + self.assertEqual(len(rdfgraph),11,'Graph length %i' %len(rdfgraph)) + (resp, zipfile) = self.doHTTP_GET( + resource="datasets/TestSubmission/testdir.zip", + expect_status=200, expect_reason="OK", expect_type="application/zip") + self.assertEqual(zipdata, zipfile, "Difference between local and remote zipfile!") + # Access and check content by manager 3 + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointmanageruser3, + endpointpass=RDFDatabankConfig.endpointmanagerpass3) + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + self.assertEqual(len(rdfgraph),11,'Graph length %i' %len(rdfgraph)) + (resp, zipfile) = self.doHTTP_GET( + resource="datasets/TestSubmission/testdir.zip", + expect_status=200, expect_reason="OK", expect_type="application/zip") + self.assertEqual(zipdata, zipfile, "Difference between local and remote zipfile!") + # Access and check content by submitter3 + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointsubmitteruser3, + endpointpass=RDFDatabankConfig.endpointsubmitterpass3) + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + self.assertEqual(len(rdfgraph),11,'Graph length %i' %len(rdfgraph)) + (resp, zipfile) = self.doHTTP_GET( + resource="datasets/TestSubmission/testdir.zip", + expect_status=200, expect_reason="OK", expect_type="application/zip") + self.assertEqual(zipdata, zipfile, "Difference between local and remote zipfile!") + + # Change embargo by submitter, check response + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointsubmitteruser, + endpointpass=RDFDatabankConfig.endpointsubmitterpass) + #d = (datetime.datetime.now() + datetime.timedelta(days=365*70)).isoformat() + fields = \ + [ ("embargoed", 'true') + ] + files =[] + (reqtype, reqdata) = SparqlQueryTestCase.encode_multipart_formdata(fields, files) + (resp,respdata) = self.doHTTP_POST( + reqdata, reqtype, + resource="datasets/TestSubmission", + expect_status=204, expect_reason="Updated") + #Access dataset and check content + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointsubmitteruser, + endpointpass=RDFDatabankConfig.endpointsubmitterpass) + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + self.assertEqual(len(rdfgraph),11,'Graph length %i' %len(rdfgraph)) + oxds = "http://vocab.ox.ac.uk/dataset/schema#" + dcterms = "http://purl.org/dc/terms/" + subj = URIRef(self.getManifestUri("datasets/TestSubmission")) + stype = URIRef(oxds+"DataSet") + self.failUnless((subj,RDF.type,stype) in rdfgraph, 'Testing submission type: '+subj+", "+stype) + self.failUnless((subj,URIRef(oxds+"isEmbargoed"),'True') in rdfgraph, 'oxds:isEmbargoed') + self.failUnless((subj,URIRef(dcterms+"identifier"),None) in rdfgraph, 'dcterms:identifier') + self.failUnless((subj,URIRef(dcterms+"created"),None) in rdfgraph, 'dcterms:created') + self.failUnless((subj,URIRef(dcterms+"mediator"),None) in rdfgraph, 'dcterms:mediator') + self.failUnless((subj,URIRef(dcterms+"rights"),None) in rdfgraph, 'dcterms:rights') + self.failUnless((subj,URIRef(dcterms+"license"),None) in rdfgraph, 'dcterms:license') + self.failUnless((subj,URIRef(dcterms+"publisher"),None) in rdfgraph, 'dcterms:publisher') + self.failUnless((subj,URIRef(oxds+"currentVersion"),'6') in rdfgraph, 'oxds:currentVersion') + self.failUnless((subj,URIRef(dcterms+"modified"),None) in rdfgraph, 'dcterms:modified') + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"testdir.zip")) in rdfgraph, 'ore:aggregates testdir.zip') + #Access state information and check + (resp, data) = self.doHTTP_GET( + resource="states/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/json") + state = data['state'] + self.assertEqual(state['metadata']['embargoed'], True, "Embargoed?") + self.assertEqual(state['metadata']['embargoed_until'], '', "Embargoed?") + # Access and check zip file content by submitter + (resp, zipfile) = self.doHTTP_GET( + resource="datasets/TestSubmission/testdir.zip", + expect_status=200, expect_reason="OK", expect_type="application/zip") + self.assertEqual(zipdata, zipfile, "Difference between local and remote zipfile!") + # Access and check content by admin + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointadminuser, + endpointpass=RDFDatabankConfig.endpointadminpass) + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + self.assertEqual(len(rdfgraph),11,'Graph length %i' %len(rdfgraph)) + (resp, zipfile) = self.doHTTP_GET( + resource="datasets/TestSubmission/testdir.zip", + expect_status=200, expect_reason="OK", expect_type="application/zip") + self.assertEqual(zipdata, zipfile, "Difference between local and remote zipfile!") + # Access and check content by manager + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointmanageruser, + endpointpass=RDFDatabankConfig.endpointmanagerpass) + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + self.assertEqual(len(rdfgraph),11,'Graph length %i' %len(rdfgraph)) + (resp, zipfile) = self.doHTTP_GET( + resource="datasets/TestSubmission/testdir.zip", + expect_status=200, expect_reason="OK", expect_type="application/zip") + self.assertEqual(zipdata, zipfile, "Difference between local and remote zipfile!") + # Access and check content by submitter2 + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointsubmitteruser2, + endpointpass=RDFDatabankConfig.endpointsubmitterpass2) + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + self.assertEqual(len(rdfgraph),11,'Graph length %i' %len(rdfgraph)) + (resp, zipfile) = self.doHTTP_GET( + resource="datasets/TestSubmission/testdir.zip", + expect_status=403, expect_reason="Forbidden", expect_type="application/zip") + # Access and check content by general user + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointgeneraluser, + endpointpass=RDFDatabankConfig.endpointgeneralpass) + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + self.assertEqual(len(rdfgraph),11,'Graph length %i' %len(rdfgraph)) + (resp, zipfile) = self.doHTTP_GET( + resource="datasets/TestSubmission/testdir.zip", + expect_status=401, expect_reason="Unauthorized", expect_type="text/plain") + # Access and check content by admin user3 + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointadminuser3, + endpointpass=RDFDatabankConfig.endpointadminpass3) + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + self.assertEqual(len(rdfgraph),11,'Graph length %i' %len(rdfgraph)) + (resp, zipfile) = self.doHTTP_GET( + resource="datasets/TestSubmission/testdir.zip", + expect_status=403, expect_reason="Forbidden", expect_type="application/zip") + # Access and check content by manager 3 + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointmanageruser3, + endpointpass=RDFDatabankConfig.endpointmanagerpass3) + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + self.assertEqual(len(rdfgraph),11,'Graph length %i' %len(rdfgraph)) + (resp, zipfile) = self.doHTTP_GET( + resource="datasets/TestSubmission/testdir.zip", + expect_status=403, expect_reason="Forbidden", expect_type="application/zip") + # Access and check content by submitter3 + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointsubmitteruser3, + endpointpass=RDFDatabankConfig.endpointsubmitterpass3) + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + self.assertEqual(len(rdfgraph),11,'Graph length %i' %len(rdfgraph)) + (resp, zipfile) = self.doHTTP_GET( + resource="datasets/TestSubmission/testdir.zip", + expect_status=403, expect_reason="Forbidden", expect_type="application/zip") + + + + def testFileUnpack(self): + """Unpack zip file to a new dataset - POST zip filename to /silo_name/items/dataset_name""" + # Create a new dataset, check response + self.createSubmissionDataset() + # Upload zip file + zipdata = self.uploadSubmissionZipfile() + zipdata = self.uploadSubmissionZipfile(file_to_upload="testdir2.zip") + zipdata = self.uploadSubmissionZipfile(file_to_upload="testrdf.zip") + zipdata = self.uploadSubmissionZipfile(file_to_upload="testrdf2.zip") + # Unpack ZIP file into a new dataset, check response + fields = \ + [ ("filename", "testdir.zip") + ] + files = [] + (reqtype, reqdata) = SparqlQueryTestCase.encode_multipart_formdata(fields, files) + (resp,respdata) = self.doHTTP_POST( + reqdata, reqtype, + resource="items/TestSubmission", + expect_status=201, expect_reason="Created") + LHobtained = resp.getheader('Content-Location', None) + LHexpected = "%sdatasets/TestSubmission-testdir"%self._endpointpath + self.assertEquals(LHobtained, LHexpected, 'Content-Location not correct') + # Access parent dataset, check response + (resp, respdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/json") + # Access and check list of contents in TestSubmission + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + subj = URIRef(self.getManifestUri("datasets/TestSubmission")) + subj2 = URIRef(self.getManifestUri("datasets/TestSubmission-testdir")) + base = self.getManifestUri("datasets/TestSubmission/") + dcterms = "http://purl.org/dc/terms/" + ore = "http://www.openarchives.org/ore/terms/" + oxds = "http://vocab.ox.ac.uk/dataset/schema#" + stype = URIRef(oxds+"DataSet") + self.assertEqual(len(rdfgraph),16,'Graph length %i' %len(rdfgraph)) + self.failUnless((subj,RDF.type,stype) in rdfgraph, 'Testing submission type: '+subj+", "+stype) + self.failUnless((subj,URIRef(dcterms+"created"),None) in rdfgraph, 'dcterms:created') + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"testdir.zip")) in rdfgraph, 'ore:aggregates testdir.zip') + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"testdir.zip")) in rdfgraph, 'ore:aggregates testrdf.zip') + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"testdir.zip")) in rdfgraph, 'ore:aggregates testdir2.zip') + self.failUnless((URIRef(base+"testdir.zip"),URIRef(dcterms+"hasVersion"),None) in rdfgraph, 'dcterms:hasVersion') + self.failUnless((subj,URIRef(dcterms+"identifier"),None) in rdfgraph, 'dcterms:identifier') + self.failUnless((subj,URIRef(dcterms+"mediator"),None) in rdfgraph, 'dcterms:mediator') + self.failUnless((subj,URIRef(dcterms+"rights"),None) in rdfgraph, 'dcterms:rights') + self.failUnless((subj,URIRef(dcterms+"license"),None) in rdfgraph, 'dcterms:license') + self.failUnless((subj,URIRef(dcterms+"publisher"),None) in rdfgraph, 'dcterms:publisher') + self.failUnless((subj,URIRef(oxds+"isEmbargoed"),'True') in rdfgraph, 'oxds:isEmbargoed') + self.failUnless((subj,URIRef(oxds+"embargoedUntil"),None) in rdfgraph, 'oxds:isEmbargoed') + self.failUnless((subj,URIRef(oxds+"currentVersion"),"4") in rdfgraph, 'oxds:currentVersion') + self.failUnless((subj,URIRef(dcterms+"modified"),None) in rdfgraph, 'dcterms:modified') + self.failUnless((URIRef(base+"testdir.zip"),URIRef(dcterms+"hasVersion"),subj2) in rdfgraph, 'ore:aggregates testrdf.zip') + # Access new dataset, check response + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission-testdir", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + subj = URIRef(self.getManifestUri("datasets/TestSubmission-testdir")) + stype = URIRef("http://vocab.ox.ac.uk/dataset/schema#Grouping") + base = self.getManifestUri("datasets/TestSubmission-testdir/") + self.assertEqual(len(rdfgraph),17,'Graph length %i' %len(rdfgraph)) + self.failUnless((subj,RDF.type,stype) in rdfgraph, 'Testing submission type: '+subj+", "+stype) + self.failUnless((subj,URIRef(dcterms+"identifier"),None) in rdfgraph, 'dcterms:identifier') + self.failUnless((subj,URIRef(dcterms+"mediator"),None) in rdfgraph, 'dcterms:mediator') + self.failUnless((subj,URIRef(dcterms+"rights"),None) in rdfgraph, 'dcterms:rights') + self.failUnless((subj,URIRef(dcterms+"license"),None) in rdfgraph, 'dcterms:license') + self.failUnless((subj,URIRef(dcterms+"publisher"),None) in rdfgraph, 'dcterms:publisher') + self.failUnless((subj,URIRef(oxds+"isEmbargoed"),'True') in rdfgraph, 'oxds:isEmbargoed') + self.failUnless((subj,URIRef(oxds+"embargoedUntil"),None) in rdfgraph, 'oxds:isEmbargoed') + self.failUnless((subj,URIRef(dcterms+"created"),None) in rdfgraph, 'dcterms:created') + self.failUnless((subj,URIRef(dcterms+"modified"),None) in rdfgraph, 'dcterms:modified') + self.failUnless((subj,URIRef(dcterms+"isVersionOf"),None) in rdfgraph, 'dcterms:isVersionOf') + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"directory")) in rdfgraph, 'ore:aggregates directory') + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"directory/file1.a")) in rdfgraph, 'ore:aggregates file1.a') + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"directory/file1.b")) in rdfgraph, 'ore:aggregates file1.b') + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"directory/file2.a")) in rdfgraph, 'ore:aggregates file2.a') + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"test-csv.csv")) in rdfgraph, 'ore:aggregates test-csv.csv') + self.failUnless((subj,URIRef(oxds+"currentVersion"),"1") in rdfgraph, 'oxds:currentVersion') + # Access and check content of a resource + (resp, filedata) = self.doHTTP_GET( + resource="datasets/TestSubmission-testdir/directory/file1.b", + expect_status=200, expect_reason="OK", expect_type="text/plain") + checkdata = open("testdata/testdir/directory/file1.b").read() + self.assertEqual(filedata, checkdata, "Difference between local and remote data!") + #Access state information of TestSubmission-testdir + (resp, data) = self.doHTTP_GET( + resource="states/TestSubmission-testdir", + expect_status=200, expect_reason="OK", expect_type="application/json") + state = data['state'] + parts = data['parts'] + self.assertEqual(len(state.keys()), 11, "States") + self.assertEqual(state['item_id'], "TestSubmission-testdir", "Submission item identifier") + self.assertEqual(len(state['versions']), 2, "Two versions") + self.assertEqual(state['versions'][0], '0', "Version 0") + self.assertEqual(state['versions'][1], '1', "Version 1") + self.assertEqual(state['currentversion'], '1', "Current version == 1") + self.assertEqual(state['rdffileformat'], 'xml', "RDF file type") + self.assertEqual(state['rdffilename'], 'manifest.rdf', "RDF file name") + self.assertEqual(len(state['files']['0']), 1, "List should contain just manifest.rdf") + self.assertEqual(len(state['files']['1']), 3, "List should contain manifest.rdf, testdir and test-csv.csv") + self.assertEqual(len(state['metadata_files']['0']), 0, "metadata_files of version 0") + self.assertEqual(len(state['metadata_files']['1']), 0, "metadata_files of version 1") + self.assertEqual(state['subdir']['0'], [], "Subdirectory for version 0") + self.assertEqual(state['subdir']['1'], ['directory'], "Subdirectory for version 1") + self.assertEqual(state['metadata']['createdby'], RDFDatabankConfig.endpointsubmitteruser, "Created by") + self.assertEqual(state['metadata']['embargoed'], True, "Embargoed?") + d = (datetime.datetime.now() + relativedelta(years=+70)).isoformat() + d = d.split('T')[0] + self.assertTrue(d in state['metadata']['embargoed_until'], "embargoed_until %s?"%d) + self.assertEqual(len(parts.keys()), 5, "Parts") + self.assertEqual(len(parts['4=TestSubmission-testdir'].keys()), 13, "File stats for 4=TestSubmission-testdir") + self.assertEqual(len(parts['manifest.rdf'].keys()), 13, "File stats for manifest.rdf") + self.assertEqual(len(parts['directory'].keys()), 0, "File stats for directory") + self.assertEqual(len(parts['test-csv.csv'].keys()), 13, "File stats for test-csv.csv") + + # Unpack ZIP file into a new dataset by admin, check response + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointadminuser, + endpointpass=RDFDatabankConfig.endpointadminpass) + fields = \ + [ ("filename", "testdir2.zip") + ] + files = [] + (reqtype, reqdata) = SparqlQueryTestCase.encode_multipart_formdata(fields, files) + (resp,respdata) = self.doHTTP_POST( + reqdata, reqtype, + resource="items/TestSubmission", + expect_status=201, expect_reason="Created") + LHobtained = resp.getheader('Content-Location', None) + LHexpected = "%sdatasets/TestSubmission-testdir2"%self._endpointpath + self.assertEquals(LHobtained, LHexpected, 'Content-Location not correct') + # Access and check list of contents in TestSubmission + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + stype = URIRef(oxds+"DataSet") + subj = URIRef(self.getManifestUri("datasets/TestSubmission")) + subj2 = URIRef(self.getManifestUri("datasets/TestSubmission-testdir2")) + base = self.getManifestUri("datasets/TestSubmission/") + self.assertEqual(len(rdfgraph),17,'Graph length %i' %len(rdfgraph)) + self.failUnless((URIRef(base+"testdir2.zip"),URIRef(dcterms+"hasVersion"),subj2) in rdfgraph, 'dcterms:hasVersion') + self.failUnless((subj,URIRef(oxds+"currentVersion"),"4") in rdfgraph, 'oxds:currentVersion') + # Access new dataset, check response + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission-testdir2", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + subj = URIRef(self.getManifestUri("datasets/TestSubmission-testdir2")) + stype = URIRef("http://vocab.ox.ac.uk/dataset/schema#Grouping") + base = self.getManifestUri("datasets/TestSubmission-testdir2/") + owl = "http://www.w3.org/2002/07/owl#" + self.assertEqual(len(rdfgraph),22,'Graph length %i' %len(rdfgraph)) + self.failUnless((subj,RDF.type,stype) in rdfgraph, 'Testing submission type: '+subj+", "+stype) + self.failUnless((subj,URIRef(dcterms+"identifier"),None) in rdfgraph, 'dcterms:identifier') + self.failUnless((subj,URIRef(dcterms+"mediator"),None) in rdfgraph, 'dcterms:mediator') + self.failUnless((subj,URIRef(dcterms+"rights"),None) in rdfgraph, 'dcterms:rights') + self.failUnless((subj,URIRef(dcterms+"license"),None) in rdfgraph, 'dcterms:license') + self.failUnless((subj,URIRef(dcterms+"publisher"),None) in rdfgraph, 'dcterms:publisher') + self.failUnless((subj,URIRef(oxds+"isEmbargoed"),'True') in rdfgraph, 'oxds:isEmbargoed') + self.failUnless((subj,URIRef(oxds+"embargoedUntil"),None) in rdfgraph, 'oxds:isEmbargoed') + self.failUnless((subj,URIRef(dcterms+"created"),None) in rdfgraph, 'dcterms:created') + self.failUnless((subj,URIRef(dcterms+"modified"),None) in rdfgraph, 'dcterms:modified') + self.failUnless((subj,URIRef(dcterms+"isVersionOf"),None) in rdfgraph, 'dcterms:isVersionOf') + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"directory1")) in rdfgraph, 'ore:aggregates directory1') + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"directory1/file1.a")) in rdfgraph, 'ore:aggregates file1.a') + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"directory1/file1.b")) in rdfgraph, 'ore:aggregates file1.b') + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"directory1/file1.c")) in rdfgraph, 'ore:aggregates file1.c') + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"directory2")) in rdfgraph, 'ore:aggregates directory2') + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"directory2/file2.a")) in rdfgraph, 'ore:aggregates file2.a') + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"directory2/file2.b")) in rdfgraph, 'ore:aggregates file2.b') + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"test-csv.csv")) in rdfgraph, 'ore:aggregates test-csv.csv') + self.failUnless((subj,URIRef(oxds+"currentVersion"),"1") in rdfgraph, 'oxds:currentVersion') + self.failUnless((subj,URIRef(dcterms+"title"),"Test dataset with merged metadata") in rdfgraph, 'dcterms:title') + self.failUnless((subj,URIRef(owl+"sameAs"),URIRef("http://example.org/testdir2/")) in rdfgraph, 'owl:sameAs') + # Access and check content of a resource + (resp, filedata) = self.doHTTP_GET( + resource="datasets/TestSubmission-testdir2/directory1/file1.b", + expect_status=200, expect_reason="OK", expect_type="text/plain") + checkdata = open("testdata/testdir2/directory1/file1.b").read() + self.assertEqual(filedata, checkdata, "Difference between local and remote data!") + # Access and check list of contents in TestSubmission as submitter + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointsubmitteruser, + endpointpass=RDFDatabankConfig.endpointsubmitterpass) + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + stype = URIRef(oxds+"DataSet") + subj = URIRef(self.getManifestUri("datasets/TestSubmission")) + subj2 = URIRef(self.getManifestUri("datasets/TestSubmission-testdir2")) + base = self.getManifestUri("datasets/TestSubmission/") + self.assertEqual(len(rdfgraph),17,'Graph length %i' %len(rdfgraph)) + self.failUnless((URIRef(base+"testdir2.zip"),URIRef(dcterms+"hasVersion"),subj2) in rdfgraph, 'dcterms:hasVersion') + self.failUnless((subj,URIRef(oxds+"currentVersion"),"4") in rdfgraph, 'oxds:currentVersion') + # Access new dataset, check response + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission-testdir2", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + subj = URIRef(self.getManifestUri("datasets/TestSubmission-testdir2")) + stype = URIRef("http://vocab.ox.ac.uk/dataset/schema#Grouping") + base = self.getManifestUri("datasets/TestSubmission-testdir2/") + owl = "http://www.w3.org/2002/07/owl#" + self.assertEqual(len(rdfgraph),22,'Graph length %i' %len(rdfgraph)) + self.failUnless((subj,RDF.type,stype) in rdfgraph, 'Testing submission type: '+subj+", "+stype) + self.failUnless((subj,URIRef(dcterms+"identifier"),None) in rdfgraph, 'dcterms:identifier') + self.failUnless((subj,URIRef(dcterms+"mediator"),None) in rdfgraph, 'dcterms:mediator') + self.failUnless((subj,URIRef(dcterms+"rights"),None) in rdfgraph, 'dcterms:rights') + self.failUnless((subj,URIRef(dcterms+"license"),None) in rdfgraph, 'dcterms:license') + self.failUnless((subj,URIRef(dcterms+"publisher"),None) in rdfgraph, 'dcterms:publisher') + self.failUnless((subj,URIRef(oxds+"isEmbargoed"),'True') in rdfgraph, 'oxds:isEmbargoed') + self.failUnless((subj,URIRef(oxds+"embargoedUntil"),None) in rdfgraph, 'oxds:isEmbargoed') + self.failUnless((subj,URIRef(dcterms+"created"),None) in rdfgraph, 'dcterms:created') + self.failUnless((subj,URIRef(dcterms+"modified"),None) in rdfgraph, 'dcterms:modified') + self.failUnless((subj,URIRef(dcterms+"isVersionOf"),None) in rdfgraph, 'dcterms:isVersionOf') + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"directory1")) in rdfgraph, 'ore:aggregates directory1') + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"directory1/file1.a")) in rdfgraph, 'ore:aggregates file1.a') + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"directory1/file1.b")) in rdfgraph, 'ore:aggregates file1.b') + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"directory1/file1.c")) in rdfgraph, 'ore:aggregates file1.c') + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"directory2")) in rdfgraph, 'ore:aggregates directory2') + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"directory2/file2.a")) in rdfgraph, 'ore:aggregates file2.a') + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"directory2/file2.b")) in rdfgraph, 'ore:aggregates file2.b') + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"test-csv.csv")) in rdfgraph, 'ore:aggregates test-csv.csv') + self.failUnless((subj,URIRef(oxds+"currentVersion"),"1") in rdfgraph, 'oxds:currentVersion') + self.failUnless((subj,URIRef(dcterms+"title"),"Test dataset with merged metadata") in rdfgraph, 'dcterms:title') + self.failUnless((subj,URIRef(owl+"sameAs"),URIRef("http://example.org/testdir2/")) in rdfgraph, 'owl:sameAs') + # Access and check content of a resource + (resp, filedata) = self.doHTTP_GET( + resource="datasets/TestSubmission-testdir2/directory1/file1.b", + expect_status=403, expect_reason="Forbidden", expect_type="text/plain") + + # Unpack ZIP file into a new dataset by manager, check response + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointmanageruser, + endpointpass=RDFDatabankConfig.endpointmanagerpass) + fields = \ + [ ("filename", "testrdf.zip") + ] + files = [] + (reqtype, reqdata) = SparqlQueryTestCase.encode_multipart_formdata(fields, files) + (resp,respdata) = self.doHTTP_POST( + reqdata, reqtype, + resource="items/TestSubmission", + expect_status=201, expect_reason="Created") + LHobtained = resp.getheader('Content-Location', None) + LHexpected = "%sdatasets/TestSubmission-testrdf"%self._endpointpath + self.assertEquals(LHobtained, LHexpected, 'Content-Location not correct') + # Access and check list of contents in TestSubmission + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + stype = URIRef(oxds+"DataSet") + base = self.getManifestUri("datasets/TestSubmission/") + subj = URIRef(self.getManifestUri("datasets/TestSubmission")) + subj2 = URIRef(self.getManifestUri("datasets/TestSubmission-testrdf")) + self.assertEqual(len(rdfgraph),18,'Graph length %i' %len(rdfgraph)) + self.failUnless((URIRef(base+"testrdf.zip"),URIRef(dcterms+"hasVersion"),subj2) in rdfgraph, 'dcterms:hasVersion') + self.failUnless((subj,URIRef(oxds+"currentVersion"),"4") in rdfgraph, 'oxds:currentVersion') + # Access new dataset, check response + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission-testrdf", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + subj = URIRef(self.getManifestUri("datasets/TestSubmission-testrdf")) + stype = URIRef("http://vocab.ox.ac.uk/dataset/schema#Grouping") + base = self.getManifestUri("datasets/TestSubmission-testrdf/") + self.assertEqual(len(rdfgraph),21,'Graph length %i' %len(rdfgraph)) + self.failUnless((subj,RDF.type,stype) in rdfgraph, 'Testing submission type: '+subj+", "+stype) + self.failUnless((subj,URIRef(dcterms+"identifier"),None) in rdfgraph, 'dcterms:identifier') + self.failUnless((subj,URIRef(dcterms+"mediator"),None) in rdfgraph, 'dcterms:mediator') + self.failUnless((subj,URIRef(dcterms+"rights"),None) in rdfgraph, 'dcterms:rights') + self.failUnless((subj,URIRef(dcterms+"license"),None) in rdfgraph, 'dcterms:license') + self.failUnless((subj,URIRef(dcterms+"publisher"),None) in rdfgraph, 'dcterms:publisher') + self.failUnless((subj,URIRef(oxds+"isEmbargoed"),'True') in rdfgraph, 'oxds:isEmbargoed') + self.failUnless((subj,URIRef(oxds+"embargoedUntil"),None) in rdfgraph, 'oxds:isEmbargoed') + self.failUnless((subj,URIRef(dcterms+"created"),None) in rdfgraph, 'dcterms:created') + self.failUnless((subj,URIRef(dcterms+"modified"),None) in rdfgraph, 'dcterms:modified') + self.failUnless((subj,URIRef(dcterms+"isVersionOf"),None) in rdfgraph, 'dcterms:isVersionOf') + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"directory")) in rdfgraph, 'ore:aggregates directory') + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"arabic.txt")) in rdfgraph, 'ore:aggregates arabic.txt') + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"directory/file1.a")) in rdfgraph, 'ore:aggregates file1.a') + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"directory/file1.b")) in rdfgraph, 'ore:aggregates file1.b') + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"directory/file2.a")) in rdfgraph, 'ore:aggregates file2.a') + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"test-csv.csv")) in rdfgraph, 'ore:aggregates test-csv.csv') + self.failUnless((subj,URIRef(oxds+"currentVersion"),"1") in rdfgraph, 'oxds:currentVersion') + self.failUnless((subj,URIRef(dcterms+"title"),"Test dataset with merged metadata") in rdfgraph, 'dcterms:title') + self.failUnless((subj,URIRef(owl+"sameAs"),URIRef("http://example.org/testrdf/")) in rdfgraph, 'owl:sameAs') + (resp, arabic_data) = self.doHTTP_GET( + resource="datasets/TestSubmission-testrdf/arabic.txt", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + #self.failUnless((subj,URIRef(dcterms+"description"),arabic_data) in rdfgraph, 'dcterms:description') + self.failUnless((subj,URIRef(dcterms+"description"),None) in rdfgraph, 'dcterms:description') + # Access and check content of a resource + (resp, filedata) = self.doHTTP_GET( + resource="datasets/TestSubmission-testrdf/directory/file1.b", + expect_status=200, expect_reason="OK", expect_type="text/plain") + checkdata = open("testdata/testrdf/directory/file1.b").read() + self.assertEqual(filedata, checkdata, "Difference between local and remote data!") + # Access and check list of contents in TestSubmission as submitter + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointsubmitteruser, + endpointpass=RDFDatabankConfig.endpointsubmitterpass) + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + stype = URIRef(oxds+"DataSet") + base = self.getManifestUri("datasets/TestSubmission/") + subj = URIRef(self.getManifestUri("datasets/TestSubmission")) + subj2 = URIRef(self.getManifestUri("datasets/TestSubmission-testrdf")) + self.assertEqual(len(rdfgraph),18,'Graph length %i' %len(rdfgraph)) + self.failUnless((URIRef(base+"testrdf.zip"),URIRef(dcterms+"hasVersion"),subj2) in rdfgraph, 'dcterms:hasVersion') + self.failUnless((subj,URIRef(oxds+"currentVersion"),"4") in rdfgraph, 'oxds:currentVersion') + # Access new dataset, check response + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission-testrdf", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + subj = URIRef(self.getManifestUri("datasets/TestSubmission-testrdf")) + stype = URIRef("http://vocab.ox.ac.uk/dataset/schema#Grouping") + base = self.getManifestUri("datasets/TestSubmission-testrdf/") + self.assertEqual(len(rdfgraph),21,'Graph length %i' %len(rdfgraph)) + self.failUnless((subj,RDF.type,stype) in rdfgraph, 'Testing submission type: '+subj+", "+stype) + self.failUnless((subj,URIRef(dcterms+"identifier"),None) in rdfgraph, 'dcterms:identifier') + self.failUnless((subj,URIRef(dcterms+"mediator"),None) in rdfgraph, 'dcterms:mediator') + self.failUnless((subj,URIRef(dcterms+"rights"),None) in rdfgraph, 'dcterms:rights') + self.failUnless((subj,URIRef(dcterms+"license"),None) in rdfgraph, 'dcterms:license') + self.failUnless((subj,URIRef(dcterms+"publisher"),None) in rdfgraph, 'dcterms:publisher') + self.failUnless((subj,URIRef(oxds+"isEmbargoed"),'True') in rdfgraph, 'oxds:isEmbargoed') + self.failUnless((subj,URIRef(oxds+"embargoedUntil"),None) in rdfgraph, 'oxds:isEmbargoed') + self.failUnless((subj,URIRef(dcterms+"created"),None) in rdfgraph, 'dcterms:created') + self.failUnless((subj,URIRef(dcterms+"modified"),None) in rdfgraph, 'dcterms:modified') + self.failUnless((subj,URIRef(dcterms+"isVersionOf"),None) in rdfgraph, 'dcterms:isVersionOf') + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"directory")) in rdfgraph, 'ore:aggregates directory') + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"arabic.txt")) in rdfgraph, 'ore:aggregates arabic.txt') + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"directory/file1.a")) in rdfgraph, 'ore:aggregates file1.a') + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"directory/file1.b")) in rdfgraph, 'ore:aggregates file1.b') + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"directory/file2.a")) in rdfgraph, 'ore:aggregates file2.a') + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"test-csv.csv")) in rdfgraph, 'ore:aggregates test-csv.csv') + self.failUnless((subj,URIRef(oxds+"currentVersion"),"1") in rdfgraph, 'oxds:currentVersion') + self.failUnless((subj,URIRef(dcterms+"title"),"Test dataset with merged metadata") in rdfgraph, 'dcterms:title') + self.failUnless((subj,URIRef(owl+"sameAs"),URIRef("http://example.org/testrdf/")) in rdfgraph, 'owl:sameAs') + # Access and check content of a resource + (resp, filedata) = self.doHTTP_GET( + resource="datasets/TestSubmission-testrdf/directory/file1.b", + expect_status=403, expect_reason="Forbidden", expect_type="text/plain") + + # Unpack ZIP file into a new dataset by submiter 2, check response + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointsubmitteruser2, + endpointpass=RDFDatabankConfig.endpointsubmitterpass2) + fields = \ + [ ("filename", "testrdf2.zip") + ] + files = [] + (reqtype, reqdata) = SparqlQueryTestCase.encode_multipart_formdata(fields, files) + (resp,respdata) = self.doHTTP_POST( + reqdata, reqtype, + resource="items/TestSubmission", + expect_status=403, expect_reason="Forbidden") + # Access and check list of contents in TestSubmission + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointsubmitteruser, + endpointpass=RDFDatabankConfig.endpointsubmitterpass) + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + subj = URIRef(self.getManifestUri("datasets/TestSubmission")) + stype = URIRef(oxds+"DataSet") + base = self.getManifestUri("datasets/TestSubmission/") + self.assertEqual(len(rdfgraph),18,'Graph length %i' %len(rdfgraph)) + self.assertFalse((URIRef(base+"testrdf2.zip"),URIRef(dcterms+"hasVersion"),None) in rdfgraph, 'dcterms:hasVersion testrdf2.zip') + self.failUnless((subj,URIRef(oxds+"currentVersion"),"4") in rdfgraph, 'oxds:currentVersion') + # Access new dataset, check response + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission-testrdf2", + expect_status=404, expect_reason="Not Found", expect_type="application/rdf+xml") + + # Unpack ZIP file into a new dataset by general user, check response + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointgeneraluser, + endpointpass=RDFDatabankConfig.endpointgeneralpass) + fields = \ + [ ("filename", "testrdf2.zip") + ] + files = [] + (reqtype, reqdata) = SparqlQueryTestCase.encode_multipart_formdata(fields, files) + (resp,respdata) = self.doHTTP_POST( + reqdata, reqtype, + resource="items/TestSubmission", + expect_status=401, expect_reason="Unauthorized", expect_type="text/plain") + # Access and check list of contents in TestSubmission + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointsubmitteruser, + endpointpass=RDFDatabankConfig.endpointsubmitterpass) + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + base = self.getManifestUri("datasets/TestSubmission/") + subj = URIRef(self.getManifestUri("datasets/TestSubmission")) + stype = URIRef(oxds+"DataSet") + self.assertEqual(len(rdfgraph),18,'Graph length %i' %len(rdfgraph)) + self.assertFalse((URIRef(base+"testrdf2.zip"),URIRef(dcterms+"hasVersion"),None) in rdfgraph, 'dcterms:hasVersion testrdf2.zip') + self.failUnless((subj,URIRef(oxds+"currentVersion"),"4") in rdfgraph, 'oxds:currentVersion') + # Access new dataset, check response + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission-testrdf2", + expect_status=404, expect_reason="Not Found", expect_type="application/rdf+xml") + + # Unpack ZIP file into a new dataset by admin user3, check response + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointadminuser3, + endpointpass=RDFDatabankConfig.endpointadminpass3) + fields = \ + [ ("filename", "testrdf2.zip") + ] + files = [] + (reqtype, reqdata) = SparqlQueryTestCase.encode_multipart_formdata(fields, files) + (resp,respdata) = self.doHTTP_POST( + reqdata, reqtype, + resource="items/TestSubmission", + expect_status=403, expect_reason="Forbidden") + # Access and check list of contents in TestSubmission + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointsubmitteruser, + endpointpass=RDFDatabankConfig.endpointsubmitterpass) + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + base = self.getManifestUri("datasets/TestSubmission/") + subj = URIRef(self.getManifestUri("datasets/TestSubmission")) + stype = URIRef(oxds+"DataSet") + self.assertEqual(len(rdfgraph),18,'Graph length %i' %len(rdfgraph)) + self.assertFalse((URIRef(base+"testrdf2.zip"),URIRef(dcterms+"hasVersion"),None) in rdfgraph, 'dcterms:hasVersion testrdf2.zip') + self.failUnless((subj,URIRef(oxds+"currentVersion"),"4") in rdfgraph, 'oxds:currentVersion') + # Access new dataset, check response + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission-testrdf2", + expect_status=404, expect_reason="Not Found", expect_type="application/rdf+xml") + + # Unpack ZIP file into a new dataset by manager user3, check response + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointmanageruser3, + endpointpass=RDFDatabankConfig.endpointmanagerpass3) + fields = \ + [ ("filename", "testrdf2.zip") + ] + files = [] + (reqtype, reqdata) = SparqlQueryTestCase.encode_multipart_formdata(fields, files) + (resp,respdata) = self.doHTTP_POST( + reqdata, reqtype, + resource="items/TestSubmission", + expect_status=403, expect_reason="Forbidden") + # Access and check list of contents in TestSubmission + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointsubmitteruser, + endpointpass=RDFDatabankConfig.endpointsubmitterpass) + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + stype = URIRef(oxds+"DataSet") + base = self.getManifestUri("datasets/TestSubmission/") + subj = URIRef(self.getManifestUri("datasets/TestSubmission")) + self.assertEqual(len(rdfgraph),18,'Graph length %i' %len(rdfgraph)) + self.assertFalse((URIRef(base+"testrdf2.zip"),URIRef(dcterms+"hasVersion"),None) in rdfgraph, 'dcterms:hasVersion testrdf2.zip') + self.failUnless((subj,URIRef(oxds+"currentVersion"),"4") in rdfgraph, 'oxds:currentVersion') + # Access new dataset, check response + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission-testrdf2", + expect_status=404, expect_reason="Not Found", expect_type="application/rdf+xml") + + # Unpack ZIP file into a new dataset by submitter user3, check response + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointsubmitteruser3, + endpointpass=RDFDatabankConfig.endpointsubmitterpass3) + fields = \ + [ ("filename", "testrdf2.zip") + ] + files = [] + (reqtype, reqdata) = SparqlQueryTestCase.encode_multipart_formdata(fields, files) + (resp,respdata) = self.doHTTP_POST( + reqdata, reqtype, + resource="items/TestSubmission", + expect_status=403, expect_reason="Forbidden") + # Access and check list of contents in TestSubmission + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointsubmitteruser, + endpointpass=RDFDatabankConfig.endpointsubmitterpass) + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + base = self.getManifestUri("datasets/TestSubmission/") + subj = URIRef(self.getManifestUri("datasets/TestSubmission")) + stype = URIRef(oxds+"DataSet") + self.assertEqual(len(rdfgraph),18,'Graph length %i' %len(rdfgraph)) + self.assertFalse((URIRef(base+"testrdf2.zip"),URIRef(dcterms+"hasVersion"),None) in rdfgraph, 'dcterms:hasVersion testrdf2.zip') + self.failUnless((subj,URIRef(oxds+"currentVersion"),"4") in rdfgraph, 'oxds:currentVersion') + # Access new dataset, check response + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission-testrdf2", + expect_status=404, expect_reason="Not Found", expect_type="application/rdf+xml") + + # Delete the dataset TestSubmission-testdir + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointadminuser, + endpointpass=RDFDatabankConfig.endpointadminpass) + resp = self.doHTTP_DELETE( + resource="datasets/TestSubmission-testdir", + expect_status="*", expect_reason="*") + resp = self.doHTTP_DELETE( + resource="datasets/TestSubmission-testdir2", + expect_status="*", expect_reason="*") + resp = self.doHTTP_DELETE( + resource="datasets/TestSubmission-testrdf", + expect_status="*", expect_reason="*") + + + + def testSymlinkFileUnpack(self): + """Unpack zip file uploaded in a previous version to a new dataset - POST zip filename to /silo_name/items/dataset_name""" + #NOTE: I do not need this test as the test above does test unpacking of a file uploaded in a previous version. + # In any case, certainly do not need to expand this test to cover the different users + # Create a new dataset, check response + self.createSubmissionDataset() + # Upload zip file testdir.zip, check response + zipdata = self.uploadSubmissionZipfile() + # Upload zip file testdir2, check response + zipdata = self.uploadSubmissionZipfile(file_to_upload="testdir2.zip") + # Unpack ZIP file into a new dataset, check response + fields = \ + [ ("filename", "testdir.zip") + ] + files = [] + (reqtype, reqdata) = SparqlQueryTestCase.encode_multipart_formdata(fields, files) + (resp,respdata) = self.doHTTP_POST( + reqdata, reqtype, + resource="items/TestSubmission", + expect_status=201, expect_reason="Created") + LHobtained = resp.getheader('Content-Location', None) + LHexpected = "%sdatasets/TestSubmission-testdir"%self._endpointpath + self.assertEquals(LHobtained, LHexpected, 'Content-Location not correct') + # Access parent dataset, check response + (resp, respdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/json") + # Access and check list of contents in TestSubmission + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + subj = URIRef(self.getManifestUri("datasets/TestSubmission")) + base = self.getManifestUri("datasets/TestSubmission/") + dcterms = "http://purl.org/dc/terms/" + ore = "http://www.openarchives.org/ore/terms/" + oxds = "http://vocab.ox.ac.uk/dataset/schema#" + stype = URIRef(oxds+"DataSet") + self.assertEqual(len(rdfgraph),14,'Graph length %i' %len(rdfgraph)) + self.failUnless((subj,RDF.type,stype) in rdfgraph, 'Testing submission type: '+subj+", "+stype) + self.failUnless((subj,URIRef(dcterms+"created"),None) in rdfgraph, 'dcterms:created') + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"testdir.zip")) in rdfgraph, 'ore:aggregates testdir.zip') + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"testdir2.zip")) in rdfgraph, 'ore:aggregates testdir2.zip') + self.failUnless((URIRef(base+"testdir.zip"),URIRef(dcterms+"hasVersion"),None) in rdfgraph, 'dcterms:hasVersion') + self.failUnless((subj,URIRef(dcterms+"identifier"),None) in rdfgraph, 'dcterms:identifier') + self.failUnless((subj,URIRef(dcterms+"mediator"),None) in rdfgraph, 'dcterms:mediator') + self.failUnless((subj,URIRef(dcterms+"rights"),None) in rdfgraph, 'dcterms:rights') + self.failUnless((subj,URIRef(dcterms+"license"),None) in rdfgraph, 'dcterms:license') + self.failUnless((subj,URIRef(dcterms+"publisher"),None) in rdfgraph, 'dcterms:publisher') + self.failUnless((subj,URIRef(oxds+"isEmbargoed"),None) in rdfgraph, 'oxds:isEmbargoed') + self.failUnless((subj,URIRef(oxds+"embargoedUntil"),None) in rdfgraph, 'oxds:embargoedUntil') + self.failUnless((subj,URIRef(oxds+"currentVersion"),"2") in rdfgraph, 'oxds:currentVersion') + self.failUnless((subj,URIRef(dcterms+"modified"),None) in rdfgraph, 'dcterms:modified') + # Access new dataset, check response + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission-testdir", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + subj = URIRef(self.getManifestUri("datasets/TestSubmission-testdir")) + stype = URIRef("http://vocab.ox.ac.uk/dataset/schema#Grouping") + base = self.getManifestUri("datasets/TestSubmission-testdir/") + self.assertEqual(len(rdfgraph),17,'Graph length %i' %len(rdfgraph)) + self.failUnless((subj,RDF.type,stype) in rdfgraph, 'Testing submission type: '+subj+", "+stype) + self.failUnless((subj,URIRef(dcterms+"identifier"),None) in rdfgraph, 'dcterms:identifier') + self.failUnless((subj,URIRef(dcterms+"mediator"),None) in rdfgraph, 'dcterms:mediator') + self.failUnless((subj,URIRef(dcterms+"rights"),None) in rdfgraph, 'dcterms:rights') + self.failUnless((subj,URIRef(dcterms+"license"),None) in rdfgraph, 'dcterms:license') + self.failUnless((subj,URIRef(dcterms+"publisher"),None) in rdfgraph, 'dcterms:publisher') + self.failUnless((subj,URIRef(oxds+"isEmbargoed"),'True') in rdfgraph, 'oxds:isEmbargoed') + self.failUnless((subj,URIRef(oxds+"embargoedUntil"),None) in rdfgraph, 'oxds:isEmbargoed') + self.failUnless((subj,URIRef(dcterms+"created"),None) in rdfgraph, 'dcterms:created') + self.failUnless((subj,URIRef(dcterms+"modified"),None) in rdfgraph, 'dcterms:modified') + self.failUnless((subj,URIRef(dcterms+"isVersionOf"),None) in rdfgraph, 'dcterms:isVersionOf') + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"directory")) in rdfgraph, 'ore:aggregates directory') + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"directory/file1.a")) in rdfgraph, 'ore:aggregates file1.a') + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"directory/file1.b")) in rdfgraph, 'ore:aggregates file1.b') + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"directory/file2.a")) in rdfgraph, 'ore:aggregates file2.a') + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"test-csv.csv")) in rdfgraph, 'ore:aggregates test-csv.csv') + self.failUnless((subj,URIRef(oxds+"currentVersion"),"1") in rdfgraph, 'oxds:currentVersion') + # Access and check content of a resource + (resp, filedata) = self.doHTTP_GET( + resource="datasets/TestSubmission-testdir/directory/file1.b", + expect_status=200, expect_reason="OK", expect_type="text/plain") + checkdata = open("testdata/testdir/directory/file1.b").read() + self.assertEqual(filedata, checkdata, "Difference between local and remote data!") + #Access state information of TestSubmission-testdir + (resp, data) = self.doHTTP_GET( + resource="states/TestSubmission-testdir", + expect_status=200, expect_reason="OK", expect_type="application/json") + state = data['state'] + parts = data['parts'] + self.assertEqual(len(state.keys()), 11, "States") + self.assertEqual(state['item_id'], "TestSubmission-testdir", "Submission item identifier") + self.assertEqual(len(state['versions']), 2, "Two versions") + self.assertEqual(state['versions'][0], '0', "Version 0") + self.assertEqual(state['versions'][1], '1', "Version 1") + self.assertEqual(state['currentversion'], '1', "Current version == 1") + self.assertEqual(state['rdffileformat'], 'xml', "RDF file type") + self.assertEqual(state['rdffilename'], 'manifest.rdf', "RDF file name") + self.assertEqual(len(state['files']['0']), 1, "List should contain just manifest.rdf") + self.assertEqual(len(state['files']['1']), 3, "List should contain manifest.rdf, test-csv.csv and testdir") + self.assertEqual(len(state['metadata_files']['0']), 0, "metadata_files of version 0") + self.assertEqual(len(state['metadata_files']['1']), 0, "metadata_files of version 1") + self.assertEqual(state['subdir']['0'], [], "Subdirectory for version 0") + self.assertEqual(state['subdir']['1'], ['directory'], "Subdirectory for version 1") + self.assertEqual(state['metadata']['createdby'], RDFDatabankConfig.endpointsubmitteruser, "Created by") + self.assertEqual(state['metadata']['embargoed'], True, "Embargoed?") + d = (datetime.datetime.now() + relativedelta(years=+70)).isoformat() + d = d.split('T')[0] + self.assertTrue(d in state['metadata']['embargoed_until'], "embargoed_until %s?"%d) + self.assertEqual(len(parts.keys()), 5, "Parts") + self.assertEqual(len(parts['4=TestSubmission-testdir'].keys()), 13, "File stats for 4=TestSubmission-testdir") + self.assertEqual(len(parts['manifest.rdf'].keys()), 13, "File stats for manifest.rdf") + self.assertEqual(len(parts['directory'].keys()), 0, "File stats for directory") + self.assertEqual(len(parts['test-csv.csv'].keys()), 13, "File stats for test-csv.csv") + # Delete the dataset TestSubmission-testdir + resp = self.doHTTP_DELETE( + resource="datasets/TestSubmission-testdir", + expect_status="*", expect_reason="*") + + + + def testFileUploadToUnpackedDataset(self): + """Upload a file to an unpacked dataset - POST filename to /silo_name/datasets/dataset_name""" + # Create a new dataset, check response + self.createSubmissionDataset() + # Upload zip file, check response + zipdata = self.uploadSubmissionZipfile() + # Unpack ZIP file into a new dataset, check response + fields = \ + [ ("filename", "testdir.zip") + ] + files = [] + (reqtype, reqdata) = SparqlQueryTestCase.encode_multipart_formdata(fields, files) + (resp,respdata) = self.doHTTP_POST( + reqdata, reqtype, + resource="items/TestSubmission", + expect_status=201, expect_reason="Created") + LHobtained = resp.getheader('Content-Location', None) + LHexpected = "%sdatasets/TestSubmission-testdir"%self._endpointpath + self.assertEquals(LHobtained, LHexpected, 'Content-Location not correct') + # Access and check list of contents in TestSubmission + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + self.assertEqual(len(rdfgraph),13,'Graph length %i' %len(rdfgraph)) + # Access new dataset TestSubmission-testdir, check response + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission-testdir", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + subj = URIRef(self.getManifestUri("datasets/TestSubmission-testdir")) + stype = URIRef("http://vocab.ox.ac.uk/dataset/schema#Grouping") + base = self.getManifestUri("datasets/TestSubmission-testdir/") + dcterms = "http://purl.org/dc/terms/" + ore = "http://www.openarchives.org/ore/terms/" + oxds = "http://vocab.ox.ac.uk/dataset/schema#" + self.assertEqual(len(rdfgraph),17,'Graph length %i' %len(rdfgraph)) + self.failUnless((subj,RDF.type,stype) in rdfgraph, 'Testing submission type: '+subj+", "+stype) + self.failUnless((subj,URIRef(dcterms+"identifier"),None) in rdfgraph, 'dcterms:identifier') + self.failUnless((subj,URIRef(dcterms+"mediator"),None) in rdfgraph, 'dcterms:mediator') + self.failUnless((subj,URIRef(dcterms+"rights"),None) in rdfgraph, 'dcterms:rights') + self.failUnless((subj,URIRef(dcterms+"license"),None) in rdfgraph, 'dcterms:license') + self.failUnless((subj,URIRef(dcterms+"publisher"),None) in rdfgraph, 'dcterms:publisher') + self.failUnless((subj,URIRef(oxds+"isEmbargoed"),'True') in rdfgraph, 'oxds:isEmbargoed') + self.failUnless((subj,URIRef(oxds+"embargoedUntil"),None) in rdfgraph, 'oxds:isEmbargoed') + self.failUnless((subj,URIRef(dcterms+"created"),None) in rdfgraph, 'dcterms:created') + self.failUnless((subj,URIRef(dcterms+"modified"),None) in rdfgraph, 'dcterms:modified') + self.failUnless((subj,URIRef(dcterms+"isVersionOf"),None) in rdfgraph, 'dcterms:isVersionOf') + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"directory")) in rdfgraph, 'ore:aggregates directory') + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"directory/file1.a")) in rdfgraph, 'ore:aggregates file1.a') + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"directory/file1.b")) in rdfgraph, 'ore:aggregates file1.b') + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"directory/file2.a")) in rdfgraph, 'ore:aggregates file2.a') + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"test-csv.csv")) in rdfgraph, 'ore:aggregates test-csv.csv') + self.failUnless((subj,URIRef(oxds+"currentVersion"),"1") in rdfgraph, 'oxds:currentVersion') + # Upload zip file to dataset TestSubmission-testdir + fields = \ + [ ("filename", "testdir2.zip") + ] + zipdata0 = open("testdata/testdir2.zip").read() + files = \ + [ ("file", "testdir2.zip", zipdata0, "application/zip") + ] + (reqtype, reqdata) = SparqlQueryTestCase.encode_multipart_formdata(fields, files) + (resp,respdata) = self.doHTTP_POST( + reqdata, reqtype, + resource="datasets/TestSubmission-testdir/", + expect_status=201, expect_reason="Created") + LHobtained = resp.getheader('Content-Location', None) + LHexpected = "%sdatasets/TestSubmission-testdir/testdir2.zip"%self._endpointpath + self.assertEquals(LHobtained, LHexpected, 'Content-Location not correct') + # Access dataset TestSubmission-testdir, check response + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission-testdir", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + subj = URIRef(self.getManifestUri("datasets/TestSubmission-testdir")) + stype = URIRef("http://vocab.ox.ac.uk/dataset/schema#Grouping") + base = self.getManifestUri("datasets/TestSubmission-testdir/") + self.assertEqual(len(rdfgraph),18,'Graph length %i' %len(rdfgraph)) + self.failUnless((subj,RDF.type,stype) in rdfgraph, 'Testing submission type: '+subj+", "+stype) + self.failUnless((subj,URIRef(dcterms+"identifier"),None) in rdfgraph, 'dcterms:identifier') + self.failUnless((subj,URIRef(dcterms+"mediator"),None) in rdfgraph, 'dcterms:mediator') + self.failUnless((subj,URIRef(dcterms+"rights"),None) in rdfgraph, 'dcterms:rights') + self.failUnless((subj,URIRef(dcterms+"license"),None) in rdfgraph, 'dcterms:license') + self.failUnless((subj,URIRef(dcterms+"publisher"),None) in rdfgraph, 'dcterms:publisher') + self.failUnless((subj,URIRef(oxds+"isEmbargoed"),'True') in rdfgraph, 'oxds:isEmbargoed') + self.failUnless((subj,URIRef(oxds+"embargoedUntil"),None) in rdfgraph, 'oxds:isEmbargoed') + self.failUnless((subj,URIRef(dcterms+"created"),None) in rdfgraph, 'dcterms:created') + self.failUnless((subj,URIRef(dcterms+"modified"),None) in rdfgraph, 'dcterms:modified') + self.failUnless((subj,URIRef(dcterms+"isVersionOf"),None) in rdfgraph, 'dcterms:isVersionOf') + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"directory")) in rdfgraph, 'ore:aggregates directory') + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"directory/file1.a")) in rdfgraph, 'ore:aggregates file1.a') + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"directory/file1.b")) in rdfgraph, 'ore:aggregates file1.b') + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"directory/file2.a")) in rdfgraph, 'ore:aggregates file2.a') + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"test-csv.csv")) in rdfgraph, 'ore:aggregates test-csv.csv') + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"testdir2.zip")) in rdfgraph, 'ore:aggregates testdir2.zip') + self.failUnless((subj,URIRef(oxds+"currentVersion"),"2") in rdfgraph, 'oxds:currentVersion') + #Access state information of TestSubmission-testdir + (resp, data) = self.doHTTP_GET( + resource="states/TestSubmission-testdir", + expect_status=200, expect_reason="OK", expect_type="application/json") + state = data['state'] + parts = data['parts'] + self.assertEqual(len(state.keys()), 11, "States") + self.assertEqual(state['item_id'], "TestSubmission-testdir", "Submission item identifier") + self.assertEqual(len(state['versions']), 3, "Three versions") + self.assertEqual(state['versions'][0], '0', "Version 0") + self.assertEqual(state['versions'][1], '1', "Version 1") + self.assertEqual(state['versions'][2], '2', "Version 2") + self.assertEqual(state['currentversion'], '2', "Current version == 2") + self.assertEqual(state['rdffileformat'], 'xml', "RDF file type") + self.assertEqual(state['rdffilename'], 'manifest.rdf', "RDF file name") + self.assertEqual(len(state['files']['0']), 1, "List should contain just manifest.rdf") + self.assertEqual(len(state['files']['1']), 3, "List should contain manifest.rdf, test-csv.csv and directory") + self.assertEqual(len(state['files']['2']), 4, "List should contain manifest.rdf, test-csv.csv, directory and testdir2.zip") + self.assertEqual(len(state['metadata_files']['0']), 0, "metadata_files of version 0") + self.assertEqual(len(state['metadata_files']['1']), 0, "metadata_files of version 1") + self.assertEqual(len(state['metadata_files']['2']), 0, "metadata_files of version 2") + self.assertEqual(state['subdir']['0'], [], "Subdirectory for version 0") + self.assertEqual(state['subdir']['1'], ['directory'], "Subdirectory for version 1") + self.assertEqual(state['subdir']['2'], ['directory'], "Subdirectory for version 2") + self.assertEqual(state['metadata']['createdby'], RDFDatabankConfig.endpointsubmitteruser, "Created by") + self.assertEqual(state['metadata']['embargoed'], True, "Embargoed?") + d = (datetime.datetime.now() + relativedelta(years=+70)).isoformat() + d = d.split('T')[0] + self.assertTrue(d in state['metadata']['embargoed_until'], "embargoed_until %s?"%d) + self.assertEqual(len(parts.keys()), 6, "Parts") + self.assertEqual(len(parts['4=TestSubmission-testdir'].keys()), 13, "File stats for 4=TestSubmission-testdir") + self.assertEqual(len(parts['manifest.rdf'].keys()), 13, "File stats for manifest.rdf") + self.assertEqual(len(parts['directory'].keys()), 0, "File stats for directory") + self.assertEqual(len(parts['test-csv.csv'].keys()), 13, "File stats for test-csv.csv") + self.assertEqual(len(parts['testdir2.zip'].keys()), 13, "File stats for testdir2.zip") + + # Upload zip file by admin, check response + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointadminuser, + endpointpass=RDFDatabankConfig.endpointadminpass) + fields = [] + zipdata = open("testdata/testrdf.zip").read() + files = \ + [ ("file", 'testrdf.zip', zipdata, "application/zip") + ] + (reqtype, reqdata) = SparqlQueryTestCase.encode_multipart_formdata(fields, files) + (resp,respdata) = self.doHTTP_POST( + reqdata, reqtype, + resource="datasets/TestSubmission-testdir/", + expect_status=201, expect_reason="Created") + LHobtained = resp.getheader('Content-Location', None) + LHexpected = "%sdatasets/TestSubmission-testdir/testrdf.zip"%self._endpointpath + self.assertEquals(LHobtained, LHexpected, 'Content-Location not correct') + # Access and check zip file content + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointsubmitteruser, + endpointpass=RDFDatabankConfig.endpointsubmitterpass) + (resp, zipfile) = self.doHTTP_GET( + resource="datasets/TestSubmission-testdir/testrdf.zip", + expect_status=200, expect_reason="OK", expect_type="application/zip") + self.assertEqual(zipdata, zipfile, "Difference between local and remote zipfile!") + + # Upload zip file by manager, check response + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointmanageruser, + endpointpass=RDFDatabankConfig.endpointmanagerpass) + fields = [] + zipdata = open("testdata/testrdf2.zip").read() + files = \ + [ ("file", 'testrdf2.zip', zipdata, "application/zip") + ] + (reqtype, reqdata) = SparqlQueryTestCase.encode_multipart_formdata(fields, files) + (resp,respdata) = self.doHTTP_POST( + reqdata, reqtype, + resource="datasets/TestSubmission-testdir/", + expect_status=201, expect_reason="Created") + LHobtained = resp.getheader('Content-Location', None) + LHexpected = "%sdatasets/TestSubmission-testdir/testrdf2.zip"%self._endpointpath + self.assertEquals(LHobtained, LHexpected, 'Content-Location not correct') + # Access and check zip file content + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointsubmitteruser, + endpointpass=RDFDatabankConfig.endpointsubmitterpass) + (resp, zipfile) = self.doHTTP_GET( + resource="datasets/TestSubmission-testdir/testrdf2.zip", + expect_status=200, expect_reason="OK", expect_type="application/zip") + self.assertEqual(zipdata, zipfile, "Difference between local and remote zipfile!") + + #Prepare data to upload + fields = [] + zipdata = open("testdata/testrdf3.zip").read() + files = \ + [ ("file", 'testrdf3.zip', zipdata, "application/zip") + ] + (reqtype, reqdata) = SparqlQueryTestCase.encode_multipart_formdata(fields, files) + + # Upload zip file by submitter2, check response + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointsubmitteruser2, + endpointpass=RDFDatabankConfig.endpointsubmitterpass2) + (resp,respdata) = self.doHTTP_POST( + reqdata, reqtype, + resource="datasets/TestSubmission-testdir/", + expect_status=403, expect_reason="Forbidden") + # Access and check zip file content + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointsubmitteruser, + endpointpass=RDFDatabankConfig.endpointsubmitterpass) + (resp, zipfile) = self.doHTTP_GET( + resource="datasets/TestSubmission-testdir/testrdf3.zip", + expect_status=404, expect_reason="Not Found", expect_type="application/zip") + + # Upload zip file by general user, check response + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointgeneraluser, + endpointpass=RDFDatabankConfig.endpointgeneralpass) + (resp,respdata) = self.doHTTP_POST( + reqdata, reqtype, + resource="datasets/TestSubmission-testdir/", + expect_status=401, expect_reason="Unauthorized", expect_type="text/plain") + # Access and check zip file content + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointsubmitteruser, + endpointpass=RDFDatabankConfig.endpointsubmitterpass) + (resp, zipfile) = self.doHTTP_GET( + resource="datasets/TestSubmission-testdir/testrdf3.zip", + expect_status=404, expect_reason="Not Found", expect_type="application/zip") + + # Upload zip file by admin user 3, check response + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointadminuser3, + endpointpass=RDFDatabankConfig.endpointadminpass3) + (resp,respdata) = self.doHTTP_POST( + reqdata, reqtype, + resource="datasets/TestSubmission-testdir/", + expect_status=403, expect_reason="Forbidden") + # Access and check zip file content + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointsubmitteruser, + endpointpass=RDFDatabankConfig.endpointsubmitterpass) + (resp, zipfile) = self.doHTTP_GET( + resource="datasets/TestSubmission-testdir/testrdf3.zip", + expect_status=404, expect_reason="Not Found", expect_type="application/zip") + + # Upload zip file by manager user 3, check response + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointmanageruser3, + endpointpass=RDFDatabankConfig.endpointmanagerpass3) + (resp,respdata) = self.doHTTP_POST( + reqdata, reqtype, + resource="datasets/TestSubmission-testdir/", + expect_status=403, expect_reason="Forbidden") + # Access and check zip file content + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointsubmitteruser, + endpointpass=RDFDatabankConfig.endpointsubmitterpass) + (resp, zipfile) = self.doHTTP_GET( + resource="datasets/TestSubmission-testdir/testrdf3.zip", + expect_status=404, expect_reason="Not Found", expect_type="application/zip") + + # Upload zip file by submitter user 3, check response + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointsubmitteruser3, + endpointpass=RDFDatabankConfig.endpointsubmitterpass3) + (resp,respdata) = self.doHTTP_POST( + reqdata, reqtype, + resource="datasets/TestSubmission-testdir/", + expect_status=403, expect_reason="Forbidden") + # Access and check zip file content + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointsubmitteruser, + endpointpass=RDFDatabankConfig.endpointsubmitterpass) + (resp, zipfile) = self.doHTTP_GET( + resource="datasets/TestSubmission-testdir/testrdf3.zip", + expect_status=404, expect_reason="Not Found", expect_type="application/zip") + + # Access and check zip file content of testdir2.zip + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointsubmitteruser, + endpointpass=RDFDatabankConfig.endpointsubmitterpass) + (resp, zipfile) = self.doHTTP_GET( + resource="datasets/TestSubmission-testdir/testdir2.zip", + expect_status=200, expect_reason="OK", expect_type="application/zip") + self.assertEqual(zipdata0, zipfile, "Difference between local and remote zipfile!") + + # Delete the dataset TestSubmission-testdir + resp = self.doHTTP_DELETE( + resource="datasets/TestSubmission-testdir", + expect_status="*", expect_reason="*") + # Delete the dataset TestSubmission + resp = self.doHTTP_DELETE( + resource="datasets/TestSubmission", + expect_status="*", expect_reason="*") + + + + def testUpdateUnpackedDataset(self): + """Update a dataset by unpacking a zip file to an existing dataset - POST zip filename to /silo_name/items/dataset_name""" + # Create a new dataset, check response + self.createSubmissionDataset() + # Upload zip files + zipdata1 = self.uploadSubmissionZipfile() + zipdata2 = self.uploadSubmissionZipfile(file_to_upload="testrdf4.zip") + zipdata3 = self.uploadSubmissionZipfile(file_to_upload="testdir2.zip") + zipdata4 = self.uploadSubmissionZipfile(file_to_upload="testrdf.zip") + zipdata5 = self.uploadSubmissionZipfile(file_to_upload="testrdf2.zip") + # Unpack ZIP file into a new dataset, check response + fields = \ + [ ("filename", "testdir.zip") + ] + files = [] + (reqtype, reqdata) = SparqlQueryTestCase.encode_multipart_formdata(fields, files) + (resp,respdata) = self.doHTTP_POST( + reqdata, reqtype, + resource="items/TestSubmission", + expect_status=201, expect_reason="Created") + LHobtained = resp.getheader('Content-Location', None) + LHexpected = "%sdatasets/TestSubmission-testdir"%self._endpointpath + self.assertEquals(LHobtained, LHexpected, 'Content-Location not correct') + # Access and check response for TestSubmission + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + self.assertEqual(len(rdfgraph),17,'Graph length %i' %len(rdfgraph)) + # Access and check list of contents in TestSubmission-testdir + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission-testdir", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + subj = URIRef(self.getManifestUri("datasets/TestSubmission-testdir")) + stype = URIRef("http://vocab.ox.ac.uk/dataset/schema#Grouping") + base = self.getManifestUri("datasets/TestSubmission-testdir/") + dcterms = "http://purl.org/dc/terms/" + ore = "http://www.openarchives.org/ore/terms/" + oxds = "http://vocab.ox.ac.uk/dataset/schema#" + self.assertEqual(len(rdfgraph),17,'Graph length %i' %len(rdfgraph)) + self.failUnless((subj,RDF.type,stype) in rdfgraph, 'Testing submission type: '+subj+", "+stype) + self.failUnless((subj,URIRef(dcterms+"identifier"),None) in rdfgraph, 'dcterms:identifier') + self.failUnless((subj,URIRef(dcterms+"mediator"),None) in rdfgraph, 'dcterms:mediator') + self.failUnless((subj,URIRef(dcterms+"rights"),None) in rdfgraph, 'dcterms:rights') + self.failUnless((subj,URIRef(dcterms+"license"),None) in rdfgraph, 'dcterms:license') + self.failUnless((subj,URIRef(dcterms+"publisher"),None) in rdfgraph, 'dcterms:publisher') + self.failUnless((subj,URIRef(oxds+"isEmbargoed"),'True') in rdfgraph, 'oxds:isEmbargoed') + self.failUnless((subj,URIRef(oxds+"embargoedUntil"),None) in rdfgraph, 'oxds:isEmbargoed') + self.failUnless((subj,URIRef(dcterms+"created"),None) in rdfgraph, 'dcterms:created') + self.failUnless((subj,URIRef(dcterms+"modified"),None) in rdfgraph, 'dcterms:modified') + self.failUnless((subj,URIRef(dcterms+"isVersionOf"),None) in rdfgraph, 'dcterms:isVersionOf') + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"directory")) in rdfgraph, 'ore:aggregates directory') + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"directory/file1.a")) in rdfgraph, 'ore:aggregates file1.a') + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"directory/file1.b")) in rdfgraph, 'ore:aggregates file1.b') + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"directory/file2.a")) in rdfgraph, 'ore:aggregates file2.a') + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"test-csv.csv")) in rdfgraph, 'ore:aggregates test-csv.csv') + self.failUnless((subj,URIRef(oxds+"currentVersion"),"1") in rdfgraph, 'oxds:currentVersion') + # Unpack second ZIP file into dataset TestSubmission-testdir, check response + fields = \ + [ ("filename", "testrdf4.zip"), + ("id", "TestSubmission-testdir") + ] + files = [] + (reqtype, reqdata) = SparqlQueryTestCase.encode_multipart_formdata(fields, files) + (resp,respdata) = self.doHTTP_POST( + reqdata, reqtype, + resource="items/TestSubmission", + expect_status=204, expect_reason="No Content") + # Access and check list of contents in TestSubmission + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + subj = URIRef(self.getManifestUri("datasets/TestSubmission")) + base = self.getManifestUri("datasets/TestSubmission/") + dcterms = "http://purl.org/dc/terms/" + ore = "http://www.openarchives.org/ore/terms/" + oxds = "http://vocab.ox.ac.uk/dataset/schema#" + stype = URIRef(oxds+"DataSet") + self.assertEqual(len(rdfgraph),18,'Graph length %i' %len(rdfgraph)) + self.failUnless((subj,RDF.type,stype) in rdfgraph, 'Testing submission type: '+subj+", "+stype) + self.failUnless((subj,URIRef(dcterms+"created"),None) in rdfgraph, 'dcterms:created') + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"testdir.zip")) in rdfgraph, 'ore:aggregates testdir.zip') + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"testdir2.zip")) in rdfgraph, 'ore:aggregates testdir2.zip') + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"testrdf.zip")) in rdfgraph, 'ore:aggregates testrdf.zip') + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"testrdf2.zip")) in rdfgraph, 'ore:aggregates testrdf2.zip') + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"testrdf4.zip")) in rdfgraph, 'ore:aggregates testrdf4.zip') + self.failUnless((URIRef(base+"testdir.zip"),URIRef(dcterms+"hasVersion"),None) in rdfgraph, 'dcterms:hasVersion') + self.failUnless((URIRef(base+"testrdf4.zip"),URIRef(dcterms+"hasVersion"),None) in rdfgraph, 'dcterms:hasVersion') + self.failUnless((subj,URIRef(dcterms+"identifier"),None) in rdfgraph, 'dcterms:identifier') + self.failUnless((subj,URIRef(dcterms+"mediator"),None) in rdfgraph, 'dcterms:mediator') + self.failUnless((subj,URIRef(dcterms+"rights"),None) in rdfgraph, 'dcterms:rights') + self.failUnless((subj,URIRef(dcterms+"license"),None) in rdfgraph, 'dcterms:license') + self.failUnless((subj,URIRef(dcterms+"publisher"),None) in rdfgraph, 'dcterms:publisher') + self.failUnless((subj,URIRef(oxds+"isEmbargoed"),None) in rdfgraph, 'oxds:isEmbargoed') + self.failUnless((subj,URIRef(oxds+"embargoedUntil"),None) in rdfgraph, 'oxds:embargoedUntil') + self.failUnless((subj,URIRef(oxds+"currentVersion"),"5") in rdfgraph, 'oxds:currentVersion') + self.failUnless((subj,URIRef(dcterms+"modified"),None) in rdfgraph, 'dcterms:modified') + # Access dataset TestSubmission-testdir, check response + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission-testdir", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + subj = URIRef(self.getManifestUri("datasets/TestSubmission-testdir")) + subj2 = URIRef(self.getManifestUri("datasets/TestSubmission-testdir/testrdf4/directory/file1.a")) + subj3 = URIRef(self.getManifestUri("datasets/TestSubmission-testdir/testrdf4/directory/file1.b")) + stype = URIRef("http://vocab.ox.ac.uk/dataset/schema#Grouping") + stype1 = URIRef("http://vocab.ox.ac.uk/dataset/schema#DataSet") + stype2 = URIRef(oxds+"item") + base = self.getManifestUri("datasets/TestSubmission-testdir/") + owl = "http://www.w3.org/2002/07/owl#" + dc = "http://purl.org/dc/elements/1.1/" + self.assertEqual(len(rdfgraph),29,'Graph length %i' %len(rdfgraph)) + self.failUnless((subj,RDF.type,stype) in rdfgraph, 'Testing submission type: '+subj+", "+stype) + self.failUnless((subj,URIRef(dcterms+"modified"),None) in rdfgraph, 'dcterms:modified') + self.failUnless((subj,URIRef(dcterms+"isVersionOf"),None) in rdfgraph, 'dcterms:isVersionOf') + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"testrdf4")) in rdfgraph, 'ore:aggregates testrdf4') + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"testrdf4/directory")) in rdfgraph, 'ore:aggregates directory') + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"testrdf4/directory/file1.a")) in rdfgraph, 'ore:aggregates file1.a') + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"testrdf4/directory/file1.b")) in rdfgraph, 'ore:aggregates file1.b') + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"testrdf4/directory/file2.a")) in rdfgraph, 'ore:aggregates file2.a') + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"testrdf4/directory/1a.rdf")) in rdfgraph, 'ore:aggregates 1a.rdf') + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"testrdf4/directory/1b.rdf")) in rdfgraph, 'ore:aggregates 1b.rdf') + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"testrdf4/directory/2a.rdf")) in rdfgraph, 'ore:aggregates 2a.rdf') + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"testrdf4/test-csv.csv")) in rdfgraph, 'ore:aggregates test-csv.csv') + self.failUnless((subj,URIRef(dcterms+"identifier"),None) in rdfgraph, 'dcterms:identifier') + self.failUnless((subj,URIRef(dcterms+"mediator"),None) in rdfgraph, 'dcterms:mediator') + self.failUnless((subj,URIRef(dcterms+"rights"),None) in rdfgraph, 'dcterms:rights') + self.failUnless((subj,URIRef(dcterms+"license"),None) in rdfgraph, 'dcterms:license') + self.failUnless((subj,URIRef(dcterms+"publisher"),None) in rdfgraph, 'dcterms:publisher') + self.failUnless((subj,URIRef(oxds+"isEmbargoed"),'True') in rdfgraph, 'oxds:isEmbargoed') + self.failUnless((subj,URIRef(oxds+"embargoedUntil"),None) in rdfgraph, 'oxds:isEmbargoed') + self.failUnless((subj,URIRef(dcterms+"created"),None) in rdfgraph, 'dcterms:created') + self.failUnless((subj,URIRef(dc+"description"),"This is a archived test item 2a ") in rdfgraph, 'dc:description') + #self.failUnless((subj,URIRef(dcterms+"title"),"Test dataset 4 with updated and merged metadata") in rdfgraph, 'dcterms:title') + self.failUnless((subj,URIRef(dcterms+"title"),"Test item 2a") in rdfgraph, 'dcterms:title') + self.failUnless((subj,URIRef(oxds+"currentVersion"),"2") in rdfgraph, 'oxds:currentVersion') + #self.failUnless((subj,URIRef(owl+"sameAs"),URIRef("2aFiles")) in rdfgraph, 'dcterms:title') + self.failUnless((subj2,RDF.type,stype2) in rdfgraph, 'Testing submission type: %s, %s'%(str(subj2), str(stype2))) + self.failUnless((subj2,URIRef(dc+"description"),"This is a archived test item 1a ") in rdfgraph, 'dc:description') + self.failUnless((subj2,URIRef(dcterms+"title"),"Test item 1a") in rdfgraph, 'dcterms:title') + self.failUnless((subj3,RDF.type,stype2) in rdfgraph, 'Testing submission type: '+subj3+", "+stype2) + self.failUnless((subj3,URIRef(dc+"description"),"This is test item 1b of type file") in rdfgraph, 'dc:description') + self.failUnless((subj3,URIRef(dcterms+"title"),"Test item 1b") in rdfgraph, 'dcterms:title') + + #Access state information of TestSubmission-testdir + (resp, data) = self.doHTTP_GET( + resource="states/TestSubmission-testdir", + expect_status=200, expect_reason="OK", expect_type="application/json") + state = data['state'] + parts = data['parts'] + self.assertEqual(len(state.keys()), 11, "States") + self.assertEqual(state['item_id'], "TestSubmission-testdir", "Submission item identifier") + self.assertEqual(len(state['versions']), 3, "Three versions") + self.assertEqual(state['versions'][0], '0', "Version 0") + self.assertEqual(state['versions'][1], '1', "Version 1") + self.assertEqual(state['versions'][2], '2', "Version 2") + self.assertEqual(state['currentversion'], '2', "Current version == 2") + self.assertEqual(state['rdffileformat'], 'xml', "RDF file type") + self.assertEqual(state['rdffilename'], 'manifest.rdf', "RDF file name") + self.assertEqual(len(state['files']['0']), 1, "List should contain just manifest.rdf") + self.assertEqual(len(state['files']['1']), 3, "List should contain manifest.rdf, directory and test-csv.csv") + self.assertEqual(len(state['files']['2']), 2, "List should contain manifest.rdf, testrdf4") + self.assertEqual(len(state['metadata_files']['0']), 0, "metadata_files of version 0") + self.assertEqual(len(state['metadata_files']['1']), 0, "metadata_files of version 1") + self.assertEqual(len(state['metadata_files']['2']), 0, "metadata_files of version 2") + self.assertEqual(state['subdir']['0'], [], "Subdirectory count for version 0") + self.assertEqual(state['subdir']['1'], ['directory'], "Subdirectory for version 1") + self.assertEqual(state['subdir']['2'], ['testrdf4'], "Subdirectory for version 2 should be directory") + self.assertEqual(state['metadata']['createdby'], RDFDatabankConfig.endpointsubmitteruser, "Created by") + self.assertEqual(state['metadata']['embargoed'], True, "Embargoed?") + d = (datetime.datetime.now() + relativedelta(years=+70)).isoformat() + d = d.split('T')[0] + self.assertTrue(d in state['metadata']['embargoed_until'], "embargoed_until %s?"%d) + self.assertEqual(len(parts.keys()), 4, "Parts") + self.assertEqual(len(parts['4=TestSubmission-testdir'].keys()), 13, "File stats for 4=TestSubmission-testdir") + self.assertEqual(len(parts['manifest.rdf'].keys()), 13, "File stats for manifest.rdf") + self.assertEqual(len(parts['testrdf4'].keys()), 0, "File stats for directory") + + # Unpack third ZIP file into dataset by admin, check response + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointadminuser, + endpointpass=RDFDatabankConfig.endpointadminpass) + fields = \ + [ ("filename", "testdir2.zip"), + ("id", "TestSubmission-testdir") + ] + files = [] + (reqtype, reqdata) = SparqlQueryTestCase.encode_multipart_formdata(fields, files) + (resp,respdata) = self.doHTTP_POST( + reqdata, reqtype, + resource="items/TestSubmission", + expect_status=204, expect_reason="No Content") + # Access and check list of contents in TestSubmission + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointsubmitteruser, + endpointpass=RDFDatabankConfig.endpointsubmitterpass) + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + subj = URIRef(self.getManifestUri("datasets/TestSubmission")) + stype = URIRef(oxds+"DataSet") + base = self.getManifestUri("datasets/TestSubmission/") + self.assertEqual(len(rdfgraph),19,'Graph length %i' %len(rdfgraph)) + self.failUnless((subj,RDF.type,stype) in rdfgraph, 'Testing submission type: '+subj+", "+stype) + self.failUnless((subj,URIRef(dcterms+"created"),None) in rdfgraph, 'dcterms:created') + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"testdir.zip")) in rdfgraph, 'ore:aggregates testdir.zip') + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"testdir2.zip")) in rdfgraph, 'ore:aggregates testdir2.zip') + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"testrdf.zip")) in rdfgraph, 'ore:aggregates testrdf.zip') + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"testrdf2.zip")) in rdfgraph, 'ore:aggregates testrdf2.zip') + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"testrdf4.zip")) in rdfgraph, 'ore:aggregates testrdf4.zip') + self.failUnless((URIRef(base+"testdir.zip"),URIRef(dcterms+"hasVersion"),None) in rdfgraph, 'dcterms:hasVersion') + self.failUnless((URIRef(base+"testrdf4.zip"),URIRef(dcterms+"hasVersion"),None) in rdfgraph, 'dcterms:hasVersion') + self.failUnless((URIRef(base+"testdir2.zip"),URIRef(dcterms+"hasVersion"),None) in rdfgraph, 'dcterms:hasVersion') + self.failUnless((subj,URIRef(dcterms+"identifier"),None) in rdfgraph, 'dcterms:identifier') + self.failUnless((subj,URIRef(dcterms+"mediator"),None) in rdfgraph, 'dcterms:mediator') + self.failUnless((subj,URIRef(dcterms+"rights"),None) in rdfgraph, 'dcterms:rights') + self.failUnless((subj,URIRef(dcterms+"license"),None) in rdfgraph, 'dcterms:license') + self.failUnless((subj,URIRef(dcterms+"publisher"),None) in rdfgraph, 'dcterms:publisher') + self.failUnless((subj,URIRef(oxds+"isEmbargoed"),None) in rdfgraph, 'oxds:isEmbargoed') + self.failUnless((subj,URIRef(oxds+"embargoedUntil"),None) in rdfgraph, 'oxds:embargoedUntil') + self.failUnless((subj,URIRef(oxds+"currentVersion"),"5") in rdfgraph, 'oxds:currentVersion') + self.failUnless((subj,URIRef(dcterms+"modified"),None) in rdfgraph, 'dcterms:modified') + # Access updated dataset, check response + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission-testdir", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + subj = URIRef(self.getManifestUri("datasets/TestSubmission-testdir")) + stype = URIRef("http://vocab.ox.ac.uk/dataset/schema#Grouping") + base = self.getManifestUri("datasets/TestSubmission-testdir/") + owl = "http://www.w3.org/2002/07/owl#" + self.assertEqual(len(rdfgraph),22,'Graph length %i' %len(rdfgraph)) + self.failUnless((subj,RDF.type,stype) in rdfgraph, 'Testing submission type: '+subj+", "+stype) + self.failUnless((subj,URIRef(dcterms+"identifier"),None) in rdfgraph, 'dcterms:identifier') + self.failUnless((subj,URIRef(dcterms+"mediator"),None) in rdfgraph, 'dcterms:mediator') + self.failUnless((subj,URIRef(dcterms+"rights"),None) in rdfgraph, 'dcterms:rights') + self.failUnless((subj,URIRef(dcterms+"license"),None) in rdfgraph, 'dcterms:license') + self.failUnless((subj,URIRef(dcterms+"publisher"),None) in rdfgraph, 'dcterms:publisher') + self.failUnless((subj,URIRef(oxds+"isEmbargoed"),None) in rdfgraph, 'oxds:isEmbargoed') + self.failUnless((subj,URIRef(oxds+"embargoedUntil"),None) in rdfgraph, 'oxds:embargoedUntil') + self.failUnless((subj,URIRef(dcterms+"created"),None) in rdfgraph, 'dcterms:created') + self.failUnless((subj,URIRef(dcterms+"modified"),None) in rdfgraph, 'dcterms:modified') + self.failUnless((subj,URIRef(dcterms+"isVersionOf"),None) in rdfgraph, 'dcterms:isVersionOf') + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"directory1")) in rdfgraph, 'ore:aggregates directory1') + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"directory1/file1.a")) in rdfgraph, 'ore:aggregates file1.a') + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"directory1/file1.b")) in rdfgraph, 'ore:aggregates file1.b') + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"directory1/file1.c")) in rdfgraph, 'ore:aggregates file1.c') + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"directory2")) in rdfgraph, 'ore:aggregates directory2') + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"directory2/file2.a")) in rdfgraph, 'ore:aggregates file2.a') + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"directory2/file2.b")) in rdfgraph, 'ore:aggregates file2.b') + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"test-csv.csv")) in rdfgraph, 'ore:aggregates test-csv.csv') + self.failUnless((subj,URIRef(oxds+"currentVersion"),"3") in rdfgraph, 'oxds:currentVersion') + self.failUnless((subj,URIRef(dcterms+"title"),"Test dataset with merged metadata") in rdfgraph, 'dcterms:title') + self.failUnless((subj,URIRef(owl+"sameAs"),URIRef("http://example.org/testdir2/")) in rdfgraph, 'owl:sameAs') + # Access and check content of a resource + (resp, filedata) = self.doHTTP_GET( + resource="datasets/TestSubmission-testdir/directory1/file1.b", + expect_status=200, expect_reason="OK", expect_type="text/plain") + checkdata = open("testdata/testdir2/directory1/file1.b").read() + self.assertEqual(filedata, checkdata, "Difference between local and remote data!") + + # Unpack fourth ZIP file into dataset by manager, check response + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointmanageruser, + endpointpass=RDFDatabankConfig.endpointmanagerpass) + fields = \ + [ ("filename", "testrdf.zip"), + ("id", "TestSubmission-testdir") + ] + files = [] + (reqtype, reqdata) = SparqlQueryTestCase.encode_multipart_formdata(fields, files) + (resp,respdata) = self.doHTTP_POST( + reqdata, reqtype, + resource="items/TestSubmission", + expect_status=204, expect_reason="No Content") + # Access and check list of contents in TestSubmission + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointsubmitteruser, + endpointpass=RDFDatabankConfig.endpointsubmitterpass) + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + stype = URIRef(oxds+"DataSet") + base = self.getManifestUri("datasets/TestSubmission/") + subj = URIRef(self.getManifestUri("datasets/TestSubmission")) + self.assertEqual(len(rdfgraph),20,'Graph length %i' %len(rdfgraph)) + self.failUnless((URIRef(base+"testrdf.zip"),URIRef(dcterms+"hasVersion"),None) in rdfgraph, 'dcterms:hasVersion') + self.failUnless((subj,URIRef(oxds+"currentVersion"),"5") in rdfgraph, 'oxds:currentVersion') + # Access updated dataset, check response + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission-testdir", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + subj = URIRef(self.getManifestUri("datasets/TestSubmission-testdir")) + stype = URIRef("http://vocab.ox.ac.uk/dataset/schema#Grouping") + base = self.getManifestUri("datasets/TestSubmission-testdir/") + self.assertEqual(len(rdfgraph),21,'Graph length %i' %len(rdfgraph)) + self.failUnless((subj,RDF.type,stype) in rdfgraph, 'Testing submission type: '+subj+", "+stype) + self.failUnless((subj,URIRef(dcterms+"identifier"),None) in rdfgraph, 'dcterms:identifier') + self.failUnless((subj,URIRef(dcterms+"mediator"),None) in rdfgraph, 'dcterms:mediator') + self.failUnless((subj,URIRef(dcterms+"rights"),None) in rdfgraph, 'dcterms:rights') + self.failUnless((subj,URIRef(dcterms+"license"),None) in rdfgraph, 'dcterms:license') + self.failUnless((subj,URIRef(dcterms+"publisher"),None) in rdfgraph, 'dcterms:publisher') + self.failUnless((subj,URIRef(oxds+"isEmbargoed"),None) in rdfgraph, 'oxds:isEmbargoed') + self.failUnless((subj,URIRef(oxds+"embargoedUntil"),None) in rdfgraph, 'oxds:embargoedUntil') + self.failUnless((subj,URIRef(dcterms+"created"),None) in rdfgraph, 'dcterms:created') + self.failUnless((subj,URIRef(dcterms+"modified"),None) in rdfgraph, 'dcterms:modified') + self.failUnless((subj,URIRef(dcterms+"isVersionOf"),None) in rdfgraph, 'dcterms:isVersionOf') + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"directory")) in rdfgraph, 'ore:aggregates directory') + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"directory/file1.a")) in rdfgraph, 'ore:aggregates file1.a') + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"directory/file1.b")) in rdfgraph, 'ore:aggregates file1.b') + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"directory/file2.a")) in rdfgraph, 'ore:aggregates file2.a') + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"test-csv.csv")) in rdfgraph, 'ore:aggregates test-csv.csv') + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"arabic.txt")) in rdfgraph, 'ore:aggregates arabic.txt') + self.failUnless((subj,URIRef(oxds+"currentVersion"),"4") in rdfgraph, 'oxds:currentVersion') + self.failUnless((subj,URIRef(dcterms+"title"),"Test dataset with merged metadata") in rdfgraph, 'dcterms:title') + self.failUnless((subj,URIRef(owl+"sameAs"),URIRef("http://example.org/testrdf/")) in rdfgraph, 'owl:sameAs') + self.failUnless((subj,URIRef(dcterms+"description"),None) in rdfgraph, 'dcterms:description') + # Access and check content of a resource + (resp, filedata) = self.doHTTP_GET( + resource="datasets/TestSubmission-testdir/directory/file1.b", + expect_status=200, expect_reason="OK", expect_type="text/plain") + checkdata = open("testdata/testrdf/directory/file1.b").read() + self.assertEqual(filedata, checkdata, "Difference between local and remote data!") + + # Unpack fifth ZIP file into dataset by submiter 2, check response + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointsubmitteruser2, + endpointpass=RDFDatabankConfig.endpointsubmitterpass2) + fields = \ + [ ("filename", "testrdf2.zip"), + ("id", "TestSubmission-testdir") + ] + files = [] + (reqtype, reqdata) = SparqlQueryTestCase.encode_multipart_formdata(fields, files) + (resp,respdata) = self.doHTTP_POST( + reqdata, reqtype, + resource="items/TestSubmission", + expect_status=403, expect_reason="Forbidden") + # Access and check list of contents in TestSubmission + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointsubmitteruser, + endpointpass=RDFDatabankConfig.endpointsubmitterpass) + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + stype = URIRef(oxds+"DataSet") + base = self.getManifestUri("datasets/TestSubmission/") + subj = URIRef(self.getManifestUri("datasets/TestSubmission")) + self.assertEqual(len(rdfgraph),20,'Graph length %i' %len(rdfgraph)) + self.assertFalse((URIRef(base+"testrdf2.zip"),URIRef(dcterms+"hasVersion"),None) in rdfgraph, 'dcterms:hasVersion testrdf2.zip') + self.failUnless((subj,URIRef(oxds+"currentVersion"),"5") in rdfgraph, 'oxds:currentVersion') + # Access new dataset, check response + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission-testrdf2", + expect_status=404, expect_reason="Not Found", expect_type="application/rdf+xml") + + # Unpack ZIP file into dataset by general user, check response + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointgeneraluser, + endpointpass=RDFDatabankConfig.endpointgeneralpass) + fields = \ + [ ("filename", "testrdf2.zip"), + ("id", "TestSubmission-testdir") + ] + files = [] + (reqtype, reqdata) = SparqlQueryTestCase.encode_multipart_formdata(fields, files) + (resp,respdata) = self.doHTTP_POST( + reqdata, reqtype, + resource="items/TestSubmission", + expect_status=401, expect_reason="Unauthorized", expect_type="text/plain") + # Access and check list of contents in TestSubmission + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointsubmitteruser, + endpointpass=RDFDatabankConfig.endpointsubmitterpass) + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + base = self.getManifestUri("datasets/TestSubmission/") + subj = URIRef(self.getManifestUri("datasets/TestSubmission")) + stype = URIRef(oxds+"DataSet") + self.assertEqual(len(rdfgraph),20,'Graph length %i' %len(rdfgraph)) + self.assertFalse((URIRef(base+"testrdf2.zip"),URIRef(dcterms+"hasVersion"),None) in rdfgraph, 'dcterms:hasVersion testrdf2.zip') + self.failUnless((subj,URIRef(oxds+"currentVersion"),"5") in rdfgraph, 'oxds:currentVersion') + # Access new dataset, check response + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission-testrdf2", + expect_status=404, expect_reason="Not Found", expect_type="application/rdf+xml") + + # Unpack ZIP file into dataset by admin user3, check response + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointadminuser3, + endpointpass=RDFDatabankConfig.endpointadminpass3) + fields = \ + [ ("filename", "testrdf2.zip"), + ("id", "TestSubmission-testdir") + ] + files = [] + (reqtype, reqdata) = SparqlQueryTestCase.encode_multipart_formdata(fields, files) + (resp,respdata) = self.doHTTP_POST( + reqdata, reqtype, + resource="items/TestSubmission", + expect_status=403, expect_reason="Forbidden") + # Access and check list of contents in TestSubmission + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointsubmitteruser, + endpointpass=RDFDatabankConfig.endpointsubmitterpass) + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + base = self.getManifestUri("datasets/TestSubmission/") + subj = URIRef(self.getManifestUri("datasets/TestSubmission")) + stype = URIRef(oxds+"DataSet") + self.assertEqual(len(rdfgraph),20,'Graph length %i' %len(rdfgraph)) + self.assertFalse((URIRef(base+"testrdf2.zip"),URIRef(dcterms+"hasVersion"),None) in rdfgraph, 'dcterms:hasVersion testrdf2.zip') + self.failUnless((subj,URIRef(oxds+"currentVersion"),"5") in rdfgraph, 'oxds:currentVersion') + # Access new dataset, check response + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission-testrdf2", + expect_status=404, expect_reason="Not Found", expect_type="application/rdf+xml") + + # Unpack ZIP file into dataset by manager user3, check response + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointmanageruser3, + endpointpass=RDFDatabankConfig.endpointmanagerpass3) + fields = \ + [ ("filename", "testrdf2.zip"), + ("id", "TestSubmission-testdir") + ] + files = [] + (reqtype, reqdata) = SparqlQueryTestCase.encode_multipart_formdata(fields, files) + (resp,respdata) = self.doHTTP_POST( + reqdata, reqtype, + resource="items/TestSubmission", + expect_status=403, expect_reason="Forbidden") + # Access and check list of contents in TestSubmission + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointsubmitteruser, + endpointpass=RDFDatabankConfig.endpointsubmitterpass) + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + stype = URIRef(oxds+"DataSet") + base = self.getManifestUri("datasets/TestSubmission/") + subj = URIRef(self.getManifestUri("datasets/TestSubmission")) + self.assertEqual(len(rdfgraph),20,'Graph length %i' %len(rdfgraph)) + self.assertFalse((URIRef(base+"testrdf2.zip"),URIRef(dcterms+"hasVersion"),None) in rdfgraph, 'dcterms:hasVersion testrdf2.zip') + self.failUnless((subj,URIRef(oxds+"currentVersion"),"5") in rdfgraph, 'oxds:currentVersion') + # Access new dataset, check response + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission-testrdf2", + expect_status=404, expect_reason="Not Found", expect_type="application/rdf+xml") + + # Unpack ZIP file into dataset by submitter user3, check response + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointsubmitteruser3, + endpointpass=RDFDatabankConfig.endpointsubmitterpass3) + fields = \ + [ ("filename", "testrdf2.zip"), + ("id", "TestSubmission-testdir") + ] + files = [] + (reqtype, reqdata) = SparqlQueryTestCase.encode_multipart_formdata(fields, files) + (resp,respdata) = self.doHTTP_POST( + reqdata, reqtype, + resource="items/TestSubmission", + expect_status=403, expect_reason="Forbidden") + # Access and check list of contents in TestSubmission + self.setRequestUserPass( + endpointuser=RDFDatabankConfig.endpointsubmitteruser, + endpointpass=RDFDatabankConfig.endpointsubmitterpass) + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + base = self.getManifestUri("datasets/TestSubmission/") + subj = URIRef(self.getManifestUri("datasets/TestSubmission")) + stype = URIRef(oxds+"DataSet") + self.assertEqual(len(rdfgraph),20,'Graph length %i' %len(rdfgraph)) + self.assertFalse((URIRef(base+"testrdf2.zip"),URIRef(dcterms+"hasVersion"),None) in rdfgraph, 'dcterms:hasVersion testrdf2.zip') + self.failUnless((subj,URIRef(oxds+"currentVersion"),"5") in rdfgraph, 'oxds:currentVersion') + # Access new dataset, check response + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission-testrdf2", + expect_status=404, expect_reason="Not Found", expect_type="application/rdf+xml") + + # Access and check list of contents in TestSubmission-testdir version 0 + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission-testdir?version=0", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + stype = URIRef(oxds+"DataSet") + stype2 = URIRef(oxds+"Grouping") + subj = URIRef(self.getManifestUri("datasets/TestSubmission-testdir")) + base = self.getManifestUri("datasets/TestSubmission-testdir/") + self.assertEqual(len(rdfgraph),10,'Graph length %i' %len(rdfgraph)) + self.failUnless((subj,RDF.type,stype) in rdfgraph, 'Testing submission type: '+subj+", "+stype) + self.failUnless((subj,URIRef(dcterms+"identifier"),None) in rdfgraph, 'dcterms:identifier') + self.failUnless((subj,URIRef(dcterms+"mediator"),None) in rdfgraph, 'dcterms:mediator') + self.failUnless((subj,URIRef(dcterms+"rights"),None) in rdfgraph, 'dcterms:rights') + self.failUnless((subj,URIRef(dcterms+"license"),None) in rdfgraph, 'dcterms:license') + self.failUnless((subj,URIRef(dcterms+"publisher"),None) in rdfgraph, 'dcterms:publisher') + self.failUnless((subj,URIRef(oxds+"isEmbargoed"),None) in rdfgraph, 'oxds:isEmbargoed') + self.failUnless((subj,URIRef(oxds+"embargoedUntil"),None) in rdfgraph, 'oxds:embargoedUntil') + self.failUnless((subj,URIRef(dcterms+"created"),None) in rdfgraph, 'dcterms:created') + self.failUnless((subj,URIRef(oxds+"currentVersion"),"0") in rdfgraph, 'oxds:currentVersion') + #Access state information of TestSubmission-testdir version 0 + (resp, data) = self.doHTTP_GET( + resource="states/TestSubmission-testdir?version=0", + expect_status=200, expect_reason="OK", expect_type="application/json") + state = data['state'] + parts = data['parts'] + self.assertEqual(len(state.keys()), 11, "States") + self.assertEqual(len(parts.keys()), 3, "Parts") + self.assertEqual(len(parts['4=TestSubmission-testdir'].keys()), 13, "File stats for 4=TestSubmission-testdir") + self.assertEqual(len(parts['manifest.rdf'].keys()), 13, "File stats for manifest.rdf") + + # Access dataset TestSubmission-testdir version 1 + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission-testdir/version1", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + self.assertEqual(len(rdfgraph),17,'Graph length %i' %len(rdfgraph)) + self.failUnless((subj,RDF.type,stype2) in rdfgraph, 'Testing submission type: '+subj+", "+stype2) + self.failUnless((subj,URIRef(dcterms+"identifier"),None) in rdfgraph, 'dcterms:identifier') + self.failUnless((subj,URIRef(dcterms+"mediator"),None) in rdfgraph, 'dcterms:mediator') + self.failUnless((subj,URIRef(dcterms+"rights"),None) in rdfgraph, 'dcterms:rights') + self.failUnless((subj,URIRef(dcterms+"license"),None) in rdfgraph, 'dcterms:license') + self.failUnless((subj,URIRef(dcterms+"publisher"),None) in rdfgraph, 'dcterms:publisher') + self.failUnless((subj,URIRef(oxds+"isEmbargoed"),None) in rdfgraph, 'oxds:isEmbargoed') + self.failUnless((subj,URIRef(oxds+"embargoedUntil"),None) in rdfgraph, 'oxds:embargoedUntil') + self.failUnless((subj,URIRef(dcterms+"created"),None) in rdfgraph, 'dcterms:created') + self.failUnless((subj,URIRef(dcterms+"modified"),None) in rdfgraph, 'dcterms:modified') + self.failUnless((subj,URIRef(dcterms+"isVersionOf"),None) in rdfgraph, 'dcterms:isVersionOf') + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"directory")) in rdfgraph, 'ore:aggregates directory') + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"directory/file1.a")) in rdfgraph, 'ore:aggregates file1.a') + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"directory/file1.b")) in rdfgraph, 'ore:aggregates file1.b') + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"directory/file2.a")) in rdfgraph, 'ore:aggregates tfile2.a') + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"test-csv.csv")) in rdfgraph, 'ore:aggregates test-csv.csv') + self.failUnless((subj,URIRef(oxds+"currentVersion"),"1") in rdfgraph, 'oxds:currentVersion') + #Access state information of TestSubmission-testdir version 1 + (resp, data) = self.doHTTP_GET( + resource="states/TestSubmission-testdir/version1", + expect_status=200, expect_reason="OK", expect_type="application/json") + state = data['state'] + parts = data['parts'] + self.assertEqual(len(state.keys()), 11, "States") + self.assertEqual(len(parts.keys()), 5, "Parts") + self.assertEqual(len(parts['4=TestSubmission-testdir'].keys()), 13, "File stats for 4=TestSubmission-testdir") + self.assertEqual(len(parts['manifest.rdf'].keys()), 13, "File stats for manifest.rdf") + self.assertEqual(len(parts['test-csv.csv'].keys()), 13, "File stats for test-csv.csv") + self.assertEqual(len(parts['directory'].keys()), 0, "File stats for directory") + + # Access dataset TestSubmission-testdir version 2 + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission-testdir/version2", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + stype3 = URIRef(oxds+"item") + subj2 = URIRef(base+"testrdf4/directory/file1.a") + subj3 = URIRef(base+"testrdf4/directory/file1.b") + self.assertEqual(len(rdfgraph),29,'Graph length %i' %len(rdfgraph)) + self.failUnless((subj,RDF.type,stype2) in rdfgraph, 'Testing submission type: '+subj+", "+stype2) + self.failUnless((subj,URIRef(dcterms+"modified"),None) in rdfgraph, 'dcterms:modified') + self.failUnless((subj,URIRef(dcterms+"isVersionOf"),None) in rdfgraph, 'dcterms:isVersionOf') + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"testrdf4")) in rdfgraph, 'ore:aggregates testrdf4') + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"testrdf4/directory")) in rdfgraph, 'ore:aggregates directory') + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"testrdf4/directory/file1.a")) in rdfgraph, 'ore:aggregates file1.a') + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"testrdf4/directory/file1.b")) in rdfgraph, 'ore:aggregates file1.b') + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"testrdf4/directory/file2.a")) in rdfgraph, 'ore:aggregates file2.a') + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"testrdf4/directory/1a.rdf")) in rdfgraph, 'ore:aggregates 1a.rdf') + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"testrdf4/directory/1b.rdf")) in rdfgraph, 'ore:aggregates 1b.rdf') + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"testrdf4/directory/2a.rdf")) in rdfgraph, 'ore:aggregates 2a.rdf') + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"testrdf4/test-csv.csv")) in rdfgraph, 'ore:aggregates test-csv.csv') + self.failUnless((subj,URIRef(dcterms+"identifier"),None) in rdfgraph, 'dcterms:identifier') + self.failUnless((subj,URIRef(dcterms+"mediator"),None) in rdfgraph, 'dcterms:mediator') + self.failUnless((subj,URIRef(dcterms+"rights"),None) in rdfgraph, 'dcterms:rights') + self.failUnless((subj,URIRef(dcterms+"license"),None) in rdfgraph, 'dcterms:license') + self.failUnless((subj,URIRef(dcterms+"publisher"),None) in rdfgraph, 'dcterms:publisher') + self.failUnless((subj,URIRef(oxds+"isEmbargoed"),None) in rdfgraph, 'oxds:isEmbargoed') + self.failUnless((subj,URIRef(oxds+"embargoedUntil"),None) in rdfgraph, 'oxds:embargoedUntil') + self.failUnless((subj,URIRef(dcterms+"created"),None) in rdfgraph, 'dcterms:created') + self.failUnless((subj,URIRef(dc+"description"),"This is a archived test item 2a ") in rdfgraph, 'dc:description') + self.failUnless((subj,URIRef(dcterms+"title"),"Test item 2a") in rdfgraph, 'dcterms:title') + self.failUnless((subj,URIRef(oxds+"currentVersion"),"2") in rdfgraph, 'oxds:currentVersion') + self.failUnless((subj2,RDF.type,stype3) in rdfgraph, 'Testing submission type: %s, %s'%(str(subj2), str(stype3))) + self.failUnless((subj2,URIRef(dc+"description"),"This is a archived test item 1a ") in rdfgraph, 'dc:description') + self.failUnless((subj2,URIRef(dcterms+"title"),"Test item 1a") in rdfgraph, 'dcterms:title') + self.failUnless((subj3,RDF.type,stype3) in rdfgraph, 'Testing submission type: '+subj3+", "+stype3) + self.failUnless((subj3,URIRef(dc+"description"),"This is test item 1b of type file") in rdfgraph, 'dc:description') + self.failUnless((subj3,URIRef(dcterms+"title"),"Test item 1b") in rdfgraph, 'dcterms:title') + #Access state information of TestSubmission-testdir version 2 + (resp, data) = self.doHTTP_GET( + resource="states/TestSubmission-testdir/version2", + expect_status=200, expect_reason="OK", expect_type="application/json") + state = data['state'] + parts = data['parts'] + self.assertEqual(len(state.keys()), 11, "States") + self.assertEqual(state['item_id'], "TestSubmission-testdir", "Submission item identifier") + self.assertEqual(len(state['versions']), 5, "Five versions") + self.assertEqual(state['currentversion'], '2', "Current version == 2") + self.assertEqual(state['rdffileformat'], 'xml', "RDF file type") + self.assertEqual(state['rdffilename'], 'manifest.rdf', "RDF file name") + self.assertEqual(len(state['files']['0']), 1, "List should contain just manifest.rdf") + self.assertEqual(len(state['files']['1']), 3, "List should contain manifest.rdf, directory and test-csv.csv") + self.assertEqual(len(state['files']['2']), 2, "List should contain manifest.rdf, testrdf4") + self.assertEqual(len(state['metadata_files']['0']), 0, "metadata_files of version 0") + self.assertEqual(len(state['metadata_files']['1']), 0, "metadata_files of version 1") + self.assertEqual(len(state['metadata_files']['2']), 0, "metadata_files of version 2") + self.assertEqual(state['subdir']['0'], [], "Subdirectory count for version 0") + self.assertEqual(state['subdir']['1'], ['directory'], "Subdirectory for version 1") + self.assertEqual(state['subdir']['2'], ['testrdf4'], "Subdirectory for version 2 should be testrdf4") + self.assertEqual(state['metadata']['createdby'], RDFDatabankConfig.endpointsubmitteruser, "Created by") + self.assertEqual(state['metadata']['embargoed'], True, "Embargoed?") + self.assertEqual(len(parts.keys()), 4, "Parts") + self.assertEqual(len(parts['4=TestSubmission-testdir'].keys()), 13, "File stats for 4=TestSubmission-testdir") + self.assertEqual(len(parts['manifest.rdf'].keys()), 13, "File stats for manifest.rdf") + self.assertEqual(len(parts['testrdf4'].keys()), 0, "File stats for directory1") + + # Access dataset TestSubmission-testdir version 3 + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission-testdir?version=3", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + subj = URIRef(self.getManifestUri("datasets/TestSubmission-testdir")) + stype = URIRef("http://vocab.ox.ac.uk/dataset/schema#Grouping") + base = self.getManifestUri("datasets/TestSubmission-testdir/") + owl = "http://www.w3.org/2002/07/owl#" + self.assertEqual(len(rdfgraph),22,'Graph length %i' %len(rdfgraph)) + self.failUnless((subj,RDF.type,stype) in rdfgraph, 'Testing submission type: '+subj+", "+stype) + self.failUnless((subj,URIRef(dcterms+"identifier"),None) in rdfgraph, 'dcterms:identifier') + self.failUnless((subj,URIRef(dcterms+"mediator"),None) in rdfgraph, 'dcterms:mediator') + self.failUnless((subj,URIRef(dcterms+"rights"),None) in rdfgraph, 'dcterms:rights') + self.failUnless((subj,URIRef(dcterms+"license"),None) in rdfgraph, 'dcterms:license') + self.failUnless((subj,URIRef(dcterms+"publisher"),None) in rdfgraph, 'dcterms:publisher') + self.failUnless((subj,URIRef(oxds+"isEmbargoed"),None) in rdfgraph, 'oxds:isEmbargoed') + self.failUnless((subj,URIRef(oxds+"embargoedUntil"),None) in rdfgraph, 'oxds:embargoedUntil') + self.failUnless((subj,URIRef(dcterms+"created"),None) in rdfgraph, 'dcterms:created') + self.failUnless((subj,URIRef(dcterms+"modified"),None) in rdfgraph, 'dcterms:modified') + self.failUnless((subj,URIRef(dcterms+"isVersionOf"),None) in rdfgraph, 'dcterms:isVersionOf') + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"directory1")) in rdfgraph, 'ore:aggregates directory1') + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"directory1/file1.a")) in rdfgraph, 'ore:aggregates file1.a') + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"directory1/file1.b")) in rdfgraph, 'ore:aggregates file1.b') + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"directory1/file1.c")) in rdfgraph, 'ore:aggregates file1.c') + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"directory2")) in rdfgraph, 'ore:aggregates directory2') + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"directory2/file2.a")) in rdfgraph, 'ore:aggregates file2.a') + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"directory2/file2.b")) in rdfgraph, 'ore:aggregates file2.b') + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"test-csv.csv")) in rdfgraph, 'ore:aggregates test-csv.csv') + self.failUnless((subj,URIRef(oxds+"currentVersion"),"3") in rdfgraph, 'oxds:currentVersion') + self.failUnless((subj,URIRef(dcterms+"title"),"Test dataset with merged metadata") in rdfgraph, 'dcterms:title') + self.failUnless((subj,URIRef(owl+"sameAs"),URIRef("http://example.org/testdir2/")) in rdfgraph, 'owl:sameAs') + # Access and check content of a resource + (resp, filedata) = self.doHTTP_GET( + resource="datasets/TestSubmission-testdir/directory1/file1.b?version=3", + expect_status=200, expect_reason="OK", expect_type="text/plain") + checkdata = open("testdata/testdir2/directory1/file1.b").read() + self.assertEqual(filedata, checkdata, "Difference between local and remote data!") + #Access state information of TestSubmission-testdir version 3 + (resp, data) = self.doHTTP_GET( + resource="states/TestSubmission-testdir?version=3", + expect_status=200, expect_reason="OK", expect_type="application/json") + state = data['state'] + parts = data['parts'] + self.assertEqual(len(state.keys()), 11, "States") + self.assertEqual(state['item_id'], "TestSubmission-testdir", "Submission item identifier") + self.assertEqual(len(state['versions']), 5, "Five versions") + self.assertEqual(state['currentversion'], '3', "Current version == 3") + self.assertEqual(state['rdffileformat'], 'xml', "RDF file type") + self.assertEqual(state['rdffilename'], 'manifest.rdf', "RDF file name") + self.assertEqual(len(state['files']['0']), 1, "List should contain just manifest.rdf") + self.assertEqual(len(state['files']['1']), 3, "List should contain manifest.rdf, directory and test-csv.csv") + self.assertEqual(len(state['files']['2']), 2, "List should contain manifest.rdf, testrdf4") + self.assertEqual(len(state['files']['3']), 4, "List should contain manifest.rdf, directory1, directory2 and test-csv.csv") + self.assertEqual(len(state['metadata_files']['0']), 0, "metadata_files of version 0") + self.assertEqual(len(state['metadata_files']['1']), 0, "metadata_files of version 1") + self.assertEqual(len(state['metadata_files']['2']), 0, "metadata_files of version 2") + self.assertEqual(len(state['metadata_files']['3']), 0, "metadata_files of version 3") + self.assertEqual(state['subdir']['0'], [], "Subdirectory count for version 0") + self.assertEqual(state['subdir']['1'], ['directory'], "Subdirectory for version 1") + self.assertEqual(state['subdir']['2'], ['testrdf4'], "Subdirectory for version 2 should be testrdf4") + self.assertEqual(len(state['subdir']['3']), 2, "Subdirectory for version 2 should be directory1 and directory2") + self.assertEqual(state['metadata']['createdby'], RDFDatabankConfig.endpointsubmitteruser, "Created by") + self.assertEqual(state['metadata']['embargoed'], True, "Embargoed?") + self.assertEqual(len(parts.keys()), 6, "Parts") + self.assertEqual(len(parts['4=TestSubmission-testdir'].keys()), 13, "File stats for 4=TestSubmission-testdir") + self.assertEqual(len(parts['manifest.rdf'].keys()), 13, "File stats for manifest.rdf") + self.assertEqual(len(parts['test-csv.csv'].keys()), 13, "File stats for test-csv.csv") + self.assertEqual(len(parts['directory1'].keys()), 0, "File stats for directory1") + self.assertEqual(len(parts['directory2'].keys()), 0, "File stats for directory1") + + # Access dataset TestSubmission-testdir version 4 + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission-testdir?version=4", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + subj = URIRef(self.getManifestUri("datasets/TestSubmission-testdir")) + stype = URIRef("http://vocab.ox.ac.uk/dataset/schema#Grouping") + base = self.getManifestUri("datasets/TestSubmission-testdir/") + self.assertEqual(len(rdfgraph),21,'Graph length %i' %len(rdfgraph)) + self.failUnless((subj,RDF.type,stype) in rdfgraph, 'Testing submission type: '+subj+", "+stype) + self.failUnless((subj,URIRef(dcterms+"identifier"),None) in rdfgraph, 'dcterms:identifier') + self.failUnless((subj,URIRef(dcterms+"mediator"),None) in rdfgraph, 'dcterms:mediator') + self.failUnless((subj,URIRef(dcterms+"rights"),None) in rdfgraph, 'dcterms:rights') + self.failUnless((subj,URIRef(dcterms+"license"),None) in rdfgraph, 'dcterms:license') + self.failUnless((subj,URIRef(dcterms+"publisher"),None) in rdfgraph, 'dcterms:publisher') + self.failUnless((subj,URIRef(oxds+"isEmbargoed"),None) in rdfgraph, 'oxds:isEmbargoed') + self.failUnless((subj,URIRef(oxds+"embargoedUntil"),None) in rdfgraph, 'oxds:embargoedUntil') + self.failUnless((subj,URIRef(dcterms+"created"),None) in rdfgraph, 'dcterms:created') + self.failUnless((subj,URIRef(dcterms+"modified"),None) in rdfgraph, 'dcterms:modified') + self.failUnless((subj,URIRef(dcterms+"isVersionOf"),None) in rdfgraph, 'dcterms:isVersionOf') + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"directory")) in rdfgraph, 'ore:aggregates directory') + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"directory/file1.a")) in rdfgraph, 'ore:aggregates file1.a') + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"directory/file1.b")) in rdfgraph, 'ore:aggregates file1.b') + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"directory/file2.a")) in rdfgraph, 'ore:aggregates file2.a') + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"test-csv.csv")) in rdfgraph, 'ore:aggregates test-csv.csv') + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"arabic.txt")) in rdfgraph, 'ore:aggregates arabic.txt') + self.failUnless((subj,URIRef(oxds+"currentVersion"),"4") in rdfgraph, 'oxds:currentVersion') + self.failUnless((subj,URIRef(dcterms+"title"),"Test dataset with merged metadata") in rdfgraph, 'dcterms:title') + self.failUnless((subj,URIRef(dcterms+"description"),None) in rdfgraph, 'dcterms:description') + self.failUnless((subj,URIRef(owl+"sameAs"),URIRef("http://example.org/testrdf/")) in rdfgraph, 'owl:sameAs') + # Access and check content of a resource + (resp, filedata) = self.doHTTP_GET( + resource="datasets/TestSubmission-testdir/directory/file1.b?version=4", + expect_status=200, expect_reason="OK", expect_type="text/plain") + checkdata = open("testdata/testrdf/directory/file1.b").read() + self.assertEqual(filedata, checkdata, "Difference between local and remote data!") + #Access state information of TestSubmission-testdir version 4 + (resp, data) = self.doHTTP_GET( + resource="states/TestSubmission-testdir?version=4", + expect_status=200, expect_reason="OK", expect_type="application/json") + state = data['state'] + parts = data['parts'] + self.assertEqual(len(state.keys()), 11, "States") + self.assertEqual(state['item_id'], "TestSubmission-testdir", "Submission item identifier") + self.assertEqual(len(state['versions']), 5, "Five versions") + self.assertEqual(state['currentversion'], '4', "Current version == 4") + self.assertEqual(state['rdffileformat'], 'xml', "RDF file type") + self.assertEqual(state['rdffilename'], 'manifest.rdf', "RDF file name") + self.assertEqual(len(state['files']['0']), 1, "List should contain just manifest.rdf") + self.assertEqual(len(state['files']['1']), 3, "List should contain manifest.rdf, directory and test-csv.csv") + self.assertEqual(len(state['files']['2']), 2, "List should contain manifest.rdf, testrdf4") + self.assertEqual(len(state['files']['3']), 4, "List should contain manifest.rdf, directory1, directory2 and test-csv.csv") + self.assertEqual(len(state['files']['4']), 4, "List should contain manifest.rdf, directory, arabic.txt and test-csv.csv") + self.assertEqual(len(state['metadata_files']['0']), 0, "metadata_files of version 0") + self.assertEqual(len(state['metadata_files']['1']), 0, "metadata_files of version 1") + self.assertEqual(len(state['metadata_files']['2']), 0, "metadata_files of version 2") + self.assertEqual(len(state['metadata_files']['3']), 0, "metadata_files of version 3") + self.assertEqual(len(state['metadata_files']['4']), 0, "metadata_files of version 4") + self.assertEqual(state['subdir']['0'], [], "Subdirectory count for version 0") + self.assertEqual(state['subdir']['1'], ['directory'], "Subdirectory for version 1") + self.assertEqual(state['subdir']['2'], ['testrdf4'], "Subdirectory for version 2 should be testrdf4") + self.assertEqual(len(state['subdir']['3']), 2, "Subdirectory for version 2 should be directory1 and directory2") + self.assertEqual(state['subdir']['4'], ['directory'], "Subdirectory for version 4") + self.assertEqual(state['metadata']['createdby'], RDFDatabankConfig.endpointsubmitteruser, "Created by") + self.assertEqual(state['metadata']['embargoed'], True, "Embargoed?") + self.assertEqual(len(parts.keys()), 6, "Parts") + self.assertEqual(len(parts['4=TestSubmission-testdir'].keys()), 13, "File stats for 4=TestSubmission-testdir") + self.assertEqual(len(parts['manifest.rdf'].keys()), 13, "File stats for manifest.rdf") + self.assertEqual(len(parts['test-csv.csv'].keys()), 13, "File stats for test-csv.csv") + self.assertEqual(len(parts['arabic.txt'].keys()), 13, "File stats for arabic.txt") + self.assertEqual(len(parts['directory'].keys()), 0, "File stats for directory") + + # Access dataset TestSubmission-testdir version 5 + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission-testdir?version=5", + expect_status=404, expect_reason="Not Found", expect_type="application/rdf+xml") + + # Delete the dataset TestSubmission-testdir + resp = self.doHTTP_DELETE( + resource="datasets/TestSubmission-testdir", + expect_status="*", expect_reason="*") + # Delete the dataset TestSubmission + resp = self.doHTTP_DELETE( + resource="datasets/TestSubmission", + expect_status="*", expect_reason="*") + + + + def testMetadataMerging(self): + """POST zipfile to /silo_name/items/dataset_name. Unpack zip file to a dataset. + manifest.rdf located in unpacked zipfile is munged with existing manifest of the dataset.""" + # Delete the dataset TestSubmission + resp = self.doHTTP_DELETE( + resource="datasets/TestSubmission-testrdf", + expect_status="*", expect_reason="*") + # Create a new dataset, check response + self.createSubmissionDataset() + # Submit ZIP file testdata/testrdf.zip, check response + fields = [] + zipdata = open("testdata/testrdf.zip").read() + files = \ + [ ("file", "testrdf.zip", zipdata, "application/zip") + ] + (reqtype, reqdata) = SparqlQueryTestCase.encode_multipart_formdata(fields, files) + (resp,respdata) = self.doHTTP_POST( + reqdata, reqtype, + resource="datasets/TestSubmission/", + expect_status=201, expect_reason="Created") + LHobtained = resp.getheader('Content-Location', None) + LHexpected = "%sdatasets/TestSubmission/testrdf.zip"%self._endpointpath + self.assertEquals(LHobtained, LHexpected, 'Content-Location not correct') + # Unpack ZIP file into a new dataset, check response + fields = \ + [ ("filename", "testrdf.zip") + ] + files = [] + (reqtype, reqdata) = SparqlQueryTestCase.encode_multipart_formdata(fields, files) + (resp,respdata) = self.doHTTP_POST( + reqdata, reqtype, + resource="items/TestSubmission", + expect_status=201, expect_reason="Created") + LHobtained = resp.getheader('Content-Location', None) + LHexpected = "%sdatasets/TestSubmission-testrdf"%self._endpointpath + self.assertEquals(LHobtained, LHexpected, 'Content-Location not correct') + # Access parent dataset, check response + (resp, respdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/json") + # Access and check list of contents in parent dataset - TestSubmission + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + subj = URIRef(self.getManifestUri("datasets/TestSubmission")) + base = self.getManifestUri("datasets/TestSubmission/") + dcterms = "http://purl.org/dc/terms/" + ore = "http://www.openarchives.org/ore/terms/" + oxds = "http://vocab.ox.ac.uk/dataset/schema#" + stype = URIRef(oxds+"DataSet") + self.assertEqual(len(rdfgraph),13,'Graph length %i' %len(rdfgraph)) + self.failUnless((subj,RDF.type,stype) in rdfgraph, 'Testing submission type: '+subj+", "+stype) + self.failUnless((subj,URIRef(dcterms+"created"),None) in rdfgraph, 'dcterms:created') + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"testrdf.zip")) in rdfgraph, 'ore:aggregates testrdf.zip') + self.failUnless((URIRef(base+"testrdf.zip"),URIRef(dcterms+"hasVersion"),None) in rdfgraph, 'dcterms:hasVersion') + self.failUnless((subj,URIRef(dcterms+"identifier"),None) in rdfgraph, 'dcterms:identifier') + self.failUnless((subj,URIRef(dcterms+"mediator"),None) in rdfgraph, 'dcterms:mediator') + self.failUnless((subj,URIRef(dcterms+"rights"),None) in rdfgraph, 'dcterms:rights') + self.failUnless((subj,URIRef(dcterms+"license"),None) in rdfgraph, 'dcterms:license') + self.failUnless((subj,URIRef(dcterms+"publisher"),None) in rdfgraph, 'dcterms:publisher') + self.failUnless((subj,URIRef(oxds+"isEmbargoed"),None) in rdfgraph, 'oxds:isEmbargoed') + self.failUnless((subj,URIRef(oxds+"embargoedUntil"),None) in rdfgraph, 'oxds:embargoedUntil') + self.failUnless((subj,URIRef(oxds+"currentVersion"),'1') in rdfgraph, 'oxds:currentVersion') + self.failUnless((subj,URIRef(dcterms+"modified"),None) in rdfgraph, 'dcterms:modified') + # Access and check list of contents in child dataset - TestSubmission-testrdf + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission-testrdf", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + fr = open('response.xml', 'w') + fr.write(rdfdata) + fr.close() + rdfgraph = Graph() + rdfgraph.parse('response.xml', format='xml') + subj = URIRef(self.getManifestUri("datasets/TestSubmission-testrdf")) + base = self.getManifestUri("datasets/TestSubmission-testrdf/") + owl = "http://www.w3.org/2002/07/owl#" + stype = URIRef(oxds+"Grouping") + self.assertEqual(len(rdfgraph),21,'Graph length %i' %len(rdfgraph)) + self.failUnless((subj,RDF.type,stype) in rdfgraph, 'Testing submission type: '+subj+", "+stype) + self.failUnless((subj,URIRef(owl+"sameAs"),URIRef("http://example.org/testrdf/")) in rdfgraph, 'owl:sameAs') + self.failUnless((subj,URIRef(dcterms+"modified"),None) in rdfgraph, 'dcterms:modified') + self.failUnless((subj,URIRef(dcterms+"isVersionOf"),None) in rdfgraph, 'dcterms:isVersionOf') + self.failUnless((subj,URIRef(dcterms+"title"),"Test dataset with merged metadata") in rdfgraph, 'dcterms:title') + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"directory")) in rdfgraph, 'ore:aggregates directory') + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"directory/file1.a")) in rdfgraph, 'ore:aggregates file1.a') + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"directory/file1.b")) in rdfgraph, 'ore:aggregates file1.b') + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"directory/file2.a")) in rdfgraph, 'ore:aggregates file2.a') + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"arabic.txt")) in rdfgraph, 'ore:aggregates arabic.txt') + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"test-csv.csv")) in rdfgraph, 'ore:aggregates test-csv.csv') + self.failUnless((subj,URIRef(dcterms+"identifier"),None) in rdfgraph, 'dcterms:identifier') + self.failUnless((subj,URIRef(dcterms+"mediator"),None) in rdfgraph, 'dcterms:mediator') + self.failUnless((subj,URIRef(dcterms+"rights"),None) in rdfgraph, 'dcterms:rights') + self.failUnless((subj,URIRef(dcterms+"license"),None) in rdfgraph, 'dcterms:license') + self.failUnless((subj,URIRef(dcterms+"publisher"),None) in rdfgraph, 'dcterms:publisher') + self.failUnless((subj,URIRef(oxds+"isEmbargoed"),'True') in rdfgraph, 'oxds:isEmbargoed') + self.failUnless((subj,URIRef(oxds+"embargoedUntil"),None) in rdfgraph, 'oxds:embargoedUntil') + self.failUnless((subj,URIRef(dcterms+"created"),None) in rdfgraph, 'dcterms:created') + self.failUnless((subj,URIRef(oxds+"currentVersion"),'1') in rdfgraph, 'oxds:currentVersion') + #Get the file arabic.txt + (resp, arabic_data) = self.doHTTP_GET( + resource="datasets/TestSubmission-testrdf/arabic.txt", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + #self.failUnless((subj,URIRef(dcterms+"description"),Literal(arabic_data)) in rdfgraph, 'dcterms:description') + # Delete the dataset TestSubmission-testrdf + resp = self.doHTTP_DELETE( + resource="datasets/TestSubmission-testrdf", + expect_status="*", expect_reason="*") + os.remove('response.xml') + + + + def testMetadataInDirectoryMerging(self): + """POST zipfile to /silo_name/items/dataset_name. Unpack zip file to a dataset. + manifest.rdf located within a folder in unpacked zipfile is munged with datsets existing manifest""" + + # Create a new dataset, check response + self.createSubmissionDataset() + # Submit ZIP file testdata/testrdf2.zip, check response + fields = [] + zipdata = open("testdata/testrdf2.zip").read() + files = \ + [ ("file", "testrdf2.zip", zipdata, "application/zip") + ] + (reqtype, reqdata) = SparqlQueryTestCase.encode_multipart_formdata(fields, files) + (resp,respdata) = self.doHTTP_POST( + reqdata, reqtype, + resource="datasets/TestSubmission/", + expect_status=201, expect_reason="Created") + LHobtained = resp.getheader('Content-Location', None) + LHexpected = "%sdatasets/TestSubmission/testrdf2.zip"%self._endpointpath + self.assertEquals(LHobtained, LHexpected, 'Content-Location not correct') + # Unpack ZIP file into a new dataset, check response + fields = \ + [ ("filename", "testrdf2.zip") + ] + files = [] + (reqtype, reqdata) = SparqlQueryTestCase.encode_multipart_formdata(fields, files) + (resp,respdata) = self.doHTTP_POST( + reqdata, reqtype, + resource="items/TestSubmission", + expect_status=201, expect_reason="Created") + LHobtained = resp.getheader('Content-Location', None) + LHexpected = "%sdatasets/TestSubmission-testrdf2"%self._endpointpath + self.assertEquals(LHobtained, LHexpected, 'Content-Location not correct') + # Access parent dataset, check response + (resp, respdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/json") + # Access and check list of contents in parent dataset - TestSubmission + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + subj = URIRef(self.getManifestUri("datasets/TestSubmission")) + base = self.getManifestUri("datasets/TestSubmission/") + dcterms = "http://purl.org/dc/terms/" + ore = "http://www.openarchives.org/ore/terms/" + oxds = "http://vocab.ox.ac.uk/dataset/schema#" + stype = URIRef(oxds+"DataSet") + self.assertEqual(len(rdfgraph),13,'Graph length %i' %len(rdfgraph)) + self.failUnless((subj,RDF.type,stype) in rdfgraph, 'Testing submission type: '+subj+", "+stype) + self.failUnless((subj,URIRef(dcterms+"created"),None) in rdfgraph, 'dcterms:created') + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"testrdf2.zip")) in rdfgraph, 'ore:aggregates testrdf2.zip') + self.failUnless((URIRef(base+"testrdf2.zip"),URIRef(dcterms+"hasVersion"),None) in rdfgraph, 'dcterms:hasVersion') + self.failUnless((subj,URIRef(dcterms+"identifier"),None) in rdfgraph, 'dcterms:identifier') + self.failUnless((subj,URIRef(dcterms+"mediator"),None) in rdfgraph, 'dcterms:mediator') + self.failUnless((subj,URIRef(dcterms+"rights"),None) in rdfgraph, 'dcterms:rights') + self.failUnless((subj,URIRef(dcterms+"license"),None) in rdfgraph, 'dcterms:license') + self.failUnless((subj,URIRef(dcterms+"publisher"),None) in rdfgraph, 'dcterms:publisher') + self.failUnless((subj,URIRef(oxds+"isEmbargoed"),None) in rdfgraph, 'oxds:isEmbargoed') + self.failUnless((subj,URIRef(oxds+"embargoedUntil"),None) in rdfgraph, 'oxds:embargoedUntil') + self.failUnless((subj,URIRef(oxds+"currentVersion"),'1') in rdfgraph, 'oxds:currentVersion') + self.failUnless((subj,URIRef(dcterms+"modified"),None) in rdfgraph, 'dcterms:modified') + # Access and check list of contents in child dataset - TestSubmission-testrdf + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission-testrdf2", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + subj = URIRef(self.getManifestUri("datasets/TestSubmission-testrdf2")) + base = self.getManifestUri("datasets/TestSubmission-testrdf2/") + owl = "http://www.w3.org/2002/07/owl#" + stype = URIRef(oxds+"Grouping") + self.assertEqual(len(rdfgraph),20, 'Graph length %i' %len(rdfgraph)) + self.failUnless((subj,RDF.type,stype) in rdfgraph, 'Testing submission type: '+subj+", "+stype) + self.failUnless((subj,URIRef(owl+"sameAs"),URIRef("http://example.org/testrdf/")) in rdfgraph, 'owl:sameAs') + self.failUnless((subj,URIRef(dcterms+"modified"),None) in rdfgraph, 'dcterms:modified') + self.failUnless((subj,URIRef(dcterms+"isVersionOf"),None) in rdfgraph, 'dcterms:isVersionOf') + self.failUnless((subj,URIRef(dcterms+"title"),"Test dataset with merged metadata") in rdfgraph, 'dcterms:title') + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"testrdf2")) in rdfgraph, 'ore:aggregates testrdf2') + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"testrdf2/directory")) in rdfgraph, 'ore:aggregates directory') + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"testrdf2/directory/file1.a")) in rdfgraph, 'ore:aggregates file1.a') + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"testrdf2/directory/file1.b")) in rdfgraph, 'ore:aggregates file1.b') + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"testrdf2/directory/file2.a")) in rdfgraph, 'ore:aggregates file2.a') + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"testrdf2/test-csv.csv")) in rdfgraph, 'ore:aggregates test-csv.csv') + self.failUnless((subj,URIRef(dcterms+"identifier"),None) in rdfgraph, 'dcterms:identifier') + self.failUnless((subj,URIRef(dcterms+"mediator"),None) in rdfgraph, 'dcterms:mediator') + self.failUnless((subj,URIRef(dcterms+"rights"),None) in rdfgraph, 'dcterms:rights') + self.failUnless((subj,URIRef(dcterms+"license"),None) in rdfgraph, 'dcterms:license') + self.failUnless((subj,URIRef(dcterms+"publisher"),None) in rdfgraph, 'dcterms:publisher') + self.failUnless((subj,URIRef(oxds+"isEmbargoed"),None) in rdfgraph, 'oxds:isEmbargoed') + self.failUnless((subj,URIRef(oxds+"embargoedUntil"),None) in rdfgraph, 'oxds:embargoedUntil') + self.failUnless((subj,URIRef(dcterms+"created"),None) in rdfgraph, 'dcterms:created') + self.failUnless((subj,URIRef(oxds+"currentVersion"),'1') in rdfgraph, 'oxds:currentVersion') + # Delete the dataset TestSubmission-testrdf2 + resp = self.doHTTP_DELETE( + resource="datasets/TestSubmission-testrdf2", + expect_status="*", expect_reason="*") + + + + def testReferencedMetadataMerging(self): + """POST zipfile to /silo_name/items/dataset_name. Unpack zip file to a dataset. + manifest.rdf located within the unpacked zipfile is munged with datsets existing manifest. + Also, manifest.rdf in the unpacked zipfile, references other metadata files, using the property rdfs:seeAlso. + The metadata from these files are munged.""" + + # Create a new dataset, check response + self.createSubmissionDataset() + # Submit ZIP file testdata/testrdf3.zip, check response + fields = [] + zipdata = open("testdata/testrdf3.zip").read() + files = \ + [ ("file", "testrdf3.zip", zipdata, "application/zip") + ] + (reqtype, reqdata) = SparqlQueryTestCase.encode_multipart_formdata(fields, files) + (resp,respdata) = self.doHTTP_POST( + reqdata, reqtype, + resource="datasets/TestSubmission/", + expect_status=201, expect_reason="Created") + LHobtained = resp.getheader('Content-Location', None) + LHexpected = "%sdatasets/TestSubmission/testrdf3.zip"%self._endpointpath + self.assertEquals(LHobtained, LHexpected, 'Content-Location not correct') + # Unpack ZIP file into a new dataset, check response + fields = \ + [ ("filename", "testrdf3.zip") + ] + files = [] + (reqtype, reqdata) = SparqlQueryTestCase.encode_multipart_formdata(fields, files) + (resp,respdata) = self.doHTTP_POST( + reqdata, reqtype, + resource="items/TestSubmission", + expect_status=201, expect_reason="Created") + LHobtained = resp.getheader('Content-Location', None) + LHexpected = "%sdatasets/TestSubmission-testrdf3"%self._endpointpath + self.assertEquals(LHobtained, LHexpected, 'Content-Location not correct') + # Access parent dataset, check response + (resp, respdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/json") + # Access and check list of contents in parent dataset - TestSubmission + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + subj = URIRef(self.getManifestUri("datasets/TestSubmission")) + base = self.getManifestUri("datasets/TestSubmission/") + dcterms = "http://purl.org/dc/terms/" + ore = "http://www.openarchives.org/ore/terms/" + oxds = "http://vocab.ox.ac.uk/dataset/schema#" + owl = "http://www.w3.org/2002/07/owl#" + dc = "http://purl.org/dc/elements/1.1/" + stype = URIRef(oxds+"DataSet") + self.assertEqual(len(rdfgraph),13,'Graph length %i' %len(rdfgraph)) + self.failUnless((subj,RDF.type,stype) in rdfgraph, 'Testing submission type: '+subj+", "+stype) + self.failUnless((subj,URIRef(dcterms+"created"),None) in rdfgraph, 'dcterms:created') + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"testrdf3.zip")) in rdfgraph, 'ore:aggregates testrdf3.zip') + self.failUnless((URIRef(base+"testrdf3.zip"),URIRef(dcterms+"hasVersion"),None) in rdfgraph, 'dcterms:hasVersion') + self.failUnless((subj,URIRef(dcterms+"identifier"),None) in rdfgraph, 'dcterms:identifier') + self.failUnless((subj,URIRef(dcterms+"mediator"),None) in rdfgraph, 'dcterms:mediator') + self.failUnless((subj,URIRef(dcterms+"rights"),None) in rdfgraph, 'dcterms:rights') + self.failUnless((subj,URIRef(dcterms+"license"),None) in rdfgraph, 'dcterms:license') + self.failUnless((subj,URIRef(dcterms+"publisher"),None) in rdfgraph, 'dcterms:publisher') + self.failUnless((subj,URIRef(oxds+"isEmbargoed"),None) in rdfgraph, 'oxds:isEmbargoed') + self.failUnless((subj,URIRef(oxds+"embargoedUntil"),None) in rdfgraph, 'oxds:embargoedUntil') + self.failUnless((subj,URIRef(oxds+"currentVersion"),'1') in rdfgraph, 'oxds:currentVersion') + self.failUnless((subj,URIRef(dcterms+"modified"),None) in rdfgraph, 'dcterms:modified') + # Access and check list of contents in child dataset - TestSubmission-testrdf3 + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission-testrdf3", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + fr = open('response.xml', 'w') + fr.write(rdfdata) + fr.close() + rdfgraph = Graph() + rdfgraph.parse('response.xml', format='xml') + subj = URIRef(self.getManifestUri("datasets/TestSubmission-testrdf3")) + subj2 = URIRef(self.getManifestUri("datasets/TestSubmission-testrdf3/testrdf3/directory/hebrew.txt")) + base = self.getManifestUri("datasets/TestSubmission-testrdf3/") + stype = URIRef(oxds+"Grouping") + stype2 = URIRef(oxds+"item") + self.assertEqual(len(rdfgraph),32,'Graph length %i' %len(rdfgraph)) + self.failUnless((subj,RDF.type,stype) in rdfgraph, 'Testing submission type: '+subj+", "+stype) + self.failUnless((subj,URIRef(dcterms+"modified"),None) in rdfgraph, 'dcterms:modified') + self.failUnless((subj,URIRef(dcterms+"isVersionOf"),None) in rdfgraph, 'dcterms:isVersionOf') + self.failUnless((subj,URIRef(dcterms+"title"),"Test dataset 3 with updated and merged metadata") in rdfgraph, 'dcterms:title') + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"testrdf3")) in rdfgraph, 'ore:aggregates testrdf3') + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"testrdf3/directory")) in rdfgraph, 'ore:aggregates directory') + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"testrdf3/directory/file1.a")) in rdfgraph, 'ore:aggregates file1.a') + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"testrdf3/directory/file1.b")) in rdfgraph, 'ore:aggregates file1.b') + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"testrdf3/directory/file2.a")) in rdfgraph, 'ore:aggregates file2.a') + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"testrdf3/directory/1a.rdf")) in rdfgraph, 'ore:aggregates 1a.rdf') + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"testrdf3/directory/1b.rdf")) in rdfgraph, 'ore:aggregates 1b.rdf') + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"testrdf3/directory/2a.rdf")) in rdfgraph, 'ore:aggregates 2a.rdf') + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"testrdf3/directory/3a.rdf")) in rdfgraph, 'ore:aggregates 3a.rdf') + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"testrdf3/directory/hebrew.txt")) in rdfgraph, 'ore:aggregates hebrew.txt') + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"testrdf3/test-csv.csv")) in rdfgraph, 'ore:aggregates test-csv.csv') + self.failUnless((subj,URIRef(dcterms+"identifier"),None) in rdfgraph, 'dcterms:identifier') + self.failUnless((subj,URIRef(dcterms+"mediator"),None) in rdfgraph, 'dcterms:mediator') + self.failUnless((subj,URIRef(dcterms+"rights"),None) in rdfgraph, 'dcterms:rights') + self.failUnless((subj,URIRef(dcterms+"license"),None) in rdfgraph, 'dcterms:license') + self.failUnless((subj,URIRef(dcterms+"publisher"),None) in rdfgraph, 'dcterms:publisher') + self.failUnless((subj,URIRef(oxds+"isEmbargoed"),'True') in rdfgraph, 'oxds:isEmbargoed') + self.failUnless((subj,URIRef(oxds+"embargoedUntil"),None) in rdfgraph, 'oxds:embargoedUntil') + self.failUnless((subj,URIRef(dcterms+"created"),None) in rdfgraph, 'dcterms:created') + self.failUnless((subj,URIRef(oxds+"currentVersion"),'1') in rdfgraph, 'oxds:currentVersion') + self.failUnless((subj,URIRef(owl+"sameAs"),URIRef("http://example.org/2aFiles/")) in rdfgraph, 'owl:sameAs') + self.failUnless((subj,URIRef(dc+"description"),"file1.a is another file") in rdfgraph, 'dc:description') + self.failUnless((subj,URIRef(dc+"description"),"file1.b is another file") in rdfgraph, 'dc:description') + self.failUnless((subj,URIRef(dc+"description"),"This is a archived test item 2a ") in rdfgraph, 'dc:description') + self.failUnless((subj2,RDF.type,stype2) in rdfgraph, 'Testing submission type: '+subj2+", "+stype2) + self.failUnless((subj2,URIRef(dcterms+"title"),"Hebrew text") in rdfgraph, 'dcterms:title') + self.failUnless((subj2,URIRef(dcterms+"source"),"http://genizah.bodleian.ox.ac.uk/") in rdfgraph, 'dcterms:source') + #Get the file hebrew.txt + (resp, hebrew_data) = self.doHTTP_GET( + resource="datasets/TestSubmission-testrdf3/testrdf3/directory/hebrew.txt", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + #self.failUnless((subj2,RDF.value,Literal(hebrew_data)) in rdfgraph, 'rdf:value') + # Delete the dataset TestSubmission-testrdf3 + resp = self.doHTTP_DELETE( + resource="datasets/TestSubmission-testrdf3", + expect_status="*", expect_reason="*") + + + + def testReferencedMetadataMerging2(self): + """POST zipfile to /silo_name/items/dataset_name. Unpack zip file to a dataset. + manifest.rdf located within the unpacked zipfile is munged with datsets existing manifest. + Also, manifest.rdf in the unpacked zipfile, references other metadata files, using the property rdfs:seeAlso. + The metadata from these files are munged. One of the referenced files, references other files, which if they exists are also munged.""" + + # Create a new dataset, check response + self.createSubmissionDataset() + # Submit ZIP file testdata/testrdf4.zip, check response + fields = [] + zipdata = open("testdata/testrdf4.zip").read() + files = \ + [ ("file", "testrdf4.zip", zipdata, "application/zip") + ] + (reqtype, reqdata) = SparqlQueryTestCase.encode_multipart_formdata(fields, files) + (resp,respdata) = self.doHTTP_POST( + reqdata, reqtype, + resource="datasets/TestSubmission/", + expect_status=201, expect_reason="Created") + LHobtained = resp.getheader('Content-Location', None) + LHexpected = "%sdatasets/TestSubmission/testrdf4.zip"%self._endpointpath + self.assertEquals(LHobtained, LHexpected, 'Content-Location not correct') + # Unpack ZIP file into a new dataset, check response + fields = \ + [ ("filename", "testrdf4.zip") + ] + files = [] + (reqtype, reqdata) = SparqlQueryTestCase.encode_multipart_formdata(fields, files) + (resp,respdata) = self.doHTTP_POST( + reqdata, reqtype, + resource="items/TestSubmission", + expect_status=201, expect_reason="Created") + LHobtained = resp.getheader('Content-Location', None) + LHexpected = "%sdatasets/TestSubmission-testrdf4"%self._endpointpath + self.assertEquals(LHobtained, LHexpected, 'Content-Location not correct') + # Access parent dataset, check response + (resp, respdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/json") + # Access and check list of contents in parent dataset - TestSubmission + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + subj = URIRef(self.getManifestUri("datasets/TestSubmission")) + base = self.getManifestUri("datasets/TestSubmission/") + dcterms = "http://purl.org/dc/terms/" + ore = "http://www.openarchives.org/ore/terms/" + oxds = "http://vocab.ox.ac.uk/dataset/schema#" + stype = URIRef(oxds+"DataSet") + self.assertEqual(len(rdfgraph),13,'Graph length %i' %len(rdfgraph)) + self.failUnless((subj,RDF.type,stype) in rdfgraph, 'Testing submission type: '+subj+", "+stype) + self.failUnless((subj,URIRef(dcterms+"created"),None) in rdfgraph, 'dcterms:created') + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"testrdf4.zip")) in rdfgraph, 'ore:aggregates testrdf4.zip') + self.failUnless((URIRef(base+"testrdf4.zip"),URIRef(dcterms+"hasVersion"),None) in rdfgraph, 'dcterms:hasVersion') + self.failUnless((subj,URIRef(dcterms+"identifier"),None) in rdfgraph, 'dcterms:identifier') + self.failUnless((subj,URIRef(dcterms+"mediator"),None) in rdfgraph, 'dcterms:mediator') + self.failUnless((subj,URIRef(dcterms+"rights"),None) in rdfgraph, 'dcterms:rights') + self.failUnless((subj,URIRef(dcterms+"license"),None) in rdfgraph, 'dcterms:license') + self.failUnless((subj,URIRef(dcterms+"publisher"),None) in rdfgraph, 'dcterms:publisher') + self.failUnless((subj,URIRef(oxds+"isEmbargoed"),None) in rdfgraph, 'oxds:isEmbargoed') + self.failUnless((subj,URIRef(oxds+"embargoedUntil"),None) in rdfgraph, 'oxds:embargoedUntil') + self.failUnless((subj,URIRef(oxds+"currentVersion"),'1') in rdfgraph, 'oxds:currentVersion') + self.failUnless((subj,URIRef(dcterms+"modified"),None) in rdfgraph, 'dcterms:modified') + # Access and check list of contents in child dataset - TestSubmission-testrdf4 + (resp, rdfdata) = self.doHTTP_GET( + resource="datasets/TestSubmission-testrdf4", + expect_status=200, expect_reason="OK", expect_type="application/rdf+xml") + rdfgraph = Graph() + rdfstream = StringIO(rdfdata) + rdfgraph.parse(rdfstream) + subj = URIRef(self.getManifestUri("datasets/TestSubmission-testrdf4")) + subj2 = URIRef(self.getManifestUri("datasets/TestSubmission-testrdf4/testrdf4/directory/file1.a")) + subj3 = URIRef(self.getManifestUri("datasets/TestSubmission-testrdf4/testrdf4/directory/file1.b")) + base = self.getManifestUri("datasets/TestSubmission-testrdf4/") + owl = "http://www.w3.org/2002/07/owl#" + dc = "http://purl.org/dc/elements/1.1/" + stype = URIRef(oxds+"Grouping") + stype2 = URIRef(oxds+"item") + self.assertEqual(len(rdfgraph),29,'Graph length %i' %len(rdfgraph)) + self.failUnless((subj,RDF.type,stype) in rdfgraph, 'Testing submission type: '+subj+", "+stype) + self.failUnless((subj,URIRef(dcterms+"modified"),None) in rdfgraph, 'dcterms:modified') + self.failUnless((subj,URIRef(dcterms+"isVersionOf"),None) in rdfgraph, 'dcterms:isVersionOf') + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"testrdf4")) in rdfgraph, 'ore:aggregates testrdf4') + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"testrdf4/directory")) in rdfgraph, 'ore:aggregates directory') + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"testrdf4/directory/file1.a")) in rdfgraph, 'ore:aggregates file1.a') + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"testrdf4/directory/file1.b")) in rdfgraph, 'ore:aggregates file1.b') + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"testrdf4/directory/file2.a")) in rdfgraph, 'ore:aggregates file2.a') + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"testrdf4/directory/1a.rdf")) in rdfgraph, 'ore:aggregates 1a.rdf') + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"testrdf4/directory/1b.rdf")) in rdfgraph, 'ore:aggregates 1b.rdf') + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"testrdf4/directory/2a.rdf")) in rdfgraph, 'ore:aggregates 2a.rdf') + self.failUnless((subj,URIRef(ore+"aggregates"),URIRef(base+"testrdf4/test-csv.csv")) in rdfgraph, 'ore:aggregates test-csv.csv') + self.failUnless((subj,URIRef(dcterms+"identifier"),None) in rdfgraph, 'dcterms:identifier') + self.failUnless((subj,URIRef(dcterms+"mediator"),None) in rdfgraph, 'dcterms:mediator') + self.failUnless((subj,URIRef(dcterms+"rights"),None) in rdfgraph, 'dcterms:rights') + self.failUnless((subj,URIRef(dcterms+"license"),None) in rdfgraph, 'dcterms:license') + self.failUnless((subj,URIRef(dcterms+"publisher"),None) in rdfgraph, 'dcterms:publisher') + self.failUnless((subj,URIRef(oxds+"isEmbargoed"),None) in rdfgraph, 'oxds:isEmbargoed') + self.failUnless((subj,URIRef(oxds+"embargoedUntil"),None) in rdfgraph, 'oxds:embargoedUntil') + self.failUnless((subj,URIRef(dcterms+"created"),None) in rdfgraph, 'dcterms:created') + self.failUnless((subj,URIRef(oxds+"currentVersion"),'1') in rdfgraph, 'oxds:currentVersion') + self.failUnless((subj,URIRef(dc+"description"),"This is a archived test item 2a ") in rdfgraph, 'dc:description') + #self.failUnless((subj,URIRef(dcterms+"title"),"Test dataset 4 with updated and merged metadata") in rdfgraph, 'dcterms:title') + self.failUnless((subj,URIRef(dcterms+"title"),"Test item 2a") in rdfgraph, 'dcterms:title') + + #self.failUnless((subj,URIRef(owl+"sameAs"),URIRef("2aFiles")) in rdfgraph, 'dcterms:title') + self.failUnless((subj2,RDF.type,stype2) in rdfgraph, 'Testing submission type: %s, %s'%(str(subj2), str(stype2))) + self.failUnless((subj2,URIRef(dc+"description"),"This is a archived test item 1a ") in rdfgraph, 'dc:description') + self.failUnless((subj2,URIRef(dcterms+"title"),"Test item 1a") in rdfgraph, 'dcterms:title') + + self.failUnless((subj3,RDF.type,stype2) in rdfgraph, 'Testing submission type: '+subj3+", "+stype2) + self.failUnless((subj3,URIRef(dc+"description"),"This is test item 1b of type file") in rdfgraph, 'dc:description') + self.failUnless((subj3,URIRef(dcterms+"title"),"Test item 1b") in rdfgraph, 'dcterms:title') + + # Delete the dataset TestSubmission-testrdf4 + resp = self.doHTTP_DELETE( + resource="datasets/TestSubmission-testrdf4", + expect_status="*", expect_reason="*") + + # Delete the dataset TestSubmission + resp = self.doHTTP_DELETE( + resource="datasets/TestSubmission", + expect_status="*", expect_reason="*") + + # Sentinel/placeholder tests + + def testUnits(self): + assert (True) + + def testComponents(self): + assert (True) + + def testIntegration(self): + assert (True) + + def testPending(self): + #Need to have performance tests and analyse performance + #Need to set the permission of file being uploaded + #assert (False), "Pending tests follow" + assert (True) + +# Assemble test suite + +def getTestSuite(select="unit"): + """ + Get test suite + + select is one of the following: + "unit" return suite of unit tests only + "component" return suite of unit and component tests + "all" return suite of unit, component and integration tests + "pending" return suite of pending tests + name a single named test to be run + """ + testdict = { + "unit": + [ "testUnits" + , "test01CreateSilo" + , "testListSilos" + , "testListDatasets" + , "testSiloState" + , "testDatasetNotPresent" + , "testDatasetCreation" + , "testDatasetCreation2" + , "testDatasetRecreation" + , "testDeleteDataset" + , "testDatasetNaming" + , "testDatasetStateInformation" + , "testFileUpload" + , "testFileDelete" + , "testFileUpdate" + #, "testGetDatasetByVersionByURI" + , "testGetDatasetByVersionByParameter" + , "testPostMetadataFile" + , "testMetadataFileUpdate" + , "testMetadataFileDelete" + , "testPutCreateFile" + , "testPutUpdateFile" + , "testPutMetadataFile" + , "testUnicodeMetadataFileUpdate" + , "testChangeEmbargo" + , "testFileUnpack" + , "testSymlinkFileUnpack" + , "testMetadataMerging" + , "testMetadataInDirectoryMerging" + , "testFileUploadToUnpackedDataset" + , "testUpdateUnpackedDataset" + , "testReferencedMetadataMerging" + , "testReferencedMetadataMerging2" + ], + "component": + [ "testComponents" + ], + "integration": + [ "testIntegration" + ], + "pending": + [ "testPending" + ] + } + return TestUtils.getTestSuite(TestSubmission, testdict, select=select) + +if __name__ == "__main__": + TestUtils.runTests("TestSubmission.log", getTestSuite, sys.argv) + +# End. diff --git a/rdfdatabank/tests/__init__.py b/rdfdatabank/tests/__init__.py index d51f6f2..e69de29 100644 --- a/rdfdatabank/tests/__init__.py +++ b/rdfdatabank/tests/__init__.py @@ -1,36 +0,0 @@ -"""Pylons application test package - -This package assumes the Pylons environment is already loaded, such as -when this script is imported from the `nosetests --with-pylons=test.ini` -command. - -This module initializes the application via ``websetup`` (`paster -setup-app`) and provides the base testing objects. -""" -from unittest import TestCase - -from paste.deploy import loadapp -from paste.script.appinstall import SetupCommand -from pylons import config, url -from routes.util import URLGenerator -from webtest import TestApp - -import pylons.test - -__all__ = ['environ', 'url', 'TestController'] - -# Invoke websetup with the current config file -SetupCommand('setup-app').run([config['__file__']]) - -environ = {} - -class TestController(TestCase): - - def __init__(self, *args, **kwargs): - if pylons.test.pylonsapp: - wsgiapp = pylons.test.pylonsapp - else: - wsgiapp = loadapp('config:%s' % config['__file__']) - self.app = TestApp(wsgiapp) - url._push_object(URLGenerator(config['routes.map'], environ)) - TestCase.__init__(self, *args, **kwargs) diff --git a/rdfdatabank/tests/functional/test_objects.py b/rdfdatabank/tests/functional/test_objects.py deleted file mode 100644 index 50f357c..0000000 --- a/rdfdatabank/tests/functional/test_objects.py +++ /dev/null @@ -1,7 +0,0 @@ -from rdfdatabank.tests import * - -class TestObjectsController(TestController): - - def test_index(self): - response = self.app.get(url(controller='objects', action='index')) - # Test response... diff --git a/rdfdatabank/tests/functional/test_packages.py b/rdfdatabank/tests/functional/test_packages.py deleted file mode 100644 index 432ad20..0000000 --- a/rdfdatabank/tests/functional/test_packages.py +++ /dev/null @@ -1,7 +0,0 @@ -from rdfdatabank.tests import * - -class TestPackagesController(TestController): - - def test_index(self): - response = self.app.get(url(controller='packages', action='index')) - # Test response... diff --git a/rdfdatabank/tests/pylons_init.py b/rdfdatabank/tests/pylons_init.py new file mode 100644 index 0000000..d51f6f2 --- /dev/null +++ b/rdfdatabank/tests/pylons_init.py @@ -0,0 +1,36 @@ +"""Pylons application test package + +This package assumes the Pylons environment is already loaded, such as +when this script is imported from the `nosetests --with-pylons=test.ini` +command. + +This module initializes the application via ``websetup`` (`paster +setup-app`) and provides the base testing objects. +""" +from unittest import TestCase + +from paste.deploy import loadapp +from paste.script.appinstall import SetupCommand +from pylons import config, url +from routes.util import URLGenerator +from webtest import TestApp + +import pylons.test + +__all__ = ['environ', 'url', 'TestController'] + +# Invoke websetup with the current config file +SetupCommand('setup-app').run([config['__file__']]) + +environ = {} + +class TestController(TestCase): + + def __init__(self, *args, **kwargs): + if pylons.test.pylonsapp: + wsgiapp = pylons.test.pylonsapp + else: + wsgiapp = loadapp('config:%s' % config['__file__']) + self.app = TestApp(wsgiapp) + url._push_object(URLGenerator(config['routes.map'], environ)) + TestCase.__init__(self, *args, **kwargs) diff --git a/rdfdatabank/tests/testdata/images.zip b/rdfdatabank/tests/testdata/images.zip new file mode 100755 index 0000000..9f40c71 Binary files /dev/null and b/rdfdatabank/tests/testdata/images.zip differ diff --git a/rdfdatabank/tests/testdata/manifest-admin.rdf b/rdfdatabank/tests/testdata/manifest-admin.rdf new file mode 100644 index 0000000..4e2eb7d --- /dev/null +++ b/rdfdatabank/tests/testdata/manifest-admin.rdf @@ -0,0 +1,9 @@ + + + Changes made by admin + + diff --git a/rdfdatabank/tests/testdata/manifest-general.rdf b/rdfdatabank/tests/testdata/manifest-general.rdf new file mode 100644 index 0000000..760a88f --- /dev/null +++ b/rdfdatabank/tests/testdata/manifest-general.rdf @@ -0,0 +1,9 @@ + + + Changes made by general user + + diff --git a/rdfdatabank/tests/testdata/manifest-manager.rdf b/rdfdatabank/tests/testdata/manifest-manager.rdf new file mode 100644 index 0000000..ea0f8ee --- /dev/null +++ b/rdfdatabank/tests/testdata/manifest-manager.rdf @@ -0,0 +1,9 @@ + + + Changes made by manager + + diff --git a/rdfdatabank/tests/testdata/manifest-submitter.rdf b/rdfdatabank/tests/testdata/manifest-submitter.rdf new file mode 100644 index 0000000..90a38c1 --- /dev/null +++ b/rdfdatabank/tests/testdata/manifest-submitter.rdf @@ -0,0 +1,9 @@ + + + Changes made by submitter + + diff --git a/rdfdatabank/tests/testdata/manifest.rdf b/rdfdatabank/tests/testdata/manifest.rdf new file mode 100644 index 0000000..a066ffb --- /dev/null +++ b/rdfdatabank/tests/testdata/manifest.rdf @@ -0,0 +1,10 @@ + + + Test dataset with merged metadata + + diff --git a/rdfdatabank/tests/testdata/manifest2.rdf b/rdfdatabank/tests/testdata/manifest2.rdf new file mode 100644 index 0000000..618dee5 --- /dev/null +++ b/rdfdatabank/tests/testdata/manifest2.rdf @@ -0,0 +1,10 @@ + + + Test dataset with updated and merged metadata + + diff --git a/rdfdatabank/tests/testdata/rdfdatabank.zip b/rdfdatabank/tests/testdata/rdfdatabank.zip new file mode 100644 index 0000000..cfa54ea Binary files /dev/null and b/rdfdatabank/tests/testdata/rdfdatabank.zip differ diff --git a/rdfdatabank/tests/testdata/testdir.zip b/rdfdatabank/tests/testdata/testdir.zip new file mode 100644 index 0000000..cfa54ea Binary files /dev/null and b/rdfdatabank/tests/testdata/testdir.zip differ diff --git a/rdfdatabank/tests/testdata/testdir/directory/file1.a b/rdfdatabank/tests/testdata/testdir/directory/file1.a new file mode 100644 index 0000000..dc86f56 --- /dev/null +++ b/rdfdatabank/tests/testdata/testdir/directory/file1.a @@ -0,0 +1 @@ +file1.a \ No newline at end of file diff --git a/rdfdatabank/tests/testdata/testdir/directory/file1.b b/rdfdatabank/tests/testdata/testdir/directory/file1.b new file mode 100644 index 0000000..e8e8839 --- /dev/null +++ b/rdfdatabank/tests/testdata/testdir/directory/file1.b @@ -0,0 +1 @@ +file1.b \ No newline at end of file diff --git a/rdfdatabank/tests/testdata/testdir/directory/file2.a b/rdfdatabank/tests/testdata/testdir/directory/file2.a new file mode 100644 index 0000000..fab6798 --- /dev/null +++ b/rdfdatabank/tests/testdata/testdir/directory/file2.a @@ -0,0 +1 @@ +file2.a \ No newline at end of file diff --git a/rdfdatabank/tests/testdata/testdir/test-csv.csv b/rdfdatabank/tests/testdata/testdir/test-csv.csv new file mode 100644 index 0000000..d9d1268 --- /dev/null +++ b/rdfdatabank/tests/testdata/testdir/test-csv.csv @@ -0,0 +1,10 @@ +rowlabel,col1,col2,col3,col4 +row1,a1,b1,c1,d1 + row2 , a2 , b2 , c2 , d2 + row3 , a3 3a , b3 3b , c3 3c , d3 3d + ' row4 ' , ' a4 ' , ' b4 ' , ' c4 ' , ' d4 ' + " row5 " , " a5 " , " b5 " , " c5 " , " d5 " + 'row6' , 'a6,6a' , 'b6,6b' , 'c6,6c' , 'd6,6d' + 'row7' , 'a7''7a' , 'b7''7b' , 'c7''7c' , 'd7''7d' + 'row8' , 'a8'', 8a' , 'b8'', 8b' , 'c8'', 8c' , 'd8'', 8d' +End. \ No newline at end of file diff --git a/rdfdatabank/tests/testdata/testdir2.zip b/rdfdatabank/tests/testdata/testdir2.zip new file mode 100644 index 0000000..23d504e Binary files /dev/null and b/rdfdatabank/tests/testdata/testdir2.zip differ diff --git a/rdfdatabank/tests/testdata/testdir2/directory1/file1.a b/rdfdatabank/tests/testdata/testdir2/directory1/file1.a new file mode 100644 index 0000000..dc86f56 --- /dev/null +++ b/rdfdatabank/tests/testdata/testdir2/directory1/file1.a @@ -0,0 +1 @@ +file1.a \ No newline at end of file diff --git a/rdfdatabank/tests/testdata/testdir2/directory1/file1.b b/rdfdatabank/tests/testdata/testdir2/directory1/file1.b new file mode 100644 index 0000000..7661d99 --- /dev/null +++ b/rdfdatabank/tests/testdata/testdir2/directory1/file1.b @@ -0,0 +1 @@ +updated file1.b diff --git a/rdfdatabank/tests/testdata/testdir2/directory1/file1.c b/rdfdatabank/tests/testdata/testdir2/directory1/file1.c new file mode 100644 index 0000000..bb6e807 --- /dev/null +++ b/rdfdatabank/tests/testdata/testdir2/directory1/file1.c @@ -0,0 +1 @@ +file1.c diff --git a/rdfdatabank/tests/testdata/testdir2/directory1/manifest.rdf b/rdfdatabank/tests/testdata/testdir2/directory1/manifest.rdf new file mode 100644 index 0000000..22c8bdc --- /dev/null +++ b/rdfdatabank/tests/testdata/testdir2/directory1/manifest.rdf @@ -0,0 +1,10 @@ + + + Test dataset with merged metadata + + diff --git a/rdfdatabank/tests/testdata/testdir2/directory2/file2.a b/rdfdatabank/tests/testdata/testdir2/directory2/file2.a new file mode 100644 index 0000000..fab6798 --- /dev/null +++ b/rdfdatabank/tests/testdata/testdir2/directory2/file2.a @@ -0,0 +1 @@ +file2.a \ No newline at end of file diff --git a/rdfdatabank/tests/testdata/testdir2/directory2/file2.b b/rdfdatabank/tests/testdata/testdir2/directory2/file2.b new file mode 100644 index 0000000..7661d99 --- /dev/null +++ b/rdfdatabank/tests/testdata/testdir2/directory2/file2.b @@ -0,0 +1 @@ +updated file1.b diff --git a/rdfdatabank/tests/testdata/testdir2/test-csv.csv b/rdfdatabank/tests/testdata/testdir2/test-csv.csv new file mode 100644 index 0000000..d9d1268 --- /dev/null +++ b/rdfdatabank/tests/testdata/testdir2/test-csv.csv @@ -0,0 +1,10 @@ +rowlabel,col1,col2,col3,col4 +row1,a1,b1,c1,d1 + row2 , a2 , b2 , c2 , d2 + row3 , a3 3a , b3 3b , c3 3c , d3 3d + ' row4 ' , ' a4 ' , ' b4 ' , ' c4 ' , ' d4 ' + " row5 " , " a5 " , " b5 " , " c5 " , " d5 " + 'row6' , 'a6,6a' , 'b6,6b' , 'c6,6c' , 'd6,6d' + 'row7' , 'a7''7a' , 'b7''7b' , 'c7''7c' , 'd7''7d' + 'row8' , 'a8'', 8a' , 'b8'', 8b' , 'c8'', 8c' , 'd8'', 8d' +End. \ No newline at end of file diff --git a/rdfdatabank/tests/testdata/testrdf.zip b/rdfdatabank/tests/testdata/testrdf.zip new file mode 100644 index 0000000..a52cdce Binary files /dev/null and b/rdfdatabank/tests/testdata/testrdf.zip differ diff --git a/rdfdatabank/tests/testdata/testrdf/arabic.txt b/rdfdatabank/tests/testdata/testrdf/arabic.txt new file mode 100644 index 0000000..5a8dd11 --- /dev/null +++ b/rdfdatabank/tests/testdata/testrdf/arabic.txt @@ -0,0 +1,6 @@ +![CDATA[

          ثمت مسايل حنين ÙÙŠ حادي عشر شهر جمادى الاخر سنه سبع وثمانين وسبعمايه بخط ألعَبد الÙَقير ألراجي عÙÙˆ رَبه يعقوب عبد اللّه بن ابى المÙرجا الاسرايلي غÙر اللّه Ù„ÙŽÙ‡ امين والحمد للّه رب العالمي +

          واذا كان البول معتدلًا ÙÙŠ قوامه مايلًا الي لون النار Ùعلي ماذا يدل اذا كان معه Ø«ÙÙ„ÙŒ راسبٌ +

          ما السبب الذي له صار البول الاحمر التانى والبول الاسود لا يكون معهما للبول قوام معتدل +

          الثÙÙ„ الراسب ÙÙŠ البول متي يكون جيدًا محمودًا ومتي يكون رديًا مذمومًا +

          اذا كان ÙÙŠ البول دم وقيح Ùعلي ما ذا يدل +

          بماذا ÙŠÙرق بين القيح الدي يخرج من الكليتين والمثانه وبين القيح الدي يخرج من الاعضآء]] \ No newline at end of file diff --git a/rdfdatabank/tests/testdata/testrdf/directory/file1.a b/rdfdatabank/tests/testdata/testrdf/directory/file1.a new file mode 100644 index 0000000..dc86f56 --- /dev/null +++ b/rdfdatabank/tests/testdata/testrdf/directory/file1.a @@ -0,0 +1 @@ +file1.a \ No newline at end of file diff --git a/rdfdatabank/tests/testdata/testrdf/directory/file1.b b/rdfdatabank/tests/testdata/testrdf/directory/file1.b new file mode 100644 index 0000000..e8e8839 --- /dev/null +++ b/rdfdatabank/tests/testdata/testrdf/directory/file1.b @@ -0,0 +1 @@ +file1.b \ No newline at end of file diff --git a/rdfdatabank/tests/testdata/testrdf/directory/file2.a b/rdfdatabank/tests/testdata/testrdf/directory/file2.a new file mode 100644 index 0000000..fab6798 --- /dev/null +++ b/rdfdatabank/tests/testdata/testrdf/directory/file2.a @@ -0,0 +1 @@ +file2.a \ No newline at end of file diff --git a/rdfdatabank/tests/testdata/testrdf/manifest.rdf b/rdfdatabank/tests/testdata/testrdf/manifest.rdf new file mode 100644 index 0000000..194f07e --- /dev/null +++ b/rdfdatabank/tests/testdata/testrdf/manifest.rdf @@ -0,0 +1,16 @@ + + + Test dataset with merged metadata + <br> <br>ثمت مسايل حنين ÙÙŠ حادي عشر شهر جمادى الاخر سنه سبع وثمانين وسبعمايه بخط ألعَبد الÙَقير ألراجي عÙÙˆ رَبه يعقوب عبد اللّه بن ابى المÙرجا الاسرايلي غÙر اللّه Ù„ÙŽÙ‡ امين والحمد للّه رب العالمي +<br> <br>واذا كان البول معتدلًا ÙÙŠ قوامه مايلًا الي لون النار Ùعلي ماذا يدل اذا كان معه Ø«ÙÙ„ÙŒ راسبٌ +<br> <br>ما السبب الذي له صار البول الاحمر التانى والبول الاسود لا يكون معهما للبول قوام معتدل +<br> <br>الثÙÙ„ الراسب ÙÙŠ البول متي يكون جيدًا محمودًا ومتي يكون رديًا مذمومًا +<br> <br>اذا كان ÙÙŠ البول دم وقيح Ùعلي ما ذا يدل +<br> <br>بماذا ÙŠÙرق بين القيح الدي يخرج من الكليتين والمثانه وبين القيح الدي يخرج من الاعضآء + + diff --git a/rdfdatabank/tests/testdata/testrdf/test-csv.csv b/rdfdatabank/tests/testdata/testrdf/test-csv.csv new file mode 100644 index 0000000..d9d1268 --- /dev/null +++ b/rdfdatabank/tests/testdata/testrdf/test-csv.csv @@ -0,0 +1,10 @@ +rowlabel,col1,col2,col3,col4 +row1,a1,b1,c1,d1 + row2 , a2 , b2 , c2 , d2 + row3 , a3 3a , b3 3b , c3 3c , d3 3d + ' row4 ' , ' a4 ' , ' b4 ' , ' c4 ' , ' d4 ' + " row5 " , " a5 " , " b5 " , " c5 " , " d5 " + 'row6' , 'a6,6a' , 'b6,6b' , 'c6,6c' , 'd6,6d' + 'row7' , 'a7''7a' , 'b7''7b' , 'c7''7c' , 'd7''7d' + 'row8' , 'a8'', 8a' , 'b8'', 8b' , 'c8'', 8c' , 'd8'', 8d' +End. \ No newline at end of file diff --git a/rdfdatabank/tests/testdata/testrdf2.zip b/rdfdatabank/tests/testdata/testrdf2.zip new file mode 100644 index 0000000..8989901 Binary files /dev/null and b/rdfdatabank/tests/testdata/testrdf2.zip differ diff --git a/rdfdatabank/tests/testdata/testrdf2/directory/file1.a b/rdfdatabank/tests/testdata/testrdf2/directory/file1.a new file mode 100644 index 0000000..dc86f56 --- /dev/null +++ b/rdfdatabank/tests/testdata/testrdf2/directory/file1.a @@ -0,0 +1 @@ +file1.a \ No newline at end of file diff --git a/rdfdatabank/tests/testdata/testrdf2/directory/file1.b b/rdfdatabank/tests/testdata/testrdf2/directory/file1.b new file mode 100644 index 0000000..e8e8839 --- /dev/null +++ b/rdfdatabank/tests/testdata/testrdf2/directory/file1.b @@ -0,0 +1 @@ +file1.b \ No newline at end of file diff --git a/rdfdatabank/tests/testdata/testrdf2/directory/file2.a b/rdfdatabank/tests/testdata/testrdf2/directory/file2.a new file mode 100644 index 0000000..fab6798 --- /dev/null +++ b/rdfdatabank/tests/testdata/testrdf2/directory/file2.a @@ -0,0 +1 @@ +file2.a \ No newline at end of file diff --git a/rdfdatabank/tests/testdata/testrdf2/manifest.rdf b/rdfdatabank/tests/testdata/testrdf2/manifest.rdf new file mode 100644 index 0000000..b08606d --- /dev/null +++ b/rdfdatabank/tests/testdata/testrdf2/manifest.rdf @@ -0,0 +1,10 @@ + + + Test dataset with merged metadata + + diff --git a/rdfdatabank/tests/testdata/testrdf2/test-csv.csv b/rdfdatabank/tests/testdata/testrdf2/test-csv.csv new file mode 100644 index 0000000..d9d1268 --- /dev/null +++ b/rdfdatabank/tests/testdata/testrdf2/test-csv.csv @@ -0,0 +1,10 @@ +rowlabel,col1,col2,col3,col4 +row1,a1,b1,c1,d1 + row2 , a2 , b2 , c2 , d2 + row3 , a3 3a , b3 3b , c3 3c , d3 3d + ' row4 ' , ' a4 ' , ' b4 ' , ' c4 ' , ' d4 ' + " row5 " , " a5 " , " b5 " , " c5 " , " d5 " + 'row6' , 'a6,6a' , 'b6,6b' , 'c6,6c' , 'd6,6d' + 'row7' , 'a7''7a' , 'b7''7b' , 'c7''7c' , 'd7''7d' + 'row8' , 'a8'', 8a' , 'b8'', 8b' , 'c8'', 8c' , 'd8'', 8d' +End. \ No newline at end of file diff --git a/rdfdatabank/tests/testdata/testrdf3.zip b/rdfdatabank/tests/testdata/testrdf3.zip new file mode 100644 index 0000000..a5b5f97 Binary files /dev/null and b/rdfdatabank/tests/testdata/testrdf3.zip differ diff --git a/rdfdatabank/tests/testdata/testrdf3/directory/1a.rdf b/rdfdatabank/tests/testdata/testrdf3/directory/1a.rdf new file mode 100644 index 0000000..6e92130 --- /dev/null +++ b/rdfdatabank/tests/testdata/testrdf3/directory/1a.rdf @@ -0,0 +1,11 @@ + + + file1.a is another file + + + diff --git a/rdfdatabank/tests/testdata/testrdf3/directory/1b.rdf b/rdfdatabank/tests/testdata/testrdf3/directory/1b.rdf new file mode 100644 index 0000000..f3f87e1 --- /dev/null +++ b/rdfdatabank/tests/testdata/testrdf3/directory/1b.rdf @@ -0,0 +1,11 @@ + + + file1.b is a text file + + + diff --git a/rdfdatabank/tests/testdata/testrdf3/directory/2a.rdf b/rdfdatabank/tests/testdata/testrdf3/directory/2a.rdf new file mode 100644 index 0000000..57f3175 --- /dev/null +++ b/rdfdatabank/tests/testdata/testrdf3/directory/2a.rdf @@ -0,0 +1,13 @@ + + + This is a archived test item 2a + + + + diff --git a/rdfdatabank/tests/testdata/testrdf3/directory/3a.rdf b/rdfdatabank/tests/testdata/testrdf3/directory/3a.rdf new file mode 100644 index 0000000..1dd4900 --- /dev/null +++ b/rdfdatabank/tests/testdata/testrdf3/directory/3a.rdf @@ -0,0 +1,17 @@ + + + + Hebrew text + ומנה××’× ×‘×™ מדר×שי קביעי ×“× ×”×¨×“×¢× ×•×“×¡×•×¨× ×¢×“×™×™×Ÿ למימר ו×ור חדש ו×̇ע̇פ̇ דר̇ +<br> <br>ריש ×ž×ª×™×‘×ª× ×‘×¡×•×¨× ×œ× ×§×‘×™×œ×• מיניה ×פילו בחייה +<br> <br>ו×תכ××œ×”× ×¢×œ×™ קלה ×פה×× ×לנ×ס פר×ית ×ן ×כתצר גמל ×ž× ×™×§×¨×‘ מן וגוה וגוב ×ל×ומ×ן ×לכפיפה ו×לתקילה ××¢× ×™ שבועת ד××•×¨×™×ª× +<br> <br>×× ×‘×’×¤×• ×™×‘× ×‘×’×¤×• ×™×¦× ×›×œ×•Ì‡ יחידי נכנס יחידי ×™×¦× ×•×›×Ÿ ×”×•× ×ומר תמכור עמך +<br> <br>מתני̇ בנה בית חדש וקנה ×›×œ×™× ×—×“×©×™× ×ומר שהחיינו וכול̇ זו השמועה משובשת ×”×™× ×‘× ×•×¡×—×ות ×בל ×נו נסחנוה מן ×”× ×•×¡×—× ×©×‘××” מבבל ובן סידורה + http://genizah.bodleian.ox.ac.uk/ + + diff --git a/rdfdatabank/tests/testdata/testrdf3/directory/file1.a b/rdfdatabank/tests/testdata/testrdf3/directory/file1.a new file mode 100644 index 0000000..dc86f56 --- /dev/null +++ b/rdfdatabank/tests/testdata/testrdf3/directory/file1.a @@ -0,0 +1 @@ +file1.a \ No newline at end of file diff --git a/rdfdatabank/tests/testdata/testrdf3/directory/file1.b b/rdfdatabank/tests/testdata/testrdf3/directory/file1.b new file mode 100644 index 0000000..e8e8839 --- /dev/null +++ b/rdfdatabank/tests/testdata/testrdf3/directory/file1.b @@ -0,0 +1 @@ +file1.b \ No newline at end of file diff --git a/rdfdatabank/tests/testdata/testrdf3/directory/file2.a b/rdfdatabank/tests/testdata/testrdf3/directory/file2.a new file mode 100644 index 0000000..fab6798 --- /dev/null +++ b/rdfdatabank/tests/testdata/testrdf3/directory/file2.a @@ -0,0 +1 @@ +file2.a \ No newline at end of file diff --git a/rdfdatabank/tests/testdata/testrdf3/directory/hebrew.txt b/rdfdatabank/tests/testdata/testrdf3/directory/hebrew.txt new file mode 100644 index 0000000..d5bd1a0 --- /dev/null +++ b/rdfdatabank/tests/testdata/testrdf3/directory/hebrew.txt @@ -0,0 +1,5 @@ +ומנה××’× ×‘×™ מדר×שי קביעי ×“× ×”×¨×“×¢× ×•×“×¡×•×¨× ×¢×“×™×™×Ÿ למימר ו×ור חדש ו×̇ע̇פ̇ דר̇ +

          ריש ×ž×ª×™×‘×ª× ×‘×¡×•×¨× ×œ× ×§×‘×™×œ×• מיניה ×פילו בחייה +

          ו×תכ××œ×”× ×¢×œ×™ קלה ×פה×× ×לנ×ס פר×ית ×ן ×כתצר גמל ×ž× ×™×§×¨×‘ מן וגוה וגוב ×ל×ומ×ן ×לכפיפה ו×לתקילה ××¢× ×™ שבועת ד××•×¨×™×ª× +

          ×× ×‘×’×¤×• ×™×‘× ×‘×’×¤×• ×™×¦× ×›×œ×•Ì‡ יחידי נכנס יחידי ×™×¦× ×•×›×Ÿ ×”×•× ×ומר תמכור עמך +

          מתני̇ בנה בית חדש וקנה ×›×œ×™× ×—×“×©×™× ×ומר שהחיינו וכול̇ זו השמועה משובשת ×”×™× ×‘× ×•×¡×—×ות ×בל ×נו נסחנוה מן ×”× ×•×¡×—× ×©×‘××” מבבל ובן סידורה \ No newline at end of file diff --git a/rdfdatabank/tests/testdata/testrdf3/manifest.rdf b/rdfdatabank/tests/testdata/testrdf3/manifest.rdf new file mode 100644 index 0000000..8753721 --- /dev/null +++ b/rdfdatabank/tests/testdata/testrdf3/manifest.rdf @@ -0,0 +1,14 @@ + + + Test dataset 3 with updated and merged metadata + + + + + + diff --git a/rdfdatabank/tests/testdata/testrdf3/test-csv.csv b/rdfdatabank/tests/testdata/testrdf3/test-csv.csv new file mode 100644 index 0000000..d9d1268 --- /dev/null +++ b/rdfdatabank/tests/testdata/testrdf3/test-csv.csv @@ -0,0 +1,10 @@ +rowlabel,col1,col2,col3,col4 +row1,a1,b1,c1,d1 + row2 , a2 , b2 , c2 , d2 + row3 , a3 3a , b3 3b , c3 3c , d3 3d + ' row4 ' , ' a4 ' , ' b4 ' , ' c4 ' , ' d4 ' + " row5 " , " a5 " , " b5 " , " c5 " , " d5 " + 'row6' , 'a6,6a' , 'b6,6b' , 'c6,6c' , 'd6,6d' + 'row7' , 'a7''7a' , 'b7''7b' , 'c7''7c' , 'd7''7d' + 'row8' , 'a8'', 8a' , 'b8'', 8b' , 'c8'', 8c' , 'd8'', 8d' +End. \ No newline at end of file diff --git a/rdfdatabank/tests/testdata/testrdf4.zip b/rdfdatabank/tests/testdata/testrdf4.zip new file mode 100644 index 0000000..376bd34 Binary files /dev/null and b/rdfdatabank/tests/testdata/testrdf4.zip differ diff --git a/rdfdatabank/tests/testdata/testrdf4/directory/1a.rdf b/rdfdatabank/tests/testdata/testrdf4/directory/1a.rdf new file mode 100644 index 0000000..6255200 --- /dev/null +++ b/rdfdatabank/tests/testdata/testrdf4/directory/1a.rdf @@ -0,0 +1,12 @@ + + + Test item 1a + This is a archived test item 1a + + + diff --git a/rdfdatabank/tests/testdata/testrdf4/directory/1b.rdf b/rdfdatabank/tests/testdata/testrdf4/directory/1b.rdf new file mode 100644 index 0000000..4e83940 --- /dev/null +++ b/rdfdatabank/tests/testdata/testrdf4/directory/1b.rdf @@ -0,0 +1,14 @@ + + + Test item 1b + This is test item 1b of type file + + + + diff --git a/rdfdatabank/tests/testdata/testrdf4/directory/2a.rdf b/rdfdatabank/tests/testdata/testrdf4/directory/2a.rdf new file mode 100644 index 0000000..7fc9a5b --- /dev/null +++ b/rdfdatabank/tests/testdata/testrdf4/directory/2a.rdf @@ -0,0 +1,14 @@ + + + Test item 2a + This is a archived test item 2a + + + + diff --git a/rdfdatabank/tests/testdata/testrdf4/directory/file1.a b/rdfdatabank/tests/testdata/testrdf4/directory/file1.a new file mode 100644 index 0000000..dc86f56 --- /dev/null +++ b/rdfdatabank/tests/testdata/testrdf4/directory/file1.a @@ -0,0 +1 @@ +file1.a \ No newline at end of file diff --git a/rdfdatabank/tests/testdata/testrdf4/directory/file1.b b/rdfdatabank/tests/testdata/testrdf4/directory/file1.b new file mode 100644 index 0000000..e8e8839 --- /dev/null +++ b/rdfdatabank/tests/testdata/testrdf4/directory/file1.b @@ -0,0 +1 @@ +file1.b \ No newline at end of file diff --git a/rdfdatabank/tests/testdata/testrdf4/directory/file2.a b/rdfdatabank/tests/testdata/testrdf4/directory/file2.a new file mode 100644 index 0000000..fab6798 --- /dev/null +++ b/rdfdatabank/tests/testdata/testrdf4/directory/file2.a @@ -0,0 +1 @@ +file2.a \ No newline at end of file diff --git a/rdfdatabank/tests/testdata/testrdf4/manifest.rdf b/rdfdatabank/tests/testdata/testrdf4/manifest.rdf new file mode 100644 index 0000000..a4b0ac2 --- /dev/null +++ b/rdfdatabank/tests/testdata/testrdf4/manifest.rdf @@ -0,0 +1,13 @@ + + + Test dataset 4 with updated and merged metadata + + + + + diff --git a/rdfdatabank/tests/testdata/testrdf4/test-csv.csv b/rdfdatabank/tests/testdata/testrdf4/test-csv.csv new file mode 100644 index 0000000..d9d1268 --- /dev/null +++ b/rdfdatabank/tests/testdata/testrdf4/test-csv.csv @@ -0,0 +1,10 @@ +rowlabel,col1,col2,col3,col4 +row1,a1,b1,c1,d1 + row2 , a2 , b2 , c2 , d2 + row3 , a3 3a , b3 3b , c3 3c , d3 3d + ' row4 ' , ' a4 ' , ' b4 ' , ' c4 ' , ' d4 ' + " row5 " , " a5 " , " b5 " , " c5 " , " d5 " + 'row6' , 'a6,6a' , 'b6,6b' , 'c6,6c' , 'd6,6d' + 'row7' , 'a7''7a' , 'b7''7b' , 'c7''7c' , 'd7''7d' + 'row8' , 'a8'', 8a' , 'b8'', 8b' , 'c8'', 8c' , 'd8'', 8d' +End. \ No newline at end of file diff --git a/rdfdatabank/tests/testdata/unicodedata/unicode.txt b/rdfdatabank/tests/testdata/unicodedata/unicode.txt new file mode 100644 index 0000000..a3fc31b --- /dev/null +++ b/rdfdatabank/tests/testdata/unicodedata/unicode.txt @@ -0,0 +1,236 @@ +A Unicode Test Page +Source: http://www.madore.org/~david/misc/unitest/ + +General punctuation + +The convention in English is “to use double quotation marks to indicate quotation, and ‘single quotation marks’ for nested quotations.†+ +En français la convention est « d'utiliser les guillemets français doubles pour les citations, et “ les guillemets anglais doubles †ou bien ‹ les guillemets français simples › pour les citations imbriquées. » + +Auf Deutsch ist die Vereinbarung »umgekehrte zweifache Anführungszeichen für die Zitate zu benutzen, sogar ›einfache Anführungszeichen‹ für die verschachtelte Zitate«; diese Anführungszeichen „dürfen auch solche ‚englische‘ Anführungszeichen sein.“ + +The en-dash is used between numbers such as in: 1685–1750 (J. S. Bach). It is longer than the hyphen (as in “en-dashâ€, or, more properly, “enâ€dashâ€) but shorter than the em-dash, which is used — like this — as a sort of parenthesis. Neither should be confused with the horizontal bar which is used to introduce quotation in some cases. +― Like this? +― Right. + +The ellipsis is… well, it just is. +A table of (some) accents + +In the following table, characters that are missing from Unicode as of version 3.0, latest draft (i.e. that cannot be represented as a single character but must use the more general form of combining diacritics) have been replaced by X, so you can tell they are not your browser's fault. +Base Grave Acute Circumflex Tilde Diaeresis Macron Breve Ogonek Dot Double acute Caron Double grave Inverted breve +a à á â ã ä Ä Äƒ Ä… ȧ X ÇŽ È Èƒ +e è é ê ẽ ë Ä“ Ä• Ä™ Ä— X Ä› È… ȇ +i ì í î Ä© ï Ä« Ä­ į i X Ç È‰ È‹ +o ò ó ô õ ö Å Å Ç« ȯ Å‘ Ç’ È È +u ù ú û Å© ü Å« Å­ ų X ű Ç” È• È— +y ỳ ý Å· ỹ ÿ ȳ X X ẠX X X X + +Note that the three characters “LATIN SMALL LETTER A WITH DOT ABOVEâ€, “LATIN SMALL LETTER O WITH DOT ABOVE†and “LATIN SMALL LETTER Y WITH MACRON†were not present in version 2.0 of the Unicode standard. So it is quite understandable if you do not see the corresponding entries. +Combining diacritics + +The following paragraph gives a few forms formed by using combining diacritics. The equal sign means that the combined form on the left should be identical in all respects (and in particular, represented identically) to the atomic form on the right. To emphasize even more: you should not see two signs on the left of the equal sign but one, the same as on the right. + +aÌ€=à; eÌ=é; iÌ‚=î (not the same as ı̂ but may be graphically identical); õ=õ; uÌ„=Å« (whereas u¯ is two different symbols); ă=ă; ė=Ä— (also note i̇ should be essentially i); ï=ï (whereas i̇̈ has three dots on the i); aÌŠ=Ã¥ (not to be confused with a° (read “a degreesâ€) nor aËš); oÌ‹=Å‘; cÌŒ=Ä +ç=ç; bÌ£=ḅ; ḏ=Ḡ(this is supposedly different from dÌ  but may be graphically identical); eÌ­=ḙ +ǖ=ǖ=Ç– (not the same as ṻ which has the diaeresis on top of the macron); ǡ=ǡ=Ç¡ +ǭ=Ç«Ì„=Ç­ (also ǭ but the latter is not so canonical); oÌ·Ì=øÌ=Ç¿ (not so sure about this one) +Various symbols + +Here is a table of the constellations of the Zodiac, in which the first column should contain the relevant astrological symbol: +Sym. English name Latin name Latin genitive α star +♈ The Ram Aries Arietis Hamal +♉ The Bull Taurus Tauri Aldebaran +♊ The Twins Gemini Geminorum Castor +♋ The Crab Cancer Cancri Acubens +♌ The Lion Leo Leonis Regulus +â™ The Virgin Virgo Virginis Spica +♎ The Scales Libra Libræ Zumen el Genubi +â™ The Scorpion Scorpius Scorpii Antares +â™ The Archer Sagittarius Sagittarii Rubkat +♑ The Sea Goat Capricornus Capricorni Giedi +â™’ The Water Bearer Aquarius Aquarii Sadalmelik +♓ The Fishes Pisces Piscium El Rischa + +The following table should show a chessboard, with a pictorial representation of the pieces: + A B C D E F G H +8 ♜ ♞ â™ â™› ♚ ♠♞ ♜ +7 ♟ ♟ ♟ ♟ ♟ ♟ ♟ ♟ +6 +5 +4 +3 +2 â™™ â™™ â™™ â™™ â™™ â™™ â™™ â™™ +1 â™– ♘ â™— ♕ â™” â™— ♘ â™– + +Here is a snowflake: â„. +Some verses in Russian + +The following is a five-verse extract of introduction of the poem Mednyj Vsadnik; by A. S. Pushkin (in Russian): + + По оживлённым берегам + Громады Ñтройные теÑнÑÑ‚ÑÑ + Дворцов и башен; корабли + Толпой Ñо вÑех концов земли + К богатым приÑтанÑм ÑтремÑÑ‚ÑÑ; + +Here is what the above might look like if your browser supports the cyrillic block of Unicode: + + [Five verses in Russian] + +And here is a transcription of it: + + Po oživlënnym beregam + Gromady strojnye tesnâtsâ + Dvorcov i baÅ¡en; korabli + Tolpoj so vseh koncov zemli + K bogatym pristanâm stremâtsâ; + +A rough translation might be: + + Along the animated banks [of the Neva] / the shapely masses press / of palaces and towers; ships / in crowd from all corners of the Earth / rush toward its rich quays. + +Some verses in ancient Greek + +The following verses are lines 1182–1185 of the tragedy Oedipus Rex by Sophocles (in ancient Greek): + + Ἰοὺ ἰού· Ï„á½° πάντʼ ἂν á¼Î¾á½µÎºÎ¿Î¹ σαφῆ. + á½® φῶς, τελευταῖόν σε Ï€ÏοσÏλέψαιμι νῦν, + ὅστις πέφασμαι φύς τʼ ἀφʼ ὧν οὠχÏῆν, ξὺν οἷς τʼ + οὠχÏῆν á½Î¼Î¹Î»á¿¶Î½, οὕς Ï„á½³ μʼ οá½Îº ἔδει κτανών. + +Here is what the above might look like if your browser supports the greek and greek extended blocks of Unicode (note that this representation uses the wrong shape of beta on the second line, because I didn't have the right one in the font I used): + + [Four verses in Greek] + +And here is the transcription of it: + + Iou iou; ta pant' an exÄ“koi saphÄ“. + ÅŒ phÅs, teleutaion se prosblepsaimi nun, + hostis pephasmai phus t' aph' hÅn ou khrÄ“n, xun hois t' + ou khrÄ“n homilÅn, hous te m' ouk edei ktanÅn. + +A rough translation might be: + + Alas! All would become clear. / O light, may I see you for the last time, / I who was born of these of which it is a crime to be born, who live with these / which which it is a crime to live, and who killed these whom I must not kill. + +Some verses in Sanskrit + +The following is one stanza of canto â…¥ of the KumÄra-saṃbhava (“the birth of KumÄraâ€) by the great Sanskrit poet KÄlidÄsa: + + पशà¥à¤ªà¤¤à¤¿à¤°à¤ªà¤¿ तानà¥à¤¯à¤¹à¤¾à¤¨à¤¿ कृचà¥à¤›à¥à¤°à¤¾à¤¦à¥ + अगमयददà¥à¤°à¤¿à¤¸à¥à¤¤à¤¾à¤¸à¤®à¤¾à¤—मोतà¥à¤•à¤ƒ । + कमपरमवशं न विपà¥à¤°à¤•à¥à¤°à¥à¤¯à¥à¤°à¥ + विभà¥à¤®à¤ªà¤¿ तं यदमी सà¥à¤ªà¥ƒà¤¶à¤¨à¥à¤¤à¤¿ भावाः ॥ + +Here is what the above might look like if your browser supports the devanÄgarÄ« block of Unicode: + + [A stanza in Sanskrit] + +And here is the transcription of it: + + PaÅ›upatirapi tÄnyahÄni ká¹›cchrÄd + agamayadadrisutÄsamÄgamotkaḥ; + kamaparamavaÅ›aṃ na viprakuryur + vibhumapi taṃ yadamÄ« spṛśanti bhÄvÄḥ? + +A rough translation might be: + + And PaÅ›upati passed those days with hardship, / eager for union with the daughter of the mountain. / Which other powerless [creature] would they not torment, / such emotions, when they affect even the powerful [Åšiva]? + +Some Chinese + +The following are the two first lines of the Analects by Confucius: + + å­æ›°ï¼šã€Œå­¸è€Œæ™‚習之,ä¸äº¦èªªä¹Žï¼Ÿæœ‰æœ‹è‡ªé æ–¹ä¾†ï¼Œä¸äº¦æ¨‚乎? + 人ä¸çŸ¥è€Œä¸æ…,ä¸äº¦å›å­ä¹Žï¼Ÿã€ + + 有å­æ›°ï¼šã€Œå…¶ç‚ºäººä¹Ÿå­å¼Ÿï¼Œè€Œå¥½çŠ¯ä¸Šè€…,鮮矣; + ä¸å¥½çŠ¯ä¸Šï¼Œè€Œå¥½ä½œäº‚者,未之有也。å›å­å‹™æœ¬ï¼Œæœ¬ç«‹è€Œé“生。 + å­å¼Ÿä¹Ÿè€…,其為ä»ä¹‹æœ¬èˆ‡ï¼ã€ + +Here is what the above might look like if your browser supports the CJK block of Unicode: + + [Two lines in Chinese] + +And here is the transcription of it: + + ZÇ yuÄ“: “Xué ér shÄ« xí zhÄ«, bú yì yuè hÅ«? YoÇ” péng zì yÇ”an fÄnglái, bú yì lè hÅ«? Rén bù zhÄ«, ér bú yùn, bú yì jÅ«nzÇ hÅ«?†+ + YóuzÇ yuÄ“: “Qí wèi rén yÄ› xiàodì, ér hàofànshàngzhÄ›, xiÄn yÇ; bú hào fànshàng, ér hàozuòluànzhÄ›, wèi zhÄ« yóu yÄ›. JÅ«nzÇ wù bÄ›n, bÄ›n lì ér dào shÄ“ng. Xiàodì yé zhÄ›, qí wèi rén zhÄ« bén yÇ”!†+ +A rough translation might be: + + The Master [Confucius] said: “To study and to practice, it is is a joy, isn't it? When friends come from afar, it is a pleasure, isn't it? If one remains unknown and isn't hurt, isn't one an honorable man?†+ + Master You said: “Few of the men who act well filially and fraternally are also fond of offending their superiors; men who are not fond of offending their superiors, but who like to cause trouble, such do not exist. The honorable man concerns himself with the foundations. Once the foundations are established, the Way is born. Is not acting well filially and fraternally the foundation of humanity?†+ +A Tamil name + +The following is the original (Tamil) name of a famous mathematician: + + ஸà¯à®±à¯€à®©à®¿à®µà®¾à®¸ ராமானà¯à®œà®©à¯ à®à®¯à®™à¯à®•à®¾à®°à¯ + +Here is what the above might look like if your browser supports the Tamil block of Unicode (note, however, that this representation is less than optimal, since the font I used didn't have the ‘sr’ ligature; so if the first two characters are replaced by a single one which looks very different, it is probably normal): + + [A name in Tamil] + +And here is a transcription of it: + + SṟīṉivÄsa RÄmÄṉujaṉ Aiyaá¹…kÄr + +Here there can be no translation, of course, since this is a proper noun. But I note that the mathematician in question (1887–1920) is typically named “Srinivasa Ramanujan Iyengar†in English. +Some Arabic + +The following lines are the first chapter of the Qur'an (note that the text runs right to left, and should probably be aligned on the right margin): + + بÙسْم٠ٱللّٰه٠ٱلرَّحْمـَبن٠ٱلرَّحÙيم٠+ + ٱلْحَمْد٠لÙلّٰه٠رَبّ٠ٱلْعَالَمÙينَ + + ٱلرَّحْمـَبن٠ٱلرَّحÙيم٠+ + مَـالÙك٠يَوْم٠ٱلدّÙين٠+ + Ø¥Ùيَّاكَ نَعْبÙد٠وَإÙيَّاكَ نَسْتَعÙين٠+ + ٱهْدÙنَــــا ٱلصّÙرَاطَ ٱلمÙسْتَقÙيمَ + + صÙرَاطَ ٱلَّذÙينَ أَنعَمْتَ عَلَيهÙمْ غَير٠ٱلمَغضÙوب٠عَلَيهÙمْ وَلاَ ٱلضَّالّÙينَ + +Here is what the above might look like if your browser supports the Arabic block of Unicode: + + [Seven verses in Arabic] + +And here is a transcription of it: + + bismi ăl-la'hi ăr-raḥma'ni ăr-raḥiymi + + ăl-ḥamdu li-lla'hi rabbi ăl-`a'lamiyna + + ăr-raḥma'ni ăr-raḥiymi + + ma'liki yawmi ăd-diyni + + 'iyya'ka na`budu wa-'iyya'ka nasta`iynu + + Ä­hdina' ăṣ-á¹£ira'á¹­a ăl-mustaqiyma + + á¹£ira'á¹­a ăllaá¸iyna 'an`amta `alayhim Ä¡ayri ăl-maÄ¡á¸uwbi `alayhim wala' ăá¸-á¸a'lliyna + +A rough translation might be: + + In the name of God, the beneficient, the merciful. + + Praise be to God, lord of the worlds. + + The beneficient, the merciful. + + Master of the day of judgment. + + Thee do we worship, and Thine aid we seek. + + Lead us on the right path. + + The path of those on whom Thou hast bestowed favors. Not of those who have earned Thy wrath, nor of those who go astray. + + diff --git a/rdfdatabank/tests/testdata/unicodedata/unicode01.txt b/rdfdatabank/tests/testdata/unicodedata/unicode01.txt new file mode 100644 index 0000000..b1f47a9 --- /dev/null +++ b/rdfdatabank/tests/testdata/unicodedata/unicode01.txt @@ -0,0 +1,10 @@ +The convention in English is “to use double quotation marks to indicate quotation, and ‘single quotation marks’ for nested quotations.†+

          +En français la convention est « d'utiliser les guillemets français doubles pour les citations, et “ les guillemets anglais doubles †ou bien ‹ les guillemets français simples › pour les citations imbriquées. » +

          +Auf Deutsch ist die Vereinbarung »umgekehrte zweifache Anführungszeichen für die Zitate zu benutzen, sogar ›einfache Anführungszeichen‹ für die verschachtelte Zitate«; diese Anführungszeichen „dürfen auch solche ‚englische‘ Anführungszeichen sein.“ +

          +The en-dash is used between numbers such as in: 1685–1750 (J. S. Bach). It is longer than the hyphen (as in “en-dashâ€, or, more properly, “en-dashâ€) but shorter than the em-dash, which is used — like this — as a sort of parenthesis. Neither should be confused with the horizontal bar which is used to introduce quotation in some cases.
          +― Like this?
          +― Right.

          +The ellipsis is… well, it just is. \ No newline at end of file diff --git a/rdfdatabank/tests/testdata/unicodedata/unicode01.xml b/rdfdatabank/tests/testdata/unicodedata/unicode01.xml new file mode 100644 index 0000000..237f4d7 --- /dev/null +++ b/rdfdatabank/tests/testdata/unicodedata/unicode01.xml @@ -0,0 +1,22 @@ + + + + General punctuation + The convention in English is “to use double quotation marks to indicate quotation, and ‘single quotation marks’ for nested quotations.†+<br> <br> +En français la convention est « d'utiliser les guillemets français doubles pour les citations, et “ les guillemets anglais doubles †ou bien ‹ les guillemets français simples › pour les citations imbriquées. » +<br> <br> +Auf Deutsch ist die Vereinbarung »umgekehrte zweifache Anführungszeichen für die Zitate zu benutzen, sogar ›einfache Anführungszeichen‹ für die verschachtelte Zitate«; diese Anführungszeichen „dürfen auch solche ‚englische‘ Anführungszeichen sein.“ +<br> <br> +The en-dash is used between numbers such as in: 1685–1750 (J. S. Bach). It is longer than the hyphen (as in “en-dashâ€, or, more properly, “en-dashâ€) but shorter than the em-dash, which is used — like this — as a sort of parenthesis. Neither should be confused with the horizontal bar which is used to introduce quotation in some cases. <br> +― Like this? <br> +― Right. <br> <br> +The ellipsis is… well, it just is. + http://www.madore.org/~david/misc/unitest/ + + diff --git a/rdfdatabank/tests/testdata/unicodedata/unicode02.txt b/rdfdatabank/tests/testdata/unicodedata/unicode02.txt new file mode 100644 index 0000000..2966720 --- /dev/null +++ b/rdfdatabank/tests/testdata/unicodedata/unicode02.txt @@ -0,0 +1,10 @@ +In the following table, characters that are missing from Unicode as of version 3.0, latest draft (i.e. that cannot be represented as a single character but must use the more general form of combining diacritics) have been replaced by X, so you can tell they are not your browser's fault.
          +Base Grave Acute Circumflex Tilde Diaeresis Macron Breve Ogonek Dot Double acute Caron Double grave Inverted breve
          +a à á â ã ä Ä Äƒ Ä… ȧ X ÇŽ È Èƒ
          +e è é ê ẽ ë ē ĕ ę ė X ě ȅ ȇ
          +i ì í î Ä© ï Ä« Ä­ į i X Ç È‰ È‹
          +o ò ó ô õ ö Å Å Ç« ȯ Å‘ Ç’ È È
          +u ù ú û ũ ü ū ŭ ų X ű ǔ ȕ ȗ
          +y ỳ ý ŷ ỹ ÿ ȳ X X ẠX X X X
          +
          +Note that the three characters “LATIN SMALL LETTER A WITH DOT ABOVEâ€, “LATIN SMALL LETTER O WITH DOT ABOVE†and “LATIN SMALL LETTER Y WITH MACRON†were not present in version 2.0 of the Unicode standard. So it is quite understandable if you do not see the corresponding entries. \ No newline at end of file diff --git a/rdfdatabank/tests/testdata/unicodedata/unicode02.xml b/rdfdatabank/tests/testdata/unicodedata/unicode02.xml new file mode 100644 index 0000000..d14e5fa --- /dev/null +++ b/rdfdatabank/tests/testdata/unicodedata/unicode02.xml @@ -0,0 +1,22 @@ + + + + A table of (some) accents + In the following table, characters that are missing from Unicode as of version 3.0, latest draft (i.e. that cannot be represented as a single character but must use the more general form of combining diacritics) have been replaced by X, so you can tell they are not your browser's fault. <br> +Base Grave Acute Circumflex Tilde Diaeresis Macron Breve Ogonek Dot Double acute Caron Double grave Inverted breve <br> +a à á â ã ä Ä Äƒ Ä… ȧ X ÇŽ È Èƒ <br> +e è é ê ẽ ë Ä“ Ä• Ä™ Ä— X Ä› È… ȇ <br> +i ì í î Ä© ï Ä« Ä­ į i X Ç È‰ È‹ <br> +o ò ó ô õ ö Å Å Ç« ȯ Å‘ Ç’ È È <br> +u ù ú û Å© ü Å« Å­ ų X ű Ç” È• È— <br> +y ỳ ý Å· ỹ ÿ ȳ X X ẠX X X X <br> +<br> +Note that the three characters “LATIN SMALL LETTER A WITH DOT ABOVEâ€, “LATIN SMALL LETTER O WITH DOT ABOVE†and “LATIN SMALL LETTER Y WITH MACRON†were not present in version 2.0 of the Unicode standard. So it is quite understandable if you do not see the corresponding entries. + http://www.madore.org/~david/misc/unitest/ + + diff --git a/rdfdatabank/tests/testdata/unicodedata/unicode03.txt b/rdfdatabank/tests/testdata/unicodedata/unicode03.txt new file mode 100644 index 0000000..6f2bcd2 --- /dev/null +++ b/rdfdatabank/tests/testdata/unicodedata/unicode03.txt @@ -0,0 +1,6 @@ +The following paragraph gives a few forms formed by using combining diacritics. The equal sign means that the combined form on the left should be identical in all respects (and in particular, represented identically) to the atomic form on the right. To emphasize even more: you should not see two signs on the left of the equal sign but one, the same as on the right. +

          +aÌ€=à; eÌ=é; iÌ‚=î (not the same as ı̂ but may be graphically identical); õ=õ; uÌ„=Å« (whereas u¯ is two different symbols); ă=ă; ė=Ä— (also note i̇ should be essentially i); ï=ï (whereas i̇̈ has three dots on the i); aÌŠ=Ã¥ (not to be confused with a° (read “a degreesâ€) nor aËš); oÌ‹=Å‘; cÌŒ=Ä +ç=ç; bÌ£=ḅ; ḏ=Ḡ(this is supposedly different from dÌ  but may be graphically identical); eÌ­=ḙ +ǖ=ǖ=Ç– (not the same as ṻ which has the diaeresis on top of the macron); ǡ=ǡ=Ç¡ +ǭ=Ç«Ì„=Ç­ (also ǭ but the latter is not so canonical); oÌ·Ì=øÌ=Ç¿ (not so sure about this one) \ No newline at end of file diff --git a/rdfdatabank/tests/testdata/unicodedata/unicode03.xml b/rdfdatabank/tests/testdata/unicodedata/unicode03.xml new file mode 100644 index 0000000..53c3684 --- /dev/null +++ b/rdfdatabank/tests/testdata/unicodedata/unicode03.xml @@ -0,0 +1,18 @@ + + + + Combining diacritics + The following paragraph gives a few forms formed by using combining diacritics. The equal sign means that the combined form on the left should be identical in all respects (and in particular, represented identically) to the atomic form on the right. To emphasize even more: you should not see two signs on the left of the equal sign but one, the same as on the right. + <br> <br> +aÌ€=à; eÌ=é; iÌ‚=î (not the same as ı̂ but may be graphically identical); õ=õ; uÌ„=Å« (whereas u¯ is two different symbols); ă=ă; ė=Ä— (also note i̇ should be essentially i); ï=ï (whereas i̇̈ has three dots on the i); aÌŠ=Ã¥ (not to be confused with a° (read “a degreesâ€) nor aËš); oÌ‹=Å‘; cÌŒ=Ä +ç=ç; bÌ£=ḅ; ḏ=Ḡ(this is supposedly different from dÌ  but may be graphically identical); eÌ­=ḙ +ǖ=ǖ=Ç– (not the same as ṻ which has the diaeresis on top of the macron); ǡ=ǡ=Ç¡ +ǭ=Ç«Ì„=Ç­ (also ǭ but the latter is not so canonical); oÌ·Ì=øÌ=Ç¿ (not so sure about this one) + http://www.madore.org/~david/misc/unitest/ + + diff --git a/rdfdatabank/tests/testdata/unicodedata/unicode04.txt b/rdfdatabank/tests/testdata/unicodedata/unicode04.txt new file mode 100644 index 0000000..bb8f458 --- /dev/null +++ b/rdfdatabank/tests/testdata/unicodedata/unicode04.txt @@ -0,0 +1,27 @@ +Here is a table of the constellations of the Zodiac, in which the first column should contain the relevant astrological symbol:
          +Sym. English name Latin name Latin genitive α star
          +♈ The Ram Aries Arietis Hamal
          +♉ The Bull Taurus Tauri Aldebaran
          +♊ The Twins Gemini Geminorum Castor
          +♋ The Crab Cancer Cancri Acubens
          +♌ The Lion Leo Leonis Regulus
          +â™ The Virgin Virgo Virginis Spica
          +♎ The Scales Libra Libræ Zumen el Genubi
          +â™ The Scorpion Scorpius Scorpii Antares
          +â™ The Archer Sagittarius Sagittarii Rubkat
          +♑ The Sea Goat Capricornus Capricorni Giedi
          +â™’ The Water Bearer Aquarius Aquarii Sadalmelik
          +♓ The Fishes Pisces Piscium El Rischa
          +
          +The following table should show a chessboard, with a pictorial representation of the pieces:
          + A B C D E F G H
          +8 ♜ ♞ ♠♛ ♚ ♠♞ ♜
          +7 ♟ ♟ ♟ ♟ ♟ ♟ ♟ ♟
          +6
          +5
          +4
          +3
          +2 â™™ â™™ â™™ â™™ â™™ â™™ â™™ â™™
          +1 ♖ ♘ ♗ ♕ ♔ ♗ ♘ ♖
          +
          +Here is a snowflake: â„. \ No newline at end of file diff --git a/rdfdatabank/tests/testdata/unicodedata/unicode04.xml b/rdfdatabank/tests/testdata/unicodedata/unicode04.xml new file mode 100644 index 0000000..47be766 --- /dev/null +++ b/rdfdatabank/tests/testdata/unicodedata/unicode04.xml @@ -0,0 +1,39 @@ + + + + Various symbols + Here is a table of the constellations of the Zodiac, in which the first column should contain the relevant astrological symbol: <br> +Sym. English name Latin name Latin genitive α star <br> +♈ The Ram Aries Arietis Hamal <br> +♉ The Bull Taurus Tauri Aldebaran <br> +♊ The Twins Gemini Geminorum Castor <br> +♋ The Crab Cancer Cancri Acubens <br> +♌ The Lion Leo Leonis Regulus <br> +â™ The Virgin Virgo Virginis Spica <br> +♎ The Scales Libra Libræ Zumen el Genubi <br> +â™ The Scorpion Scorpius Scorpii Antares <br> +â™ The Archer Sagittarius Sagittarii Rubkat <br> +♑ The Sea Goat Capricornus Capricorni Giedi <br> +â™’ The Water Bearer Aquarius Aquarii Sadalmelik <br> +♓ The Fishes Pisces Piscium El Rischa <br> + <br> +The following table should show a chessboard, with a pictorial representation of the pieces: <br> + A B C D E F G H <br> +8 ♜ ♞ â™ â™› ♚ ♠♞ ♜ <br> +7 ♟ ♟ ♟ ♟ ♟ ♟ ♟ ♟ <br> +6 <br> +5 <br> +4 <br> +3 <br> +2 â™™ â™™ â™™ â™™ â™™ â™™ â™™ â™™ <br> +1 â™– ♘ â™— ♕ â™” â™— ♘ â™– <br> + <br> +Here is a snowflake: â„. + http://www.madore.org/~david/misc/unitest/ + + diff --git a/rdfdatabank/tests/testdata/unicodedata/unicode05.txt b/rdfdatabank/tests/testdata/unicodedata/unicode05.txt new file mode 100644 index 0000000..0659d44 --- /dev/null +++ b/rdfdatabank/tests/testdata/unicodedata/unicode05.txt @@ -0,0 +1,19 @@ +The following is a five-verse extract of introduction of the poem Mednyj Vsadnik; by A. S. Pushkin (in Russian):
          +
          + По оживлённым берегам
          + Громады Ñтройные теÑнÑÑ‚ÑÑ
          + Дворцов и башен; корабли
          + Толпой Ñо вÑех концов земли
          + К богатым приÑтанÑм ÑтремÑÑ‚ÑÑ;
          +
          +Here is a transcription of it:
          +
          + Po oživlënnym beregam
          + Gromady strojnye tesnâtsâ
          + Dvorcov i bašen; korabli
          + Tolpoj so vseh koncov zemli
          + K bogatym pristanâm stremâtsâ;
          +
          +A rough translation might be:
          +
          + Along the animated banks [of the Neva] / the shapely masses press / of palaces and towers; ships / in crowd from all corners of the Earth / rush toward its rich quays. \ No newline at end of file diff --git a/rdfdatabank/tests/testdata/unicodedata/unicode05.xml b/rdfdatabank/tests/testdata/unicodedata/unicode05.xml new file mode 100644 index 0000000..9c62fd3 --- /dev/null +++ b/rdfdatabank/tests/testdata/unicodedata/unicode05.xml @@ -0,0 +1,31 @@ + + + + Some verses in Russian + The following is a five-verse extract of introduction of the poem Mednyj Vsadnik; by A. S. Pushkin (in Russian): <br> + <br> + По оживлённым берегам <br> + Громады Ñтройные теÑнÑÑ‚ÑÑ <br> + Дворцов и башен; корабли <br> + Толпой Ñо вÑех концов земли <br> + К богатым приÑтанÑм ÑтремÑÑ‚ÑÑ; <br> + <br> +Here is a transcription of it: <br> + <br> + Po oživlënnym beregam <br> + Gromady strojnye tesnâtsâ <br> + Dvorcov i baÅ¡en; korabli <br> + Tolpoj so vseh koncov zemli <br> + K bogatym pristanâm stremâtsâ; <br> + <br> +A rough translation might be: <br> + <br> + Along the animated banks [of the Neva] / the shapely masses press / of palaces and towers; ships / in crowd from all corners of the Earth / rush toward its rich quays. + http://www.madore.org/~david/misc/unitest/ + + diff --git a/rdfdatabank/tests/testdata/unicodedata/unicode06.txt b/rdfdatabank/tests/testdata/unicodedata/unicode06.txt new file mode 100644 index 0000000..0678561 --- /dev/null +++ b/rdfdatabank/tests/testdata/unicodedata/unicode06.txt @@ -0,0 +1,17 @@ +The following verses are lines 1182–1185 of the tragedy Oedipus Rex by Sophocles (in ancient Greek):
          +
          + Ἰοὺ ἰού· Ï„á½° πάντʼ ἂν á¼Î¾á½µÎºÎ¿Î¹ σαφῆ.
          + á½® φῶς, τελευταῖόν σε Ï€ÏοσÏλέψαιμι νῦν,
          + ὅστις πέφασμαι φύς τʼ ἀφʼ ὧν οὠχÏῆν, ξὺν οἷς τʼ
          + οὠχÏῆν á½Î¼Î¹Î»á¿¶Î½, οὕς Ï„á½³ μʼ οá½Îº ἔδει κτανών.
          +
          +Here is the transcription of it:
          +
          + Iou iou; ta pant' an exēkoi saphē.
          + ÅŒ phÅs, teleutaion se prosblepsaimi nun,
          + hostis pephasmai phus t' aph' hÅn ou khrÄ“n, xun hois t'
          + ou khrÄ“n homilÅn, hous te m' ouk edei ktanÅn.
          +
          +A rough translation might be:
          +
          + Alas! All would become clear. / O light, may I see you for the last time, / I who was born of these of which it is a crime to be born, who live with these / which which it is a crime to live, and who killed these whom I must not kill. \ No newline at end of file diff --git a/rdfdatabank/tests/testdata/unicodedata/unicode06.xml b/rdfdatabank/tests/testdata/unicodedata/unicode06.xml new file mode 100644 index 0000000..d6da5ac --- /dev/null +++ b/rdfdatabank/tests/testdata/unicodedata/unicode06.xml @@ -0,0 +1,29 @@ + + + + Some verses in ancient Greek + The following verses are lines 1182–1185 of the tragedy Oedipus Rex by Sophocles (in ancient Greek): <br> + <br> + Ἰοὺ ἰού· Ï„á½° πάντʼ ἂν á¼Î¾á½µÎºÎ¿Î¹ σαφῆ. <br> + á½® φῶς, τελευταῖόν σε Ï€ÏοσÏλέψαιμι νῦν, <br> + ὅστις πέφασμαι φύς τʼ ἀφʼ ὧν οὠχÏῆν, ξὺν οἷς τʼ <br> + οὠχÏῆν á½Î¼Î¹Î»á¿¶Î½, οὕς Ï„á½³ μʼ οá½Îº ἔδει κτανών. <br> + <br> +Here is the transcription of it: <br> + <br> + Iou iou; ta pant' an exÄ“koi saphÄ“. <br> + ÅŒ phÅs, teleutaion se prosblepsaimi nun, <br> + hostis pephasmai phus t' aph' hÅn ou khrÄ“n, xun hois t' <br> + ou khrÄ“n homilÅn, hous te m' ouk edei ktanÅn. <br> + <br> +A rough translation might be: <br> + <br> + Alas! All would become clear. / O light, may I see you for the last time, / I who was born of these of which it is a crime to be born, who live with these / which which it is a crime to live, and who killed these whom I must not kill. + http://www.madore.org/~david/misc/unitest/ + + diff --git a/rdfdatabank/tests/testdata/unicodedata/unicode07.txt b/rdfdatabank/tests/testdata/unicodedata/unicode07.txt new file mode 100644 index 0000000..713b308 --- /dev/null +++ b/rdfdatabank/tests/testdata/unicodedata/unicode07.txt @@ -0,0 +1,17 @@ +The following is one stanza of canto â…¥ of the KumÄra-saṃbhava (“the birth of KumÄraâ€) by the great Sanskrit poet KÄlidÄsa:
          +
          + पशà¥à¤ªà¤¤à¤¿à¤°à¤ªà¤¿ तानà¥à¤¯à¤¹à¤¾à¤¨à¤¿ कृचà¥à¤›à¥à¤°à¤¾à¤¦à¥
          + अगमयददà¥à¤°à¤¿à¤¸à¥à¤¤à¤¾à¤¸à¤®à¤¾à¤—मोतà¥à¤•à¤ƒ ।
          + कमपरमवशं न विपà¥à¤°à¤•à¥à¤°à¥à¤¯à¥à¤°à¥
          + विभà¥à¤®à¤ªà¤¿ तं यदमी सà¥à¤ªà¥ƒà¤¶à¤¨à¥à¤¤à¤¿ भावाः ॥
          +
          +And here is the transcription of it:
          +
          + PaÅ›upatirapi tÄnyahÄni ká¹›cchrÄd
          + agamayadadrisutÄsamÄgamotkaḥ;
          + kamaparamavaśaṃ na viprakuryur
          + vibhumapi taṃ yadamÄ« spṛśanti bhÄvÄḥ?
          +
          +A rough translation might be:
          +
          + And PaÅ›upati passed those days with hardship, / eager for union with the daughter of the mountain. / Which other powerless [creature] would they not torment, / such emotions, when they affect even the powerful [Åšiva]? \ No newline at end of file diff --git a/rdfdatabank/tests/testdata/unicodedata/unicode07.xml b/rdfdatabank/tests/testdata/unicodedata/unicode07.xml new file mode 100644 index 0000000..cd7a87c --- /dev/null +++ b/rdfdatabank/tests/testdata/unicodedata/unicode07.xml @@ -0,0 +1,29 @@ + + + + Some verses in Sanskrit + The following is one stanza of canto â…¥ of the KumÄra-saṃbhava (“the birth of KumÄraâ€) by the great Sanskrit poet KÄlidÄsa: <br> + <br> + पशà¥à¤ªà¤¤à¤¿à¤°à¤ªà¤¿ तानà¥à¤¯à¤¹à¤¾à¤¨à¤¿ कृचà¥à¤›à¥à¤°à¤¾à¤¦à¥ <br> + अगमयददà¥à¤°à¤¿à¤¸à¥à¤¤à¤¾à¤¸à¤®à¤¾à¤—मोतà¥à¤•à¤ƒ । <br> + कमपरमवशं न विपà¥à¤°à¤•à¥à¤°à¥à¤¯à¥à¤°à¥ <br> + विभà¥à¤®à¤ªà¤¿ तं यदमी सà¥à¤ªà¥ƒà¤¶à¤¨à¥à¤¤à¤¿ भावाः ॥ <br> + <br> +And here is the transcription of it: <br> + <br> + PaÅ›upatirapi tÄnyahÄni ká¹›cchrÄd <br> + agamayadadrisutÄsamÄgamotkaḥ; <br> + kamaparamavaÅ›aṃ na viprakuryur <br> + vibhumapi taṃ yadamÄ« spṛśanti bhÄvÄḥ? <br> + <br> +A rough translation might be: <br> + <br> + And PaÅ›upati passed those days with hardship, / eager for union with the daughter of the mountain. / Which other powerless [creature] would they not torment, / such emotions, when they affect even the powerful [Åšiva]? + http://www.madore.org/~david/misc/unitest/ + + diff --git a/rdfdatabank/tests/testdata/unicodedata/unicode08.txt b/rdfdatabank/tests/testdata/unicodedata/unicode08.txt new file mode 100644 index 0000000..eece156 --- /dev/null +++ b/rdfdatabank/tests/testdata/unicodedata/unicode08.txt @@ -0,0 +1,20 @@ +The following are the two first lines of the Analects by Confucius:
          +
          + å­æ›°ï¼šã€Œå­¸è€Œæ™‚習之,ä¸äº¦èªªä¹Žï¼Ÿæœ‰æœ‹è‡ªé æ–¹ä¾†ï¼Œä¸äº¦æ¨‚乎?
          + 人ä¸çŸ¥è€Œä¸æ…,ä¸äº¦å›å­ä¹Žï¼Ÿã€
          +
          + 有å­æ›°ï¼šã€Œå…¶ç‚ºäººä¹Ÿå­å¼Ÿï¼Œè€Œå¥½çŠ¯ä¸Šè€…,鮮矣;
          + ä¸å¥½çŠ¯ä¸Šï¼Œè€Œå¥½ä½œäº‚者,未之有也。å›å­å‹™æœ¬ï¼Œæœ¬ç«‹è€Œé“生。
          + å­å¼Ÿä¹Ÿè€…,其為ä»ä¹‹æœ¬èˆ‡ï¼ã€
          +
          +And here is the transcription of it:
          +
          + ZÇ yuÄ“: “Xué ér shÄ« xí zhÄ«, bú yì yuè hÅ«? YoÇ” péng zì yÇ”an fÄnglái, bú yì lè hÅ«? Rén bù zhÄ«, ér bú yùn, bú yì jÅ«nzÇ hÅ«?â€
          +
          + YóuzÇ yuÄ“: “Qí wèi rén yÄ› xiàodì, ér hàofànshàngzhÄ›, xiÄn yÇ; bú hào fànshàng, ér hàozuòluànzhÄ›, wèi zhÄ« yóu yÄ›. JÅ«nzÇ wù bÄ›n, bÄ›n lì ér dào shÄ“ng. Xiàodì yé zhÄ›, qí wèi rén zhÄ« bén yÇ”!â€
          +
          +A rough translation might be:
          +
          + The Master [Confucius] said: “To study and to practice, it is is a joy, isn't it? When friends come from afar, it is a pleasure, isn't it? If one remains unknown and isn't hurt, isn't one an honorable man?â€
          +
          + Master You said: “Few of the men who act well filially and fraternally are also fond of offending their superiors; men who are not fond of offending their superiors, but who like to cause trouble, such do not exist. The honorable man concerns himself with the foundations. Once the foundations are established, the Way is born. Is not acting well filially and fraternally the foundation of humanity?†\ No newline at end of file diff --git a/rdfdatabank/tests/testdata/unicodedata/unicode08.xml b/rdfdatabank/tests/testdata/unicodedata/unicode08.xml new file mode 100644 index 0000000..05a4e60 --- /dev/null +++ b/rdfdatabank/tests/testdata/unicodedata/unicode08.xml @@ -0,0 +1,32 @@ + + + + Some Chinese + The following are the two first lines of the Analects by Confucius: <br> + <br> + å­æ›°ï¼šã€Œå­¸è€Œæ™‚習之,ä¸äº¦èªªä¹Žï¼Ÿæœ‰æœ‹è‡ªé æ–¹ä¾†ï¼Œä¸äº¦æ¨‚乎? <br> + 人ä¸çŸ¥è€Œä¸æ…,ä¸äº¦å›å­ä¹Žï¼Ÿã€ <br> + <br> + 有å­æ›°ï¼šã€Œå…¶ç‚ºäººä¹Ÿå­å¼Ÿï¼Œè€Œå¥½çŠ¯ä¸Šè€…,鮮矣; <br> + ä¸å¥½çŠ¯ä¸Šï¼Œè€Œå¥½ä½œäº‚者,未之有也。å›å­å‹™æœ¬ï¼Œæœ¬ç«‹è€Œé“生。 <br> + å­å¼Ÿä¹Ÿè€…,其為ä»ä¹‹æœ¬èˆ‡ï¼ã€ <br> + <br> +And here is the transcription of it: <br> + <br> + ZÇ yuÄ“: “Xué ér shÄ« xí zhÄ«, bú yì yuè hÅ«? YoÇ” péng zì yÇ”an fÄnglái, bú yì lè hÅ«? Rén bù zhÄ«, ér bú yùn, bú yì jÅ«nzÇ hÅ«?†<br> + <br> + YóuzÇ yuÄ“: “Qí wèi rén yÄ› xiàodì, ér hàofànshàngzhÄ›, xiÄn yÇ; bú hào fànshàng, ér hàozuòluànzhÄ›, wèi zhÄ« yóu yÄ›. JÅ«nzÇ wù bÄ›n, bÄ›n lì ér dào shÄ“ng. Xiàodì yé zhÄ›, qí wèi rén zhÄ« bén yÇ”!†<br> + <br> +A rough translation might be: <br> + <br> + The Master [Confucius] said: “To study and to practice, it is is a joy, isn't it? When friends come from afar, it is a pleasure, isn't it? If one remains unknown and isn't hurt, isn't one an honorable man?†<br> + <br> + Master You said: “Few of the men who act well filially and fraternally are also fond of offending their superiors; men who are not fond of offending their superiors, but who like to cause trouble, such do not exist. The honorable man concerns himself with the foundations. Once the foundations are established, the Way is born. Is not acting well filially and fraternally the foundation of humanity?†+ http://www.madore.org/~david/misc/unitest/ + + diff --git a/rdfdatabank/tests/testdata/unicodedata/unicode09.txt b/rdfdatabank/tests/testdata/unicodedata/unicode09.txt new file mode 100644 index 0000000..9c28fbb --- /dev/null +++ b/rdfdatabank/tests/testdata/unicodedata/unicode09.txt @@ -0,0 +1,9 @@ +The following is the original (Tamil) name of a famous mathematician:
          +
          + ஸà¯à®±à¯€à®©à®¿à®µà®¾à®¸ ராமானà¯à®œà®©à¯ à®à®¯à®™à¯à®•à®¾à®°à¯
          +
          +And here is a transcription of it:
          +
          + SṟīṉivÄsa RÄmÄṉujaṉ Aiyaá¹…kÄr
          +
          +Here there can be no translation, of course, since this is a proper noun. But I note that the mathematician in question (1887–1920) is typically named “Srinivasa Ramanujan Iyengar†in English. \ No newline at end of file diff --git a/rdfdatabank/tests/testdata/unicodedata/unicode09.xml b/rdfdatabank/tests/testdata/unicodedata/unicode09.xml new file mode 100644 index 0000000..7a7ea6d --- /dev/null +++ b/rdfdatabank/tests/testdata/unicodedata/unicode09.xml @@ -0,0 +1,21 @@ + + + + A Tamil name + The following is the original (Tamil) name of a famous mathematician: <br> + <br> + ஸà¯à®±à¯€à®©à®¿à®µà®¾à®¸ ராமானà¯à®œà®©à¯ à®à®¯à®™à¯à®•à®¾à®°à¯ <br> + <br> +And here is a transcription of it: <br> + <br> + SṟīṉivÄsa RÄmÄṉujaṉ Aiyaá¹…kÄr <br> + <br> +Here there can be no translation, of course, since this is a proper noun. But I note that the mathematician in question (1887–1920) is typically named “Srinivasa Ramanujan Iyengar†in English. + http://www.madore.org/~david/misc/unitest/ + + diff --git a/rdfdatabank/tests/testdata/unicodedata/unicode10.txt b/rdfdatabank/tests/testdata/unicodedata/unicode10.txt new file mode 100644 index 0000000..b1c8dfc --- /dev/null +++ b/rdfdatabank/tests/testdata/unicodedata/unicode10.txt @@ -0,0 +1,47 @@ +The following lines are the first chapter of the Qur'an (note that the text runs right to left, and should probably be aligned on the right margin):
          + + بÙسْم٠ٱللّٰه٠ٱلرَّحْمـَبن٠ٱلرَّحÙيم٠+

          + ٱلْحَمْد٠لÙلّٰه٠رَبّ٠ٱلْعَالَمÙينَ +

          + ٱلرَّحْمـَبن٠ٱلرَّحÙيم٠+

          + مَـالÙك٠يَوْم٠ٱلدّÙين٠+

          + Ø¥Ùيَّاكَ نَعْبÙد٠وَإÙيَّاكَ نَسْتَعÙين٠+

          + ٱهْدÙنَــــا ٱلصّÙرَاطَ ٱلمÙسْتَقÙيمَ +

          + صÙرَاطَ ٱلَّذÙينَ أَنعَمْتَ عَلَيهÙمْ غَير٠ٱلمَغضÙوب٠عَلَيهÙمْ وَلاَ ٱلضَّالّÙينَ +

          +And here is a transcription of it:
          +
          + bismi ăl-la'hi ăr-raḥma'ni ăr-raḥiymi
          +
          + ăl-ḥamdu li-lla'hi rabbi ăl-`a'lamiyna
          +
          + ăr-raḥma'ni ăr-raḥiymi
          +
          + ma'liki yawmi ăd-diyni
          +
          + 'iyya'ka na`budu wa-'iyya'ka nasta`iynu
          +
          + ĭhdina' ăṣ-ṣira'ṭa ăl-mustaqiyma
          +
          + á¹£ira'á¹­a ăllaá¸iyna 'an`amta `alayhim Ä¡ayri ăl-maÄ¡á¸uwbi `alayhim wala' ăá¸-á¸a'lliyna
          +
          +A rough translation might be:
          +
          + In the name of God, the beneficient, the merciful.
          +
          + Praise be to God, lord of the worlds.
          +
          + The beneficient, the merciful.
          +
          + Master of the day of judgment.
          +
          + Thee do we worship, and Thine aid we seek.
          +
          + Lead us on the right path.
          +
          + The path of those on whom Thou hast bestowed favors. Not of those who have earned Thy wrath, nor of those who go astray. \ No newline at end of file diff --git a/rdfdatabank/tests/testdata/unicodedata/unicode10.xml b/rdfdatabank/tests/testdata/unicodedata/unicode10.xml new file mode 100644 index 0000000..b5bc9d1 --- /dev/null +++ b/rdfdatabank/tests/testdata/unicodedata/unicode10.xml @@ -0,0 +1,58 @@ + + + + Some Arabic + The following lines are the first chapter of the Qur'an (note that the text runs right to left, and should probably be aligned on the right margin): <br> + + بÙسْم٠ٱللّٰه٠ٱلرَّحْمـَبن٠ٱلرَّحÙيم٠+ <br> <br> + ٱلْحَمْد٠لÙلّٰه٠رَبّ٠ٱلْعَالَمÙينَ + <br> <br> + ٱلرَّحْمـَبن٠ٱلرَّحÙيم٠+ <br> <br> + مَـالÙك٠يَوْم٠ٱلدّÙين٠+ <br> <br> + Ø¥Ùيَّاكَ نَعْبÙد٠وَإÙيَّاكَ نَسْتَعÙين٠+ <br> <br> + ٱهْدÙنَــــا ٱلصّÙرَاطَ ٱلمÙسْتَقÙيمَ + <br> <br> + صÙرَاطَ ٱلَّذÙينَ أَنعَمْتَ عَلَيهÙمْ غَير٠ٱلمَغضÙوب٠عَلَيهÙمْ وَلاَ ٱلضَّالّÙينَ + <br> <br> +And here is a transcription of it: <br> + <br> + bismi ăl-la'hi ăr-raḥma'ni ăr-raḥiymi <br> + <br> + ăl-ḥamdu li-lla'hi rabbi ăl-`a'lamiyna <br> + <br> + ăr-raḥma'ni ăr-raḥiymi <br> + <br> + ma'liki yawmi ăd-diyni <br> + <br> + 'iyya'ka na`budu wa-'iyya'ka nasta`iynu <br> + <br> + Ä­hdina' ăṣ-á¹£ira'á¹­a ăl-mustaqiyma <br> + <br> + á¹£ira'á¹­a ăllaá¸iyna 'an`amta `alayhim Ä¡ayri ăl-maÄ¡á¸uwbi `alayhim wala' ăá¸-á¸a'lliyna <br> + <br> +A rough translation might be: <br> + <br> + In the name of God, the beneficient, the merciful. <br> + <br> + Praise be to God, lord of the worlds. <br> + <br> + The beneficient, the merciful. <br> + <br> + Master of the day of judgment. <br> + <br> + Thee do we worship, and Thine aid we seek. <br> + <br> + Lead us on the right path. <br> + <br> + The path of those on whom Thou hast bestowed favors. Not of those who have earned Thy wrath, nor of those who go astray. + http://www.madore.org/~david/misc/unitest/ + + diff --git a/rdfdatabank/tests/testdata/ww1-2862-manifest.xml b/rdfdatabank/tests/testdata/ww1-2862-manifest.xml new file mode 100644 index 0000000..6f9e364 --- /dev/null +++ b/rdfdatabank/tests/testdata/ww1-2862-manifest.xml @@ -0,0 +1,49 @@ + + + + + + Thomas, Edward + A Cat + Poem + + Copyright Edward Thomas, 1979, reproduced under licence from Faber and Faber Ltd. + <br> She had a name among the children; +<br> But no one loved though someone owned +<br> Her, locked her out of doors at bedtime +<br> And had her kittens duly drowned. +<br> In Spring, nevertheless, this cat +<br> Ate blackbirds, thrushes, nightingales, +<br> And birds of bright voice and plume and flight, +<br> As well as scraps from neighbours' pails. +<br> I loathed and hated her for this; +<br> One speckle on a thrush's breast +<br> Was worth a million such; and yet +<br> She lived long, till God gave her rest. +<br><br> + Edward Thomas Collected Poems + 1979-01-01/1979-12-31 + Thomas, George + + + ProQuest + http://lion.chadwyck.co.uk/ + + + + + Faber and Faber + London + + + + diff --git a/rdfdatabank/tests/testdata/ww1-860b-manifest.xml b/rdfdatabank/tests/testdata/ww1-860b-manifest.xml new file mode 100644 index 0000000..7f79989 --- /dev/null +++ b/rdfdatabank/tests/testdata/ww1-860b-manifest.xml @@ -0,0 +1,45 @@ + + + + + + + Thomas, Edward + Two Houses + This manuscript is untitled but entitled 'Two Houses' in Edward Thomas Collected Poems + London + Notebook + Paper + Poem + 13 + MS Don d.28 f.13 + Everett Sharp + ETBODDOND28-13.jpg + Copyright of The Bodleian Library, Oxford University / The Edward Thomas Literary Estate + 51.501 + 1915-07-22 + + + + + Bodleian Library, University of Oxford + Western Manuscripts Collections + Broad Street + Oxford + Oxfordshire + OX13BG + United Kingdom + http://www.bodley.ox.ac.uk/ + + + + diff --git a/rdfdatabank/tests/testdata/ziptestdir.sh b/rdfdatabank/tests/testdata/ziptestdir.sh new file mode 100644 index 0000000..e968397 --- /dev/null +++ b/rdfdatabank/tests/testdata/ziptestdir.sh @@ -0,0 +1,14 @@ +#! /bin/bash +# +# Create ZIP files of test directories +# + +rm testdir.zip +rm testdir2.zip +rm testrdf.zip +zip -r testdir.zip testdir +zip -r testdir2.zip testdir2 +cd testrdf +zip -r ../testrdf.zip * +cd .. + diff --git a/rdfdatabank/tests/testlib/SparqlQueryTestCase.py b/rdfdatabank/tests/testlib/SparqlQueryTestCase.py new file mode 100644 index 0000000..4bd158a --- /dev/null +++ b/rdfdatabank/tests/testlib/SparqlQueryTestCase.py @@ -0,0 +1,405 @@ +#!/usr/bin/python +""" +Copyright (c) 2012 University of Oxford + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, --INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +""" + +""" +HTTP and SPARQL query test case support functions +""" + +import os, os.path +import sys +import unittest +import logging +import httplib +import base64 +import mimetypes +import urllib +import urlparse +try: + # Running Python 2.5 with simplejson? + import simplejson as simplejson +except ImportError: + import json as simplejson + +if __name__ == "__main__": + # For testing: + # add main library directory to python path if running stand-alone + sys.path.append("..") + +#from testlib import TestUtils +import TestUtils + +logger = logging.getLogger('SparqlQueryTestCase') + +# Originally copied from http://code.activestate.com/recipes/146306/: +def get_content_type(filename): + return mimetypes.guess_type(filename)[0] or 'application/octet-stream' + +# Originally copied from http://code.activestate.com/recipes/146306/: +def encode_multipart_formdata(fields, files): + """ + fields is a sequence of (name, value) elements for regular form fields. + files is a sequence of (name, filename, value, filetype) elements for data to be uploaded as files + Return (content_type, body) ready for httplib.HTTP instance + """ + BOUNDARY = '----------ThIs_Is_tHe_bouNdaRY_$' + CRLF = '\r\n' + L = [] + for (key, value) in fields: + L.append('--' + BOUNDARY) + L.append('Content-Disposition: form-data; name="%s"' % key) + L.append('') + L.append(value) + for (key, filename, value, filetype) in files: + L.append('--' + BOUNDARY) + L.append('Content-Disposition: form-data; name="%s"; filename="%s"' % (key, filename)) + L.append('Content-Type: %s' % (filetype or get_content_type(filename))) + L.append('') + L.append(value) + L.append('--' + BOUNDARY + '--') + L.append('') + body = CRLF.join(L) + content_type = 'multipart/form-data; boundary=%s' % BOUNDARY + return content_type, body + +def bindingType(b): + """ + Function returns the type of a variable binding. Commonly 'uri' or 'literal'. + """ + type = b['type'] + if type == "typed-literal" and b['datatype'] == "http://www.w3.org/2001/XMLSchema#string": + type = 'literal' + return type + +def findVarBindings(data, var): + """ + Returns a list of (type,value) pairs to which the supplied variable is bound in the results + """ + return [ (bindingType(b[var]),b[var]['value']) + for b in data['results']['bindings'] if var in b ] + +def findBindingSets(data): + """ + Returns a list of lists of (var:(type,value)) dictionaries from the supplied results + """ + return [ dict([ (var,{'type':bindingType(bindset[var]), 'value':bindset[var]['value']} ) for var in bindset ]) + for bindset in data['results']['bindings'] ] + +class SparqlQueryTestCase(unittest.TestCase): + """ + Test simple query patterns against data in SPARQL endpoint + + Although this module appears as a test suite, its main intended use is as a class + that can be subclassed in place of unittest.TestCase, providing additional methods + for testing HTTP access and SPATRQL queries. + """ + def setUp(self): + # Default SPARQL endpoint details + self._endpointhost = "localhost" + self._endpointpath = "/sparqlquerytest" # Really just a placeholder + self._endpointuser = None + self._endpointpass = None + self._manifesturiroot = None + return + + def tearDown(self): + return + + def setRequestEndPoint(self, endpointhost=None, endpointpath=None): + if endpointhost or endpointpath: + if endpointhost: + self._endpointhost = endpointhost + # Reset credentials if setting host + self._endpointuser = None + self._endpointpass = None + logger.debug("setRequestEndPoint: endpointhost %s: " % self._endpointhost) + if endpointpath: + self._endpointpath = endpointpath + logger.debug("setRequestEndPoint: endpointpath %s: " % self._endpointpath) + return + + def setRequestUserPass(self, endpointuser=None, endpointpass=None): + if endpointuser: + self._endpointuser = endpointuser + self._endpointpass = endpointpass + logger.debug("setRequestEndPoint: endpointuser %s: " % self._endpointuser) + logger.debug("setRequestEndPoint: endpointpass %s: " % self._endpointpass) + else: + self._endpointuser = None + self._endpointpass = None + return + + def setRequestUriRoot(self, manifesturiroot=None): + if manifesturiroot: + self._manifesturiroot = manifesturiroot + logger.debug("setRequestUriRoot: %s: " % self._manifesturiroot) + else: + self._manifesturiroot = None + return + + def getRequestPath(self, rel): + rel = rel or "" + if self._endpointpath: + return urlparse.urljoin(self._endpointpath,rel) + else: + return "" + + def getRequestUri(self, rel): + return "http://"+self._endpointhost+self.getRequestPath(rel) + + def getManifestUri(self, rel): + return self._manifesturiroot+self.getRequestPath(rel) + + def doRequest(self, command, resource, reqdata=None, reqheaders={}, expect_status=200, expect_reason="OK"): + logger.debug(command+" "+self.getRequestUri(resource)) + #if self._endpointuser: + # auth = base64.encodestring("%s:%s" % (self._endpointuser, self._endpointpass)).strip() + # reqheaders["Authorization"] = "Basic %s" % auth + auth = base64.encodestring("%s:%s" % (self._endpointuser, self._endpointpass)).strip() + reqheaders["Authorization"] = "Basic %s" % auth + hc = httplib.HTTPConnection(self._endpointhost) + #hc = httplib.HTTPSConnection(self._endpointhost) + path = self.getRequestPath(resource) + response = None + responsedata = None + repeat = 10 + while path and repeat > 0: + repeat -= 1 + hc.request(command, path, reqdata, reqheaders) + response = hc.getresponse() + if response.status != 301: break + path = response.getheader('Location', None) + if path[0:6] == "https:": + # close old connection, create new HTTPS connection + hc.close() + hc = httplib.HTTPSConnection(self._endpointhost) # Assume same host for https: + else: + response.read() # Seems to be needed to free up connection for new request + logger.debug("Status: %i %s" % (response.status, response.reason)) + + if expect_status != "*": self.assertEqual(response.status, expect_status) + if expect_status == 201: self.assertTrue(response.getheader('Content-Location', None)) + if expect_reason != "*": self.assertEqual(response.reason, expect_reason) + responsedata = response.read() + hc.close() + return (response, responsedata) + + def doHTTP_GET(self, + endpointhost=None, endpointpath=None, resource=None, + expect_status=200, expect_reason="OK", + expect_type="text/plain"): + reqheaders = { + "Accept": expect_type + } + self.setRequestEndPoint(endpointhost, endpointpath) + (response, responsedata) = self.doRequest("GET", resource, + reqheaders=reqheaders, + expect_status=expect_status, expect_reason=expect_reason) + if (expect_type.lower() == "application/json"): responsedata = simplejson.loads(responsedata) + return (response, responsedata) + + def doQueryGET(self, query, + endpointhost=None, endpointpath=None, + expect_status=200, expect_reason="OK", + JSON=False): + self.setRequestEndPoint(endpointhost, endpointpath) + encodequery = urllib.urlencode({"query": query}) + self.doHTTP_GET(endpointpath=self.getRequestPath("?"+encodequery), + expect_status=expect_status, expect_reason=expect_reason, + expect_type=("application/JSON" if JSON else None)) + return responsedata + + def doHTTP_POST(self, data, data_type="application/octet-strem", + endpointhost=None, endpointpath=None, resource=None, + expect_status=200, expect_reason="OK", + expect_type="text/plain"): + reqheaders = { + "Content-type": data_type, + "Accept": expect_type + } + self.setRequestEndPoint(endpointhost, endpointpath) + (response, responsedata) = self.doRequest("POST", resource, + reqdata=data, reqheaders=reqheaders, + expect_status=expect_status, expect_reason=expect_reason) + if (expect_type.lower() == "application/json"): responsedata = simplejson.loads(responsedata) + return (response, responsedata) + + def doQueryPOST(self, query, + endpointhost=None, endpointpath=None, + expect_status=200, expect_reason="OK", + JSON=False): + reqheaders = { + "Content-type": "application/x-www-form-urlencoded", + "Accept": "application/JSON" + } + encodequery = urllib.urlencode({"query": query}) + return self.doHTTP_POST( + encodequery, data_type="application/x-www-form-urlencoded", + endpointhost=None, endpointpath=None, + expect_status=200, expect_reason="OK", + expect_type=("application/JSON" if JSON else None)) + + def doHTTP_PUT(self, data, data_type="application/octet-strem", + endpointhost=None, endpointpath=None, resource=None, + expect_status=200, expect_reason="OK", + expect_type="text/plain"): + reqheaders = { + "Content-type": data_type, + "Accept": expect_type + } + self.setRequestEndPoint(endpointhost, endpointpath) + (response, responsedata) = self.doRequest("PUT", resource, + reqdata=data, reqheaders=reqheaders, + expect_status=expect_status, expect_reason=expect_reason) + return (response, responsedata) + + def doHTTP_DELETE(self, + endpointhost=None, endpointpath=None, resource=None, + expect_status=200, expect_reason="OK"): + self.setRequestEndPoint(endpointhost, endpointpath) + (response, _) = self.doRequest("DELETE", resource, + expect_status=expect_status, expect_reason=expect_reason) + return response + + def assertVarBinding(self, data, var, type, value): + """ + Asserts that the results for 'var' containing a binding + """ + self.assertTrue( (type, value) in findVarBindings(data, var), + """Expected to find %s bound as %s:"%s" in query results"""%(var, type, value)) + + def assertBinding(self, data, var, type=None, value=None): + self.assertTrue(var in data['head']['vars'], "Expected variable %s binding in query results"%(var)) + bindings = findBindingSets(data) + found = False + for b in bindings: + if var in b: + match = True + match &= (type == None) or (b[var]['type'] == type) + match &= (value == None) or (b[var]['value'] == value) + if match: + found = True + break + self.assertTrue(found, "Expected to find %s bound with type %s to value %s"%(var, type, value)) + + def assertBindingCount(self, data, count): + bindings = len(data['results']['bindings']) + self.assertEqual(bindings, count, "Expected %i result bindings, found %i"%(count, bindings)) + + def assertBindingSet(self, data, expectbindingset): + """ + Asserts that a given set of variable bindings occurs in at least one of the + result variable bindings from a query. + """ + found = False + for resultbinding in findBindingSets(data): + # For each query result... + match = True + for [var, expect] in expectbindingset: + # For each expected variable binding + self.assertTrue(var in data['head']['vars'], + "Expected variable %s binding in query results"%(var)) + # If variable is not bound in result, continue to next result + if not var in resultbinding: + match = False + continue + # Match details for single variable in binding set + for facet in expect: + match &= facet in resultbinding[var] and resultbinding[var][facet] == expect[facet] + # Exit if all variables matched in single binding + if match: return + # No matching binding found + self.assertTrue(False, "Expected to find binding set %s"%(expectbindingset)) + + def assertBindingSetPos(self, data, pos, expectbindingset): + """ + Asserts that a given set of variable bindings occurs in at least one of the + result variable bindings from a query. + """ + resultbinding = findBindingSets(data)[pos] + for [var, expect] in expectbindingset: + # For each expected variable binding + self.assertTrue(var in data['head']['vars'], "Expected variable %s binding in query results"%(var)) + # If variable is not bound in result, continue to next result + self.assertTrue(var in resultbinding, "Expected variable %s binding in query results"%(var)) + # Match details for single variable in binding set + for facet in expect: + self.assertTrue( + (facet in resultbinding[var] and resultbinding[var][facet] == expect[facet]), + "Result %i expected binding set %s"%(pos,expectbindingset)) + + # Actual tests follow + + def testNull(self): + # Just checking that this module compiles and loads OK + assert True, 'Null test failed' + + # Sentinel/placeholder tests + + def testUnits(self): + assert (True) + + def testComponents(self): + assert (True) + + def testIntegration(self): + assert (True) + + def testPending(self): + #assert (False), "Pending tests follow" + assert (True) + +# Assemble test suite + +def getTestSuite(select="unit"): + """ + Get test suite + + select is one of the following: + "unit" return suite of unit tests only + "component" return suite of unit and component tests + "all" return suite of unit, component and integration tests + "pending" return suite of pending tests + name a single named test to be run + """ + testdict = { + "unit": + [ "testUnits" + , "testNull" + ], + "component": + [ "testComponents" + ], + "integration": + [ "testIntegration" + ], + "pending": + [ "testPending" + ] + } + return TestUtils.getTestSuite(SparqlQueryTestCase, testdict, select=select) + +if __name__ == "__main__": + TestUtils.runTests("SparqlQueryTestCase.log", getTestSuite, sys.argv) + +# End. diff --git a/rdfdatabank/tests/testlib/TestUtils.py b/rdfdatabank/tests/testlib/TestUtils.py new file mode 100644 index 0000000..79949ea --- /dev/null +++ b/rdfdatabank/tests/testlib/TestUtils.py @@ -0,0 +1,125 @@ +""" +Copyright (c) 2012 University of Oxford + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, --INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +""" +# $Id: TestUtils.py 1047 2009-01-15 14:48:58Z graham $ +# +# Support functions for running different test suites +# +# Test suites are selected using a command line argument: +# +# Test classes are: +# "unit" These are stand-alone tests that all complete within a few +# seceonds and do not depend on resources external to the +# package being tested, (other than other libraries used). +# "component" These are tests that take loonger to run, or depend on +# external resources, (files, etc.) but do not depend on +# external services. +# "integration" These are tests that exercise interactions with seperate +# services. +# "pending" These are tests that have been designed and created, but +# for which the corresponding implementation has not been +# completed. +# "all" return suite of unit, component and integration tests +# name a single named test to be run. +# + +import logging +import unittest + +def getTestSuite(testclass,testdict,select="unit"): + """ + Assemble test suite from supplied class, dictionary and selector + + testclass is the test class whose methods are test cases + testdict is a dictionary of test cases in named test suite, + keyed by "unit", "component", etc., or by a named test. + select is the test suite selector: + "unit" return suite of unit tests only + "component" return suite of component tests + "integrate" return suite of integration tests + "pending" return suite of pending tests + "all" return suite of unit and component tests + name a single named test to be run + """ + suite = unittest.TestSuite() + # Named test only + if select[0:3] not in ["uni","com","all","int","pen"]: + if not hasattr(testclass, select): + print "%s: no test named '%s'"%(testclass.__name__, select) + return None + suite.addTest(testclass(select)) + return suite + # Select test classes to include + if select[0:3] == "uni": + testclasses = ["unit"] + elif select[0:3] == "com": + testclasses = ["component"] + elif select[0:3] == "int": + testclasses = ["integration"] + elif select[0:3] == "pen": + testclasses = ["pending"] + elif select[0:3] == "all": + testclasses = ["unit", "component"] + else: + testclasses = ["unit"] + for c in testclasses: + for t in testdict.get(c,[]): + if not hasattr(testclass, t): + print "%s: in '%s' tests, no test named '%s'"%(testclass.__name__, c, t) + return None + suite.addTest(testclass(t)) + return suite + +def runTests(logname, getSuite, args): + """ + Run unit tests based on supplied command line argument values + + logname name for logging output file, if used + getSuite function to retrieve test suite, given selector value + args command line arguments (or equivalent values) + """ + sel = "unit" + vrb = 1 + if len(args) > 1: + sel = args[1] + if sel[0:3] in ["uni","com","all","int","pen"]: + logging.basicConfig(level=logging.WARNING) + if sel[0:3] in ["com","all"]: vrb = 2 + else: + # Run single test with elevated logging to file via new handler + logging.basicConfig(level=logging.DEBUG) + # Enable debug logging to a file + fileloghandler = logging.FileHandler(logname,"w") + fileloghandler.setLevel(logging.DEBUG) + # Use this formatter for shorter log records + ###filelogformatter = logging.Formatter('%(levelname)s %(message)s', "%H:%M:%S") + # Use this formnatter to display timing information: + filelogformatter = logging.Formatter('%(asctime)s.%(msecs)03d %(levelname)s %(message)s', "%H:%M:%S") + fileloghandler.setFormatter(filelogformatter) + logging.getLogger('').addHandler(fileloghandler) + vrb = 2 + runner = unittest.TextTestRunner(verbosity=vrb) + tests = getSuite(select=sel) + if tests: runner.run(tests) + return + +# End. diff --git a/rdfdatabank/tests/testlib/__init__.py b/rdfdatabank/tests/testlib/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/rdfdatabank/tests/userRolesForTesting.txt b/rdfdatabank/tests/userRolesForTesting.txt new file mode 100644 index 0000000..eadb8e9 --- /dev/null +++ b/rdfdatabank/tests/userRolesForTesting.txt @@ -0,0 +1,10 @@ +username password role silo +sandbox_user sandbox Submitter (user) sandbox +sandbox_user2 sandbox2 Submitter (user) sandbox +sandbox_user3 sandbox3 Submitter (user) sandbox2 +sandbox_manger managertest Manager (manager) sandbox +sandbox_manger2 managertest2 Manager (manager) sandbox +sandbox_manger3 managertest3 Manager (manager) sandbox2 +admin test Administrator (admin) sandbox +admin2 test2 Administrator (admin) sandbox +admin3 test3 Administrator (admin) sandbox2 \ No newline at end of file diff --git a/rdfdatabank/tools/broadcastDatasets.py b/rdfdatabank/tools/broadcastDatasets.py new file mode 100644 index 0000000..18d32cd --- /dev/null +++ b/rdfdatabank/tools/broadcastDatasets.py @@ -0,0 +1,58 @@ +#-*- coding: utf-8 -*- +""" +Copyright (c) 2012 University of Oxford + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, --INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +""" + +import os +from rdfdatabank.lib.broadcast import BroadcastToRedis +from pylons import config + +def get_objs_in_dir(items_list, dirname, fnames): + for fname in fnames: + a = os.path.join(dirname,fname) + if fname == 'obj': + item = a.split('pairtree_root')[1].strip('/').split('obj')[0].replace('/', '') + silo = a.split('pairtree_root')[0].strip('/').split('/')[-1] + if not (silo, item) in items_list: + items_list.append((silo, item)) + return + +def broadcast_links(src_dir): + links_list = [] + os.path.walk(src_dir,get_objs_in_dir,links_list) + b = BroadcastToRedis(config['redis.host'], config['broadcast.queue']) + + for silo, item in links_list: + b.creation(silo, item) + return + +src_dirs = [ +'/silos', +] + +for src_dir in src_dirs: + print "starting", src_dir + links_list = [] + os.path.walk(src_dir,get_objs_in_dir,links_list) + b = BroadcastToRedis(config['redis.host'], config['broadcast.queue']) + for silo, item in links_list: + b.creation(silo, item) diff --git a/rdfdatabank/tools/digitalbooksMetadataWrite.py b/rdfdatabank/tools/digitalbooksMetadataWrite.py new file mode 100644 index 0000000..365ed8d --- /dev/null +++ b/rdfdatabank/tools/digitalbooksMetadataWrite.py @@ -0,0 +1,50 @@ +from solr import SolrConnection +import json +import codecs + +solrhost = "http://localhost:8080/solr" +s = SolrConnection(solrhost) + +fieldnames = ['silo', 'id', 'uuid', 'aggregatedResource', 'created', 'creator', 'currentVersion', 'date', 'dateAccepted', 'dateCopyrighted', 'dateSubmitted', 'description', 'embargoStatus', 'embargoedUntilDate', 'mediator', 'isPartOf', 'isVersionOf', 'license', 'modified', 'publisher', 'rights', 'subject', 'timestamp', 'title', 'type'] + +solr_params = {} +solr_params['q'] = "silo:digitalbooks" +solr_params['wt'] = 'json' +solr_params['fl'] = ','.join(fieldnames) +solr_params['rows'] = 500000 +solr_params['start'] = 0 + +solr_response = s.raw_query(**solr_params) + +numFound = 0 +docs = None +fname = "digitalbooks.csv" +delimiter = '$' + +if solr_response: + ans = json.loads(solr_response) + numFound = ans['response'].get('numFound',None) + try: + numFound = int(numFound) + except: + numFound = 0 + docs = ans['response'].get('docs',None) + if numfound > 0 and docs: + out_f = codecs.open(fname, 'a', 'utf-8') + for row in docs: + row_val = [] + for name in fieldnames: + if name in row and row[name] and isinstance(row[name], basestring): + row_val.append(row[name]) + elif name in row and row[name] and isinstance(row[name], list): + row_val.append(";".join(row[name])) + else: + row_val.append("") + if row_val: + out_f.write("%s\n" %delimiter.join(row_val) + out_f.close() + else: + print 'The search resulted in no documents' +else: + print 'The search resulted in no matches' + diff --git a/rdfdatabank/tools/indexingItemsinDatabank.py b/rdfdatabank/tools/indexingItemsinDatabank.py new file mode 100644 index 0000000..45d13c1 --- /dev/null +++ b/rdfdatabank/tools/indexingItemsinDatabank.py @@ -0,0 +1,112 @@ +#-*- coding: utf-8 -*- +""" +Copyright (c) 2012 University of Oxford + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, --INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +""" + +#To test keys in redis +from redis import Redis +r = Redis() +k = r.keys('*:embargoed') +k2 = r.keys('*:embargoed_until') +ka = r.keys('*') +len(ka) +for i in ka: + if not 'embargoed' in i: + print i + +r.llen('silochanges') +for i in range(r.llen('silochanges')): + r.lindex('silochanges', i) + +#====================================================================== + +# To add items to SOLR once in redis (for testing). Stop supervisor workers +from redis import Redis +from recordsilo import Granary +from solr import SolrConnection +from solr_worker import gather_document +import simplejson + +r = Redis() +r.llen('silochanges') +for i in range(r.llen('silochanges')): + r.lindex('silochanges', i) + +g = Granary("/opt/RDFDatabank/silos") +solr = SolrConnection("http://localhost:8080/solr") + +line = r.rpop("silochanges") +msg = simplejson.loads(line) +silo_name = msg['silo'] +s = g.get_rdf_silo(silo_name) +itemid = msg.get('id') +if itemid and s.exists(itemid): + item = s.get_item(itemid) + solr_doc = gather_document(silo_name, item) + solr.add(_commit=True, **solr_doc) + +#r.rpush("silochanges", line) + +#====================================================================== + +# To add items to redis +from rdfdatabank.lib.broadcast import BroadcastToRedis +b = BroadcastToRedis("localhost", 'silochanges') + +b.creation("demo", "Apocalypse-auctm315", ident="admin") +b.creation("demo", "Apocalypse-douce249", ident="admin") +b.creation("demo", "BibliaPauperum-archgc14", ident="admin") +b.creation("demo", "CanticumCanticorum-auctm312", ident="admin") +b.creation("demo", "MCSimulation-WW4jet", ident="admin") +b.creation("demo", "MCSimulation-WW4jet-CR", ident="admin") +b.creation("demo", "MonteCarloSimulations", ident="admin") +b.creation("demo", "blockbooks", ident="admin") +b.creation("test", "TestSubmission_2", ident="sandbox_user") +b.creation("dataflow", "GabrielTest", ident="admin") +b.creation("dataflow", "anusha-test", ident="admin") +b.creation("dataflow", "anusha-test-testrdf3", ident="admin") +b.creation("dataflow", "anusha:test", ident="admin") +b.creation("dataflow", "joe-test-2011-09-16-1", ident="admin") +b.creation("dataflow", "joetest", ident="admin") +b.creation("dataflow", "monica-test", ident="admin") +b.creation("dataflow", "test123", ident="admin") +b.creation("dataflow", "testdir123", ident="admin") +b.creation("dataflow", "testdir2", ident="admin") +b.creation("dataflow", "unpackingTest", ident="admin") + +#====================================================================== +""" +To install the correct versions of Redis-server and python-redis, +download the latest packages from oneiric + +cd ~ +aptitude show redis-server +sudo apt-get remove --purge redis-server +wget http://ubuntu.intergenia.de/ubuntu//pool/universe/r/redis/redis-server_2.2.11-3_amd64.deb +sudo dpkg -i redis-server_2.2.11-3_amd64.deb + +cd ~ +sudo rm -r /usr/local/lib/python2.6/dist-packages/redis-1.34.1-py2.6.egg +sudo apt-get remove --purge python-redis +wget http://de.archive.ubuntu.com/ubuntu/pool/universe/p/python-redis/python-redis_2.4.5-1_all.deb +sudo dpkg -i python-redis_2.4.5-1_all.deb +""" diff --git a/rdfdatabank/tools/populateState.py b/rdfdatabank/tools/populateState.py new file mode 100755 index 0000000..e552d50 --- /dev/null +++ b/rdfdatabank/tools/populateState.py @@ -0,0 +1,63 @@ +#-*- coding: utf-8 -*- +""" +Copyright (c) 2012 University of Oxford + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, --INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +""" + +import os +import simplejson +from pylons import config + +def get_objs_in_dir(items_in_silo, dirname, fnames): + for fname in fnames: + a = os.path.join(dirname,fname) + if fname == 'obj': + item = a.split('pairtree_root')[1].strip('/').split('obj')[0].replace('/', '') + silo = a.split('pairtree_root')[0].strip('/').split('/')[-1] + if not silo in items_in_silo: + items_in_silo[silo] = set() + items_in_silo[silo].add(item) + return + +def update_silo_persisted_state(root_dir, src_dir): + silo_items = {} + os.path.walk(src_dir,get_objs_in_dir,silo_items) + for silo, items in silo_items.iteritems(): + filepath = "%s/%s/persisted_state.json"%(root_dir, silo) + if not os.path.isfile(filepath): + print "File %s does not exist"%filepath + return + with open(filepath, "r") as serialised_file: + state = simplejson.load(serialised_file) + state['items'] = list(items) + state['item_count'] = "%d"%len(state['items']) + with open(filepath, "w") as serialised_file: + simplejson.dump(state, serialised_file) + return + +if __name__ == '__main__': + src_dirs = [ + '/silos/loadtest' + ] + root_dir = '/silos' + for src_dir in src_dirs: + print "starting", src_dir + update_silo_persisted_state(root_dir, src_dir) diff --git a/rdfdatabank/tools/populateTable.py b/rdfdatabank/tools/populateTable.py new file mode 100644 index 0000000..602702e --- /dev/null +++ b/rdfdatabank/tools/populateTable.py @@ -0,0 +1,32 @@ +import sqlalchemy as sa +import ConfigParser, os +from rdfdatabank import model +from rdfdatabank.lib.auth_entry import add_dataset + +class populateTable: + + def __init__(self, configFile="/var/lib/databank/production.ini"): + Config = ConfigParser.ConfigParser() + Config.read("/var/lib/databank/production.ini") + db_conn = Config.get("app:main", "sqlalchemy.url") + self.root_dir = Config.get("app:main", "granary.store") + engine = sa.create_engine(db_conn) + model.init_model(engine) + + def add_objs_in_dir(self, items_in_silo, dirname, fnames): + for fname in fnames: + a = os.path.join(dirname,fname) + if fname == 'obj': + item = a.split('pairtree_root')[1].strip('/').split('obj')[0].replace('/', '') + silo = a.split('pairtree_root')[0].strip('/').split('/')[-1] + add_dataset(silo, item) + return + + def populate(self): + silo_items = {} + os.path.walk(self.root_dir,self.add_objs_in_dir,silo_items) + + +if __name__ == '__main__': + p = populateTable() + p.populate() diff --git a/rdfdatabank/tools/renamingSymLinks.py b/rdfdatabank/tools/renamingSymLinks.py new file mode 100644 index 0000000..5028d4f --- /dev/null +++ b/rdfdatabank/tools/renamingSymLinks.py @@ -0,0 +1,80 @@ +#-*- coding: utf-8 -*- +""" +Copyright (c) 2012 University of Oxford + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, --INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +""" + +""" +This program is used to rename symlinks when the location of silos has changed +1. Move silo from your old location to the new location +2. Run this script using + +$python renamingSymLinks.py OLDPATH NEWPATH + + OR by calling the function rewrite_links + +OLDPATH = "/opt/RDFDatabank/silos" +NEWPATH = "/silos" + +src_dirs = [ + '/silos/dataflow/pairtree_root' + ,'/silos/demo/pairtree_root' + ,'/silos/test/pairtree_root' +] + +for src_dir in src_dirs: + print "starting", src_dir + rewrite_links(src_dir, OLDPATH, NEWPATH) +""" + +import os +import sys + +def get_links_in_dir(items_list, dirname, fnames): + for fname in fnames: + a = os.path.join(dirname,fname) + #if fname == 'obj': + # print a + if os.path.islink(a): + items_list.append(os.path.join(dirname,fname)) + return + +def rewrite_links(src_dir, OLDPATH, NEWPATH): + links_list = [] + os.path.walk(src_dir,get_links_in_dir,links_list) + + for i in range(len(links_list)): + linkname = links_list[i] + #print "linkname:", linkname + realpath = os.readlink(linkname) + if realpath.startswith(OLDPATH): + newpath = realpath.replace(OLDPATH, NEWPATH) + if os.path.islink(linkname) and os.path.isfile(newpath): + os.remove(linkname) + os.symlink(newpath, linkname) + #print "oldpath:", realpath, "\n newpath:", newpath + +if __name__ == "__main__": + OLDPATH = sys.argv[1] + NEWPATH = sys.argv[2] + src_dir = NEWPATH + rewrite_links(src_dir, OLDPATH, NEWPATH) + diff --git a/rdfdatabank/websetup.py b/rdfdatabank/websetup.py index e6c53b4..fc95140 100644 --- a/rdfdatabank/websetup.py +++ b/rdfdatabank/websetup.py @@ -1,10 +1,190 @@ +#-*- coding: utf-8 -*- +""" +Copyright (c) 2012 University of Oxford + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, --INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +""" + """Setup the rdfdatabank application""" import logging from rdfdatabank.config.environment import load_environment +from rdfdatabank.model import meta, User, Group, Permission +from sqlalchemy.exc import IntegrityError log = logging.getLogger(__name__) def setup_app(command, conf, vars): """Place any commands to setup rdfdatabank here""" load_environment(conf.global_conf, conf.local_conf) + log.info("Creating tables") + meta.metadata.create_all(bind=meta.engine) + log.info("Successfully setup") + + try: + g0a = Group() + g0a.group_name = u'databank_administrator' + g0a.silo = u'*' + meta.Session.add(g0a) + """ + g1a = Group() + g1a.group_name = u'sandbox_administrator' + g1a.silo = u'sandbox' + meta.Session.add(g1a) + + g1b = Group() + g1b.group_name = u'sandbox_manager' + g1b.silo = u'sandbox' + meta.Session.add(g1b) + + g1c = Group() + g1c.group_name = u'sandbox_submitter' + g1c.silo = u'sandbox' + meta.Session.add(g1c) + + g2a = Group() + g2a.group_name = u'sandbox2_administrator' + g2a.silo = u'sandbox2' + meta.Session.add(g2a) + + g2b = Group() + g2b.group_name = u'sandbox2_manager' + g2b.silo = u'sandbox2' + meta.Session.add(g2b) + + g2c = Group() + g2c.group_name = u'sandbox2_submitter' + g2c.silo = u'sandbox2' + meta.Session.add(g2c) + + g3a = Group() + g3a.group_name = u'sandbox3_administrator' + g3a.silo = u'sandbox3' + meta.Session.add(g3a) + + g3b = Group() + g3b.group_name = u'sandbox3_manager' + g3b.silo = u'sandbox3' + meta.Session.add(g3b) + + g3c = Group() + g3c.group_name = u'sandbox3_submitter' + g3c.silo = u'sandbox3' + meta.Session.add(g3c) + """ + p1 = Permission() + p1.permission_name = u'administrator' + p1.groups.append(g0a) + #p1.groups.append(g1a) + #p1.groups.append(g2a) + #p1.groups.append(g3a) + meta.Session.add(p1) + + p2 = Permission() + p2.permission_name = u'manager' + #p2.groups.append(g1b) + #p2.groups.append(g2b) + #p2.groups.append(g3b) + meta.Session.add(p2) + + p3 = Permission() + p3.permission_name = u'submitter' + #p3.groups.append(g1c) + #p3.groups.append(g2c) + #p3.groups.append(g3c) + meta.Session.add(p3) + """ + u0 = User() + u0.user_name = u'admin' + u0.name = u'Databank Administrator' + u0._set_password(u'test') + u0.groups.append(g0a) + meta.Session.add(u0) + + u1 = User() + u1.user_name = u'sandbox_user' + u1.name = u'Test User I' + u1._set_password(u'sandbox') + u1.groups.append(g1c) + meta.Session.add(u1) + + u2 = User() + u2.user_name = u'sandbox_user2' + u2.name = u'Test User II' + u2._set_password(u'sandbox2') + u2.groups.append(g2c) + meta.Session.add(u2) + + u3 = User() + u3.user_name = u'sandbox_user3' + u3.name = u'Test User III' + u3._set_password(u'sandbox3') + u3.groups.append(g3c) + meta.Session.add(u3) + + u4 = User() + u4.user_name = u'admin1' + u4.name = u'Test Administrator I' + u4._set_password(u'test') + u4.groups.append(g1a) + meta.Session.add(u4) + + u5 = User() + u5.user_name = u'admin2' + u5.name = u'Test Administrator II' + u5._set_password(u'test2') + u5.groups.append(g2a) + meta.Session.add(u5) + + u6 = User() + u6.user_name = u'admin3' + u6.name = u'Test Administrator III' + u6._set_password(u'test3') + u6.groups.append(g3a) + meta.Session.add(u6) + + u7 = User() + u7.user_name = u'sandbox_manager' + u7.name = u'Test Manager I' + u7._set_password(u'managertest') + u7.groups.append(g1b) + meta.Session.add(u7) + + u8 = User() + u8.user_name = u'sandbox_manager2' + u8.name = u'Test Manager II' + u8._set_password(u'managertest2') + u8.groups.append(g2b) + meta.Session.add(u8) + + u9 = User() + u9.user_name = u'sandbox_manager3' + u9.name = u'Test Manager III' + u9._set_password(u'managertest3') + u9.groups.append(g3b) + meta.Session.add(u9) + """ + meta.Session.flush() + meta.Session.commit() + except IntegrityError: + log.error('there was a problem adding your auth data, it may have already been added. Continuing with bootstrapping...') + #import traceback + #print traceback.format_exc() + meta.Session.rollback() diff --git a/requirements-dev.txt b/requirements-dev.txt new file mode 100644 index 0000000..1359fc3 --- /dev/null +++ b/requirements-dev.txt @@ -0,0 +1,13 @@ +nose +pairtree==0.5.6-T +pylons==0.9.7 +repoze.who==2.0a4 +repoze.who-friendlyform +repoze.profile +rdflib==2.4.2 +rdfobject +solrpy +uuid +redis==1.34.1 +python-dateutil==1.5 + diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..a1eae4d --- /dev/null +++ b/requirements.txt @@ -0,0 +1,11 @@ +nose +pairtree==0.5.6-T +pylons==0.9.7 +repoze.who==2.0a4 +repoze.who-friendlyform +rdflib==2.4.2 +rdfobject +solrpy +uuid +redis==1.34.1 +python-dateutil==1.5 diff --git a/setup.py b/setup.py index afdc0a6..6ee5f5b 100644 --- a/setup.py +++ b/setup.py @@ -7,7 +7,7 @@ setup( name='rdfdatabank', - version='0.1', + version='0.2', description='', author='', author_email='', diff --git a/setup_db.py b/setup_db.py new file mode 100644 index 0000000..c798c01 --- /dev/null +++ b/setup_db.py @@ -0,0 +1,61 @@ +import sqlalchemy as sa +from rdfdatabank.model import init_model +from rdfdatabank.lib.auth_entry import add_user, add_user_groups, add_silo +import ConfigParser +import sys, os + +class setupDB(): + + def __init__(self, config_file='/var/lib/databank/production.ini'): + if not os.path.exists(config_file): + print "Config file not found" + sys.exit() + c = ConfigParser.ConfigParser() + c.read(config_file) + if not 'app:main' in c.sections(): + print "Section app:main not found in config file" + sys.exit() + engine = sa.create_engine(c.get('app:main', 'sqlalchemy.url')) + init_model(engine) + return + + def addUser(self, user_details): + if not ('username' in user_details and user_details['username'] and \ + 'password' in user_details and user_details['password'] and \ + ('name' in user_details and user_details['name'] or \ + ('firstname' in user_details and user_details['firstname'] and \ + 'lastname' in user_details and user_details['lastname']))): + return False + add_user(user_details) + return True + + def addSilo(self, silo): + add_silo(silo) + return + + def addUserGroup(self, username, silo, permission): + groups = [] + groups.append((silo, permission)) + add_user_groups(username, groups) + return + +if __name__ == "__main__": + #Initialize sqlalchemy + s = setupDB() + + #add user + username = sys.argv[1] + password = sys.argv[2] + email = sys.argv[3] + + user_details = { + 'username':u'%s'%username, + 'password':u"%s"%password, + 'name':u'Databank Administrator', + 'email':u"%s"%email + } + s.addUser(user_details) + + #Add user membership + s.addUserGroup(username, '*', 'administrator') + diff --git a/sss.conf.json b/sss.conf.json new file mode 100644 index 0000000..a32889d --- /dev/null +++ b/sss.conf.json @@ -0,0 +1,165 @@ +{ + ############################################################################ + # SWORD SERVER CONFIGURATION + ############################################################################ + # This configuration file specifies the parameters for SSS + # + # Each configuration option can be accessed as an attribute of the + # Configuration python object. e.g. + # + # Configuration().base_url + # + # You may add any other configuration options directly to this JSON file + # and they will be picked up in the same way by the Configuration object. + # + # Some core configuration options have special methods for access built into + # the Configuration object (check the docs for details) + # + # This file is JSON formatted with one extension: comments are allowed. + # Comments are must be on a line of their own, and prefixed with #. The # + # must be the first non-whitespace character on the line. The configuration + # interpreter will strip all such lines before parsing the JSON, but will + # leave blank lines in the resulting JSON so that errors may be detected + # accurately by line number. + # + # To validate an this file, run: + # + # python config.py /path/to/sss.conf.json + # + ############################################################################ + + # The base url of the webservice where SSS is deployed + "base_url" : "http://localhost:5000/swordv2/", + + # the DataBank base url for the UI and other such reference points + "db_base_url" : "http://192.168.23.133/", + + # explicitly set the sword version, so if you're testing validation of + # service documents you can "break" it. + "sword_version" : "2.0", + + # require authentication (although DataBank will enforce this anyway) + "authenticate" : true, + + # DataBank does not require support of On-Behalf-Of as users will be + # authenticated using OAuth. So we turn mediation off in the service document + "mediation" : false, + + # What media ranges should the app:accept element in the Service Document support + "app_accept" : [ "*/*" ], + "multipart_accept" : [ "*/*" ], + + # What packaging formats should the sword:acceptPackaging element in the Service Document support + # In DataBank we explicitly support the DataBankBagIt format, as well as the + # required Binary format (Note that it does not support SimpleZip + "sword_accept_package" : [ + "http://purl.org/net/sword/package/Binary", + "http://dataflow.ox.ac.uk/package/DataBankBagIt" + ], + + # For DataBank this is currently disabled; it can be re-enabled at any + # point if upload size limits become necessary. + # + # maximum upload size to be allowed, in bytes (this default is 16Mb) + # (omitting this config option means there is no max_upload_size limit) + #"max_upload_size" : 16777216, + + # FIXME: will we ever really support SimpleZip + # list of package formats that SSS can provide when retrieving the Media Resource + # Here we support the required SimpleZip format and the standard DataBankBagIt + "sword_disseminate_package" : [ + "http://purl.org/net/sword/package/SimpleZip", + "http://dataflow.ox.ac.uk/package/DataBankBagIt" + ], + + + # FIXME: what are the dissemination packagers for DataBank? + # FIXME: this is a bad config option - way too complex, can we simplify? (e.g. each disseminator is a + # dictionary whose keys match parameters, and for which there is an "implementation" key pointing + # to the disseminator + # Supported package format disseminators; for the content type (dictionary key), the associated + # class will be used to package the content for dissemination + "package_disseminators" : { + # "(& (type=\"application/zip\") (packaging=\"http://purl.org/net/sword/package/SimpleZip\") )" : "sss.ingesters_disseminators.DefaultDisseminator", + # "(& (type=\"application/zip\") )" : "sss.ingesters_disseminators.DefaultDisseminator", + # "(& (type=\"application/atom+xml;type=feed\") )" : "sss.ingesters_disseminators.FeedDisseminator" + }, + + + # FIXME: this is probably not going to be used, as the unpacking will be done asynchronously + # in DataBank + # + # Supported package format ingesters; for the Packaging header (dictionary key), the associated class will + # be used to unpackage deposited content + "package_ingesters" : { + "http://purl.org/net/sword/package/Binary" : "sss.ingesters_disseminators.BinaryIngester", + "http://dataflow.ox.ac.uk/package/DataBankBagIt" : "sss.ingesters_disseminators.SimpleZipIngester" + }, + + # FIXME: what is the Entry Ingester for DataBank? + # FIXME: at the moment the Entry Ingester in DataBank is hard coded, but we should break it out here + # + # Ingester to use for atom entries + "entry_ingester" : "rdfdatabank.lib.sword_server.DefaultEntryIngester", + + # we can turn off updates and deletes in order to examine the behaviour of Method Not Allowed errors + # We leave these in and configured to true for DataBank, just in case we ever need to turn + # off any of the features + "allow_update" : true, + "allow_delete" : true, + + # we can turn off deposit receipts, which is allowed by the specification + # DataBank is well behaved, and ALWAYS returns a deposit receipt + "return_deposit_receipt" : true, + + # FIXME: this relates to the package_disseminators configuration above, which + # is not yet stabilised or used in DataBank. This configuration is therefore + # speculative + # + # The acceptable formats that the server can return the media resource in + # on request. + # This is used in Content Negotiation during GET on the EM-URI + "media_resource_formats" : [ + {"content_type" : "application/zip", "packaging": "http://dataflow.ox.ac.uk/package/DataBankBagIt"}, + {"content_type" : "application/zip"}, + {"content_type" : "application/atom+xml;type=feed"}, + {"content_type" : "text/html"} + ], + + # FIXME: this relates to the package_disseminators configuration above, which + # is not yet stabilised or used in DataBank. This configuration is therefore + # speculative + # + # If no Accept parameters are given to the server on GET to the EM-URI the + # following defaults will be used to determine the response type + "media_resource_default" : { + "content_type" : "application/zip", "packaging": "http://dataflow.ox.ac.uk/package/DataBankBagIt" + }, + + # FIXME: this is a standard required set for SWORD, although at present + # DataBank only supports type=entry for real. The rest coming in the full + # sword2 implementation + # + # The acceptable formats that the server can return the entry document in + # on request + # This is used in Content Negotiation during GET on the Edit-URI + "container_formats" : [ + {"content_type" : "application/atom+xml;type=entry" }, + {"content_type" : "application/atom+xml;type=feed" }, + {"content_type" : "application/rdf+xml" } + ], + + # If no Accept parameters are given to the server on GET to the Edit-URI the + # following defaults will be used to determine the response type + "container_format_default" : { + "content_type" : "application/atom+xml;type=entry" + }, + + # Dynamically load the implementation classes for the 3 main interfaces + # Here DataBank provides the implementation classes for the server and + # authenticator, and no WebUI is provided, as this is done by the main + # application + "sword_server" : "rdfdatabank.lib.sword_server.SwordDataBank", + "authenticator" : "rdfdatabank.lib.sword_server.DataBankAuthenticator" + #"webui" : "sss.repository.WebInterface" +} diff --git a/test.ini b/test.ini index ae777ba..8a97006 100644 --- a/test.ini +++ b/test.ini @@ -1,3 +1,23 @@ +# Copyright (c) 2012 University of Oxford +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, --INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # # rdfdatabank - Pylons testing environment configuration # diff --git a/who.ini b/who.ini index 88f2cb4..e74b8a6 100644 --- a/who.ini +++ b/who.ini @@ -1,11 +1,52 @@ +# Copyright (c) 2012 University of Oxford +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, --INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# +[plugin:redirector] +# identificaion and challenge +#use = repoze.who.plugins.redirector:make_plugin +#login_url = /login + +[plugin:friendlyform] +# Redirecting form which does login via a "post" from a regular /login form +use = repoze.who.plugins.friendlyform:FriendlyFormPlugin +login_form_url= /login +login_handler_path = /login_handler +post_login_url = /welcome +logout_handler_path = /logout_handler +post_logout_url = /logout +rememberer_name = auth_tkt +login_counter_name = logins + [plugin:auth_tkt] # identification and authentication use = repoze.who.plugins.auth_tkt:make_plugin secret = sup3rs33kr1t cookie_name = databank -secure = True +secure = False include_ip = False +[plugin:sqlauth] +# An SQLAlchemy authorization plugin +use = rdfdatabank.lib.auth:authenticator + [plugin:basicauth] # identification and challenge use = repoze.who.plugins.basicauth:make_plugin @@ -18,27 +59,31 @@ filename = %(here)s/passwd check_fn = repoze.who.plugins.htpasswd:crypt_check [general] -request_classifier = repoze.who.classifiers:default_request_classifier +#request_classifier = repoze.who.classifiers:default_request_classifier +request_classifier = rdfdatabank.lib.reqclassifier:custom_request_classifier challenge_decider = repoze.who.classifiers:default_challenge_decider remote_user_key = REMOTE_USER [identifiers] -# plugin_name;classifier_name:.. or just plugin_name (good for any) plugins = - auth_tkt - basicauth + friendlyform;browser + auth_tkt + basicauth [authenticators] -# plugin_name;classifier_name.. or just plugin_name (good for any) plugins = - auth_tkt - htpasswd + auth_tkt + sqlauth +# htpasswd [challengers] # plugin_name;classifier_name:.. or just plugin_name (good for any) plugins = - basicauth + friendlyform;browser + basicauth [mdproviders] plugins = - rdfdatabank.lib.ident_md:IdentMDProvider + rdfdatabank.lib.auth:user + rdfdatabank.lib.auth:group +# rdfdatabank.lib.ident_md:IdentMDProvider