Merge pull request #97 from danieldupriest/implement-sending-email
Implement sending email using Joe's code
This commit is contained in:
		
						commit
						b58fff8888
					
				
					 9 changed files with 119 additions and 15 deletions
				
			
		
							
								
								
									
										4
									
								
								back/.env
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								back/.env
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,4 @@ | ||||||
|  | EMAIL_HOST_USER=accountemail@yourmail.com | ||||||
|  | EMAIL_HOST_PASSWORD=accountpasswordhere | ||||||
|  | SUBMIT_REPORT_DESTINATION_EMAIL=to-address@yourmail.com | ||||||
|  | SUBMIT_REPORT_FROM_EMAIL=from-address@yourmail.com | ||||||
|  | @ -12,6 +12,7 @@ djangorestframework = "==3.8.2" | ||||||
| django-rest-auth = "==0.9.3" | django-rest-auth = "==0.9.3" | ||||||
| django-allauth = "==0.37.1" | django-allauth = "==0.37.1" | ||||||
| gunicorn = "==19.6.0" | gunicorn = "==19.6.0" | ||||||
|  | python-decouple = "==3.1" | ||||||
| 
 | 
 | ||||||
| [requires] | [requires] | ||||||
| python_version = "3.5" | python_version = "3.5" | ||||||
|  |  | ||||||
							
								
								
									
										15
									
								
								back/Pipfile.lock
									
										
									
										generated
									
									
									
								
							
							
						
						
									
										15
									
								
								back/Pipfile.lock
									
										
									
										generated
									
									
									
								
							|  | @ -1,7 +1,7 @@ | ||||||
| { | { | ||||||
|     "_meta": { |     "_meta": { | ||||||
|         "hash": { |         "hash": { | ||||||
|             "sha256": "b1fc6b06ec8daa4efd9573865bc6c1732ae9354309e036bfe3ce0ab76b1a3bcd" |             "sha256": "b8697c2d821e12f9f3e7bbad8a765782fc8a89df3f4260bc5f2b6f2e3d454510" | ||||||
|         }, |         }, | ||||||
|         "pipfile-spec": 6, |         "pipfile-spec": 6, | ||||||
|         "requires": { |         "requires": { | ||||||
|  | @ -39,11 +39,11 @@ | ||||||
|         }, |         }, | ||||||
|         "django": { |         "django": { | ||||||
|             "hashes": [ |             "hashes": [ | ||||||
|                 "sha256:a32c22af23634e1d11425574dce756098e015a165be02e4690179889b207c7a8", |                 "sha256:275bec66fd2588dd517ada59b8bfb23d4a9abc5a362349139ddda3c7ff6f5ade", | ||||||
|                 "sha256:d6393918da830530a9516bbbcbf7f1214c3d733738779f06b0f649f49cc698c3" |                 "sha256:939652e9d34d7d53d74d5d8ef82a19e5f8bb2de75618f7e5360691b6e9667963" | ||||||
|             ], |             ], | ||||||
|             "index": "pypi", |             "index": "pypi", | ||||||
|             "version": "==2.1.5" |             "version": "==2.1.7" | ||||||
|         }, |         }, | ||||||
|         "django-allauth": { |         "django-allauth": { | ||||||
|             "hashes": [ |             "hashes": [ | ||||||
|  | @ -97,6 +97,13 @@ | ||||||
|             ], |             ], | ||||||
|             "version": "==3.0.1" |             "version": "==3.0.1" | ||||||
|         }, |         }, | ||||||
|  |         "python-decouple": { | ||||||
|  |             "hashes": [ | ||||||
|  |                 "sha256:1317df14b43efee4337a4aa02914bf004f010cd56d6c4bd894e6474ec8c4fe2d" | ||||||
|  |             ], | ||||||
|  |             "index": "pypi", | ||||||
|  |             "version": "==3.1" | ||||||
|  |         }, | ||||||
|         "python3-openid": { |         "python3-openid": { | ||||||
|             "hashes": [ |             "hashes": [ | ||||||
|                 "sha256:0086da6b6ef3161cfe50fb1ee5cceaf2cda1700019fda03c2c5c440ca6abe4fa", |                 "sha256:0086da6b6ef3161cfe50fb1ee5cceaf2cda1700019fda03c2c5c440ca6abe4fa", | ||||||
|  |  | ||||||
|  | @ -114,8 +114,8 @@ def fare_limit_rule(report, fields): | ||||||
| planning_section.add_rule(title="Fare limits", rule=fare_limit_rule) | planning_section.add_rule(title="Fare limits", rule=fare_limit_rule) | ||||||
| 
 | 
 | ||||||
| def lowest_fare_rule(report, fields): | def lowest_fare_rule(report, fields): | ||||||
|     diff = field['lowest_fare_duration'] - field['preferred_flight_duration'] |     diff = fields['lowest_fare_duration'] - fields['preferred_flight_duration'] | ||||||
|     lowest_Fare = field['lowest_fare'] |     lowest_fare = fields['lowest_fare'] | ||||||
|     maximum = 0 |     maximum = 0 | ||||||
|     if diff <= 0: |     if diff <= 0: | ||||||
|         maximum = lowest_fare + 100 |         maximum = lowest_fare + 100 | ||||||
|  | @ -127,14 +127,14 @@ def lowest_fare_rule(report, fields): | ||||||
|         maximum = lowest_fare + 350 |         maximum = lowest_fare + 350 | ||||||
|     else: |     else: | ||||||
|         maximum = lowest_fare + 600 |         maximum = lowest_fare + 600 | ||||||
|     if field['preferred_fare'] > maximum: |     if fields['preferred_fare'] > maximum: | ||||||
|         return "For the lowest fare you have provided, your maximum in-policy fare amount is {} USD.".format(maximum) |         return "For the lowest fare you have provided, your maximum in-policy fare amount is {} USD.".format(maximum) | ||||||
|     return None |     return None | ||||||
| 
 | 
 | ||||||
| planning_section.add_rule(title="Lowest fare check", rule=lowest_fare_rule) | planning_section.add_rule(title="Lowest fare check", rule=lowest_fare_rule) | ||||||
| 
 | 
 | ||||||
| def departure_date_limit_rule(report, fields): | def departure_date_limit_rule(report, fields): | ||||||
|     days_to_departure = date(field['departure_date']) - date(field['screenshot_date']) |     days_to_departure = date(fields['departure_date']) - date(fields['screenshot_date']) | ||||||
|     if days_to_departure < 14: |     if days_to_departure < 14: | ||||||
|         return "Flights must be booked at least 14 days in advance." |         return "Flights must be booked at least 14 days in advance." | ||||||
|     if days_to_departure > 365: |     if days_to_departure > 365: | ||||||
|  | @ -174,7 +174,7 @@ flight_section.add_rule(title="Fare limits", rule=actual_fare_limit_rule) | ||||||
| 
 | 
 | ||||||
| def request_date_rule(report, fields): | def request_date_rule(report, fields): | ||||||
|     now = date.today() |     now = date.today() | ||||||
|     last_travel = date(field['return_date']) |     last_travel = date(fields['return_date']) | ||||||
|     if now - last_travel > 90: |     if now - last_travel > 90: | ||||||
|         return "Reimbursement requests must be made within 90 days of the last day of travel." |         return "Reimbursement requests must be made within 90 days of the last day of travel." | ||||||
|     return None |     return None | ||||||
|  |  | ||||||
							
								
								
									
										26
									
								
								back/backend/templates/backend/email.html
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								back/backend/templates/backend/email.html
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,26 @@ | ||||||
|  | <!DOCTYPE html> | ||||||
|  | <html> | ||||||
|  |     <head> | ||||||
|  |     </head> | ||||||
|  |     <body> | ||||||
|  |         <h1>Title: {{ title }}</h1> | ||||||
|  |         {% for section in sections %} | ||||||
|  | 	{% if section.completed %} | ||||||
|  |             <h2>{{section.title}}</h2> | ||||||
|  |             <table border="1"> | ||||||
|  |             {% for field in section.fields %} | ||||||
|  |                 <tr> | ||||||
|  |                     <td>{{field.label}}</td> | ||||||
|  |                     <td>{{field.value|default:""}}</td> | ||||||
|  |                 </tr> | ||||||
|  |             {% endfor %} | ||||||
|  |             {% for rule in section.rule_violations %} | ||||||
|  |                 <tr style="color:#ff3333"> | ||||||
|  | 		    <td colspan=2><strong>{{rule.label}}</strong><br/>{{rule.rule_break_text}}</td> | ||||||
|  |                 </tr> | ||||||
|  |             {% endfor %} | ||||||
|  |             </table> | ||||||
|  | 	{% endif %} | ||||||
|  |         {% endfor %}     | ||||||
|  |     </body> | ||||||
|  | </html> | ||||||
							
								
								
									
										15
									
								
								back/backend/templates/backend/email.txt
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								back/backend/templates/backend/email.txt
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,15 @@ | ||||||
|  | ***Deprecated: | ||||||
|  |     Replaced by converting html template to plaintext | ||||||
|  |     using the html2text library | ||||||
|  | *** | ||||||
|  | 
 | ||||||
|  | Title: {{title}} | ||||||
|  | {% for section in sections %} | ||||||
|  |     {{section.title}} | ||||||
|  |     {% for field in section.fields %} | ||||||
|  |         {{field.label}}:  {{field.value|default:"empty"}} | ||||||
|  |     {% endfor %} | ||||||
|  |     {% for rule in section.rule_violations %} | ||||||
|  |         [RULE] {{rule.label}}:  {{rule.rule_break_text}} | ||||||
|  |     {% endfor %} | ||||||
|  | {% endfor %}     | ||||||
|  | @ -3,6 +3,11 @@ from django.http import JsonResponse | ||||||
| from .models import * | from .models import * | ||||||
| from .policy import pol | from .policy import pol | ||||||
| import os | import os | ||||||
|  | from rest_framework.exceptions import ParseError | ||||||
|  | from rest_framework.parsers import FileUploadParser, MultiPartParser | ||||||
|  | from django.core.mail import EmailMultiAlternatives | ||||||
|  | from django.template.loader import render_to_string | ||||||
|  | from decouple import config | ||||||
| 
 | 
 | ||||||
| def get_report(report_pk): | def get_report(report_pk): | ||||||
|     """ |     """ | ||||||
|  | @ -199,8 +204,8 @@ def report_detail(request, report_pk): | ||||||
|             return JsonResponse({"message": "Cannot submit a report that has already been submitted."}, status=409) |             return JsonResponse({"message": "Cannot submit a report that has already been submitted."}, status=409) | ||||||
|         rep.submitted = True; |         rep.submitted = True; | ||||||
|         rep.save() |         rep.save() | ||||||
|         # Send email here |         # Send email | ||||||
|         ################# |         send_report_to_admin(request, report_pk) | ||||||
|         return JsonResponse({"message": "Report submitted."}) |         return JsonResponse({"message": "Report submitted."}) | ||||||
| 
 | 
 | ||||||
|     # DELETE: Deletes a report from the user's account. |     # DELETE: Deletes a report from the user's account. | ||||||
|  | @ -365,3 +370,46 @@ def section_complete(section_pk): | ||||||
|         if i.completed: |         if i.completed: | ||||||
|             return True |             return True | ||||||
|     return False |     return False | ||||||
|  | 
 | ||||||
|  | def send_report_to_admin(request, report_pk): | ||||||
|  |     """ | ||||||
|  |     Sends an email message to admin with html/txt version of report, | ||||||
|  |     along with file attachments. Cc sent to user. | ||||||
|  |      | ||||||
|  |     request -- Request object with user info inside | ||||||
|  |     report_pk -- ID of the report to submit | ||||||
|  |     """ | ||||||
|  |     params = get_report(report_pk) | ||||||
|  |     to_email = config('SUBMIT_REPORT_DESTINATION_EMAIL') | ||||||
|  |     from_email = config('SUBMIT_REPORT_FROM_EMAIL') | ||||||
|  |     cc = request.user.email | ||||||
|  |     msg_html = render_to_string('backend/email.html', params) | ||||||
|  |     msg_plain = render_to_string('backend/email.txt', params) | ||||||
|  |     message = EmailMultiAlternatives( | ||||||
|  |         "Reimbursinator - {}".format(params['title']), | ||||||
|  |         msg_plain, | ||||||
|  |         from_email, | ||||||
|  |         [to_email], | ||||||
|  |         cc=[request.user.email], | ||||||
|  |     ) | ||||||
|  |     message.attach_alternative(msg_html, "text/html") | ||||||
|  |     for f in get_files(report_pk): | ||||||
|  |         message.attach_file(f) | ||||||
|  |     message.send() | ||||||
|  | 
 | ||||||
|  | def get_files(report_pk): | ||||||
|  |     """ | ||||||
|  |     collects all the files in a particular report and returns them | ||||||
|  |     in an array. | ||||||
|  | 
 | ||||||
|  |     report_pk -- ID of the report to collect files for | ||||||
|  |     """ | ||||||
|  |     sections = Section.objects.filter(report_id=report_pk) | ||||||
|  |     files = [] | ||||||
|  |     for section in sections: | ||||||
|  |         fields = Field.objects.filter(section_id=section.id, completed=True) | ||||||
|  |         for field in fields: | ||||||
|  |             if field.field_type == "file": | ||||||
|  |                 print("appending {}".format(field.data_file.name)) | ||||||
|  |                 files.append(field.data_file.name) | ||||||
|  |     return files | ||||||
|  |  | ||||||
							
								
								
									
										
											BIN
										
									
								
								back/db.sqlite3
									
										
									
									
									
								
							
							
						
						
									
										
											BIN
										
									
								
								back/db.sqlite3
									
										
									
									
									
								
							
										
											Binary file not shown.
										
									
								
							|  | @ -11,12 +11,11 @@ https://docs.djangoproject.com/en/2.1/ref/settings/ | ||||||
| """ | """ | ||||||
| 
 | 
 | ||||||
| import os | import os | ||||||
| #from reimbursinator.custom_auth import BearerAuthentication | from decouple import config | ||||||
| 
 | 
 | ||||||
| # Build paths inside the project like this: os.path.join(BASE_DIR, ...) | # Build paths inside the project like this: os.path.join(BASE_DIR, ...) | ||||||
| BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) | BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
| # Quick-start development settings - unsuitable for production | # Quick-start development settings - unsuitable for production | ||||||
| # See https://docs.djangoproject.com/en/2.1/howto/deployment/checklist/ | # See https://docs.djangoproject.com/en/2.1/howto/deployment/checklist/ | ||||||
| 
 | 
 | ||||||
|  | @ -28,7 +27,6 @@ DEBUG = True | ||||||
| 
 | 
 | ||||||
| ALLOWED_HOSTS = ['localhost','192.168.99.100', '127.0.0.1'] | ALLOWED_HOSTS = ['localhost','192.168.99.100', '127.0.0.1'] | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
| # Application definition | # Application definition | ||||||
| 
 | 
 | ||||||
| INSTALLED_APPS = [ | INSTALLED_APPS = [ | ||||||
|  | @ -157,7 +155,12 @@ STATIC_URL = 'https://192.168.99.100:8445/' | ||||||
| 
 | 
 | ||||||
| # Email Config | # Email Config | ||||||
| 
 | 
 | ||||||
| EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend' | EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend' | ||||||
|  | EMAIL_USE_TLS = True | ||||||
|  | EMAIL_HOST = 'smtp.gmail.com' | ||||||
|  | EMAIL_HOST_USER = config('EMAIL_HOST_USER') | ||||||
|  | EMAIL_HOST_PASSWORD = config('EMAIL_HOST_PASSWORD') | ||||||
|  | EMAIL_PORT = 587 | ||||||
| 
 | 
 | ||||||
| SITE_ID = 1 | SITE_ID = 1 | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		
		Reference in a new issue
	
	 ppdom
						ppdom