From f95bd9ecee99b3c047246515d64c17e641cc2b0c Mon Sep 17 00:00:00 2001 From: kououken Date: Thu, 14 Feb 2019 14:19:15 -0800 Subject: [PATCH 1/2] Implemented rule_violations in json for GET report. --- back/backend/policy.py | 20 ++++++++++---------- back/backend/views.py | 26 +++++++++++++++++++++++++- back/db.sqlite3 | Bin 105472 -> 105472 bytes 3 files changed, 35 insertions(+), 11 deletions(-) diff --git a/back/backend/policy.py b/back/backend/policy.py index 381405e..7af3281 100644 --- a/back/backend/policy.py +++ b/back/backend/policy.py @@ -45,7 +45,7 @@ general_section = Section( general_section.add_rule( title="Destination city check", - rule=lambda report, section: section.field.destination == "Timbuktu", + rule=lambda report, fields: fields['destination'] == "Timbuktu", rule_break_text="What did the cowboy say about Tim, his wild horse?" ) @@ -68,7 +68,7 @@ flight_section = Section( flight_section.add_rule( title="Airline fare pre-approval check", - rule=lambda report, section: section.fields.fare < 500, + rule=lambda report, fields: fields['fare'] < 500, rule_break_text="Fares cannot be more than $500" ) @@ -88,14 +88,14 @@ lodging_section = Section( } ) -def nightly_rate_check(report, section): - checkin_date = date(section.fields.checkin_date) - checkout_date = date(section.fields.checkout_date) +def nightly_rate_check(report, fields): + checkin_date = date(fields['checkin_date']) + checkout_date = date(fields['checkout_date']) duration = checkout_date - checkin_date - return section.fields.cost <= duration * section.fields.rate + return fields['cost'] <= duration * fields['rate'] lodging_section.add_rule( - title="", + title="Average nightly rate", rule=nightly_rate_check, rule_break_text="The average nightly rate cannot be more than the USGSA rate." ) @@ -108,14 +108,14 @@ transport_section = Section( title="Local Transportation", html_description="

How much did you spend on local transportation, in total?

", fields={ - "duration": {"label": "How many days was your trip?", "field_type": "decimal"}, + "duration": {"label": "How many days was your trip?", "field_type": "integer"}, "cost": {"label": "Total cost", "field_type": "decimal"} } ) transport_section.add_rule( title="Total cost check", - rule=lambda report, section: section.fields.cost <= section.fields.duration * 10, + rule=lambda report, fields: fields['cost'] <= fields['duration'] * 10, rule_break_text="Local transportation costs must be less than $10 per day, on average." ) @@ -136,7 +136,7 @@ per_diem_section = Section( per_diem_section.add_rule( title="Per Diem Cost Check", - rule=lambda report, section: section.fields.cost <= section.fields.duration * section.fields.rate, + rule=lambda report, fields: fields['cost'] <= fields['duration'] * fields['rate'], rule_break_text="The average cost per day for per diem expenses cannot be more than the rate specified by the USGSA." ) diff --git a/back/backend/views.py b/back/backend/views.py index 04fc6dd..4445435 100644 --- a/back/backend/views.py +++ b/back/backend/views.py @@ -28,15 +28,31 @@ def get_sections(r_id): # create a dict of arrays for section section_set = {"sections": []} queryset = Section.objects.filter(report_id=r_id) - for i in queryset: + for index in range(len(queryset)): + i = queryset[index] data = { "id": i.id, "completed": i.completed, "title": i.title, "html_description": i.html_description, + "rule_violations": [], } # append the fields for corresponding section data.update(get_fields(i.id)) + # process rules from the policy file if the section is completed + if i.completed: + rules = pol.sections[index].rules + for rule in rules: + try: + named_fields = generate_named_fields_for_section(data['fields']) + if not rule['rule'](data, named_fields): + info = { + "label": rule['title'], + "rule_break_text": rule['rule_break_text'], + } + data['rule_violations'].append(info) + except Exception as e: + print('Rule "{}" encountered an error. {}'.format(rule['title'], e)) # append section to the array section_set["sections"].append(data.copy()) @@ -67,6 +83,14 @@ def get_fields(s_id): return field_set +def generate_named_fields_for_section(fields): + result = {} + for field in fields: + key = field['field_name'] + value = field['value'] + result[key] = value + return result + # API Endpoints @api_view(['POST']) def report(request): diff --git a/back/db.sqlite3 b/back/db.sqlite3 index df940de7c8c054f4ee96162876249a47ea35ec8b..7617b83a92b94fe463a87a79d04c015cf53b7ac3 100644 GIT binary patch delta 3248 zcmbW34Qx}_6@cHp*ACAi4hkeGO$ap*LIUy2`+t5eKXHijA14MwVnayW#3XUzI59YnQ9HqzcXN4j)K|P@=Jpe ztb1jF6E%;jXhw}BWQ)bihz^NfUiAtdPSsV9CS?C0iH&Q5xSSz>A)k^@$VcP|eg|K~ zJFt!=?8cjn(iz@{PvUu_X{K^DegRM8Bp$_sxDWT>Aa2KgybhOV<1^nub9RCd8?@L> znb&18V6oF;hsAbEw#{Oz#TJXr7Mm>kEjC)*X|chgk8)YP#T{19c8lBW+1E00I|<@( zW7)AIRrr1UEBr2A#8>cTd;!0S&*AUmv-o9v3crYtfg#W1LwGzZus2Y@jW|${qMM?N zB0$ke(LvEp(MHiq(L&Ko(L~{=Xr$Ol(Lmv&sHfPG{a`^yEA8YC#Ec1(f03`rm*fj_ zCtG^_BVvcXgU#75PL{BJq?aiq)ePB1I!F_#Cte~Gj#QIBk<$4d-MfKnYj*)x*9L(rYrBC3wOzoxE!GLx+(+Z2B zCtopnFhKh!Kz-i*QFZdlS!7!dgNWQ?!2EmJf>-a7N|;fAOXxgY?8F>nM^_ni6@86+ z@t0&b(@h^^_3Iw`x*cIgNpH2eGa6 z${RMD%T7LLAo6#?^sgZDKmO*Et$gbZ(gNBliv^%s%mew7I^gCKj`o+hsZ&cui;_jz zqGC~Xk`v(EMobz>W47QzhS*?L#yEA+Lw{_~zJIZe&Bs?s3%*4b@xQZc*=}sauLah{ zv{6N#mB&U#wV{llspH9*JUOk!6Up?*prntl$lkiP&CZe+@g@8>d?{PB<|S-gdau3I z^RNVIf+CB;inYXtj01lx%WnGRr^Wf0;TSZ-aQKEH*j!oe<5L);w(Ku%HqBQ$@hzs1 z*-OZ8$jc-|Y>>d`n7vqLUP6CI@1lORn)v`$i4E##A+yd=&a%s#5Sx;9p=26Dy)!wL z9*PZ)CZgl@-`bSFZSu`e zN4C}ZjLwiBkv4n>UI7K@RpeoAFb1Qa@m08cgAT^G>Zw8GKo5)*tapt7?;3>@R9#ki zf!76|=Xm4%$+EejXgb=zw3qdLM~?Kd*xgNqtSZVS1lEbNCTNlXACBJ#)6-q~jIYQ& zZ-0Eg=#?c-_2?c+QuzN)^y&?K&Au1b5002`&7x<_u31!G6#%V8Tl#WI5mZhQWLXy! z!8|>S^y*Y6)mf8UC$DpgDCizp5zUHO)UkcIgNojkL$u&kR8H0<4JJz+$w4@rp;UAN zs!u9y9hK@0l6h_xZILG1sm6`DGz##N=M+`Y6itz>Dl-~ORBw>!!j06nvf5}Hb!o}r z$Bh%blE(46;_)Z~FNRXnG4uQ^s`S;m)?1-@E}0x(ic7ea3VAw5a0RcRaS#<*^9a18 zDG*#My1pFI4Yxd53a@Y;NtGl`;1wC9$TB@6t8z%WHiVGN3FnnSnZVc3IK!Rs6^|@5 zudsI>ukejUpPycMmj1T;{#S+SY8;yh^z6w5di+YHIV=S-O^MEse^B+4DKi`=Mijii&5Q&F{&O|F8Y4l|RVt6K;8CD|k z*2AzQ9*#%io$;_7j)x`qHqQKPmBC)_duFC}g5UNaQN+h8;v#n(wIOz)xxg~YIJD?m z{Okm(a+WP;4lv}Wa6PENf5mh7Y4mF}f(n`I%mG7S>)pN0)W^Bf5H5rsPWE`f%>u7! z%2J@V=5TSSi3(YfBiy`KQaDAH6&(`UBheVu=Md2AfATIw0WO}rsKa^D#Rt2{d0ZNw zbX<5ZFL9ctNg|X2UV+M>ScfjBEjyoJ3-9rYBFBR>B&(!QTXy8IWvGD)C^_<20`Z!a z!m21~GVCLeoVcGrJ0B#_=tMd;97`?bP|RofB&Qrn=5$3;RaFo~krqfR6XzZXdbCI? z^&qII#`*Y$RIjXZ;8&UVh`jG`Y$}iGE(=fOOjrjb0UF(1xJVOX2{u2(&(E`=ITIm;4EA!r^5X-+K);p#K2mq3t*T delta 1104 zcmYMyUuYCZ90%~7`R*m#T!K;dq}XVWklI{KHoLcX<~(XJG`VQv<Z&0sia8g?jP z`z^<3TTJ&~A!5nT=fi=J5!z-3LpHl@`73)um`*%X#ildw z5@&~Yft}5cyU<8)IK>kun5DH+c4KNADOa(|526g=2_EBjtTO$}@4;5z_Go>4x(2F( zKk)}1;URv)4_L-cT*v2F!Xg$hk4u=vMa7%ap^0< z+L*Lk+9mB2Zs`!#^htYNJ~4>O!ET|iUD_tyCEY2kZe5rms+$)DcqEI$N-sP_l~|)5 zsqW&d1W&k1@4p45Ci$9FI?uiz;!QY8lV?7iLw}y9Ss3Yl&#Y|805N5?1x$EaZuGOPoqK zg`Rq;A+3|vO1B908mUiOUB)*9nklZ~G%LNFg&Ou0E6$m@kouBdtgtWU+O!hvr1wyu zAJf0tCasG)>3oNCec4Og>|TDmOQXNy6rHE@Y;DCk>dY^-ueZH^OFUgWV3@XLSzFzk z&_ta6I~&-GpWW9>pco3tCd*;MnohYYm5n?V-_B@Qva0@sT6Z=EIr9Xw*)`W9jI=$Vg9oPo^`z z%b&=$_@mjD@y^!nU}tmG>PSb1`FFOXwZ%+0KVI43{B}oo+HZF|AAH;8++A9yO|_P~ zq9->tImZg#!h_RfbD2gFQ}7XDyiyV5Vmb23MseK)j{zRyekmUPJYEpN|2t0$`HTDl F{{X1X3grL* From 4704b86f2ae94b2c44ba08f39ceb3ca9cdd9457e Mon Sep 17 00:00:00 2001 From: kououken Date: Thu, 14 Feb 2019 14:23:05 -0800 Subject: [PATCH 2/2] Set section boolean values to default to False, and set field numerical values to default to 0. --- .../migrations/0008_auto_20190214_1421.py | 38 ++++++++++++++++++ back/backend/models.py | 10 ++--- back/db.sqlite3 | Bin 105472 -> 105472 bytes 3 files changed, 43 insertions(+), 5 deletions(-) create mode 100644 back/backend/migrations/0008_auto_20190214_1421.py diff --git a/back/backend/migrations/0008_auto_20190214_1421.py b/back/backend/migrations/0008_auto_20190214_1421.py new file mode 100644 index 0000000..0aff026 --- /dev/null +++ b/back/backend/migrations/0008_auto_20190214_1421.py @@ -0,0 +1,38 @@ +# Generated by Django 2.1.5 on 2019-02-14 22:21 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('backend', '0007_merge_20190213_2318'), + ] + + operations = [ + migrations.AlterField( + model_name='field', + name='data_decimal', + field=models.DecimalField(decimal_places=2, default=0, max_digits=9), + ), + migrations.AlterField( + model_name='field', + name='number', + field=models.IntegerField(default=0), + ), + migrations.AlterField( + model_name='section', + name='auto_submit', + field=models.BooleanField(default=False), + ), + migrations.AlterField( + model_name='section', + name='completed', + field=models.BooleanField(default=False), + ), + migrations.AlterField( + model_name='section', + name='required', + field=models.BooleanField(default=False), + ), + ] diff --git a/back/backend/models.py b/back/backend/models.py index 0ab711a..e52b763 100644 --- a/back/backend/models.py +++ b/back/backend/models.py @@ -15,9 +15,9 @@ class Report(models.Model): class Section(models.Model): report_id = models.ForeignKey(Report, on_delete=models.CASCADE) - auto_submit = models.BooleanField() - required = models.BooleanField() - completed = models.BooleanField() + auto_submit = models.BooleanField(default=False) + required = models.BooleanField(default=False) + completed = models.BooleanField(default=False) title = models.CharField(max_length=256) html_description = models.TextField() number = models.IntegerField() @@ -29,11 +29,11 @@ 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() + number = models.IntegerField(default=0) field_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, default=0) data_date = models.DateField(null=True, blank=True) 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/db.sqlite3 b/back/db.sqlite3 index 7617b83a92b94fe463a87a79d04c015cf53b7ac3..e74167e1ff0b568b03610b45df5cec5bd420eaa7 100644 GIT binary patch delta 2099 zcmcIlZA?>F7(VZPTez3DSHVD$QYfJES=Sa?tPF=D+cL6Yj0*Fs7J8LRX=^EFnNBS@ zHE|iZ1G&qh&f(%m;tyw|!j{Z^WpQ&+r;F^zW}?ZaKcwey?e0C7$mW$-z449)tsUiP@Oi~U$^@tZhXW8%4s zEWjJsf)duD7LRksxL@!cynqi(BlaOd@fq2YlD0`q${hO3Etjlrzb2$y8S&p3{hjuG2x6ttbE+n z6~Co3heZv^BCwfUz;5;sqc6x~@e5mBGfG&_BAy`l0ROMvdRMG@o6LXfYyER!^pduET#Ecyg7w5p0)on(j!L;)X+pj2qWLc-Qo7a^?25y z@4yhNaAgplH}|;#1lb#cS}MQq*ROLbR%gt>tn}f*R((#5VkqiU408QOD3QlHp;MpZ zsuq~Nq(xvjIB3;1IF$8lFVEKJ4%8+}g6og)ewgGHR9^luUP0yMQ@n!8%Rj*@D7<`% zS5SC)h4<7kIH}Hn>?Ew?1Go$|+&H(N^Klh$2aZ55PwMb@djsUW!)A9|Z4Rrg*3}W}iE>RLku)*5z~BI8K@M(%l36Jf z=koPNyo$zF1x3y#b5DG?9TX<=|Az zO#y{}(!tCp_;CtqlE1D?B(+H-D=brjd`EK%S)(L?^EQg4G@EBLR_ZT>#!jiD%jyqW zeC(}q%YVNOx!XCHKauG~ITM*SrZR2VTAEQGK0H2!KuOr$DU$MJ{Bk}KKhzsd5=?ba z$=OSf!d$sQB-yM2HX4UIo9Dd|X`(OO-IQDPdS=I0){Eq2lORkMB7e3WEa~l`P(bnq zSFgI2EL{gGWxP)ISF+i#vQ)naV8#UGW9U`&L zoa|x=FMkvUqx{7nWQ)qj&A|c!oDdwR$i1p4Rg|i7a$c3WkbwOMne%K)iGb;f%w26$0xBwN*#08x z0wJp0g(MiBkbBBcRl84>dosEiRJmuVsKgHTzwIQV0X`udu@Yj%c4B24Mj`g=UZ+=w zDxiX18p)Am4~TT${ke3>u$k^2zD73==jqA`fuqJ&C&ZfRvqSDQ^`t4IEh9PftszH? zx-7Po6-T6SC=&Jfee5b$oDLYBPI$95>TM55PdlxiW-C|n&`&+nsQtc^=;M34kVFuR$dEIriZq#CT@@@7#0s#ad#jTR1^tv)Sf(*nt@G zD4n)B>@^;Hjl(`U9D9w$;h4`ZTlHL*&26i8on5pSZs-kb*oarMo7F?gSm0gU*)#9K GOYkqHPaz`! delta 2027 zcmai!e{54#6vyv*r!BABTBK{ct{Y=xIM!dI9bIu7=-3#8@oS798NxO?`o@}eZPyn6 z!j)-Mh*1U)yv6`Aq6sP?#KHhWVoZ!dr%2QgjXxqJiUuViAw&^>oY#&`!08|N``$f2 z?z#7K&wD#gYj&L03>QrsVvKDa8#(mczsEV(GsIj!^l6;G0~4*VT&;&uEPui$0; z2EV|Qcpg8%ckpd|4?n}>vPT?91Ed9Yl)`QD8JwQi9-`L@H%aO2^ksZdRiqKl(*lp< zDwMDS>rsub3rB?SaWC#odmiu$@{>7@FhhPnr$kd$Qh-e&-_s05vO2fe%BDb%KR*W@ z^w<)TB7yFNv?UpgMq1_i+zznH`*JHCRp8&2?CXw6@%~^UK~8Uhxez_$DZN1{9O&+j zh6AzmmE7sEJY=H`9%G}}k3qRDJVHmtx`OoRc3*xub}&6wGHl1?xELF-4!u}~mH0Ig zxPaI2BHohS!%n$jI4`|vxD9*pA}evWlUkgsNe#|6(mdxXQjKG!8UgH`iq`Zi>WeDs z4P`80yumbM_6PyfWWBzaPUvFDsu{b$s<8!ggoNx-mEus5gG(W306R~0f|;KU!6JMz z*UsI2V24T$U1S{SgAIIY2(0|WKDcPM(Yikscs%e_FzN3JOIePxlYcH7wx4%u5_E`klIaEio@i}2K^%;o19(9_tp_OUM_se^lUv+%kRdus1gQ* zLu|d^WzWMea2C43!ba(Os`>g3s4`Z&J+&@(waY!PKBQ;!phy&f9~*>n>oRZLpOhy2 z%VK)A4vMs*z>meDx`dtcZHXq569LUpJ!{rdfL5mizB&OFC4w)ojaH!^p9tF+(X)_9 zb*Av&;xM0gZ2+;*cXzEh{d!iW&Ftml0VuErYVIi&rc%lpz*i=~2FUjeLO%a)5b7%E z_>Qf5W>StK=yW2O$`OxHh+!ro15q%_?H*;JZ>#C1|z$ObS}KCk97f zNP;RyfJO#iK_lL#zs4XAl}7pQO>j8>sM4G}P2 z-rogDe!B~<@~d57JoQ&Ms8w9G13GzW3XFw!_T;+zbPuiL;W(6ni$52K0!{5&J+or6f%kNj3JvTO7TdhSN!3L(7KiUa13S@I|5Q78O-`8ytOMP(AdY&EWZzp z{T?*S{uhmHQZ%jqjm9lJ_?`QbFf<2>{bkD^f~2U z57wZ`?BMPQm?+0&=l*`!CmMY=EfYmi{^Ot?-!Km8^yKt8vp