Skip to content

Commit 44bd043

Browse files
authored
Merge pull request #51 from ctreffe/release-v0.8
Release v0.8
2 parents 8742945 + 6f53802 commit 44bd043

31 files changed

+1125
-1182
lines changed

.gitignore

+2
Original file line numberDiff line numberDiff line change
@@ -42,3 +42,5 @@ tests/run_locally/run.py
4242
src/mortimer.egg-info
4343
build/
4444
dist/
45+
mortimer_venv/
46+
.mortimer/

CHANGELOG.md

+40-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,46 @@ All notable changes to this project will be documented in this file.
55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/),
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

8-
## [Unreleased]
8+
## Mortimer v0.8.4 (Released 2021-04-15)
9+
10+
### Added v0.8.4
11+
12+
- Added button for starting an experiment directly with url parameters
13+
14+
### Changed v0.8.4
15+
- Participant registration route now works with experiment version. The version is required for registration and optional for checking.
16+
- Changed internal handling of page tokens
17+
- /callable route in alfredo.py does not automatically redirect to /experiment anymore. This prevents unnecessary redirects. Previously, those redirects happened every time the called function return `None`.
18+
- /callable route in alfredo.py now automatically jsonifies return values.
19+
20+
## Mortimer v0.8.2
21+
22+
### Added
23+
24+
Starting from v0.8.2, Mortimer allows experimenters to check, whether partcipants have
25+
participated in a specific experiment through the route `/participation`.
26+
27+
A guide on how to use it is available in the [wiki](https://github.com/ctreffe/mortimer/wiki/Check-participation)
28+
29+
## Mortimer v0.8.0
30+
31+
### Changed
32+
33+
Mortimer was updated to be compatible with alfred3 v2.0. This included
34+
first and foremost an update to the data export handling and some small
35+
updates to configuration handling. Due to these changes, Mortimer v0.8.0
36+
is not compatible with older versions of alfred3.
37+
38+
## Mortimer v0.7.0
39+
40+
### Added
41+
42+
* Added support for two additional collections in the alfred database.
43+
* Redesigned and enhanced data export. When used with alfred v1.4+, Mortimer now offers export of unlinked data and codebooks.
44+
45+
### Fixed
46+
47+
* Fixed a bug in the password reset email.
948

1049
## Mortimer v0.6.1 (Released 2020-07-23)
1150

src/mortimer/__init__.py

-2
Original file line numberDiff line numberDiff line change
@@ -38,15 +38,13 @@ def create_app(instance_path=None):
3838
from mortimer.web_experiments.routes import web_experiments
3939
from mortimer.web_experiments.alfredo import alfredo
4040
from mortimer.main.routes import main
41-
from mortimer.local_experiments.routes import local_experiments
4241
from mortimer.errors.handlers import errors
4342

4443
# register blueprints
4544
app.register_blueprint(users)
4645
app.register_blueprint(web_experiments)
4746
app.register_blueprint(main)
4847
app.register_blueprint(alfredo)
49-
app.register_blueprint(local_experiments)
5048
app.register_blueprint(errors)
5149

5250
# global variables for use in templates

src/mortimer/_version.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,4 @@
33
# 2) we can import it in setup.py for the same reason
44
# 3) we can import it into your module module
55

6-
__version__ = "0.6.1"
6+
__version__ = "0.8.4"

src/mortimer/config.py

+1
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@ class BaseConfig:
9696
MAIL_USE_TLS = None
9797
MAIL_USERNAME = None
9898
MAIL_PASSWORD = None
99+
MAIL_SENDER_ADDRESS = None
99100

100101
# Flask-Dropzone settings:
101102
DROPZONE_ALLOWED_FILE_CUSTOM = True

src/mortimer/export.py

+34-14
Original file line numberDiff line numberDiff line change
@@ -7,29 +7,51 @@
77
import csv
88
import re
99
import io
10+
import random
11+
12+
from bson import json_util
1013

1114
# this is necessary, because send_file requires bytes-like objects
1215
def make_str_bytes(f):
1316
# Creating the byteIO object from the StringIO Object
1417
bytes_f = io.BytesIO()
15-
bytes_f.write(f.getvalue().encode("utf-8"))
18+
try:
19+
bytes_f.write(f.getvalue().encode("utf-8"))
20+
f.close()
21+
except AttributeError:
22+
bytes_f.write(f.encode("utf-8"))
1623
# seeking was necessary. Python 3.5.2, Flask 0.12.2
24+
1725
bytes_f.seek(0)
18-
f.close()
1926
return bytes_f
2027

2128

22-
def to_json(cursor):
23-
sIO = io.StringIO()
24-
L = list(cursor)
25-
json.dump(L, sIO)
26-
sIO.seek(0)
27-
return sIO
29+
def to_json(cursor, shuffle=False, decryptor=None):
30+
"""Turns a MongoDB Cursor into a JSON file.
31+
32+
Args:
33+
shuffle: If *True*, the document order will be shuffled
34+
(useful e.g. for preventing a link of experiment and
35+
unlinked data).
36+
decryptor: An alfred3.data_manager.Decryptor instance, which,
37+
if provided, will be used to try decryption of the values
38+
of all decouments.
39+
"""
40+
data = list(cursor)
41+
42+
if decryptor:
43+
decrypted_data = []
44+
for doc in data:
45+
decrypted_data.append(decryptor.decrypt(doc))
46+
data = decrypted_data
47+
48+
if shuffle:
49+
random.shuffle(data)
50+
out = json_util.dumps(obj=data, indent=4)
51+
return out
2852

2953

30-
def to_csv(
31-
cursor, none_value=None, remove_linebreaks=False, dialect="excel", **writerparams
32-
):
54+
def to_csv(cursor, none_value=None, remove_linebreaks=False, dialect="excel", **writerparams):
3355
rows = cursor_to_rows(cursor, none_value)
3456
if remove_linebreaks:
3557
for i in range(1, len(rows)):
@@ -168,9 +190,7 @@ def getDataFromDoc(self, doc):
168190
raise Exception("break")
169191
rv = rv + child.getDataFromDoc(subDoc if found else {})
170192
if self.additional_data:
171-
rv = rv + self.additional_data.getDataFromDoc(
172-
doc.get("additional_data", {})
173-
)
193+
rv = rv + self.additional_data.getDataFromDoc(doc.get("additional_data", {}))
174194
return rv
175195

176196
def getDataFromDocs(self, docs):

src/mortimer/forms.py

+28-30
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,10 @@
1111
TextAreaField,
1212
SelectField,
1313
SelectMultipleField,
14+
RadioField,
1415
)
1516
from wtforms.validators import DataRequired, Length, Email, EqualTo, ValidationError
16-
from mortimer.models import User, WebExperiment, LocalExperiment
17+
from mortimer.models import User, WebExperiment
1718
from flask import current_app
1819
import re
1920

@@ -112,34 +113,6 @@ def validate_version(self, version):
112113
submit = SubmitField("Create")
113114

114115

115-
class LocalExperimentForm(FlaskForm):
116-
title = StringField("Title", validators=[DataRequired()])
117-
exp_id = StringField("Experiment ID", validators=[DataRequired()])
118-
description = TextAreaField("Description")
119-
120-
def validate_title(self, title):
121-
# , version=self.version
122-
experiment = LocalExperiment.objects(
123-
title__exact=title.data, author__exact=current_user.username
124-
).first()
125-
if experiment is not None:
126-
raise ValidationError(
127-
"You already have a local experiment with this title. Please choose a unique title."
128-
)
129-
130-
def validate_exp_id(self, exp_id):
131-
experiment = LocalExperiment.objects(
132-
exp_id__exact=exp_id.data, author__exact=current_user.username
133-
).first()
134-
if experiment is not None:
135-
136-
raise ValidationError(
137-
"There already exists an experiment with this ID in the database. Please choose a unique ID. If you already collected data using this ID, please contanct an administrator."
138-
)
139-
140-
submit = SubmitField("Create")
141-
142-
143116
class ExperimentScriptForm(FlaskForm):
144117
script = TextAreaField("Script")
145118
version = StringField("Updated version", validators=[DataRequired()])
@@ -218,6 +191,32 @@ class ResetPasswordForm(FlaskForm):
218191
submit = SubmitField("Reset Password")
219192

220193

194+
class ExportCodebookForm(FlaskForm):
195+
choices = [
196+
("csv1", "csv ( , )"),
197+
("csv2", "csv ( ; )"),
198+
("json", "json"),
199+
]
200+
file_type = RadioField(default="csv2", choices=choices, validators=[DataRequired()])
201+
version = SelectField("Version", validators=[DataRequired()], default="latest")
202+
203+
204+
class ExportExpDataForm(FlaskForm):
205+
choices = [
206+
("csv1", "csv ( , )"),
207+
("csv2", "csv ( ; )"),
208+
("json", "json"),
209+
]
210+
file_type = RadioField(default="csv2", choices=choices, validators=[DataRequired()])
211+
data_type = RadioField(
212+
default="exp_data",
213+
choices=[("exp_data", "Experiment Data"), ("unlinked", "Unlinked Data")],
214+
validators=[DataRequired()],
215+
)
216+
version = SelectMultipleField("Version", validators=[DataRequired()])
217+
submit = SubmitField("Export Experiment Data")
218+
219+
221220
class ExperimentExportForm(FlaskForm):
222221

223222
file_type = SelectField(
@@ -291,4 +290,3 @@ class ExperimentConfigurationForm(FlaskForm):
291290
minimum_display_time = StringField("Minimum display time")
292291

293292
submit = SubmitField("Save")
294-

src/mortimer/local_experiments/__init__.py

Whitespace-only changes.

0 commit comments

Comments
 (0)