from __future__ import absolute_import from __future__ import division from __future__ import print_function from __future__ import unicode_literals #from __future__ import print_function from .statements import * from django.utils import timezone nu = datetime.datetime.now(tz=timezone.utc) import rowers.courses as courses import rowers.plannedsessions as plannedsessions @override_settings(TESTING=True) class VirtualEventViewTest(MyTestCase): def setUp(self): # Main User self.u = UserFactory() self.r = Rower.objects.create(user=self.u, birthdate=faker.profile()['birthdate'], gdproptin=True, ftpset=True, surveydone=True,gdproptindate=timezone.now(), rowerplan='pro') self.c = Client() self.user_workouts = WorkoutFactory.create_batch(5, user=self.r) self.factory = RequestFactory() self.password = faker.word() self.u.set_password(self.password) self.u.save() # Piet self.upiet = UserFactory(username='piet') self.rpiet = Rower.objects.create(user=self.upiet, birthdate=faker.profile()['birthdate'], gdproptin=True, ftpset=True,surveydone=True,gdproptindate=timezone.now(), rowerplan='basic') self.piet_workouts = WorkoutFactory.create_batch(5, user=self.rpiet) self.passwordpiet = faker.word() self.upiet.set_password(self.passwordpiet) self.upiet.save() # Klaas self.uklaas = UserFactory(username='klaas') self.rklaas = Rower.objects.create(user=self.uklaas, birthdate=faker.profile()['birthdate'], gdproptin=True, ftpset=True,surveydone=True,gdproptindate=timezone.now(), rowerplan='basic') self.klaas_workouts = WorkoutFactory.create_batch(5, user=self.rklaas) self.passwordklaas = faker.word() self.uklaas.set_password(self.passwordklaas) self.uklaas.save() # Henk self.uhenk = UserFactory(username='henk') self.rhenk = Rower.objects.create(user=self.uhenk, birthdate=faker.profile()['birthdate'], gdproptin=True, ftpset=True,surveydone=True,gdproptindate=timezone.now(), rowerplan='basic') self.henk_workouts = WorkoutFactory.create_batch(5, user=self.rhenk) self.passwordhenk = faker.word() self.uhenk.set_password(self.passwordhenk) self.uhenk.save() # a few handy dates yesterday = nu-datetime.timedelta(days=1) tomorrow = nu+datetime.timedelta(days=1) nextweek = nu+datetime.timedelta(days=7) intwoweeks = nu+datetime.timedelta(days=14) lastweek = nu-datetime.timedelta(days=7) self.yesterday = yesterday self.tomorrow = tomorrow self.nextweek = nextweek self.lastweek = lastweek self.intwoweeks = intwoweeks # erg races self.openergrace = VirtualRace.objects.create( name = faker.word(), startdate = tomorrow.date(), enddate = nextweek.date(), start_time = nu.time(), end_time = nu.time(), preferreddate = tomorrow.date(), comment = faker.text(), sessiontype = 'indoorrace', sessionunit = 'm', sessionmode = 'distance', sessionvalue = 1000, course = None, timezone = 'UTC', evaluation_closure = nextweek, registration_closure = tomorrow, country = 'Indoor', manager = self.u ) self.submitergrace = VirtualRace.objects.create( name = faker.word(), startdate = yesterday.date(), enddate = nextweek.date(), start_time = nu.time(), end_time = nu.time(), preferreddate = tomorrow.date(), comment = faker.text(), sessiontype = 'indoorrace', sessionunit = 'm', sessionmode = 'distance', sessionvalue = 1000, course = None, timezone = 'UTC', evaluation_closure = nextweek, registration_closure = tomorrow, country = 'Indoor', manager = self.u ) # register Piet for submitergrace self.pietrecord = IndoorVirtualRaceResult.objects.create( userid = self.rpiet.id, teamname = faker.word(), race = self.submitergrace, username = u'{f} {l}'.format( f = self.upiet.first_name, l = self.upiet.last_name ), weightcategory='hwt', adaptiveclass='None', duration=datetime.time(0,0), boatclass='rower', coursecompleted=False, sex='male' ) result = plannedsessions.add_rower_race(self.rpiet,self.submitergrace) # register Henk for submitergrace self.henkrecord = IndoorVirtualRaceResult.objects.create( userid = self.rhenk.id, teamname = faker.word(), race = self.submitergrace, username = u'{f} {l}'.format( f = self.uhenk.first_name, l = self.uhenk.last_name ), weightcategory='hwt', adaptiveclass='None', duration=datetime.time(0,0), boatclass='rower', coursecompleted=False, ) result = plannedsessions.add_rower_race(self.rhenk,self.submitergrace) # course coursefile = 'rowers/tests/testdata/course_22.kml' coursefiletemp = 'rowers/tests/testdata/temp/course.kml' copy(coursefile,coursefiletemp) cs = courses.kmltocourse(coursefiletemp) for course in cs: cname = course['name'] cnotes = course['description'] polygons = course['polygons'] course = courses.createcourse(self.r,cname,polygons,notes=cnotes) self.course = course # otw races self.openotwrace = VirtualRace.objects.create( name = faker.word(), startdate = tomorrow.date(), enddate = nextweek.date(), start_time = nu.time(), end_time = nu.time(), preferreddate = tomorrow.date(), comment = faker.text(), sessiontype = 'race', course = self.course, timezone = 'UTC', evaluation_closure = nextweek, registration_closure = tomorrow, country = self.course.country, manager = self.u ) # course self.submitotwrace = VirtualRace.objects.create( name = faker.word(), startdate = yesterday.date(), enddate = nextweek.date(), start_time = nu.time(), end_time = nu.time(), preferreddate = tomorrow.date(), comment = faker.text(), sessiontype = 'race', course = self.course, timezone = 'UTC', evaluation_closure = nextweek, registration_closure = tomorrow, country = self.course.country, manager = self.u ) # register Piet for submitotwgrace self.pietotwrecord = VirtualRaceResult.objects.create( userid = self.rpiet.id, teamname = faker.word(), race = self.submitotwrace, username = u'{f} {l}'.format( f = self.upiet.first_name, l = self.upiet.last_name ), weightcategory='hwt', adaptiveclass='None', duration=datetime.time(0,0), boatclass='water', coursecompleted=False, ) result = plannedsessions.add_rower_race(self.rpiet,self.submitotwrace) # race,with entrants, open for submission # workouts # OTW matching course result = get_random_file(filename='rowers/tests/testdata/uherskehradiste_otw.csv') self.wuh_otw = WorkoutFactory(user=self.rpiet, csvfilename=result['filename'], starttime=result['starttime'], startdatetime=result['startdatetime'], duration=result['duration'], distance=result['totaldist'], workouttype = 'water', ) # OTW not matching course result = get_random_file(filename='rowers/tests/testdata/onwater2.csv') self.wwater = WorkoutFactory(user=self.r, csvfilename=result['filename'], starttime=result['starttime'], startdatetime=result['startdatetime'], duration=result['duration'], distance=result['totaldist'], workouttype = 'water', ) # OTE 1k result = get_random_file(filename='rowers/tests/testdata/duzend_ote.csv') self.wduzend_r = WorkoutFactory(user=self.r, csvfilename=result['filename'], starttime=nu.time(), startdatetime=nu, duration=result['duration'], distance=result['totaldist'], workouttype = 'rower', ) # Piet result = get_random_file(filename='rowers/tests/testdata/duzend_ote.csv') self.wduzend_rpiet = WorkoutFactory(user=self.rpiet, csvfilename=result['filename'], starttime=nu.time(), startdatetime=nu, duration=result['duration'], distance=result['totaldist'], workouttype = 'rower', ) # Klaas result = get_random_file(filename='rowers/tests/testdata/duzend_ote.csv') self.wduzend_rklaas = WorkoutFactory(user=self.rklaas, csvfilename=result['filename'], starttime=nu.time(), startdatetime=nu, duration=result['duration'], distance=result['totaldist'], workouttype = 'rower', ) # Henk result = get_random_file(filename='rowers/tests/testdata/duzend_ote.csv') self.wduzend_rhenk = WorkoutFactory(user=self.rhenk, csvfilename=result['filename'], starttime=nu.time(), startdatetime=nu, duration=result['duration'], distance=result['totaldist'], workouttype = 'rower', ) # OTE different result = get_random_file() self.wother_rpiet = WorkoutFactory(user=self.rpiet, csvfilename=result['filename'], starttime=nu.time(), startdatetime=nu, duration=result['duration'], distance=result['totaldist'], workouttype='rower', ) def tearDown(self): try: os.remove('rowers/tests/testdata/temp/course.kml') except (FileNotFoundError, OSError, IOError): pass #Scenarios # races page @patch('django.contrib.gis.geoip2.GeoIP2.city', side_effect=mocked_requests) @patch('rowers.courses.geocoder.osm', side_effect=mock_osm) def test_races_view(self, mock_get, mock_osm): login = self.c.login(username=self.u.username, password=self.password) self.assertTrue(login) url = reverse('virtualevents_view') response = self.c.get(url) self.assertEqual(response.status_code,200) # post form_data = { 'country':'All', 'regattatype':'upcoming' } form = VirtualRaceSelectForm(form_data) self.assertTrue(form.is_valid()) response = self.c.post(url,form_data) self.assertEqual(response.status_code,200) form_data = { 'country':'All', 'regattatype':'ongoing' } form = VirtualRaceSelectForm(form_data) self.assertTrue(form.is_valid()) response = self.c.post(url,form_data) self.assertEqual(response.status_code,200) form_data = { 'country':'All', 'regattatype':'previous' } form = VirtualRaceSelectForm(form_data) self.assertTrue(form.is_valid()) response = self.c.post(url,form_data) self.assertEqual(response.status_code,200) # set up new OTE race @patch('django.contrib.gis.geoip2.GeoIP2.city', side_effect=mocked_requests) def test_new_indoorrace(self, mock_get): login = self.c.login(username=self.u.username, password=self.password) self.assertTrue(login) url = reverse('indoorvirtualevent_create_view') response = self.c.get(url) self.assertEqual(response.status_code,200) form_data = { 'startdate':self.tomorrow.strftime('%Y-%m-%d'), 'start_time':'8:00:00', 'enddate': self.nextweek.strftime('%Y-%m-%d'), 'end_time':'8:00:00', 'comment': faker.text(), 'sessionunit': 'm', 'sessionvalue': 500, 'name': faker.word(), 'registration_form':'deadline', 'registration_closure_0': self.nextweek.strftime('%Y-%m-%d'), 'registration_closure_1': self.nextweek.strftime('%H:%M:%S'), 'evaluation_closure_0': self.intwoweeks.strftime('%Y-%m-%d'), 'evaluation_closure_1': self.intwoweeks.strftime('%H:%M:%S'), 'contact_phone': '', 'contact_email': self.u.email, 'timezone': 'UTC' } form = IndoorVirtualRaceForm(form_data) if not form.is_valid(): print(form.errors) self.assertTrue(form.is_valid()) response = self.c.post(url,form_data,follow=True) self.assertRedirects(response, expected_url = reverse('virtualevents_view'), status_code=302,target_status_code=200) # set up new OTE race def test_new_fastestrace(self): login = self.c.login(username=self.u.username, password=self.password) self.assertTrue(login) url = reverse('fastestvirtualevent_create_view') response = self.c.get(url) self.assertEqual(response.status_code,200) form_data = { 'startdate':self.tomorrow.strftime('%Y-%m-%d'), 'start_time':'8:00:00', 'enddate': self.nextweek.strftime('%Y-%m-%d'), 'end_time':'8:00:00', 'comment': faker.text(), 'sessionunit': 'm', 'sessionvalue': 500, 'name': faker.word(), 'registration_form':'deadline', 'registration_closure_0': self.nextweek.strftime('%Y-%m-%d'), 'registration_closure_1': self.nextweek.strftime('%H:%M:%S'), 'evaluation_closure_0': self.intwoweeks.strftime('%Y-%m-%d'), 'evaluation_closure_1': self.intwoweeks.strftime('%H:%M:%S'), 'contact_phone': '', 'contact_email': self.u.email, 'timezone': 'UTC' } form = IndoorVirtualRaceForm(form_data) if not form.is_valid(): print(form.errors) self.assertTrue(form.is_valid()) response = self.c.post(url,form_data,follow=True) self.assertRedirects(response, expected_url = reverse('virtualevents_view'), status_code=302,target_status_code=200) # set up new OTE race @patch('django.contrib.gis.geoip2.GeoIP2.city', side_effect=mocked_requests) def test_edit_indoorrace(self, mock_get): login = self.c.login(username=self.u.username, password=self.password) self.assertTrue(login) url = reverse('indoorvirtualevent_edit_view', kwargs={'id':self.openergrace.id}) response = self.c.get(url) self.assertEqual(response.status_code,200) form_data = { 'startdate':self.tomorrow.strftime('%Y-%m-%d'), 'start_time':'8:00:00', 'enddate': self.nextweek.strftime('%Y-%m-%d'), 'end_time':'8:00:00', 'comment': faker.text(), 'sessionunit': 'm', 'sessionvalue': 500, 'name': faker.word(), 'registration_form':'deadline', 'registration_closure_0': self.nextweek.strftime('%Y-%m-%d'), 'registration_closure_1': self.nextweek.strftime('%H:%M:%S'), 'evaluation_closure_0': self.intwoweeks.strftime('%Y-%m-%d'), 'evaluation_closure_1': self.intwoweeks.strftime('%H:%M:%S'), 'contact_phone': '', 'contact_email': self.u.email, 'timezone': 'UTC' } form = IndoorVirtualRaceForm(form_data) if not form.is_valid(): print(form.errors) self.assertTrue(form.is_valid()) response = self.c.post(url,form_data,follow=True) self.assertRedirects(response, expected_url = reverse('virtualevent_view', kwargs={'id':self.openergrace.id}), status_code=302,target_status_code=200) # set up new otw race # set up new OTE race @patch('django.contrib.gis.geoip2.GeoIP2.city', side_effect=mocked_requests) def test_new_race(self, mock_get): login = self.c.login(username=self.u.username, password=self.password) self.assertTrue(login) url = reverse('virtualevent_create_view') response = self.c.get(url) self.assertEqual(response.status_code,200) form_data = { 'startdate':self.tomorrow.strftime('%Y-%m-%d'), 'start_time':'8:00:00', 'enddate': self.nextweek.strftime('%Y-%m-%d'), 'end_time':'8:00:00', 'comment': faker.text(), 'course': str(self.course.id), 'name': faker.word(), 'registration_form':'deadline', 'registration_closure_0': self.nextweek.strftime('%Y-%m-%d'), 'registration_closure_1': self.nextweek.strftime('%H:%M:%S'), 'evaluation_closure_0': self.intwoweeks.strftime('%Y-%m-%d'), 'evaluation_closure_1': self.intwoweeks.strftime('%H:%M:%S'), 'contact_phone': '', 'contact_email': self.u.email, } form = VirtualRaceForm(form_data) if not form.is_valid(): print(form.errors) self.assertTrue(form.is_valid()) response = self.c.post(url,form_data,follow=True) self.assertRedirects(response, expected_url = reverse('virtualevents_view'), status_code=302,target_status_code=200) @patch('django.contrib.gis.geoip2.GeoIP2.city', side_effect=mocked_requests) def test_edit_race(self, mock_get): login = self.c.login(username=self.u.username, password=self.password) self.assertTrue(login) url = reverse('virtualevent_edit_view',kwargs={'id':self.openotwrace.id}) response = self.c.get(url) self.assertEqual(response.status_code,200) form_data = { 'startdate':self.tomorrow.strftime('%Y-%m-%d'), 'start_time':'8:00:00', 'enddate': self.nextweek.strftime('%Y-%m-%d'), 'end_time':'8:00:00', 'comment': faker.text(), 'course': str(self.course.id), 'name': faker.word(), 'registration_form':'deadline', 'registration_closure_0': self.nextweek.strftime('%Y-%m-%d'), 'registration_closure_1': self.nextweek.strftime('%H:%M:%S'), 'evaluation_closure_0': self.intwoweeks.strftime('%Y-%m-%d'), 'evaluation_closure_1': self.intwoweeks.strftime('%H:%M:%S'), 'contact_phone': '', 'contact_email': self.u.email, } form = VirtualRaceForm(form_data) if not form.is_valid(): print(form.errors) self.assertTrue(form.is_valid()) response = self.c.post(url,form_data,follow=True) self.assertRedirects(response, expected_url = reverse('virtualevent_view', kwargs={'id':self.openotwrace.id}), status_code=302,target_status_code=200) # view @patch('django.contrib.gis.geoip2.GeoIP2.city', side_effect=mocked_requests) def test_race_view(self, mock_get): login = self.c.login(username=self.u.username, password=self.password) self.assertTrue(login) url = reverse('virtualevent_view',kwargs={'id':self.openotwrace.id}) response = self.c.get(url) self.assertEqual(response.status_code,200) url = reverse('virtualevent_view',kwargs={'id':self.submitotwrace.id}) response = self.c.get(url) self.assertEqual(response.status_code,200) url = reverse('virtualevent_view',kwargs={'id':self.openergrace.id}) response = self.c.get(url) self.assertEqual(response.status_code,200) url = reverse('virtualevent_view',kwargs={'id':self.submitergrace.id}) response = self.c.get(url) self.assertEqual(response.status_code,200) # register # withdraw @patch('django.contrib.gis.geoip2.GeoIP2.city', side_effect=mocked_requests) def test_register_race_view(self, mock_get): login = self.c.login(username=self.u.username, password=self.password) self.assertTrue(login) url = reverse('indoorvirtualevent_register_view', kwargs={'id':self.openergrace.id}) response = self.c.get(url) self.assertEqual(response.status_code,200) form_data = { 'teamname': faker.word(), 'weightcategory': 'hwt', 'adaptiveclass': 'None', 'age': 46, 'boatclass':'rower' } form = IndoorVirtualRaceResultForm(form_data) if not form.is_valid(): print(form.errors) self.assertTrue(form.is_valid()) response = self.c.post(url,form_data,follow=True) expected_url = reverse('virtualevent_view',kwargs={'id':self.openergrace.id}) self.assertRedirects(response, expected_url=expected_url, status_code=302,target_status_code=200) login = self.c.login(username=self.upiet.username, password=self.passwordpiet) self.assertTrue(login) url = reverse('indoorvirtualevent_register_view',kwargs={'id':self.openergrace.id}) response = self.c.get(url) self.assertEqual(response.status_code,200) form_data = { 'teamname': faker.word(), 'weightcategory': 'hwt', 'adaptiveclass': 'None', 'age': 46, 'boatclass':'rower' } form = IndoorVirtualRaceResultForm(form_data) if not form.is_valid(): print(form.errors) self.assertTrue(form.is_valid()) response = self.c.post(url,form_data,follow=True) expected_url = reverse('virtualevent_view',kwargs={'id':self.openergrace.id}) self.assertRedirects(response, expected_url=expected_url, status_code=302,target_status_code=200) url = reverse('indoorvirtualevent_toggle_email_view', kwargs={'id':self.openergrace.id}) response = self.c.get(url,follow=True) self.assertEqual(response.status_code,200) url = reverse('virtualevent_withdraw_view',kwargs={'id':self.openergrace.id}) response = self.c.get(url,follow=True) expected_url = reverse('virtualevent_view',kwargs={'id':self.openergrace.id}) self.assertRedirects(response, expected_url=expected_url, status_code=302,target_status_code=200) # add boat (OTW) @patch('django.contrib.gis.geoip2.GeoIP2.city', side_effect=mocked_requests) def test_register_otwrace_view(self, mock_get): login = self.c.login(username=self.u.username, password=self.password) self.assertTrue(login) url = reverse('virtualevent_register_view', kwargs={'id':self.openotwrace.id}) response = self.c.get(url) self.assertEqual(response.status_code,200) form_data = { 'teamname': faker.word(), 'weightcategory': 'hwt', 'adaptiveclass': 'None', 'age': 46, 'boatclass':'water', 'boattype':'1x' } form = VirtualRaceResultForm(form_data) if not form.is_valid(): print(form.errors) self.assertTrue(form.is_valid()) response = self.c.post(url,form_data,follow=True) expected_url = reverse('virtualevent_view',kwargs={'id':self.openotwrace.id}) self.assertRedirects(response, expected_url=expected_url, status_code=302,target_status_code=200) url = reverse('virtualevent_addboat_view', kwargs = {'id':self.openotwrace.id}) response = self.c.get(url) self.assertEqual(response.status_code,200) form_data = { 'teamname': faker.word(), 'weightcategory': 'hwt', 'adaptiveclass': 'None', 'age': 46, 'boatclass':'water', 'boattype':'2x' } form = VirtualRaceResultForm(form_data) if not form.is_valid(): print(form.errors) self.assertTrue(form.is_valid()) response = self.c.post(url,form_data,follow=True) expected_url = reverse('virtualevent_view',kwargs={'id':self.openotwrace.id}) self.assertRedirects(response, expected_url=expected_url, status_code=302,target_status_code=200) url = reverse('virtualevent_toggle_email_view', kwargs={'id':self.openotwrace.id}) response = self.c.get(url,follow=True) self.assertEqual(response.status_code,200) # submit result (OTE) @patch('django.contrib.gis.geoip2.GeoIP2.city', side_effect=mocked_requests) def test_ote_submit(self, mock_get): login = self.c.login(username=self.upiet.username, password=self.passwordpiet) self.assertTrue(login) url = reverse('virtualevent_submit_result_view', kwargs={'id':self.submitergrace.id, 'workoutid':self.wduzend_rpiet.id}) response = self.c.get(url) self.assertEqual(response.status_code,200) form_data = { 'workouts':[self.wduzend_rpiet.id], 'record': self.pietrecord.id } response = self.c.post(url,form_data,follow=True) self.assertEqual(response.status_code,200) therecord = IndoorVirtualRaceResult.objects.get(userid=self.rpiet.id, race=self.submitergrace, id=self.pietrecord.id) self.assertTrue(therecord.coursecompleted) url = reverse('virtualevent_ranking_view', kwargs={'id':self.submitergrace.id}) response = self.c.get(url) self.assertEqual(response.status_code,200) # submit result (OTE) @patch('django.contrib.gis.geoip2.GeoIP2.city', side_effect=mocked_requests) def test_ote_submitfalse(self, mock_get): login = self.c.login(username=self.upiet.username, password=self.passwordpiet) self.assertTrue(login) url = reverse('virtualevent_submit_result_view', kwargs={'id':self.submitergrace.id, 'workoutid':self.wother_rpiet.id}) response = self.c.get(url) self.assertEqual(response.status_code,200) form_data = { 'workouts':[self.wother_rpiet.id], 'record': self.pietrecord.id } response = self.c.post(url,form_data,follow=True) self.assertEqual(response.status_code,200) therecord = IndoorVirtualRaceResult.objects.get(userid=self.rpiet.id, race=self.submitergrace, id=self.pietrecord.id) self.assertTrue(not therecord.coursecompleted) # submit result (OTW) @patch('django.contrib.gis.geoip2.GeoIP2.city', side_effect=mocked_requests) def test_otw_submit(self, mock_get): login = self.c.login(username=self.upiet.username, password=self.passwordpiet) self.assertTrue(login) url = reverse('virtualevent_submit_result_view', kwargs={'id':self.submitotwrace.id, 'workoutid':self.wuh_otw.id}) response = self.c.get(url) self.assertEqual(response.status_code,200) form_data = { 'workouts':[self.wuh_otw.id], 'record': self.pietotwrecord.id } response = self.c.post(url,form_data,follow=True) self.assertEqual(response.status_code,200) # course view # submit result (OTW) @patch('django.contrib.gis.geoip2.GeoIP2.city', side_effect=mocked_requests) def test_otw_courses(self, mock_get): login = self.c.login(username=self.upiet.username, password=self.passwordpiet) self.assertTrue(login) url = reverse('courses_view') response = self.c.get(url) self.assertEqual(response.status_code,200) url = reverse('course_map_view',kwargs={'id':self.course.id}) response = self.c.get(url) self.assertEqual(response.status_code,200) url = reverse('course_view',kwargs={'id':self.course.id}) response = self.c.get(url) self.assertEqual(response.status_code,200) @patch('django.contrib.gis.geoip2.GeoIP2.city', side_effect=mocked_requests) def test_otw_courses_edit(self, mock_get): login = self.c.login(username=self.u.username, password=self.password) self.assertTrue(login) url = reverse('course_edit_view',kwargs={'id':self.course.id}) response = self.c.get(url) self.assertEqual(response.status_code,200) form_data = { 'name': faker.word(), 'country': 'Czechia', 'notes': faker.text(), } form = GeoCourseEditForm(form_data) self.assertTrue(form.is_valid()) response = self.c.post(url,form_data) self.assertEqual(response.status_code,200) url = reverse('course_map_view',kwargs={'id':self.course.id}) response = self.c.get(url) self.assertEqual(response.status_code,200) # disqualify # standalone # calculate course adherence # other background jobs