Skip to content

Commit 86374ed

Browse files
committed
Merge branch 'master' into release
2 parents 55a561b + 21037d4 commit 86374ed

File tree

4 files changed

+60
-135
lines changed

4 files changed

+60
-135
lines changed

README.rst

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,8 @@ PyMICE Python library
1313
:target: https://www.gnu.org/licenses/gpl-3.0
1414
:alt: License: GPL v3 badge
1515

16-
.. image:: https://zenodo.org/badge/DOI/10.5281/zenodo.832982.svg
17-
:target: https://doi.org/10.5281/zenodo.832982
16+
.. image:: https://zenodo.org/badge/DOI/10.5281/zenodo.884419.svg
17+
:target: https://doi.org/10.5281/zenodo.884419
1818
:alt: DOI badge
1919

2020
PyMICE is a Python® library for mice behavioural data analysis.
@@ -38,12 +38,12 @@ We ask that that reference to our paper as well as to the library itself is
3838
provided in any published research making use of PyMICE.
3939

4040
The recommended in-text citation format is:
41-
``PyMICE (Dzik, Puścian, et al. 2017) v. 1.2.0 (Dzik, Łęski, & Puścian 2017)``
41+
``PyMICE (Dzik, Puścian, et al. 2017) v. 1.2.1 (Dzik, Łęski, & Puścian 2017)``
4242

4343
and the recommended bibliography entry format:
4444

45-
Dzik J. M., Łęski S., Puścian A. (July 21, 2017) "PyMICE" computer software
46-
(v. 1.2.0; RRID:nlx_158570) doi: 10.5281/zenodo.832982
45+
Dzik J. M., Łęski S., Puścian A. (September 5, 2017) "PyMICE" computer
46+
software (v. 1.2.1; RRID:nlx_158570) doi: 10.5281/zenodo.884419
4747

4848
Dzik J. M., Puścian A., Mijakowska Z., Radwanska K., Łęski S. (June 22, 2017)
4949
"PyMICE: A Python library for analysis of IntelliCage data" Behavior Research
@@ -52,13 +52,13 @@ and the recommended bibliography entry format:
5252
If the journal does not allow for inclusion of the `resource identifier
5353
<http://journals.plos.org/plosone/article?id=10.1371/journal.pone.0146300>`_
5454
(RRID:nlx_158570) in the bibliography, we ask to provide it in-text:
55-
``PyMICE (RRID:nlx_158570) [1] v. 1.2.0 [2]``
55+
``PyMICE (RRID:nlx_158570) [1] v. 1.2.1 [2]``
5656

5757
1. Dzik JM, Puścian A, Mijakowska Z, Radwanska K, Łęski S. PyMICE: A Python
5858
library for analysis of IntelliCage data. Behav Res Methods. 2017.
5959
DOI: 10.3758/s13428-017-0907-5
60-
2. Dzik JM, Łęski S, Puścian A. PyMICE [computer software]. Version 1.2.0.
61-
Warsaw: Nencki Institute - PAS; 2017. DOI: 10.5281/zenodo.832982
60+
2. Dzik JM, Łęski S, Puścian A. PyMICE [computer software]. Version 1.2.1.
61+
Warsaw: Nencki Institute - PAS; 2017. DOI: 10.5281/zenodo.884419
6262

6363
We have provided a solution to facilitate referencing to the library. Please
6464
run::

lib/pymice/_ICData.py

Lines changed: 26 additions & 111 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,6 @@
3131
unicode = str
3232

3333
import os
34-
import zipfile
3534
import csv
3635
import warnings
3736

@@ -63,8 +62,8 @@
6362
DoorHardwareEvent, LedHardwareEvent,
6463
UnknownHardwareEvent, Session)
6564

66-
from ._Tools import (timeToList, PathZipFile, warn, groupBy, isString,
67-
mapAsList)
65+
from ._Tools import (timeToList, ArchiveZipFile, DirectoryZipFile, warn, groupBy,
66+
isString, mapAsList)
6867
from ._FixTimezones import inferTimezones, LatticeOrderer
6968
from ._Analysis import Aggregator
7069

@@ -200,10 +199,10 @@ def _appendData(self, fname):
200199

201200
if fname.endswith('.zip') or os.path.isdir(fname):
202201
if isString(fname) and os.path.isdir(fname):
203-
zf = PathZipFile(fname)
202+
zf = DirectoryZipFile(fname)
204203

205204
else:
206-
zf = zipfile.ZipFile(fname)
205+
zf = ArchiveZipFile(fname)
207206

208207
self._loadZip(zf, source=fname)
209208

@@ -349,8 +348,9 @@ def _loadZip(self, zf, source=None):
349348

350349
def _extractSessions(self, zf):
351350
try:
352-
fh = self._openZipFile(zf, 'Sessions.xml')
353-
dom = minidom.parse(fh)
351+
with self._findAndOpenZipFile(zf, 'Sessions.xml') as fh:
352+
dom = minidom.parse(fh)
353+
354354
aos = dom.getElementsByTagName('ArrayOfSession')[0]
355355
ss = aos.getElementsByTagName('Session')
356356
sessions = []
@@ -416,55 +416,39 @@ def _getZipLoader(self, zf):
416416

417417

418418
def _checkVersion(self, zf):
419-
fh = self._openZipFile(zf, 'DataDescriptor.xml')
420-
dom = minidom.parse(fh)
419+
with self._findAndOpenZipFile(zf, 'DataDescriptor.xml') as fh:
420+
dom = minidom.parse(fh)
421+
421422
dd = dom.getElementsByTagName('DataDescriptor')[0]
422423
version = dd.getElementsByTagName('Version')[0]
423424
versionStr = version.childNodes[0]
424425
assert versionStr.nodeType == versionStr.TEXT_NODE
425426
return versionStr.nodeValue.strip().lower()
426427

427428
def _fromZipCSV(self, zf, path, source=None, oldLabels=None):
428-
return self._fromCSV(self._openZipFile(zf, path + '.txt'),
429-
source=source,
430-
convert=self._convertZip.get(path),
431-
oldLabels=oldLabels)
429+
with self._findAndOpenZipFile(zf, path + '.txt') as fh:
430+
return self._fromCSV(fh,
431+
source=source,
432+
convert=self._convertZip.get(path),
433+
oldLabels=oldLabels)
432434

433435
@staticmethod
434-
def _openZipFile(zf, path):
436+
def _findAndOpenZipFile(zf, path):
435437
try:
436-
fh = zf.open(path)
438+
return zf.open(path)
437439

438440
except KeyError:
439-
fh = zf.open('IntelliCage/' + path)
440-
441-
if sys.version_info >= (3, 0):
442-
# if sys.version_info < (3, 2):
443-
# # XXX: Python3 monkey-path
444-
# fh.readable = lambda: True
445-
# fh.writable = lambda: False
446-
# fh.seekable = lambda: False
447-
# fh.read1 = items_file.read
448-
# #io.BytesIO(fh.read())
449-
return io.TextIOWrapper(fh)
441+
return zf.open('IntelliCage/' + path)
450442

451-
return fh
443+
def _fromCSV(self, fh, source=None, convert=None, oldLabels=None):
444+
return self.__fromCSV(list(csv.reader(fh, delimiter='\t')),
445+
source,
446+
convert,
447+
oldLabels)
452448

453-
@staticmethod
454-
def _fromCSV(fname, source=None, convert=None, oldLabels=None):
455-
if isString(fname):
456-
fname = open(fname, 'rb')
457-
458-
reader = csv.reader(fname, delimiter='\t')
459-
data = list(reader)
460-
fname.close()
461-
462-
return Loader.__fromCSV(data, source, convert, oldLabels)
463-
464-
@staticmethod
465-
def __fromCSV(data, source, convert, oldLabels):
449+
def __fromCSV(self, data, source, convert, oldLabels):
466450
if len(data) == 0:
467-
return
451+
return None
468452

469453
labels = data.pop(0)
470454
if isinstance(oldLabels, set):
@@ -476,7 +460,7 @@ def __fromCSV(data, source, convert, oldLabels):
476460
return dict((l, []) for l in labels)
477461

478462
emptyStringToNone(data)
479-
return Loader.__DictOfColumns(labels, data, source, convert)
463+
return self.__DictOfColumns(labels, data, source, convert)
480464

481465
class __DictOfColumns(dict):
482466
def __init__(self, labels, rows, source, conversions):
@@ -755,75 +739,6 @@ def _appendDataSource(self, dataSource):
755739

756740
self._buildCache()
757741

758-
# # TO BE MOVED TO DEBUG MODULE
759-
# import matplotlib.pyplot as plt
760-
# import matplotlib.patches as patches
761-
# from matplotlib.path import Path
762-
# import matplotlib.ticker
763-
# def _plotData(self):
764-
# # FIXME: obsoleted
765-
# #fig, ax = plt.subplots()
766-
# ax = plt.gca()
767-
# ax.xaxis.set_major_formatter(matplotlib.ticker.FuncFormatter(lambda x, y: hTime(x)))
768-
# plt.xticks(rotation=10)
769-
# labels = []
770-
# yticks = []
771-
# times = []
772-
# for i, dataSource in enumerate(self._dataSources):
773-
# labels.append(str(i))
774-
# start = dataSource.getStart()
775-
# end = dataSource.getEnd()
776-
# #plt.broken_barh([(start, end - start)], [i - 0.4, 0.8])
777-
# times.extend([start, end])
778-
# dataSource.plotChannel(i + 0.6, i + 1.4)
779-
# yticks.append(i)
780-
#
781-
# plt.yticks(yticks, labels)
782-
# start = min(times)
783-
# end = max(times)
784-
# span = end - start
785-
# plt.xlim(start - 0.1 * span, end + 0.1 * span)
786-
# plt.ylim(0, len(self._dataSources) + 1)
787-
#
788-
# def _plotChannelR(self, top=1., bottom=0.):
789-
# h = top - bottom
790-
# l = len(self._dataSources)
791-
# h2 = h / l
792-
# starts, ends = [], []
793-
#
794-
# for i, dataSource in enumerate(self._dataSources):
795-
# start, end = dataSource._plotChannelR(bottom + i * h2, bottom + (i + 1) * h2)
796-
# starts.append(start)
797-
# ends.append(end)
798-
#
799-
# if self.maskTimeStart is not None and self.maskTimeEnd is not None:
800-
# left = self.maskTimeStart if self.maskTimeStart is not None else self.getStart()
801-
# right = self.maskTimeEnd if self.maskTimeEnd is not None else self.getEnd()
802-
# ax = plt.gca()
803-
# codes = [Path.MOVETO, Path.LINETO]
804-
# verts = [(left, top), (right, top)]
805-
# if self.maskTimeEnd is not None:
806-
# codes.append(Path.LINETO)
807-
# ends.append(self.maskTimeEnd)
808-
#
809-
# else:
810-
# codes.append(Path.MOVETO)
811-
#
812-
# verts.append((right, bottom))
813-
# codes.append(Path.LINETO)
814-
# verts.append((left, bottom))
815-
# if self.maskTimeStart is not None:
816-
# verts.append((left, top))
817-
# codes.append(Path.CLOSEPOLY)
818-
# starts.append(self.maskTimeStart)
819-
#
820-
# path = Path(verts, codes)
821-
# patch = patches.PathPatch(path, facecolor='none', edgecolor='red')
822-
# ax.add_patch(patch)
823-
#
824-
#
825-
# return min(starts), max(ends)
826-
827742

828743
class ICSide(int):
829744
#__slots__ = ('__Corner',)

lib/pymice/_Tools.py

Lines changed: 21 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -32,21 +32,33 @@
3232
from operator import attrgetter
3333

3434
import pytz
35-
36-
#from numbers import Number
37-
#import numpy as np
38-
#if not issubclass(np.floating, Number):
39-
# Number.register(np.floating)
35+
import zipfile
4036

4137
from ._FixTimezones import LatticeOrderer
4238

43-
# XXX: isString, mapAsList are imported from elsewhere
44-
if sys.version_info >= (3, 0):
39+
# XXX: imported objects are imported from elsewhere
40+
if sys.version_info.major >= 3:
4541
from ._Python3.Tools import isString, mapAsList
4642

43+
import io
44+
45+
class ArchiveZipFile(zipfile.ZipFile):
46+
def open(self, *args, **kwargs):
47+
return io.TextIOWrapper(super(ArchiveZipFile, self).open(*args, **kwargs))
48+
# if sys.version_info < (3, 2):
49+
# # XXX: Python3 monkey-path
50+
# fh.readable = lambda: True
51+
# fh.writable = lambda: False
52+
# fh.seekable = lambda: False
53+
# fh.read1 = items_file.read
54+
# #io.BytesIO(fh.read())
55+
4756
else:
4857
from ._Python2.Tools import isString, mapAsList
4958

59+
class ArchiveZipFile(zipfile.ZipFile):
60+
pass
61+
5062

5163
def timeString(x, tz=None):
5264
return datetime.fromtimestamp(x, tz).strftime('%Y-%m-%d %H:%M:%S.%f%z')
@@ -165,6 +177,7 @@ def timeToList(tStr):
165177
#return map(int, tokens[:5] + [seconds, round(decimal * 1000000)])
166178
return LatticeOrderer.Node(map(int, tokens[:5] + [seconds, round(decimal * 1000000)]))
167179

180+
168181
class timeListList(list):
169182
def __eq__(self, x):
170183
return self[0] == x[0]
@@ -185,7 +198,7 @@ def __gt__(self, x):
185198
return self[0] > x[0]
186199

187200

188-
class PathZipFile(object):
201+
class DirectoryZipFile(object):
189202
"""
190203
A class emulating zipfile.ZipFile behaviour with filesystem directories.
191204
"""
@@ -201,14 +214,6 @@ def open(self, name, mode='r'):
201214
raise KeyError(name)
202215

203216

204-
205-
206-
207-
208-
209-
210-
211-
212217
def groupBy(objects, getKey=lambda x: x, requiredKeys=()):
213218
"""
214219
>>> import operator

test/testData.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1314,6 +1314,11 @@ def testGetOrderedHwTypes(self):
13141314
self.assertEqual(['Air', 'Air', 'LED', 'Door', 'Door', 'LED'],
13151315
[h.Type for h in self.data.getHardwareEvents(order='DateTime')])
13161316

1317+
1318+
class LoadUncompressedIntelliCagePlus3DataTest(LoadIntelliCagePlus3DataTest):
1319+
DATA_FILE = 'icp3_data'
1320+
1321+
13171322
class LoadEmptyDataTest(LoaderIntegrationTest):
13181323
DATA_FILE = 'empty_data.zip'
13191324

0 commit comments

Comments
 (0)