:Class OQ : MiPage :Include #.HTMLInput ⍝ Useful functions for creating HTML pages :Field Public Action←'' ⍝ All Buttons will have this name, Value will tell which one was pressed :Field Public DOBm←'' ⍝ Name of dropdown field :Field Public DOBd←'' ⍝ Name of dropdown field :Field Public DOBy←'' ⍝ Name of dropdown field :Field Public MEAm←'' ⍝ Name of dropdown field :Field Public MEAd←'' ⍝ Name of dropdown field :Field Public MEAy←'' ⍝ Name of dropdown field :Field Public Sex←'' ⍝ Name of dropdown field :Field Public Name←'' ⍝ Name of input field :Field Public HTft←'' ⍝ Name of dropdown field :Field Public HTin←'' ⍝ Name of dropdown field :Field Public WTlb←'' ⍝ Name of dropdown field :Field Public WToz←'' ⍝ Name of dropdown field :Field Public HTcm←'' ⍝ Name of input field :Field Public WTkg←'' ⍝ Name of input field :Field Public res←'' ⍝ Name of readonly edit window ∇ Init :Implements Constructor :Access Public Colors←'white' '#a5a8b0' '#E6E6FA' ⍝ '#728FCE' ⍝ white, violet '#8D38C9' ,light steel blue Colors←'#a5a8b0' '#FF6868' 'yellow' 'LightGreen' ⍝ #a5a8b0 is dark grey #FF6868 light red ⍝ p.ex {color:white;} Name AgeD Oq CRwt CRht MBMI Bmi Sex HTft HTin WTlb WToz HTcm WTkg res←⊂'' DOBy DOBm DOBd←⍕¨3↑⎕TS MEAy MEAm MEAd←⍕¨3↑⎕TS ∇ ∇ Render req;html;BR;sp;atts;Oqs;res;cells;findrm;colorrow;TOQ;TWQ;THQ :Access Public :If ~req.IsPost ⍝ reset field values if not submitted from the form Init :EndIf sp←⊂' ' ⍝ blank space BR←'<br>' Note←'' DoAction ⍝ If a button was pressed, deal with it ⍝ {⍺←0 ⋄ 0=⎕NC ⍵:⍎⍵,'←''',(⍕⍺),''''}¨'AgeD' 'Oq' 'CRwt' 'CRht' 'OqTile' 'wtTile' 'htTile' ⍝ initialize if needed to '0' req.Title'OQ:Obesity Quotient for Children 0-5' ⍝ Page title html←'h2'Enclose'OQ:Obesity Quotient for Children 0-5 years old: A New Index' ⍝ Header in page html,←'h3'Enclose'by P. Dunn-Rankin, R. Rudoy and J. M. Brennan obesityquotient.com' ⍝ html,←'h3'Enclose'<a href="http://cpj.sagepub.com/cgi/reprint/54/5/484.pdf?ijkey=iCazqHDiPNBFzgN&keytype=finite">(Click here to download our PDF research article.)</a>' html,←'h3'Enclose'<a href="http://www.bbc.com/news/health-18770328">(Click here if you are 15 or older for interesting international weight comparisons!)</a>' html,←'<style>hx{font-size:16px}ha{font-size:14px}</style></head>' html,←'This program calculates a new metric, the Obesity Quotient (OQ). The OQ is infant''s Body Mass Index' html,←' (BMI) divided by the Median Body Mass Index (MBMI) of the criterion sex/age in days group.' html,←BR,BR,'The ratio is easily calculated and interpreted as a percentage above or below normal body mass.' html,←' For example an OQ=1.08 means body mass is 8% above average while an OQ=.95' html,←' would mean body mass is 5% below average.' html,←' An OQ=1.08 would place the child at the 85th percentile so only 15%(100-85) of kids' html,←' have body masses 8% or more above average. Similarly an OQ=.95 would place the child at the 25th percentile.' html,←BR,BR,'The Wt(WQ) & Ht(HQ) quotients displayed below provide similar easily interpreted weight & height percentages and percentiles.' html,←BR,BR,'Unlike the BMI by itself the OQ''s value is stable during the first years of an infant’s life' html,←' and may also be a more effective indicator of above average body mass or obesity later in life.' html,←BR,BR,'If you have any questions or concerns about your child''s OQ discuss them with their doctor.' html,←' This site provides information only. It does not provide medical' html,←' advice, professional diagnosis, opinion, treatment or services to you or any other individual, does not' html,←' create any patient-physician relationship and should not be used as a substitute for professional ' html,←' diagnosis and treatment.' html,←BR,BR,'<b><hx>Enter US or Metric child data & Press Calculate button to see results.</hx></b>' html,←BR,BR,' Sex=',('Sex'DropDown('Male' 'Female')(,Sex)),' Name: ',('Name'Edit Name 36),'Optional' html,←BR,' Month=',('DOBm'DropDown(⍕¨⍳12)(,DOBm)) html,←' Day=',('DOBd'DropDown(⍕¨⍳31)(,DOBd)) html,←' Year=',('DOBy'DropDown(⍕¨⌽1+(1↑⎕TS)-⍳100)(,DOBy)),'of Birth' html,←BR,' Month=',('MEAm'DropDown(⍕¨⍳12)(,MEAm)) html,←'Day=',('MEAd'DropDown(⍕¨⍳31)(,MEAd)) html,←'Year=',('MEAy'DropDown(⍕¨⌽1+(1↑⎕TS)-⍳15)(,MEAy)),' of Measurement' ⍝ EXAMPLES NORMAL AT BIRTH: 1ft 8" and 7lb 6oz are OR 31 DAYS OLD: 55cm 4.5kg html,←BR,'Height: ',('HTft'DropDown(⍕¨0,⍳10)(,HTft)),'feet',(⊃,/2⍴sp),('HTin'DropDown(⍕¨0,⍳90)(,HTin)),'inches',(⊃,/8⍴sp),'Weight: ' html,←('WTlb'DropDown(⍕¨0,⍳400)(,WTlb)),'pounds',(⊃,/2⍴sp),('WToz'DropDown(⍕¨0,⍳15)(,WToz)),'ounces',(⊃,/9⍴sp),'<b>',('Action'Submit'US Calculate'),'</b>' html,←BR,'OR' html,←BR,'Height: ',('HTcm'Edit HTcm 6),'cm',(⊃,/10⍴sp),'Weight: ' html,←('WTkg'Edit WTkg 8),'kg',(⊃,/16⍴sp),'<b>',('Action'Submit'Metric Calculate'),'</b>' ⍝ 50cm and 3.35kg are normal at birth cells←16 3⍴'<b>OQ' '<b>OQ %tile' '<b>OQ Label' '≥ 1.30' '≥99.9' 'Morbid' '1.22' '99' 'Obese' '1.17' '97' 'Decidedly' '1.15' '95' 'Quite' '1.11' '90' 'Somewhat' '1.08' '85' 'Slightly Over' '1.06' '75' 'High' '1.00' '50' 'Normal' '0.95' '25' 'Low' '0.92' '15' 'Slightly Under' '0.90' '10' 'Somewhat' '0.88' '5' 'Quite' '0.86' '3' 'Decidedly' '0.84' '1' 'Very' '≤ 0.79' '≤0.1' 'Extremely' Oqs←⊃¨{⊃(//⎕VFI ⍵)}¨1↓,cells[;1] ⍝ get OQ category values from col 1 findpc←{0=⍴,⍵:'0' ⍝ find correct percentile Input is Oqs findpc Oq (or CRwt CRht) p←cells[1+((1+dif)=1+min←⌊/dif←|⍺-⍎⍵)/⍳⍴⍺;2] 1=⍴p:(⍕p)~' ' 2=⍴p:{⍕(+/⍵)÷⍴⍵}⍎¨p~¨⊂'≤≥' } OqTile wtTile htTile←(⊂Oqs)findpc¨Oq CRwt CRht ⍝ percentiles for Oq CRwt & CRht :If 0=⍴,Oq ⋄ res←'<b><hx>RESULTS:</hx><ha>',Note,BR,BR,'BMI=',Bmi,'</ha></b>' :ElseIf 0=⍴,CRwt ⋄ res←'<b><hx>RESULTS:</hx><ha>',Note res,←BR,BR,'BMI=',Bmi,' MBMI=',MBMI,'</ha></b>' res,←BR,BR,'<ha><b>OQ=',Oq,'</b> body mass is ',(⍕100×|(|⍎Oq)-1),'% ',(⊃'below average' 'above average'[1+1<⍎Oq]),' placing child at about ',OqTile,' Percentile.' :Else res←'' html,←BR,BR,'<b><hx>RESULTS FOR THIS CHILD :' html,←BR,BR,'</hx><ha> Age in Years=',(2⍕(⍎AgeD)÷365),' Age in Days=',AgeD,' BMI=',Bmi,' MBMI=',MBMI,'</b>' html,←BR,BR,'<ha><b>OQ=',Oq,'</b> body mass is ',(⍕100×|(|⍎Oq)-1),'% ',(⊃'below average' 'above average'[1+1<⍎Oq]),' placing child at about ',OqTile,' Percentile.' html,←BR,BR,'<b>WQ=',CRwt,'</b> weight is ',(⍕100×|(|⍎CRwt)-1),'% ',(⊃'below average' 'above average'[1+1<⍎CRwt]),' placing child at about ',wtTile,' Percentile.' html,←BR,BR,'<b>HQ=',CRht,'</b> height is ',(⍕100×|(|⍎CRht)-1),'% ',(⊃'below average' 'above average'[1+1<⍎CRht]),' placing child at about ',htTile,' Percentile.</ha>' ⍝ html,←BR,BR,'<b>OQ=',Oq,'</b> (BMI=',Bmi,' / MBMI=',MBMI,')',' [child''s OQ = their BMI / Median BMI for their age]</b></ha>' :EndIf ⍝ html,←BR,BR,'res'MultiEdit(11 100)res('readonly="readonly" style="width:99%;font-weight:bold;font-size:14px" title="Result field. You cannot type into this field."') →AfterTable ⍝ Prepare Table for OQ WQ and HQ cells←16 3⍴'<b>OQ' '<b>OQ %tile' '<b>OQ Label' '≥ 1.30' '≥99.9' 'Over-Extremely' '1.22' '99' 'Over-Unusually' '1.17' '97' 'Over-Decidedly' '1.15' '95' 'Over-Quite' '1.11' '90' 'Over-Somewhat' '1.08' '85' 'Over-Slightly' '1.06' '75' 'Normal-High' '1.00' '50' 'Normal' '0.95' '25' 'Normal-Low' '0.92' '15' 'Under-Slightly' '0.90' '10' 'Under-Somewhat' '0.88' '5' 'Under-Quite' '0.86' '3' 'Under-Decidedly' '0.84' '1' 'Under-Unusually' '≤ 0.79' '≤0.1' 'Under-Extremely' atts←16 3⍴(⊂' align="center" style=" background-color:'),¨(Colors[3 6 12 9 12 6/1 2 3 4 3 2]),¨⊂';"' ⍝ put in color grading findrm←{0=⍴,⍵:0 ⋄ (15 3⍴⍳45)[((1+dif)=1+min←⌊/dif←|⍺-⍎⍵)/⍳⍴⍺;]} ⍝ find correct row to colour boldrm←{r←⍵ ⋄ p←(('">'⍷⍵)/⍳⍴⍵)[⍺] ⋄ r[p+1]←⊂'><big><b>' ⋄ enlist r} TOQ←Table(cells)'border=1'atts ⍝ OQ obesity table TOQ←(3+Oqs findrm Oq)boldrm TOQ ⍝ find and bold closest row(s) cells[1;]←'<b>WQ' '<b>WQ %tile' '<b>WQ Label' ⋄ cells[(2 3)(3 3)]←'Extremely' 'Very' ⍝ change some col labels TWQ←Table(cells)'border=1'atts ⍝ WQ table TWQ←(3+Oqs findrm CRwt)boldrm TWQ ⍝ find and bold closest row(s) cells[1;]←'<b>HQ' '<b>HQ %tile' '<b>HQ Label' ⋄ cells[(2 3)(3 3)]←'Extremely' 'Very' ⍝ change some col labels THQ←Table(cells)'border=1'atts ⍝ HQ table THQ←(3+Oqs findrm CRht)boldrm THQ ⍝ find and bold closest row(s) html,←BR,BR,BR,Table(2 3⍴('<big><b><center>OQ:Obesity Quotient=',⍕Oq)('<big><b><center>WQ: Weight Quotient',⍕CRwt)('<big><b><center>HQ: Height Quotient',⍕CRht)TOQ TWQ THQ)'border=20' ⍝ 'align="center" border=20' AfterTable: :If 0≠⍴,res ⋄ html,←BR,BR,res ⋄ :End req.Return html←req('post'Form)html ⍝ Put a 'post' form around it ∇ ∇ DoAction :Select 2↑Action :Case 'Re' ⍝ Reset is NOT BEING USED YET DOBm DOBd DOBy MEAm MEAd MEAy Sex HTft HTin WTlb WToz HTcm WTkg AgeD MBMI Bmi Oq CRwt CRht Note←⊂'' ⋄ AgeD←'0' Init :Case 'US'⍝ US Calculate OQcalc ⋄ Action←'' :Case 'Me' ⍝ Metric Calculate ⍝ HTft←⍕0 ⋄ HTin←⍕0.393701×1↑(⊃(//⎕VFI HTcm)),1 ⍝ chk ht & if input error set to 1 ⍝ WTlb←⍕0 ⋄ WToz←⍕35.274×1↑(⊃(//⎕VFI WTkg)),1 ⍝ chk wt & if input error set to 1 OQcalc ⋄ Action←'' :EndSelect ∇ ∇ OQcalc;sex;ht;wt;agd;files;bmiB;bmiG;lenB;lenG;weiB;weiG;lookup;Mbmi;Mht;Mwt;BMI ⍝ calculates OQ from sex(1=m 2=f) dob(y m d) measuredate(y m d) ht(ft in) wt(oz) ⍝ uses global days fns path←(⌽((⌽⎕WSID)⍳'\')↓⌽⎕WSID),'\Intro\jbgames\BMI\' show←{enlist ⍵,'=',⍎⍵} elim2←{⍺←⎕D,'. ' ⋄ (⍵∊⍺)/⍵} ⍝ elim all except:digits, space and decimal. - not allowed either ⍝ sex dob dmeas ht wt←1(2007 12 1)(2007 12 6)(1 8.25)111(6lb 15oz) ⍝ aged=5 bmi=11.89 for id 905 in output BMI20130618HTflag.txt ⍝ 'inputs:' ⋄ show¨'sex' 'dob' 'dmeas' 'ht' 'wt' sex←'MF'⍳1↑Sex ⍝ convert 1=Male 2=Female ⍝ calculate ht and wt in cm and kg for lookup table and cal Metric or US input counterpart for table display :If 'US'≡2↑Action ⍝ US caculation input HTcm←⍕ht←2.54×12⊥⍎¨HTft HTin ⍝ convert ft & in to in. THEN convert in to cm. WTkg←⍕wt←0.0283495×16⊥⍎¨WTlb WToz ⍝ convert lbs & oz to oz and the oz to kilograms. :ElseIf 'Me'≡2↑Action ⍝ Metric calculation input wt ht←⍎¨WTkg HTcm←{0=⍴,⍵:'0' ⋄ ⍵}¨WTkg HTcm WTkg HTcm←{⍕⍎⍵~' '}¨WTkg HTcm ⍝ get rid of anomoly when calc done on nothing twice WTlb WToz←⍕¨⌊¨0 16⊤0.5+0.03527396194958×1000×⍎WTkg ⍝ convert kg-->gm-->oz HTft HTin←⍕¨⌊¨12 12⊤0.5+(⍎HTcm)÷2.54 ⍝ convert cm -->in :EndIf agd←0⌈1856⌊agdInp←-/days¨(⍎¨MEAy MEAm MEAd)(⍎¨DOBy DOBm DOBd) ⍝ compute age in days must be less than 1856 days as that end of who data ⍝ BMILookUP ⍝ read the 6 day files for boys and girls an get median: bmi length and wt for all days files←'bmi fa_boys_p_exp.txt' 'bmi fa_girls_p_exp.txt' 'length hfa_boys_p_exp.txt' 'length hfa_girls_p_exp.txt' 'weight fa_boys_p_exp.txt.bak' 'weight fa_girls_p_exp.txt' bmiB bmiG lenB lenG weiB weiG←2⊃¨((⊂path),¨files)ReadFile¨2 ⍝ look up day from Xl col 11 in Length and weight files? find M in col 3 ⍝ Day L M S P01 P1 P3 P5 P10 P15 P25 P50 P75 P85 P90 P95 P97 P99 P999 ⍝ sex agd bmiB bmiG lenB lenG weiB weiG←4↑¨sex agd bmiB bmiG lenB lenG weiB weiG ⋄ agd←0 1 2 3 ⋄ sex←1 2 1 2 ⍝ FOR TEST PURPOSES ONLY ⍝ lookup←{(sex,¨⍳⍴sex)⌷¨⊂↑(⊂(1+agd)3)⌷¨⍵} ⍝ lookup fns to get median(col 3) in approp file using sex & agd(days+1 as table starts at 0) for multiple cases lookup←{sex⊃(⊂(1+agd)3)⌷¨⍵} ⍝ lookup fns to get median(col 3) in approp file using sex & agd(days+1 as table starts at 0) Mbmi Mht Mwt←lookup¨(bmiB bmiG)(lenB lenG)(weiB weiG) ⍝ get medians for each child for each date and store in: Mbmi Mht and Mwt ⍝ compute BMI. normal ratio OQ. :If (agdInp>1856)∧(agdInp<20×365) ⍝ person between 5 and 20 years old look up MBMI by age in months from data table res←2⊃(path,sex⊃'CDCBmiMdnMonthly2to20Male.txt' 'CDCBmiMdnMonthly2to20Fema.txt')ReadFile 1 Mbmi←res[res[;1]⍳0.5+months agdInp;2] BMI←10000×wt÷ht*2 ⋄ ⋄ Oq←2⍕BMI÷Mbmi ⋄ CRwt←'' ⋄ CRht←'' ⍝ compute results into Display vars AgeD←⍕agd ⋄ MBMI←2⍕Mbmi ⋄ Bmi←2⍕BMI ⍝ put intermediate results in display variables Note←'Age in Years=',(2⍕(agdInp)÷365),' (Ages 5-20 can only compute BMI and OQ)' :ElseIf (agdInp<0)∨(agdInp>1856) ⍝ days old is < 0 or greater than 20 years so cannot compute OQ etc BMI←10000×wt÷ht*2 ⋄ Oq←'' ⋄ CRwt←'' ⋄ CRht←'' ⍝ compute results into Display vars AgeD←⍕agd ⋄ MBMI←'' ⋄ Bmi←2⍕BMI ⍝ put intermediate results in display variables Note←'Adult Age in Years=',(2⍕(agdInp)÷365),' (Age in days of ',(⍕agdInp),' is <0 or >20 years so can compute BMI but not OQ.)' :Else BMI←10000×wt÷ht*2 ⋄ Oq←2⍕BMI÷Mbmi ⋄ CRwt←2⍕wt÷Mwt ⋄ CRht←2⍕ht÷Mht ⍝ compute results into Display vars AgeD←⍕agd ⋄ MBMI←2⍕Mbmi ⋄ Bmi←2⍕BMI ⍝ put intermediate results in display variables :EndIf ∇ ⍝ ---------------- Utilities --------------------------------------------------------------------------------------- days←{ ⍝ Days since 1899-12-31 (Meeus). ⍺←17520902 ⍝ start of Gregorian calendar. yy mm dd h m s ms←7↑⊂[⍳¯1+⍴⍴⍵]⍵ ⍝ ⎕ts-style 7-item date-time. D←dd+(0 60 60 1000⊥↑h m s ms)÷86400000 ⍝ day with fractional part. Y M←yy mm+¯1 12×⊂mm≤2 ⍝ Jan, Feb → month 13 14. A←⌊Y÷100 ⍝ century number. B←(⍺<0 100 100⊥↑yy mm dd)×(2-A)+⌊A÷4 ⍝ Gregorian calendar correction. ¯2416544+D+B+⊃+/⌊365.25 30.6×Y M+4716 1 ⍝ (fractional) days. } months←{⌊.5+⍵÷ 365÷12} ⍝ input is days output is months(rounded) ∇ res←file ReadFile typ;data;vn;r;xl ⍝ reads a data file . returns filename data [var names] ⍝ type:0= just read1=data matrix 2=first line is var names for columns of data :If ''≡typ ⋄ typ←1 ⋄ :End res←⊂file ⍝ put file name in first part of res r←ReadAllLines file ⍝ just read data r←{ss ⍵(⎕AV[10])' '}¨r ⍝ I think replace tab with blank ⍝ r←{(⍵∊⎕AV[⊃,/(⊂¯1+⍳26)+¨⎕AV⍳'aA'],' 0123456789.-')/⍵}¨r ⍝ elim strange chars r←{(⍵∊' .',⎕A,⎕D,⎕UCS 96+⍳26)/⍵}¨r ⍝ keep only:' .', up case, #'s, low case r←(~∧/¨r=¨' ')/r ⍝ elim blank lines :Select typ ⍝ typ is input options :Case 0 ⍝ just return already read plain data res,←⊂r :Case 1 ⍝ read data into a numeric matrix res,←⊂r←↑⍎¨r :Case 2 ⍝ line1:var names rest is numeric matrix res,←⊂↑⍎¨1↓r ⍝ convert all data to numbers. NO NEG NUMBERS ALLOWED res,←⊂vn←partition 1⊃r ⍝ var names & store in 3rd part or res :EndSelect ∇ ∇ lines←{method}ReadAllLines name;txt;LF;CR;EOL;sample ⍝ Read all lines of a text file as single byte text method←'APL'{×⎕NC ⍵:⍎⍵ ⋄ ⍺}'method' txt←ReadAllText name ⍝ identify EOL sequence LF CR←⎕TC[2 3] sample←txt↑⍨1+⌊/txt⍳LF CR EOL←(LF CR)(CR LF)(LF)(CR){⊃⍺[⍋1⍳⍨¨⍺⍷¨⊂⍵]}sample :Select method :Case 'APL' lines←(⍴,EOL)↓¨EOL{(⍺⍷⍵)⊂⍵}EOL,txt ⍝ Using the XUTILS external functions can halve the time ⍝ required to lineate a longer file (eg 10,000 lines) ⍝ To use this method, you need to have run ⍝ 'XUTILS' ⎕CMD '' ⍝ to define the external function ltov :Case 'XUTILS' lines←(⊃⍴1↓EOL){(⊃⍵),⍺↓¨(1↓⍵)}(⊃EOL)ltov txt :EndSelect ∇ ∇ r←ReadAllText name;tn ⍝ Read a text file as single byte text tn←name ⎕NTIE 0 r←⎕NREAD tn(⎕DR' ')(⎕NSIZE tn) ⎕NUNTIE tn ∇ partition←{⍺←' ' ⋄ ⎕ML←3 ⋄ (⍺≠⍵)⊂⍵} ss←{⎕ML←1 ⍝ Approx alternative to xutils' ss. srce find repl←,¨⍵ ⍝ source, find and replace vectors. mask←find⍷srce ⍝ mask of matching strings. prem←(⍴find)↑1 ⍝ leading pre-mask. cvex←(prem,mask)⊂find,srce ⍝ partitioned at find points. (⍴repl)↓∊{repl,(⍴find)↓⍵}¨cvex ⍝ collected with replacements. } ⍝ Not used yet ∇ {f}writedata x;tie;file;lf;nl ⍝ also used for Debugging MildServer on the server see Boot.Demo ⍝ file←⎕WSID,'.txt' :If 0=⎕NC'f' ⋄ f←'BMI.txt' ⋄ :End file←(⌽((⌽⎕WSID)⍳'\')↓⌽⎕WSID),'\Intro\jbgames\BMI\',f lf←⎕AV[3] ⋄ nl←⎕AV[4] ⍝ 2=linefeed 3=newline :Trap 0 ⋄ tie←file ⎕NCREATE 0 :Else ⋄ tie←file ⎕NTIE 0 2 :EndTrap (1↓¨⍕¨(x,¨nl),¨lf)⎕NAPPEND¨tie ⎕NUNTIE tie ∇ :EndClass