From 38a9464870c0bf6aa412496007d82d990a59af1c Mon Sep 17 00:00:00 2001 From: kououken Date: Thu, 7 Feb 2019 15:32:21 -0800 Subject: [PATCH] Implemented policy parsing, applied policy to report creation, added field_name to fields. --- .../migrations/0005_field_field_name.py | 18 ++++ back/backend/models.py | 3 +- back/backend/policy.py | 68 +++++++------ back/backend/test.py | 3 + back/backend/views.py | 93 +++++------------- back/db.sqlite3 | Bin 89088 -> 97280 bytes 6 files changed, 87 insertions(+), 98 deletions(-) create mode 100644 back/backend/migrations/0005_field_field_name.py create mode 100644 back/backend/test.py diff --git a/back/backend/migrations/0005_field_field_name.py b/back/backend/migrations/0005_field_field_name.py new file mode 100644 index 0000000..caf25f5 --- /dev/null +++ b/back/backend/migrations/0005_field_field_name.py @@ -0,0 +1,18 @@ +# Generated by Django 2.1.5 on 2019-02-07 22:02 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('backend', '0004_auto_20190131_1645'), + ] + + operations = [ + migrations.AddField( + model_name='field', + name='field_name', + field=models.CharField(default='field', max_length=512), + ), + ] diff --git a/back/backend/models.py b/back/backend/models.py index bb26ed3..b59c1ec 100644 --- a/back/backend/models.py +++ b/back/backend/models.py @@ -26,12 +26,13 @@ class Section(models.Model): class Field(models.Model): section_id = models.ForeignKey(Section, on_delete=models.CASCADE) + field_name = models.CharField(max_length=512, default="field") label = models.CharField(max_length=512) number = models.IntegerField() type = models.CharField(max_length=128) completed = models.BooleanField(default=False) data_bool = models.BooleanField(default=False) - data_decimal = models.DecimalField(max_digits=9,decimal_places=2, null=True, blank=True) + data_decimal = models.DecimalField(max_digits=9, decimal_places=2, null=True, blank=True) data_date = models.DateField(default=datetime.date.today) data_file = models.FileField(upload_to='uploads/%Y/%m/%d/', max_length=512, null=True, blank=True) data_string = models.TextField(default='', blank=True) diff --git a/back/backend/policy.py b/back/backend/policy.py index 95e401b..1131e30 100644 --- a/back/backend/policy.py +++ b/back/backend/policy.py @@ -1,10 +1,36 @@ -# simple_policy.py from datetime import date -from policy import Policy, Section -# - For the rules, should one refer to fields by 'section.fields.x' -# or by the section name eg. 'general_section.fields.x'? +#### Classes for policy, sections. +class Policy(): + + def __init__(self): + self.sections = [] + + def add_section(self, section): + self.sections.append(section) + +class Section(): + + def __init__(self, title="Section", html_description="", required=False, auto_submit=False, fields={}): + self.title = title + self.html_description = html_description + self.required = required + self.auto_submit = auto_submit + self.fields = fields + self.rules = [] + + def add_rule(self, title="Rule", rule=None, rule_break_text=""): + rule = { + "title": title, + "rule": rule, + "rule_break_text": rule_break_text, + } + self.rules.append(rule) + +#### Policy configuration begin here + +pol = Policy() #### General #### Section 0 @@ -12,7 +38,7 @@ general_section = Section( title="General Info", html_description="", fields={ - "destination": {"label": "Destination City", "type": "string"} + "destination": {"label": "Destination City", "type": "string"}, } ) @@ -22,7 +48,7 @@ general_section.add_rule( rule_break_text="What did the cowboy say about Tim, his wild horse?" ) -Policy.add_section(general_section) +pol.add_section(general_section) #### Flight #### Section 1 @@ -43,7 +69,7 @@ flight_section.add_rule( rule_break_text="Fares cannot be more than $500" ) -Policy.add_section(flight_section) +pol.add_section(flight_section) #### Lodging #### Section 2 @@ -64,13 +90,13 @@ def nightly_rate_check(report, section): duration = checkout_date - checkin_date return section.fields.cost <= duration * section.fields.rate -section.add_rule( +lodging_section.add_rule( title="", rule=nightly_rate_check, rule_break_text="The average nightly rate cannot be more than the USGSA rate." ) -Policy.add_section(lodging_section) +pol.add_section(lodging_section) #### Local Transportation #### Section 3 @@ -89,7 +115,7 @@ transport_section.add_rule( rule_break_text="Local transportation costs must be less than $10 per day, on average." ) -Policy.add_section(transport_section) +pol.add_section(transport_section) #### Per Diem #### Section 4 @@ -109,24 +135,4 @@ per_diem_section.add_rule( rule_break_text="The average cost per day for per diem expenses cannot be more than the rate specified by the USGSA." ) -Policy.add_section(per_diem_section) - -''' -Section( - title="", - html_description="

", - fields={ - "": {"label": "", "type": ""} - } -) - -section.add_rule( - title="", - rule=lambda report, section: boolean_statement, - rule_break_text="" -) - -#// or, for a rule which doesn’t apply to a specific section... -#// -#// add_general_rule(...) -''' +pol.add_section(per_diem_section) diff --git a/back/backend/test.py b/back/backend/test.py new file mode 100644 index 0000000..6ecdccb --- /dev/null +++ b/back/backend/test.py @@ -0,0 +1,3 @@ +from policy import pol + +print(pol) diff --git a/back/backend/views.py b/back/backend/views.py index 70e3f79..1c368c2 100644 --- a/back/backend/views.py +++ b/back/backend/views.py @@ -1,7 +1,8 @@ from rest_framework.decorators import api_view from django.http import JsonResponse from .models import * - +from .policy import pol +import json # function that prints all the reports def get_reports(report_pk): @@ -47,15 +48,14 @@ def get_sections(r_id): def get_fields(s_id): # create dict of arrays for fields field_set = {"fields": []} - queryset = Field.objects.filter(section_id=s_id) - # queryset = Field.objects.all() + queryset = Field.objects.filter(section_id=s_id).order_by('number') for i in queryset: data = { - "field_name": "TODO", + "field_name": i.field_name, "label": i.label, "type": i.type, "number": i.number, - "value": "get_value", + "value": "i.to_json()", } # append the fields to array # use copy() to avoid overwriting @@ -70,73 +70,34 @@ def report(request): ''' Generate a new empty report and return it ''' - 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": "

Enter flight details here.

", - "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": "

If you used a hotel, please enter the details.

", - "fields": { - "total": { - "label": "Total cost", - "type": "decimal" - } - }, - "rule_violations": [ - ] - } - ] - } + + # Create the report + report = Report.objects.create(user_id=request.user, title=request.data['title'], date_created=datetime.date.today()) + report.save() + + # Create the sections + for i in range(len(pol.sections)): + section = pol.sections[i] + s = Section.objects.create(report_id=report, auto_submit=section.auto_submit, required=section.required, completed=False, title=section.title, html_description=section.html_description, number=i) + s.save() + + # Create the fields + j = 0 + for key in section.fields: + field = section.fields[key] + f = Field.objects.create(section_id=s, field_name=key, label=field['label'], number=j, type=field['type'], completed=False) + f.save() + j = j+1 + + # Return the newly created report + data = get_reports(report.id) return JsonResponse(data) # List of reports @api_view(['GET']) def reports(request): report_set = {"reports": []} - queryset = Report.objects.all() + queryset = Report.objects.all().order_by('date_created') for i in queryset: data = { "report_pk": i.id, diff --git a/back/db.sqlite3 b/back/db.sqlite3 index 8074d09696b7ab103c40188a656433376bef08ba..77fd67fd08e54caf24731ac1e3dfd3ef6ad93a2e 100644 GIT binary patch delta 12088 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 TOVPAFnN74?9o9``L`DA(muqBP delta 2260 zcma)8du&rx7(eHHx9h!Y?*@}OHc-0_onsE}qwO|?g-7W&la8n`Y<*zY-fex)wuF~v zEFh@CL_KQ#Lk&Sp^be!)h75@WgT|;azRiq}Kh)?V@r4O!#2CMO_vj)TZEk<3=k)hI z&hPt9Pxt=j*n8aZKtm1rlMu3X=1p64R8U@!&&$qrr<01 z0zQEc;T?DrPQhz%0*-N2ESQu&&-e>V99LWX5rmKh8zKnT-Hy<`1z}}BLhBlYt2FZq z@C(&ffEOMD2RzH3Vc)QCOw}D_d|%~y+RI_p~p8u?^Y10(7t6kCB6`w~@S9!beS<%(0cjy|@50mlMbz+??w~5kt6@N?nb)ny* zsjKkudRHi_?(=t=ALv?OQ4{zGe2p4U!uxO+`~=@Y%Iw~GH{)x?$4uXzcLivIt=zeH zqq%UY3;5Q3&$$_##~5V5s*<_jjc>s`f3Qk`YLes5LmsY)y=LyvBe2vBCkgxkzrru@ z1AGP_!@F3+Dfkjzg_G#*3FvhIrGnc=mxZj-M#)CeMyG{PVWUF;i3%2g1q?uvWOf^I zn2v{UVYJp9eDnwv8Jr_<4lZDb?_i1Tgb6?;kTGciDkz+Uov<1zkZ_)j>4HQ=QEcp^ zv^Q5Y(w@HTP;T{T_w*!z6HnYI#PW$ztN1HcuN>`WjI097N@@&K>EEno+T~*F4Ge81 zu{j~pV0tVuJW`yE87K$xsY>?u}_1KD>cuQ{ypPmai?%LeJ0Qd5k+Tw*c0n6{eLN6t~V0B0zc^ei6FGbrDu z&}8c?psrYyX|3(5pl1dL2_D#k$lC*B*D@z*(M77?Xkg#RJvZ3rX1a^jc1`yv$|}u^ zUCd_Jbk$m$eyEdNsY_H3Ch~>i-)vR?jIHW}SzEPZW=-p`HtD7f_Yim+4nqN|*(5v4 za%??YN~h=vdXK4!BXmr0%cPwy5k(@)B5my7o*6XqRuQt6{sor6vpQ_#h1m$Y+UAl; zi&Q^n5zQDDf5d8{LNTAn4$mX&l``p~wX@`4G;eqcMl>HA@nCNm##pw1hQd%HZOjv3 znIx0ia#EHR#En=Y6HU)!wTUumE&C1_qtSeEI|hhHiw3iyoVD(hiBFo_XCH1WpN$sB zu(zY>nQj}*<Md=2zhs_m0SN#rK9?PWJ67*tudUNSSJh}^;KMUS+ zhJ%7F6&hw_JYg3}RaS~m?N@?j!!|tOsFox;v4qoxvudr)zkzK)NSVBx*`nDyLUQTH z2(1sQ?W(3xTOdiY8bNBD73d%IDLb?7E(#Z{2!6Q0RiV=izIP1jaqbf;5i6jXz~|5m zy)c3IPX^CKHC(w2LNw+biSEiJ{h4e^%jr8hRCVW$RAzgpH1+-xrhtzY25$O;(`B4ZBrnnZwdqx;mmc( zNML;`lnjpaC;O9OZ6MX(gP*pBlt?DDYt!aFCA>MLgm!OH`!{pX8C6u{di}Y+% zBgwED%JeCbz`EW2T6kx8_pmRL?AsCEjNc?9$xt%v4JX4oj{J_v26`huG&xwKU6i}I z|Kt0z4F14B2fd}60(7|QCQnmge09oMXKbM3x0V`PXg&Y4(mC-%fYw#J1$LUi0f?|4 s*=dyIMK*DIfOgYGRt;xf12|fLAbQZ}E2SqydaSfqqn}O0gY