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 4, 2016
2 parents 4992110 + 716480c commit cde2c85
Show file tree
Hide file tree
Showing 16 changed files with 214 additions and 78 deletions.
8 changes: 1 addition & 7 deletions .coveragerc
Original file line number Diff line number Diff line change
@@ -1,9 +1,3 @@
# This file is used by coverage command, called from tox.ini
[run]
source = gazelib
omit =
*/python?.?/*
*/lib-python/?.?/*.py
*/lib_pypy/_*.py
*/site-packages/ordereddict.py
*/site-packages/nose/*
*/unittest2/*
3 changes: 0 additions & 3 deletions .python-version
Original file line number Diff line number Diff line change
@@ -1,7 +1,4 @@
2.6.9
2.7.10
3.1.5
3.2.6
3.3.6
3.4.3
3.5.0
21 changes: 8 additions & 13 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,23 +1,18 @@
sudo: false
language: python
python:
- "2.7"
- "3.3"
- "3.4"
- "3.5"
#- "3.5-dev" # 3.5 development branch
#sudo: true
before_install:
- wget -O miniconda.sh https://repo.continuum.io/miniconda/Miniconda-latest-Linux-x86_64.sh
- chmod +x miniconda.sh
- ./miniconda.sh -b
- export PATH=/home/travis/miniconda2/bin:$PATH
- conda update --yes conda

# command to install dependencies
install:
- conda install --yes python=$TRAVIS_PYTHON_VERSION atlas nose
- pip install python-coveralls nose-cov unittest2
- "pip install $(python setup.py --require)"
- pip install tox-travis python-coveralls

# command to run tests
script:
- nosetests --with-cov --cov gazelib --cov-config .coveragerc --logging-level=INFO
- tox

after_success:
- coveralls --config_file .coveragerc
- coveralls
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
Copyright 2015 Jussi Kaatiala, Akseli Palén
Copyright 2015 Jussi Kaatiala, Akseli Palen

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
Expand Down
27 changes: 19 additions & 8 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,11 @@ With `pip
$ pip install gazelib



Usage
========

API
Docs
======

API docs are available under `docs/
<https://rawgit.com/infant-cognition-tampere/gazelib/develop/docs/build/>`_.
Examples and API docs are available at `http://gazelib.readthedocs.org/
<http://gazelib.readthedocs.org/>`_.

For developers
=================
Expand Down Expand Up @@ -87,7 +83,7 @@ Follow `instructions to install pyenv

or run comprehensive tests for multiple Python versions listed in ``tox.ini``::

$ pyenv local 2.6.9 2.7.10 3.1.5 3.2.6 3.3.6 3.4.3 3.5.0
$ pyenv local 2.7.10 3.1.5 3.2.6 3.3.6 3.4.3 3.5.0
$ eval "$(pyenv init -)"
$ pyenv rehash
$ tox
Expand All @@ -99,6 +95,18 @@ Install new pyenv environments for example by::
Validate README.rst at `http://rst.ninjs.org/
<http://rst.ninjs.org/>`_

Tox + Travis CI + `Coveralls
<https://coveralls.io/github/infant-cognition-tampere/gazelib>`_:

- https://www.rfk.id.au/blog/entry/testing-better-coverage-tox/
- http://coverage.readthedocs.org/en/latest/
- https://github.com/z4r/python-coveralls
- https://github.com/ryanhiebert/tox-travis
- http://jsatt.com/blog/using-tox-with-travis-ci-to-test-django-apps/
- http://agiliq.com/blog/2014/05/continuous-integration-with-travis-and-coverallsio/
- https://github.com/openwebinars-django/testango



Publishing to PyPI
-----------------------
Expand Down Expand Up @@ -132,6 +140,9 @@ Version release
5. Push commits and tags: ``$ git push && git push --tags``
6. Publish to PyPI. See *4.4. Publishing to PyPI*.

See also `a successful Git branching model
<http://nvie.com/posts/a-successful-git-branching-model/>`_.


Compile documentation
---------------------
Expand Down
6 changes: 3 additions & 3 deletions docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,8 @@

# General information about the project.
project = 'gazelib'
copyright = '2016, Akseli Palén'
author = 'Akseli Palén'
copyright = '2016, Akseli Palen'
author = 'Akseli Palen'

# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
Expand Down Expand Up @@ -229,7 +229,7 @@
# author, documentclass [howto, manual, or own class]).
latex_documents = [
(master_doc, 'gazelib.tex', 'gazelib Documentation',
'Akseli Palén', 'manual'),
'Akseli Palen', 'manual'),
]

# The name of an image file (relative to this directory) to place at the top of
Expand Down
1 change: 1 addition & 0 deletions gazelib/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
# -*- coding: utf-8 -*-
'''
Gazelib, a toolkit for gaze analysis.
'''
Expand Down
135 changes: 105 additions & 30 deletions gazelib/containers.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
# -*- coding: utf-8 -*-
'''
Classes that store the gaze data and can be fed to analysis functions.
'''
from .validation import has_keys, is_list_of_strings, is_real, ValidationException
from .validation import has_keys, is_list_of_strings, is_real
from .settings import min_event_slice_overlap_seconds as min_overlap
from deepdiff import DeepDiff
from bisect import bisect_left # binary tree search tool
from jsonschema import validate as validate_jsonschema


class CommonV1(object):
Expand All @@ -21,21 +23,94 @@ class InvalidStreamException(Exception):
class InvalidEventException(Exception):
pass

# JSON Schema to validate raw input
SCHEMA = {
'$schema': 'http://json-schema.org/draft-04/schema#',
'title': 'gazelib/common/v1',
'type': 'object',
'properties': {
'schema': {
'type': 'string',
'pattern': 'gazelib/common/v1'
},
'global_posix_time': {
'type': 'number'
},
'environment': {
'type': 'object',
'patternProperties': {
'.+': {}
}
},
'timelines': {
'type': 'object',
'patternProperties': {
'.+': {
'type': 'array',
'items': {
'type': 'number'
}
}
}
},
'streams': {
'type': 'object',
'patternProperties': {
'.+': {
'type': 'object',
'properties': {
'timeline': {
'type': 'string'
},
'values': {
'type': 'array'
},
'confidence': {
'type': 'array',
'items': {
'type': 'number',
'maximum': 1.0,
'minimum': 0.0
}
}
},
'required': ['timeline', 'values']
}
}
},
'events': {
'type': 'array',
'items': {
'type': 'object',
'properties': {
'tags': {
'type': 'array',
'items': {
'type': 'string'
}
},
'range': {
'type': 'array',
'items': {
'type': 'number'
}
},
'extra': {}
},
'required': ['tags', 'range']
}
}
},
'required': ['schema', 'global_posix_time', 'environment',
'timelines', 'streams', 'events']
}

@staticmethod
def validate(raw_common):
keys = [
'schema',
'global_posix_time',
'environment',
'timelines',
'streams',
'events'
]

if not has_keys(raw_common, keys):
raise CommonV1.ValidationException('Keys missing')

# TODO validate schema value
# TODO validate types
'''
Raises ValidationError if raw_common is not valid gazelib/common/v1
'''
validate_jsonschema(raw_common, CommonV1.SCHEMA)


def __init__(self, raw_common):
Expand All @@ -52,13 +127,13 @@ def __eq__(self, other):
# Accessors

def convert_to_global_time(self, relative_time_seconds):
if relative_time_seconds == None:
if relative_time_seconds is None:
return None
gt = self.raw['global_posix_time']
return gt + relative_time_seconds

def convert_to_relative_time(self, global_time_seconds):
if global_time_seconds == None:
if global_time_seconds is None:
return None
gt = self.raw['global_posix_time']
return global_time_seconds - gt
Expand Down Expand Up @@ -86,7 +161,7 @@ def iter_events_by_tag(self, tag):
def slice_by_relative_time(self, rel_start_time, rel_end_time=None):
'''
Return new CommonV1 object with data only in the time range.
Does not update global_posix_time because easier implementation
Does not update global_posix_time because easier implementation.
'''

# The new copy
Expand Down Expand Up @@ -160,15 +235,14 @@ def slice_by_relative_time(self, rel_start_time, rel_end_time=None):

return CommonV1(slice_raw)


def slice_by_global_time(self, start_time, end_time=None):
'''
Return new CommonV1 object with data only in the time range.
Updates the global_posix_time to start_time
to minimize representation size.
Updates the global_posix_time to start_time
to minimize representation size.
'''
r_start = self.convert_to_relative_time(start_time)
r_end = self.convert_to_relative_time(end_time)
r_end = self.convert_to_relative_time(end_time)

return self.slice_by_relative_time(r_start, r_end)

Expand All @@ -178,10 +252,11 @@ def slice_by_timeline(self, timeline_name, start_index, end_index=None):
by the indices of the timeline.
Parameters
timeline_name
start_index, inclusive
end_index (optional), exclusive, first element to not be included.
if None given, slice to the end
timeline_name
start_index: inclusive
end_index: optional, exclusive, first element to not be included.
If None given, slice to the end.
'''
timelines = self.raw['timelines']

Expand Down Expand Up @@ -228,15 +303,15 @@ def slice_by_tag(self, tag, index=0):
for event in self.raw['events']:
if tag in event['tags']:
if index == i:
target_event = event
break
target_event = event
break
i += 1

if target_event is None:
return None

range_start = target_event['range'][0]
range_end = target_event['range'][1]
range_end = target_event['range'][1]

return self.slice_by_relative_time(range_start, range_end)

Expand All @@ -257,7 +332,7 @@ def iter_slices_by_tag(self, tag, limit_to=None):

for index, event in enumerate(self.iter_events_by_tag(tag)):
range_start = event['range'][0]
range_end = event['range'][1]
range_end = event['range'][1]
yield self.slice_by_relative_time(range_start, range_end)
if limit_to is not None:
if index + 2 > limit_to:
Expand Down
9 changes: 6 additions & 3 deletions gazelib/io.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
# -*- coding: utf-8 -*-
'''
Input-Output functions that help to read and write gazefiles in various formats.
Input-Output functions that help to read and write gaze data.
'''
import json
import csv


def load_json(filename):
# load json-type file

Expand All @@ -22,7 +24,8 @@ def write_json(filename, data):
def write_fancy_json(filename, data):
# Write json so that it is readable by humans (rowchange, indent..).
with open(filename, 'w') as outfile:
json.dump(data, outfile, sort_keys = True, indent = 4, ensure_ascii=False)
json.dump(data, outfile, sort_keys=True,
indent=4, ensure_ascii=False)


def load_csv_as_dictlist(filename, delimit='\t'):
Expand All @@ -37,7 +40,7 @@ def load_csv_as_dictlist(filename, delimit='\t'):
print("File loading error: " + str(ex))
return

reader = csv.reader(ifile, delimiter = delimit)#"\t")
reader = csv.reader(ifile, delimiter=delimit)

rownum = 0
rows = []
Expand Down
1 change: 1 addition & 0 deletions gazelib/settings.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
# -*- coding: utf-8 -*-
'''
Gazelib settings
'''
Expand Down

0 comments on commit cde2c85

Please sign in to comment.