1.15 release

This commit is contained in:
Dan 2021-07-09 09:51:14 -04:00
parent 510ed0ebd1
commit 06021d5fe4
43 changed files with 7522 additions and 26 deletions

View File

@ -1,5 +1,5 @@
You don't have to read all this crap.  It's going to lead you down a dark road my friend.  One that only those that have accepted the twilight truth of system administration need see.  Save yourself.  Just write code.  Don't look too deeply into this.  It seems easy to you now.  Simple.  Just leave it at that.  Don't delve.  Don't do it.  Stay naive as long as you can, and cherish those moments.  When they are gone, the are gone forever.  I write this with pain.  It took hours of late nights away from me that I could have otherwise spent advancing my early demise with alcohol and elicit drugs.  But I decided, no, no, the stress of system administration, that's how I want to accelerate my decrepitude.  That's the life for me! But not you.  You get to fall asleep to the soft chirps of crickets, and wake up each day to sunlight and the twitter of gentle birds as the soft breeze of summer tickles you into the consciousness of one more day of blissful ignorance to the horrible truth.  You are not standing on the back of one system administrator.  No.  You rest quietly in a hammock, strung betwixt trees that were fostered out of the soil of of 50 feet of expired system administrators.  Rocking gently in the breeze, smiling at the sun against your gentle skin.
# This is terrible crap. It should be a docker container, or at least a set of shell scripts. We can't install docker on the servers at UVA currently. So we deploy to production by pushing to this star-drive-dist directory, and then pulling from these repos on production. Potentially running some flask commands if required.
# Release-It
We are using Release-It to manage pushing code that can be easily
@ -9,9 +9,11 @@ here: https://www.npmjs.com/package/release-it
I'll bet you skimmed that quick! I'll bet you didn't read it carefully! It took me at least 5 re-reads, so let me tell you the important bit, and save you some time. At a minimum, you will need to follow these directions to set up your local environment to handle github releases:
https://www.npmjs.com/package/release-it#github-releases
Once Release-it is installed and you have a github token setup per those instructions, use it to create
a tag.
# That was kind.
This is just a section to remind you. I am a kind person. You should like me. Buy me some chocolate. Did you read that last part? You did? Did you follow it? Don't ask me about it later. I'll bite you.
# But I am actually am a pain.
for instance, I expect that beside this directory, you have checked out the star-drive repository, and called it star-drive.
@ -24,13 +26,17 @@ for instance, I expect that beside this directory, you have checked out the star
That would create a whole pile of cool, pre-complied, ready to deploy angular wonder, that is darn near ready for production. But Wait! There is more!
If you did that, then you can run this command:
If you did that, then you can run these commands that will set up this star-drive dist:
``` bash
rm -rf backend
rm -rf frontend
cp -r ../star-drive/frontend/dist/star-drive/ frontend/
cp -r ../star-drive/backend backend
rm -rf backend/python-env
rm -rf __pycache__/
rm -rf backend/__pycache__/
git add backend
git add frontend
```

13
backend/Dockerfile Normal file
View File

@ -0,0 +1,13 @@
FROM python:3.8-slim
RUN apt-get update
RUN apt-get install apache2-dev libpq-dev postgresql-client -y
COPY ./backend/requirements.txt /
RUN /bin/bash -c "python3 -m venv python-env \
&& source python-env/bin/activate \
&& pip3 install -r requirements.txt"
COPY ./ /star-drive
RUN /bin/bash -c "cd /star-drive/backend/ && echo $PWD && mkdir instance && cp -r config instance/config && cp instance/config/docker.py instance/config.py"
ENV FLASK_DEBUG=1
ENV FLASK_APP=star-drive/backend/app/__init__.py
CMD /bin/bash -c "source python-env/bin/activate \
&& /star-drive/backend/containerrun.sh"

BIN
backend/app/TEST_DB Normal file

Binary file not shown.

View File

@ -233,6 +233,18 @@ class ElasticIndex:
else:
elastic_search = elastic_search.filter('bool', **{"should": self._default_filter()})
if search.geo_box:
elastic_search = elastic_search.filter('geo_bounding_box', **{"geo_point": {
"top_left" : {
"lat" : search.geo_box.top_left.lat,
"lon" : search.geo_box.top_left.lon
},
"bottom_right" : {
"lat" : search.geo_box.bottom_right.lat,
"lon" : search.geo_box.bottom_right.lon
}
}})
if sort is not None:
elastic_search = elastic_search.sort(sort)

View File

@ -34,4 +34,4 @@ class AgeRange:
if clean_age in AgeRange.age_map.keys():
return AgeRange.age_map[clean_age]
else:
raise Exception('Unkown age range:"' + bad_age + '" see Age Range Class to fix it.')
raise Exception('Unknown age range:"' + bad_age + '" see Age Range Class to fix it.')

View File

@ -23,14 +23,14 @@ class Search:
return list(map(lambda age_name: (AggCount(age_name, 0, False)),
['english', 'spanish', 'chinese', 'korean', 'vietnamese', 'arabic', 'tagalog']))
def __init__(self, words="", types=[], ages=[], languages=[], start=0, size=10, sort=None, category=None, date=None,
map_data_only=False):
def __init__(self, words="", types=None, ages=None, languages=None, start=0, size=10, sort=None, category=None, date=None,
map_data_only=False, geo_point = None, geo_box = None):
self.words = words
self.total = 0
self.hits = []
self.types = types
self.ages = ages
self.languages = languages
self.types = [] if types is None else types
self.ages = [] if ages is None else ages
self.languages = [] if languages is None else languages
self.start = start
self.size = size
self.sort = sort
@ -40,6 +40,7 @@ class Search:
self.language_counts = Search.known_language_counts()
self.date = date
self.map_data_only = map_data_only # When we should return a limited set of details just for mapping.
self.geo_box = geo_box
# Method called when updating a search with fresh results.
# This should zero-out any existing data that should be overwritten.
@ -78,6 +79,15 @@ class Search:
def has_language():
return next((ac for ac in Search.known_language_counts() if ac.value == 'value'), None) is not None
class Geopoint:
def __init__(self, lat, lon):
self.lat = lat
self.lon = lon
class Geobox:
def __init__(self, top_left, bottom_right):
self.top_left = top_left
self.bottom_right = bottom_right
class Sort:
def __init__(self, field, latitude, longitude, order, unit):

View File

@ -1,6 +1,10 @@
import datetime
import flask.scaffold
from sqlalchemy.dialects.postgresql import Any
from app.model.age_range import AgeRange
flask.helpers._endpoint_from_view_func = flask.scaffold._endpoint_from_view_func
import flask_restful
from flask import request
@ -78,3 +82,12 @@ class StudyByStatusListEndpoint(flask_restful.Resource):
def get(self, status):
studies = db.session.query(Study).filter_by(status=status).order_by(Study.last_updated.desc())
return self.studiesSchema.dump(studies)
class StudyByAgeEndpoint(flask_restful.Resource):
studiesSchema = StudySchema(many=True)
def get(self, status, age):
# session.query(TestArr).filter(Any(2, TestArr.partners)).all()
studies = db.session.query(Study).filter_by(status=status).filter(Study.ages.any(age)).order_by(Study.last_updated.desc())
return self.studiesSchema.dump(studies)

View File

@ -16,7 +16,7 @@ from app.model.resource import Resource
from app.model.resource_category import ResourceCategory
from app.model.resource_change_log import ResourceChangeLog
from app.model.role import Role
from app.model.search import Search, Sort
from app.model.search import Search, Sort, Geopoint, Geobox
from app.model.step_log import StepLog
from app.model.study import Study, Status
from app.model.study_category import StudyCategory
@ -610,6 +610,13 @@ class StudyInvestigatorSchema(ModelSchema):
'study': ma.URLFor('api.studyendpoint', id='<study_id>')
})
class GeopointSchema(ma.Schema):
lat = fields.Float(missing=None)
lon = fields.Float(missing=None)
@post_load
def make_geo_point(self, data, **kwargs):
return Geopoint(**data)
class SearchSchema(ma.Schema):
class Meta:
@ -648,6 +655,14 @@ class SearchSchema(ma.Schema):
count = fields.Integer()
is_selected = fields.Boolean()
class GeoboxSchema(ma.Schema):
top_left = ma.Nested(GeopointSchema)
bottom_right= ma.Nested(GeopointSchema)
@post_load
def make_geo_box(self, data, **kwargs):
return Geobox(**data)
words = fields.Str()
start = fields.Integer()
size = fields.Integer()
@ -664,6 +679,8 @@ class SearchSchema(ma.Schema):
ordered = True
date = fields.DateTime(allow_none=True)
map_data_only = fields.Boolean()
geo_box = ma.Nested(GeoboxSchema, allow_none=True, default=None)
@post_load
def make_search(self, data, **kwargs):

View File

@ -29,7 +29,7 @@ from app.resources.AdminNoteEndpoint import (
AdminNoteListByResourceEndpoint,
AdminNoteListEndpoint,
AdminNoteEndpoint)
from app.resources.StudyEndpoint import StudyEndpoint, StudyListEndpoint, StudyByStatusListEndpoint
from app.resources.StudyEndpoint import StudyEndpoint, StudyListEndpoint, StudyByStatusListEndpoint, StudyByAgeEndpoint
from app.resources.InvestigatorEndpoint import InvestigatorEndpoint, InvestigatorListEndpoint
from app.resources.SessionEndpoint import SessionEndpoint
from app.resources.CategoryEndpoint import (
@ -161,7 +161,6 @@ def root():
return jsonify(output)
endpoints = [
# Categories
(CategoryListEndpoint, "/category"),
@ -205,6 +204,7 @@ endpoints = [
# Studies
(StudyListEndpoint, "/study"),
(StudyByStatusListEndpoint, "/study/status/<status>"),
(StudyByAgeEndpoint, "/study/status/<status>/<age>"),
(StudyEndpoint, "/study/<id>"),
(CategoryByStudyEndpoint, "/study/<study_id>/category"),
(StudyCategoryListEndpoint, "/study_category"),

60
backend/config/docker.py Normal file
View File

@ -0,0 +1,60 @@
NAME = "STAR DRIVE Database"
VERSION = "0.1"
CORS_ENABLED = False
DEVELOPMENT = True
TESTING = True
MIRRORING = False
DELETE_RECORDS = True
EXPORT_CHECK_INTERNAL_MINUTES = 1
IMPORT_INTERVAL_MINUTES = 1
SQLALCHEMY_DATABASE_URI = "postgresql://ed_user:ed_pass@star-drive_db_1/stardrive"
# Elastic Search
ELASTIC_SEARCH = {
"index_prefix": "stardrive",
"hosts": ["star-drive_es_1"],
"port": 9200,
"timeout": 20,
"verify_certs": False,
"use_ssl": False,
"http_auth_user": "",
"http_auth_pass": ""
}
API_URL = "http://localhost:5000"
SITE_URL = "http://localhost:4200"
SECRET_KEY = 'stardrive_impossibly_bad_key_stored_in_public_repo_dont_use_this_outside_development_yuck!'
FRONTEND_AUTH_CALLBACK = SITE_URL + "/#/session"
FRONTEND_EMAIL_RESET = SITE_URL + "/#/reset_password/"
FRONTEND_FORGOT_PASSWORD = SITE_URL + "/#/forgot-password"
MAIL_SERVER = 'smtp.mailtrap.io'
MAIL_PORT = 2525
MAIL_USE_SSL = False
MAIL_USE_TLS = True
MAIL_USERNAME = "YOUR-MAILTRAP-NAME - Copy these lines to your instance/config! edit there."
MAIL_PASSWORD = "YOUR-MAILTRAP-PASSWORD - Copy these lines to your instance/config! edit there."
MAIL_DEFAULT_SENDER='someaddress@fake.com'
MAIL_DEFAULT_USER='someaddress@fake.com'
MAIL_TIMEOUT = 10
GOOGLE_MAPS_API_KEY = "PRODUCTION_API_KEY_GOES_HERE"
GOOGLE_ANALYTICS_API_KEY = "PRODUCTION_API_KEY_GOES_HERE"
ADMIN_EMAIL = "admin@tester.com"
PRINCIPAL_INVESTIGATOR_EMAIL = "pi@tester.com" # Receives some high levl alerts per agreement with InfoSec.
ADMIN_PASSWORD_REGEX = r"((?=.*\d)(?=.*[a-z])(?=.*[A-Z])(?=.*[\W]).{25})"
ADMIN_PASSWORD_INSTRUCTIONS = "Your password must be at least 25 characters long and contain at least one of each of " \
"the following: uppercase letters, lowercase letters, numbers, and punctuation " \
"characters. Don't use a password that you have used for any other purpose."
USER_PASSWORD_REGEX = r"((?=.*\d)(?=.*[a-z])(?=.*[A-Z])(?=.*[\W]).{8})"
USER_PASSWORD_INSTRUCTIONS = "Your password must be at least 8 characters long, but we recommend 20 characters. It " \
"should include at least one of each of the following: uppercase letters, " \
"lowercase letters, numbers, and punctuation characters."

11
backend/containerrun.sh Executable file
View File

@ -0,0 +1,11 @@
#! /bin/bash
es=${es:="star-drive_es_1"}
timeout 300 bash -c "until curl --silent --output /dev/null http://$es:9200/_cat/health?h=st; do printf '.'; sleep 5; done; printf '\n'"
flask db init
sleep 5
flask db migrate
sleep 5
flask db upgrade
sleep 5
psql $(grep -i "SQLALCHEMY_DATABASE_URI" /star-drive/backend/instance/config.py | awk -F'"' '{print $2}') -lqt | cut -d \| -f 1 | grep stardrive | grep 0 && flask initdb
flask run --host 0.0.0.0

View File

347
backend/tests/base_test.py Normal file
View File

@ -0,0 +1,347 @@
# Set environment variable to testing before loading.
# IMPORTANT - Environment must be loaded before app, models, etc....
import os
os.environ["TESTING"] = "true"
import base64
import datetime
import quopri
import re
from flask import json
from flask.json import JSONEncoder
from app import app, db, elastic_index
from app.model.questionnaires.challenging_behavior import ChallengingBehavior
from app.model.admin_note import AdminNote
from app.model.category import Category
from app.model.chain_step import ChainStep
from app.model.email_log import EmailLog
from app.model.event import Event
from app.model.event_user import EventUser
from app.model.investigator import Investigator
from app.model.location import Location
from app.model.participant import Participant
from app.model.resource import Resource
from app.model.resource_category import ResourceCategory
from app.model.resource_change_log import ResourceChangeLog
from app.model.step_log import StepLog
from app.model.study import Study, Status
from app.model.study_category import StudyCategory
from app.model.study_investigator import StudyInvestigator
from app.model.study_user import StudyUser
from app.model.user import User, Role
from app.model.user_favorite import UserFavorite
from app.model.zip_code import ZipCode
from app.model.user_meta import UserMeta
def clean_db(database):
database.session.commit()
for table in reversed(database.metadata.sorted_tables):
database.session.execute(table.delete())
database.session.commit()
class BaseTest:
auths = {}
@classmethod
def setUpClass(cls):
app.config.from_object('config.testing')
app.config.from_pyfile('testing.py')
cls.ctx = app.test_request_context()
cls.app = app.test_client()
db.create_all()
@classmethod
def tearDownClass(cls):
db.drop_all()
db.session.remove()
elastic_index.clear()
def setUp(self):
self.ctx.push()
clean_db(db)
elastic_index.clear()
self.auths = {}
def tearDown(self):
db.session.rollback()
self.ctx.pop()
def logged_in_headers(self, user=None):
# If no user is provided, generate a dummy Admin user
if not user:
existing_user = self.construct_user(email="admin@star.org", role=Role.admin)
else:
existing_user = User.query.filter_by(id=user.id).first()
if existing_user.id in self.auths:
return self.auths[existing_user.id]
self.auths[existing_user.id] = dict(
Authorization='Bearer ' + existing_user.encode_auth_token().decode())
return self.auths[existing_user.id]
def decode(self, encoded_words):
"""
Useful for checking the content of email messages
(which we store in an array for testing)
"""
encoded_word_regex = r'=\?{1}(.+)\?{1}([b|q])\?{1}(.+)\?{1}='
charset, encoding, encoded_text = re.match(encoded_word_regex,
encoded_words).groups()
if encoding == 'b':
byte_string = base64.b64decode(encoded_text)
elif encoding == 'q':
byte_string = quopri.decodestring(encoded_text)
text = byte_string.decode(charset)
text = text.replace("_", " ")
return text
def jsonify(self, data):
"""
Returns given data as JSON string, converting dates to ISO format.
"""
class DateTimeEncoder(JSONEncoder):
def default(self, obj):
if isinstance(obj, (datetime.date, datetime.datetime)):
return obj.isoformat()
return json.dumps(data, cls=DateTimeEncoder)
def assert_success(self, rv, msg=""):
try:
data = json.loads(rv.get_data(as_text=True))
self.assertTrue(rv.status_code >= 200 and rv.status_code < 300,
"BAD Response: %i. \n %s" %
(rv.status_code, self.jsonify(data)) + ". " + msg)
except:
self.assertTrue(rv.status_code >= 200 and rv.status_code < 300,
"BAD Response: %i." % rv.status_code + ". " + msg)
def construct_user(self, email="stan@staunton.com", role=Role.user, last_login=datetime.datetime.now()):
db_user = db.session.query(User).filter_by(email=email).first()
if db_user:
return db_user
user = User(email=email, role=role, last_login=last_login)
db.session.add(user)
db_user = db.session.query(User).filter_by(email=user.email).first()
self.assertEqual(db_user.email, user.email)
return db_user
def construct_participant(self, user, relationship):
participant = Participant(user=user, relationship=relationship)
db.session.add(participant)
db.session.commit()
# db_participant = db.session.query(Participant).filter_by(id=participant.id).first()
# self.assertEqual(db_participant.relationship, participant.relationship)
return participant
def construct_usermeta(self, user):
usermeta = UserMeta(id=user.id)
db.session.add(usermeta)
db.session.commit()
return usermeta
def construct_admin_note(self, user, resource, id=976, note="I think all sorts of things about this resource and I'm telling you now."):
admin_note = AdminNote(id=id, user_id=user.id, resource_id=resource.id, note=note)
db.session.add(admin_note)
db.session.commit()
db_admin_note = db.session.query(AdminNote).filter_by(id=admin_note.id).first()
self.assertEqual(db_admin_note.note, admin_note.note)
return db_admin_note
def construct_category(self, name="Ultimakers", parent=None):
category = Category(name=name)
if parent is not None:
category.parent = parent
db.session.add(category)
db_category = db.session.query(Category).filter_by(name=category.name).first()
self.assertIsNotNone(db_category.id)
return db_category
def construct_resource(self, title="A+ Resource", description="A delightful Resource destined to create rejoicing",
phone="555-555-5555", website="http://stardrive.org", is_draft=False,
organization_name="Some Org", categories=[], ages=[], languages=[], covid19_categories=[], is_uva_education_content=False):
resource = Resource(title=title, description=description, phone=phone, website=website, ages=ages,
organization_name=organization_name, is_draft=is_draft, languages=languages,
covid19_categories=covid19_categories, is_uva_education_content=is_uva_education_content)
db.session.add(resource)
db.session.commit()
for category in categories:
rc = ResourceCategory(resource_id=resource.id, category=category, type='resource')
db.session.add(rc)
db_resource = db.session.query(Resource).filter_by(title=resource.title).first()
self.assertEqual(db_resource.website, resource.website)
elastic_index.add_document(db_resource, 'Resource')
return db_resource
def construct_location(self, title="A+ location", description="A delightful location destined to create rejoicing",
street_address1="123 Some Pl", street_address2="Apt. 45", is_draft=False,
city="Stauntonville", state="QX", zip="99775", phone="555-555-5555",
website="http://stardrive.org", latitude=38.98765, longitude=-93.12345,
organization_name="Location Org"):
location = Location(title=title, description=description, street_address1=street_address1,
street_address2=street_address2, city=city, state=state, zip=zip,phone=phone,
website=website, latitude=latitude, longitude=longitude, is_draft=is_draft,
organization_name=organization_name)
db.session.add(location)
db.session.commit()
db_location = db.session.query(Location).filter_by(title=location.title).first()
self.assertEqual(db_location.website, location.website)
elastic_index.add_document(document=db_location, flush=True, latitude=latitude, longitude=longitude)
return db_location
def construct_location_category(self, location_id, category_name):
c = self.construct_category(name=category_name)
rc = ResourceCategory(resource_id=location_id, category=c, type='location')
db.session.add(rc)
db.session.commit()
return c
def construct_study_category(self, study_id, category_name):
c = self.construct_category(name=category_name)
sc = StudyCategory(study_id=study_id, category=c)
db.session.add(sc)
db.session.commit()
return c
def construct_study(self, title="Fantastic Study", description="A study that will go down in history",
participant_description="Even your pet hamster could benefit from participating in this study",
benefit_description="You can expect to have your own rainbow following you around afterwards",
coordinator_email="hello@study.com", categories=[], organization_name="Study Org"):
study = Study(title=title, description=description, participant_description=participant_description,
benefit_description=benefit_description, status=Status.currently_enrolling,
coordinator_email=coordinator_email, organization_name=organization_name)
db.session.add(study)
db.session.commit()
db_study = db.session.query(Study).filter_by(title=study.title).first()
self.assertEqual(db_study.description, description)
for category in categories:
sc = StudyCategory(study_id=db_study.id, category_id=category.id)
db.session.add(sc)
db.session.commit()
elastic_index.add_document(db_study, 'Study')
db_study = db.session.query(Study).filter_by(id=db_study.id).first()
self.assertEqual(len(db_study.categories), len(categories))
return db_study
def construct_investigator(self, name="Judith Wonder", title="Ph.D., Assistant Professor of Mereology"):
investigator = Investigator(name=name, title=title)
investigator.organization_name = "Investigator Org"
db.session.add(investigator)
db.session.commit()
db_inv = db.session.query(Investigator).filter_by(name=investigator.name).first()
self.assertEqual(db_inv.title, investigator.title)
return db_inv
def construct_event(self, title="A+ Event", description="A delightful event destined to create rejoicing",
street_address1="123 Some Pl", street_address2="Apt. 45", is_draft=False, city="Stauntonville",
state="QX", zip="99775", phone="555-555-5555", website="http://stardrive.org",
date=datetime.datetime.now() + datetime.timedelta(days=7), organization_name="Event Org",
post_survey_link="http://stardrive.org/survey", webinar_link="http://stardrive.org/event",
includes_registration=True, max_users=35, registered_users=None, post_event_description=None):
if registered_users is None:
registered_users = [self.construct_user(email="e1@sartography.com"),
self.construct_user("e2@sartography.com")]
event = Event(title=title, description=description, street_address1=street_address1,
street_address2=street_address2, city=city, state=state, zip=zip, phone=phone, website=website,
date=date, is_draft=is_draft, organization_name=organization_name, webinar_link=webinar_link,
post_survey_link=post_survey_link, includes_registration=includes_registration, max_users=max_users,
post_event_description=post_event_description)
db.session.add(event)
db_event = db.session.query(Event).filter_by(title=event.title).first()
self.assertEqual(db_event.website, event.website)
for user in registered_users:
eu = EventUser(event_id=db_event.id, user_id=user.id)
db.session.add(eu)
elastic_index.add_document(db_event, 'Event')
return db_event
def construct_zip_code(self, id=24401, latitude=38.146216, longitude=-79.07625):
z = ZipCode(id=id, latitude=latitude, longitude=longitude)
db.session.add(z)
db.session.commit()
db_z = ZipCode.query.filter_by(id=id).first()
self.assertEqual(db_z.id, z.id)
self.assertEqual(db_z.latitude, z.latitude)
self.assertEqual(db_z.longitude, z.longitude)
return db_z
def construct_chain_steps(self):
num_steps = db.session.query(ChainStep).count()
if num_steps == 0:
self.construct_chain_step(id=0, name="time_warp_01", instruction="Jump to the left")
self.construct_chain_step(id=1, name="time_warp_02", instruction="Step to the right")
self.construct_chain_step(id=2, name="time_warp_03", instruction="Put your hands on your hips")
self.construct_chain_step(id=3, name="time_warp_04", instruction="Pull your knees in tight")
return db.session.query(ChainStep).all()
def construct_chain_step(self, id=0, name="time_warp_01", instruction="Jump to the left", last_updated=datetime.datetime.now()):
db.session.add(ChainStep(id=id, name=name, instruction=instruction, last_updated=last_updated))
db.session.commit()
return db.session.query(ChainStep).filter(ChainStep.id == id).first()
def construct_everything(self):
self.construct_all_questionnaires()
cat = self.construct_category()
self.construct_resource()
study = self.construct_study()
location = self.construct_location()
self.construct_event()
self.construct_location_category(location.id, cat.name)
self.construct_study_category(study.id, cat.name)
self.construct_zip_code()
investigator = Investigator(name="Sam I am")
db.session.add(StudyInvestigator(study=study, investigator=investigator))
db.session.add(StudyUser(study=study, user=self.construct_user()))
db.session.add(AdminNote(user_id=self.construct_user().id, resource_id=self.construct_resource().id, note=''))
db.session.add(UserFavorite(user_id=self.construct_user().id))
db.session.add(investigator)
db.session.add(EmailLog())
db.session.add(ResourceChangeLog())
db.session.add(StepLog())
db.session.commit()
def get_identification_questionnaire(self, participant_id):
return {
'first_name': "Darah",
'middle_name': "Soo",
'last_name': "Ubway",
'is_first_name_preferred': True,
'birthdate': '2002-02-02T00:00:00.000000Z',
'birth_city': 'Staunton',
'birth_state': 'VA',
'is_english_primary': True,
'participant_id': participant_id
}

View File

@ -0,0 +1,700 @@
import random
import string
from dateutil import parser
from tests.base_test import BaseTest
from app import db
from app.model.participant import Relationship
from app.model.questionnaires.alternative_augmentative import AlternativeAugmentative
from app.model.questionnaires.assistive_device import AssistiveDevice
from app.model.questionnaires.chain_questionnaire import ChainQuestionnaire
from app.model.questionnaires.chain_session import ChainSession
from app.model.questionnaires.chain_session_step import ChainSessionStep
from app.model.questionnaires.clinical_diagnoses_questionnaire import ClinicalDiagnosesQuestionnaire
from app.model.questionnaires.contact_questionnaire import ContactQuestionnaire
from app.model.questionnaires.current_behaviors_dependent_questionnaire import CurrentBehaviorsDependentQuestionnaire
from app.model.questionnaires.current_behaviors_self_questionnaire import CurrentBehaviorsSelfQuestionnaire
from app.model.questionnaires.demographics_questionnaire import DemographicsQuestionnaire
from app.model.questionnaires.developmental_questionnaire import DevelopmentalQuestionnaire
from app.model.questionnaires.education_dependent_questionnaire import EducationDependentQuestionnaire
from app.model.questionnaires.education_self_questionnaire import EducationSelfQuestionnaire
from app.model.questionnaires.employment_questionnaire import EmploymentQuestionnaire
from app.model.questionnaires.evaluation_history_dependent_questionnaire import EvaluationHistoryDependentQuestionnaire
from app.model.questionnaires.evaluation_history_self_questionnaire import EvaluationHistorySelfQuestionnaire
from app.model.questionnaires.home_dependent_questionnaire import HomeDependentQuestionnaire
from app.model.questionnaires.home_self_questionnaire import HomeSelfQuestionnaire
from app.model.questionnaires.housemate import Housemate
from app.model.questionnaires.identification_questionnaire import IdentificationQuestionnaire
from app.model.questionnaires.medication import Medication
from app.model.questionnaires.professional_profile_questionnaire import ProfessionalProfileQuestionnaire
from app.model.questionnaires.registration_questionnaire import RegistrationQuestionnaire
from app.model.questionnaires.supports_questionnaire import SupportsQuestionnaire
from app.model.questionnaires.therapy import Therapy
from app.model.user import User, Role
class BaseTestQuestionnaire(BaseTest):
"""Tools for building questionnaires of all types and descriptions."""
def randomString(self):
char_set = string.ascii_uppercase + string.digits
return ''.join(random.sample(char_set * 6, 6))
def get_field_from_response(self, response, name):
for field in response['fields']:
if field["name"] == name:
return field
def construct_admin_user(self, email="rmond@virginia.gov"):
user = User(email=email, role=Role.admin)
db.session.add(user)
db.session.commit()
db_user = db.session.query(User).filter_by(email=user.email).first()
self.assertEqual(db_user.role, user.role)
return db_user
def construct_assistive_device(self, type_group='mobility', type='prosthetic', timeframe='current',
notes='I love my new leg!', supports_questionnaire=None):
ad = AssistiveDevice(type=type, timeframe=timeframe, notes=notes)
if supports_questionnaire is not None:
ad.supports_questionnaire_id = supports_questionnaire.id
db.session.add(ad)
db.session.commit()
db_ad = db.session.query(AssistiveDevice).filter_by(id=ad.id).first()
self.assertEqual(db_ad.notes, ad.notes)
self.assertEqual(db_ad.type_group, ad.type_group)
self.assertEqual(db_ad.type, ad.type)
return db_ad
def construct_alternative_augmentative(self, type='lowTechAAC', timeframe='current', notes='We use pen and paper', supports_questionnaire=None):
aac = AlternativeAugmentative(type=type, timeframe=timeframe, notes=notes)
if supports_questionnaire is not None:
aac.supports_questionnaire_id = supports_questionnaire.id
db.session.add(aac)
db.session.commit()
db_aac = db.session.query(AlternativeAugmentative).filter_by(last_updated=aac.last_updated).first()
self.assertEqual(db_aac.notes, aac.notes)
return db_aac
def construct_clinical_diagnoses_questionnaire(self, developmental=['speechLanguage'], mental_health=['ocd'],
medical=['insomnia'], genetic=['corneliaDeLange'], participant=None,
user=None):
cdq = ClinicalDiagnosesQuestionnaire(developmental=developmental, mental_health=mental_health, medical=medical,
genetic=genetic)
if user is None:
u = self.construct_user(email='clinic@questionnaire.com')
cdq.user_id = u.id
else:
u = user
cdq.user_id = u.id
if participant is None:
cdq.participant_id = self.construct_participant(user=u, relationship=Relationship.dependent).id
else:
cdq.participant_id = participant.id
db.session.add(cdq)
db.session.commit()
db_cdq = db.session.query(ClinicalDiagnosesQuestionnaire).filter_by(user_id=cdq.user_id).first()
self.assertEqual(db_cdq.participant_id, cdq.participant_id)
return db_cdq
def construct_contact_questionnaire(self, phone="123-456-7890", zip=55678, marketing_channel="Zine Ad",
participant=None, user=None):
cq = ContactQuestionnaire(phone=phone, zip=zip,
marketing_channel=marketing_channel)
if user is None:
u = self.construct_user(email='contact@questionnaire.com')
cq.user_id = u.id
else:
u = user
cq.user_id = u.id
if participant is None:
cq.participant_id = self.construct_participant(user=u, relationship=Relationship.dependent).id
else:
cq.participant_id = participant.id
db.session.add(cq)
db.session.commit()
db_cq = db.session.query(ContactQuestionnaire).filter_by(zip=cq.zip).first()
self.assertEqual(db_cq.phone, cq.phone)
return db_cq
def construct_current_behaviors_dependent_questionnaire(self, dependent_verbal_ability='fluent',
concerning_behaviors=['hoarding'],
has_academic_difficulties=True,
academic_difficulty_areas=['math', 'writing'],
participant=None, user=None):
cb = CurrentBehaviorsDependentQuestionnaire(dependent_verbal_ability=dependent_verbal_ability,
concerning_behaviors=concerning_behaviors,
has_academic_difficulties=has_academic_difficulties,
academic_difficulty_areas=academic_difficulty_areas)
if user is None:
u = self.construct_user(email='cbd@questionnaire.com')
cb.user_id = u.id
else:
u = user
cb.user_id = u.id
if participant is None:
p = self.construct_participant(user=u, relationship=Relationship.dependent)
cb.participant_id = p.id
else:
p = participant
cb.participant_id = p.id
db.session.add(cb)
db.session.commit()
db_cbd = db.session.query(CurrentBehaviorsDependentQuestionnaire).filter_by(
participant_id=cb.participant_id).first()
self.assertEqual(db_cbd.concerning_behaviors, cb.concerning_behaviors)
return db_cbd
def construct_current_behaviors_self_questionnaire(self, self_verbal_ability='verbal',
has_academic_difficulties=True, academic_difficulty_areas='math',
participant=None, user=None):
cb = CurrentBehaviorsSelfQuestionnaire(self_verbal_ability=self_verbal_ability,
has_academic_difficulties=has_academic_difficulties,
academic_difficulty_areas=academic_difficulty_areas)
if user is None:
u = self.construct_user(email='cbs@questionnaire.com')
cb.user_id = u.id
else:
u = user
cb.user_id = u.id
if participant is None:
p = self.construct_participant(user=u, relationship=Relationship.self_participant)
cb.participant_id = p.id
else:
p = participant
cb.participant_id = p.id
db.session.add(cb)
db.session.commit()
db_cb = db.session.query(CurrentBehaviorsSelfQuestionnaire).filter_by(participant_id=cb.participant_id).first()
self.assertEqual(db_cb.academic_difficulty_areas, cb.academic_difficulty_areas)
return db_cb
def construct_demographics_questionnaire(self, birth_sex="intersex", gender_identity="intersex",
race_ethnicity="raceBlack", participant=None, user=None):
dq = DemographicsQuestionnaire(birth_sex=birth_sex, gender_identity=gender_identity,
race_ethnicity=race_ethnicity)
if user is None:
u = self.construct_user(email='demograph@questionnaire.com')
dq.user_id = u.id
else:
u = user
dq.user_id = u.id
if participant is None:
dq.participant_id = self.construct_participant(user=u, relationship=Relationship.self_participant).id
else:
dq.participant_id = participant.id
db.session.add(dq)
db.session.commit()
db_dq = db.session.query(DemographicsQuestionnaire).filter_by(birth_sex=dq.birth_sex).first()
self.assertEqual(db_dq.gender_identity, dq.gender_identity)
return db_dq
def construct_developmental_questionnaire(self, had_birth_complications=False, when_motor_milestones='delayed',
when_language_milestones='early', when_toileting_milestones='notYet',
participant=None, user=None):
dq = DevelopmentalQuestionnaire(had_birth_complications=had_birth_complications,
when_motor_milestones=when_motor_milestones,
when_language_milestones=when_language_milestones,
when_toileting_milestones=when_toileting_milestones)
if user is None:
u = self.construct_user(email='develop@questionnaire.com')
dq.user_id = u.id
else:
u = user
dq.user_id = u.id
if participant is None:
dq.participant_id = self.construct_participant(user=u, relationship=Relationship.dependent).id
else:
dq.participant_id = participant.id
db.session.add(dq)
db.session.commit()
db_dq = db.session.query(DevelopmentalQuestionnaire).filter_by(participant_id=dq.participant_id).first()
self.assertEqual(db_dq.when_language_milestones, dq.when_language_milestones)
return db_dq
def construct_education_dependent_questionnaire(self, attends_school=True, school_name='Harvard',
school_type='privateSchool',
dependent_placement='graduate', participant=None, user=None):
eq = EducationDependentQuestionnaire(attends_school=attends_school, school_name=school_name,
school_type=school_type,
dependent_placement=dependent_placement)
if user is None:
u = self.construct_user(email='edudep@questionnaire.com')
eq.user_id = u.id
else:
u = user
eq.user_id = u.id
if participant is None:
p = self.construct_participant(user=u, relationship=Relationship.dependent)
eq.participant_id = p.id
else:
p = participant
eq.participant_id = p.id
db.session.add(eq)
db.session.commit()
db_eq = db.session.query(EducationDependentQuestionnaire).filter_by(participant_id=eq.participant_id).first()
self.assertEqual(db_eq.school_name, eq.school_name)
return db_eq
def construct_education_self_questionnaire(self, attends_school=True, school_name='Harvard',
school_type='privateSchool',
self_placement='graduate', participant=None, user=None):
eq = EducationSelfQuestionnaire(attends_school=attends_school, school_name=school_name, school_type=school_type,
self_placement=self_placement)
if user is None:
u = self.construct_user(email='eduself@questionnaire.com')
eq.user_id = u.id
else:
u = user
eq.user_id = u.id
if participant is None:
p = self.construct_participant(user=u, relationship=Relationship.self_participant)
eq.participant_id = p.id
else:
p = participant
eq.participant_id = p.id
db.session.add(eq)
db.session.commit()
db_eq = db.session.query(EducationSelfQuestionnaire).filter_by(participant_id=eq.participant_id).first()
self.assertEqual(db_eq.school_name, eq.school_name)
return db_eq
def construct_employment_questionnaire(self, is_currently_employed=True, employment_capacity='fullTime',
has_employment_support=False, participant=None, user=None):
eq = EmploymentQuestionnaire(is_currently_employed=is_currently_employed,
employment_capacity=employment_capacity,
has_employment_support=has_employment_support)
if user is None:
u = self.construct_user(email='employ@questionnaire.com')
eq.user_id = u.id
else:
u = user
eq.user_id = u.id
if participant is None:
eq.participant_id = self.construct_participant(user=u, relationship=Relationship.self_participant).id
else:
eq.participant_id = participant.id
db.session.add(eq)
db.session.commit()
db_eq = db.session.query(EmploymentQuestionnaire).filter_by(participant_id=eq.participant_id).first()
self.assertEqual(db_eq.employment_capacity, eq.employment_capacity)
return db_eq
def construct_evaluation_history_dependent_questionnaire(self, self_identifies_autistic=True,
has_autism_diagnosis=True,
years_old_at_first_diagnosis=7,
who_diagnosed="pediatrician",
participant=None, user=None):
ehq = EvaluationHistoryDependentQuestionnaire(self_identifies_autistic=self_identifies_autistic,
has_autism_diagnosis=has_autism_diagnosis,
years_old_at_first_diagnosis=years_old_at_first_diagnosis,
who_diagnosed=who_diagnosed)
if user is None:
u = self.construct_user(email='evaldep@questionnaire.com')
ehq.user_id = u.id
else:
u = user
ehq.user_id = u.id
if participant is None:
p = self.construct_participant(user=u, relationship=Relationship.dependent)
ehq.participant_id = p.id
else:
p = participant
ehq.participant_id = p.id
db.session.add(ehq)
db.session.commit()
db_ehq = db.session.query(EvaluationHistoryDependentQuestionnaire).filter_by(
participant_id=ehq.participant_id).first()
self.assertEqual(db_ehq.who_diagnosed, ehq.who_diagnosed)
return db_ehq
def construct_evaluation_history_self_questionnaire(self, self_identifies_autistic=True, has_autism_diagnosis=True,
years_old_at_first_diagnosis=7, who_diagnosed="pediatrician",
participant=None, user=None):
ehq = EvaluationHistorySelfQuestionnaire(self_identifies_autistic=self_identifies_autistic,
has_autism_diagnosis=has_autism_diagnosis,
years_old_at_first_diagnosis=years_old_at_first_diagnosis,
who_diagnosed=who_diagnosed)
if user is None:
u = self.construct_user(email='evalself@questionnaire.com')
ehq.user_id = u.id
else:
u = user
ehq.user_id = u.id
if participant is None:
p = self.construct_participant(user=u, relationship=Relationship.self_participant)
ehq.participant_id = p.id
else:
p = participant
ehq.participant_id = p.id
db.session.add(ehq)
db.session.commit()
db_ehq = db.session.query(EvaluationHistorySelfQuestionnaire).filter_by(
participant_id=ehq.participant_id).first()
self.assertEqual(db_ehq.who_diagnosed, ehq.who_diagnosed)
return db_ehq
def construct_home_dependent_questionnaire(self, dependent_living_situation='fullTimeGuardian', housemates=None,
struggle_to_afford=False, participant=None, user=None):
hq = HomeDependentQuestionnaire(dependent_living_situation=dependent_living_situation,
struggle_to_afford=struggle_to_afford)
if user is None:
u = self.construct_user(email='homedep@questionnaire.com')
hq.user_id = u.id
else:
u = user
hq.user_id = u.id
if participant is None:
p = self.construct_participant(user=u, relationship=Relationship.dependent)
hq.participant_id = p.id
else:
p = participant
hq.participant_id = p.id
db.session.add(hq)
db.session.commit()
if housemates is None:
self.construct_housemate(home_dependent_questionnaire=hq)
else:
hq.housemates = housemates
db_hq = db.session.query(HomeDependentQuestionnaire).filter_by(participant_id=hq.participant_id).first()
self.assertEqual(db_hq.dependent_living_situation, hq.dependent_living_situation)
return db_hq
def construct_home_self_questionnaire(self, self_living_situation='alone', housemates=None,
struggle_to_afford=False,
participant=None, user=None):
hq = HomeSelfQuestionnaire(self_living_situation=self_living_situation, struggle_to_afford=struggle_to_afford)
if user is None:
u = self.construct_user(email='homeself@questionnaire.com')
hq.user_id = u.id
else:
u = user
hq.user_id = u.id
if participant is None:
p = self.construct_participant(user=u, relationship=Relationship.self_participant)
hq.participant_id = p.id
else:
p = participant
hq.participant_id = p.id
db.session.add(hq)
db.session.commit()
if housemates is None:
self.construct_housemate(home_self_questionnaire=hq)
else:
hq.housemates = housemates
db_hq = db.session.query(HomeSelfQuestionnaire).filter_by(participant_id=hq.participant_id).first()
self.assertEqual(db_hq.self_living_situation, hq.self_living_situation)
return db_hq
def construct_housemate(self, name="Fred Fredly", relationship='bioSibling', age=23, has_autism=True,
home_dependent_questionnaire=None, home_self_questionnaire=None):
h = Housemate(name=name, relationship=relationship, age=age, has_autism=has_autism)
if home_dependent_questionnaire is not None:
h.home_dependent_questionnaire_id = home_dependent_questionnaire.id
if home_self_questionnaire is not None:
h.home_self_questionnaire_id = home_self_questionnaire.id
db.session.add(h)
db.session.commit()
db_h = db.session.query(Housemate).filter_by(last_updated=h.last_updated).first()
self.assertEqual(db_h.relationship, h.relationship)
return db_h
def construct_identification_questionnaire(self, relationship_to_participant='adoptFather', first_name='Karl',
is_first_name_preferred=False, nickname='Big K', birth_state='VA',
is_english_primary=False, participant=None, user=None):
iq = IdentificationQuestionnaire(relationship_to_participant=relationship_to_participant, first_name=first_name,
is_first_name_preferred=is_first_name_preferred, nickname=nickname,
birth_state=birth_state, is_english_primary=is_english_primary)
if user is None:
u = self.construct_user(email='ident@questionnaire.com')
iq.user_id = u.id
else:
u = user
iq.user_id = u.id
if participant is None:
iq.participant_id = self.construct_participant(user=u, relationship=Relationship.dependent).id
else:
iq.participant_id = participant.id
db.session.add(iq)
db.session.commit()
db_iq = db.session.query(IdentificationQuestionnaire).filter_by(participant_id=iq.participant_id).first()
self.assertEqual(db_iq.nickname, iq.nickname)
return db_iq
def construct_professional_questionnaire(self, purpose="profResearch",
professional_identity=["artTher", "profOther"],
professional_identity_other="Astronaut",
learning_interests=["insuranceCov", "learnOther"],
learning_interests_other="Data plotting using dried fruit",
currently_work_with_autistic=True, previous_work_with_autistic=False,
length_work_with_autistic='3 minutes', participant=None, user=None):
pq = ProfessionalProfileQuestionnaire(purpose=purpose, professional_identity=professional_identity,
professional_identity_other=professional_identity_other,
learning_interests=learning_interests,
learning_interests_other=learning_interests_other,
currently_work_with_autistic=currently_work_with_autistic,
previous_work_with_autistic=previous_work_with_autistic,
length_work_with_autistic=length_work_with_autistic)
if user is None:
u = self.construct_user(email='prof@questionnaire.com')
pq.user_id = u.id
else:
u = user
pq.user_id = u.id
if participant is None:
pq.participant_id = self.construct_participant(user=u, relationship=Relationship.dependent).id
else:
pq.participant_id = participant.id
db.session.add(pq)
db.session.commit()
db_pq = db.session.query(ProfessionalProfileQuestionnaire).filter_by(participant_id=pq.participant_id).first()
self.assertEqual(db_pq.learning_interests, pq.learning_interests)
return db_pq
def construct_registration_questionnaire(self, first_name='Nora', last_name='Bora', email='nora@bora.com',
zip_code=24401, relationship_to_autism=None, marketing_channel=None,
user=None, event=None):
rq = RegistrationQuestionnaire(first_name=first_name, last_name=last_name, email=email, zip_code=zip_code,
relationship_to_autism=relationship_to_autism,
marketing_channel=marketing_channel)
if marketing_channel is None:
rq.marketing_channel = ['drive', 'facebook']
if relationship_to_autism is None:
rq.relationship_to_autism = ['self', 'professional']
if user is None:
u = self.construct_user(email='nora@bora.com')
rq.user_id = u.id
else:
u = user
rq.user_id = u.id
if event is None:
rq.event_id = self.construct_event(title='Webinar: You have to be here (virtually)').id
else:
rq.event_id = event.id
db.session.add(rq)
db.session.commit()
db_rq = db.session.query(RegistrationQuestionnaire).filter_by(user_id=rq.user_id).first()
self.assertEqual(db_rq.email, rq.email)
return db_rq
def construct_medication(self, symptom='symptomInsomnia', name='Magic Potion', notes='I feel better than ever!', supports_questionnaire=None):
m = Medication(symptom=symptom, name=name, notes=notes)
if supports_questionnaire is not None:
m.supports_questionnaire_id = supports_questionnaire.id
db.session.add(m)
db.session.commit()
db_m = db.session.query(Medication).filter_by(last_updated=m.last_updated).first()
self.assertEqual(db_m.notes, m.notes)
return db_m
def construct_therapy(self, type='behavioral', timeframe='current', notes='Small steps',
supports_questionnaire=None):
t = Therapy(type=type, timeframe=timeframe, notes=notes)
if supports_questionnaire is not None:
t.supports_questionnaire_id = supports_questionnaire.id
db.session.add(t)
db.session.commit()
db_t = db.session.query(Therapy).filter_by(last_updated=t.last_updated).first()
self.assertEqual(db_t.notes, t.notes)
return db_t
def construct_supports_questionnaire(self, medications=None, therapies=None, assistive_devices=None,
alternative_augmentative=None, participant=None, user=None):
sq = SupportsQuestionnaire()
if user is None:
u = self.construct_user(email='support@questionnaire.com')
sq.user_id = u.id
else:
u = user
sq.user_id = u.id
if participant is None:
sq.participant_id = self.construct_participant(user=u, relationship=Relationship.dependent).id
else:
sq.participant_id = participant.id
db.session.add(sq)
db.session.commit()
if assistive_devices is None:
self.construct_assistive_device(supports_questionnaire=sq)
else:
sq.assistive_devices = assistive_devices
if alternative_augmentative is None:
self.construct_alternative_augmentative(supports_questionnaire=sq)
else:
sq.alternative_augmentative = alternative_augmentative
if medications is None:
self.construct_medication(supports_questionnaire=sq)
else:
sq.medications = medications
if therapies is None:
self.construct_therapy(supports_questionnaire=sq)
else:
sq.therapies = therapies
db_sq = db.session.query(SupportsQuestionnaire).filter_by(participant_id=sq.participant_id).first()
self.assertEqual(db_sq.last_updated, sq.last_updated)
return db_sq
def construct_chain_session_questionnaire(self, participant=None, user=None):
self.construct_chain_steps()
bq = ChainQuestionnaire()
if user is None:
u = self.construct_user(email='edudep@questionnaire.com', role=Role.researcher)
bq.user_id = u.id
else:
u = user
bq.user_id = u.id
if participant is None:
p = self.construct_participant(user=u, relationship=Relationship.dependent)
bq.participant_id = p.id
else:
p = participant
bq.participant_id = p.id
session_date = parser.parse("2020-12-14T17:46:14.030Z")
session_1_step_1 = ChainSessionStep(
date=session_date,
chain_step_id=0,
status="focus",
completed=False,
was_prompted=True,
prompt_level="partial_physical",
had_challenging_behavior=True,
reason_step_incomplete="challenging_behavior",
num_stars=0,
)
session_1 = ChainSession(date=session_date)
session_1.step_attempts = [session_1_step_1]
bq.sessions = [session_1]
db.session.add(bq)
db.session.commit()
db_bq = db.session.query(ChainQuestionnaire).filter_by(participant_id=bq.participant_id).first()
self.assertEqual(db_bq.participant_id, bq.participant_id)
self.assertEqual(db_bq.user_id, bq.user_id)
self.assertEqual(db_bq.sessions, bq.sessions)
self.assertEqual(db_bq.sessions[0].date, session_date)
self.assertEqual(db_bq.sessions[0].step_attempts[0].date, session_date)
self.assertEqual(db_bq.sessions[0].step_attempts[0].num_stars, 0)
return db_bq
def construct_all_questionnaires(self, user=None):
if user is None:
user = self.construct_user()
participant = self.construct_participant(user=user, relationship=Relationship.dependent)
self.construct_usermeta(user=user)
self.construct_clinical_diagnoses_questionnaire(user=user, participant=participant)
self.construct_contact_questionnaire(user=user, participant=participant)
self.construct_current_behaviors_dependent_questionnaire(user=user, participant=participant)
self.construct_current_behaviors_self_questionnaire(user=user, participant=participant)
self.construct_demographics_questionnaire(user=user, participant=participant)
self.construct_developmental_questionnaire(user=user, participant=participant)
self.construct_education_dependent_questionnaire(user=user, participant=participant)
self.construct_education_self_questionnaire(user=user, participant=participant)
self.construct_employment_questionnaire(user=user, participant=participant)
self.construct_evaluation_history_dependent_questionnaire(user=user, participant=participant)
self.construct_evaluation_history_self_questionnaire(user=user, participant=participant)
self.construct_home_dependent_questionnaire(user=user, participant=participant)
self.construct_home_self_questionnaire(user=user, participant=participant)
self.construct_identification_questionnaire(user=user, participant=participant)
self.construct_professional_questionnaire(user=user, participant=participant)
self.construct_supports_questionnaire(user=user, participant=participant)
self.construct_registration_questionnaire(user=user)
self.construct_chain_session_questionnaire(user=user, participant=participant)

View File

@ -0,0 +1,121 @@
import unittest
from flask import json
from tests.base_test import BaseTest
from app import db
from app.model.admin_note import AdminNote
class TestAdminNote(BaseTest, unittest.TestCase):
def test_admin_note_basics(self):
u = self.construct_user()
l = self.construct_location()
self.construct_admin_note(id=377, user=u, resource=l, note="This resource is related to an event record")
an = db.session.query(AdminNote).first()
self.assertIsNotNone(an)
rv = self.app.get('/api/admin_note/%i' % an.id,
follow_redirects=True,
content_type="application/json", headers=self.logged_in_headers())
self.assert_success(rv)
response = json.loads(rv.get_data(as_text=True))
self.assertEqual(response["id"], an.id)
self.assertEqual(response["note"], 'This resource is related to an event record')
def test_modify_admin_note_basics(self):
u = self.construct_user()
e = self.construct_event()
self.construct_admin_note(id=342, user=u, resource=e, note="This event is related to a location record")
an = db.session.query(AdminNote).first()
self.assertIsNotNone(an)
rv = self.app.get('/api/admin_note/%i' % an.id, content_type="application/json", headers=self.logged_in_headers())
response = json.loads(rv.get_data(as_text=True))
response['note'] = 'Related to Location #42'
rv = self.app.put('/api/admin_note/%i' % an.id, data=self.jsonify(response), content_type="application/json",
follow_redirects=True, headers=self.logged_in_headers())
self.assert_success(rv)
db.session.commit()
rv = self.app.get('/api/admin_note/%i' % an.id, content_type="application/json", headers=self.logged_in_headers())
self.assert_success(rv)
response = json.loads(rv.get_data(as_text=True))
self.assertEqual(response['note'], 'Related to Location #42')
def test_delete_admin_note(self):
an = self.construct_admin_note(user=self.construct_user(), resource=self.construct_resource())
an_id = an.id
rv = self.app.get('api/admin_note/%i' % an_id, content_type="application/json", headers=self.logged_in_headers())
self.assert_success(rv)
rv = self.app.delete('api/admin_note/%i' % an_id, content_type="application/json", headers=self.logged_in_headers())
self.assert_success(rv)
rv = self.app.get('api/admin_note/%i' % an_id, content_type="application/json", headers=self.logged_in_headers())
self.assertEqual(404, rv.status_code)
def test_create_admin_note(self):
admin_note = {'note': "My Favorite Things", 'user_id': self.construct_user().id, 'resource_id': self.construct_resource().id}
rv = self.app.post('api/admin_note', data=self.jsonify(admin_note), content_type="application/json",
follow_redirects=True, headers=self.logged_in_headers())
self.assert_success(rv)
response = json.loads(rv.get_data(as_text=True))
self.assertEqual(response['note'], 'My Favorite Things')
self.assertIsNotNone(response['id'])
def test_admin_note_by_user_basics(self):
u = self.construct_user()
r = self.construct_resource()
self.construct_admin_note(id=467, user=u, resource=r, note="Lotsa stuff to say about this resource")
an = db.session.query(AdminNote).first()
self.assertIsNotNone(an)
rv = self.app.get('/api/user/%i/admin_note' % u.id,
follow_redirects=True,
content_type="application/json", headers=self.logged_in_headers())
self.assert_success(rv)
response = json.loads(rv.get_data(as_text=True))
self.assertEqual(response[0]["id"], an.id)
self.assertEqual(response[0]["id"], 467)
self.assertEqual(response[0]["note"], 'Lotsa stuff to say about this resource')
def test_admin_note_by_resource_basics(self):
u = self.construct_user()
r = self.construct_resource()
self.construct_admin_note(user=u, resource=r, note="This resource is a duplicate")
an = db.session.query(AdminNote).first()
self.assertIsNotNone(an)
rv = self.app.get('/api/resource/%i/admin_note' % r.id,
follow_redirects=True,
content_type="application/json", headers=self.logged_in_headers())
self.assert_success(rv)
response = json.loads(rv.get_data(as_text=True))
self.assertEqual(response[0]["id"], an.id)
self.assertEqual(response[0]["note"], 'This resource is a duplicate')
def test_many_notes(self):
u1 = self.construct_user(email='u1@sartography.com')
u2 = self.construct_user(email='u2@sartography.com')
r1 = self.construct_resource(title='R1')
r2 = self.construct_resource(title='R2')
r3 = self.construct_resource(title='R3')
r4 = self.construct_resource(title='R4')
self.construct_admin_note(id=324, user=u1, resource=r1, note="This resource is a duplicate")
self.construct_admin_note(id=249, user=u1, resource=r3, note="This is my favorite resource")
self.construct_admin_note(id=569, user=u2, resource=r1, note="I don't agree - I think this is a separate resource")
self.construct_admin_note(id=208, user=u2, resource=r2, note="Their hours have changed to 3-4PM Sundays")
self.construct_admin_note(id=796, user=u2, resource=r4, note="They have a waiting list of 20 as of today.")
rv = self.app.get('/api/admin_note', follow_redirects=True, content_type="application/json",
headers=self.logged_in_headers())
self.assert_success(rv)
response = json.loads(rv.get_data(as_text=True))
self.assertEqual(5, len(response))
rv = self.app.get('/api/resource/%i/admin_note' % r1.id, follow_redirects=True, content_type="application/json",
headers=self.logged_in_headers())
self.assert_success(rv)
response = json.loads(rv.get_data(as_text=True))
self.assertEqual(2, len(response))
rv = self.app.get('/api/user/%i/admin_note' % u2.id, follow_redirects=True, content_type="application/json",
headers=self.logged_in_headers())
self.assert_success(rv)
response = json.loads(rv.get_data(as_text=True))
self.assertEqual(3, len(response))

View File

@ -0,0 +1,187 @@
import unittest
from flask import json
from tests.base_test import BaseTest
from app import db
from app.model.category import Category
class TestCategory(BaseTest, unittest.TestCase):
def test_category_basics(self):
self.construct_category()
c = db.session.query(Category).first()
self.assertIsNotNone(c)
c_id = c.id
c.parent = self.construct_category(name="3d Printers")
rv = self.app.get('/api/category/%i' % c_id,
follow_redirects=True,
content_type="application/json")
self.assert_success(rv)
response = json.loads(rv.get_data(as_text=True))
self.assertEqual(response["id"], c_id)
self.assertEqual(response["name"], 'Ultimakers')
self.assertEqual(response["parent"]["name"], '3d Printers')
def test_modify_category_basics(self):
self.construct_category()
c = db.session.query(Category).first()
self.assertIsNotNone(c)
c_id = c.id
c.parent = self.construct_category(name="3d Printers")
rv = self.app.get('/api/category/%i' % c_id, content_type="application/json")
response = json.loads(rv.get_data(as_text=True))
response['name'] = 'JellyBoxes'
new_parent = self.construct_category(name="Strange Kitchen Gadgets")
response['parent_id'] = new_parent.id
rv = self.app.put('/api/category/%i' % c_id, data=self.jsonify(response),
content_type="application/json", follow_redirects=True,
headers=self.logged_in_headers())
self.assert_success(rv)
db.session.commit()
rv = self.app.get('/api/category/%i' % c_id, content_type="application/json")
self.assert_success(rv)
response = json.loads(rv.get_data(as_text=True))
self.assertEqual(response['name'], 'JellyBoxes')
self.assertEqual(response['parent']['name'], 'Strange Kitchen Gadgets')
def test_delete_category(self):
self.construct_category(name="Unicorns")
self.construct_category(name="Typewriters")
c = self.construct_category(name="Pianos")
c_id = c.id
rv = self.app.get('api/category/%i' % c_id, content_type="application/json")
self.assert_success(rv)
rv = self.app.get('api/category', content_type="application/json")
self.assert_success(rv)
response = json.loads(rv.get_data(as_text=True))
self.assertEqual(3, len(response))
rv = self.app.delete('api/category/%i' % c_id,
content_type="application/json",
headers=self.logged_in_headers())
self.assert_success(rv)
rv = self.app.get('api/category/%i' % c_id, content_type="application/json")
self.assertEqual(404, rv.status_code)
rv = self.app.get('api/category', content_type="application/json")
self.assert_success(rv)
response = json.loads(rv.get_data(as_text=True))
self.assertEqual(2, len(response))
def test_delete_category_will_not_delete_descendants(self):
wool = self.construct_category(name='wool')
yarn = self.construct_category(name='yarn', parent=wool)
self.construct_category(name='roving', parent=wool)
self.construct_category(name='worsted weight', parent=yarn)
self.construct_category(name='sport weight', parent=yarn)
rv = self.app.get('api/category/root', content_type="application/json")
self.assert_success(rv)
response = json.loads(rv.get_data(as_text=True))
self.assertEqual(1, len(response))
self.assertEqual(2, len(response[0]['children']))
rv = self.app.get('api/category', content_type="application/json")
self.assert_success(rv)
response = json.loads(rv.get_data(as_text=True))
self.assertEqual(5, len(response))
rv = self.app.delete('api/category/%i' % wool.id,
content_type="application/json",
headers=self.logged_in_headers())
self.assertEqual(400, rv.status_code)
response = json.loads(rv.get_data(as_text=True))
self.assertIsNotNone(response)
self.assertEqual("can_not_delete", response["code"])
self.assertEqual("You must delete all dependent records first.", response["message"])
def test_create_category(self):
category = {'name': "My Favorite Things"}
rv = self.app.post('api/category', data=self.jsonify(category),
content_type="application/json",
follow_redirects=True,
headers=self.logged_in_headers())
self.assert_success(rv)
response = json.loads(rv.get_data(as_text=True))
self.assertEqual(response['name'], 'My Favorite Things')
self.assertIsNotNone(response['id'])
def test_category_has_links(self):
c = self.construct_category()
rv = self.app.get(
'/api/category/' + str(c.id),
follow_redirects=True,
content_type="application/json")
self.assert_success(rv)
response = json.loads(rv.get_data(as_text=True))
self.assertEqual(response["_links"]["self"], '/api/category/' + str(c.id))
self.assertEqual(response["_links"]["collection"], '/api/category')
def test_category_has_children(self):
c1 = self.construct_category()
c2 = self.construct_category(name="I'm the kid", parent=c1)
rv = self.app.get(
'/api/category/' + str(c1.id),
follow_redirects=True,
content_type="application/json")
self.assert_success(rv)
response = json.loads(rv.get_data(as_text=True))
self.assertEqual(response["children"][0]['id'], c2.id)
self.assertEqual(response["children"][0]['name'], "I'm the kid")
def test_category_has_parents_and_that_parent_has_no_children(self):
c1 = self.construct_category()
c2 = self.construct_category(name="I'm the kid", parent=c1)
c3 = self.construct_category(name="I'm the grand kid", parent=c2)
rv = self.app.get(
'/api/category/' + str(c3.id),
follow_redirects=True,
content_type="application/json")
self.assert_success(rv)
response = json.loads(rv.get_data(as_text=True))
self.assertEqual(response["parent"]['id'], c2.id)
self.assertNotIn("children", response["parent"])
def test_category_can_create_searchable_path(self):
c1 = self.construct_category()
c2 = self.construct_category(name="I'm the kid", parent=c1)
c3 = self.construct_category(name="I'm the grand kid", parent=c2)
c1_path = str(c1.id)
c2_path = str(c1.id) + "," + str(c2.id)
c3_path = str(c1.id) + "," + str(c2.id) + "," + str(c3.id)
self.assertEqual(1, len(c1.all_search_paths()))
self.assertEqual(2, len(c2.all_search_paths()))
self.assertEqual(3, len(c3.all_search_paths()))
self.assertIn(c3_path, c3.all_search_paths())
self.assertIn(c2_path, c3.all_search_paths())
self.assertIn(c1_path, c3.all_search_paths())
self.assertIn(c2_path, c2.all_search_paths())
self.assertIn(c1_path, c2.all_search_paths())
self.assertIn(c1_path, c1.all_search_paths())
# def test_category_depth_is_limited(self):
# c1 = self.construct_category()
# c2 = self.construct_category(
# name="I'm the kid", parent=c1)
# c3 = self.construct_category(
# name="I'm the grand kid",
# parent=c2)
# c4 = self.construct_category(
# name="I'm the great grand kid",
# parent=c3)
#
# rv = self.app.get(
# '/api/category',
# follow_redirects=True,
# content_type="application/json")
#
# self.assert_success(rv)
# response = json.loads(rv.get_data(as_text=True))
#
# self.assertEqual(1, len(response))
# self.assertEqual(1, len(response[0]["children"]))

View File

@ -0,0 +1,83 @@
import unittest
from flask import json
from tests.base_test import BaseTest
from app import db
from app.model.chain_step import ChainStep
from app.model.questionnaires.chain_session_step import ChainSessionStep
class TestChainStep(BaseTest, unittest.TestCase):
def test_chain_step_basics(self):
self.construct_chain_step()
chain_step = db.session.query(ChainStep).first()
self.assertIsNotNone(chain_step)
rv = self.app.get('/api/chain_step/%i' % chain_step.id,
follow_redirects=True,
content_type="application/json", headers=self.logged_in_headers())
self.assert_success(rv)
response = json.loads(rv.get_data(as_text=True))
self.assertEqual(response["id"], chain_step.id)
def test_modify_chain_step_basics(self):
self.construct_chain_step()
chain_step = db.session.query(ChainStep).first()
self.assertIsNotNone(chain_step)
rv = self.app.get('/api/chain_step/%i' % chain_step.id, content_type="application/json", headers=self.logged_in_headers())
response = json.loads(rv.get_data(as_text=True))
response['instruction'] = 'Take out the trash'
rv = self.app.put('/api/chain_step/%i' % chain_step.id, data=self.jsonify(response), content_type="application/json",
follow_redirects=True, headers=self.logged_in_headers())
self.assert_success(rv)
db.session.commit()
rv = self.app.get('/api/chain_step/%i' % chain_step.id, content_type="application/json", headers=self.logged_in_headers())
self.assert_success(rv)
response = json.loads(rv.get_data(as_text=True))
self.assertEqual(response['instruction'], 'Take out the trash')
def test_delete_chain_step(self):
chain_step = self.construct_chain_step()
chain_step_id = chain_step.id
rv = self.app.get('api/chain_step/%i' % chain_step_id, content_type="application/json")
self.assert_success(rv)
rv = self.app.delete('api/chain_step/%i' % chain_step_id, content_type="application/json", headers=self.logged_in_headers())
self.assert_success(rv)
rv = self.app.get('api/chain_step/%i' % chain_step_id, content_type="application/json")
self.assertEqual(404, rv.status_code)
def test_disallow_deleting_chain_step_if_being_used(self):
chain_step = self.construct_chain_step()
chain_step_id = chain_step.id
db.session.add(ChainSessionStep(chain_step_id=chain_step_id))
db.session.commit()
rv = self.app.get('api/chain_step/%i' % chain_step_id, content_type="application/json")
self.assert_success(rv)
rv = self.app.delete('api/chain_step/%i' % chain_step_id, content_type="application/json", headers=self.logged_in_headers())
self.assertEqual(rv.status_code, 400)
self.assertEqual(rv.json['code'], 'can_not_delete')
def test_multiple_chain_steps(self):
chain_steps = self.construct_chain_steps()
self.assertEqual(4, len(chain_steps))
rv = self.app.get('/api/chain_step', follow_redirects=True, content_type="application/json",
headers=self.logged_in_headers())
self.assert_success(rv)
response = json.loads(rv.get_data(as_text=True))
self.assertEqual(len(chain_steps), len(response))
def test_export_chain_data(self):
chain_steps = self.construct_chain_steps()
for chain_step in chain_steps:
chain_step_id = chain_step.id
db.session.add(ChainSessionStep(chain_step_id=chain_step_id))
db.session.commit()

View File

@ -0,0 +1,18 @@
import unittest
from flask import json
from tests.base_test import BaseTest
class TestConfig(BaseTest, unittest.TestCase):
def test_config(self):
rv = self.app.get('/api/config',
follow_redirects=True,
content_type="application/json")
self.assert_success(rv)
response = json.loads(rv.get_data(as_text=True))
self.assertEqual(response["mirroring"], False)
self.assertEqual(response["testing"], True)
self.assertEqual(response["development"], False)

View File

@ -0,0 +1,126 @@
import math
import unittest
from datetime import datetime
from app import db, data_loader, elastic_index
from app.model.category import Category
from app.model.chain_step import ChainStep
from app.model.event import Event
from app.model.location import Location
from app.model.participant import Participant
from app.model.resource import Resource
from app.model.resource_category import ResourceCategory
from app.model.search import Search
from app.model.study import Study
from app.model.study_category import StudyCategory
from app.model.user import User
from app.model.zip_code import ZipCode
from tests.base_test import BaseTest, clean_db
class TestDataLoader(BaseTest, unittest.TestCase):
def setUp(self):
self.ctx.push()
clean_db(db)
elastic_index.clear()
self.auths = {}
def _load_and_assert_success(self, class_to_load, load_method='', category_class=None, category_type=''):
num_rc_after = -math.inf
num_rc_before = math.inf
if category_class is not None:
num_rc_before = self._count(category_class, category_type)
num_before = self._count(class_to_load)
data_loader.DataLoader().__getattribute__(load_method)()
num_after = self._count(class_to_load)
self.assertGreater(num_after, num_before)
if category_class is not None:
num_rc_after = self._count(category_class, category_type)
self.assertGreater(num_rc_after, num_rc_before)
def _count(self, class_to_query, type_to_filter=''):
if type_to_filter != '':
return db.session.query(class_to_query).filter(class_to_query.type == type_to_filter).count()
else:
return db.session.query(class_to_query).count()
def test_load_categories(self):
self._load_and_assert_success(Category, 'load_categories')
def test_load_events(self):
self.test_load_categories()
self.test_load_users()
self._load_and_assert_success(Event, 'load_events', ResourceCategory, 'event')
def test_load_locations(self):
self.test_load_categories()
self._load_and_assert_success(Location, 'load_locations', ResourceCategory, 'location')
def test_load_resources(self):
self.test_load_categories()
self._load_and_assert_success(Resource, 'load_resources', ResourceCategory, 'resource')
def test_load_studies(self):
self.test_load_categories()
self._load_and_assert_success(Study, 'load_studies', StudyCategory)
def test_load_users(self):
self._load_and_assert_success(User, 'load_users')
# Participants depends on Users
def test_load_participants(self):
self._load_and_assert_success(User, 'load_users')
self._load_and_assert_success(Participant, 'load_participants')
def test_load_zip_codes(self):
self._load_and_assert_success(ZipCode, 'load_zip_codes')
def test_load_chain_steps(self):
self._load_and_assert_success(ChainStep, 'load_chain_steps')
def test_get_category_by_name(self):
expected_name = 'Schools of Witchcraft and Wizardry'
loader = data_loader.DataLoader()
cat = loader.get_category_by_name(expected_name, create_missing=True)
self.assertIsNotNone(cat)
self.assertEqual(cat.name, expected_name)
def test_build_index(self):
elastic_index.clear()
# Populate the database
self._load_and_assert_success(User, 'load_users')
self._load_and_assert_success(Category, 'load_categories')
self._load_and_assert_success(Resource, 'load_resources', ResourceCategory, 'resource')
self._load_and_assert_success(Event, 'load_events', ResourceCategory, 'event')
self._load_and_assert_success(Location, 'load_locations', ResourceCategory, 'location')
self._load_and_assert_success(Study, 'load_studies', StudyCategory)
# Build the index
data_loader.DataLoader().build_index()
# Get the number of items in the database
num_db_resources = db.session.query(Resource).filter(Resource.type == 'resource').count()
num_db_events = db.session.query(Event).filter(Event.date >= datetime.now()).count()
num_db_locations = db.session.query(Resource).filter(Resource.type == 'location').count()
num_db_studies = db.session.query(Study).count()
# Get the number of items in the search index
es_resources = elastic_index.search(Search(types=[Resource.__tablename__]))
es_events = elastic_index.search(Search(types=[Event.__tablename__]))
es_locations = elastic_index.search(Search(types=[Location.__tablename__]))
es_studies = elastic_index.search(Search(types=[Study.__tablename__]))
# Verify that the number of items in the database match the number of items in the search index
self.assertEqual(num_db_resources, es_resources.hits.total)
self.assertEqual(num_db_events, es_events.hits.total)
self.assertEqual(num_db_locations, es_locations.hits.total)
self.assertEqual(num_db_studies, es_studies.hits.total)
# Assure there are not age related categories.
self.assertEqual(0, db.session.query(Category).filter(Category.name == 'Age Range').count())
self.assertEqual(0, db.session.query(Category).filter(Category.name == 'Pre-K (0 - 5 years)').count())

View File

@ -0,0 +1,30 @@
import unittest
from flask import json
from app import db
from app.model.data_transfer_log import DataTransferLog, DataTransferLogDetail, DataTransferLogSchema
from tests.base_test import BaseTest
class TestDataTransferLogs(BaseTest, unittest.TestCase):
def test_export_logs_endpoint(self):
for i in range(20):
elog = DataTransferLog()
for x in range(2):
dlog = DataTransferLogDetail()
elog.details.append(dlog)
db.session.add(elog)
rv = self.app.get('/api/data_transfer_log?pageSize=10',
follow_redirects=True,
content_type="application/json",
headers=self.logged_in_headers())
self.assert_success(rv)
response = json.loads(rv.get_data(as_text=True))
self.assertEqual(20, response['total'])
self.assertEqual(2, response['pages'])
self.assertEqual(10, len(response['items']))
results = DataTransferLogSchema(many=True, session=db.session).load(response['items'])
self.assertEqual(10, len(results))

View File

@ -0,0 +1,300 @@
import datetime
import unittest
import os
from flask import json
os.environ["TESTING"] = "true"
from tests.base_test_questionnaire import BaseTestQuestionnaire
from app import app, db
from app.model.email_log import EmailLog
from app.model.participant import Relationship
from app.model.study import Study
from app.model.user import User
from app.email_service import TEST_MESSAGES
from app.email_prompt_service import EmailPromptService
class TestExportCase(BaseTestQuestionnaire, unittest.TestCase):
def create_email_log_records(self, num_records, days_removed, log_type, user=None):
if user is None:
user = self.construct_user()
for _ in range(num_records):
log = EmailLog(last_updated=datetime.datetime.now() - datetime.timedelta(days=days_removed),
user_id=user.id, type=log_type)
db.session.add(log)
db.session.commit()
days_removed += 2
def create_complete_guardian(self):
u1 = self.construct_user(email='test1@sartography.com', last_login='12/4/19 10:00')
p1 = self.construct_participant(user=u1, relationship=Relationship.self_guardian)
q1 = {
'user_id': u1.id,
'participant_id': p1.id
}
self.app.post('api/flow/guardian_intake/identification_questionnaire', data=self.jsonify(q1),
content_type="application/json",
follow_redirects=True, headers=self.logged_in_headers(u1))
self.app.post('api/flow/guardian_intake/contact_questionnaire', data=self.jsonify(q1),
content_type="application/json",
follow_redirects=True, headers=self.logged_in_headers(u1))
self.app.post('api/flow/guardian_intake/demographics_questionnaire', data=self.jsonify(q1),
content_type="application/json",
follow_redirects=True, headers=self.logged_in_headers(u1))
self.assertTrue(u1.self_registration_complete())
return u1
def test_prompting_emails_sent_after_7_days(self):
message_count = len(TEST_MESSAGES)
self.create_email_log_records(1, 6, 'confirm_email')
# Prompting email should not be sent before 7 days.
EmailPromptService(app, db, EmailLog, Study, User).send_confirm_prompting_emails()
self.assertEqual(len(TEST_MESSAGES), message_count)
db.session.query(EmailLog).delete()
db.session.commit()
self.create_email_log_records(1, 8, 'confirm_email')
EmailPromptService(app, db, EmailLog, Study, User).send_confirm_prompting_emails()
self.assertEqual(len(TEST_MESSAGES), message_count + 1)
self.assertEqual("Autism DRIVE: Confirm Email", self.decode(TEST_MESSAGES[-1]['subject']))
def test_prompting_emails_sent_after_14_days(self):
message_count = len(TEST_MESSAGES)
self.create_email_log_records(2, 6, 'confirm_email')
# Prompting email should not be sent between 7 and 14 days.
EmailPromptService(app, db, EmailLog, Study, User).send_confirm_prompting_emails()
self.assertEqual(len(TEST_MESSAGES), message_count)
db.session.query(EmailLog).delete()
db.session.commit()
self.create_email_log_records(2, 8, 'confirm_email')
EmailPromptService(app, db, EmailLog, Study, User).send_confirm_prompting_emails()
self.assertEqual(len(TEST_MESSAGES), message_count + 1)
self.assertEqual("Autism DRIVE: Confirm Email", self.decode(TEST_MESSAGES[-1]['subject']))
def test_prompting_emails_sent_after_30_days(self):
message_count = len(TEST_MESSAGES)
self.create_email_log_records(3, 12, 'confirm_email')
# Prompting email should not be sent between 14 and 30 days.
EmailPromptService(app, db, EmailLog, Study, User).send_confirm_prompting_emails()
self.assertEqual(len(TEST_MESSAGES), message_count)
db.session.query(EmailLog).delete()
db.session.commit()
self.create_email_log_records(3, 17, 'confirm_email')
EmailPromptService(app, db, EmailLog, Study, User).send_confirm_prompting_emails()
self.assertEqual(len(TEST_MESSAGES), message_count + 1)
self.assertEqual("Autism DRIVE: Confirm Email", self.decode(TEST_MESSAGES[-1]['subject']))
def test_prompting_emails_sent_after_60_days(self):
message_count = len(TEST_MESSAGES)
self.create_email_log_records(4, 28, 'confirm_email')
# Prompting email should not be sent between 30 and 60 days.
EmailPromptService(app, db, EmailLog, Study, User).send_confirm_prompting_emails()
self.assertEqual(len(TEST_MESSAGES), message_count)
db.session.query(EmailLog).delete()
db.session.commit()
self.create_email_log_records(4, 31, 'confirm_email')
EmailPromptService(app, db, EmailLog, Study, User).send_confirm_prompting_emails()
self.assertEqual(len(TEST_MESSAGES), message_count + 1)
self.assertEqual("Autism DRIVE: Confirm Email", self.decode(TEST_MESSAGES[-1]['subject']))
def test_prompting_emails_sent_after_90_days(self):
message_count = len(TEST_MESSAGES)
self.create_email_log_records(5, 28, 'confirm_email')
# Prompting email should not be sent between 60 and 90 days.
EmailPromptService(app, db, EmailLog, Study, User).send_confirm_prompting_emails()
self.assertEqual(len(TEST_MESSAGES), message_count)
db.session.query(EmailLog).delete()
db.session.commit()
self.create_email_log_records(5, 31, 'confirm_email')
EmailPromptService(app, db, EmailLog, Study, User).send_confirm_prompting_emails()
self.assertEqual(len(TEST_MESSAGES), message_count + 1)
self.assertEqual("Autism DRIVE: Confirm Email", self.decode(TEST_MESSAGES[-1]['subject']))
def test_prompting_emails_do_not_send_more_than_5_times_total(self):
message_count = len(TEST_MESSAGES)
self.create_email_log_records(6, 31, 'confirm_email')
EmailPromptService(app, db, EmailLog, Study, User).send_confirm_prompting_emails()
self.assertEqual(len(TEST_MESSAGES), message_count)
def test_self_registration_prompting_email(self):
u1 = self.construct_user(email='test1@sartography.com')
p1 = self.construct_participant(user=u1, relationship=Relationship.self_guardian)
q1 = {
'user_id': u1.id,
'participant_id': p1.id
}
self.app.post('api/flow/guardian_intake/identification_questionnaire', data=self.jsonify(q1),
content_type="application/json",
follow_redirects=True, headers=self.logged_in_headers(u1))
self.app.post('api/flow/guardian_intake/contact_questionnaire', data=self.jsonify(q1),
content_type="application/json",
follow_redirects=True, headers=self.logged_in_headers(u1))
self.app.post('api/flow/guardian_intake/demographics_questionnaire', data=self.jsonify(q1),
content_type="application/json",
follow_redirects=True, headers=self.logged_in_headers(u1))
self.assertTrue(u1.self_registration_complete())
u2 = self.construct_user(email='test2@sartography.com', last_login="12/4/19 10:00")
p2 = self.construct_participant(user=u2, relationship=Relationship.self_guardian)
q2 = {
'user_id': u2.id,
'participant_id': p2.id
}
self.app.post('api/flow/guardian_intake/identification_questionnaire', data=self.jsonify(q2),
content_type="application/json",
follow_redirects=True, headers=self.logged_in_headers(u2))
self.app.post('api/flow/guardian_intake/contact_questionnaire', data=self.jsonify(q2),
content_type="application/json",
follow_redirects=True, headers=self.logged_in_headers(u2))
self.assertFalse(u2.self_registration_complete())
message_count = len(TEST_MESSAGES)
EmailPromptService(app, db, EmailLog, Study, User).send_complete_registration_prompting_emails()
self.assertEqual(len(TEST_MESSAGES), message_count + 1)
self.assertEqual("Autism DRIVE: Complete Your Registration", self.decode(TEST_MESSAGES[-1]['subject']))
self.assertEqual("test2@sartography.com", TEST_MESSAGES[-1]['To'])
def test_dependent_profile_sends_prompt_with_no_dependent(self):
u1 = self.create_complete_guardian()
message_count = len(TEST_MESSAGES)
EmailPromptService(app, db, EmailLog, Study, User).send_dependent_profile_prompting_emails()
self.assertEqual(len(TEST_MESSAGES), message_count + 1)
self.assertEqual("Autism DRIVE: Complete Your Dependent's Profile", self.decode(TEST_MESSAGES[-1]['subject']))
self.assertEqual(u1.email, TEST_MESSAGES[-1]['To'])
def test_dependent_profile_sends_scheduled_prompt_with_no_dependent(self):
u1 = self.create_complete_guardian()
message_count = len(TEST_MESSAGES)
self.create_email_log_records(5, 28, 'dependent_profile_prompt', user=u1)
# Prompting email should not be sent between 60 and 90 days.
EmailPromptService(app, db, EmailLog, Study, User).send_dependent_profile_prompting_emails()
self.assertEqual(len(TEST_MESSAGES), message_count)
db.session.query(EmailLog).delete()
db.session.commit()
self.create_email_log_records(5, 31, 'dependent_profile_prompt', user=u1)
EmailPromptService(app, db, EmailLog, Study, User).send_dependent_profile_prompting_emails()
self.assertEqual(len(TEST_MESSAGES), message_count + 1)
self.assertEqual("Autism DRIVE: Complete Your Dependent's Profile", self.decode(TEST_MESSAGES[-1]['subject']))
self.assertEqual(u1.email, TEST_MESSAGES[-1]['To'])
def test_dependent_profile_sends_prompt_with_incomplete_dependent(self):
u1 = self.create_complete_guardian()
d1 = self.construct_participant(user=u1, relationship=Relationship.dependent)
q1 = {
'user_id': u1.id,
'participant_id': d1.id
}
rv = self.app.post('api/flow/dependent_intake/developmental_questionnaire', data=self.jsonify(q1),
content_type="application/json",
follow_redirects=True, headers=self.logged_in_headers(u1))
self.assert_success(rv)
message_count = len(TEST_MESSAGES)
EmailPromptService(app, db, EmailLog, Study, User).send_dependent_profile_prompting_emails()
self.assertEqual(len(TEST_MESSAGES), message_count + 1)
self.assertEqual("Autism DRIVE: Complete Your Dependent's Profile", self.decode(TEST_MESSAGES[-1]['subject']))
self.assertEqual(u1.email, TEST_MESSAGES[-1]['To'])
def test_dependent_profile_does_not_send_prompt_with_complete_dependent(self):
u1 = self.create_complete_guardian()
d1 = self.construct_participant(user=u1, relationship=Relationship.dependent)
q1 = {
'user_id': u1.id,
'participant_id': d1.id
}
rv = self.app.post('api/flow/dependent_intake/identification_questionnaire', data=self.jsonify(q1),
content_type="application/json",
follow_redirects=True, headers=self.logged_in_headers(u1))
self.assert_success(rv)
rv = self.app.post('api/flow/dependent_intake/demographics_questionnaire', data=self.jsonify(q1),
content_type="application/json",
follow_redirects=True, headers=self.logged_in_headers(u1))
self.assert_success(rv)
rv = self.app.post('api/flow/dependent_intake/home_dependent_questionnaire', data=self.jsonify(q1),
content_type="application/json",
follow_redirects=True, headers=self.logged_in_headers(u1))
self.assert_success(rv)
rv = self.app.post('api/flow/dependent_intake/evaluation_history_dependent_questionnaire', data=self.jsonify(q1),
content_type="application/json",
follow_redirects=True, headers=self.logged_in_headers(u1))
self.assert_success(rv)
rv = self.app.post('api/flow/dependent_intake/clinical_diagnoses_questionnaire', data=self.jsonify(q1),
content_type="application/json",
follow_redirects=True, headers=self.logged_in_headers(u1))
self.assert_success(rv)
rv = self.app.post('api/flow/dependent_intake/developmental_questionnaire', data=self.jsonify(q1),
content_type="application/json",
follow_redirects=True, headers=self.logged_in_headers(u1))
self.assert_success(rv)
rv = self.app.post('api/flow/dependent_intake/current_behaviors_dependent_questionnaire', data=self.jsonify(q1),
content_type="application/json",
follow_redirects=True, headers=self.logged_in_headers(u1))
self.assert_success(rv)
rv = self.app.post('api/flow/dependent_intake/education_dependent_questionnaire', data=self.jsonify(q1),
content_type="application/json",
follow_redirects=True, headers=self.logged_in_headers(u1))
self.assert_success(rv)
rv = self.app.post('api/flow/dependent_intake/supports_questionnaire', data=self.jsonify(q1),
content_type="application/json",
follow_redirects=True, headers=self.logged_in_headers(u1))
self.assert_success(rv)
message_count = len(TEST_MESSAGES)
EmailPromptService(app, db, EmailLog, Study, User).send_dependent_profile_prompting_emails()
self.assertEqual(len(TEST_MESSAGES), message_count)
def test_self_participants_that_are_not_their_own_legal_guardians_do_not_get_reminders(self):
u2 = self.construct_user(email='test2@sartography.com', last_login="12/4/19 10:00")
u2._password = b'123412'
user_meta = self.construct_usermeta(user=u2)
user_meta.self_participant = True
user_meta.self_has_guardian = True
# Assure no new messages to go out to this individual who is not their own legal guardian.
message_count = len(TEST_MESSAGES)
EmailPromptService(app, db, EmailLog, Study, User).send_complete_registration_prompting_emails()
self.assertEqual(len(TEST_MESSAGES), message_count)

View File

@ -0,0 +1,231 @@
import unittest
from flask import json
from app import db, data_loader
from app.model.event import Event
from tests.base_test import BaseTest
from app.model.resource_category import ResourceCategory
from app.model.resource_change_log import ResourceChangeLog
from app.model.user import Role
class TestEvents(BaseTest, unittest.TestCase):
def test_event_basics(self):
self.construct_event()
r = db.session.query(Event).first()
self.assertIsNotNone(r)
r_id = r.id
rv = self.app.get('/api/event/%i' % r_id,
follow_redirects=True,
content_type="application/json")
self.assert_success(rv)
response = json.loads(rv.get_data(as_text=True))
self.assertEqual(response["id"], r_id)
self.assertEqual(response["title"], 'A+ Event')
self.assertEqual(response["description"], 'A delightful event destined to create rejoicing')
def test_modify_event_basics(self):
data_loader.DataLoader().load_partial_zip_codes()
self.construct_event()
r = db.session.query(Event).first()
self.assertIsNotNone(r)
r_id = r.id
rv = self.app.get('/api/event/%i' % r_id, content_type="application/json")
response = json.loads(rv.get_data(as_text=True))
response['title'] = 'Edwarardos Lemonade and Oil Change'
response['description'] = 'Better fluids for you and your car.'
response['website'] = 'http://sartography.com'
orig_date = response['last_updated']
rv = self.app.put('/api/event/%i' % r_id, data=self.jsonify(response), content_type="application/json",
follow_redirects=True, headers=self.logged_in_headers())
self.assert_success(rv)
rv = self.app.get('/api/event/%i' % r_id, content_type="application/json")
self.assert_success(rv)
response = json.loads(rv.get_data(as_text=True))
self.assertEqual(response['title'], 'Edwarardos Lemonade and Oil Change')
self.assertEqual(response['description'], 'Better fluids for you and your car.')
self.assertEqual(response['website'], 'http://sartography.com')
self.assertNotEqual(orig_date, response['last_updated'])
def test_delete_event(self):
r = self.construct_event()
r_id = r.id
rv = self.app.get('api/event/%i' % r_id, content_type="application/json")
self.assert_success(rv)
rv = self.app.delete('api/event/%i' % r_id, content_type="application/json", headers=self.logged_in_headers())
self.assert_success(rv)
rv = self.app.get('api/event/%i' % r_id, content_type="application/json")
self.assertEqual(404, rv.status_code)
def test_create_event(self):
data_loader.DataLoader().load_partial_zip_codes()
event = {'title': "event of events", 'description': "You need this event in your life.", 'time': "4PM sharp",
'ticket_cost': "$500 suggested donation", 'organization_name': "Event Org"}
rv = self.app.post('api/event', data=self.jsonify(event), content_type="application/json",
follow_redirects=True, headers=self.logged_in_headers())
self.assert_success(rv)
response = json.loads(rv.get_data(as_text=True))
self.assertEqual(response['title'], 'event of events')
self.assertEqual(response['description'], 'You need this event in your life.')
self.assertEqual(response['time'], '4PM sharp')
self.assertEqual(response['ticket_cost'], '$500 suggested donation')
self.assertIsNotNone(response['id'])
def test_get_event_by_category(self):
c = self.construct_category()
event = self.construct_event()
rc = ResourceCategory(resource_id=event.id, category=c, type='event')
db.session.add(rc)
rv = self.app.get(
'/api/category/%i/resource' % c.id,
content_type="application/json",
headers=self.logged_in_headers())
self.assert_success(rv)
response = json.loads(rv.get_data(as_text=True))
self.assertEqual(1, len(response))
self.assertEqual(event.id, response[0]["resource_id"])
self.assertEqual(event.description, response[0]["resource"]["description"])
def test_get_event_by_category_includes_category_details(self):
c = self.construct_category(name="c1")
c2 = self.construct_category(name="c2")
event = self.construct_event()
rc = ResourceCategory(resource_id=event.id, category=c, type='event')
rc2 = ResourceCategory(resource_id=event.id, category=c2, type='event')
db.session.add_all([rc, rc2])
rv = self.app.get(
'/api/category/%i/resource' % c.id,
content_type="application/json",
headers=self.logged_in_headers())
self.assert_success(rv)
response = json.loads(rv.get_data(as_text=True))
self.assertEqual(event.id, response[0]["resource_id"])
self.assertEqual(2,
len(response[0]["resource"]["resource_categories"]))
self.assertEqual(
"c1", response[0]["resource"]["resource_categories"][0]["category"]
["name"])
def test_category_event_count(self):
c = self.construct_category()
event = self.construct_event()
revcat = self.construct_resource()
rc = ResourceCategory(resource_id=event.id, category=c, type='event')
rc2 = ResourceCategory(resource_id=revcat.id, category=c, type='resource')
db.session.add_all([rc, rc2])
rv = self.app.get(
'/api/category/%i' % c.id, content_type="application/json")
self.assert_success(rv)
response = json.loads(rv.get_data(as_text=True))
self.assertEqual(1, response["event_count"])
def test_get_category_by_event(self):
c = self.construct_category()
event = self.construct_event()
rc = ResourceCategory(resource_id=event.id, category=c, type='event')
db.session.add(rc)
rv = self.app.get(
'/api/resource/%i/category' % event.id,
content_type="application/json")
self.assert_success(rv)
response = json.loads(rv.get_data(as_text=True))
self.assertEqual(1, len(response))
self.assertEqual(c.id, response[0]["category_id"])
self.assertEqual(c.name, response[0]["category"]["name"])
def test_add_category_to_event(self):
c = self.construct_category()
event = self.construct_event()
evcat_data = {"resource_id": event.id, "category_id": c.id}
rv = self.app.post(
'/api/resource_category',
data=self.jsonify(evcat_data),
content_type="application/json")
self.assert_success(rv)
response = json.loads(rv.get_data(as_text=True))
self.assertEqual(c.id, response["category_id"])
self.assertEqual(event.id, response["resource_id"])
def test_set_all_categories_on_event(self):
c1 = self.construct_category(name="c1")
c2 = self.construct_category(name="c2")
c3 = self.construct_category(name="c3")
event = self.construct_event()
evcat_data = [
{
"category_id": c1.id
},
{
"category_id": c2.id
},
{
"category_id": c3.id
},
]
rv = self.app.post(
'/api/event/%i/category' % event.id,
data=self.jsonify(evcat_data),
content_type="application/json")
self.assert_success(rv)
response = json.loads(rv.get_data(as_text=True))
self.assertEqual(3, len(response))
evcat_data = [{"category_id": c1.id}]
rv = self.app.post(
'/api/event/%i/category' % event.id,
data=self.jsonify(evcat_data),
content_type="application/json")
self.assert_success(rv)
response = json.loads(rv.get_data(as_text=True))
self.assertEqual(1, len(response))
def test_remove_category_from_event(self):
self.test_add_category_to_event()
rv = self.app.delete('/api/resource_category/%i' % 1)
self.assert_success(rv)
rv = self.app.get(
'/api/resource/%i/category' % 1, content_type="application/json")
self.assert_success(rv)
response = json.loads(rv.get_data(as_text=True))
self.assertEqual(0, len(response))
def test_resource_change_log(self):
data_loader.DataLoader().load_partial_zip_codes()
event = self.construct_event(title='A Event that is Super and Great')
u = self.construct_user(email="editor@sartorgraphy.com", role=Role.admin)
rv = self.app.get('api/event/%i' % event.id, content_type="application/json")
self.assert_success(rv)
response = json.loads(rv.get_data(as_text=True))
response['title'] = 'Super Great Event'
rv = self.app.put('/api/event/%i' % event.id, data=self.jsonify(response), content_type="application/json",
follow_redirects=True, headers=self.logged_in_headers(user=u))
self.assert_success(rv)
rv = self.app.get('/api/event/%i' % event.id, content_type="application/json")
self.assert_success(rv)
response = json.loads(rv.get_data(as_text=True))
self.assertEqual(response['title'], 'Super Great Event')
logs = ResourceChangeLog.query.all()
self.assertIsNotNone(logs[-1].resource_id)
self.assertIsNotNone(logs[-1].user_id)
rv = self.app.get('/api/resource/%i/change_log' % event.id, content_type="application/json",
headers=self.logged_in_headers())
self.assert_success(rv)
response = json.loads(rv.get_data(as_text=True))
self.assertEqual(response[-1]['user_id'], u.id)
rv = self.app.get('/api/user/%i/resource_change_log' % u.id, content_type="application/json",
headers=self.logged_in_headers())
self.assert_success(rv)
response = json.loads(rv.get_data(as_text=True))
self.assertEqual(response[-1]['resource_id'], event.id)

View File

@ -0,0 +1,382 @@
import datetime
import unittest
import os
from app.import_service import ImportService
from app.model.data_transfer_log import DataTransferLog
from app.model.export_info import ExportInfoSchema
os.environ["TESTING"] = "true"
from flask import json
from tests.base_test_questionnaire import BaseTestQuestionnaire
from app import db, app
from app.email_service import TEST_MESSAGES
from app.export_service import ExportService
from app.model.participant import Relationship, Participant
from app.model.questionnaires.identification_questionnaire import IdentificationQuestionnaire
from app.model.user import Role, User
from app.schema.schema import UserSchema, ParticipantSchema
from tests.base_test import clean_db
class TestExportCase(BaseTestQuestionnaire, unittest.TestCase):
def test_get_list_of_exportables_requires_admin(self):
rv = self.app.get('/api/export')
self.assertEqual(401, rv.status_code)
headers = self.logged_in_headers(self.construct_user(email="joe@smoe.com", role=Role.user))
rv = self.app.get('/api/export', headers=headers)
self.assertEqual(403, rv.status_code)
def test_get_list_of_exportables_contains_common_tables(self):
rv = self.app.get('/api/export',
follow_redirects=True,
content_type="application/json",
headers=self.logged_in_headers())
self.assert_success(rv)
response = json.loads(rv.get_data(as_text=True))
self.assertTrue(len(response) > 1)
self.assertEqual(1, len(list(filter(lambda field: field['class_name'] == 'Category', response))))
self.assertEqual(1, len(list(filter(lambda field: field['class_name'] == 'Participant', response))))
self.assertEqual(1, len(list(filter(lambda field: field['class_name'] == 'User', response))))
self.assertEqual(1, len(
list(filter(lambda field: field['class_name'] == 'EvaluationHistorySelfQuestionnaire', response))))
self.assertEqual(1, len(list(filter(lambda field: field['class_name'] == 'Category', response))))
def test_get_list_of_exportables_has_basic_attributes(self):
rv = self.app.get('/api/export', headers=self.logged_in_headers())
self.assert_success(rv)
response = json.loads(rv.get_data(as_text=True))
user_data = list(filter(lambda field: field['class_name'] == 'User', response))
self.assertTrue(len(user_data) > 0)
self.assertEqual("/api/export/user", user_data[0]['url'])
self.assertEqual("User", user_data[0]['class_name'])
self.assertEqual("stardrive_user", user_data[0]['table_name'])
def test_get_list_of_exportables_has_url_for_all_endpoints(self):
rv = self.app.get('/api/export', headers=self.logged_in_headers())
self.assert_success(rv)
response = json.loads(rv.get_data(as_text=True))
for entry in response:
self.assertTrue('url' in entry, msg="No url provided for " + entry["class_name"])
self.assertNotEqual("", entry['url'], msg="No url provided for " + entry["class_name"])
def test_all_urls_respond_with_success(self):
rv = self.app.get('/api/export',
follow_redirects=True,
content_type="application/json",
headers=self.logged_in_headers())
self.assert_success(rv)
exports = json.loads(rv.get_data(as_text=True))
for export in exports:
rv = self.app.get(export['url'],
follow_redirects=True,
content_type="application/json",
headers=self.logged_in_headers())
self.assert_success(rv, msg="Failed to retrieve a list for " + export['class_name'])
print("Successful export of " + export['class_name'])
def test_user_has_no_identifying_information(self):
rv = self.app.get('/api/export/User', headers=self.logged_in_headers())
self.assert_success(rv)
response = json.loads(rv.get_data(as_text=True))
self.assertFalse("email" in response)
print(response)
def test_user_with_participant_properly_exported(self):
u = self.construct_user()
p = self.construct_participant(user=u, relationship=Relationship.self_participant)
db.session.commit()
rv = self.app.get('/api/export/user', headers=self.logged_in_headers())
self.assert_success(rv)
rv = self.app.get('/api/export/participant', headers=self.logged_in_headers())
self.assert_success(rv)
response = json.loads(rv.get_data(as_text=True))
self.assertEqual(u.id, response[0]["user_id"])
def get_export(self):
"""Grabs everything exportable via the API, and returns it fully serialized ss json"""
all_data = {}
rv = self.app.get('/api/export', headers=self.logged_in_headers())
response = json.loads(rv.get_data(as_text=True))
exports = ExportInfoSchema(many=True).load(response)
for export in exports:
rv = self.app.get(export.url, follow_redirects=True, content_type="application/json",
headers=self.logged_in_headers())
all_data[export.class_name] = json.loads(rv.get_data(as_text=True))
return all_data
def load_database(self, all_data):
rv = self.app.get('/api/export', headers=self.logged_in_headers())
response = json.loads(rv.get_data(as_text=True))
exports = ExportInfoSchema(many=True).load(response)
importer = ImportService(app, db)
log = importer.log_for_export(exports, datetime.datetime.utcnow())
for export in exports:
export.json_data = all_data[export.class_name]
importer.load_data(export, log)
def test_insert_user_with_participant(self):
u = self.construct_user()
u._password = b"xxxxx"
u.email_verified = True
role = u.role
email_verified = u.email_verified
orig_u_date = u.last_updated
orig_user_dict = UserSchema().dump(u) # Use standard schema
p = self.construct_participant(user=u, relationship=Relationship.self_participant)
orig_p_dict = ParticipantSchema().dump(p) # Use standard schema
orig_p_date = p.last_updated
db.session.commit()
data = self.get_export()
clean_db(db)
db.session.commit()
self.load_database(data)
db_user = db.session.query(User).filter_by(id=orig_user_dict["id"]).first()
self.assertIsNotNone(db_user, msg="User is recreated.")
self.assertNotEqual(orig_user_dict["email"], db_user.email, msg="Email should be obfuscated")
self.assertEqual(db_user.last_updated, orig_u_date, msg="Dates are kept in tact")
self.assertEqual(db_user.role, role)
self.assertEqual(db_user.email_verified, email_verified)
self.assertEqual(None, db_user._password, msg="Passwords should not be exported.")
db_par = db.session.query(Participant).filter_by(id=orig_p_dict["id"]).first()
self.assertIsNotNone(db_par, msg="Participant is recreated.")
self.assertEqual(db_par.user, db_user, msg="Relationship in tact")
self.assertEqual(db_par.last_updated, orig_p_date, msg="Dates are kept in tact")
def test_re_insert_user_with_modifications(self):
# Construct the base user.
u = self.construct_user()
id = u.id
db.session.commit()
data = self.get_export()
clean_db(db)
db.session.commit()
self.load_database(data)
db_user = db.session.query(User).filter_by(id=id).first()
self.assertFalse(db_user.email_verified, msg="Email should start off unverified")
# Modify the exported data slightly, and reload
for user in data['User']:
user['email_verified'] = True
self.load_database(data)
db_user = db.session.query(User).filter_by(id=id).first()
self.assertTrue(db_user.email_verified, msg="Email should now be verified.")
def test_identifying_questionnaire_does_not_export(self):
# Construct the base user.
u = self.construct_user()
p = self.construct_participant(user=u, relationship=Relationship.self_participant)
iq = self.construct_identification_questionnaire(user=u, participant=p)
id = u.id
db.session.commit()
data = self.get_export()
clean_db(db)
db.session.commit()
self.load_database(data)
self.assertEqual(ExportService.TYPE_IDENTIFYING,
IdentificationQuestionnaire().__question_type__)
self.assertEqual(0, len(db.session.query(IdentificationQuestionnaire).all()),
msg="Identifying Questionnaires should not import.")
def test_all_sensitive_exports_have_links_to_self(self):
self.construct_everything()
exports = ExportService.get_table_info()
for export in exports:
if export.question_type != ExportService.TYPE_SENSITIVE:
continue
rv = self.app.get(export.url, follow_redirects=True, content_type="application/json",
headers=self.logged_in_headers())
data = json.loads(rv.get_data(as_text=True))
for d in data:
self.assertTrue('_links' in d, msg="%s should have links in json." % export.class_name)
self.assertTrue('self' in d['_links'])
self.assert_success(self.app.get(d['_links']['self'], headers=self.logged_in_headers()))
rv_link = self.app.get(d['_links']['self'], follow_redirects=True, content_type="application/json",
headers=self.logged_in_headers())
rv_link_data = json.loads(rv_link.get_data(as_text=True))
def test_sensitive_records_returned_can_be_deleted(self):
self.construct_all_questionnaires()
exports = ExportService.get_table_info()
for export in exports:
rv = self.app.get(export.url, follow_redirects=True, content_type="application/json",
headers=self.logged_in_headers())
data = json.loads(rv.get_data(as_text=True))
for d in data:
if export.question_type == ExportService.TYPE_SENSITIVE:
del_rv = self.app.delete(d['_links']['self'], headers=self.logged_in_headers())
self.assert_success(del_rv)
def test_retrieve_records_later_than(self):
self.construct_everything()
date = datetime.datetime.utcnow() + datetime.timedelta(seconds=1) # One second in the future
exports = ExportService.get_table_info()
params = "?after=" + date.strftime(ExportService.DATE_FORMAT)
for export in exports:
rv = self.app.get(export.url + params, follow_redirects=True, content_type="application/json",
headers=self.logged_in_headers())
data = json.loads(rv.get_data(as_text=True))
self.assertEqual(0, len(data), msg=export.url + " does not respect 'after' param in get request.")
def test_export_list_count_is_date_based(self):
self.construct_everything()
date = datetime.datetime.utcnow() + datetime.timedelta(seconds=1)
params = "?after=" + date.strftime(ExportService.DATE_FORMAT)
rv = self.app.get('/api/export', headers=self.logged_in_headers())
response = json.loads(rv.get_data(as_text=True))
for export in response:
self.assertGreater(export['size'], 0, msg=export['class_name'] + " should have a count > 0")
rv = self.app.get('/api/export' + params, headers=self.logged_in_headers())
response = json.loads(rv.get_data(as_text=True))
for export in response:
self.assertEqual(export['size'], 0, msg=export['class_name'] + " should have a count of 0")
def test_it_all_crazy_madness_wohoo(self):
# Sanity check, can we load everything, export it, delete, and reload it all without error.
self.construct_everything()
data = self.get_export()
clean_db(db)
db.session.commit()
self.load_database(data)
def test_export_admin_details(self):
user = self.construct_user(email="testadmin@test.com", role=Role.admin)
user.password = "This_is_my_password!12345"
db.session.add(user)
db.session.commit()
rv = self.app.get('/api/export/admin',
follow_redirects=True,
content_type="application/json",
headers=self.logged_in_headers())
self.assert_success(rv)
response = json.loads(rv.get_data(as_text=True))
self.assertTrue(len(response) > 1)
self.assertEqual(1, len(list(filter(lambda field: field['email'] == 'testadmin@test.com', response))))
self.assertEqual(1, len(list(filter(lambda field: field['_password'] is not None, response))))
def test_exporter_logs_export_calls(self):
rv = self.app.get('/api/export',
follow_redirects=True,
content_type="application/json",
headers=self.logged_in_headers())
self.assert_success(rv)
export_logs = db.session.query(DataTransferLog).filter(DataTransferLog.type == "export").all()
self.assertEqual(1, len(export_logs))
self.assertIsNotNone(export_logs[0].last_updated)
self.assertTrue(export_logs[0].total_records > 0, msg="The act of setting up this test harness should mean "
"at least one user record is avialable for export")
self.assertEqual(1, len(export_logs[0].details))
detail = export_logs[0].details[0]
self.assertEqual("User", detail.class_name)
self.assertEqual(True, detail.successful)
self.assertEqual(1, detail.success_count)
def test_exporter_sends_no_email_alert_if_less_than_30_minutes_pass_without_export(self):
message_count = len(TEST_MESSAGES)
log = DataTransferLog(last_updated=datetime.datetime.utcnow() - datetime.timedelta(minutes=28), total_records=2,
type="export")
db.session.add(log)
db.session.commit()
ExportService.send_alert_if_exports_not_running()
self.assertEqual(len(TEST_MESSAGES), message_count)
def test_exporter_sends_email_alert_if_30_minutes_pass_without_export(self):
"""
If more than 30 minutes pass without an export from the Public Mirror to the Private Mirror, an email should be
sent to an administrative email address.
"""
message_count = len(TEST_MESSAGES)
log = DataTransferLog(last_updated=datetime.datetime.utcnow() - datetime.timedelta(minutes=45), total_records=2,
type="export")
db.session.add(log)
db.session.commit()
ExportService.send_alert_if_exports_not_running()
self.assertGreater(len(TEST_MESSAGES), message_count)
self.assertEqual("Autism DRIVE: Error - 45 minutes since last successful export",
self.decode(TEST_MESSAGES[-1]['subject']))
ExportService.send_alert_if_exports_not_running()
ExportService.send_alert_if_exports_not_running()
ExportService.send_alert_if_exports_not_running()
self.assertEqual(message_count + 1, len(TEST_MESSAGES), msg="No more messages should be sent.")
self.assertEqual("admin@tester.com", TEST_MESSAGES[-1]['To'])
def test_exporter_sends_second_email_after_2_hours(self):
"""
If more than 2 hours pass without an export from the Public Mirror to the Private Mirror, an email will be
sent to an administrative email address at the 30 minute and 2 hour marks.
"""
message_count = len(TEST_MESSAGES)
log = DataTransferLog(last_updated=datetime.datetime.utcnow() - datetime.timedelta(minutes=30), total_records=2, type="export")
db.session.add(log)
db.session.commit()
ExportService.send_alert_if_exports_not_running()
print('@ 30 minutes:', len(TEST_MESSAGES), 'messages')
self.assertGreater(len(TEST_MESSAGES), message_count)
self.assertEqual("Autism DRIVE: Error - 30 minutes since last successful export", self.decode(TEST_MESSAGES[-1]['subject']))
log.last_updated = datetime.datetime.utcnow() - datetime.timedelta(minutes=120)
db.session.add(log)
db.session.commit()
ExportService.send_alert_if_exports_not_running()
print('@ 2 hours:', len(TEST_MESSAGES), 'messages')
self.assertGreater(len(TEST_MESSAGES), message_count + 1, "another email should have gone out")
self.assertEqual("Autism DRIVE: Error - 2 hours since last successful export", self.decode(TEST_MESSAGES[-1]['subject']))
def test_exporter_sends_12_emails_over_first_24_hours(self):
"""
If more than 24 hours pass without an export from the Public Mirror to the Private Mirror, an email will be
sent to an administrative email address at the 30 minute and then every 2 hours after that.
"""
message_count = len(TEST_MESSAGES)
date = datetime.datetime.utcnow() - datetime.timedelta(hours=22)
log = DataTransferLog(last_updated=date,
total_records=2, type="export")
db.session.add(log)
db.session.commit()
for i in range(12):
ExportService.send_alert_if_exports_not_running()
self.assertEqual(message_count + 12, len(TEST_MESSAGES), msg="12 emails should have gone out.")
def test_exporter_sends_20_emails_over_first_48_hours(self):
message_count = len(TEST_MESSAGES)
log = DataTransferLog(last_updated=datetime.datetime.utcnow() - datetime.timedelta(days=2), total_records=2,
type="export")
db.session.add(log)
db.session.commit()
for i in range(20):
ExportService.send_alert_if_exports_not_running()
self.assertEqual(message_count + 20, len(TEST_MESSAGES), msg="20 emails should have gone out.")
def test_exporter_notifies_PI_after_24_hours(self):
message_count = len(TEST_MESSAGES)
log = DataTransferLog(last_updated=datetime.datetime.utcnow() - datetime.timedelta(hours=24), total_records=2,
type="export")
db.session.add(log)
db.session.commit()
ExportService.send_alert_if_exports_not_running()
self.assertTrue("pi@tester.com" in TEST_MESSAGES[-1]['To'])

View File

@ -0,0 +1,310 @@
import datetime
import unittest
import httpretty
import requests
from flask import json
from app import app, db
from app.import_service import ImportService
from app.model.data_transfer_log import DataTransferLog, DataTransferLogDetail
from app.model.export_info import ExportInfo, ExportInfoSchema
from app.model.questionnaires.clinical_diagnoses_questionnaire import ClinicalDiagnosesQuestionnaireSchema
from app.model.questionnaires.employment_questionnaire import EmploymentQuestionnaireSchema
from app.model.user import User, Role
from app.schema.export_schema import UserExportSchema, AdminExportSchema
from tests.base_test_questionnaire import BaseTestQuestionnaire
class TestImportCase(BaseTestQuestionnaire, unittest.TestCase):
"""Please note that the actual loading of data is tested in the Export tests, this
Is really assuring that the import service makes the right calls to the API with the
right arguments."""
def setUp(self):
super().setUp()
app.config["MASTER_URL"] = "http://na.edu"
app.config["MASTER_EMAIL"] = "dan@test.com"
app.config["MASTER_PASS"] = "12345"
@httpretty.activate
def test_httppretty(self):
# Just wanted a simple exmple to show how to use HTTP Pretty
httpretty.register_uri(
httpretty.GET,
"https://httpbin.org/ip",
body='{"origin": "127.0.0.1"}'
)
response = requests.get('https://httpbin.org/ip')
rv = response.json()
self.assertTrue("origin" in rv)
self.assertTrue(rv["origin"] == "127.0.0.1")
self.assertEqual(1, len(httpretty.httpretty.latest_requests))
self.assertIsNotNone(httpretty.last_request())
self.assertEqual(httpretty.last_request().body, b'')
@httpretty.activate
def test_login(self):
# Just wanted a simple exmple to show how to use HTTP Pretty
httpretty.register_uri(
httpretty.POST,
"http://na.edu/api/login_password",
body='{"token": "my_token"}'
)
data_importer = ImportService(app, db)
data_importer.login()
self.assertIsNotNone(httpretty.last_request())
@httpretty.activate
def test_headers(self):
# Assure that a failed request to get_headers will cause a re-login attempt.
httpretty.register_uri(
httpretty.GET,
"http://na.edu/api/session",
body='{"error": "not logged in"}',
status=400
)
httpretty.register_uri(
httpretty.POST,
"http://na.edu/api/login_password",
body='{"token": "my_token"}',
)
app.config["MASTER_URL"] = "http://na.edu"
app.config["MASTER_EMAIL"] = "dan@test.com"
app.config["MASTER_PASS"] = "12345"
data_importer = ImportService(app, db)
headers = data_importer.get_headers()
self.assertIsNotNone(httpretty.last_request())
self.assertIsNotNone(headers)
self.assertEqual("Bearer my_token", headers['Authorization'])
self.assertEqual(3, len(httpretty.httpretty.latest_requests))
def get_data_importer_setup_auth(self):
httpretty.register_uri(
httpretty.GET,
"http://na.edu/api/session",
body='{"email": "dan@test.com"}',
status=200
)
data_importer = ImportService(app, db)
data_importer.token = "my_token"
return data_importer
@httpretty.activate
def test_get_export_list(self):
info = [ExportInfo('my_table', 'my_class', size=0, url="http://na.edu/api/export/my_class")]
info_json = ExportInfoSchema(many=True).jsonify(info).data
httpretty.register_uri(
httpretty.GET,
"http://na.edu/api/export",
body=info_json,
status=200
)
data_importer = self.get_data_importer_setup_auth()
exportables = data_importer.get_export_list()
self.assertEqual(1, len(exportables))
@httpretty.activate
def test_no_subsequent_requests_when_size_is_0(self):
data_importer = self.get_data_importer_setup_auth()
info = [ExportInfo('my_table', 'my_class', size=0, url="http://na.edu/api/export/my_class")]
info_json = ExportInfoSchema(many=True).jsonify(info).data
httpretty.register_uri(
httpretty.GET,
"http://na.edu/api/export",
body=info_json,
status=200
)
export_list = data_importer.get_export_list()
data_importer.request_data(export_list)
self.assertEqual(2, len(httpretty.httpretty.latest_requests)) # Assumes one request for auth check.
self.assertEqual("/api/export", httpretty.last_request().path)
def request_user_setup(self):
info = [ExportInfo('star_user', 'User', size=1, url="/api/export/user")]
info_json = ExportInfoSchema(many=True).jsonify(info).data
user = User(id=4, last_updated=datetime.datetime.now(), email="dan@test.com",
role=Role.user, email_verified=True, _password="m@kerspace")
user_json = self.jsonify(UserExportSchema(many=True).dump([user]))
admin_json = self.jsonify(AdminExportSchema(many=True).dump([user]))
httpretty.register_uri(
httpretty.GET,
"http://na.edu/api/export",
body=info_json,
status=200
)
httpretty.register_uri(
httpretty.GET,
"http://na.edu/api/export/user",
body=user_json,
status=200
)
httpretty.register_uri(
httpretty.GET,
"http://na.edu/api/export/admin",
body=admin_json,
status=200
)
@httpretty.activate
def test_request_when_size_larger_than_0(self):
data_importer = self.get_data_importer_setup_auth()
self.request_user_setup()
export_list = data_importer.get_export_list()
data_importer.request_data(export_list)
self.assertEqual("/api/export/user", httpretty.last_request().path)
@httpretty.activate
def test_import_logs_success(self):
data_importer = self.get_data_importer_setup_auth()
self.request_user_setup()
data_importer.run_backup()
self.assertEqual("/api/export/admin", httpretty.last_request().path)
logs = db.session.query(DataTransferLog).all()
self.assertTrue(len(logs) > 0)
log = logs[0]
self.assertIsNotNone(log.date_started)
self.assertIsNotNone(log.last_updated)
self.assertEqual(1, len(logs[0].details))
detail = logs[0].details[0]
self.assertEqual("User", detail.class_name)
self.assertTrue(detail.successful)
self.assertEqual(1, detail.success_count)
self.assertEqual(0, detail.failure_count)
@httpretty.activate
def test_import_logs_schema_error(self):
info = [ExportInfo('star_user', 'User', size=1, url="/api/export/user")]
info_json = ExportInfoSchema(many=True).jsonify(info).data
user_json = self.jsonify([{"id": "55", "pickes": "42"}])
httpretty.register_uri(
httpretty.GET,
"http://na.edu/api/export",
body=info_json,
status=200
)
httpretty.register_uri(
httpretty.GET,
"http://na.edu/api/export/user",
body=user_json,
status=200
)
data_importer = self.get_data_importer_setup_auth()
date = datetime.datetime.now()
export_list = data_importer.get_export_list()
log = data_importer.log_for_export(export_list, date)
data = data_importer.request_data(export_list)
try:
data_importer.load_all_data(data, log)
except:
pass # Totally should happen.
self.assertEqual("/api/export/user", httpretty.last_request().path)
logs = db.session.query(DataTransferLog).all()
self.assertTrue(len(logs) > 0)
log = logs[-1]
self.assertIsNotNone(log.date_started)
self.assertIsNotNone(log.last_updated)
details = log.details
self.assertEqual("User", log.details[0].class_name)
self.assertFalse(log.details[0].successful)
self.assertEqual(0, log.details[0].success_count)
self.assertEqual(1, log.details[0].failure_count)
@httpretty.activate
def test_request_includes_date_param_if_log_exists(self):
# log a previous success
last_date = datetime.datetime.now() - datetime.timedelta(days=1)
log = DataTransferLogDetail(date_started=last_date, class_name="User")
db.session.add(log)
data_importer = self.get_data_importer_setup_auth()
self.request_user_setup()
data_importer.run_backup(load_admin=False)
self.assertTrue("after" in httpretty.last_request().querystring)
@httpretty.activate
def test_admin_accounts_should_be_requested_in_full_and_import_with_working_password(self):
password = "Tacos are good! 823497!#%$^&*"
data_importer = self.get_data_importer_setup_auth()
user = User(id=4, last_updated=datetime.datetime.now(), email="dan@test.com",
role=Role.admin, email_verified=True)
user.password = password
user_json = self.jsonify(AdminExportSchema(many=True).dump([user]))
httpretty.register_uri(
httpretty.GET,
"http://na.edu/api/export/admin",
body=user_json,
status=200
)
data_importer.load_admin()
# encoded = base64.encodestring(user._password)
# data = {'email': 'dan@test.com', 'password': encoded.decode()}
data = {'email': 'dan@test.com', 'password': password}
rv = self.app.post(
'/api/login_password',
data=self.jsonify(data),
content_type="application/json")
self.assertEqual(200, rv.status_code)
@httpretty.activate
def test_import_calls_delete_on_sensitive_data(self):
export_list = [ExportInfo('clinical_diagnoses_questionnaire', 'ClinicalDiagnosesQuestionnaire', size=1,
url="/api/export/clinical_diagnoses_questionnaire")]
q = self.construct_clinical_diagnoses_questionnaire()
id = q.id
json_q = self.jsonify(ClinicalDiagnosesQuestionnaireSchema(many=True).dump([q]))
db.session.delete(q)
httpretty.register_uri(
httpretty.GET,
"http://na.edu/api/export/clinical_diagnoses_questionnaire",
body=json_q,
status=200
)
expected_delete_url = "http://na.edu/api/q/clinical_diagnoses_questionnaire/" + str(id)
httpretty.register_uri(
httpretty.DELETE,
expected_delete_url,
body=json_q,
status=200
)
app.config['DELETE_RECORDS'] = True
data_importer = self.get_data_importer_setup_auth()
date = datetime.datetime.now()
data = data_importer.request_data(export_list)
log = data_importer.log_for_export(data, date)
data_importer.load_all_data(data, log)
self.assertEqual("/api/q/clinical_diagnoses_questionnaire/" + str(id), httpretty.last_request().path)
self.assertEqual("DELETE", httpretty.last_request().method)
@httpretty.activate
def test_import_does_not_call_delete_on_non_sensitive_data(self):
export_list = [ExportInfo('employment_questionnaire', 'EmploymentQuestionnaire', size=1,
url="/api/export/employment_questionnaire")]
q = self.construct_employment_questionnaire()
id = q.id
json_q = self.jsonify(EmploymentQuestionnaireSchema(many=True).dump([q]))
db.session.delete(q)
httpretty.register_uri(
httpretty.GET,
"http://na.edu/api/export/employment_questionnaire",
body=json_q,
status=200
)
data_importer = self.get_data_importer_setup_auth()
date = datetime.datetime.now()
data = data_importer.request_data(export_list)
log = data_importer.log_for_export(data, date)
data_importer.load_all_data(data, log)
self.assertEqual("GET", httpretty.last_request().method) # Last call should not be a delete call.

View File

@ -0,0 +1,113 @@
import unittest
from flask import json
from tests.base_test import BaseTest
from app import db
from app.model.investigator import Investigator
from app.model.study_investigator import StudyInvestigator
class TestStudy(BaseTest, unittest.TestCase):
def test_investigator_basics(self):
self.construct_investigator()
i = db.session.query(Investigator).first()
self.assertIsNotNone(i)
i_id = i.id
rv = self.app.get('/api/investigator/%i' % i_id,
follow_redirects=True,
content_type="application/json", headers=self.logged_in_headers())
self.assert_success(rv)
response = json.loads(rv.get_data(as_text=True))
self.assertEqual(response["id"], i_id)
self.assertEqual(response["name"], i.name)
def test_modify_investigator_basics(self):
self.construct_investigator()
i = db.session.query(Investigator).first()
self.assertIsNotNone(i)
rv = self.app.get('/api/investigator/%i' % i.id, content_type="application/json", headers=self.logged_in_headers())
self.assert_success(rv)
response = json.loads(rv.get_data(as_text=True))
response['title'] = 'dungeon master'
orig_date = response['last_updated']
rv = self.app.put('/api/investigator/%i' % i.id, data=self.jsonify(response), content_type="application/json",
follow_redirects=True, headers=self.logged_in_headers())
self.assert_success(rv)
response = json.loads(rv.get_data(as_text=True))
self.assertEqual(response['title'], 'dungeon master')
self.assertNotEqual(orig_date, response['last_updated'])
def test_delete_investigator(self):
i = self.construct_investigator()
i_id = i.id
rv = self.app.get('api/investigator/%i' % i_id, content_type="application/json", headers=self.logged_in_headers())
self.assert_success(rv)
rv = self.app.delete('api/investigator/%i' % i_id, content_type="application/json",
headers=self.logged_in_headers())
self.assert_success(rv)
rv = self.app.get('api/investigator/%i' % i_id, content_type="application/json", headers=self.logged_in_headers())
self.assertEqual(404, rv.status_code)
def test_create_investigator(self):
investigator = {'name': "Tara Tarantula", 'title': "Assistant Professor of Arachnology",
'organization_name': "Spider University"}
rv = self.app.post('api/investigator', data=self.jsonify(investigator), content_type="application/json",
headers=self.logged_in_headers(), follow_redirects=True)
self.assert_success(rv)
response = json.loads(rv.get_data(as_text=True))
self.assertEqual(response['name'], 'Tara Tarantula')
self.assertEqual(response['title'], 'Assistant Professor of Arachnology')
self.assertIsNotNone(response['id'])
def test_investigator_list_alphabetical_by_name(self):
self.construct_investigator(name="Adelaide Smith")
self.construct_investigator(name="Sarah Blakemore")
self.construct_investigator(name="Zelda Cat")
self.construct_investigator(name="Benjamin Jensen")
rv = self.app.get('api/investigator', content_type="application/json", headers=self.logged_in_headers())
self.assert_success(rv)
response = json.loads(rv.get_data(as_text=True))
self.assertEqual(response[0]['name'], 'Adelaide Smith')
self.assertEqual(response[1]['name'], 'Benjamin Jensen')
self.assertEqual(response[2]['name'], 'Sarah Blakemore')
self.assertEqual(response[3]['name'], 'Zelda Cat')
def test_create_investigator_checks_for_name(self):
self.test_create_investigator()
rv = self.app.get('api/investigator', content_type="application/json", headers=self.logged_in_headers())
self.assert_success(rv)
response = json.loads(rv.get_data(as_text=True))
self.assertEqual(response[0]['name'], 'Tara Tarantula')
self.assertEqual(response[0]['title'], 'Assistant Professor of Arachnology')
investigator = {'name': "Tara Tarantula", 'title': "Spider"}
rv = self.app.post('api/investigator', data=self.jsonify(investigator), content_type="application/json",
headers=self.logged_in_headers(), follow_redirects=True)
self.assert_success(rv)
response = json.loads(rv.get_data(as_text=True))
self.assertEqual(response['name'], 'Tara Tarantula')
self.assertEqual(response['title'], 'Assistant Professor of Arachnology')
def test_delete_investigator_deletes_relationship(self):
i = self.construct_investigator()
s = self.construct_study()
si = StudyInvestigator(investigator_id=i.id, study_id=s.id)
db.session.add(si)
db.session.commit()
si_id = si.id
rv = self.app.get('api/study_investigator/%i' % si_id, content_type="application/json", headers=self.logged_in_headers())
self.assert_success(rv)
rv = self.app.delete('api/investigator/%i' % i.id, content_type="application/json",
headers=self.logged_in_headers())
self.assert_success(rv)
rv = self.app.get('api/study_investigator/%i' % si_id, content_type="application/json", headers=self.logged_in_headers())
self.assertEqual(404, rv.status_code)

View File

@ -0,0 +1,262 @@
import unittest
from flask import json
from tests.base_test import BaseTest
from app import db, data_loader
from app.model.location import Location
from app.model.resource_category import ResourceCategory
from app.model.resource_change_log import ResourceChangeLog
from app.model.user import Role
class TestLocations(BaseTest, unittest.TestCase):
def test_location_basics(self):
self.construct_location()
r = db.session.query(Location).first()
self.assertIsNotNone(r)
r_id = r.id
rv = self.app.get('/api/location/%i' % r_id,
follow_redirects=True,
content_type="application/json")
self.assert_success(rv)
response = json.loads(rv.get_data(as_text=True))
self.assertEqual(response["id"], r_id)
self.assertEqual(response["title"], 'A+ location')
self.assertEqual(response["description"], 'A delightful location destined to create rejoicing')
self.assertEqual(response["latitude"], 38.98765)
self.assertEqual(response["longitude"], -93.12345)
def test_modify_location_basics(self):
self.construct_location()
r = db.session.query(Location).first()
self.assertIsNotNone(r)
r_id = r.id
rv = self.app.get('/api/location/%i' % r_id, content_type="application/json")
response = json.loads(rv.get_data(as_text=True))
response['title'] = 'Edwarardos Lemonade and Oil Change'
response['description'] = 'Better fluids for you and your car.'
response['website'] = 'http://sartography.com'
response['latitude'] = 34.5678
response['longitude'] = -98.7654
orig_date = response['last_updated']
rv = self.app.put('/api/location/%i' % r_id, data=self.jsonify(response), content_type="application/json",
follow_redirects=True, headers=self.logged_in_headers())
self.assert_success(rv)
rv = self.app.get('/api/location/%i' % r_id, content_type="application/json")
self.assert_success(rv)
response = json.loads(rv.get_data(as_text=True))
self.assertEqual(response['title'], 'Edwarardos Lemonade and Oil Change')
self.assertEqual(response['description'], 'Better fluids for you and your car.')
self.assertEqual(response['website'], 'http://sartography.com')
self.assertEqual(response['latitude'], 34.5678)
self.assertEqual(response['longitude'], -98.7654)
self.assertNotEqual(orig_date, response['last_updated'])
def test_delete_location(self):
r = self.construct_location()
r_id = r.id
rv = self.app.get('api/location/%i' % r_id, content_type="application/json")
self.assert_success(rv)
rv = self.app.delete('api/location/%i' % r_id, content_type="application/json", headers=self.logged_in_headers())
self.assert_success(rv)
rv = self.app.get('api/location/%i' % r_id, content_type="application/json")
self.assertEqual(404, rv.status_code)
def test_create_location(self):
data_loader.DataLoader().load_partial_zip_codes()
location = {'title': "location of locations", 'description': "You need this location in your life.",
'organization_name': "Location Org"}
rv = self.app.post('api/location', data=self.jsonify(location), content_type="application/json",
follow_redirects=True, headers=self.logged_in_headers())
self.assert_success(rv)
response = json.loads(rv.get_data(as_text=True))
self.assertEqual(response['title'], 'location of locations')
self.assertEqual(response['description'], 'You need this location in your life.')
self.assertIsNotNone(response['id'])
def test_get_location_by_category(self):
loc = self.construct_location()
c = self.construct_location_category(loc.id, "c1")
rv = self.app.get(
'/api/category/%i/location' % c.id,
content_type="application/json",
headers=self.logged_in_headers())
self.assert_success(rv)
response = json.loads(rv.get_data(as_text=True))
self.assertEqual(1, len(response))
self.assertEqual(loc.id, response[0]["resource_id"])
self.assertEqual(loc.description, response[0]["resource"]["description"])
def test_get_location_by_category_includes_category_details(self):
loc = self.construct_location()
c1 = self.construct_location_category(loc.id, "c1")
c2 = self.construct_location_category(loc.id, "c2")
rv = self.app.get(
'/api/category/%i/location' % c1.id,
content_type="application/json",
headers=self.logged_in_headers())
self.assert_success(rv)
response = json.loads(rv.get_data(as_text=True))
self.assertEqual(loc.id, response[0]["resource_id"])
self.assertEqual(2, len(response[0]["resource"]["resource_categories"]))
self.assertEqual("c1", response[0]["resource"]["resource_categories"][0]["category"]["name"])
def test_category_location_count(self):
loc = self.construct_location()
c = self.construct_location_category(loc.id, "c1")
rv = self.app.get(
'/api/category/%i' % c.id, content_type="application/json")
self.assert_success(rv)
response = json.loads(rv.get_data(as_text=True))
self.assertEqual(1, response["location_count"])
def test_get_category_by_location(self):
c = self.construct_category()
loc = self.construct_location()
rc = ResourceCategory(resource_id=loc.id, category=c, type='location')
db.session.add(rc)
db.session.commit()
rv = self.app.get(
'/api/location/%i/category' % loc.id,
content_type="application/json")
self.assert_success(rv)
response = json.loads(rv.get_data(as_text=True))
self.assertEqual(1, len(response))
self.assertEqual(c.id, response[0]["id"])
self.assertEqual(c.name, response[0]["category"]["name"])
def test_add_category_to_location(self):
c = self.construct_category()
loc = self.construct_location()
rc_data = {"resource_id": loc.id, "category_id": c.id}
rv = self.app.post(
'/api/resource_category',
data=self.jsonify(rc_data),
content_type="application/json")
self.assert_success(rv)
response = json.loads(rv.get_data(as_text=True))
self.assertEqual(c.id, response["category_id"])
self.assertEqual(loc.id, response["resource_id"])
def test_set_all_categories_on_location(self):
c1 = self.construct_category(name="c1")
c2 = self.construct_category(name="c2")
c3 = self.construct_category(name="c3")
loc = self.construct_location()
lc_data = [
{
"category_id": c1.id
},
{
"category_id": c2.id
},
{
"category_id": c3.id
},
]
rv = self.app.post(
'/api/location/%i/category' % loc.id,
data=self.jsonify(lc_data),
content_type="application/json")
self.assert_success(rv)
response = json.loads(rv.get_data(as_text=True))
self.assertEqual(3, len(response))
lc_data = [{"category_id": c1.id}]
rv = self.app.post(
'/api/location/%i/category' % loc.id,
data=self.jsonify(lc_data),
content_type="application/json")
self.assert_success(rv)
response = json.loads(rv.get_data(as_text=True))
self.assertEqual(1, len(response))
def test_remove_category_from_location(self):
self.test_add_category_to_location()
rv = self.app.delete('/api/resource_category/%i' % 1)
self.assert_success(rv)
rv = self.app.get(
'/api/location/%i/category' % 1, content_type="application/json")
self.assert_success(rv)
response = json.loads(rv.get_data(as_text=True))
self.assertEqual(0, len(response))
def test_zip_code_coordinates(self):
z = self.construct_zip_code()
rv = self.app.get(
'/api/zip_code_coords/%i' % z.id, content_type="application/json")
self.assert_success(rv)
response = json.loads(rv.get_data(as_text=True))
self.assertEqual(z.id, response["id"])
self.assertEqual(z.latitude, response["latitude"])
self.assertEqual(z.longitude, response["longitude"])
def test_resource_change_log(self):
l = self.construct_location(title='A Location that is Super and Great')
u = self.construct_user(email="editor@sartorgraphy.com", role=Role.admin)
rv = self.app.get('api/location/%i' % l.id, content_type="application/json")
self.assert_success(rv)
response = json.loads(rv.get_data(as_text=True))
response['title'] = 'Super Great Location'
rv = self.app.put('/api/location/%i' % l.id, data=self.jsonify(response), content_type="application/json",
follow_redirects=True, headers=self.logged_in_headers(user=u))
self.assert_success(rv)
rv = self.app.get('/api/location/%i' % l.id, content_type="application/json")
self.assert_success(rv)
response = json.loads(rv.get_data(as_text=True))
self.assertEqual(response['title'], 'Super Great Location')
logs = ResourceChangeLog.query.all()
self.assertIsNotNone(logs[-1].resource_id)
self.assertIsNotNone(logs[-1].user_id)
rv = self.app.get('/api/resource/%i/change_log' % l.id, content_type="application/json", headers=self.logged_in_headers())
self.assert_success(rv)
response = json.loads(rv.get_data(as_text=True))
self.assertEqual(response[-1]['user_id'], u.id)
rv = self.app.get('/api/user/%i/resource_change_log' % u.id, content_type="application/json", headers=self.logged_in_headers())
self.assert_success(rv)
response = json.loads(rv.get_data(as_text=True))
self.assertEqual(response[-1]['resource_id'], l.id)
def test_geocode_setting(self):
data_loader.DataLoader().load_partial_zip_codes()
location = {'title': "Some super place", 'description': "You should go here every day."}
rv = self.app.post('api/location', data=self.jsonify(location), content_type="application/json",
follow_redirects=True, headers=self.logged_in_headers())
self.assert_success(rv)
response = json.loads(rv.get_data(as_text=True))
self.assertIsNotNone(response['latitude'])
self.assertIsNotNone(response['longitude'])
location_id = response['id']
initial_lat_lng = str(response['latitude']) + str(response['longitude'])
# lat and lng shouldn't change on edit unless the street_address1 or zip fields are edited.
response['description'] = "Something different"
rv = self.app.put('/api/location/%i' % location_id, data=self.jsonify(response), content_type="application/json",
follow_redirects=True, headers=self.logged_in_headers())
self.assert_success(rv)
rv = self.app.get('/api/location/%i' % location_id, content_type="application/json")
self.assert_success(rv)
response = json.loads(rv.get_data(as_text=True))
self.assertEqual(initial_lat_lng, str(response['latitude']) + str(response['longitude']))
response['zip'] = '24401'
rv = self.app.put('/api/location/%i' % location_id, data=self.jsonify(response), content_type="application/json",
follow_redirects=True, headers=self.logged_in_headers())
self.assert_success(rv)
rv = self.app.get('/api/location/%i' % location_id, content_type="application/json")
self.assert_success(rv)
response = json.loads(rv.get_data(as_text=True))
self.assertNotEqual(initial_lat_lng, str(response['latitude']) + str(response['longitude']))

View File

@ -0,0 +1,336 @@
import unittest
from datetime import datetime
from flask import json
from app.schema.schema import UserMetaSchema
from tests.base_test_questionnaire import BaseTestQuestionnaire
from app.model.participant import Relationship, Participant
from app.model.user_meta import UserMeta
from app import db
class TestParticipant(BaseTestQuestionnaire, unittest.TestCase):
def test_participant_relationships(self):
u = self.construct_user()
participant = self.construct_participant(user=u, relationship=Relationship.self_participant)
guardian = self.construct_participant(user=u, relationship=Relationship.self_guardian)
dependent = self.construct_participant(user=u, relationship=Relationship.dependent)
professional = self.construct_participant(user=u, relationship=Relationship.self_professional)
interested = self.construct_participant(user=u, relationship=Relationship.self_interested)
rv = self.app.get('/api/user/%i' % u.id,
follow_redirects=True,
content_type="application/json", headers=self.logged_in_headers())
self.assert_success(rv)
response = json.loads(rv.get_data(as_text=True))
self.assertEqual(response["id"], u.id)
self.assertEqual(len(response["participants"]), 5)
self.assertEqual(response["participants"][0]["id"], participant.id)
self.assertEqual(response["participants"][0]["relationship"], 'self_participant')
self.assertEqual(response["participants"][1]["id"], guardian.id)
self.assertEqual(response["participants"][1]["relationship"], 'self_guardian')
self.assertEqual(response["participants"][2]["id"], dependent.id)
self.assertEqual(response["participants"][2]["relationship"], 'dependent')
self.assertEqual(response["participants"][3]["id"], professional.id)
self.assertEqual(response["participants"][3]["relationship"], 'self_professional')
self.assertEqual(response["participants"][4]["id"], interested.id)
self.assertEqual(response["participants"][4]["relationship"], 'self_interested')
def test_participant_basics(self):
self.construct_participant(user=self.construct_user(), relationship=Relationship.dependent)
p = db.session.query(Participant).first()
self.assertIsNotNone(p)
p_id = p.id
rv = self.app.get('/api/participant/%i' % p_id,
follow_redirects=True,
content_type="application/json", headers=self.logged_in_headers())
self.assert_success(rv)
response = json.loads(rv.get_data(as_text=True))
self.assertEqual(response["id"], p_id)
self.assertEqual(response["relationship"], p.relationship.name)
def test_modify_participant_you_do_not_own(self):
u = self.construct_user()
p = self.construct_participant(user=u, relationship=Relationship.self_participant)
good_headers = self.logged_in_headers(u)
p = db.session.query(Participant).first()
odd_user = self.construct_user(email='frankie@badfella.rf')
participant = {'first_name': "Lil' Johnny", 'last_name': "Tables"}
rv = self.app.put('/api/participant/%i' % p.id, data=self.jsonify(participant), content_type="application/json",
follow_redirects=True)
self.assertEqual(401, rv.status_code, "you have to be logged in to edit participant.")
rv = self.app.put('/api/participant/%i' % p.id, data=self.jsonify(participant), content_type="application/json",
follow_redirects=True, headers=self.logged_in_headers(odd_user))
self.assertEqual(400, rv.status_code, "you have to have a relationship with the user to do stuff.")
response = json.loads(rv.get_data(as_text=True))
self.assertEqual("unrelated_participant", response['code'])
rv = self.app.put('/api/participant/%i' % p.id, data=self.jsonify(participant), content_type="application/json",
follow_redirects=True, headers=good_headers)
self.assertEqual(200, rv.status_code, "The owner can edit the user.")
def test_modify_participant_you_do_own(self):
u = self.construct_user()
p = self.construct_participant(user=u, relationship=Relationship.self_guardian)
headers = self.logged_in_headers(u)
participant = {'id': 567}
rv = self.app.put('/api/participant/%i' % p.id, data=self.jsonify(participant), content_type="application/json",
follow_redirects=True, headers=headers)
self.assert_success(rv)
p = db.session.query(Participant).filter_by(id=p.id).first()
self.assertEqual(567, p.id)
def test_modify_participant_basics_admin(self):
self.construct_participant(user=self.construct_user(), relationship=Relationship.dependent)
user2 = self.construct_user(email="theotherguy@stuff.com")
logged_in_headers = self.logged_in_headers()
p = db.session.query(Participant).first()
self.assertIsNotNone(p)
p_id = p.id
rv = self.app.get('/api/participant/%i' % p_id, content_type="application/json",
headers=logged_in_headers)
self.assert_success(rv)
response = json.loads(rv.get_data(as_text=True))
response['user_id'] = user2.id
orig_date = response['last_updated']
rv = self.app.put('/api/participant/%i' % p_id, data=self.jsonify(response), content_type="application/json",
follow_redirects=True, headers=logged_in_headers)
self.assert_success(rv)
rv = self.app.get('/api/participant/%i' % p_id, content_type="application/json",
headers=logged_in_headers)
self.assert_success(rv)
response = json.loads(rv.get_data(as_text=True))
self.assertEqual(response['user_id'], user2.id)
self.assertNotEqual(orig_date, response['last_updated'])
def test_delete_participant(self):
u = self.construct_user()
p = self.construct_participant(user=u, relationship=Relationship.dependent)
p_id = p.id
rv = self.app.get('api/participant/%i' % p_id, content_type="application/json")
self.assertEqual(401, rv.status_code)
rv = self.app.get('api/participant/%i' % p_id, content_type="application/json",
headers=self.logged_in_headers())
self.assert_success(rv)
rv = self.app.delete('api/participant/%i' % p_id, content_type="application/json")
self.assertEqual(401, rv.status_code)
rv = self.app.delete('api/participant/%i' % p_id, content_type="application/json",
headers=self.logged_in_headers())
self.assert_success(rv)
rv = self.app.get('api/participant/%i' % p_id, content_type="application/json")
self.assertEqual(401, rv.status_code)
rv = self.app.get('api/participant/%i' % p_id, content_type="application/json",
headers=self.logged_in_headers())
self.assertEqual(404, rv.status_code)
def test_create_participant(self):
p = {'id': 7, 'relationship': 'self_participant'}
rv = self.app.post('/api/session/participant', data=self.jsonify(p), content_type="application/json",
follow_redirects=True)
self.assertEqual(401, rv.status_code, "you can't create a participant without an account.")
rv = self.app.post(
'/api/session/participant', data=self.jsonify(p),
content_type="application/json", headers=self.logged_in_headers())
self.assert_success(rv)
participant = db.session.query(Participant).filter_by(id=p['id']).first()
self.assertIsNotNone(participant.id)
self.assertIsNotNone(participant.user_id)
def test_create_participant_to_have_bad_relationship(self):
participant = {'id': 234, 'relationship': 'free_loader'}
rv = self.app.post('/api/session/participant', data=self.jsonify(participant),
content_type="application/json", follow_redirects=True, headers=self.logged_in_headers())
self.assertEqual(400, rv.status_code, "you can't create a participant using an invalid relationship")
response = json.loads(rv.get_data(as_text=True))
self.assertEqual(response["code"], "unknown_relationship")
def test_create_participant_to_have_invalid_relationship(self):
u = self.construct_user()
p = {'id': 10, 'relationship': 'self_guardian', 'user_id': u.id}
rv = self.app.post(
'/api/session/participant', data=self.jsonify(p),
content_type="application/json", headers=self.logged_in_headers())
self.assert_success(rv)
p_bad = {'id': 10, 'relationship': 'self_guardian', 'user_id': u.id}
rv_bad = self.app.post(
'/api/session/participant', data=self.jsonify(p_bad),
content_type="application/json", headers=self.logged_in_headers())
self.assertEqual(400, rv_bad.status_code, "You may not edit another users account.")
response = json.loads(rv_bad.get_data(as_text=True))
self.assertEqual(response["code"], "permission_denied")
def test_get_participant_by_user(self):
u = self.construct_user()
p = self.construct_participant(user=u, relationship=Relationship.self_participant)
db.session.commit()
rv = self.app.get(
'/api/user/%i' % u.id,
content_type="application/json", headers=self.logged_in_headers())
self.assert_success(rv)
response = json.loads(rv.get_data(as_text=True))
self.assertEqual(u.id, response['id'])
self.assertEqual(1, len(response['participants']))
self.assertEqual(p.relationship.name, response['participants'][0]["relationship"])
def test_participant_ids_are_not_sequential(self):
u = self.construct_user()
p1 = self.construct_participant(user=u, relationship=Relationship.self_participant)
p2 = self.construct_participant(user=u, relationship=Relationship.dependent)
p3 = self.construct_participant(user=u, relationship=Relationship.dependent)
self.assertNotEqual(p1.id + 1, p2.id)
self.assertNotEqual(p2.id + 1, p3.id)
def test_participant_admin_list(self):
u1 = self.construct_user(email='test1@sartography.com')
u2 = self.construct_user(email='test2@sartography.com')
self.construct_participant(user=u1, relationship=Relationship.self_participant)
self.construct_participant(user=u2, relationship=Relationship.self_participant)
self.construct_participant(user=u1, relationship=Relationship.self_guardian)
self.construct_participant(user=u1, relationship=Relationship.dependent)
self.construct_participant(user=u2, relationship=Relationship.self_professional)
rv = self.app.get(
'/api/participant_admin_list',
content_type="application/json", headers=self.logged_in_headers())
self.assert_success(rv)
response = json.loads(rv.get_data(as_text=True))
self.assertEqual(2, response['num_self_participants'])
self.assertEqual(1, response['num_self_guardians'])
self.assertIsNotNone(response['num_dependents'])
self.assertIsNotNone(response['num_self_professionals'])
self.assertIsNotNone(response['all_participants'])
def test_participant_percent_complete(self):
u = self.construct_user()
p = self.construct_participant(user=u, relationship=Relationship.self_participant)
rv = self.app.get(
'/api/participant',
content_type="application/json", headers=self.logged_in_headers())
self.assert_success(rv)
response = json.loads(rv.get_data(as_text=True))
self.assertEqual(0, response[0]['percent_complete'])
iq = self.get_identification_questionnaire(p.id)
self.app.post('api/flow/self_intake/identification_questionnaire', data=self.jsonify(iq),
content_type="application/json",
follow_redirects=True, headers=self.logged_in_headers(u))
rv = self.app.get(
'/api/participant',
content_type="application/json", headers=self.logged_in_headers())
self.assert_success(rv)
response = json.loads(rv.get_data(as_text=True))
self.assertGreater(response[0]['percent_complete'], 0)
def test_participant_name(self):
p = self.construct_participant(user=self.construct_user(), relationship=Relationship.self_participant)
rv = self.app.get(
'/api/participant',
content_type="application/json", headers=self.logged_in_headers())
self.assert_success(rv)
response = json.loads(rv.get_data(as_text=True))
self.assertEqual(response[0]['name'], '')
self.construct_identification_questionnaire(first_name='Felicity', nickname="City", participant=p)
rv = self.app.get(
'/api/participant',
content_type="application/json", headers=self.logged_in_headers())
self.assert_success(rv)
response = json.loads(rv.get_data(as_text=True))
self.assertEqual(response[0]['name'], 'City')
def test_user_meta(self):
u = self.construct_user()
usermeta = UserMeta(id=u.id, interested=True)
db.session.add(usermeta)
db.session.commit()
rv = self.app.get('/api/user/%i/usermeta' % u.id,
follow_redirects=True,
content_type="application/json", headers=self.logged_in_headers())
self.assert_success(rv)
response = json.loads(rv.get_data(as_text=True))
self.assertEqual(response["interested"], True)
def test_create_user_meta(self):
u = self.construct_user()
um = {'id': u.id, 'professional': True}
rv = self.app.post('/api/user/%i/usermeta' % u.id, data=self.jsonify(um), content_type="application/json",
follow_redirects=True)
self.assertEqual(401, rv.status_code, "you can't create a participant without an account.")
rv = self.app.post('/api/user/%i/usermeta' % u.id, data=self.jsonify(um),
content_type="application/json", headers=self.logged_in_headers(u))
self.assert_success(rv)
response = json.loads(rv.get_data(as_text=True))
self.assertEqual(response["professional"], True)
self.assertIsNotNone(response['id'])
def test_post_self_has_guardian(self):
u = self.construct_user()
usermeta = UserMeta(id=u.id, self_participant=True, self_has_guardian=True)
db.session.add(usermeta)
db.session.commit()
rv = self.app.get('/api/user/%i/usermeta' % u.id,
follow_redirects=True,
content_type="application/json", headers=self.logged_in_headers())
self.assert_success(rv)
response = json.loads(rv.get_data(as_text=True))
self.assertEqual(response["self_has_guardian"], True)
rel = usermeta.get_relationship()
self.assertEqual(rel, None)
def test_post_no_metadata(self):
u = self.construct_user()
usermeta = UserMeta(id=u.id)
db.session.add(usermeta)
db.session.commit()
rv = self.app.get('/api/user/%i/usermeta' % u.id,
follow_redirects=True,
content_type="application/json", headers=self.logged_in_headers())
self.assert_success(rv)
response = json.loads(rv.get_data(as_text=True))
rel = usermeta.get_relationship()
self.assertEqual(rel, '')
def test_delete_user_meta(self):
u = self.construct_user()
usermeta = UserMeta(id=u.id, interested=True)
db.session.add(usermeta)
db.session.commit()
rv = self.app.get('/api/user/%i/usermeta' % u.id,
follow_redirects=True,
content_type="application/json")
self.assertEqual(401, rv.status_code)
rv = self.app.get('/api/user/%i/usermeta' % u.id,
follow_redirects=True,
content_type="application/json", headers=self.logged_in_headers())
self.assert_success(rv)
rv = self.app.delete('/api/user/%i/usermeta' % u.id, content_type="application/json")
self.assertEqual(401, rv.status_code)
rv = self.app.delete('/api/user/%i/usermeta' % u.id, content_type="application/json",
headers=self.logged_in_headers())
self.assert_success(rv)
rv = self.app.get('/api/user/%i/usermeta' % u.id,
follow_redirects=True,
content_type="application/json")
self.assertEqual(401, rv.status_code)
rv = self.app.get('/api/user/%i/usermeta' % u.id,
follow_redirects=True,
content_type="application/json", headers=self.logged_in_headers())
self.assertEqual(404, rv.status_code)
def test_user_meta_serialize(self):
u = self.construct_user()
usermeta = UserMeta(id=u.id, interested=True)
result = usermeta.get_relationship()
self.assertEqual(result, "self_interested")
UserMetaSchema().dump(usermeta)

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,331 @@
import unittest
from flask import json
from tests.base_test import BaseTest
from app import db, elastic_index
from app.model.resource import Resource
from app.model.resource_category import ResourceCategory
from app.model.resource_change_log import ResourceChangeLog
from app.model.user import Role
class TestResources(BaseTest, unittest.TestCase):
def test_resource_basics(self):
self.construct_resource()
r = db.session.query(Resource).first()
self.assertIsNotNone(r)
r_id = r.id
rv = self.app.get('/api/resource/%i' % r_id,
follow_redirects=True,
content_type="application/json")
self.assert_success(rv)
response = json.loads(rv.get_data(as_text=True))
self.assertEqual(response["id"], r_id)
self.assertEqual(response["title"], 'A+ Resource')
self.assertEqual(response["description"], 'A delightful Resource destined to create rejoicing')
def test_modify_resource_basics(self):
self.construct_resource()
r = db.session.query(Resource).first()
self.assertIsNotNone(r)
r_id = r.id
rv = self.app.get('/api/resource/%i' % r_id, content_type="application/json")
response = json.loads(rv.get_data(as_text=True))
response['title'] = 'Edwarardos Lemonade and Oil Change'
response['description'] = 'Better fluids for you and your car.'
response['website'] = 'http://sartography.com'
orig_date = response['last_updated']
rv = self.app.put('/api/resource/%i' % r_id, data=self.jsonify(response), content_type="application/json",
follow_redirects=True, headers=self.logged_in_headers())
self.assert_success(rv)
rv = self.app.get('/api/resource/%i' % r_id, content_type="application/json")
self.assert_success(rv)
response = json.loads(rv.get_data(as_text=True))
self.assertEqual(response['title'], 'Edwarardos Lemonade and Oil Change')
self.assertEqual(response['description'], 'Better fluids for you and your car.')
self.assertEqual(response['website'], 'http://sartography.com')
self.assertNotEqual(orig_date, response['last_updated'])
def test_delete_resource(self):
r = self.construct_resource()
r_id = r.id
rv = self.app.get('api/resource/%i' % r_id, content_type="application/json")
self.assert_success(rv)
rv = self.app.delete('api/resource/%i' % r_id, content_type="application/json", headers=self.logged_in_headers())
self.assert_success(rv)
rv = self.app.get('api/resource/%i' % r_id, content_type="application/json")
self.assertEqual(404, rv.status_code)
def test_delete_resource_with_admin_note_and_no_elastic_record(self):
r = self.construct_resource()
r_id = r.id
rv = self.app.get('api/resource/%i' % r_id, content_type="application/json")
self.assert_success(rv)
self.construct_admin_note(user=self.construct_user(), resource=r)
elastic_index.remove_document(r, 'Resource')
rv = self.app.delete('api/resource/%i' % r_id, content_type="application/json", headers=self.logged_in_headers())
self.assert_success(rv)
rv = self.app.get('api/resource/%i' % r_id, content_type="application/json")
self.assertEqual(404, rv.status_code)
def test_create_resource(self):
resource = {'title': "Resource of Resources", 'description': "You need this resource in your life.",
'organization_name': "Resource Org"}
rv = self.app.post('api/resource', data=self.jsonify(resource), content_type="application/json",
follow_redirects=True, headers=self.logged_in_headers())
self.assert_success(rv)
response = json.loads(rv.get_data(as_text=True))
self.assertEqual(response['title'], 'Resource of Resources')
self.assertEqual(response['description'], 'You need this resource in your life.')
self.assertIsNotNone(response['id'])
def test_get_resource_by_category(self):
c = self.construct_category()
r = self.construct_resource()
cr = ResourceCategory(resource=r, category=c, type='resource')
db.session.add(cr)
db.session.commit()
rv = self.app.get(
'/api/category/%i/resource' % c.id,
content_type="application/json",
headers=self.logged_in_headers())
self.assert_success(rv)
response = json.loads(rv.get_data(as_text=True))
self.assertEqual(1, len(response))
self.assertEqual(r.id, response[0]["resource_id"])
self.assertEqual(r.description, response[0]["resource"]["description"])
def test_get_resource_by_category_includes_category_details(self):
c = self.construct_category(name="c1")
c2 = self.construct_category(name="c2")
r = self.construct_resource()
cr = ResourceCategory(resource=r, category=c, type='resource')
cr2 = ResourceCategory(resource=r, category=c2, type='resource')
db.session.add_all([cr, cr2])
db.session.commit()
rv = self.app.get(
'/api/category/%i/resource' % c.id,
content_type="application/json",
headers=self.logged_in_headers())
self.assert_success(rv)
response = json.loads(rv.get_data(as_text=True))
self.assertEqual(r.id, response[0]["resource_id"])
self.assertEqual(2,
len(response[0]["resource"]["resource_categories"]))
self.assertEqual(
"c1", response[0]["resource"]["resource_categories"][0]["category"]
["name"])
def test_category_resource_count(self):
c = self.construct_category()
r = self.construct_resource()
cr = ResourceCategory(resource=r, category=c, type='resource')
db.session.add(cr)
db.session.commit()
rv = self.app.get(
'/api/category/%i' % c.id, content_type="application/json")
self.assert_success(rv)
response = json.loads(rv.get_data(as_text=True))
self.assertEqual(1, response["resource_count"])
def test_get_category_by_resource(self):
c = self.construct_category()
r = self.construct_resource()
cr = ResourceCategory(resource=r, category=c, type='resource')
db.session.add(cr)
db.session.commit()
rv = self.app.get(
'/api/resource/%i/category' % r.id,
content_type="application/json")
self.assert_success(rv)
response = json.loads(rv.get_data(as_text=True))
self.assertEqual(1, len(response))
self.assertEqual(c.id, response[0]["id"])
self.assertEqual(c.name, response[0]["category"]["name"])
def test_add_category_to_resource(self):
c = self.construct_category()
r = self.construct_resource()
rc_data = {"resource_id": r.id, "category_id": c.id}
rv = self.app.post(
'/api/resource_category',
data=self.jsonify(rc_data),
content_type="application/json")
self.assert_success(rv)
response = json.loads(rv.get_data(as_text=True))
self.assertEqual(c.id, response["category_id"])
self.assertEqual(r.id, response["resource_id"])
def test_set_all_categories_on_resource(self):
c1 = self.construct_category(name="c1")
c2 = self.construct_category(name="c2")
c3 = self.construct_category(name="c3")
r = self.construct_resource()
rc_data = [
{
"category_id": c1.id
},
{
"category_id": c2.id
},
{
"category_id": c3.id
},
]
rv = self.app.post(
'/api/resource/%i/category' % r.id,
data=self.jsonify(rc_data),
content_type="application/json")
self.assert_success(rv)
response = json.loads(rv.get_data(as_text=True))
self.assertEqual(3, len(response))
rc_data = [{"category_id": c1.id}]
rv = self.app.post(
'/api/resource/%i/category' % r.id,
data=self.jsonify(rc_data),
content_type="application/json")
self.assert_success(rv)
response = json.loads(rv.get_data(as_text=True))
self.assertEqual(1, len(response))
def test_remove_category_from_resource(self):
self.test_add_category_to_resource()
rv = self.app.delete('/api/resource_category/%i' % 1)
self.assert_success(rv)
rv = self.app.get(
'/api/resource/%i/category' % 1, content_type="application/json")
self.assert_success(rv)
response = json.loads(rv.get_data(as_text=True))
self.assertEqual(0, len(response))
def test_resource_change_log_types(self):
u = self.construct_user(email="editor@sartorgraphy.com", role=Role.admin)
r = {'id': 258, 'title': "A Resource that is Super and Great", 'description': "You need this resource in your life."}
rv = self.app.post('api/resource', data=self.jsonify(r), content_type="application/json",
follow_redirects=True, headers=self.logged_in_headers())
self.assert_success(rv)
logs = ResourceChangeLog.query.all()
self.assertIsNotNone(logs[-1].resource_id)
self.assertIsNotNone(logs[-1].user_id)
self.assertEqual(logs[-1].type, 'create')
rv = self.app.get('api/resource/%i' % r['id'], content_type="application/json")
self.assert_success(rv)
response = json.loads(rv.get_data(as_text=True))
response['title'] = 'Super Great Resource'
rv = self.app.put('/api/resource/%i' % r['id'], data=self.jsonify(response), content_type="application/json",
follow_redirects=True, headers=self.logged_in_headers(user=u))
self.assert_success(rv)
rv = self.app.get('/api/resource/%i' % r['id'], content_type="application/json")
self.assert_success(rv)
response = json.loads(rv.get_data(as_text=True))
self.assertEqual(response['title'], 'Super Great Resource')
logs = ResourceChangeLog.query.all()
self.assertIsNotNone(logs[-1].resource_id)
self.assertIsNotNone(logs[-1].user_id)
self.assertEqual(logs[-1].type, 'edit')
rv = self.app.delete('api/resource/%i' % r['id'], content_type="application/json",
headers=self.logged_in_headers())
self.assert_success(rv)
logs = ResourceChangeLog.query.all()
self.assertIsNotNone(logs[-1].resource_id)
self.assertIsNotNone(logs[-1].user_id)
self.assertEqual(logs[-1].type, 'delete')
def test_get_resource_change_log_by_resource(self):
r = self.construct_resource()
u = self.construct_user(email="editor@sartorgraphy.com", role=Role.admin)
rv = self.app.get('api/resource/%i' % r.id, content_type="application/json")
self.assert_success(rv)
response = json.loads(rv.get_data(as_text=True))
response['title'] = 'Super Great Resource'
rv = self.app.put('/api/resource/%i' % r.id, data=self.jsonify(response), content_type="application/json",
follow_redirects=True, headers=self.logged_in_headers(user=u))
self.assert_success(rv)
rv = self.app.get('/api/resource/%i/change_log' % r.id, content_type="application/json", headers=self.logged_in_headers())
self.assert_success(rv)
response = json.loads(rv.get_data(as_text=True))
self.assertEqual(response[-1]['user_id'], u.id)
def test_get_resource_change_log_by_user(self):
r = self.construct_resource()
u = self.construct_user(email="editor@sartorgraphy.com", role=Role.admin)
rv = self.app.get('api/resource/%i' % r.id, content_type="application/json")
self.assert_success(rv)
response = json.loads(rv.get_data(as_text=True))
response['title'] = 'Super Great Resource'
rv = self.app.put('/api/resource/%i' % r.id, data=self.jsonify(response), content_type="application/json",
follow_redirects=True, headers=self.logged_in_headers(user=u))
self.assert_success(rv)
rv = self.app.get('/api/user/%i/resource_change_log' % u.id, content_type="application/json", headers=self.logged_in_headers())
self.assert_success(rv)
response = json.loads(rv.get_data(as_text=True))
self.assertEqual(response[-1]['resource_id'], r.id)
def test_covid19_resource_lists(self):
self.construct_resource(covid19_categories=['COVID-19_for_Autism', 'Free_educational_resources'])
self.construct_resource(covid19_categories=['COVID-19_for_Autism', 'Edu-tainment', 'Free_educational_resources'])
self.construct_resource(covid19_categories=['COVID-19_for_Autism', 'Edu-tainment', 'Supports_with_Living'])
self.construct_resource(covid19_categories=['COVID-19_for_Autism', 'Edu-tainment', 'Visual_Aids'])
self.construct_resource(covid19_categories=['COVID-19_for_Autism', 'Edu-tainment', 'Health_and_Telehealth'])
rv = self.app.get('api/resource/covid19/COVID-19_for_Autism', content_type="application/json")
self.assert_success(rv)
response = json.loads(rv.get_data(as_text=True))
self.assertEqual(len(response), 5)
rv = self.app.get('api/resource/covid19/Edu-tainment', content_type="application/json")
self.assert_success(rv)
response = json.loads(rv.get_data(as_text=True))
self.assertEqual(len(response), 4)
rv = self.app.get('api/resource/covid19/Free_educational_resources', content_type="application/json")
self.assert_success(rv)
response = json.loads(rv.get_data(as_text=True))
self.assertEqual(len(response), 2)
rv = self.app.get('api/resource/covid19/Supports_with_Living', content_type="application/json")
self.assert_success(rv)
response = json.loads(rv.get_data(as_text=True))
self.assertEqual(len(response), 1)
rv = self.app.get('api/resource/covid19/Visual_Aids', content_type="application/json")
self.assert_success(rv)
response = json.loads(rv.get_data(as_text=True))
self.assertEqual(len(response), 1)
rv = self.app.get('api/resource/covid19/Health_and_Telehealth', content_type="application/json")
self.assert_success(rv)
response = json.loads(rv.get_data(as_text=True))
self.assertEqual(len(response), 1)
def test_is_uva_education_content(self):
self.construct_resource(is_draft=True, title='Autism at UVA', is_uva_education_content=True)
self.construct_resource(is_draft=False, title='Healthy Eating', is_uva_education_content=True)
self.construct_resource(is_draft=True, title='Autism and the Arts', is_uva_education_content=False)
self.construct_resource(is_draft=False, title='Autism One', is_uva_education_content=True)
self.construct_resource(is_draft=False, title='Two', is_uva_education_content=False)
rv = self.app.get('api/resource/education', content_type="application/json")
self.assert_success(rv)
response = json.loads(rv.get_data(as_text=True))
self.assertEqual(len(response), 2)
rv = self.app.get('api/resource', content_type="application/json")
self.assert_success(rv)
response = json.loads(rv.get_data(as_text=True))
self.assertEqual(len(response), 5)

View File

@ -0,0 +1,729 @@
import unittest
import json
import dateutil.parser
from datetime import datetime, timedelta
from tests.base_test import BaseTest
from app import elastic_index, db
from app.model.category import Category
from app.model.role import Role
class TestSearch(BaseTest, unittest.TestCase):
def search(self, query, user=None, path=''):
"""Executes a query as the given user, returning the resulting search results object."""
rv = self.app.post(
'/api/search' + path,
data=self.jsonify(query),
follow_redirects=True,
content_type="application/json",
headers=self.logged_in_headers(user))
self.assert_success(rv)
return json.loads(rv.get_data(as_text=True))
def search_resources(self, query, user=None):
return self.search(query, user, '/resources')
def search_studies(self, query, user=None):
return self.search(query, user, '/studies')
def search_anonymous(self, query):
"""Executes a query as an anonymous user, returning the resulting search results object."""
rv = self.app.post(
'/api/search',
data=self.jsonify(query),
follow_redirects=True,
content_type="application/json")
self.assert_success(rv)
return json.loads(rv.get_data(as_text=True))
def test_config(self):
self.assertEqual(elastic_index.index_name, "stardrive_test_resources",
msg="Something is wrong with you configuration or import order. " +
"You are not working with the test index. Make sure the " +
"first thing you import in this file is base_test.")
def test_search_has_counts_by_type(self):
basic_query = {'words': ''}
search_results = self.search(basic_query)
self.assertEqual(0, len(search_results['hits']))
# test that elastic resource is created with post
res = self.construct_resource(title="space unicorn", description="delivering rainbows")
res2 = self.construct_location(title="space unicorn Palace", description="come buy unicorn poop here.")
rv = self.app.get('api/resource/%i' % res.id, content_type="application/json", follow_redirects=True)
self.assert_success(rv)
search_results = self.search(basic_query)
self.assertEqual(2, len(search_results['hits']))
locations = next(x for x in search_results['type_counts'] if x['value'] == "location")
resources = next(x for x in search_results['type_counts'] if x['value'] == "resource")
self.assertEqual(1, locations["count"])
self.assertEqual(1, resources["count"])
def test_search_has_counts_by_age_range(self):
basic_query = {'words': ''}
search_results = self.search(basic_query)
self.assertEqual(0, len(search_results['hits']))
# test that elastic resource is created with post
res = self.construct_resource(title="Bart", description="free lovin young fella", ages=['young folks'])
res2 = self.construct_resource(title="Abe", description="delightful grandpop.", ages=['old folks'])
rv = self.app.get('api/resource/%i' % res.id, content_type="application/json", follow_redirects=True)
self.assert_success(rv)
search_results = self.search(basic_query)
self.assertEqual(2, len(search_results['hits']))
young_folks = next(x for x in search_results['age_counts'] if x['value'] == "young folks")
old_folks = next(x for x in search_results['age_counts'] if x['value'] == "old folks")
self.assertEqual(1, young_folks["count"])
self.assertEqual(1, old_folks["count"])
def test_search_has_counts_by_languages(self):
basic_query = {'words': ''}
search_results = self.search(basic_query)
self.assertEqual(0, len(search_results['hits']))
# test that elastic resource is created with post
res = self.construct_resource(title="Bart", description="free lovin young fella", languages=['english', 'spanish'])
res2 = self.construct_resource(title="Abe", description="delightful grandpop.", languages=['english', 'tagalog'])
rv = self.app.get('api/resource/%i' % res.id, content_type="application/json", follow_redirects=True)
self.assert_success(rv)
rv = self.app.get('api/resource/%i' % res2.id, content_type="application/json", follow_redirects=True)
self.assert_success(rv)
search_results = self.search(basic_query)
self.assertEqual(2, len(search_results['hits']))
english = next(x for x in search_results['language_counts'] if x['value'] == "english")
spanish = next(x for x in search_results['language_counts'] if x['value'] == "spanish")
tagalog = next(x for x in search_results['language_counts'] if x['value'] == "tagalog")
self.assertEqual(2, english["count"])
self.assertEqual(1, spanish["count"])
self.assertEqual(1, tagalog["count"])
def test_study_search_basics(self):
umbrella_query = {'words': 'umbrellas'}
universe_query = {'words': 'universe'}
search_results = self.search(umbrella_query)
self.assertEqual(0, len(search_results['hits']))
search_results = self.search(universe_query)
self.assertEqual(0, len(search_results['hits']))
# test that elastic resource is created with post
study = {'title': "space platypus", 'description': "delivering umbrellas", 'organization_name': "Study Org"}
rv = self.app.post('api/study', data=self.jsonify(study), content_type="application/json",
follow_redirects=True)
self.assert_success(rv)
response = json.loads(rv.get_data(as_text=True))
search_results = self.search(umbrella_query)
self.assertEqual(1, len(search_results['hits']))
self.assertEqual(search_results['hits'][0]['id'], response['id'])
search_results = self.search(universe_query)
self.assertEqual(0, len(search_results['hits']))
def test_search_basics(self):
rainbow_query = {'words': 'rainbows'}
world_query = {'words': 'world'}
search_results = self.search(rainbow_query)
self.assertEqual(0, len(search_results['hits']))
search_results = self.search(world_query)
self.assertEqual(0, len(search_results['hits']))
# test that elastic resource is created with post
resource = {'title': "space unicorn", 'description': "delivering rainbows", 'organization_name': "Resource Org"}
rv = self.app.post('api/resource', data=self.jsonify(resource), content_type="application/json",
follow_redirects=True, headers=self.logged_in_headers())
self.assert_success(rv)
response = json.loads(rv.get_data(as_text=True))
search_results = self.search(rainbow_query)
self.assertEqual(1, len(search_results['hits']))
self.assertEqual(search_results['hits'][0]['id'], response['id'])
self.assertEqual(search_results['hits'][0]['title'], response['title'])
self.assertEqual(search_results['hits'][0]['type'], "resource")
self.assertEqual(search_results['hits'][0]['highlights'], "space unicorn delivering <em>rainbows</em>")
self.assertIsNotNone(search_results['hits'][0]['last_updated'])
search_results = self.search(world_query)
self.assertEqual(0, len(search_results['hits']))
def test_search_location_by_geo_point(self):
geo_query = {
'words': 'rainbows',
'sort': {
'field': 'geo_point',
'latitude': 38.065229,
'longitude': -79.079076,
'order': 'asc',
'unit': 'mi'
}
}
# Add a location within the distance filter
location_near = self.construct_location(title='local unicorn',
description="delivering rainbows within the orbit of Uranus",
latitude=38.149595, longitude=-79.072557)
# Add a location beyond the distance filter
location_far = self.construct_location(title='distant unicorn',
description="delivering rainbows to the greater Trans-Neptunian Region",
latitude=-38.149595, longitude=100.927443)
# Add a location somewhere in between
location_mid = self.construct_location(title='middle unicorn',
description="delivering rainbows somewhere in between",
latitude=37.5246403, longitude=-77.5633015)
search_results = self.search(geo_query)
self.assertEqual(3, len(search_results['hits']))
self.assertIsNotNone(search_results['hits'][0]['latitude'])
self.assertIsNotNone(search_results['hits'][0]['longitude'])
self.assertEqual(search_results['hits'][0]['title'], location_near.title)
self.assertEqual(search_results['hits'][1]['title'], location_mid.title)
self.assertEqual(search_results['hits'][2]['title'], location_far.title)
# Reverse the sort order
geo_query['sort']['order'] = 'desc'
search_results = self.search(geo_query)
self.assertEqual(3, len(search_results['hits']))
self.assertIsNotNone(search_results['hits'][0]['latitude'])
self.assertIsNotNone(search_results['hits'][0]['longitude'])
self.assertEqual(search_results['hits'][0]['title'], location_far.title)
self.assertEqual(search_results['hits'][1]['title'], location_mid.title)
self.assertEqual(search_results['hits'][2]['title'], location_near.title)
# Change which point is closest
geo_query['sort']['latitude'] = location_mid.latitude
geo_query['sort']['longitude'] = location_mid.longitude
geo_query['sort']['order'] = 'asc'
search_results = self.search(geo_query)
self.assertEqual(3, len(search_results['hits']))
self.assertIsNotNone(search_results['hits'][0]['latitude'])
self.assertIsNotNone(search_results['hits'][0]['longitude'])
self.assertEqual(search_results['hits'][0]['title'], location_mid.title)
self.assertEqual(search_results['hits'][1]['title'], location_near.title)
self.assertEqual(search_results['hits'][2]['title'], location_far.title)
def test_geo_box_query(self):
location_near = self.construct_location(title='local unicorn',
description="delivering rainbows within the orbit of Uranus",
latitude=38.149595, longitude=-93)
location_not_near = self.construct_location(title='local',
description="delivering rainbows",
latitude=28.149595, longitude=-93)
geo_query = {
'geo_box': {
'top_left': {"lat": 39, "lon": -94},
'bottom_right': {"lat": 38, "lon": -92},
}
}
search_results = self.search(geo_query)
self.assertEqual(1, len(search_results['hits']))
def test_modify_resource_search_basics(self):
rainbow_query = {'words': 'rainbows'}
world_query = {'words': 'world'}
# create the resource
resource = self.construct_resource(
title='space unicorn', description="delivering rainbows")
# test that it indeed exists
rv = self.app.get('/api/resource/%i' % resource.id, content_type="application/json",
follow_redirects=True)
self.assert_success(rv)
search_results = self.search(rainbow_query)
self.assertEqual(1, len(search_results['hits']))
self.assertEqual(search_results['hits'][0]['id'], resource.id)
response = json.loads(rv.get_data(as_text=True))
response['description'] = 'all around the world'
rv = self.app.put('/api/resource/%i' % resource.id, data=self.jsonify(response), content_type="application/json",
follow_redirects=True, headers=self.logged_in_headers())
self.assert_success(rv)
search_results = self.search(rainbow_query)
self.assertEqual(0, len(search_results['hits']))
search_results = self.search(world_query)
self.assertEqual(1, len(search_results['hits']))
self.assertEqual(resource.id, search_results['hits'][0]['id'])
def test_delete_search_item(self):
rainbow_query = {'words': 'rainbows'}
resource = self.construct_resource(
title='space unicorn', description="delivering rainbows")
search_results = self.search(rainbow_query)
self.assertEqual(1, len(search_results['hits']))
elastic_index.remove_document(resource, 'Resource')
search_results = self.search(rainbow_query)
self.assertEqual(0, len(search_results['hits']))
def test_filter_resources_returns_resources_and_past_events(self):
rainbow_query = {'types': ['resource']}
r = self.construct_resource(title='space unicorn online resource',
description="Electronically-delivered rainbows through the internets")
l = self.construct_location(title='space unicorn main office',
description="Where rainbows are manufactured for galactic distribution")
future_date = datetime.utcnow() + timedelta(days=7)
future_event = self.construct_event(title='space unicorn workshop',
description="Learn how to deliver sparkling rainbows in this interactive workshop",
date=future_date)
past_date = datetime.utcnow() + timedelta(days=-7)
past_event = self.construct_event(title='space rainbows webinar',
description="Future practical tips on how to generate interplanetary sparkly colors",
post_event_description="Past practical tips on how to generate interplanetary sparkly colors",
date=past_date)
s = self.construct_study(title='space unicorn research study',
description="Investigating the long-term outcomes of interstellar unicorn-based delivery of rainbows")
search_results = self.search_resources(rainbow_query)
self.assertEqual(2, len(search_results['hits']), 'should only return 2 results')
expected_types = ['resource', 'event']
not_expected_types = ['study', 'location']
self.check_search_results(search_results, expected_types, not_expected_types)
def test_search_resources_returns_only_resources(self):
rainbow_query = {'words': 'rainbows'}
r = self.construct_resource(title='space unicorn online resource',
description="Electronically-delivered rainbows through the internets")
l = self.construct_location(title='space unicorn main office',
description="Where rainbows are manufactured for galactic distribution")
e = self.construct_event(title='space unicorn workshop',
description="Learn how to deliver sparkling rainbows in this interactive workshop")
s = self.construct_study(title='space unicorn research study',
description="Investigating the long-term outcomes of interstellar unicorn-based delivery of rainbows")
search_results = self.search_resources(rainbow_query)
self.assertEqual(3, len(search_results['hits']), 'should only return 3 results')
expected_types = ['resource', 'location', 'event']
not_expected_types = ['study']
self.check_search_results(search_results, expected_types, not_expected_types)
def test_search_studies_returns_only_studies(self):
rainbow_query = {'words': 'rainbows'}
r = self.construct_resource(title='space unicorn online resource',
description="Electronically-delivered rainbows through the internets")
l = self.construct_location(title='space unicorn main office',
description="Where rainbows are manufactured for galactic distribution")
e = self.construct_event(title='space unicorn workshop',
description="Learn how to deliver sparkling rainbows in this interactive workshop")
s = self.construct_study(title='space unicorn research study',
description="Investigating the long-term outcomes of interstellar unicorn-based delivery of rainbows")
search_results = self.search_studies(rainbow_query)
self.assertEqual(1, len(search_results['hits']), 'should only return 1 result')
expected_types = ['study']
not_expected_types = ['resource', 'location', 'event']
self.check_search_results(search_results, expected_types, not_expected_types)
def check_search_results(self, search_results, expected_types, not_expected_types):
for hit in search_results['hits']:
self.assertIn(hit['type'], expected_types)
self.assertNotIn(hit['type'], not_expected_types)
for expected in expected_types:
value = next(x for x in search_results['type_counts'] if x['value'] == expected)
self.assertTrue(value['count'] > 0)
for expected in not_expected_types:
for value in search_results['type_counts']:
if value['value'] == expected:
self.assertTrue(value['count'] == 0)
def setup_category_aggregations(self):
# There are two types of people in this world.
makers = self.construct_category(name="Makers")
talkers = self.construct_category(name="Talkers")
maker_wood = self.construct_category(name="Woodworkers", parent=makers)
maker_pot = self.construct_category(name="Potters", parent=makers)
talker_dreamer = self.construct_category(name="Dreamers", parent=talkers)
talker_yaya = self.construct_category(name="Le-Me-Tell-Ya-Somethins", parent=talkers)
maker_wood_cabinet = self.construct_category(name="Cabinet Makers", parent=maker_wood)
rick = self.construct_resource(title="Rick Hubbard", description="Cabinet maker extrordinair",
categories=[maker_wood_cabinet])
meghan = self.construct_resource(title="Meghan Williamson", description="A darn good potter",
categories=[maker_pot])
jon_yap = self.construct_resource(title="Jonny Yapper", description="He shows up to lots of meetins",
categories=[talker_yaya])
joe_dream_do = self.construct_resource(title="Joe Dream Maker", description="He does it all.",
categories=[makers, talker_dreamer])
def test_top_level_category_counts(self):
self.setup_category_aggregations()
type_query = {'words': ''}
search_results = self.search(type_query)
self.assertEqual(4, len(search_results['hits']))
self.assertIsNotNone(search_results['category'], msg="A category should always exist")
self.assertEqual("Topics", search_results['category']['name'],
msg="It is a top level category if no filters are applied")
self.assertEqual(2, len(search_results['category']['children']),
msg="The two true Top level categories are returned as children")
talker_cat = search_results['category']['children'][0]
maker_cat = search_results['category']['children'][1]
self.assertEqual("Talkers", talker_cat['name'], "The first category returned is 'talkers'")
self.assertEqual("Makers", maker_cat['name'], "The second category returned is 'makers'")
self.assertEqual(3, maker_cat['hit_count'], "There are three makers present.")
def test_top_level_category_repost_does_not_create_error(self):
self.setup_category_aggregations()
type_query = {'words': ''}
search_results = self.search(type_query)
self.search(search_results) # This should not create an error.
def test_second_level_filtered_category_counts(self):
self.setup_category_aggregations()
maker_cat = db.session.query(Category).filter(Category.name == 'Makers').first()
type_query = {'words': '', 'category': {'id': maker_cat.id}}
search_results = self.search(type_query)
self.assertEqual(3, len(search_results['hits']))
self.assertIsNotNone(search_results['category'], msg="A category should always exist")
self.assertEqual("Makers", search_results['category']['name'],
msg="Selected Category Id should be the category returned")
self.assertEqual(2, len(search_results['category']['children']),
msg="The two true Top level categories are returned as children")
children = search_results['category']['children']
self.assertEqual(1, len(list(filter(lambda cat: cat['name'] == 'Woodworkers', children))))
self.assertEqual(1, len(list(filter(lambda cat: cat['name'] == 'Potters', children))))
woodworkers = next(x for x in children if x['name'] == "Woodworkers")
self.assertEqual(1, woodworkers['hit_count'], "There is one wood worker.")
def test_third_level_filtered_category_counts(self):
self.setup_category_aggregations()
maker_wood_cat = db.session.query(Category).filter(Category.name == 'Woodworkers').first()
type_query = {'words': '', 'category': {'id': maker_wood_cat.id}}
search_results = self.search(type_query)
self.assertEqual(1, len(search_results['hits']))
self.assertIsNotNone(search_results['category'], msg="A category should always exist")
self.assertEqual("Woodworkers", search_results['category']['name'],
msg="Selected Category Id should be the category returned")
self.assertEqual(1, len(search_results['category']['children']), msg="Woodworkers has only one child")
cabinet_maker = search_results['category']['children'][0]
self.assertEqual(1, cabinet_maker['hit_count'], "There is one cabinet maker.")
def test_that_top_level_category_is_always_present(self):
self.setup_category_aggregations()
maker_wood_cat = db.session.query(Category).filter(Category.name == 'Woodworkers').first()
query = {'words': '', 'category': {'id': maker_wood_cat.id}}
search_results = self.search(query)
self.assertEqual("Makers", search_results['category']['parent']['name'])
self.assertEqual("Topics", search_results['category']['parent']['parent']['name'])
def test_find_related_resource(self):
# You have to build a lot of documents for this to start working .... And I liked 1985.
movie = self.construct_category("Movies Vaguely Remembered by Middle-Aged Gen Xers")
sausage = self.construct_category("Sausages of the World")
sweets = self.construct_category("Delicious Treats Full of Sugar")
breakfast_club = self.construct_resource(title="The Breakfast Club",
description="A 1985 American comedy-drama film written, produced, and "
"directed by John Hughes. Teenagers from different high "
"school cliques who spend a Saturday in detention with "
"their authoritarian assistant principal",
categories=[movie])
back_to_the_future = self.construct_resource(title="Back to the Future",
description="1985 American comedy science fiction film directed by"
" Robert Zemeckisteenager. Marty McFly, who "
"accidentally travels back in time from 1985 to 1955, "
"where he meets his future parents and becomes his "
"mother's romantic interest.",
categories=[movie])
goonies = self.construct_resource(title="The Goonies",
description="a 1985 American adventure comedy film co-produced"
" and directed by Richard Donner from a screenplay "
"by Chris Columbus, based on a story by executive "
"producer Steven Spielberg. In the film, a band of"
" kids who live in the \"Goon Docks\" neighborhood",
categories=[movie])
weird_science = self.construct_resource(title="Weird Science",
description="a 1985 American teen comic science fiction film "
"written and directed by John Hughes and starring "
"Anthony Michael Hall, Ilan Mitchell-Smith and "
"Kelly LeBrock.",
categories=[movie])
cocoon = self.construct_resource(title="Cocoon",
description="a 1985 American science-fiction fantasy comedy-drama "
"film directed by Ron Howard about a group of elderly "
"people rejuvenated by aliens",
categories=[movie])
study = self.construct_study(title="Narrative analysis of 1985 movies vaguely remembered by Middle-Aged Gen Xers",
description="If you remember Marty McFly, who accidentally traveled back in time "
"from 1985 to 1955, or have watched every John Hughes movie starring "
"Anthony Michael Hall, feel like you lived in the Goon Docks with "
"the Goonies, you might be interested in this study.",
participant_description="We're looking for elderly people rejuvenated by aliens, "
"teens conducting weird science experiments, and parents "
"who have gone back to the future to spend detention "
"with their authoritarian assistant principal.",
benefit_description="Participants will receive a cocoon breakfast screenplay "
"treatment, delivered by a club of band kids from an 1985 "
"American teen comic science fiction film format.",
categories=[movie])
# Add a bunch of other irrelevant stuff
for i in range(10):
self.construct_resource(title="Andouillette %d" % i,
description="Chorizo Bratwurst Hot Dog Sausage Andouillette Chorizo Bratwurst Hot "
"Dog Sausage Andouillette Chorizo Bratwurst Hot Dog Sausage "
"Andouillette Chorizo Bratwurst Hot Dog Sausage Andouillette Chorizo "
"Bratwurst Hot Dog Sausage Andouillette Chorizo Bratwurst Hot Dog "
"Sausage Andouillette Chorizo Bratwurst Hot Dog Sausage Andouillette "
"Chorizo Bratwurst Hot Dog Sausage Andouillette Chorizo Bratwurst Hot "
"Dog Sausage Andouillette Chorizo Bratwurst Hot Dog Sausage "
"Andouillette %d" % i,
categories=[sausage])
self.construct_resource(title="Snickers Candy Bar %d" % i,
description="Jaw Breakers Brach's Royals Necco Wafers Snickers Candy Bar Jaw "
"Breakers Brach's Royals Necco Wafers Snickers Candy Bar Jaw Breakers "
"Brach's Royals Necco Wafers Snickers Candy Bar Jaw Breakers Brach's "
"Royals Necco Wafers Snickers Candy Bar Jaw Breakers Brach's Royals "
"Necco Wafers Snickers Candy Bar Jaw Breakers Brach's Royals Necco "
"Wafers Snickers Candy Bar Jaw Breakers Brach's Royals Necco Wafers "
"Snickers Candy Bar Jaw Breakers Brach's Royals Necco Wafers Snickers "
"Candy Bar Jaw Breakers Brach's Royals Necco Wafers Snickers Candy "
"Bar %d" % i,
categories=[sweets])
self.construct_study(title="The correlation between sausage and beer consumption %d" % i,
description="Chorizo Bratwurst Hot Dog Sausage Andouillette Chorizo Bratwurst Hot "
"Dog Sausage Andouillette Chorizo Bratwurst Hot Dog Sausage Andouillette "
"Chorizo Bratwurst Hot Dog Sausage Andouillette Chorizo Bratwurst Hot "
"Dog Sausage Andouillette Chorizo Bratwurst Hot Dog Sausage Andouillette "
"Chorizo Bratwurst Hot Dog Sausage Andouillette Chorizo Bratwurst Hot "
"Dog Sausage Andouillette Chorizo Bratwurst Hot Dog Sausage Andouillette "
"Chorizo Bratwurst Hot Dog Sausage Andouillette %d" % i,
categories=[sausage])
self.construct_study(title="Ethnographic study of sugar as a construction material %d" % i,
description="Jaw Breakers Brach's Royals Necco Wafers Snickers Candy Bar Jaw "
"Breakers Brach's Royals Necco Wafers Snickers Candy Bar Jaw Breakers "
"Brach's Royals Necco Wafers Snickers Candy Bar Jaw Breakers Brach's "
"Royals Necco Wafers Snickers Candy Bar Jaw Breakers Brach's Royals "
"Necco Wafers Snickers Candy Bar Jaw Breakers Brach's Royals Necco "
"Wafers Snickers Candy Bar Jaw Breakers Brach's Royals Necco Wafers "
"Snickers Candy Bar Jaw Breakers Brach's Royals Necco Wafers Snickers "
"Candy Bar Jaw Breakers Brach's Royals Necco Wafers Snickers Candy "
"Bar %d" % i,
categories=[sweets])
rv = self.app.post('/api/related', data=self.jsonify({'resource_id': breakfast_club.id}), content_type="application/json")
self.assert_success(rv)
response = json.loads(rv.get_data(as_text=True))
self.assertTrue('resources' in response.keys())
self.assertGreater(len(response['resources']), 0)
self.assertTrue('studies' in response.keys())
self.assertGreater(len(response['studies']), 0)
# Most relevant result should be another movie, not sausage or candy
self.assertIn(response['resources'][0]['title'], [
back_to_the_future.title,
goonies.title,
weird_science.title,
cocoon.title
])
self.assertIn(study.title, list(map(lambda s: s['title'], response['studies'])))
def test_search_paginates(self):
self.construct_location(title="one")
self.construct_location(title="two")
self.construct_location(title="three")
query = {'start': 0, 'size': 1}
search_results = self.search(query)
self.assertEqual(1, len(search_results['hits']))
title1 = search_results['hits'][0]['title']
query = {'start': 1, 'size': 1}
search_results = self.search(query)
self.assertEqual(1, len(search_results['hits']))
title2 = search_results['hits'][0]['title']
query = {'start': 2, 'size': 1}
search_results = self.search(query)
self.assertEqual(1, len(search_results['hits']))
title3 = search_results['hits'][0]['title']
self.assertNotEqual(title1, title2)
self.assertNotEqual(title2, title3)
def test_search_sort_by_date_filters_out_past_events(self):
now = datetime.utcnow()
last_year = self.construct_event(title="A year ago", date=now + timedelta(days=-365))
last_week = self.construct_event(title="A week ago", date=now + timedelta(days=-7))
yesterday = self.construct_event(title="Yesterday", date=now + timedelta(days=-1))
# Events earlier in the day are included. They aren't excluded until the local time is past midnight.
earlier_today = self.construct_event(title="Earlier today", date=now - timedelta(hours=1))
later_today = self.construct_event(title="Later today", date=now + timedelta(hours=1))
tomorrow = self.construct_event(title="Tomorrow", date=now + timedelta(days=1))
next_week = self.construct_event(title="Next week", date=now + timedelta(days=7))
next_year = self.construct_event(title="Next year", date=now + timedelta(days=365))
query = {
'date': now,
'sort': {
'field': 'date',
'order': 'asc'
}
}
search_results = self.search(query)
self.assertEqual(5, len(search_results['hits']))
self.assertEqual(search_results['hits'][0]['title'], earlier_today.title)
self.assertEqual(search_results['hits'][1]['title'], later_today.title)
self.assertEqual(search_results['hits'][2]['title'], tomorrow.title)
self.assertEqual(search_results['hits'][3]['title'], next_week.title)
self.assertEqual(search_results['hits'][4]['title'], next_year.title)
def test_search_filters_out_past_events(self):
now = datetime.utcnow()
self.construct_resource(title="How to style unicorn hair")
self.construct_resource(title="Rainbow-emitting capabilities of unicorn horns")
self.construct_resource(title="Tips for time travel with a unicorn")
self.construct_event(
title="Unicorn council last year",
date=now + timedelta(days=-365),
description="Upcoming unicorn council",
post_event_description="Previously recorded unicorn council"
)
self.construct_event(title="Unicorn meetup a week ago", date=now + timedelta(days=-7))
self.construct_event(title="Unicorn workshop yesterday", date=now + timedelta(days=-1))
self.construct_event(title="Unicorn playdate tomorrow", date=now + timedelta(days=1))
self.construct_event(title="Unicorn training next week", date=now + timedelta(days=7))
self.construct_event(title="Unicorn conference next year", date=now + timedelta(days=365))
# Should return future events, plus past events that have a post_event_description
query = {'words': 'unicorn'}
search_results = self.search(query)
self.assertEqual(7, len(search_results['hits']))
for hit in search_results['hits']:
if hit['type'] == 'event':
hit_date = dateutil.parser.parse(hit['date'])
if hit_date >= now:
self.assertGreaterEqual(hit_date, now)
else:
self.assertTrue('post_event_description' in hit, 'Hit has no post-event description.')
self.assertIsNotNone(hit['post_event_description'], 'Post-event description is empty.')
else:
self.assertTrue(hit['date'] is None)
# Should only return future events when filtering by event type
event_query = {'words': 'unicorn', 'types': ['event']}
event_search_results = self.search(event_query)
self.assertEqual(3, len(event_search_results['hits']))
for hit in event_search_results['hits']:
self.assertEqual(hit['type'], 'event')
hit_date = dateutil.parser.parse(hit['date'])
self.assertGreaterEqual(hit_date, now)
def test_search_for_map_points_only(self):
# Add some locations with coordinates, and some with out.
location_near = self.construct_location(title='local unicorn',
description="delivering rainbows within the orbit of Uranus",
latitude=38.149595, longitude=-79.072557)
location_far = self.construct_location(title='distant unicorn',
description="delivering rainbows to the greater Trans-Neptunian Region",
latitude=-38.149595, longitude=100.927443)
location_mid = self.construct_location(title='middle unicorn',
description="delivering rainbows somewhere in between",
latitude=37.5246403, longitude=-77.5633015)
self.construct_resource(title="Rainbow with a bad hair day and no place to be.")
self.construct_resource(title="A non-rainbow blue sky that covers nearly all locations.")
self.construct_resource(title="A very very tiny rainbow in a sprinkler, that occurs in various places.")
query = {'words': 'rainbows'}
search_results = self.search(query)
self.assertEqual(6, len(search_results['hits']))
query = {'words': 'rainbows', 'map_data_only': True}
search_results = self.search(query)
self.assertEqual(3, len(search_results['hits']))
self.assertTrue('latitude' in search_results['hits'][0])
self.assertTrue('longitude' in search_results['hits'][0])
self.assertFalse('content' in search_results['hits'][0])
self.assertFalse('description' in search_results['hits'][0])
self.assertFalse('highlights' in search_results['hits'][0])
def test_study_search_record_updates(self):
umbrella_query = {'words': 'umbrellas'}
# test that elastic resource is created with post
study = {'status': "currently_enrolling", 'title': "space platypus", 'description': "delivering umbrellas",
'organization_name': "Study Org"}
rv = self.app.post('api/study', data=self.jsonify(study), content_type="application/json",
follow_redirects=True)
self.assert_success(rv)
response = json.loads(rv.get_data(as_text=True))
s_id = response['id']
search_results = self.search(umbrella_query)
self.assertEqual(1, len(search_results['hits']))
self.assertEqual(search_results['hits'][0]['id'], s_id)
self.assertEqual(search_results['hits'][0]['status'], 'Currently enrolling')
response['status'] = "study_in_progress"
rv = self.app.put('/api/study/%i' % s_id, data=self.jsonify(response), content_type="application/json",
follow_redirects=True)
self.assert_success(rv)
rv = self.app.get('/api/study/%i' % s_id, content_type="application/json")
self.assert_success(rv)
search_results = self.search(umbrella_query)
self.assertEqual(1, len(search_results['hits']))
self.assertEqual(search_results['hits'][0]['id'], s_id)
self.assertEqual(search_results['hits'][0]['status'], 'Study in progress')
def test_search_with_drafts(self):
resource_query = {'words': 'resource'}
password = 'Silly password 305 for all the pretend users'
self.construct_resource(title="Drafty Draft Draft Resource", is_draft=True)
self.construct_resource(title="Published Resource", is_draft=False)
self.construct_resource(title="Officially Published Resource", is_draft=False)
self.construct_resource(title="A Draft Resource for the ages", is_draft=True)
admin = self.construct_user(email='admin@sartography.com', role=Role.admin)
editor = self.construct_user(email='editor@sartography.com', role=Role.editor)
researcher = self.construct_user(email='researcher@sartography.com', role=Role.researcher)
user = self.construct_user(email='user@sartography.com', role=Role.user)
self.login_user(admin, password)
search_results = self.search(resource_query, user=admin)
self.assertEqual(4, len(search_results['hits']))
self.login_user(editor, password)
search_results = self.search(resource_query, user=editor)
self.assertEqual(4, len(search_results['hits']))
self.login_user(researcher, password)
search_results = self.search(resource_query, user=researcher)
self.assertEqual(2, len(search_results['hits']))
self.login_user(user, password)
search_results = self.search(resource_query, user=user)
self.assertEqual(2, len(search_results['hits']))
def login_user(self, user, password):
user.email_verified = True
user.password = password
db.session.add(user)
data = {'email': user.email, 'password': password}
rv = self.app.post(
'/api/login_password',
data=self.jsonify(data),
content_type="application/json")
self.assert_success(rv)
response = json.loads(rv.get_data(as_text=True))
self.assertIsNotNone(response["token"])

398
backend/tests/test_study.py Normal file
View File

@ -0,0 +1,398 @@
import unittest
from flask import json
from tests.base_test import BaseTest
from app import db
from app.email_service import TEST_MESSAGES
from app.model.email_log import EmailLog
from app.model.investigator import Investigator
from app.model.participant import Relationship
from app.model.questionnaires.identification_questionnaire import IdentificationQuestionnaire
from app.model.questionnaires.contact_questionnaire import ContactQuestionnaire
from app.model.study import Study, Status
from app.model.study_category import StudyCategory
from app.model.study_investigator import StudyInvestigator
class TestStudy(BaseTest, unittest.TestCase):
def test_study_basics(self):
self.construct_study()
s = db.session.query(Study).first()
self.assertIsNotNone(s)
s_id = s.id
rv = self.app.get('/api/study/%i' % s_id,
follow_redirects=True,
content_type="application/json")
self.assert_success(rv)
response = json.loads(rv.get_data(as_text=True))
self.assertEqual(response["id"], s_id)
self.assertEqual(response["title"], 'Fantastic Study')
self.assertEqual(response["description"], 'A study that will go down in history')
self.assertNotIn('study_users', response, "Never include info about other users in a non-protected endpoint")
self.assertNotIn('users', response, "Never include info about other users in a non-protected endpoint")
def test_modify_study_basics(self):
self.construct_study()
s = db.session.query(Study).first()
self.assertIsNotNone(s)
s_id = s.id
rv = self.app.get('/api/study/%i' % s_id, content_type="application/json")
response = json.loads(rv.get_data(as_text=True))
response['title'] = 'Edwarardos Lemonade and Oil Change'
response['description'] = 'Better fluids for you and your car.'
response['benefit_description'] = 'Better fluids for you and your car, Duh.'
response["short_title"] = 'Edwardos'
response["short_description"] = 'Better fluids yada yada.'
response["image_url"] = '/some/url'
response["coordinator_email"] = 'hello@study.com'
orig_date = response['last_updated']
rv = self.app.put('/api/study/%i' % s_id, data=self.jsonify(response), content_type="application/json",
follow_redirects=True)
self.assert_success(rv)
rv = self.app.get('/api/study/%i' % s_id, content_type="application/json")
self.assert_success(rv)
response = json.loads(rv.get_data(as_text=True))
self.assertEqual(response['title'], 'Edwarardos Lemonade and Oil Change')
self.assertEqual(response['description'], 'Better fluids for you and your car.')
self.assertEqual(response['benefit_description'], 'Better fluids for you and your car, Duh.')
self.assertEqual(response["short_title"], 'Edwardos')
self.assertEqual(response["short_description"], 'Better fluids yada yada.')
self.assertEqual(response["image_url"], '/some/url')
self.assertEqual(response["coordinator_email"], 'hello@study.com')
self.assertNotEqual(orig_date, response['last_updated'])
def test_delete_study(self):
s = self.construct_study()
s_id = s.id
rv = self.app.get('api/study/%i' % s_id, content_type="application/json")
self.assert_success(rv)
rv = self.app.delete('api/study/%i' % s_id, content_type="application/json")
self.assert_success(rv)
rv = self.app.get('api/study/%i' % s_id, content_type="application/json")
self.assertEqual(404, rv.status_code)
def test_create_study(self):
study = {'title': "Study of Studies", 'benefit_description': "This study will change your life.",
'organization_name': "Study Org"}
rv = self.app.post('api/study', data=self.jsonify(study), content_type="application/json",
follow_redirects=True)
self.assert_success(rv)
response = json.loads(rv.get_data(as_text=True))
self.assertEqual(response['title'], 'Study of Studies')
self.assertEqual(response['benefit_description'], 'This study will change your life.')
self.assertIsNotNone(response['id'])
def test_get_study_by_category(self):
c = self.construct_category()
s = self.construct_study()
cs = StudyCategory(study=s, category=c)
db.session.add(cs)
db.session.commit()
rv = self.app.get(
'/api/category/%i/study' % c.id,
content_type="application/json",
headers=self.logged_in_headers())
self.assert_success(rv)
response = json.loads(rv.get_data(as_text=True))
self.assertEqual(1, len(response))
self.assertEqual(s.id, response[0]["study_id"])
self.assertEqual(s.description, response[0]["study"]["description"])
def test_get_study_by_category_includes_category_details(self):
c = self.construct_category(name="c1")
c2 = self.construct_category(name="c2")
s = self.construct_study()
cs = StudyCategory(study=s, category=c)
cs2 = StudyCategory(study=s, category=c2)
db.session.add_all([cs, cs2])
db.session.commit()
rv = self.app.get(
'/api/category/%i/study' % c.id,
content_type="application/json",
headers=self.logged_in_headers())
self.assert_success(rv)
response = json.loads(rv.get_data(as_text=True))
self.assertEqual(s.id, response[0]["study_id"])
self.assertEqual(2,
len(response[0]["study"]["study_categories"]))
self.assertEqual(
"c1", response[0]["study"]["study_categories"][0]["category"]
["name"])
def test_category_study_count(self):
c = self.construct_category()
s = self.construct_study()
cs = StudyCategory(study=s, category=c)
db.session.add(cs)
db.session.commit()
rv = self.app.get(
'/api/category/%i' % c.id, content_type="application/json")
self.assert_success(rv)
response = json.loads(rv.get_data(as_text=True))
self.assertEqual(1, response["study_count"])
def test_get_category_by_study(self):
c = self.construct_category()
s = self.construct_study()
cs = StudyCategory(study=s, category=c)
db.session.add(cs)
db.session.commit()
rv = self.app.get(
'/api/study/%i/category' % s.id,
content_type="application/json")
self.assert_success(rv)
response = json.loads(rv.get_data(as_text=True))
self.assertEqual(1, len(response))
self.assertEqual(c.id, response[0]["id"])
self.assertEqual(c.name, response[0]["category"]["name"])
def test_add_category_to_study(self):
c = self.construct_category()
s = self.construct_study()
sc_data = {"study_id": s.id, "category_id": c.id}
rv = self.app.post(
'/api/study_category',
data=self.jsonify(sc_data),
content_type="application/json")
self.assert_success(rv)
response = json.loads(rv.get_data(as_text=True))
self.assertEqual(c.id, response["category_id"])
self.assertEqual(s.id, response["study_id"])
def test_set_all_categories_on_study(self):
c1 = self.construct_category(name="c1")
c2 = self.construct_category(name="c2")
c3 = self.construct_category(name="c3")
s = self.construct_study()
sc_data = [
{
"category_id": c1.id
},
{
"category_id": c2.id
},
{
"category_id": c3.id
},
]
rv = self.app.post(
'/api/study/%i/category' % s.id,
data=self.jsonify(sc_data),
content_type="application/json")
self.assert_success(rv)
response = json.loads(rv.get_data(as_text=True))
self.assertEqual(3, len(response))
sc_data = [{"category_id": c1.id}]
rv = self.app.post(
'/api/study/%i/category' % s.id,
data=self.jsonify(sc_data),
content_type="application/json")
self.assert_success(rv)
response = json.loads(rv.get_data(as_text=True))
self.assertEqual(1, len(response))
def test_remove_category_from_study(self):
self.test_add_category_to_study()
rv = self.app.delete('/api/study_category/%i' % 1)
self.assert_success(rv)
rv = self.app.get(
'/api/study/%i/category' % 1, content_type="application/json")
self.assert_success(rv)
response = json.loads(rv.get_data(as_text=True))
self.assertEqual(0, len(response))
def test_add_investigator_to_study(self):
i = self.construct_investigator()
s = self.construct_study()
si_data = {"study_id": s.id, "investigator_id": i.id}
rv = self.app.post(
'/api/study_investigator',
data=self.jsonify(si_data),
content_type="application/json")
self.assert_success(rv)
response = json.loads(rv.get_data(as_text=True))
self.assertEqual(i.id, response["investigator_id"])
self.assertEqual(s.id, response["study_id"])
def test_set_all_investigators_on_study(self):
i1 = self.construct_investigator(name="person1")
i2 = self.construct_investigator(name="person2")
i3 = self.construct_investigator(name="person3")
s = self.construct_study()
si_data = [
{"investigator_id": i1.id},
{"investigator_id": i2.id},
{"investigator_id": i3.id},
]
rv = self.app.post(
'/api/study/%i/investigator' % s.id,
data=self.jsonify(si_data),
content_type="application/json")
self.assert_success(rv)
response = json.loads(rv.get_data(as_text=True))
self.assertEqual(3, len(response))
si_data = [{"investigator_id": i1.id}]
rv = self.app.post(
'/api/study/%i/investigator' % s.id,
data=self.jsonify(si_data),
content_type="application/json")
self.assert_success(rv)
response = json.loads(rv.get_data(as_text=True))
self.assertEqual(1, len(response))
def test_remove_investigator_from_study(self):
self.test_add_investigator_to_study()
rv = self.app.delete('/api/study_investigator/%i' % 1)
self.assert_success(rv)
rv = self.app.get(
'/api/study/%i/investigator' % 1, content_type="application/json")
self.assert_success(rv)
response = json.loads(rv.get_data(as_text=True))
self.assertEqual(0, len(response))
def test_study_inquiry_sends_email(self):
message_count = len(TEST_MESSAGES)
s = self.construct_study(title="The Best Study")
u = self.construct_user()
guardian = self.construct_participant(user=u, relationship=Relationship.self_guardian)
dependent1 = self.construct_participant(user=u, relationship=Relationship.dependent)
self.construct_contact_questionnaire(user=u, participant=guardian, phone="540-669-8855")
self.construct_identification_questionnaire(user=u, participant=guardian, first_name="Fred")
self.construct_identification_questionnaire(user=u, participant=dependent1, first_name="Fred", is_first_name_preferred=False, nickname="Zorba")
data = {'user_id': u.id, 'study_id': s.id}
rv = self.app.post('/api/study_inquiry',
data=self.jsonify(data),
follow_redirects=True,
content_type="application/json",
headers=self.logged_in_headers())
self.assert_success(rv)
self.assertGreater(len(TEST_MESSAGES), message_count)
self.assertEqual("Autism DRIVE: Study Inquiry Email",
self.decode(TEST_MESSAGES[-1]['subject']))
logs = EmailLog.query.all()
self.assertIsNotNone(logs[-1].tracking_code)
def test_study_inquiry_creates_StudyUser(self):
s = self.construct_study(title="The Best Study")
u = self.construct_user()
self.assertEquals(0, len(s.study_users))
guardian = self.construct_participant(user=u, relationship=Relationship.self_guardian)
self.construct_contact_questionnaire(user=u, participant=guardian, phone="540-669-8855")
self.construct_identification_questionnaire(user=u, participant=guardian, first_name="Fred")
data = {'user_id': u.id, 'study_id': s.id}
rv = self.app.post('/api/study_inquiry',
data=self.jsonify(data),
follow_redirects=True,
content_type="application/json",
headers=self.logged_in_headers())
self.assert_success(rv)
self.assertEquals(1, len(s.study_users))
def test_study_inquiry_fails_without_valid_study_or_user(self):
s = self.construct_study(title="The Best Study")
u = self.construct_user()
guardian = self.construct_participant(user=u, relationship=Relationship.self_guardian)
self.construct_contact_questionnaire(user=u, participant=guardian, phone="540-669-8855")
self.construct_identification_questionnaire(user=u, participant=guardian, first_name="Fred")
data = {'user_id': u.id, 'study_id': 456}
rv = self.app.post('/api/study_inquiry',
data=self.jsonify(data),
follow_redirects=True,
content_type="application/json",
headers=self.logged_in_headers())
self.assertEqual(400, rv.status_code)
response = json.loads(rv.get_data(as_text=True))
self.assertEqual(response['message'], 'Error in finding correct user and study to complete study inquiry')
data = {'user_id': 456, 'study_id': s.id}
rv = self.app.post('/api/study_inquiry',
data=self.jsonify(data),
follow_redirects=True,
content_type="application/json",
headers=self.logged_in_headers())
self.assertEqual(400, rv.status_code)
response = json.loads(rv.get_data(as_text=True))
self.assertEqual(response['message'], 'Error in finding correct user and study to complete study inquiry')
def construct_identification_questionnaire(self, relationship_to_participant='adoptFather', first_name='Karl',
is_first_name_preferred=True, nickname=None, participant=None, user=None):
iq = IdentificationQuestionnaire(relationship_to_participant=relationship_to_participant, first_name=first_name,
is_first_name_preferred=is_first_name_preferred, nickname=nickname)
if user is None:
u = self.construct_user(email='ident@questionnaire.com')
iq.user_id = u.id
else:
u = user
iq.user_id = u.id
if participant is None:
iq.participant_id = self.construct_participant(user=u, relationship=Relationship.dependent).id
else:
iq.participant_id = participant.id
db.session.add(iq)
db.session.commit()
db_iq = db.session.query(IdentificationQuestionnaire).filter_by(participant_id=iq.participant_id).first()
self.assertEqual(db_iq.nickname, iq.nickname)
return db_iq
def construct_contact_questionnaire(self, phone="123-456-7890", can_leave_voicemail=True, contact_times="whenever",
email='contact@questionnaire.com', participant=None, user=None):
cq = ContactQuestionnaire(phone=phone, can_leave_voicemail=can_leave_voicemail, contact_times=contact_times,
email=email)
if user is None:
u = self.construct_user(email='contact@questionnaire.com')
cq.user_id = u.id
else:
u = user
cq.user_id = u.id
if participant is None:
cq.participant_id = self.construct_participant(user=u, relationship=Relationship.dependent).id
else:
cq.participant_id = participant.id
db.session.add(cq)
db.session.commit()
db_cq = db.session.query(ContactQuestionnaire).filter_by(zip=cq.zip).first()
self.assertEqual(db_cq.phone, cq.phone)
return db_cq
def test_delete_study_deletes_relationship(self):
i = self.construct_investigator()
s = self.construct_study()
si = StudyInvestigator(investigator_id=i.id, study_id=s.id)
db.session.add(si)
db.session.commit()
si_id = si.id
rv = self.app.get('api/study_investigator/%i' % si_id, content_type="application/json", headers=self.logged_in_headers())
self.assert_success(rv)
rv = self.app.delete('api/study/%i' % s.id, content_type="application/json",
headers=self.logged_in_headers())
self.assert_success(rv)
rv = self.app.get('api/study_investigator/%i' % si_id, content_type="application/json", headers=self.logged_in_headers())
self.assertEqual(404, rv.status_code)

605
backend/tests/test_user.py Normal file
View File

@ -0,0 +1,605 @@
import unittest
from flask import json
from nose.tools import raises
from app.model.role import Permission
from app.model.user_favorite import UserFavorite
from tests.base_test import BaseTest
from app import db, RestException
from app.email_service import TEST_MESSAGES
from app.model.email_log import EmailLog
from app.model.study_user import StudyUser, StudyUserStatus
from app.model.user import User, Role
from app.model.participant import Relationship
class TestUser(BaseTest, unittest.TestCase):
def test_user_basics(self):
self.construct_user()
u = db.session.query(User).first()
self.assertIsNotNone(u)
u_id = u.id
headers = self.logged_in_headers(u)
rv = self.app.get('/api/user/%i' % u_id,
follow_redirects=True,
content_type="application/json", headers=headers)
self.assert_success(rv)
response = json.loads(rv.get_data(as_text=True))
self.assertEqual(response["id"], u_id)
self.assertEqual(response["email"], 'stan@staunton.com')
def test_modify_user_basics(self):
self.construct_user()
u = db.session.query(User).first()
admin_headers = self.logged_in_headers()
user_headers = self.logged_in_headers(u)
self.assertIsNotNone(u)
# A user should be able to access and modify their user record, with the exception of making themselves Admin
rv = self.app.get('/api/user/%i' % u.id, content_type="application/json", headers=user_headers)
self.assert_success(rv)
response = json.loads(rv.get_data(as_text=True))
response['email'] = 'ed@edwardos.com'
orig_date = response['last_updated']
rv = self.app.put('/api/user/%i' % u.id, data=self.jsonify(response), content_type="application/json",
follow_redirects=True, headers=user_headers)
self.assert_success(rv)
# Only Admin users can make other admin users
response['role'] = 'admin'
rv = self.app.put('/api/user/%i' % u.id, data=self.jsonify(response), content_type="application/json",
follow_redirects=True, headers=admin_headers)
self.assert_success(rv)
rv = self.app.get('/api/user/%i' % u.id, content_type="application/json", headers=user_headers)
self.assert_success(rv)
response = json.loads(rv.get_data(as_text=True))
self.assertEqual(response['email'], 'ed@edwardos.com')
self.assertEqual(response['role'], 'admin')
self.assertNotEqual(orig_date, response['last_updated'])
def test_delete_user(self):
u = self.construct_user()
u_id = u.id
rv = self.app.get('api/user/%i' % u_id, content_type="application/json")
self.assertEqual(401, rv.status_code)
rv = self.app.get('api/user/%i' % u_id, content_type="application/json", headers=self.logged_in_headers())
self.assert_success(rv)
rv = self.app.delete('api/user/%i' % u_id, content_type="application/json", headers=None)
self.assertEqual(401, rv.status_code)
rv = self.app.delete('api/user/%i' % u_id, content_type="application/json", headers=self.logged_in_headers())
self.assert_success(rv)
rv = self.app.get('api/user/%i' % u_id, content_type="application/json")
self.assertEqual(401, rv.status_code)
rv = self.app.get('api/user/%i' % u_id, content_type="application/json", headers=self.logged_in_headers())
self.assertEqual(404, rv.status_code)
def test_create_user(self):
user = {'email': "tara@spiders.org"}
rv = self.app.post('api/user', data=self.jsonify(user), content_type="application/json",
headers=self.logged_in_headers(), follow_redirects=True)
self.assert_success(rv)
response = json.loads(rv.get_data(as_text=True))
self.assertEqual(response['email'], 'tara@spiders.org')
self.assertIsNotNone(response['id'])
def test_create_user_with_bad_role(self):
user = {'email': "tara@spiders.org", 'role': 'web_weaver'}
# post should change unknown role to 'user'
rv = self.app.post('/api/user', data=self.jsonify(user),
content_type="application/json", follow_redirects=True, headers=self.logged_in_headers())
self.assert_success(rv)
response = json.loads(rv.get_data(as_text=True))
self.assertEqual(response['role'], 'user')
def test_non_admin_cannot_create_admin_user(self):
u = self.construct_user()
# post should make role 'user'
new_admin_user = {'email': "tara@spiders.org", 'role': 'admin'}
rv = self.app.post('/api/user', data=self.jsonify(new_admin_user),
content_type="application/json", follow_redirects=True, headers=self.logged_in_headers(user=u))
self.assert_success(rv)
response = json.loads(rv.get_data(as_text=True))
self.assertEqual(response['role'], 'user')
new_id = response['id']
# put as non-admin user should keep role as 'user'
rv = self.app.put('/api/user/%i' % u.id, data=self.jsonify({'email': u.email, 'role': 'admin'}),
content_type="application/json", follow_redirects=True, headers=self.logged_in_headers(user=u))
self.assert_success(rv)
response = json.loads(rv.get_data(as_text=True))
self.assertEqual(response['role'], 'user')
# put as admin user should allow to make role 'admin'
rv = self.app.put('/api/user/%i' % new_id, data=self.jsonify(new_admin_user),
content_type="application/json", follow_redirects=True, headers=self.logged_in_headers())
self.assert_success(rv)
response = json.loads(rv.get_data(as_text=True))
self.assertEqual(response['role'], 'admin')
@raises(RestException)
def test_create_user_with_bad_password(self, id=8, email="tyrion@got.com", role=Role.user):
data = {
"id": id,
"email": email
}
rv = self.app.post(
'/api/user',
data=self.jsonify(data),
follow_redirects=True,
headers=self.logged_in_headers(),
content_type="application/json")
self.assert_success(rv)
user = User.query.filter_by(id=id).first()
user.role = role
# Should raise exception
user.password = "badpass"
def test_create_user_with_password(self, id=8, email="tyrion@got.com", role=Role.user):
data = {
"id": id,
"email": email
}
rv = self.app.post(
'/api/user',
data=self.jsonify(data),
follow_redirects=True,
headers=self.logged_in_headers(),
content_type="application/json")
self.assert_success(rv)
user = User.query.filter_by(id=id).first()
self.assertIsNotNone(user)
self.assertIsNotNone(user.token_url)
user.role = role
password = "Wowbagger the Infinitely Prolonged !@#%$12354"
user.password = password
db.session.add(user)
db.session.commit()
rv = self.app.get(
'/api/user/%i' % user.id,
content_type="application/json",
headers=self.logged_in_headers())
self.assert_success(rv)
response = json.loads(rv.get_data(as_text=True))
self.assertEqual(email, response["email"])
self.assertEqual(role.name, response["role"])
self.assertEqual(True, user.is_correct_password(password))
return user
def test_login_user(self):
user = self.test_create_user_with_password()
data = {"email": "tyrion@got.com", "password": "Wowbagger the Infinitely Prolonged !@#%$12354"}
# Login shouldn't work with email not yet verified
rv = self.app.post(
'/api/login_password',
data=self.jsonify(data),
content_type="application/json")
self.assertEqual(400, rv.status_code)
user.email_verified = True
rv = self.app.post(
'/api/login_password',
data=self.jsonify(data),
content_type="application/json")
self.assert_success(rv)
response = json.loads(rv.get_data(as_text=True))
self.assertIsNotNone(response["token"])
return user
def test_get_current_user(self):
""" Test for the current user status """
user = self.test_login_user()
# Now get the user back.
response = self.app.get(
'/api/session',
headers=dict(
Authorization='Bearer ' + user.encode_auth_token().decode()))
self.assert_success(response)
return json.loads(response.data.decode())
def test_register_sends_email(self):
message_count = len(TEST_MESSAGES)
self.test_create_user_with_password()
self.assertGreater(len(TEST_MESSAGES), message_count)
self.assertEqual("Autism DRIVE: Confirm Email",
self.decode(TEST_MESSAGES[-1]['subject']))
logs = EmailLog.query.all()
self.assertIsNotNone(logs[-1].tracking_code)
def test_forgot_password_sends_email(self):
user = self.test_create_user_with_password(id=10101, email="forgot_password_sends_email@test.com")
message_count = len(TEST_MESSAGES)
data = {"email": user.email}
rv = self.app.post(
'/api/forgot_password',
data=self.jsonify(data),
content_type="application/json")
self.assert_success(rv)
self.assertGreater(len(TEST_MESSAGES), message_count)
self.assertEqual("Autism DRIVE: Password Reset Email",
self.decode(TEST_MESSAGES[-1]['subject']))
logs = EmailLog.query.all()
self.assertIsNotNone(logs[-1].tracking_code)
def test_enrolled_vs_inquiry_studies_by_user(self):
u = self.construct_user(email="u1@sartography.com")
s1 = self.construct_study(title="Super Study")
s2 = self.construct_study(title="Amazing Study")
s3 = self.construct_study(title="Phenomenal Study")
su1 = StudyUser(study=s1, user=u, status=StudyUserStatus.inquiry_sent)
su2 = StudyUser(study=s3, user=u, status=StudyUserStatus.inquiry_sent)
su3 = StudyUser(study=s2, user=u, status=StudyUserStatus.enrolled)
db.session.add_all([su1, su2, su3])
db.session.commit()
rv = self.app.get(
'/api/user/%i/inquiry/study' % u.id,
content_type="application/json",
headers=self.logged_in_headers())
self.assert_success(rv)
response = json.loads(rv.get_data(as_text=True))
self.assertEqual(2, len(response))
self.assertNotEqual(s2.id, response[0]["study_id"])
self.assertNotEqual(s2.id, response[1]["study_id"])
rv = self.app.get(
'/api/user/%i/enrolled/study' % u.id,
content_type="application/json",
headers=self.logged_in_headers())
self.assert_success(rv)
response = json.loads(rv.get_data(as_text=True))
self.assertEqual(1, len(response))
self.assertEqual(s2.id, response[0]["study_id"])
def test_get_user_by_study(self):
u = self.construct_user()
s = self.construct_study()
su = StudyUser(study=s, user=u, status=StudyUserStatus.inquiry_sent)
db.session.add(su)
db.session.commit()
rv = self.app.get(
'/api/study/%i/user' % s.id,
content_type="application/json",
headers=self.logged_in_headers())
self.assert_success(rv)
response = json.loads(rv.get_data(as_text=True))
self.assertEqual(1, len(response))
self.assertEqual(su.id, response[0]["id"])
self.assertEqual(u.id, response[0]["user"]["id"])
self.assertEqual(u.email, response[0]["user"]["email"])
def test_add_user_to_study(self):
u = self.construct_user()
s = self.construct_study()
us_data = {"study_id": s.id, "user_id": u.id}
rv = self.app.post(
'/api/study_user',
data=self.jsonify(us_data),
content_type="application/json",
headers=self.logged_in_headers())
self.assert_success(rv)
response = json.loads(rv.get_data(as_text=True))
self.assertEqual(u.id, response["user_id"])
self.assertEqual(s.id, response["study_id"])
def test_set_all_users_on_study(self):
u1 = self.construct_user(email="u1@sartography.com")
u2 = self.construct_user(email="u2@sartography.com")
u3 = self.construct_user(email="u3@sartography.com")
s = self.construct_study()
us_data = [
{
"user_id": u1.id
},
{
"user_id": u2.id
},
{
"user_id": u3.id
},
]
rv = self.app.post(
'/api/study/%i/user' % s.id,
data=self.jsonify(us_data),
content_type="application/json",
headers=self.logged_in_headers())
self.assert_success(rv)
response = json.loads(rv.get_data(as_text=True))
self.assertEqual(3, len(response))
us_data = [{"user_id": u1.id}]
rv = self.app.post(
'/api/study/%i/user' % s.id,
data=self.jsonify(us_data),
content_type="application/json",
headers=self.logged_in_headers())
self.assert_success(rv)
response = json.loads(rv.get_data(as_text=True))
self.assertEqual(1, len(response))
def test_remove_user_from_study(self):
self.test_add_user_to_study()
rv = self.app.delete('/api/study_user/%i' % 1,
headers=self.logged_in_headers())
self.assert_success(rv)
rv = self.app.get(
'/api/study/%i/user' % 1,
content_type="application/json",
headers=self.logged_in_headers())
self.assert_success(rv)
response = json.loads(rv.get_data(as_text=True))
self.assertEqual(0, len(response))
def test_user_ids_are_not_sequential(self):
u1 = self.construct_user(email="hopper@strangerthings.org")
u2 = self.construct_user(email="eleven@strangerthings.org")
u3 = self.construct_user(email="murray@strangerthings.org")
self.assertNotEqual(u1.id + 1, u2.id)
self.assertNotEqual(u2.id + 1, u3.id)
def test_get_user_permissions(self):
admin = self.construct_user(role=Role.admin, email='admin@sartography.com')
test = self.construct_user(role=Role.test, email='test@sartography.com')
researcher = self.construct_user(role=Role.researcher, email='researcher@sartography.com')
editor = self.construct_user(role=Role.editor, email='editor@sartography.com')
user = self.construct_user(role=Role.user, email='user@sartography.com')
self.assertEqual(admin.role.permissions(), list(Permission))
self.assertEqual(researcher.role.permissions(), [Permission.user_detail_admin, ])
self.assertEqual(editor.role.permissions(), [Permission.create_resource, Permission.edit_resource,
Permission.delete_resource])
self.assertEqual(user.role.permissions(), [])
self.assertTrue(Permission.user_roles in admin.role.permissions())
self.assertFalse(Permission.user_roles in test.role.permissions())
self.assertFalse(Permission.delete_user in test.role.permissions())
self.assertTrue(Permission.user_detail_admin in researcher.role.permissions())
self.assertTrue(Permission.create_resource in editor.role.permissions())
self.assertTrue(Permission.user_roles not in user.role.permissions())
self.assertTrue(Permission.user_roles not in editor.role.permissions())
self.assertTrue(Permission.user_roles not in researcher.role.permissions())
def test_login_tracks_login_date(self):
user = self.test_create_user_with_password()
self.assertIsNone(user.last_login)
data = {"email": "tyrion@got.com", "password": "Wowbagger the Infinitely Prolonged !@#%$12354"}
user.email_verified = True
rv = self.app.post(
'/api/login_password',
data=self.jsonify(data),
content_type="application/json")
self.assert_success(rv)
response = json.loads(rv.get_data(as_text=True))
self.assertIsNotNone(response["last_login"])
first_login = response["last_login"]
rv = self.app.post(
'/api/login_password',
data=self.jsonify(data),
content_type="application/json")
self.assert_success(rv)
response = json.loads(rv.get_data(as_text=True))
self.assertNotEqual(response["last_login"], first_login)
def test_get_favorites_by_user(self):
u = self.construct_user()
r = self.construct_resource()
c = self.construct_category()
fav1 = UserFavorite(resource_id=r.id, user=u, type='resource')
fav2 = UserFavorite(category_id=c.id, user=u, type='category')
db.session.add_all([fav1, fav2])
db.session.commit()
rv = self.app.get(
'/api/user/%i/favorite' % u.id,
content_type="application/json",
headers=self.logged_in_headers())
self.assert_success(rv)
response = json.loads(rv.get_data(as_text=True))
self.assertEqual(2, len(response))
self.assertEqual(r.id, response[0]["resource_id"])
self.assertEqual('resource', response[0]["type"])
self.assertEqual(c.id, response[1]["category_id"])
self.assertEqual('category', response[1]["type"])
def test_user_favorite_types(self):
u = self.construct_user(email="u1@sartography.com")
r = self.construct_resource()
c = self.construct_category(name='cat1')
c2 = self.construct_category(name='cat2')
fav1 = UserFavorite(resource_id=r.id, user=u, type='resource')
fav2 = UserFavorite(category_id=c.id, user=u, type='category')
fav3 = UserFavorite(category_id=c2.id, user=u, type='category')
fav4 = UserFavorite(age_range='adult', user=u, type='age_range')
fav5 = UserFavorite(age_range='aging', user=u, type='age_range')
fav6 = UserFavorite(age_range='transition', user=u, type='age_range')
fav7 = UserFavorite(language='arabic', user=u, type='language')
fav8 = UserFavorite(covid19_category='edu-tainment', user=u, type='covid19_category')
db.session.add_all([fav1, fav2, fav3, fav4, fav5, fav6, fav7, fav8])
rv = self.app.get(
'/api/user/%i/favorite' % u.id,
content_type="application/json",
headers=self.logged_in_headers())
self.assert_success(rv)
response = json.loads(rv.get_data(as_text=True))
self.assertEqual(8, len(response))
rv = self.app.get(
'/api/user/%i/favorite/resource' % u.id,
content_type="application/json",
headers=self.logged_in_headers())
self.assert_success(rv)
response = json.loads(rv.get_data(as_text=True))
self.assertEqual(1, len(response))
self.assertEqual(fav1.id, response[0]["id"])
rv = self.app.get(
'/api/user/%i/favorite/category' % u.id,
content_type="application/json",
headers=self.logged_in_headers())
self.assert_success(rv)
response = json.loads(rv.get_data(as_text=True))
self.assertEqual(2, len(response))
self.assertEqual(fav2.id, response[0]["id"])
rv = self.app.get(
'/api/user/%i/favorite/age_range' % u.id,
content_type="application/json",
headers=self.logged_in_headers())
self.assert_success(rv)
response = json.loads(rv.get_data(as_text=True))
self.assertEqual(3, len(response))
self.assertEqual(fav4.id, response[0]["id"])
rv = self.app.get(
'/api/user/%i/favorite/language' % u.id,
content_type="application/json",
headers=self.logged_in_headers())
self.assert_success(rv)
response = json.loads(rv.get_data(as_text=True))
self.assertEqual(1, len(response))
self.assertEqual(fav7.id, response[0]["id"])
rv = self.app.get(
'/api/user/%i/favorite/covid19_category' % u.id,
content_type="application/json",
headers=self.logged_in_headers())
self.assert_success(rv)
response = json.loads(rv.get_data(as_text=True))
self.assertEqual(1, len(response))
self.assertEqual(fav8.id, response[0]["id"])
def test_add_favorite_to_user(self):
u = self.construct_user()
r = self.construct_resource()
fav_data = [{"resource_id": r.id, "user_id": u.id, "type": 'resource'}]
rv = self.app.post(
'/api/user_favorite',
data=self.jsonify(fav_data),
content_type="application/json",
headers=self.logged_in_headers())
self.assert_success(rv)
response = json.loads(rv.get_data(as_text=True))
self.assertEqual(u.id, response[0]["user_id"])
self.assertEqual(r.id, response[0]["resource_id"])
def test_remove_favorite_from_user(self):
self.test_add_favorite_to_user()
rv = self.app.delete('/api/user_favorite/%i' % 1,
headers=self.logged_in_headers())
self.assert_success(rv)
rv = self.app.get(
'/api/user/%i/favorite' % 1,
content_type="application/json",
headers=self.logged_in_headers())
self.assert_success(rv)
response = json.loads(rv.get_data(as_text=True))
self.assertEqual(0, len(response))
def test_delete_user_deletes_favorites(self):
u = self.construct_user()
r = self.construct_resource()
fav_data = [{"resource_id": r.id, "user_id": u.id, "type": 'resource'},
{"age_range": 'adult', "user_id": u.id, "type": 'age_range'},]
rv = self.app.post(
'/api/user_favorite',
data=self.jsonify(fav_data),
content_type="application/json",
headers=self.logged_in_headers())
self.assert_success(rv)
response = json.loads(rv.get_data(as_text=True))
self.assertEqual(2, len(response))
self.assertEqual(u.id, response[0]["user_id"])
rv = self.app.delete('api/user/%i' % u.id, content_type="application/json", headers=self.logged_in_headers())
self.assert_success(rv)
rv = self.app.get(
'/api/user_favorite',
content_type="application/json",
headers=self.logged_in_headers())
self.assert_success(rv)
response = json.loads(rv.get_data(as_text=True))
self.assertEqual(0, len(response))
def test_user_participant_count(self):
u1 = self.construct_user(email='1@sartography.com')
u2 = self.construct_user(email='2@sartography.com')
u3 = self.construct_user(email='3@sartography.com')
self.construct_participant(user=u1, relationship=Relationship.self_guardian)
self.construct_participant(user=u1, relationship=Relationship.dependent)
self.construct_participant(user=u2, relationship=Relationship.self_participant)
rv = self.app.get('api/user/%i' % u1.id, content_type="application/json", headers=self.logged_in_headers())
self.assert_success(rv)
response = json.loads(rv.get_data(as_text=True))
self.assertEqual(2, response['participant_count'])
rv = self.app.get('api/user/%i' % u2.id, content_type="application/json", headers=self.logged_in_headers())
self.assert_success(rv)
response = json.loads(rv.get_data(as_text=True))
self.assertEqual(1, response['participant_count'])
rv = self.app.get('api/user/%i' % u3.id, content_type="application/json", headers=self.logged_in_headers())
self.assert_success(rv)
response = json.loads(rv.get_data(as_text=True))
self.assertEqual(0, response['participant_count'])
def test_user_created_password(self):
pass_user = self.test_create_user_with_password()
self.assertEqual(pass_user.created_password(), True)
non_pass_user = self.construct_user()
self.assertEqual(non_pass_user.created_password(), False)
def test_user_identity(self):
u = self.construct_user(email='superuser@sartography.com')
self.construct_participant(user=u, relationship=Relationship.self_guardian)
self.assertEqual(u.identity(), 'self_guardian')
u2 = self.construct_user(email='superuser2@sartography.com')
self.construct_participant(user=u2, relationship=Relationship.self_professional)
self.assertEqual(u2.identity(), 'self_professional')
def test_percent_self_registration_complete(self):
u = self.construct_user(email='prof@sartography.com')
p = self.construct_participant(user=u, relationship=Relationship.self_participant)
iq = self.get_identification_questionnaire(p.id)
self.app.post('api/flow/self_intake/identification_questionnaire', data=self.jsonify(iq),
content_type="application/json",
follow_redirects=True, headers=self.logged_in_headers(u))
self.assertGreater(u.percent_self_registration_complete(), 0)
def test_user_participant_count_new_enum(self):
u1 = self.construct_user(email='1@sartography.com')
u4 = self.construct_user(email='4@sartography.com')
self.construct_participant(user=u1, relationship=Relationship.self_guardian)
self.construct_participant(user=u4, relationship=Relationship.self_interested)
rv = self.app.get('api/user/%i' % u1.id, content_type="application/json", headers=self.logged_in_headers())
self.assert_success(rv)
response = json.loads(rv.get_data(as_text=True))
self.assertEqual(1, response['participant_count'])
rv = self.app.get('api/user/%i' % u4.id, content_type="application/json", headers=self.logged_in_headers())
self.assert_success(rv)
response = json.loads(rv.get_data(as_text=True))
self.assertEqual(1, response['participant_count'])

View File

@ -505,7 +505,6 @@ ng2-pdfjs-viewer
MIT
ngx-device-detector
MIT
ngx-markdown
MIT

View File

@ -13,9 +13,9 @@
<meta property="og:description" content="Transform Outcomes. Together. A centralized system for autism research &amp; resources for individuals, families &amp; professionals. "/>
<meta name="twitter:text:title" content="Transform Outcomes. Together."/>
<link rel="icon" type="image/x-icon" href="/assets/favicon.ico">
<style type="text/css">@font-face{font-family:'Material Icons';font-style:normal;font-weight:400;src:url(https://fonts.gstatic.com/s/materialicons/v88/flUhRq6tzZclQEJ-Vdg-IuiaDsNa.woff) format('woff');}.material-icons{font-family:'Material Icons';font-weight:normal;font-style:normal;font-size:24px;line-height:1;letter-spacing:normal;text-transform:none;display:inline-block;white-space:nowrap;word-wrap:normal;direction:ltr;font-feature-settings:'liga';}@font-face{font-family:'Material Icons';font-style:normal;font-weight:400;src:url(https://fonts.gstatic.com/s/materialicons/v88/flUhRq6tzZclQEJ-Vdg-IuiaDsNcIhQ8tQ.woff2) format('woff2');}.material-icons{font-family:'Material Icons';font-weight:normal;font-style:normal;font-size:24px;line-height:1;letter-spacing:normal;text-transform:none;display:inline-block;white-space:nowrap;word-wrap:normal;direction:ltr;-webkit-font-feature-settings:'liga';-webkit-font-smoothing:antialiased;}</style>
<style type="text/css">@font-face{font-family:'Material Icons';font-style:normal;font-weight:400;src:url(https://fonts.gstatic.com/s/materialicons/v92/flUhRq6tzZclQEJ-Vdg-IuiaDsNa.woff) format('woff');}.material-icons{font-family:'Material Icons';font-weight:normal;font-style:normal;font-size:24px;line-height:1;letter-spacing:normal;text-transform:none;display:inline-block;white-space:nowrap;word-wrap:normal;direction:ltr;font-feature-settings:'liga';}@font-face{font-family:'Material Icons';font-style:normal;font-weight:400;src:url(https://fonts.gstatic.com/s/materialicons/v92/flUhRq6tzZclQEJ-Vdg-IuiaDsNcIhQ8tQ.woff2) format('woff2');}.material-icons{font-family:'Material Icons';font-weight:normal;font-style:normal;font-size:24px;line-height:1;letter-spacing:normal;text-transform:none;display:inline-block;white-space:nowrap;word-wrap:normal;direction:ltr;-webkit-font-feature-settings:'liga';-webkit-font-smoothing:antialiased;}</style>
<link rel="stylesheet" href="https://use.typekit.net/ato2zta.css">
<link rel="stylesheet" href="styles.35b435832e3d0f5e33f9.css"></head>
<link rel="stylesheet" href="styles.9398790610febe3da3f7.css"></head>
<body>
<app-root></app-root>
@ -26,6 +26,6 @@
var firstScriptTag = document.getElementsByTagName('script')[0];
firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);
</script>
<script src="runtime-es2015.a4dadbc03350107420a4.js" type="module"></script><script src="runtime-es5.a4dadbc03350107420a4.js" nomodule="" defer=""></script><script src="polyfills-es5.76dc8db8f9a88de44008.js" nomodule="" defer=""></script><script src="polyfills-es2015.a90b108734566404e685.js" type="module"></script><script src="main-es2015.8a00e4746542b63ca428.js" type="module"></script><script src="main-es5.8a00e4746542b63ca428.js" nomodule="" defer=""></script></body>
<script src="runtime-es2015.a4dadbc03350107420a4.js" type="module"></script><script src="runtime-es5.a4dadbc03350107420a4.js" nomodule="" defer=""></script><script src="polyfills-es5.e6fc9488c015b7e85901.js" nomodule="" defer=""></script><script src="polyfills-es2015.2f61e63415c6de3ac93d.js" type="module"></script><script src="main-es2015.0a8f56ae65d4d2315ae4.js" type="module"></script><script src="main-es5.0a8f56ae65d4d2315ae4.js" nomodule="" defer=""></script></body>
</html>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long