<SYSTEM_TASK:>
Given the following text description, write Python code to implement the functionality described below step by step
<END_TASK>
<USER_TASK:>
Description:
 Tutorial
Step1:  First, make sure all logging messages are sent to stdout
Step2:  Connecting to a hurray server
Make sure you have a Hurray server, e.g.
Step3:  Working with files
Let's create a file test.h5 (overwrite=True replaces the file if it already exists)
Step4:  Note that Hurray objects (files, datasets, groups) display nicely in Jupyter notebooks.
Step5:  Working with existing files works like this
Step6:  Deleting and renaming files is also possible
Step7:  Note that the object referenced by f becomes unusable after deleting the file.
Let's create another file and renamed it to test.h5
Step8:  Note that rename() is not "in place". We must (re-)assign its return value.
Step9:  Files can be in subdirectories
Step10:  Working with datasets
A file can contain two kinds of objects
Step11:  A dataset has a shape and a dtype, just like NumPy arrays
Step12:  It also has a path, which is the name of the dataset, prefixed by the names of containing groups. Our dataset is not contained in a group. It therefore appears under the root node / (actually, it is in a group
Step13:  Let's check what data our dataset contains. Numpy-style indexing allows to read/write from/to a dataset. A [
Step14:  Let's overwrite this dataset with increasing floating point numbers
Step15:  Creating a dataset has increased file size
Step16:  Fancy indexing allows allows to read/write only portions of a dataset. In the following example, only columns 50 to 55 of rows 10 and 11 are sent over the wire
Step17:  We can also overwrite the above cells using the same notation
Step18:  Require ... TODO
Step19:  This shoud result in an error because dtypes do not match
Step20:  Working with groups
Datasets can be organised in groups (and subgroups). A group is like a folder and acts like a Python dictionary. Let's create a group named "data"
Step21:  Recall that every file object is also a group and therefore acts like a dictionary. Its keys() now lists are newly created group
Step22:  Let's create a subgroup (note that groups follow POSIX filesystem conventions)
Step23:  Now let's put a dataset in our subgroup
Step24:  Every group has a tree() method that displays sub groups and datasets as a tree.
Step25:  If you're not in a notebook or ipython console, tree() will give you a text based representation
Step26:  Attributes
Every group and dataset can be assigned a number of key/value pairs, so-called attributes
Step27:  Objects that have attributes get a red "A"
<ASSISTANT_TASK:>
Python Code:
import os
import sys
import logging
module_path = os.path.abspath(os.path.join('..'))
if module_path not in sys.path:
    sys.path.append(module_path)
import hurraypy as hurray
import numpy as np
hurray.__version__
Explanation: Tutorial
End of explanation
logger = logging.getLogger('hurraypy')
# console = logging.StreamHandler()
# console.setLevel(logging.DEBUG)
# console.setFormatter(logging.Formatter('%(levelname)s --- %(message)s'))
# logger.addHandler(console)
# logger.setLevel(logging.DEBUG)
logger.handlers
hurray.log.log.debug("bla")
hurray.log.log.info("bla")
Explanation: First, make sure all logging messages are sent to stdout:
End of explanation
# conn = hurray.connect('localhost:2222')
conn = hurray.connect('~/hurray.sock')
conn
Explanation: Connecting to a hurray server
Make sure you have a Hurray server, e.g.:
$ hurray --logging=debug --debug=1 --socket=~/hurray.sock
[I 170620 09:46:50 __main__:180] Listening on localhost:2222
[I 170620 09:46:50 __main__:184] Listening on /home/rg/hurray.sock
[I 170619 11:16:50 process:132] Starting 8 processes
End of explanation
f = conn.create_file("test.h5", overwrite=True)
Explanation: Working with files
Let's create a file test.h5 (overwrite=True replaces the file if it already exists):
End of explanation
f
Explanation: Note that Hurray objects (files, datasets, groups) display nicely in Jupyter notebooks.
End of explanation
f = conn.File("test.h5")
print(f)
with conn.File("test.h5") as f:
    print(f)
Explanation: Working with existing files works like this:
End of explanation
f.delete()
Explanation: Deleting and renaming files is also possible:
End of explanation
f2 = conn.create_file("test2.h5", overwrite=True)
f2
f = f2.rename("test.h5")
f
Explanation: Note that the object referenced by f becomes unusable after deleting the file.
Let's create another file and renamed it to test.h5:
End of explanation
f3 = conn.create_file("test3.h5", overwrite=True)
try:
    f3.rename("test.h5")
except hurray.exceptions.DatabaseError as e:
    print(e)
Explanation: Note that rename() is not "in place". We must (re-)assign its return value.
End of explanation
f4 = conn.create_file("project1/data.h5", overwrite=True)
f4
conn.list_files("project1/")
conn.list_files("")
Explanation: Files can be in subdirectories:
End of explanation
dst = f.create_dataset("mydata", shape=(400, 300), dtype=np.float64)
dst
Explanation: Working with datasets
A file can contain two kinds of objects: groups and datasets. Essentially, groups work like Python dictionaries and datasets work like NumPy arrays.
Every group and dataset has a name. First, let's try to create a dataset. We must specify the dataset either by passing a NumPy array or by passing a shape and a datatype:
End of explanation
dst.shape, dst.dtype
Explanation: A dataset has a shape and a dtype, just like NumPy arrays:
End of explanation
dst.path
Explanation: It also has a path, which is the name of the dataset, prefixed by the names of containing groups. Our dataset is not contained in a group. It therefore appears under the root node / (actually, it is in a group: the file itself is the root group).
End of explanation
dst[:]
Explanation: Let's check what data our dataset contains. Numpy-style indexing allows to read/write from/to a dataset. A [:]-index reads the whole dataset into memory. Apparently, our dataset has been initialized with zeros:
End of explanation
arr = np.linspace(0, 1, num=dst.shape[0] * dst.shape[1]).reshape(dst.shape)
arr.shape == dst.shape
dst[:] = arr
dst[:]
Explanation: Let's overwrite this dataset with increasing floating point numbers:
End of explanation
f
Explanation: Creating a dataset has increased file size:
End of explanation
dst[10:12, 50:55]
Explanation: Fancy indexing allows allows to read/write only portions of a dataset. In the following example, only columns 50 to 55 of rows 10 and 11 are sent over the wire:
End of explanation
dst[10:12, 50:55] = 999
dst[9:13, 50:55]
Explanation: We can also overwrite the above cells using the same notation:
End of explanation
dst = f.require_dataset("mydata", shape=(400, 300), dtype=np.float64, exact=True)
dst[9:13, 50:55]
Explanation: Require ... TODO
End of explanation
f.require_dataset("mydata", shape=(400, 300), dtype=np.int16, exact=True)
Explanation: This shoud result in an error because dtypes do not match:
End of explanation
f.create_group("mygroup")
Explanation: Working with groups
Datasets can be organised in groups (and subgroups). A group is like a folder and acts like a Python dictionary. Let's create a group named "data":
End of explanation
f.keys()
Explanation: Recall that every file object is also a group and therefore acts like a dictionary. Its keys() now lists are newly created group:
End of explanation
f.create_group("mygroup/subgroup")
subgrp = f["mygroup/subgroup"]
subgrp
Explanation: Let's create a subgroup (note that groups follow POSIX filesystem conventions):
End of explanation
data = np.random.random((600, 400))
dst = subgrp.create_dataset("randomdata", data=data)
dst
Explanation: Now let's put a dataset in our subgroup:
End of explanation
f.tree()
Explanation: Every group has a tree() method that displays sub groups and datasets as a tree.
End of explanation
print(f.tree())
Explanation: If you're not in a notebook or ipython console, tree() will give you a text based representation:
End of explanation
dst = f["mygroup/subgroup/randomdata"]
dst.attrs["unit"] = "celsius"
dst.attrs["max_value"] = 50
Explanation: Attributes
Every group and dataset can be assigned a number of key/value pairs, so-called attributes:
End of explanation
dst
dst.attrs.keys()
dst.attrs["unit"], dst.attrs["max_value"]
f.tree()
Explanation: Objects that have attributes get a red "A":
End of explanation
<END_TASK>