From 78ece5b783539e5cb223870e95eb1b2f24fa78a7 Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Wed, 7 Oct 2020 17:58:01 +0200 Subject: [PATCH 1/3] accepting alternative email addresses --- rowers/models.py | 73 ++++++++++++++++++++++++++++++++++++++- rowers/views/userviews.py | 3 ++ 2 files changed, 75 insertions(+), 1 deletion(-) diff --git a/rowers/models.py b/rowers/models.py index 0471b72f..22169084 100644 --- a/rowers/models.py +++ b/rowers/models.py @@ -131,6 +131,56 @@ class TemplateListField(models.TextField): value = self._get_val_from_obj(obj) return self.get_deb_prep_value(value) +# model for Emails field +class AlternativeEmails(models.TextField): + def __init__(self, *args, **kwargs): + self.token = kwargs.pop('token',',') + super(AlternativeEmails, self).__init__(*args,**kwargs) + + def to_python(self, value): + if not value: return + if isinstance(value, list): + return value + # remove double quotes and brackets + value = re.sub(r'u\"','',value) + value = re.sub(r'u\'','',value) + value = re.sub(r'\\','',value) + value = re.sub(r'\"','',value) + value = re.sub(r'\'','',value) + value = re.sub(r'\[','',value) + value = re.sub(r'\]','',value) + value = re.sub(r'\[\[','[',value) + value = re.sub(r'\]\]',']',value) + value = re.sub(r'\ \ ',' ',value) + value = re.sub(r', ',',',value) + + return value.split(self.token) + + def from_db_value(self,value, expression, connection): + if value is None: + return value + if isinstance(value, list): + return value + return value.split(self.token) + + def get_db_prep_value(self, value, connection, prepared=False): + if not value: return + assert(isinstance(value, list) or isinstance(value, tuple)) + newlist = [] + for s in value: + + try: + validate_email(s) + newlist.append(s) + except ValidationError: + pass + + return self.token.join([str(s) for s in newlist]) + + def value_to_string(self, obj): + value = self._get_val_from_obj(obj) + return self.get_deb_prep_value(value) + # model for Power Zone names class PowerZonesField(models.TextField): # __metaclass__ = models.SubfieldBase @@ -804,6 +854,8 @@ class Rower(models.Model): 'Pwr TR', 'Pwr AN']) + emailalternatives = AlternativeEmails(default=[],null=True,blank=True,verbose_name='Alternative Email addresses (separate with ",")') + # Site Settings workflowleftpanel = TemplateListField(default=defaultleft) workflowmiddlepanel = TemplateListField(default=defaultmiddle) @@ -3677,7 +3729,7 @@ class AccountRowerForm(ModelForm): 'getimportantemails', 'defaulttimezone','showfavoritechartnotes', 'defaultlandingpage', - 'offercoaching','autojoin'] + 'offercoaching','autojoin','emailalternatives'] widgets = { 'birthdate': SelectDateWidget( @@ -3686,10 +3738,29 @@ class AccountRowerForm(ModelForm): } def __init__(self, *args, **kwargs): + super(AccountRowerForm, self).__init__(*args, **kwargs) if 'coach' not in self.instance.rowerplan: self.fields.pop('offercoaching') + self.initial['emailalternatives'] = ', '.join(self.instance.emailalternatives) + + def clean(self): + cd = self.cleaned_data + z = "".join(cd['emailalternatives'].split()).split(',') + emailalternatives = [] + for addr in z: + try: + print(addr) + validate_email(addr) + match = User.objects.filter(email__iexact=addr) + if match.count() == 0: + emailalternatives.append(addr) + except ValidationError: + pass + + self.cleaned_data['emailalternatives'] = emailalternatives + # Form to set static chart settings class StaticChartRowerForm(ModelForm): class Meta: diff --git a/rowers/views/userviews.py b/rowers/views/userviews.py index 88e3a9c0..caa61060 100644 --- a/rowers/views/userviews.py +++ b/rowers/views/userviews.py @@ -415,6 +415,7 @@ def rower_edit_view(request,rowerid=0,userid=0,message=""): u.email = email resetbounce = True + emailalternatives = cd['emailalternatives'] u.save() r.defaulttimezone=defaulttimezone @@ -428,6 +429,8 @@ def rower_edit_view(request,rowerid=0,userid=0,message=""): r.sex = sex r.birthdate = birthdate r.autojoin = autojoin + r.emailalternatives = emailalternatives + if resetbounce and r.emailbounced: r.emailbounced = False From b5b2969ef8473077b0a16749f3cc49030f62c0b0 Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Wed, 7 Oct 2020 18:06:36 +0200 Subject: [PATCH 2/3] should now process alternative email addresses --- rowers/management/commands/processemail.py | 4 ++++ rowers/views/workoutviews.py | 8 ++++++-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/rowers/management/commands/processemail.py b/rowers/management/commands/processemail.py index 96e39eb8..7f566e6b 100644 --- a/rowers/management/commands/processemail.py +++ b/rowers/management/commands/processemail.py @@ -235,6 +235,10 @@ class Command(BaseCommand): rowers = [ r for r in Rower.objects.all() if r.user.email.lower() == from_address ] + rowers2 = [ + r for r in Rower.objects.all() if from_address in r.emailalternatives + ] + rowers = rowers+rowers2 except IOError: rowers = [] except Message.DoesNotExist: diff --git a/rowers/views/workoutviews.py b/rowers/views/workoutviews.py index 16ee013e..bf620b2a 100644 --- a/rowers/views/workoutviews.py +++ b/rowers/views/workoutviews.py @@ -4691,8 +4691,12 @@ def workout_upload_api(request): u = us[0] r = getrower(u) else: - message = {'status':'false','message':'could not find user'} - return JSONResponse(status=400,data=message) + rs = [r for r in Rower.objects.all() if from_address in r.emailalternatives] + if len(rs): + r = rs[0] + else: + message = {'status':'false','message':'could not find user'} + return JSONResponse(status=400,data=message) else: message = {'status':'false','message':'invalid user'} return JSONResponse(status=400,data=message) From 3107182310f9ca91fa910c126c554c6b308f4e50 Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Wed, 7 Oct 2020 19:29:17 +0200 Subject: [PATCH 3/3] processing emails from alternate address --- rowers/management/commands/processemail.py | 11 ++++--- rowers/models.py | 6 ++-- rowers/tests/test_emails.py | 34 ++++++++++++++++++++-- rowers/views/workoutviews.py | 23 ++++++++++----- 4 files changed, 58 insertions(+), 16 deletions(-) diff --git a/rowers/management/commands/processemail.py b/rowers/management/commands/processemail.py index 7f566e6b..6835e089 100644 --- a/rowers/management/commands/processemail.py +++ b/rowers/management/commands/processemail.py @@ -235,10 +235,13 @@ class Command(BaseCommand): rowers = [ r for r in Rower.objects.all() if r.user.email.lower() == from_address ] - rowers2 = [ - r for r in Rower.objects.all() if from_address in r.emailalternatives - ] - rowers = rowers+rowers2 + try: + rowers2 = [ + r for r in Rower.objects.all() if from_address in r.emailalternatives + ] + rowers = rowers+rowers2 + except TypeError: + pass except IOError: rowers = [] except Message.DoesNotExist: diff --git a/rowers/models.py b/rowers/models.py index 22169084..c302b3ad 100644 --- a/rowers/models.py +++ b/rowers/models.py @@ -3743,7 +3743,10 @@ class AccountRowerForm(ModelForm): if 'coach' not in self.instance.rowerplan: self.fields.pop('offercoaching') - self.initial['emailalternatives'] = ', '.join(self.instance.emailalternatives) + try: + self.initial['emailalternatives'] = ', '.join(self.instance.emailalternatives) + except TypeError: + pass def clean(self): cd = self.cleaned_data @@ -3751,7 +3754,6 @@ class AccountRowerForm(ModelForm): emailalternatives = [] for addr in z: try: - print(addr) validate_email(addr) match = User.objects.filter(email__iexact=addr) if match.count() == 0: diff --git a/rowers/tests/test_emails.py b/rowers/tests/test_emails.py index 11149be0..eca8a37c 100644 --- a/rowers/tests/test_emails.py +++ b/rowers/tests/test_emails.py @@ -12,9 +12,12 @@ class EmailUpload(TestCase): self.c = Client() u = User.objects.create_user('john', 'sander@ds.ds', - 'koeinsloot') + 'koeinsloot', + first_name='John', + last_name='Sloot') r = Rower.objects.create(user=u,gdproptin=True,surveydone=True, - gdproptindate=timezone.now() + gdproptindate=timezone.now(), + emailalternatives=['sander2@ds.nl'] ) self.theadmin = UserFactory(is_staff=True) @@ -76,6 +79,33 @@ workout run self.assertEqual(w.name,'test') self.assertEqual(w.notes,'aap noot mies') + + + @patch('rowers.dataprep.create_engine') + @patch('rowers.dataprep.getsmallrowdata_db',side_effect=mocked_getsmallrowdata_db) + def test_uploadapi2(self,mocked_sqlalchemy,mocked_getsmallrowdata_db): + form_data = { + 'title': 'test', + 'workouttype':'rower', + 'boattype': '1x', + 'notes': 'aap noot mies', + 'make_plot': False, + 'upload_to_C2': False, + 'plottype': 'timeplot', + 'file': 'media/mailbox_attachments/colin3.csv', + 'secret': settings.UPLOAD_SERVICE_SECRET, + 'useremail': 'sander2@ds.nl', + } + + url = reverse('workout_upload_api') + response = self.c.post(url,form_data,HTTP_HOST='127.0.0.1:4533') + self.assertEqual(response.status_code,200) + + # should also test if workout is created + w = Workout.objects.get(id=1) + self.assertEqual(w.name,'test') + self.assertEqual(w.notes,'aap noot mies') + @patch('rowers.dataprep.create_engine') @patch('rowers.dataprep.getsmallrowdata_db',side_effect=mocked_getsmallrowdata_db) def test_uploadapi_credentials(self,mocked_sqlalchemy,mocked_getsmallrowdata_db): diff --git a/rowers/views/workoutviews.py b/rowers/views/workoutviews.py index bf620b2a..4bcd3e23 100644 --- a/rowers/views/workoutviews.py +++ b/rowers/views/workoutviews.py @@ -4685,21 +4685,25 @@ def workout_upload_api(request): if rowerform.is_valid(): u = rowerform.cleaned_data['user'] r = getrower(u) - elif 'useremail' in post_data: + else: + message = {'status':'false','message':'invalid user'} + return JSONResponse(status=400,data=message) + + if 'useremail' in post_data: us = User.objects.filter(email=post_data['useremail']) if len(us): u = us[0] r = getrower(u) else: - rs = [r for r in Rower.objects.all() if from_address in r.emailalternatives] - if len(rs): - r = rs[0] - else: + r = None + for rwr in Rower.objects.all(): + if post_data['useremail'] in rwr.emailalternatives: + r = rwr + break + if post_data['useremail'] not in r.emailalternatives: message = {'status':'false','message':'could not find user'} return JSONResponse(status=400,data=message) - else: - message = {'status':'false','message':'invalid user'} - return JSONResponse(status=400,data=message) + notes = form.cleaned_data['notes'] if optionsform.is_valid(): @@ -4716,6 +4720,9 @@ def workout_upload_api(request): message = optionsform.errors return JSONResponse(status=400,data=message) + if r is None: + message = {'status':'false','message':'something went wrong'} + return JSONResponse(status=400,data=message) id, message, f2 = dataprep.new_workout_from_file( r,f2,