def test_instances(): users = [UserFactory(username='******'), UserFactory(username='******')] surfaces = [ SurfaceFactory(creator=users[0]), SurfaceFactory(creator=users[0]), ] topographies = [Topography1DFactory(surface=surfaces[0])] impl = AnalysisFunctionImplementationFactory() TopographyAnalysisFactory(function=impl.function, subject=topographies[0]) return users, surfaces, topographies
def test_recalculate(client): user = UserFactory() surface = SurfaceFactory(creator=user) topo1 = Topography1DFactory(surface=surface) topo2 = Topography1DFactory(surface=surface) func = AnalysisFunctionFactory(name="test function") impl = AnalysisFunctionImplementationFactory(function=func) client.force_login(user) with transaction.atomic(): # trigger "recalculate" for two topographies response = client.post( reverse('analysis:card-submit'), { 'function_id': func.id, 'subjects_ids_json': subjects_to_json([topo1, topo2]), 'function_kwargs_json': '{}' }, HTTP_X_REQUESTED_WITH='XMLHttpRequest') # we need an AJAX request assert response.status_code == 200 # # Analysis objects should be there and marked for the user # analysis1 = Analysis.objects.get(function=func, topography=topo1) analysis2 = Analysis.objects.get(function=func, topography=topo2) assert user in analysis1.users.all() assert user in analysis2.users.all()
def test_publication_link_in_xlsx_download(client, two_analyses_two_publications, handle_usage_statistics): (analysis1, analysis2, pub1, pub2) = two_analyses_two_publications # # Now two publications are involved in these analyses # download_url = reverse('analysis:download', kwargs=dict(ids=f"{analysis1.id},{analysis2.id}", card_view_flavor='plot', file_format='xlsx')) user = UserFactory(username='******') client.force_login(user) response = client.get(download_url) assert response.status_code == 200 tmp = tempfile.NamedTemporaryFile(suffix='.xlsx') # will be deleted automatically tmp.write(response.content) tmp.seek(0) xlsx = openpyxl.load_workbook(tmp.name) sheet = xlsx['INFORMATION'] col_B = sheet['B'] col_B_values = [str(c.value) for c in col_B] assert any(pub1.get_absolute_url() in v for v in col_B_values) assert any(pub2.get_absolute_url() in v for v in col_B_values)
def test_import_downloaded_surface_archive(client): username = '******' surface_name = "Test Surface for Import" surface_category = 'dum' user = UserFactory(username=username) surface = SurfaceFactory(creator=user, name=surface_name, category=surface_category) topo1 = Topography2DFactory(surface=surface, name='2D Measurement', size_x=10, size_y=10, unit='mm') topo2 = Topography1DFactory(surface=surface, name='1D Measurement', size_x=10, unit='µm') client.force_login(user) download_url = reverse('manager:surface-download', kwargs=dict(surface_id=surface.id)) response = client.get(download_url) # write downloaded data to temporary file and open with tempfile.NamedTemporaryFile(mode='wb') as zip_archive: zip_archive.write(response.content) zip_archive.seek(0) # reimport the surface call_command('import_surfaces', username, zip_archive.name) surface_copy = Surface.objects.get( description__icontains='imported from file') # # Check surface # assert surface_copy.name == surface.name assert surface_copy.category == surface.category assert surface.description in surface_copy.description assert surface_copy.tags == surface.tags # # Check imported topographies # assert surface_copy.num_topographies() == surface.num_topographies() for tc, t in zip(surface_copy.topography_set.order_by('name'), surface.topography_set.order_by('name')): # # Compare individual topographies # for attrname in [ 'name', 'description', 'size_x', 'size_y', 'height_scale', 'measurement_date', 'unit', 'creator', 'data_source', 'tags' ]: assert getattr(tc, attrname) == getattr(t, attrname)
def test_go_link(client, example_pub): user = UserFactory() client.force_login(user) response = client.get(reverse( 'publication:go', kwargs=dict(short_url=example_pub.short_url)), follow=True) assert response.status_code == 200 assert_in_content(response, example_pub.surface.name)
def stats_instances(db): user_1 = UserFactory() user_2 = UserFactory() surf_1A = SurfaceFactory(creator=user_1) surf_1B = SurfaceFactory(creator=user_1) surf_2A = SurfaceFactory(creator=user_2) topo_1Aa = Topography1DFactory(surface=surf_1A) topo_1Ab = Topography1DFactory(surface=surf_1A) topo_1Ba = Topography1DFactory(surface=surf_1B) topo_2Aa = Topography1DFactory(surface=surf_2A) func = AnalysisFunctionFactory() AnalysisFunctionImplementationFactory(function=func) TopographyAnalysisFactory(subject=topo_1Aa, function=func) TopographyAnalysisFactory(subject=topo_1Ab, function=func) TopographyAnalysisFactory(subject=topo_2Aa, function=func) return user_1, user_2, surf_1A
def test_login_statistics(client): today = datetime.date.today() user1 = UserFactory() user2 = UserFactory() client.force_login(user1) client.logout() client.force_login(user2) client.logout() # Signal is not called for some reason - calling signal handler manually track_user_login(sender=user2.__class__) # # There should be two logins for two users now for today # m = Metric.objects.USERS_LOGIN_COUNT s = StatisticByDate.objects.get(metric=m, date=today) assert s.value == 2 # # Check also for a specific day # yesterday = today - datetime.timedelta(1) with freeze_time(yesterday): client.force_login(user1) client.logout() track_user_login(sender=user1.__class__) # # There should be one login for one user for yesterday # s = StatisticByDate.objects.get(metric=m, date=yesterday) assert s.value == 1 # # There should be still those logins from today # s = StatisticByDate.objects.get(metric=m, date=today) assert s.value == 2
def test_warnings_for_different_arguments(client, handle_usage_statistics): user = UserFactory() surf1 = SurfaceFactory(creator=user) surf2 = SurfaceFactory(creator=user) topo1a = Topography1DFactory(surface=surf1) topo1b = Topography1DFactory(surface=surf1) topo2a = Topography1DFactory(surface=surf2) func = AnalysisFunctionFactory() topo_impl = AnalysisFunctionImplementationFactory(function=func, subject_type=topo1a.get_content_type(), code_ref='topography_analysis_function_for_tests') surf_impl = AnalysisFunctionImplementationFactory(function=func, subject_type=surf1.get_content_type(), code_ref='surface_analysis_function_for_tests') # # Generate analyses for topographies with differing arguments # kwargs_1a = pickle.dumps(dict(a=1, b=2)) kwargs_1b = pickle.dumps(dict(a=1, b=3)) # differing from kwargs_1a! ana1a = TopographyAnalysisFactory(subject=topo1a, function=func, kwargs=kwargs_1a) ana1b = TopographyAnalysisFactory(subject=topo1b, function=func, kwargs=kwargs_1b) ana2a = TopographyAnalysisFactory(subject=topo2a, function=func) # default arguments # # Generate analyses for surfaces with differing arguments # kwargs_1 = pickle.dumps(dict(a=1, c=2)) kwargs_2 = pickle.dumps(dict(a=1, c=3)) # differing from kwargs_1a! ana1 = SurfaceAnalysisFactory(subject=surf1, function=func, kwargs=kwargs_1) ana2 = SurfaceAnalysisFactory(subject=surf2, function=func, kwargs=kwargs_2) client.force_login(user) # # request card, there should be warnings, one for topographies and one for surfaces # response = client.post(reverse("analysis:card"), data={ 'subjects_ids_json': subjects_to_json([topo1a, topo1b, topo2a, surf1, surf2]), 'function_id': func.id, 'card_id': "card-1", 'template_flavor': 'list' }, HTTP_X_REQUESTED_WITH='XMLHttpRequest', follow=True) assert response.status_code == 200 assert_in_content(response, "Arguments for this analysis function differ among chosen " "subjects of type 'manager | topography'") assert_in_content(response, "Arguments for this analysis function differ among chosen " "subjects of type 'manager | surface'")
def test_request_analysis(mocker): """Make sure analysis objects were created with correct parameters""" user = UserFactory() af = AnalysisFunctionFactory.create(name="somefunc") afimpl = AnalysisFunctionImplementationFactory.create(function=af) m = mocker.patch('topobank.analysis.models.AnalysisFunctionImplementation.python_function', new_callable=mocker.PropertyMock) m.return_value = lambda: lambda topography, a, b, bins=15, window='hann': None # defining default parameters here # mocker.patch('topobank.analysis.models.Analysis.objects.create') # mocker.patch('django.db.models.QuerySet.delete') # we don't need to test with delete here mocker.patch('topobank.taskapp.tasks.perform_analysis.delay') # we don't want to calculate anything topo = Topography1DFactory() # just an abbreviation def assert_correct_args(analysis, expected_kwargs): kwargs = pickle.loads(analysis.kwargs) assert expected_kwargs == kwargs assert user in analysis.users.all() # make sure the user has been set # test case 1 analysis = request_analysis(user, af, topo, a=1, b=2) assert_correct_args(analysis, dict(a=1, b=2, bins=15, window='hann')) # check default parameters in database # test case 2 analysis = request_analysis(user, af, topo, 1, 2, bins=10) assert_correct_args(analysis, dict(a=1, b=2, bins=10, window='hann')) # test case 3 analysis = request_analysis(user, af, topo, 2, 1, window='hamming', bins=5) assert_correct_args(analysis, dict(a=2, b=1, bins=5, window='hamming'))
def test_card_templates_simple(client, mocker, handle_usage_statistics): """Check whether correct template is selected.""" # # Create database objects # password = "******" user = UserFactory(password=password) func1 = AnalysisFunctionFactory(card_view_flavor='power spectrum') topo1 = Topography1DFactory() # An analysis function with card_view_flavor='power spectrum' # should use the template which is needed for PowerSpectrumCardView. # # For the "detail" mode, there is an own template for power spectrum, # which should be returned. The the "list" mode, there is no # special template. Therefore, since "PowerSpectrumCardView" is # derived from the "PlotCardView" so far, the resulting # template should be 'plot_card_list.html'. assert client.login(username=user.username, password=password) response = client.post( reverse('analysis:card'), data={ 'function_id': func1.id, 'card_id': 'card', 'template_flavor': 'list', 'subjects_ids_json': subjects_to_json([topo1]), }, HTTP_X_REQUESTED_WITH='XMLHttpRequest') # we need an AJAX request assert response.template_name == ['analysis/plot_card_list.html'] response = client.post( reverse('analysis:card'), data={ 'function_id': func1.id, 'card_id': 'card', 'template_flavor': 'detail', 'subjects_ids_json': subjects_to_json([topo1]), }, HTTP_X_REQUESTED_WITH='XMLHttpRequest') # we need an AJAX request assert response.template_name == [ 'analysis/powerspectrum_card_detail.html' ]
def example_pub(): """Fixture returning a publication which can be used as test example""" user = UserFactory() authors = "Alice, Bob" publication_date = datetime.date(2020, 1, 1) description = "This is a nice surface for testing." name = "Diamond Structure" surface = SurfaceFactory(name=name, creator=user, description=description) surface.tags = ['diamond'] with freeze_time(publication_date): pub = surface.publish('cc0-1.0', authors) return pub
def test_publication_link_in_txt_download(client, two_analyses_two_publications, handle_usage_statistics): (analysis1, analysis2, pub1, pub2) = two_analyses_two_publications # # Now two publications are involved in these analyses # download_url = reverse('analysis:download', kwargs=dict(ids=f"{analysis1.id},{analysis2.id}", card_view_flavor='plot', file_format='txt')) user = UserFactory(username='******') client.force_login(user) response = client.get(download_url) assert response.status_code == 200 txt = response.content.decode() assert pub1.get_absolute_url() in txt assert pub2.get_absolute_url() in txt
def test_renewal_on_topography_detrend_mode_change(client, mocker, django_capture_on_commit_callbacks): """Check whether thumbnail is renewed if detrend mode changes for a topography """ from ..models import Topography renew_squeezed_mock = mocker.patch('topobank.manager.views.renew_squeezed_datafile.si') renew_topo_analyses_mock = mocker.patch('topobank.manager.views.renew_analyses_related_to_topography.si') renew_topo_images_mock = mocker.patch('topobank.manager.views.renew_topography_images.si') user = UserFactory() surface = SurfaceFactory(creator=user) topo = Topography1DFactory(surface=surface, size_y=1, detrend_mode='center') client.force_login(user) with django_capture_on_commit_callbacks(execute=True) as callbacks: response = client.post(reverse('manager:topography-update', kwargs=dict(pk=topo.pk)), data={ 'save-stay': 1, # we want to save, but stay on page 'surface': surface.pk, 'data_source': 0, 'name': topo.name, 'measurement_date': topo.measurement_date, 'description': "something", 'size_x': 1, 'size_y': 1, 'unit': 'nm', 'height_scale': 1, 'detrend_mode': 'height', 'instrument_type': Topography.INSTRUMENT_TYPE_UNDEFINED, 'has_undefined_data': False, 'fill_undefined_data_mode': Topography.FILL_UNDEFINED_DATA_MODE_NOFILLING, }, follow=True) # we just check here that the form is filled completely, otherwise the thumbnail would not be recreated too assert_no_form_errors(response) assert response.status_code == 200 assert len(callbacks) == 1 # single chain for squeezed file, thumbnail and for analyses assert renew_topo_images_mock.called assert renew_topo_analyses_mock.called assert renew_squeezed_mock.called # was directly called, not as callback from commit
def test_go_download_link(client, example_pub, handle_usage_statistics): user = UserFactory() client.force_login(user) response = client.get(reverse( 'publication:go-download', kwargs=dict(short_url=example_pub.short_url)), follow=True) assert response.status_code == 200 surface = example_pub.surface # open zip file and look into meta file, there should be two surfaces and three topographies with zipfile.ZipFile(BytesIO(response.content)) as zf: meta_file = zf.open('meta.yml') meta = yaml.safe_load(meta_file) assert len(meta['surfaces']) == 1 assert len( meta['surfaces'][0]['topographies']) == surface.num_topographies() assert meta['surfaces'][0]['name'] == surface.name assert_in_content(response, example_pub.surface.name)
def test_card_templates_for_power_spectrum(client, mocker, handle_usage_statistics): # # Create database objects # password = "******" user = UserFactory(password=password) func1 = AnalysisFunctionFactory(card_view_flavor='power spectrum') topo1 = Topography1DFactory() assert client.login(username=user.username, password=password) response = client.post( reverse('analysis:card'), data={ 'function_id': func1.id, 'card_id': 'card', 'template_flavor': 'list', 'subjects_ids_json': subjects_to_json([topo1]), }, HTTP_X_REQUESTED_WITH='XMLHttpRequest') # we need an AJAX request # we get the inherited "plot" template with "list" flavor, because power spectrum # hasn't got an own template with "list" flavor assert response.template_name == ['analysis/plot_card_list.html'] response = client.post( reverse('analysis:card'), data={ 'function_id': func1.id, 'card_id': 'card', 'template_flavor': 'detail', 'subjects_ids_json': subjects_to_json([topo1]), }, HTTP_X_REQUESTED_WITH='XMLHttpRequest') # we need an AJAX request # for the power spectrum detail card there should be an own template assert response.template_name == [ 'analysis/powerspectrum_card_detail.html' ]
def test_unmark_other_analyses_during_request_analysis(mocker): """ When requesting an analysis with new arguments, the old analyses should still exist (at the moment, maybe delete later analyses without user), but only the latest one should be marked as "used" by the user """ user = UserFactory() m = mocker.patch('topobank.analysis.models.AnalysisFunctionImplementation.python_function', new_callable=mocker.PropertyMock) m.return_value = lambda: lambda topography, a, b, bins=15, window='hann': None af = AnalysisFunctionFactory(name="somefunc") afim = AnalysisFunctionImplementationFactory(function=af) topo = Topography1DFactory() a1 = TopographyAnalysisFactory(subject=topo, function=af, kwargs=pickle.dumps(dict(a=9, b=19)), users=[]) a2 = TopographyAnalysisFactory(subject=topo, function=af, kwargs=pickle.dumps(dict(a=29, b=39)), users=[user]) a3 = request_analysis(user, af, topo, a=1, b=2) # # Now there are three analyses for af+topo # assert Analysis.objects.filter(topography=topo, function=af).count() == 3 # # Only one analysis is marked for user 'user' # analyses = Analysis.objects.filter(topography=topo, function=af, users__in=[user]) assert len(analyses) == 1 assert a1 not in analyses assert a2 not in analyses assert a3 in analyses assert pickle.loads(analyses[0].kwargs) == dict(a=1, b=2, bins=15, window='hann')
def test_sheets(client, handle_usage_statistics): user = UserFactory() surface = SurfaceFactory(creator=user) topo1 = Topography2DFactory(surface=surface) topo2 = Topography2DFactory(surface=surface) save_landing_page_statistics( ) # save current state for users, surfaces, .. client.force_login(user) # not counted as login here client.get(reverse('manager:surface-detail', kwargs=dict(pk=surface.pk))) # Now there is one surface view # tried to use freezegun.freeze_time here, # but openpyxl had problems with FakeDate class call_command('export_usage_statistics') df = pd.read_excel("usage_statistics.xlsx", sheet_name="summary", engine='openpyxl') assert df.columns[0] == 'month' assert df.iloc[0, 1:].tolist() == [0, 1, 0, 0, 1, 1, 2, 0]
def test_plot_card_if_no_successful_topo_analysis(client, handle_usage_statistics): # # Create database objects # password = "******" user = UserFactory(password=password) topography_ct = ContentType.objects.get_for_model(Topography) surface_ct = ContentType.objects.get_for_model(Surface) func1 = AnalysisFunctionFactory(card_view_flavor='power spectrum') AnalysisFunctionImplementationFactory(function=func1, subject_type=topography_ct) AnalysisFunctionImplementationFactory(function=func1, subject_type=surface_ct) surf = SurfaceFactory(creator=user) topo = Topography1DFactory(surface=surf) # also generates the surface # There is a successful surface analysis, but no successful topography analysis SurfaceAnalysisFactory(task_state='su', subject_id=topo.surface.id, subject_type_id=surface_ct.id, function=func1, users=[user]) # add a failed analysis for the topography TopographyAnalysisFactory(task_state='fa', subject_id=topo.id, subject_type_id=topography_ct.id, function=func1, users=[user]) assert Analysis.objects.filter(function=func1, subject_id=topo.id, subject_type_id=topography_ct.id, task_state='su').count() == 0 assert Analysis.objects.filter(function=func1, subject_id=topo.id, subject_type_id=topography_ct.id, task_state='fa').count() == 1 assert Analysis.objects.filter(function=func1, subject_id=topo.surface.id, subject_type_id=surface_ct.id, task_state='su').count() == 1 # login and request plot card view assert client.login(username=user.username, password=password) response = client.post( reverse('analysis:card'), data={ 'function_id': func1.id, 'card_id': 'card', 'template_flavor': 'list', 'subjects_ids_json': subjects_to_json([topo, topo.surface ]), # also request results for surface here }, HTTP_X_REQUESTED_WITH='XMLHttpRequest') # we need an AJAX request # should return without errors assert response.status_code == 200
def user_bob(): return UserFactory(username='******', password=PASSWORD, name='Bob Marley')
def user_alice(): return UserFactory(username='******', password=PASSWORD, name='Alice Wonderland')
def test_latest_analyses_if_no_analyses(): user = UserFactory() function = AnalysisFunctionFactory() assert get_latest_analyses(user, function, []).count() == 0
def test_view_shared_analysis_results(client, handle_usage_statistics): password = '******' # # create database objects # user1 = UserFactory(password=password) user2 = UserFactory(password=password) surface1 = SurfaceFactory(creator=user1) surface2 = SurfaceFactory(creator=user2) # create topographies + functions + analyses func1 = AnalysisFunctionFactory() impl1 = AnalysisFunctionImplementationFactory(function=func1) # func2 = AnalysisFunctionFactory() # Two topographies for surface1 topo1a = Topography1DFactory(surface=surface1, name='topo1a') topo1b = Topography1DFactory(surface=surface1, name='topo1b') # One topography for surface2 topo2a = Topography1DFactory(surface=surface2, name='topo2a') # analyses, differentiate by start time analysis1a_1 = TopographyAnalysisFactory(subject=topo1a, function=func1, start_time=datetime.datetime(2019, 1, 1, 12)) analysis1b_1 = TopographyAnalysisFactory(subject=topo1b, function=func1, start_time=datetime.datetime(2019, 1, 1, 13)) analysis2a_1 = TopographyAnalysisFactory(subject=topo2a, function=func1, start_time=datetime.datetime(2019, 1, 1, 14)) # Function should have three analyses, all successful (the default when using the factory) assert func1.analysis_set.count() == 3 assert all(a.task_state == 'su' for a in func1.analysis_set.all()) # user2 shares surfaces, so user 1 should see surface1+surface2 surface2.share(user1) # # Now we change to the analysis card view and look what we get # assert client.login(username=user1.username, password=password) response = client.post(reverse("analysis:card"), data={ 'subjects_ids_json': subjects_to_json([topo1a, topo1b, topo2a]), 'function_id': func1.id, 'card_id': 1, 'template_flavor': 'list' }, HTTP_X_REQUESTED_WITH='XMLHttpRequest', follow=True) # Function should still have three analyses, all successful (the default when using the factory) assert func1.analysis_set.count() == 3 assert all(a.task_state == 'su' for a in func1.analysis_set.all()) assert response.status_code == 200 # We should see start times of all three topographies assert_in_content(response, '2019-01-01 12:00:00') # topo1a assert_in_content(response, '2019-01-01 13:00:00') # topo1b assert_in_content(response, '2019-01-01 14:00:00') # topo2a client.logout() # # user 2 cannot access results from topo1, it is not shared # assert client.login(username=user2.username, password=password) response = client.post(reverse("analysis:card"), data={ 'subjects_ids_json': subjects_to_json([topo1a, topo1b, topo2a]), 'function_id': func1.id, 'card_id': 1, 'template_flavor': 'list' }, HTTP_X_REQUESTED_WITH='XMLHttpRequest', follow=True) assert response.status_code == 200 assert_not_in_content(response, '2019-01-01 12:00:00') # topo1a assert_not_in_content(response, '2019-01-01 13:00:00') # topo1b assert_in_content(response, '2019-01-01 14:00:00') # topo2a client.logout()