Skip to content

Commit

Permalink
Merge branch 'develop'
Browse files Browse the repository at this point in the history
  • Loading branch information
axelpale committed Mar 23, 2016
2 parents d07ee0b + 93d2515 commit 7ac5270
Show file tree
Hide file tree
Showing 8 changed files with 100 additions and 61 deletions.
6 changes: 6 additions & 0 deletions gazelib/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,9 @@
Gazelib, a toolkit for gaze analysis.
'''
from gazelib.package import version as __version__ # noqa
import gazelib.containers # noqa
import gazelib.io # noqa
import gazelib.legacy # noqa
import gazelib.settings # noqa
import gazelib.statistics # noqa
import gazelib.validation # noqa
20 changes: 16 additions & 4 deletions gazelib/containers.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@
'''
Classes that store the gaze data and can be fed to analysis functions.
'''
from .validation import is_list_of_strings, is_real
from .settings import min_event_slice_overlap_seconds as min_overlap
from .statistics.utils import arithmetic_mean, deltas
from .io import load_json, write_json, write_fancy_json, write_dictlist_as_csv
from gazelib.validation import is_list_of_strings, is_real
from gazelib.settings import min_event_slice_overlap_seconds as min_overlap
from gazelib.statistics import arithmetic_mean, deltas
from gazelib.io import (load_json, write_json, write_fancy_json,
write_dictlist_as_csv)
from time import time as get_current_posix_time
from deepdiff import DeepDiff
from bisect import bisect_left # binary tree search tool
Expand Down Expand Up @@ -303,6 +304,13 @@ def get_relative_end_time(self):

return max(tl_max, ev_max)

def get_relative_time_by_index(self, timeline_name, index):
'''
Return relative time on the timeline at index.
'''
tl = self.get_timeline(timeline_name)
return tl[index]

def get_duration(self):
'''
Difference in seconds between the smallest and largest time point.
Expand Down Expand Up @@ -522,6 +530,10 @@ def slice_by_tag(self, tag, index=0):
return self.slice_by_relative_time(range_start, range_end)

def iter_slices_by_tag(self, tag, limit_to=None):
'''DEPRECATED. Use iter_by_tag instead.'''
return self.iter_by_tag(tag, limit_to)

def iter_by_tag(self, tag, limit_to=None):
'''
Slice to multiple portions. E.g. if there is ten events with "trial"
tag, returns iterable over ten slices, one for each tag.
Expand Down
4 changes: 4 additions & 0 deletions gazelib/legacy/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
'''
Tools from the previous igazelib project
'''
import gazelib.legacy.igazelib # noqa
2 changes: 1 addition & 1 deletion gazelib/package.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# -*- coding: utf-8 -*-

name = 'gazelib'
version = '1.2.0'
version = '1.2.1'
description = 'Software tools to manage and analyze data from eye-trackers'
url = 'https://github.com/infant-cognition-tampere/gazelib'
author = 'Akseli Palen'
Expand Down
1 change: 1 addition & 0 deletions gazelib/statistics/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
'''
Statistical tools and methods.
'''
from .utils import arithmetic_mean, weighted_arithmetic_mean, deltas # noqa
61 changes: 14 additions & 47 deletions tests/test_containers.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,27 +12,11 @@
import gazelib
from gazelib.containers import CommonV1

from .utils import get_temp_filepath, remove_temp_file, frange
import jsonschema
import difflib
from .utils import (get_temp_filepath, remove_temp_file, frange,
get_fixture_filepath, load_sample, assert_files_equal)
import jsonschema # import ValidationError
import os

def get_fixture_filepath(sample_name):
'''
Create absolute filepath from sample filename.
'''
this_dir = os.path.dirname(os.path.realpath(__file__))
full_path = os.path.join(this_dir, 'fixtures', sample_name)
return full_path

def load_sample(sample_name):
'''
Reads from fixtures/ directory
Access e.g. by: load_sample('sample.common.json')
'''
full_path = get_fixture_filepath(sample_name)
return gazelib.io.load_json(full_path)


def assert_valid(self, common_raw, msg='Invalid CommonV1 structure'):
'''
Expand All @@ -44,32 +28,6 @@ def assert_valid(self, common_raw, msg='Invalid CommonV1 structure'):
self.fail(msg)


def assert_files_equal(self, filepath1, filepath2,
msg='Files should be equal but are not'):
'''
Assert the content of two files are equal. Do not care about empty spaces
at the end of the files.
'''
with open(filepath1, 'r') as f1, open(filepath2, 'r') as f2:
# Two lists of lines. Each element is a string that ends with '\n'
s1 = list(f1.readlines())
s2 = list(f2.readlines())
# Trim new lines because it causes problems because
# hand-written JSON includes trailing empty line where
# the generated JSON does not.
s1 = list(map(lambda l: l.rstrip('\n'), s1))
s2 = list(map(lambda l: l.rstrip('\n'), s2))
# File names for nice message.
b1 = filepath1
b2 = filepath2
diffs = list(difflib.unified_diff(s1, s2, b1, b2, lineterm='', n=0))
if len(diffs) != 0:
# Files not equal
diffmsg = '\n'.join(diffs)
msg = msg + ':\n' + diffmsg
self.fail(msg)


class TestCommonV1(unittest.TestCase):

def test_empty_init(self):
Expand Down Expand Up @@ -123,6 +81,15 @@ def test_get_start_end_time(self):
self.assertEqual(t1, 0.5)
self.assertEqual(dur, 1.0)

def test_get_relative_time_by_index(self):
c = CommonV1(get_fixture_filepath('sample.common.json'))

t = c.get_relative_time_by_index('ecg', 3)
self.assertEqual(t, 0.03)

f = lambda: c.get_relative_time_by_index('ecg', 100)
self.assertRaises(IndexError, f)

def test_slice_by_relative_time(self):

raw = load_sample('sample.common.json')
Expand Down Expand Up @@ -186,12 +153,12 @@ def test_slice_by_tag(self):
slicec = g.slice_by_tag('test/center', index=1)
self.assertEqual(len(slicec.get_timeline('eyetracker')), 1)

def test_iter_slices_by_tag(self):
def test_iter_by_tag(self):

raw = load_sample('sample.common.json')
g = gazelib.containers.CommonV1(raw)

slices = list(g.iter_slices_by_tag('test/center'))
slices = list(g.iter_by_tag('test/center'))

self.assertEqual(len(slices), 2)
self.assertEqual(len(slices[0].raw['events']), 5)
Expand Down
18 changes: 9 additions & 9 deletions tests/test_statistics.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,34 +5,34 @@

from gazelib import statistics as unit

class TestUtils(unittest.TestCase):
class TestStatistics(unittest.TestCase):

def test_deltas(self):

l = [1, 2, 3, 4, 5]
self.assertListEqual(unit.utils.deltas(l), [1, 1, 1, 1])
self.assertListEqual(unit.deltas(l), [1, 1, 1, 1])
l = [-1, 0, 1, 2]
self.assertListEqual(unit.utils.deltas(l), [1, 1, 1])
self.assertListEqual(unit.deltas(l), [1, 1, 1])
l = [1, 0, 1, 0, -1]
self.assertListEqual(unit.utils.deltas(l), [-1, 1, -1, -1])
self.assertListEqual(unit.deltas(l), [-1, 1, -1, -1])

def test_arithmetic_mean(self):

l = [1, 2, 3]
self.assertEqual(unit.utils.arithmetic_mean(l), 2.0)
self.assertEqual(unit.arithmetic_mean(l), 2.0)
l = []
self.assertEqual(unit.utils.arithmetic_mean(l), None)
self.assertEqual(unit.arithmetic_mean(l), None)
l = [None, 1]
self.assertEqual(unit.utils.arithmetic_mean(l), 1.0)
self.assertEqual(unit.arithmetic_mean(l), 1.0)

def test_weighted_arithmetic_mean(self):

l = [1, 5, 1]
w = [1, 0, 1]
self.assertEqual(unit.utils.weighted_arithmetic_mean(l, w), 1.0)
self.assertEqual(unit.weighted_arithmetic_mean(l, w), 1.0)
l = [2, 5, 0]
w = [1, None, 1]
self.assertEqual(unit.utils.weighted_arithmetic_mean(l, w), 1.0)
self.assertEqual(unit.weighted_arithmetic_mean(l, w), 1.0)

if __name__ == '__main__':
unittest.main()
49 changes: 49 additions & 0 deletions tests/utils/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import os
import tempfile
import gazelib
from sys import float_info
import difflib


def get_temp_filepath(file_name):
'''
Expand All @@ -10,6 +13,7 @@ def get_temp_filepath(file_name):
p = tempfile.mkdtemp()
return os.path.join(p, file_name)


def remove_temp_file(abs_filepath):
'''
Remove file created by the tempfile.mkdtemp
Expand All @@ -19,6 +23,7 @@ def remove_temp_file(abs_filepath):
os.remove(abs_filepath)
os.rmdir(d)


def frange(x, y, jump=1.0):
'''
Range for floats.
Expand All @@ -30,3 +35,47 @@ def frange(x, y, jump=1.0):
yield x
i += 1
x = x0 + i * jump


def get_fixture_filepath(sample_name):
'''
Create absolute filepath from sample filename.
'''
this_dir = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
full_path = os.path.join(this_dir, 'fixtures', sample_name)
return full_path


def load_sample(sample_name):
'''
Reads from fixtures/ directory
Access e.g. by: load_sample('sample.common.json')
'''
full_path = get_fixture_filepath(sample_name)
return gazelib.io.load_json(full_path)


def assert_files_equal(self, filepath1, filepath2,
msg='Files should be equal but are not'):
'''
Assert the content of two files are equal. Do not care about empty spaces
at the end of the files.
'''
with open(filepath1, 'r') as f1, open(filepath2, 'r') as f2:
# Two lists of lines. Each element is a string that ends with '\n'
s1 = list(f1.readlines())
s2 = list(f2.readlines())
# Trim new lines because it causes problems because
# hand-written JSON includes trailing empty line where
# the generated JSON does not.
s1 = list(map(lambda l: l.rstrip('\n'), s1))
s2 = list(map(lambda l: l.rstrip('\n'), s2))
# File names for nice message.
b1 = filepath1
b2 = filepath2
diffs = list(difflib.unified_diff(s1, s2, b1, b2, lineterm='', n=0))
if len(diffs) != 0:
# Files not equal
diffmsg = '\n'.join(diffs)
msg = msg + ':\n' + diffmsg
self.fail(msg)

0 comments on commit 7ac5270

Please sign in to comment.