Merge branch 'master' into show-report-history

This commit is contained in:
Preston Doman 2019-01-30 00:27:29 -08:00
commit f1da66abcc
11 changed files with 273 additions and 235 deletions

View file

@ -0,0 +1,18 @@
# Generated by Django 2.1.5 on 2019-01-25 22:25
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('backend', '0002_auto_20190123_0038'),
]
operations = [
migrations.AlterField(
model_name='datafile',
name='data',
field=models.FileField(blank=True, null=True, upload_to='uploads/%Y/%m/%d/'),
),
]

View file

@ -0,0 +1,18 @@
# Generated by Django 2.1.5 on 2019-01-25 22:34
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('backend', '0003_auto_20190125_1425'),
]
operations = [
migrations.AlterField(
model_name='datafile',
name='data',
field=models.FileField(blank=True, max_length=512, null=True, upload_to='uploads/%Y/%m/%d/'),
),
]

View file

@ -8,6 +8,9 @@ class Report(models.Model):
date_submitted = models.DateTimeField('date submitted', null=True, blank=True) date_submitted = models.DateTimeField('date submitted', null=True, blank=True)
submitted = models.BooleanField(default=False) submitted = models.BooleanField(default=False)
def __str__(self):
return self.title
class Section(models.Model): class Section(models.Model):
report_id = models.ForeignKey(Report, on_delete=models.CASCADE) report_id = models.ForeignKey(Report, on_delete=models.CASCADE)
completed = models.BooleanField() completed = models.BooleanField()
@ -15,6 +18,9 @@ class Section(models.Model):
html_description = models.TextField() html_description = models.TextField()
number = models.IntegerField() number = models.IntegerField()
def __str__(self):
return "{0}(#{1})".format(self.title, self.number)
class Field(models.Model): class Field(models.Model):
section_id = models.ForeignKey(Section, on_delete=models.CASCADE) section_id = models.ForeignKey(Section, on_delete=models.CASCADE)
label = models.CharField(max_length=256) label = models.CharField(max_length=256)
@ -22,26 +28,50 @@ class Field(models.Model):
type = models.CharField(max_length=128) type = models.CharField(max_length=128)
completed = models.BooleanField(default=False) completed = models.BooleanField(default=False)
def __str__(self):
return "{0}(#{1})".format(self.label, self.number)
class DataBool(models.Model): class DataBool(models.Model):
field_id = models.ForeignKey(Field, on_delete=models.CASCADE) field_id = models.ForeignKey(Field, on_delete=models.CASCADE)
data = models.BooleanField(default=False) data = models.BooleanField(default=False)
def __str__(self):
if self.data:
return "True"
else:
return "False"
class DataDecimal(models.Model): class DataDecimal(models.Model):
field_id = models.ForeignKey(Field, on_delete=models.CASCADE) field_id = models.ForeignKey(Field, on_delete=models.CASCADE)
data = models.DecimalField(max_digits=9,decimal_places=2, null=True, blank=True) data = models.DecimalField(max_digits=9,decimal_places=2, null=True, blank=True)
def __str__(self):
return "{0}".format(self.data)
class DataDate(models.Model): class DataDate(models.Model):
field_id = models.ForeignKey(Field, on_delete=models.CASCADE) field_id = models.ForeignKey(Field, on_delete=models.CASCADE)
data = models.DateField(null=True, blank=True) data = models.DateField(null=True, blank=True)
def __str__(self):
return "{0}".format(self.data)
class DataFile(models.Model): class DataFile(models.Model):
field_id = models.ForeignKey(Field, on_delete=models.CASCADE) field_id = models.ForeignKey(Field, on_delete=models.CASCADE)
data = models.FileField(null=True, blank=True) data = models.FileField(upload_to='uploads/%Y/%m/%d/', max_length=512, null=True, blank=True)
def __str__(self):
return "{0}".format(self.data)
class DataString(models.Model): class DataString(models.Model):
field_id = models.ForeignKey(Field, on_delete=models.CASCADE) field_id = models.ForeignKey(Field, on_delete=models.CASCADE)
data = models.TextField(default='') data = models.TextField(default='')
def __str__(self):
return "{0}".format(self.data)
class DataInteger(models.Model): class DataInteger(models.Model):
field_id = models.ForeignKey(Field, on_delete=models.CASCADE) field_id = models.ForeignKey(Field, on_delete=models.CASCADE)
data = models.IntegerField(null=True, blank=True) data = models.IntegerField(null=True, blank=True)
def __str__(self):
return "{0}".format(self.data)

View file

@ -1,23 +1,20 @@
# Rupika Dikkala # Link report and account functionality to views
# January 19, 2019
# Add urls and link to the
# views
from django.urls import path from django.urls import path
from rest_framework.urlpatterns import format_suffix_patterns
from . import views from . import views
urlpatterns = [ urlpatterns = [
path('', views.List.as_view()), #path('', views.List.as_view()),
path('<int:pk>/', views.Detail.as_view()), #path('<int:pk>/', views.Detail.as_view()),
path('create_report/', views.create_report), path('report', views.report),
path('delete_report/', views.delete_report), path('reports', views.reports),
path('get_report/', views.get_report), path('report/<int:report_pk>', views.report_detail),
path('list_report/', views.list_report), path('report/<int:report_pk>/section/<int:section_pk>', views.section),
path('update_report/', views.update_report), path('account', views.account),
path('submit_report/', views.submit_report), path('account/login', views.account_login),
path('update_section/', views.update_section), path('account/logout', views.account_logout),
path('create_account/', views.create_account),
path('login/', views.login),
path('logout/', views.logout),
] ]
urlpatterns = format_suffix_patterns(urlpatterns)

View file

@ -1,17 +1,13 @@
# Rupika Dikkala from rest_framework import generics
# January 19, 2019 from rest_framework import status
# Creating views for URL that from rest_framework.decorators import api_view
# returns JSON data
from django.shortcuts import render from django.shortcuts import render
from django.http import JsonResponse from django.http import JsonResponse
from .models import * from .models import *
from .serializers import * from .serializers import *
from rest_framework import generics # Sample view using generics
# create sample view
class List(generics.ListCreateAPIView): class List(generics.ListCreateAPIView):
queryset = Report.objects.all() queryset = Report.objects.all()
serializer_class = ReportSerializer serializer_class = ReportSerializer
@ -20,11 +16,13 @@ class Detail(generics.RetrieveUpdateDestroyAPIView):
queryset = Report.objects.all() queryset = Report.objects.all()
serializer_class = ReportSerializer serializer_class = ReportSerializer
# API Endpoints
####################################### @api_view(['POST'])
def report(request):
# Create Report '''
def create_report(request): Generate a new empty report and return it
'''
data = { data = {
"title": "2018 Portland trip", "title": "2018 Portland trip",
"date_created": "2018-05-22T14:56:28.000Z", "date_created": "2018-05-22T14:56:28.000Z",
@ -87,94 +85,26 @@ def create_report(request):
} }
return JsonResponse(data) return JsonResponse(data)
# Delete report @api_view(['GET'])
def delete_report(request): def reports(request):
data = {
'name': 'Delete report',
}
return JsonResponse(data)
# Get report
def get_report(request):
data = {
"title": "2018 Portland trip",
"date_created": "2018-05-22T14:56:28.000Z",
"submitted": False,
"date_submitted": "0000-00-00T00:00:00.000Z",
"sections": [
{
"id": 1,
"completed": True,
"title": "Flight Info",
"html_description": "<p>Enter flight details here.</p>",
"fields": {
"international": {
"label": "International flight",
"type": "boolean",
"value": True
},
"travel_date": {
"label": "Travel start date",
"type": "date",
"value": "2016-05-22T14:56:28.000Z"
},
"fare": {
"label": "Fare",
"type": "decimal",
"value": "1024.99"
},
"lowest_fare_screenshot": {
"label": "Lowest fare screenshot",
"type": "file",
"value": "e92h842jiu49f8..."
},
"plane_ticket_invoice": {
"label": "Plane ticket invoice PDF",
"type": "file",
"value": ""
}
},
"rule_violations": [
{
"error_text": "Plane ticket invoice must be submitted."
}
]
},
{
"id": 2,
"completed": False,
"title": "Hotel info",
"html_description": "<p>If you used a hotel, please enter the details.</p>",
"fields": {
"total": {
"label": "Total cost",
"type": "decimal"
}
},
"rule_violations": [
]
}
]
}
return JsonResponse(data)
# List Reports
def list_report(request):
data = { data = {
"reports": [ "reports": [
{ {
"report_pk": 1,
"title": "2018 Portland trip", "title": "2018 Portland trip",
"date_created": "2018-05-22T14:56:28.000Z", "date_created": "2018-05-22T14:56:28.000Z",
"state": "created", "state": "created",
"date_submitted": "0000-00-00T00:00:00.000Z" "date_submitted": "0000-00-00T00:00:00.000Z"
}, },
{ {
"report_pk": 2,
"title": "2017 Los Angeles trip", "title": "2017 Los Angeles trip",
"date_created": "2017-05-22T14:56:28.000Z", "date_created": "2017-05-22T14:56:28.000Z",
"state": "submitted", "state": "submitted",
"date_submitted": "2017-07-22T14:56:28.000Z" "date_submitted": "2017-07-22T14:56:28.000Z"
}, },
{ {
"report_pk": 3,
"title": "2017 Denver trip", "title": "2017 Denver trip",
"date_created": "2015-04-22T14:56:28.000Z", "date_created": "2015-04-22T14:56:28.000Z",
"state": "accepted", "state": "accepted",
@ -184,24 +114,83 @@ def list_report(request):
} }
return JsonResponse(data) return JsonResponse(data)
# Update Reports @api_view(['GET', 'PUT', 'DELETE'])
def update_report(request): def report_detail(request, report_pk):
data = { if request.method == 'GET':
'name': 'update report', data = {
'state': 'SUBMITTED!', "report_pk": report_pk,
} "title": "2018 Portland trip",
return JsonResponse(data) "date_created": "2018-05-22T14:56:28.000Z",
"submitted": False,
"date_submitted": "0000-00-00T00:00:00.000Z",
"sections": [
{
"id": 1,
"completed": True,
"title": "Flight Info",
"html_description": "<p>Enter flight details here.</p>",
"fields": {
"international": {
"label": "International flight",
"type": "boolean",
"value": True
},
"travel_date": {
"label": "Travel start date",
"type": "date",
"value": "2016-05-22T14:56:28.000Z"
},
"fare": {
"label": "Fare",
"type": "decimal",
"value": "1024.99"
},
"lowest_fare_screenshot": {
"label": "Lowest fare screenshot",
"type": "file",
"value": "e92h842jiu49f8..."
},
"plane_ticket_invoice": {
"label": "Plane ticket invoice PDF",
"type": "file",
"value": ""
}
},
"rule_violations": [
{
"error_text": "Plane ticket invoice must be submitted."
}
]
},
{
"id": 2,
"completed": False,
"title": "Hotel info",
"html_description": "<p>If you used a hotel, please enter the details.</p>",
"fields": {
"total": {
"label": "Total cost",
"type": "decimal"
}
},
"rule_violations": [
]
}
]
}
return JsonResponse(data)
elif request.method == 'PUT':
return JsonResponse({"message": "Report submitted."})
elif request.method == 'DELETE':
return JsonResponse({"message": "Deleted report {0}.".format(report_pk)})
# Submit Reports @api_view(['PUT'])
def submit_report(request): def section(request, report_pk, section_pk):
data = { '''
'name': 'submit report', Update a section with new data.
} '''
return JsonResponse(data)
# Update section
def update_section(request):
data = { data = {
"message": "Updated report {0}, section {1}.".format(report_pk, section_pk),
"fields": { "fields": {
"international": True, "international": True,
"travel_date": "2012-04-23T18:25:43.511Z", "travel_date": "2012-04-23T18:25:43.511Z",
@ -211,25 +200,23 @@ def update_section(request):
} }
return JsonResponse(data) return JsonResponse(data)
@api_view(['POST'])
def account(request):
'''
Create a new user account
'''
return JsonResponse({"message": "Account creation successful."})
# Create account @api_view(['POST'])
def create_account(request): def account_login(request):
data = { '''
'name': 'create account', Log in to a user account
} '''
return JsonResponse(data) return JsonResponse({"message": "Successfully logged in."})
# Login
def login(request):
data = {
'name': 'login',
}
return JsonResponse(data)
# Logout
def logout(request):
data = {
'name': 'logout',
}
return JsonResponse(data)
@api_view(['DELETE'])
def account_logout(request):
'''
Log out from a user account
'''
return JsonResponse({"message": "User logged out."})

Binary file not shown.

View file

@ -2,26 +2,26 @@ import hashlib
hasher = hashlib.md5() hasher = hashlib.md5()
with open ('simple_policy.py', 'rb') as afile: with open ('simple_policy.py', 'rb') as afile:
buf = afile.read() buf = afile.read()
hasher.update(buf) hasher.update(buf)
print("md5 of simple: " + hasher.hexdigest()) print("md5 of simple: " + hasher.hexdigest())
hasher = hashlib.md5() hasher = hashlib.md5()
with open ('moderate_policy.py', 'rb') as afile: with open ('moderate_policy.py', 'rb') as afile:
buf = afile.read() buf = afile.read()
hasher.update(buf) hasher.update(buf)
print("md5 of moderate: " + hasher.hexdigest()) print("md5 of moderate: " + hasher.hexdigest())
hasher = hashlib.sha1() hasher = hashlib.sha1()
with open ('simple_policy.py', 'rb') as afile: with open ('simple_policy.py', 'rb') as afile:
buf = afile.read() buf = afile.read()
hasher.update(buf) hasher.update(buf)
print("sha1 of simple: " + hasher.hexdigest()) print("sha1 of simple: " + hasher.hexdigest())
hasher = hashlib.sha1() hasher = hashlib.sha1()
with open ('moderate_policy.py', 'rb') as afile: with open ('moderate_policy.py', 'rb') as afile:
buf = afile.read() buf = afile.read()
hasher.update(buf) hasher.update(buf)
print("sha1 of moderate: " + hasher.hexdigest()) print("sha1 of moderate: " + hasher.hexdigest())

View file

@ -9,22 +9,22 @@ from datetime import date
#### General #### General
#### Section 0 #### Section 0
general_section = Section( general_section = Section(
title = "General Info", title = "General Info",
html_description = "", html_description = "",
fields = { fields = {
"destination": {"label": "Destination City", "type": "string"} "destination": {"label": "Destination City", "type": "string"}
} }
) )
general_section.add_rule( general_section.add_rule(
title = "Destination city check", title = "Destination city check",
rule = lambda report, section: rule = lambda report, section:
if section.fields.destination == "Timbuktu": if section.fields.destination == "Timbuktu":
return True return True
else: else:
return False return False
, ,
rule_break_text = "What did the cowboy say about Tim, his wild horse?" rule_break_text = "What did the cowboy say about Tim, his wild horse?"
) )
@ -32,22 +32,22 @@ general_section.add_rule(
#### Flight #### Flight
#### Section 1 #### Section 1
flight_section = Section( flight_section = Section(
title = "Flight Info", title = "Flight Info",
html_description = "<p>Enter flight details here.</p>", html_description = "<p>Enter flight details here.</p>",
fields = { fields = {
"international": {"label": "Is this an international flight?", "type": "boolean"}, "international": {"label": "Is this an international flight?", "type": "boolean"},
"departure_date": {"label": "Departure date", "type": "date"}, "departure_date": {"label": "Departure date", "type": "date"},
"return_date": {"label": "Return date", "type": "date"}, "return_date": {"label": "Return date", "type": "date"},
"fare": {"label": "Fare", "type": "decimal"}, "fare": {"label": "Fare", "type": "decimal"},
} }
) )
flight_section.add_rule( flight_section.add_rule(
title = "Airline fare pre-approval check", title = "Airline fare pre-approval check",
rule = lambda report, section: rule = lambda report, section:
return section.fields.fare < 500 return section.fields.fare < 500
, ,
rule_break_text = "Fares cannot be more than $500" rule_break_text = "Fares cannot be more than $500"
) )
@ -55,25 +55,25 @@ flight_section.add_rule(
#### Lodging #### Lodging
#### Section 2 #### Section 2
lodging_section = Section( lodging_section = Section(
title = "Hotel Info", title = "Hotel Info",
html_description = "<p>Enter hotel info here.\nPer diem rates can be found at <a href='https://www.gsa.gov/travel/plan-book/per-diem-rates'></a></p>", html_description = "<p>Enter hotel info here.\nPer diem rates can be found at <a href='https://www.gsa.gov/travel/plan-book/per-diem-rates'></a></p>",
fields = { fields = {
"check-in_date": {"label": "Check-in date", "type": "date"}, "check-in_date": {"label": "Check-in date", "type": "date"},
"check-out_date": {"label": "Check-out date", "type": "date"}, "check-out_date": {"label": "Check-out date", "type": "date"},
"rate": {"label": "Per diem nightly rate", "type": "decimal"}, "rate": {"label": "Per diem nightly rate", "type": "decimal"},
"cost": {"label": "Total Cost", "type": "decimal"} "cost": {"label": "Total Cost", "type": "decimal"}
} }
) )
section.add_rule( section.add_rule(
title = "", title = "",
rule = lambda report, section: rule = lambda report, section:
check-in_date = date(section.fields.check-in_date) check-in_date = date(section.fields.check-in_date)
check-out_date = date(section.fields.check-out_date) check-out_date = date(section.fields.check-out_date)
duration = check-out_date - check-in_date duration = check-out_date - check-in_date
return section.fields.cost <= duration * section.fields.rate return section.fields.cost <= duration * section.fields.rate
, ,
rule_break_text = "The average nightly rate cannot be more than the USGSA rate." rule_break_text = "The average nightly rate cannot be more than the USGSA rate."
) )
@ -82,20 +82,20 @@ section.add_rule(
#### Local Transportation #### Local Transportation
#### Section 3 #### Section 3
transport_section = Section( transport_section = Section(
title = "Local Transportation", title = "Local Transportation",
html_description = "<p>How much did you spend on local transportation, in total?</p>", html_description = "<p>How much did you spend on local transportation, in total?</p>",
fields = { fields = {
"duration": {"label": "How many days was your trip?", "type": "decimal"}, "duration": {"label": "How many days was your trip?", "type": "decimal"},
"cost": {"label": "Total cost", "type": "decimal"} "cost": {"label": "Total cost", "type": "decimal"}
} }
) )
transport_section.add_rule( transport_section.add_rule(
title = "Total cost check", title = "Total cost check",
rule = lambda report, section: rule = lambda report, section:
return section.fields.cost <= section.fields.duration * 10 return section.fields.cost <= section.fields.duration * 10
, ,
rule_break_text = "Local transportation costs must be less than $10 per day, on average." rule_break_text = "Local transportation costs must be less than $10 per day, on average."
) )
@ -104,38 +104,38 @@ transport_section.add_rule(
#### Per Diem #### Per Diem
#### Section 4 #### Section 4
per_diem_section = Section( per_diem_section = Section(
title = "Per Diem", title = "Per Diem",
html_description = "<p>Enter info about meals and incidentals here.\nPer diem rates can be found at <a href='https://www.gsa.gov/travel/plan-book/per-diem-rates'></a></p>", html_description = "<p>Enter info about meals and incidentals here.\nPer diem rates can be found at <a href='https://www.gsa.gov/travel/plan-book/per-diem-rates'></a></p>",
fields = { fields = {
"duration": {"label": "How many days was your trip?", "type": "decimal"}, "duration": {"label": "How many days was your trip?", "type": "decimal"},
"rate": {"label": "What is the per diem rate for your destination?", "type": "decimal"}, "rate": {"label": "What is the per diem rate for your destination?", "type": "decimal"},
"cost": {"label": "Total Cost for meals and incidentals", "type": "decimal"} "cost": {"label": "Total Cost for meals and incidentals", "type": "decimal"}
} }
) )
per_diem_section.add_rule( per_diem_section.add_rule(
title = "Per Diem Cost Check", title = "Per Diem Cost Check",
rule = lambda report, section: rule = lambda report, section:
return section.fields.cost <= section.fields.duration * section.fields.rate return section.fields.cost <= section.fields.duration * section.fields.rate
, ,
rule_break_text = "The average cost per day for per diem expenses cannot be more than the rate specified by the USGSA." rule_break_text = "The average cost per day for per diem expenses cannot be more than the rate specified by the USGSA."
) )
''' '''
Section( Section(
title = "", title = "",
html_description = "<p></p>", html_description = "<p></p>",
fields = { fields = {
"": {"label": "", "type": ""} "": {"label": "", "type": ""}
} }
) )
section.add_rule( section.add_rule(
title = "", title = "",
rule = lambda report, section: return boolean_statement, rule = lambda report, section: return boolean_statement,
rule_break_text = "" rule_break_text = ""
) )
#// or, for a rule which doesnt apply to a specific section... #// or, for a rule which doesnt apply to a specific section...

View file

@ -1,17 +1,5 @@
"""reimbursinator URL Configuration """
reimbursinator URL Configuration
The `urlpatterns` list routes URLs to views. For more information please see:
https://docs.djangoproject.com/en/2.1/topics/http/urls/
Examples:
Function views
1. Add an import: from my_app import views
2. Add a URL to urlpatterns: path('', views.home, name='home')
Class-based views
1. Add an import: from other_app.views import Home
2. Add a URL to urlpatterns: path('', Home.as_view(), name='home')
Including another URLconf
1. Import the include() function: from django.urls import include, path
2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
""" """
# Rupika Dikkala # Rupika Dikkala
@ -23,5 +11,5 @@ from django.urls import path, include
# add urls to this array # add urls to this array
urlpatterns = [ urlpatterns = [
path('admin/', admin.site.urls), path('admin/', admin.site.urls),
path('backend/', include("backend.urls")), path('api/v1/', include("backend.urls")),
] ]

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB