Here are a few examples along with a short explanation.
Sometimes you need to invite a lot of people to a project. If you already have a list of their E-Mail addresses, you can easily iterate over the list and invite every single one of them.
import coscine
from typing import List
TOKEN: str = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
PROJECT_NAME: str = "My Project"
EMAILS: List[str] = [
"adam@example.com",
"eva@example.com"
]
client = coscine.Client(TOKEN)
project = client.project(PROJECT_NAME)
for email in EMAILS:
project.invite(email)
# Import the package
import coscine
# We read our token from a file called 'token.txt'.
# Note: We could store our token directly inside the sourcecode
# as a string, but this is not recommended!
fd = open("token.txt", "rt")
token = fd.read()
fd.close()
# We create an instance of the Coscine client using
# the CoscineClient constructor.
client = coscine.Client(token)
project = client.project(displayName = "My Project")
resource = project.resource(displayName = "My Resource")
# We could create metadata in json-ld format by hand and use it
# to upload a file or assign metadata to a file. However this
# proves to be cumbersome and prone to ambiguous errors.
# Therefore we'd like to use some form or template, which the
# coscine python package is able to create for us!
# The template is created by requesting the application profile
# and the controlled vocabulary used within project and resource
# and examining their fields as well as the constraints put
# upon those fields.
# After that a custom dictionary-like datatype is created and filled
# with default and fixed values (as specified during resource creation).
# We can interact with this dictionary just like with any other dictionary
# with the difference being, that we can only set fields specified in
# the application profile and - in case of fields controlled
# by a vocabulary - can only use values of that vocabulary.
# Furthermore the printing functionality has been altered, to convey
# more information about the fields stored within the template.
# Just try print(form) and figure it out for yourself.
form = coscine.MetadataForm(resource)
# We can now modify the template.
# Let's assume our resource is using the ENGMETA metadata scheme, and set
# the required values for our metadata:
form["Title"] = "Hello World!"
form["Creator"] = "John Doe"
form["Contact"] = "John Doe"
form["Creation Date"] = "2021-01-21"
form["Publication Date"] = "2021-01-21"
form["Embargo End Date"] = "2023-01-01"
form["Version"] = "1.0"
form["Mode"] = "Experiment"
form["Step"] = "42"
form["Type"] = "Image"
form["Subject Area"] = "Medicine"
# As you can see in the example above, every value in the template
# is a string. This is keeping things simple - you just need to
# remember to cast any datetime object to a string in yyyy-mm-dd
# format and any integer to a string.
# Do note that the fields "Mode", "Type" and "Subject Area" are
# controlled by a vocabulary. You can just set the value you'd
# select in the web interface of Coscine and let the custom dictionary
# automatically resolve the actual value.
# "Actual value?" you are asking? Well - when we set "Type" to "Image"
# we are not actually assigning the string "Image" to it, but rather
# a uniform resource identifier containing the image type. This is
# intransparent to the user, but allows us to work with "Images" and
# "Waveforms" rather than "http://long-url.xd/id=Images", etc.
# Note that this also applies to controlled fields containing a fixed
# or default value - they will not contain "Images" but rather a
# uri resolving to "Images".
# Let's print our template and inspect how it looks.
# Make sure we filled in all required fields, so that no errors
# are thrown around when we try to upload.
print(form)
# We can now directly use this template as our metadata or generate
# a json-ld representation of our metadata using:
metadata = form.generate()
# Note that the generate() function will validate that each required
# field is present and that all fields are correctly formatted before
# generating the metadata. Thus be prepared to catch some exceptions.
# We do not need to generate the metadata before using functions like
# upload() though, as this is done internally.
# The upload_file() function needs 3 arguments:
# resource: Where do you want to store your file?
# filename: Which filename/path should the file assume within the resource?
# path: The local path to the file
# metadata: Either a filled in metadata template or json-ld formatted
# metadata string. If you specify a template it is validated before
# uploading. Thus, if you have not used template.generate() before
# you should now be prepared to catch some exceptions in case the
# metadata is containing bad values.
filename = "My Research Data.csv"
resource.upload(filename, filename, metadata)
# We can reset our template to its default values and re-use it for
# other files without requesting anything from the API. This spares
# us valuable bandwidth and processing time. Simply call the reset()
# function and fill it with new data or alter the data already present
# without calling reset().
# Note: A template for resource A should only be used for resource A
# and not for another resource B. Use a unique template per resource!
form.reset()
Files stored in an s3-resource do not require you to specify metadata on upload. However it is considered good practice to tag each file with metadata anyway. Here is an easy way of doing just that. The example assumes you've got a few files present inside an s3-resource, all of which have little to no metadata yet.
import coscine
TOKEN: str = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
PROJECT_NAME: str = "My Project"
RESOURCE_NAME: str = "My Resource"
client = coscine.Client(TOKEN)
project = client.project(PROJECT_NAME)
resource = project.resource(RESOURCE_NAME)
# This loop would set the same metadata for each file.
# This doesn't make much sense in the real world. Use
# a list of metadata or something similar to set unique metadata.
for file in resource.objects():
form = file.form()
form["Title"] = "My Title"
form["Author"] = "Mrs. X"
file.update(form)
Using the S3 library of your choice it's quite easy to connect to an S3 resource. The following short snippet shows how to use amazons boto3 SDK to connect to a Coscine S3 resource.
import coscine
import boto3
TOKEN: str = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
PROJECT_NAME: str = "My Project"
RESOURCE_NAME: str = "My S3 Resource"
FILE_NAME: str = "MyFile.ext"
client = coscine.Client(TOKEN)
project = client.project(PROJECT_NAME)
resource = project.resource(RESOURCE_NAME)
s3 = boto3.resource("s3", aws_access_key_id = resource.s3.access_key,\
aws_secret_access_key = resource.s3.secret_key,\
endpoint_url = resource.s3.endpoint)
bucket = s3.Bucket(resource.s3.bucket)
filesize = bucket.Object(FILE_NAME).content_length
bucket.download_file(FILE_NAME, "./") # path "./" (current directory)
If you are working from an interactive shell, you might at some point use one of those silly oneliners to quickly query some information.
import coscine
pid = coscine.Client(TOKEN).project("My Project").resource("My resource").pid
coscine.Client(TOKEN).project("My Project").resource("My resource").object("My filename").metadata
You get the idea...
The Coscine Python SDK has been implicitly written with GUIs in mind. Building a GUI around it is very easy, assuming you have some understanding of your GUI library of choice. The Qt Framework is well established in commercial, industrial and scientific use. We'll be using its python binding PyQt5. If you are just starting out, we recommend ditching TKinter and similar libraries in favor of Qt or WxWidgets.
You can easily edit a metadata form with a GUI.
import coscine
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
TOKEN: str = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
PROJECT_NAME: str = "My Project"
RESOURCE_NAME: str = "My Resource"
FILE_NAME: str = "My file.jpeg"
client = coscine.Client(TOKEN)
project = client.project(PROJECT_NAME)
resource = project.resource(RESOURCE_NAME)
file = resource.object(FILE_NAME)
metadata = file.form()
# GUI visualization
# (Obviously you should put that in a class)
app = QApplication([])
dialog = QDialog()
dialog.setWindowTitle("Metadata Editor")
buttonBox = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
#buttonBox.accepted.connect(self.accept)
#buttonBox.rejected.connect(self.reject)
# The magic happens here
group = QGroupBox("Metadata")
layout = QFormLayout()
for key in metadata.keys():
if metadata.is_controlled(key):
widget = QComboBox()
widget.setEditable(True)
widget.setInsertPolicy(QComboBox.NoInsert)
widget.completer().setCompletionMode(QCompleter.PopupCompletion)
for entry in metadata.get_vocabulary(key):
widget.addItem(entry)
else:
widget = QLineEdit(metadata[key])
layout.addRow(QLabel(key), widget)
group.setLayout(layout)
scrollArea = QScrollArea()
scrollArea.setWidget(group)
scrollArea.setHorizontalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOff)
mainLayout = QVBoxLayout()
mainLayout.addWidget(scrollArea)
mainLayout.addWidget(buttonBox)
dialog.setLayout(mainLayout)
dialog.exec_()
0
That wraps it up for the examples. The author this has also created a simple GUI file explorer (similar to WinSCP, Cyberduck, etc.) based on the Coscine Python SDK and PyQt5. Have a look at that project if you plan on doing something similar. It should be easy to build upon the existing codebase, extend it or steal some code from it. :^)