Entitlements user interaction designs and proof of concept

entitlements.org 65KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777
  1. # -*- mode: org; -*-
  2. #+HTML_HEAD: <link rel="stylesheet" type="text/css" href="http://www.pirilampo.org/styles/readtheorg/css/htmlize.css"/>
  3. #+HTML_HEAD: <link rel="stylesheet" type="text/css" href="http://www.pirilampo.org/styles/readtheorg/css/readtheorg.css"/>
  4. #+HTML_HEAD: <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
  5. #+HTML_HEAD: <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.4/js/bootstrap.min.js"></script>
  6. #+HTML_HEAD: <script type="text/javascript" src="http://www.pirilampo.org/styles/lib/js/jquery.stickytableheaders.js"></script>
  7. #+HTML_HEAD: <script type="text/javascript" src="http://www.pirilampo.org/styles/readtheorg/js/readtheorg.js"></script>
  8. [[./Logo.png]]
  9. * Introduction
  10. The purpose of this document is to investigate possible User Interaction designs for Decode task 4.4.
  11. More specifically the focus is on investigating how the user of a decode wallet grants permission to decode application for a specific set of personal data.
  12. ** Privacy levels
  13. A prelimary definition of six privacy levels (ordered from most private to least private):
  14. #+name: privacy_levels
  15. | id | title | description |
  16. |----+-----------+---------------------------------------------------------------------------------|
  17. | 5 | secret | proofs, passwords, keys etc. |
  18. | 4 | private | ssn etc, strict need to know basis stuff |
  19. | 3 | intimate | e.g. stuff you share with family |
  20. | 2 | affiliate | e.g. stuff you share with work, project etc |
  21. | 1 | public | e.g. stuff that everybody may know, your e.g. twitter handle |
  22. | 0 | commons | stuff that is intended for the public good / commons, e.g. anonimized IoT stuff |
  23. ** Context types
  24. A preliminary definition of context types
  25. #+name: context_types
  26. | id | title | description | generator |
  27. |----+-----------+-------------------------------------------------------------------------------------------------------------+----------------------------------------------------------------------------------------------------------|
  28. | 0 | personal | data that relates to your personal life, you can have different instances, for example friends, family etc. | Faker::Name.first_name |
  29. | 1 | health | health data, you can define different instances (biosignals, stuff to share with dentist, gp, hospital etc) | ['Dentist','Hospital','General Practicioner'].sample |
  30. | 2 | education | school / educational data (grades, certificates etc) | Faker::University.name |
  31. | 3 | work | stuff you share in a professional context | Faker::Company.name |
  32. | 4 | hobby | stuff you share in the context of a pastime | [Faker::Music.instrument,Faker::ProgrammingLanguage.name, Faker::RockBand.name, Faker::Team.name].sample |
  33. | 5 | financial | for data about mortgages, insurance, taxes etc. | ['Mortgage', 'Insurance', 'Taxes'].sample |
  34. | 6 | other | for everything that doesn't fit the above | Faker::StarWars.call_sign |
  35. ** Properties
  36. A list of properties with code to eval for fake instances, all properties from id 4 come from gebiedonline.
  37. We've made an inventory of the data that is related to a gebied online profile.
  38. The question arises what part of the data would theoretically go inside the wallet.
  39. This is for the application developer to decide, but we think it is sensible to develop some guidelines on this.
  40. For now we have tried to be as inclusive as possible, and put everything in the wallet.
  41. Properties that are very application specific have the 'go' (and not the 'decode') namespace.
  42. This is pretty speculative, but serves as a discussion starter.
  43. #+name: properties
  44. | id | title | default_privacy_level | description | generator |
  45. |----+-------------------------------------+-----------------------+------------------------------------------------+----------------------------------------------------------------------------------------------------|
  46. | 0 | decode:name | 2 | full name | Faker::Name.name |
  47. | 1 | decode:email | 2 | Email address | Faker::Internet.email |
  48. | 2 | decode:address | 2 | Address | Faker::Address.street_address |
  49. | 3 | decode:telephone | 2 | Telephone number | Faker::PhoneNumber.cell_phone |
  50. | 4 | go:proof_of_membership | 5 | instead of the password that is used now | SecureRandom.hex |
  51. | 5 | decode:first_name | 2 | first name | Faker::Name.first_name |
  52. | 6 | decode:last_name | 2 | last name | Faker::Name.last_name |
  53. | 7 | go:newsletter_approval | 2 | boolean | [true,false].sample |
  54. | 8 | decode:profile_picture | 2 | profile picture | Faker::LoremPixel.image |
  55. | 9 | decode:gender | 3 | gender | Faker::Demographic.marital_status |
  56. | 10 | decode:birthdate | 3 | date of birthday | Faker::Date.birthday(15,99) |
  57. | 11 | decode:homepage | 2 | personal homepage | Faker::Internet.url |
  58. | 12 | skype:id | 2 | skype handle | Faker::Internet.user_name |
  59. | 13 | twitter:id | 1 | twitter handle | Faker::Internet.user_name |
  60. | 14 | facebook:id | 3 | facebook handle | Faker::Internet.email |
  61. | 15 | linkedin:id | 1 | linkedin handle | Faker::Internet.email |
  62. | 16 | decode:street | 4 | streetname + number | Faker::Address.street_address |
  63. | 17 | decode:postcode | 4 | postcode | Faker::Address.postcode |
  64. | 18 | decode:persons_in_household | 4 | taken from 'woonsituatie' | Faker::Number.between(1,5) |
  65. | 19 | decode:profession | 4 | taken from 'werk en hobbies' | Faker::Job.title |
  66. | 20 | go:my_dreams | 3 | taken from 'mijn droom' | Faker::RickAndMorty.quote |
  67. | 21 | go:improve_my_environment | 2 | taken from 'mijn omgeving' | Faker::RickAndMorty.location |
  68. | 22 | go:expertise | 3 | taken from 'mijn expertise' list structure? | Faker::Educator.course |
  69. | 23 | decode:marital_status | 4 | taken from 'woonsituatie' | Faker::Demographic.marital_status |
  70. | 24 | go:neighborhood_role | 1 | taken from 'rol & nieuwsbrief' | ["bewoner","toekomstige","ondernemer","professional","scholier","student","geinteresseerd"].sample |
  71. | 25 | decode:avg_yearly_gas_use_m3 | 4 | taken from 'jaarlijks warmte verbruik' | Faker::Number.between(1000,2000) |
  72. | 26 | decode:avg_yearly_electricy_use_kwh | 4 | taken from 'jaarlijks electriciteits verbruik' | Faker::Number.between(2000,3000) |
  73. | 27 | decode:membership_organization | 2 | taken from 'lidmaatschap van organisaties' | Faker::StarTrek.specie |
  74. | 28 | go:project_association | 1 | taken from 'associatie met projecten' | Faker::SiliconValley.invention |
  75. | 29 | go:offer | 0 | taken from 'vraag en aanbod items' | Faker::SiliconValley.motto |
  76. | 30 | go:need | 0 | taken from 'vraag en aanbod items' | Faker::SiliconValley.quote |
  77. * Data Model
  78. ** Example wallet profile
  79. This sample wallet profile datastructure consists of multiple contexts.
  80. No assumptions are made about ontology for now so mention of existing e.g. skos/foaf,
  81. everything is in the decode namespace which is well known across applications.
  82. Each context has a name and groups on or more properties that consist of a well known type and a value.
  83. A type can be part of more than one context.
  84. Every property instance has a privacy level attached to it, so we can calculate the weight of requests and profiles.
  85. It overrides the default privacy level specified by the property type.
  86. #+name: profile
  87. #+begin_src js :results output
  88. var profile = {
  89. contexts :
  90. [
  91. {
  92. title : "personal",
  93. context_type : 5,//PERSONAL
  94. properties :
  95. [
  96. {
  97. type : "decode:name",
  98. value: "Taco van Dijk",
  99. pl: 1 //public, everyone may know my name
  100. },
  101. {
  102. type : "decode:email",
  103. value : "[REDACTED]",
  104. pl: 2 //affiliate, parties i have personal business with may know
  105. },
  106. {
  107. type: "decode:address",
  108. value: "[REDACTED]",
  109. pl: 2 //affiliate, parties i have personal business with may know
  110. },
  111. {
  112. type: "decode:phone",
  113. value : "[REDACTED]",
  114. pl: 2 //affiliate, parties i have personal business with may know
  115. }
  116. ],
  117. pl_sum: 7 //this is a calculated value based on the attributed pl values or default if they were not user specified
  118. },
  119. {
  120. title: "Waag society",
  121. context_type: 2,//WORK
  122. properties :
  123. [
  124. {
  125. type : "decode:name",
  126. value : "Taco van Dijk",
  127. pl: 1
  128. },
  129. {
  130. type : "decode:email",
  131. value : "taco@waag.org",
  132. pl: 2
  133. },
  134. {
  135. type : "decode:address",
  136. value : "St. Antoniesbreestraat 69",
  137. pl: 1 //since this is shared with all my colleagues i find this public
  138. }
  139. ],
  140. pl_sum: 4
  141. },
  142. {
  143. title: "Dyne",
  144. context_type: 2,//WORK
  145. properties :[{
  146. type : "decode:name",
  147. value : "Ocat",
  148. pl: 1
  149. },
  150. {
  151. type : "decode:email",
  152. value: "taco@gogs.dyne.org",
  153. pl: 2
  154. }
  155. ],
  156. pl_sum: 3
  157. }
  158. ]
  159. };
  160. process.stdout.write(JSON.stringify(profile));
  161. #+end_src
  162. #+RESULTS: profile
  163. : {"contexts":[{"title":"personal","context_type":5,"properties":[{"type":"decode:name","value":"Taco van Dijk","pl":1},{"type":"decode:email","value":"[REDACTED]","pl":2},{"type":"decode:address","value":"[REDACTED]","pl":2},{"type":"decode:phone","value":"[REDACTED]","pl":2}],"pl_sum":7},{"title":"Waag society","context_type":2,"properties":[{"type":"decode:name","value":"Taco van Dijk","pl":1},{"type":"decode:email","value":"taco@waag.org","pl":2},{"type":"decode:address","value":"St. Antoniesbreestraat 69","pl":1}],"pl_sum":4},{"title":"Dyne","context_type":2,"properties":[{"type":"decode:name","value":"Ocat","pl":1},{"type":"decode:email","value":"taco@gogs.dyne.org","pl":2}],"pl_sum":3}]}
  164. ** profile generator
  165. #+name: profile_generator
  166. #+BEGIN_SRC ruby :var context_types=context_types :var privacy_levels=privacy_levels :var properties=properties :results output
  167. require 'faker'
  168. require 'json'
  169. require 'securerandom'
  170. CMIN = 4 #minimum amount of contexts
  171. CMAX = 12 #maximum amount of extra contexts
  172. PC_MIN = 4 #minimum amount of properties
  173. PC_MAX = 12 #maximum amount of extra properties
  174. profile = {}
  175. contexts = []
  176. #create between CMIN and CMAX contexts in this profile
  177. (CMIN + rand(CMAX)).times do
  178. context = {}
  179. context_type_idx = rand(context_types.count) #pick a random context type
  180. context[:title] = eval(context_types[context_type_idx][3]) #eval a fake title based on random context type
  181. context[:context_type] = context_types[context_type_idx][0] #context type index
  182. context[:properties] = []
  183. #sample a random amount up to 8 properties for each context
  184. properties.sample(PC_MIN + rand(PC_MAX)).each do |rec|
  185. property = {}
  186. property[:type] = rec[1]
  187. property[:value] = eval(rec[4])
  188. property[:pl] = rec[2] + rand(2) #default privacy level, + random markup upwards
  189. context[:properties] << property
  190. end
  191. context[:pl_sum] = context[:properties].map{|p|p[:pl]}.reduce(0,:+) #calculate pl sum for each context
  192. context[:pl_mean] = (context[:pl_sum] / context[:properties].count)
  193. contexts << context
  194. end
  195. # sort the contexts by contextType
  196. sorted = contexts.sort { | a, b | [a[:context_type], a[:pl_mean]] <=> [b[:context_type], b[:pl_mean]] }
  197. profile[:contexts] = sorted
  198. puts profile.to_json
  199. #+END_SRC
  200. #+RESULTS: profile_generator
  201. : {"contexts":[{"title":"Theresa","context_type":0,"properties":[{"type":"decode:email","value":"buford.dare@gleichnergorczany.name","pl":2},{"type":"go:improve_my_environment","value":"Hideout Planet","pl":3},{"type":"skype:id","value":"eula","pl":3},{"type":"decode:persons_in_household","value":4,"pl":5},{"type":"decode:homepage","value":"http://osinskivandervort.io/tevin","pl":2},{"type":"decode:profile_picture","value":"http://lorempixel.com/300/300","pl":3},{"type":"go:neighborhood_role","value":"geinteresseerd","pl":1},{"type":"go:my_dreams","value":"It's a figure of speech, Morty! They're bureaucrats! I don't respect them. Just keep shooting, Morty! You have no idea what prison is like here!","pl":4}],"pl_sum":23,"pl_mean":2},{"title":"Hospital","context_type":1,"properties":[{"type":"decode:email","value":"alexys@kling.org","pl":2},{"type":"decode:name","value":"Price Miller","pl":2},{"type":"decode:postcode","value":"90304-4175","pl":4},{"type":"decode:address","value":"4296 Turcotte Throughway","pl":3},{"type":"go:proof_of_membership","value":"eb00c76b1f66467dcffbdc49b175d351","pl":5}],"pl_sum":16,"pl_mean":3},{"title":"Hospital","context_type":1,"properties":[{"type":"decode:avg_yearly_electricy_use_kwh","value":2094,"pl":5},{"type":"decode:persons_in_household","value":4,"pl":5},{"type":"decode:homepage","value":"http://schulist.io/sophie","pl":2},{"type":"decode:last_name","value":"Padberg","pl":3},{"type":"decode:marital_status","value":"Never married","pl":5},{"type":"decode:profession","value":"Senior Legal Manager","pl":4},{"type":"decode:first_name","value":"Ryleigh","pl":3},{"type":"go:neighborhood_role","value":"scholier","pl":1}],"pl_sum":28,"pl_mean":3},{"title":"Halvorson, Kohler and Bernhard","context_type":3,"properties":[{"type":"go:project_association","value":"Liquid Shrimp","pl":2},{"type":"twitter:id","value":"lenora_feil","pl":1},{"type":"decode:avg_yearly_gas_use_m3","value":1825,"pl":5},{"type":"go:expertise","value":"Associate Degree in Design","pl":3},{"type":"go:improve_my_environment","value":"Pluto","pl":3},{"type":"decode:telephone","value":"1-153-431-3983","pl":3},{"type":"decode:gender","value":"Widowed","pl":4},{"type":"decode:street","value":"699 Samantha Stream","pl":4},{"type":"go:neighborhood_role","value":"bewoner","pl":2},{"type":"decode:first_name","value":"Ima","pl":3},{"type":"decode:address","value":"533 Kassulke Curve","pl":3}],"pl_sum":33,"pl_mean":3},{"title":"Washington dragons","context_type":4,"properties":[{"type":"go:project_association","value":"Bit Soup","pl":1},{"type":"decode:homepage","value":"http://oberbrunner.net/orlo.wunsch","pl":2},{"type":"facebook:id","value":"anastacio.volkman@borercain.name","pl":4},{"type":"decode:last_name","value":"Leannon","pl":2},{"type":"decode:address","value":"1106 Gerhold Plaza","pl":2},{"type":"linkedin:id","value":"darren@pfannerstillolson.biz","pl":2},{"type":"go:neighborhood_role","value":"ondernemer","pl":1},{"type":"decode:postcode","value":"95406-0979","pl":4},{"type":"go:improve_my_environment","value":"Parblesnops","pl":2},{"type":"go:newsletter_approval","value":false,"pl":2},{"type":"go:expertise","value":"Associate Degree in Creative Arts","pl":3},{"type":"decode:marital_status","value":"Never married","pl":4}],"pl_sum":29,"pl_mean":2},{"title":"Alice In Chains","context_type":4,"properties":[{"type":"decode:email","value":"fredrick@fahey.biz","pl":3},{"type":"decode:profile_picture","value":"http://lorempixel.com/300/300","pl":3},{"type":"go:neighborhood_role","value":"geinteresseerd","pl":2},{"type":"go:expertise","value":"Associate Degree in Engineering","pl":4},{"type":"go:my_dreams","value":"WUBBA LUBBA DUB DUBS!!!","pl":4},{"type":"decode:street","value":"3703 Sanford Rest","pl":4},{"type":"go:newsletter_approval","value":true,"pl":3},{"type":"facebook:id","value":"emerson@barton.org","pl":3},{"type":"decode:homepage","value":"http://mills.com/emely_gulgowski","pl":3},{"type":"decode:avg_yearly_gas_use_m3","value":1263,"pl":4},{"type":"decode:first_name","value":"Clark","pl":3}],"pl_sum":36,"pl_mean":3},{"title":"The Clash","context_type":4,"properties":[{"type":"decode:birthdate","value":"1937-03-13","pl":4},{"type":"decode:gender","value":"Never married","pl":4},{"type":"go:newsletter_approval","value":false,"pl":2},{"type":"go:neighborhood_role","value":"scholier","pl":2},{"type":"decode:profile_picture","value":"http://lorempixel.com/300/300","pl":3}],"pl_sum":15,"pl_mean":3},{"title":"Taxes","context_type":5,"properties":[{"type":"go:project_association","value":"Cold Duck","pl":2},{"type":"decode:name","value":"Shyanne Berge","pl":2},{"type":"decode:homepage","value":"http://boganjacobi.name/lacey","pl":3},{"type":"go:proof_of_membership","value":"40cdec20e4df4de5a0414c252cc023a3","pl":5},{"type":"decode:avg_yearly_electricy_use_kwh","value":2401,"pl":5},{"type":"facebook:id","value":"alfred@nitzsche.name","pl":4},{"type":"decode:profession","value":"Global Design Engineer","pl":4},{"type":"decode:persons_in_household","value":5,"pl":4},{"type":"decode:avg_yearly_gas_use_m3","value":1349,"pl":4},{"type":"decode:first_name","value":"Josiah","pl":2},{"type":"go:improve_my_environment","value":"Hideout Planet","pl":2},{"type":"decode:gender","value":"Separated","pl":4}],"pl_sum":41,"pl_mean":3},{"title":"Taxes","context_type":5,"properties":[{"type":"go:my_dreams","value":"Sometimes science is a lot more art, than science. A lot of people don't get that.","pl":3},{"type":"decode:address","value":"5405 Schneider Court","pl":3},{"type":"facebook:id","value":"miguel.ortiz@kunde.name","pl":3},{"type":"go:offer","value":"Making the world a better place","pl":1},{"type":"go:improve_my_environment","value":"Earth","pl":2},{"type":"decode:marital_status","value":"Widowed","pl":5},{"type":"decode:avg_yearly_electricy_use_kwh","value":2492,"pl":5},{"type":"twitter:id","value":"damian_beer","pl":2},{"type":"decode:birthdate","value":"1932-12-27","pl":4},{"type":"decode:email","value":"jeffery.kris@larsonschiller.co","pl":3},{"type":"go:neighborhood_role","value":"scholier","pl":1},{"type":"decode:homepage","value":"http://robertseichmann.biz/mellie","pl":2},{"type":"go:proof_of_membership","value":"6912d95386c454537a8f038410586c04","pl":6},{"type":"decode:profession","value":"Construction Agent","pl":5}],"pl_sum":45,"pl_mean":3},{"title":"Mortgage","context_type":5,"properties":[{"type":"go:project_association","value":"BamBot","pl":2},{"type":"decode:marital_status","value":"Divorced","pl":4},{"type":"decode:first_name","value":"Marlon","pl":3},{"type":"decode:membership_organization","value":"Vidiian","pl":3},{"type":"go:expertise","value":"Bachelor of Engineering","pl":3},{"type":"decode:homepage","value":"http://west.biz/ray","pl":3},{"type":"decode:persons_in_household","value":5,"pl":4},{"type":"decode:email","value":"stanford.larson@parisian.biz","pl":3},{"type":"decode:postcode","value":"68025","pl":4},{"type":"decode:profile_picture","value":"http://lorempixel.com/300/300","pl":3},{"type":"decode:profession","value":"International Legal Director","pl":4},{"type":"go:improve_my_environment","value":"Screaming Sun Earth","pl":3},{"type":"go:proof_of_membership","value":"9260b7c91284c0cd2177fc34e9f06fe1","pl":5},{"type":"twitter:id","value":"dereck","pl":1},{"type":"go:need","value":"Jian-Yang, what're you doing? This is Palo Alto. People are lunatics about smoking here. We don't enjoy all the freedoms that you have in China.","pl":1}],"pl_sum":46,"pl_mean":3},{"title":"Gray 1","context_type":6,"properties":[{"type":"decode:name","value":"Myrtice Sipes DVM","pl":3},{"type":"decode:persons_in_household","value":2,"pl":4},{"type":"decode:postcode","value":"37078-3579","pl":5},{"type":"go:expertise","value":"Bachelor of Law","pl":3}],"pl_sum":15,"pl_mean":3},{"title":"Green 9","context_type":6,"properties":[{"type":"decode:postcode","value":"51411-7341","pl":4},{"type":"decode:marital_status","value":"Never married","pl":5},{"type":"skype:id","value":"marcellus","pl":2},{"type":"decode:last_name","value":"Schmidt","pl":2},{"type":"twitter:id","value":"sadye_white","pl":1},{"type":"decode:profession","value":"Internal Education Representative","pl":4}],"pl_sum":18,"pl_mean":3},{"title":"Gold Leader","context_type":6,"properties":[{"type":"decode:profile_picture","value":"http://lorempixel.com/300/300","pl":2},{"type":"go:improve_my_environment","value":"Parblesnops","pl":3},{"type":"decode:telephone","value":"785-355-6153","pl":2},{"type":"decode:profession","value":"National Strategist","pl":5},{"type":"go:newsletter_approval","value":false,"pl":2},{"type":"go:neighborhood_role","value":"geinteresseerd","pl":2},{"type":"go:offer","value":"Our products are products, producing unrivaled results","pl":0},{"type":"decode:persons_in_household","value":2,"pl":4},{"type":"decode:street","value":"882 Gutmann Route","pl":4}],"pl_sum":24,"pl_mean":2}]}
  202. ** Example request
  203. This sample application request consists of an application name, a set of required property types and a set of optional property types.
  204. Each application has a default context type attached to it, (so we can assign it a hue).
  205. For each request we can calculate the average privacy level,
  206. and the cumulative privacy weight by adding the privacy levels of each property in the request.
  207. #+name: request
  208. #+BEGIN_SRC js :results output
  209. var request = {
  210. application : "decodeapp:facebook",
  211. context_type : 5,//personal
  212. required : ["decode:name", "decode:email", "decode:address"],
  213. optional : ["decode:phone"]
  214. }
  215. var data = JSON.stringify(request) + "\n";
  216. process.stdout.write(data);
  217. #+END_SRC
  218. #+RESULTS: request
  219. : {"application":"decodeapp:facebook","context_type":5,"required":["decode:name","decode:email","decode:address"],"optional":["decode:phone"]}
  220. ** request generator
  221. #+name: request_generator
  222. #+BEGIN_SRC ruby :var context_types=context_types :var privacy_levels=privacy_levels :var properties=properties :results output
  223. require 'faker'
  224. require 'json'
  225. require 'securerandom'
  226. request = {}
  227. request[:application] = "decodeapp:#{Faker::App.name.downcase.gsub(' ','_')}"
  228. request[:context_type] = rand(context_types.count)
  229. required_recs = properties.sample(1 + rand(3))
  230. optional_recs = (properties - required_recs).sample(2)
  231. request[:required] = required_recs.map{|rec| rec[1]} #map property types
  232. request[:optional] = optional_recs.map{|rec| rec[1]} #map property types
  233. all_recs = required_recs + optional_recs
  234. request[:pl_sum] = all_recs.map{|rec|(rec[2] + rand(2))}.reduce(0,:+) #calculate pl sum for each context
  235. request[:pl_mean] = (request[:pl_sum] / all_recs.count)
  236. puts request.to_json
  237. #+END_SRC
  238. #+RESULTS: request_generator
  239. : {"application":"decodeapp:konklab","context_type":5,"required":["decode:telephone","decode:email"],"optional":["decode:address","decode:name"],"pl_sum":10,"pl_mean":2}
  240. * Data Comparison
  241. During the interaction we want to give the user insight into a couple of things;
  242. - How does the requested set of properties relate to the different contexts? How well does a context match to the request?
  243. - What would it mean for the context if the request was accepted? How many / which properties would have to be added to the context in order to fulfill the request?
  244. - What would it mean for the cumulative weight of the context?
  245. In below ruby code a comparison is made by on creating the intersection and its inverse between the request and each context.
  246. #+name: diff_src
  247. #+BEGIN_SRC ruby
  248. require 'json'
  249. require 'nokogiri' #for creating xml
  250. request = JSON.parse(request_data)
  251. profile = JSON.parse(profile_data)
  252. context_diffs = []
  253. profile["contexts"].each do | context |
  254. requested = request["required"] + request["optional"]
  255. available = context["properties"].map {|p| p["type"]}
  256. intersect = available & requested
  257. except = requested - available
  258. diff = {:context => context["title"], :intersect => intersect, :except => except}
  259. context_diffs << diff
  260. end
  261. #+END_SRC
  262. #+RESULTS: diff_src
  263. #+name: xml
  264. #+BEGIN_SRC ruby :exports none
  265. # unfortunately processing.js doesn't support json yet, so we have to use xml
  266. doc = Nokogiri::XML::Builder.new do |xml|
  267. xml.result {
  268. xml.request {
  269. xml.application request["application"]
  270. xml.contextType request["context_type"]
  271. xml.required {
  272. request["required"].each do |p| xml.property p end
  273. }
  274. xml.optional {
  275. request["optional"].each do |p| xml.property p end
  276. }
  277. xml.plSum request["pl_sum"]
  278. xml.plMean request["pl_mean"]
  279. }
  280. xml.diffs {
  281. context_diffs.each do |diff|
  282. xml.diff {
  283. xml.contextName diff[:context]
  284. xml.intersect {
  285. diff[:intersect].each do |p| xml.property p end
  286. }
  287. xml.except {
  288. diff[:except].each do |p| xml.property p end
  289. }
  290. }
  291. end
  292. }
  293. xml.profile {
  294. profile["contexts"].each do | context |
  295. xml.contextObj {
  296. xml.contextName context["title"]
  297. xml.contextType context["context_type"]
  298. xml.plSum context["pl_sum"]
  299. xml.plMean context["pl_mean"]
  300. xml.properties {
  301. context["properties"].each do |property|
  302. xml.property {
  303. xml.type property["type"]
  304. xml.value property["value"]
  305. xml.pl property["pl"]
  306. }
  307. end
  308. }
  309. }
  310. end
  311. }
  312. }
  313. end
  314. path = "diff.xml"
  315. File.write(path, doc.to_xml)
  316. path
  317. #+END_SRC
  318. #+name: diff
  319. #+BEGIN_SRC ruby :exports none :noweb yes :var profile_data=profile_generator :var request_data=request_generator :results value file
  320. <<diff_src>>
  321. <<xml>>
  322. #+END_SRC
  323. NOTE: We export to file diff.xml here for easy parsing in processing.js below.
  324. #+RESULTS: diff
  325. [[file:diff.xml]]
  326. * Visualization
  327. We want to visualize the following things;
  328. - The request with the application name and it's size / quality (Who's asking what)
  329. - The different contexts with it's name and size / quality relative to the request. (What would it mean to accept?)
  330. Per the design of Dyne, we want to use color to indicate the relation between the request and each context.
  331. A color should indicate something about privacy level and context type.
  332. For now the mapping is as follows;
  333. Different hues can be mapped to each context type.
  334. Different tones within the hue can be mapped to each privacy level.
  335. #+name: colors
  336. #+BEGIN_SRC java :exports none
  337. //color definitions
  338. color a3 = #3A3B58;
  339. color b3 = #734246;
  340. color d3 = #B4561F;
  341. color c3 = #336F60;
  342. color f3 = #7A3E2A;
  343. color g3 = #A48137;
  344. color e2 = #97BBCB;
  345. color a4 = #3B4257;
  346. color b4 = #6A4345;
  347. color d4 = #86451F;
  348. color c4 = #345A48;
  349. color f4 = #A92F21;
  350. color g4 = #BC983B;
  351. color a5 = #3D4358;
  352. color b5 = #402623;
  353. color d5 = #85442D;
  354. color c5 = #3B403A;
  355. color f5 = #7A150B;
  356. color g5 = #252F2B;
  357. color a1 = #597099;
  358. color e4 = #0A3878;
  359. color b1 = #D16365;
  360. color d1 = #FFD43B;
  361. color c1 = #B7BF98;
  362. color e1 = #CAD2C8;
  363. color e0 = #F5EDE5;
  364. color f1 = #D17978;
  365. color g1 = #FDD23E;
  366. color a0 = #C5C3CC;
  367. color e3 = #0485B1;
  368. color b0 = #FFDCD6;
  369. color d0 = #FFE9BE;
  370. color c0 = #F0E9D5;
  371. color f0 = #E4C8BF;
  372. color g0 = #FBE6BA;
  373. color a2 = #3D4B79;
  374. color e5 = #084064;
  375. color b2 = #974244;
  376. color d2 = #F8AA08;
  377. color c2 = #4E937F;
  378. color f2 = #8F4330;
  379. color g2 = #FFDB03;
  380. color colors[][] = {
  381. {b0,b1,b2,b3,b4,b5},
  382. {c0,c1,c2,c3,c4,c5},
  383. {a0,a1,a2,a3,a4,a5},
  384. {d0,d1,d2,d3,d4,d5},
  385. {e0,e1,e2,e3,e4,e5},
  386. {f0,f1,f2,f3,f4,f5},
  387. {g0,g1,g2,g3,g4,g5}
  388. };
  389. static class PrivacyLevel {
  390. public static int SECRET = 5;
  391. public static int PRIVATE = 4;
  392. public static int INTIMATE = 3;
  393. public static int AFFILIATE = 2;
  394. public static int PUBLIC = 1;
  395. public static int COMMONS = 0;
  396. }
  397. static class ContextType {
  398. public static int PERSONAL = 0;
  399. public static int HEALTH = 1;
  400. public static int EDUCATION = 2;
  401. public static int WORK = 3;
  402. public static int HOBBY = 4;
  403. public static int FINANCIAL = 5;
  404. public static int OTHER = 6;
  405. }
  406. public int getColor(int privacy_level, int context_type)
  407. {
  408. return colors[context_type][privacy_level];
  409. }
  410. #+END_SRC
  411. #+name: pcolors
  412. #+BEGIN_SRC java :exports none
  413. String[] levels = {"commons", "public", "affiliate", "intimate", "private", "secret"};
  414. String[] contextTypes = {"personal", "health", "education", "work", "hobby", "financial", "other"};
  415. static class PrivacyLevel {
  416. public static int SECRET = 5;
  417. public static int PRIVATE = 4;
  418. public static int INTIMATE = 3;
  419. public static int AFFILIATE = 2;
  420. public static int PUBLIC = 1;
  421. public static int COMMONS = 0;
  422. }
  423. static class ContextType {
  424. public static int PERSONAL = 0;
  425. public static int HEALTH = 1;
  426. public static int EDUCATION = 2;
  427. public static int WORK = 3;
  428. public static int HOBBY = 4;
  429. public static int FINANCIAL = 5;
  430. public static int OTHER = 6;
  431. }
  432. int[][] colors = generateColors();
  433. //generate colors to maximize contrast
  434. int[][] generateColors()
  435. {
  436. int[][] pcolors = new int[contextTypes.length][levels.length];
  437. //we start with d0 as a seed color
  438. color seed = #FFE9BE;//seed color
  439. colorMode(HSB);
  440. float h_seed = hue(seed);
  441. float s_seed = saturation(seed);
  442. float v_seed = brightness(seed);
  443. float levelshift = 255/levels.length;
  444. float contextshift = 255/contextTypes.length + 1;
  445. //cycle hue for each level
  446. for (int i = 0; i < contextTypes.length; i++){
  447. float h = h_seed + i * contextshift;
  448. if(h > 255){h = h - 255;}
  449. //cycle brightness for each context
  450. for(int j = 0; j < levels.length; j++)
  451. {
  452. float v = v_seed - (j * levelshift);
  453. float s = s_seed + (j * levelshift);
  454. color c = color(h,s,v);
  455. pcolors[i][j] = c;
  456. }
  457. }
  458. colorMode(RGB);
  459. return pcolors;
  460. }
  461. public int getColor(int privacy_level, int context_type)
  462. {
  463. return colors[context_type][privacy_level];
  464. }
  465. #+END_SRC
  466. This table shows all colors for each context / privacy level combination.
  467. Click on the diagram to compare colors sets.
  468. #+name: color_table
  469. #+BEGIN_SRC processing
  470. //color definitions
  471. color a3 = #3A3B58;
  472. color b3 = #734246;
  473. color d3 = #B4561F;
  474. color c3 = #336F60;
  475. color f3 = #7A3E2A;
  476. color g3 = #A48137;
  477. color e2 = #97BBCB;
  478. color a4 = #3B4257;
  479. color b4 = #6A4345;
  480. color d4 = #86451F;
  481. color c4 = #345A48;
  482. color f4 = #A92F21;
  483. color g4 = #BC983B;
  484. color a5 = #3D4358;
  485. color b5 = #402623;
  486. color d5 = #85442D;
  487. color c5 = #3B403A;
  488. color f5 = #7A150B;
  489. color g5 = #252F2B;
  490. color a1 = #597099;
  491. color e4 = #0A3878;
  492. color b1 = #D16365;
  493. color d1 = #FFD43B;
  494. color c1 = #B7BF98;
  495. color e1 = #CAD2C8;
  496. color e0 = #F5EDE5;
  497. color f1 = #D17978;
  498. color g1 = #FDD23E;
  499. color a0 = #C5C3CC;
  500. color e3 = #0485B1;
  501. color b0 = #FFDCD6;
  502. color d0 = #FFE9BE;
  503. color c0 = #F0E9D5;
  504. color f0 = #E4C8BF;
  505. color g0 = #FBE6BA;
  506. color a2 = #3D4B79;
  507. color e5 = #084064;
  508. color b2 = #974244;
  509. color d2 = #F8AA08;
  510. color c2 = #4E937F;
  511. color f2 = #8F4330;
  512. color g2 = #FFDB03;
  513. color colors[][] = {
  514. {b0,b1,b2,b3,b4,b5},
  515. {c0,c1,c2,c3,c4,c5},
  516. {a0,a1,a2,a3,a4,a5},
  517. {d0,d1,d2,d3,d4,d5},
  518. {e0,e1,e2,e3,e4,e5},
  519. {f0,f1,f2,f3,f4,f5},
  520. {g0,g1,g2,g3,g4,g5}
  521. };
  522. boolean procedural = true;
  523. String[] levels = {"commons", "public", "affiliate", "intimate", "private", "secret"};
  524. String[] contextTypes = {"personal", "health", "education", "work", "hobby", "financial", "other"};
  525. static class PrivacyLevel {
  526. public static int SECRET = 5;
  527. public static int PRIVATE = 4;
  528. public static int INTIMATE = 3;
  529. public static int AFFILIATE = 2;
  530. public static int PUBLIC = 1;
  531. public static int COMMONS = 0;
  532. }
  533. static class ContextType {
  534. public static int PERSONAL = 0;
  535. public static int HEALTH = 1;
  536. public static int EDUCATION = 2;
  537. public static int WORK = 3;
  538. public static int HOBBY = 4;
  539. public static int FINANCIAL = 5;
  540. public static int OTHER = 6;
  541. }
  542. public int getColor(int privacy_level, int context_type)
  543. {
  544. if(procedural){
  545. return pcolors[context_type][privacy_level];
  546. }
  547. return colors[context_type][privacy_level];
  548. }
  549. int[][] pcolors = new int[contextTypes.length][levels.length];
  550. void setup()
  551. {
  552. generateColors();
  553. size(600,480);
  554. }
  555. void mousePressed()
  556. {
  557. //toggle colors to see the difference
  558. procedural = !procedural;
  559. }
  560. //replace colors in the table with generated ones to maximize contrast
  561. void generateColors()
  562. {
  563. //we start with b0 as a seed color
  564. color seed = d0;
  565. colorMode(HSB);
  566. float h_seed = hue(seed);
  567. float s_seed = saturation(seed);
  568. float v_seed = brightness(seed);
  569. float levelshift = 255/levels.length;
  570. float contextshift = 255/contextTypes.length + 1;
  571. //cycle hue for each level
  572. for (int i = 0; i < contextTypes.length; i++){
  573. float h = h_seed + i * contextshift;
  574. if(h > 255){h = h - 255;}
  575. //cycle brightness for each context
  576. for(int j = 0; j < levels.length; j++)
  577. {
  578. float v = v_seed - (j * levelshift);
  579. float s = s_seed + (j * levelshift);
  580. color c = color(h,s,v);
  581. pcolors[i][j] = c;
  582. }
  583. }
  584. colorMode(RGB);
  585. }
  586. void draw()
  587. {
  588. background(200);
  589. float row_height = 50;
  590. float column_width = 80;
  591. fill(0);
  592. //draw privacy level headers
  593. for (int i = 0; i < levels.length; i++ ){
  594. text(levels[i], (i + 1) * column_width, row_height );
  595. }
  596. //draw context headers
  597. for (int j = 0; j < contextTypes.length; j++)
  598. {
  599. text(contextTypes[j], 20, (j+2) * row_height);
  600. }
  601. //draw colors
  602. for(int i = 0; i < levels.length; i++)
  603. {
  604. float x = 20 + (i + 1) * column_width;
  605. for(int j = 0; j < contextTypes.length; j++)
  606. {
  607. float y = (j + 2) * row_height;
  608. color c = getColor(i,j);
  609. fill(c);
  610. ellipse(x,y, 20, 20);
  611. }
  612. }
  613. }
  614. #+END_SRC
  615. #+RESULTS: color_table
  616. #+BEGIN_EXPORT html
  617. <script src="processing.js"></script>
  618. <script type="text/processing" data-processing-target="ob-ba6f36d590b36241a391b75f1bf3a4080ef6a0b7">
  619. //color definitions
  620. color a3 = #3A3B58;
  621. color b3 = #734246;
  622. color d3 = #B4561F;
  623. color c3 = #336F60;
  624. color f3 = #7A3E2A;
  625. color g3 = #A48137;
  626. color e2 = #97BBCB;
  627. color a4 = #3B4257;
  628. color b4 = #6A4345;
  629. color d4 = #86451F;
  630. color c4 = #345A48;
  631. color f4 = #A92F21;
  632. color g4 = #BC983B;
  633. color a5 = #3D4358;
  634. color b5 = #402623;
  635. color d5 = #85442D;
  636. color c5 = #3B403A;
  637. color f5 = #7A150B;
  638. color g5 = #252F2B;
  639. color a1 = #597099;
  640. color e4 = #0A3878;
  641. color b1 = #D16365;
  642. color d1 = #FFD43B;
  643. color c1 = #B7BF98;
  644. color e1 = #CAD2C8;
  645. color e0 = #F5EDE5;
  646. color f1 = #D17978;
  647. color g1 = #FDD23E;
  648. color a0 = #C5C3CC;
  649. color e3 = #0485B1;
  650. color b0 = #FFDCD6;
  651. color d0 = #FFE9BE;
  652. color c0 = #F0E9D5;
  653. color f0 = #E4C8BF;
  654. color g0 = #FBE6BA;
  655. color a2 = #3D4B79;
  656. color e5 = #084064;
  657. color b2 = #974244;
  658. color d2 = #F8AA08;
  659. color c2 = #4E937F;
  660. color f2 = #8F4330;
  661. color g2 = #FFDB03;
  662. color colors[][] = {
  663. {b0,b1,b2,b3,b4,b5},
  664. {c0,c1,c2,c3,c4,c5},
  665. {a0,a1,a2,a3,a4,a5},
  666. {d0,d1,d2,d3,d4,d5},
  667. {e0,e1,e2,e3,e4,e5},
  668. {f0,f1,f2,f3,f4,f5},
  669. {g0,g1,g2,g3,g4,g5}
  670. };
  671. static class PrivacyLevel {
  672. public static int SECRET = 5;
  673. public static int PRIVATE = 4;
  674. public static int INTIMATE = 3;
  675. public static int AFFILIATE = 2;
  676. public static int PUBLIC = 1;
  677. public static int COMMONS = 0;
  678. }
  679. static class ContextType {
  680. public static int FINANCIAL = 6;
  681. public static int PERSONAL = 5;
  682. public static int HEALTH = 4;
  683. public static int EDUCATION = 3;
  684. public static int WORK = 2;
  685. public static int HOBBY = 1;
  686. public static int OTHER = 0;
  687. }
  688. boolean procedural = false;
  689. String[] levels = {"commons", "public", "affiliate", "intimate", "private", "secret"};
  690. String[] contexts = {"other", "hobby", "work", "education", "health", "personal", "financial"};
  691. public int getColor(int privacy_level, int context_type)
  692. {
  693. if(procedural){
  694. return pcolors[context_type][privacy_level];
  695. }
  696. return colors[context_type][privacy_level];
  697. }
  698. int[][] pcolors = new int[contexts.length][levels.length];
  699. void setup()
  700. {
  701. generateColors();
  702. size(600,480);
  703. }
  704. void mousePressed()
  705. {
  706. //toggle colors to see the difference
  707. procedural = !procedural;
  708. }
  709. //replace colors in the table with generated ones to maximize contrast
  710. void generateColors()
  711. {
  712. //we start with b0 as a seed color
  713. color seed = d0;
  714. colorMode(HSB);
  715. float h_seed = hue(seed);
  716. float s_seed = saturation(seed);
  717. float v_seed = brightness(seed);
  718. float levelshift = 255/levels.length;
  719. float contextshift = 255/contexts.length + 1;
  720. //cycle hue for each level
  721. for (int i = 0; i < contexts.length; i++){
  722. float h = h_seed + i * contextshift;
  723. if(h > 255){h = h - 255;}
  724. //cycle brightness for each context
  725. for(int j = 0; j < levels.length; j++)
  726. {
  727. float v = v_seed - (j * levelshift);
  728. float s = s_seed + (j * levelshift);
  729. color c = color(h,s,v);
  730. pcolors[i][j] = c;
  731. }
  732. }
  733. colorMode(RGB);
  734. }
  735. void draw()
  736. {
  737. background(200);
  738. float row_height = 50;
  739. float column_width = 80;
  740. fill(0);
  741. //draw privacy level headers
  742. for (int i = 0; i < levels.length; i++ ){
  743. text(levels[i], (i + 1) * column_width, row_height );
  744. }
  745. //draw context headers
  746. for (int j = 0; j < contexts.length; j++)
  747. {
  748. text(contexts[j], 20, (j+2) * row_height);
  749. }
  750. //draw colors
  751. for(int i = 0; i < levels.length; i++)
  752. {
  753. float x = 20 + (i + 1) * column_width;
  754. for(int j = 0; j < contexts.length; j++)
  755. {
  756. float y = (j + 2) * row_height;
  757. color c = getColor(i,j);
  758. fill(c);
  759. ellipse(x,y, 20, 20);
  760. }
  761. }
  762. }
  763. </script> <canvas id="ob-ba6f36d590b36241a391b75f1bf3a4080ef6a0b7"></canvas>
  764. #+END_EXPORT
  765. #+name: glue
  766. #+BEGIN_SRC java :exports none
  767. class Request
  768. {
  769. public String application;
  770. public int contextType;
  771. public String[] required_properties;
  772. public String[] optional_properties;
  773. public int plSum;
  774. public int plMean;
  775. public Request(String app, int contextType, String[] req, String[] opt, int sum, int mean)
  776. {
  777. this.application = app;
  778. this.contextType = contextType;
  779. this.required_properties = req;
  780. this.optional_properties = opt;
  781. this.plSum = sum;
  782. this.plMean = mean;
  783. }
  784. }
  785. class Diff
  786. {
  787. public String context;
  788. public String[] intersect;
  789. public String[] except;
  790. public Diff(String ctx, String[] intersect, String[] except)
  791. {
  792. this.context = ctx;
  793. this.intersect = intersect;
  794. this.except = except;
  795. }
  796. }
  797. class Property
  798. {
  799. public String type;
  800. public String value;
  801. public int pl;//privacy level
  802. public Property(String type, String value, int pl)
  803. {
  804. this.type = type;
  805. this.value = value;
  806. this.pl = pl;
  807. }
  808. }
  809. class Context
  810. {
  811. public String title;
  812. public int plSum;
  813. public int plMean;
  814. public int contextType;
  815. public Property[] properties;
  816. public Context(String title, int plSum, int plMean, int contextType, Property[] properties)
  817. {
  818. this.title = title;
  819. this.plSum = plSum;
  820. this.plMean = plMean;
  821. this.properties = properties;
  822. this.contextType = contextType;
  823. }
  824. }
  825. XMLElement doc = new XMLElement(this, 'diff.xml');
  826. //create typed versions because this is java :-(
  827. Request parseRequest(XMLElement xml)
  828. {
  829. XMLElement req = xml.getChild(0);
  830. String name = req.getChild(0).getContent();
  831. int contextType = req.getChild(1).getContent();
  832. String[] required = new String[req.getChild(2).getChildCount()];
  833. String[] optional = new String[req.getChild(3).getChildCount()];
  834. int plSum = parseInt(req.getChild(4).getContent());
  835. int plMean = parseInt(req.getChild(5).getContent());
  836. for (int i = 0; i < required.length; i++) {
  837. required[i] = req.getChild(2).getChild(i).getContent();
  838. }
  839. for (int i = 0; i < optional.length; i++) {
  840. optional[i] = req.getChild(3).getChild(i).getContent();
  841. }
  842. Request r = new Request(name,contextType,required,optional, plSum, plMean);
  843. return r;
  844. }
  845. Context[] parseProfile(XMLElement xml)
  846. {
  847. XMLElement profile = xml.getChild(2);
  848. Context[] contexts = new Context[profile.getChildCount()];
  849. for(int i = 0; i < contexts.length; i++)
  850. {
  851. String contextName = profile.getChild(i).getChild(0).getContent();
  852. int contextType = parseInt(profile.getChild(i).getChild(1).getContent());
  853. int plSum = parseInt(profile.getChild(i).getChild(2).getContent());
  854. int plMean = parseInt(profile.getChild(i).getChild(3).getContent());
  855. XMLElement propertiesEl = profile.getChild(i).getChild(4);
  856. Property[] properties = new Property[propertiesEl.getChildCount()];
  857. for(int j = 0; j < properties.length; j++)
  858. {
  859. Property prop = parseProperty(propertiesEl.getChild(j));
  860. properties[j] = prop;
  861. }
  862. Context context = new Context(contextName, plSum, plMean, contextType, properties);
  863. contexts[i] = context;
  864. }
  865. return contexts;
  866. }
  867. Property parseProperty(XMLElement propertyEl)
  868. {
  869. String propertyType = propertyEl.getChild(0).getContent();
  870. String value = propertyEl.getChild(1).getContent();
  871. int pl = parseInt(propertyEl.getChild(2).getContent());
  872. return new Property(propertyType, value, pl);
  873. }
  874. //create typed versions because this is java :-(
  875. Diff[] parseDiffs(xml)
  876. {
  877. XMLElement diffsEl = xml.getChild(1);
  878. Diff[] diffs = new Diff[diffsEl.getChildCount()];
  879. for(int i = 0; i < diffs.length; i++)
  880. {
  881. String contextName = diffsEl.getChild(i).getChild(0).getContent();
  882. String[] intersects = new String[diffsEl.getChild(i).getChild(1).getChildCount()];
  883. String[] except = new String[diffsEl.getChild(i).getChild(2).getChildCount()];
  884. for(int j = 0; j < intersects.length; j++)
  885. {
  886. intersects[j] = diffsEl.getChild(i).getChild(1).getChild(j).getContent();
  887. }
  888. for(int j = 0; j < except.length; j++)
  889. {
  890. except[j] = diffsEl.getChild(i).getChild(2).getChild(j).getContent();
  891. }
  892. Diff diff = new Diff(contextName,intersects,except);
  893. diffs[i] = diff;
  894. }
  895. return diffs;
  896. }
  897. Request request = parseRequest(doc);
  898. Diff[] diffs = parseDiffs(doc);
  899. Context[] contexts = parseProfile(doc);
  900. #+END_SRC
  901. * Interaction
  902. We have come up with an interaction for entitlements that is based around a 'context switcher'.
  903. The color of the section in the center always represents the current context.
  904. The outer ring is divided into separate 'sectors' for each context that you have defined.
  905. Each context has a different color, based on the context type and the data that it holds.
  906. For example if you are at work you would (or the application will automatically)
  907. set the current context to blue by clicking on the blue sector.
  908. When a request comes in through decode, it will be positioned in orbit around the context switcher,
  909. close by the sector of the calculated best fitted context. Based on the category and the data diff.
  910. #+name: fitness_src
  911. #+BEGIN_SRC java
  912. //calculate the fitness score for the given context and diff for the current request
  913. int fitnessScore(c,d)
  914. {
  915. int score = 0;
  916. if(c.contextType == request.contextType)
  917. {
  918. score += 5;
  919. }
  920. //add one point for each property that is present
  921. score += d.intersect.length;
  922. //subtract one point for each property that is missing
  923. score -= d.except.length;
  924. return score;
  925. }
  926. #+END_SRC
  927. The user can drag the request around the outer ring to compare the impact it would have,
  928. accepting the request for each sector in the context switcher.
  929. After a suitable context is found, the user can let go of the request, and a dialog appears in the center,
  930. asking the user to either accept or decline the request.
  931. The example below demonstrates the concept with a randomly generated profile and request.
  932. #+name: context_switcher_src
  933. #+BEGIN_SRC java :exports none
  934. //magic numbers
  935. float contextSize = 500;//random(50,100); //random between 50 and 100
  936. float requestSize = 50;
  937. float eqd_angle = TWO_PI / contexts.length;
  938. float PROPORTION_CENTER = 0.6;
  939. float DISTANCE_FACTOR = 1.4;
  940. //globals
  941. float centerX, centerY;
  942. float xOffset, yOffset;
  943. float requestX, requestY;//request coordinates, they can move
  944. boolean hitRequest = false;
  945. boolean dragging = false;
  946. int hitContext = -1;
  947. int currentContext = 1;//first context is the default context
  948. PFont font;
  949. PFont titleFont;
  950. void setup()
  951. {
  952. size(800,800);
  953. rectMode(RADIUS);
  954. smooth();
  955. titleFont = createFont("HelveticaNeue", 20);
  956. font = createFont("HelveticaNeue", 14);
  957. centerX = width/2;
  958. centerY = height/2;
  959. int bestContext = bestFit();
  960. PVector position = positionForSectorIndex(bestContext);
  961. requestX = position.x;
  962. requestY = position.y;
  963. }
  964. //calculate best fitting sector
  965. int bestFit()
  966. {
  967. int count = 0;
  968. int bestContext = -1;
  969. int bestScore = -99;
  970. for(Context context : contexts)
  971. {
  972. Diff diff = diffs[count];
  973. int score = fitnessScore(context,diff);
  974. if(score > bestScore)
  975. {
  976. bestScore = score;
  977. bestContext = count;
  978. }
  979. count++;
  980. }
  981. return bestContext;
  982. }
  983. //the position should be outside the center of the middle of the sector
  984. PVector positionForSectorIndex(int index)
  985. {
  986. float angle = (PI/2) + (index * eqd_angle) + (eqd_angle/2);
  987. float x = centerX + (contextSize/2 * DISTANCE_FACTOR) * cos(angle); //calculate xPos
  988. float y = centerY + (contextSize/2 * DISTANCE_FACTOR) * sin(angle); //calculate yPos
  989. return new PVector(x,y);
  990. }
  991. //draw each frame
  992. void draw()
  993. {
  994. background(255);
  995. drawTitle();
  996. //drawBorder();
  997. drawSectors();
  998. drawCurrentContext();
  999. drawRequest();
  1000. }
  1001. //draw the request and label
  1002. void drawRequest()
  1003. {
  1004. color strokeColor = hitRequest? 255 : 153;
  1005. stroke(strokeColor);
  1006. color reqColor = getColor(request.plMean, request.contextType);
  1007. fill(reqColor,127);
  1008. ellipse(requestX,requestY, requestSize,requestSize);
  1009. colorMode(HSB);
  1010. fill(darken(reqColor));
  1011. textAlign(CENTER,CENTER);
  1012. text(request.application + "\n(" + contextTypes[request.contextType]+ ")", requestX, requestY + 50/1.5);
  1013. text("" + request.plSum, requestX, requestY);
  1014. colorMode(RGB);
  1015. }
  1016. //draw current context as center circle over the pie
  1017. void drawCurrentContext()
  1018. {
  1019. Context current = contexts[currentContext];
  1020. //set up colors for current context
  1021. color contextColor = getColor(current.plMean, current.contextType);
  1022. colorMode(HSB);
  1023. color darkContextColor = darken(contextColor);
  1024. colorMode(RGB);
  1025. fill(contextColor);
  1026. noStroke();
  1027. ellipse(centerX, centerY, contextSize * PROPORTION_CENTER, contextSize * PROPORTION_CENTER);
  1028. //if we have hit a context draw detailed information in the center
  1029. if(hitContext > -1)
  1030. {
  1031. fill(255);
  1032. noStroke();
  1033. ellipse(centerX, centerY, contextSize * PROPORTION_CENTER * 0.95, contextSize * PROPORTION_CENTER * 0.95);
  1034. if(dragging == false)
  1035. {
  1036. drawButtons(darkContextColor);
  1037. }
  1038. else
  1039. {
  1040. textFont(font);
  1041. drawDiff(current, contextColor);
  1042. }
  1043. }
  1044. else //just draw the current context title
  1045. {
  1046. textFont(font);
  1047. fill(255);
  1048. noStroke();
  1049. ellipse(centerX, centerY, contextSize * 0.4, contextSize * 0.4);
  1050. fill(darkContextColor);
  1051. textAlign(CENTER,CENTER);
  1052. text("My \n" + current.title + "\n data", centerX, centerY);
  1053. }
  1054. }
  1055. //darken the given color, expects hsb color mode
  1056. int darken(int c)
  1057. {
  1058. float h = hue(c);
  1059. float s = saturation(c);
  1060. float v = brightness(c);
  1061. return color(h,s,v * 0.6);
  1062. }
  1063. //draw the accept / decline buttons
  1064. void drawButtons(color darkContextColor)
  1065. {
  1066. //draw accept and decline button
  1067. fill(darkContextColor);
  1068. textFont(font);
  1069. textAlign(CENTER,CENTER);
  1070. text("ACCEPT | DECLINE", centerX, centerY);
  1071. }
  1072. //show diff information
  1073. void drawDiff(Context c, color contextColor)
  1074. {
  1075. ellipse(centerX, centerY, contextSize * PROPORTION_CENTER * 0.95, contextSize * PROPORTION_CENTER * 0.95);
  1076. colorMode(HSB);
  1077. fill(darken(contextColor));
  1078. colorMode(RGB);
  1079. Diff d = diffs[hitContext];
  1080. String available = getIntersection(c,d);
  1081. String missing = getMissing(d);
  1082. String diffMessage = c.title + " (" + c.plSum + ")\n";
  1083. if(d.intersect.length > 0) { diffMessage += "\nAvailable: \n" + available; }
  1084. if(d.except.length > 0){ diffMessage += "\nMissing: \n" + missing;}
  1085. textAlign(CENTER,CENTER);
  1086. text(diffMessage, centerX, centerY);
  1087. }
  1088. //draw Sectors for each context as pie pieces
  1089. void drawSectors()
  1090. {
  1091. noStroke();
  1092. int count = 0;
  1093. float begin = (PI/2);
  1094. float end = begin + eqd_angle;
  1095. int previousContextType = contexts[0].contextType;
  1096. for(Context context : contexts)
  1097. {
  1098. color contextColor = getColor(context.plMean, context.contextType);
  1099. colorMode(HSB);
  1100. color darkContextColor = darken(contextColor);
  1101. colorMode(RGB);
  1102. fill(contextColor);
  1103. arc(centerX, centerY, contextSize, contextSize, begin, end);
  1104. int ext = 20;
  1105. if(count == currentContext)
  1106. {
  1107. noStroke();
  1108. arc(centerX, centerY, contextSize + ext , contextSize + ext, begin, end);
  1109. }
  1110. if(context.contextType != previousContextType)
  1111. {
  1112. stroke(255);
  1113. strokeWeight(3);
  1114. }
  1115. else
  1116. {
  1117. ext = 0;
  1118. stroke(darkContextColor);
  1119. strokeWeight(1);
  1120. }
  1121. //draw white line as divider
  1122. float angle = (PI/2) + (count * eqd_angle);
  1123. float x = centerX + (contextSize/2 + ext) * cos(angle);
  1124. float y = centerY + (contextSize/2 + ext) * sin(angle);
  1125. line(centerX,centerY,x,y);
  1126. noStroke();
  1127. strokeWeight(1);
  1128. begin = end;
  1129. end = begin + eqd_angle;
  1130. previousContextType = context.contextType;
  1131. count++;
  1132. }
  1133. //draw first white divider from center down.
  1134. stroke(255);
  1135. strokeWeight(3);
  1136. line(centerX,centerY,centerX,centerY + contextSize/2);
  1137. strokeWeight(1);
  1138. }
  1139. //draw outer rim
  1140. void drawBorder()
  1141. {
  1142. Context current = contexts[currentContext];
  1143. //set up colors for current context
  1144. color contextColor = getColor(current.plMean, current.contextType);
  1145. colorMode(HSB);
  1146. color darkContextColor = darken(contextColor);
  1147. colorMode(RGB);
  1148. stroke(darkContextColor);
  1149. fill(255);
  1150. ellipse(centerX, centerY, contextSize + 10, contextSize + 10);
  1151. }
  1152. //draw title
  1153. void drawTitle()
  1154. {
  1155. textFont(titleFont);
  1156. fill(150);
  1157. textAlign(CENTER);
  1158. text("Do you want to entitle " + request.application + " ?\nPlease drag the request on to your preferred context." , centerX, 80);
  1159. }
  1160. /* interaction functions */
  1161. void mousePressed()
  1162. {
  1163. //test if request hit
  1164. boolean hitRequest = (mouseX > requestX - requestSize/2 && mouseX < requestX + requestSize/2 &&
  1165. mouseY > requestY - requestSize/2 && mouseY < requestY + requestSize/2);
  1166. //update global
  1167. dragging = hitRequest;
  1168. //test if context hit
  1169. float deltaX = mouseX - centerX;
  1170. float deltaY = mouseY - centerY;
  1171. int index = hitContextIndex(deltaX,deltaY);
  1172. if(index > -1)
  1173. {
  1174. currentContext = index;
  1175. }
  1176. }
  1177. void mouseDragged()
  1178. {
  1179. float deltaX = mouseX - centerX;
  1180. float deltaY = mouseY - centerY;
  1181. float distance = sqrt(deltaX*deltaX + deltaY*deltaY);
  1182. float fraction = distance / (contextSize / 2);
  1183. float minDistanceFactor = (PROPORTION_CENTER + (1-PROPORTION_CENTER)/2);
  1184. //outside the circle is allowed anywhere
  1185. if(fraction > minDistanceFactor && dragging)
  1186. {
  1187. requestX = mouseX - xOffset;
  1188. requestY = mouseY - yOffset;
  1189. hitContext = hitContextIndex(deltaX,deltaY);
  1190. if(hitContext > -1){currentContext = hitContext;}
  1191. }
  1192. else if(dragging) //else calculate closest point that is allowed
  1193. {
  1194. float mouseAngle = (PI/2) - atan2(deltaX, deltaY);
  1195. float x = centerX + (contextSize/2 * minDistanceFactor) * cos(mouseAngle); //calculate xPos
  1196. float y = centerY + (contextSize/2 * minDistanceFactor) * sin(mouseAngle); //calculate yPos
  1197. requestX = x;
  1198. requestY = y;
  1199. hitContext = hitContextIndex(x - centerX, y - centerY);
  1200. currentContext = hitContext;
  1201. }
  1202. }
  1203. void mouseReleased()
  1204. {
  1205. dragging = false;
  1206. //hitContext = -1;
  1207. //PVector position = positionForSectorIndex(1);
  1208. //requestX = position.x;
  1209. //requestY = position.y;
  1210. }
  1211. //calculate which pie part was clicked,
  1212. //by means of the angle between the mouse point an the center of the circle
  1213. //returns -1 if no pie part was clicked
  1214. //(because the distance is not between 0.8 and 1 times the distanceSize)
  1215. int hitContextIndex(float deltaX, float deltaY)
  1216. {
  1217. float distance = sqrt(deltaX*deltaX + deltaY*deltaY);
  1218. float fraction = distance / (contextSize / 2);
  1219. if(fraction < PROPORTION_CENTER || fraction > 1)
  1220. {
  1221. return -1;
  1222. }
  1223. //overstaande, aanliggende => tan dus atan2
  1224. float mouseAngle = atan2(deltaX, deltaY);
  1225. float degrees = mouseAngle * 180 / PI;
  1226. if(degrees < 0){degrees = 360 + degrees;} //so everyting is on one continuum
  1227. float part = eqd_angle * 180 /PI;
  1228. int index = (contexts.length - ((int) (degrees / part))) - 1;//because we start at the bottom.
  1229. return index;
  1230. }
  1231. /* data functions */
  1232. //create a string that lists the values of the intersection in d, with the values in c
  1233. String getIntersection(Context c, Diff d)
  1234. {
  1235. String result = "";
  1236. for(String key : d.intersect)
  1237. {
  1238. String value = getPropertyValue(c, key);
  1239. if(value != null) result += key.substring(key.indexOf(":")+1) + " (" + value + ")\n";
  1240. }
  1241. return result;
  1242. }
  1243. //create a string that lists the missing keys in the intersection d
  1244. String getMissing(Diff d)
  1245. {
  1246. String result = "";
  1247. for(String key : d.except)
  1248. {
  1249. result += key.substring(key.indexOf(":")+1) + "\n";
  1250. }
  1251. return result;
  1252. }
  1253. //get the value of a property value in c designated by key
  1254. String getPropertyValue(Context c, String key)
  1255. {
  1256. int MAX_LENGTH = 10;
  1257. for(Property p : c.properties)
  1258. {
  1259. if(p.type == key)
  1260. {
  1261. String value = p.value;
  1262. if(value.length > MAX_LENGTH)
  1263. {
  1264. value = value.substring(0, Math.min(value.length(), MAX_LENGTH)) + "...";
  1265. }
  1266. return value;
  1267. }
  1268. }
  1269. return null;
  1270. }
  1271. #+END_SRC
  1272. #+name: context_switcher
  1273. #+BEGIN_SRC processing :noweb yes
  1274. <<pcolors>>
  1275. <<glue>>
  1276. <<fitness_src>>
  1277. <<context_switcher_src>>
  1278. #+END_SRC
  1279. #+RESULTS: context_switcher
  1280. #+BEGIN_EXPORT html
  1281. <script src="processing.js"></script>
  1282. <script type="text/processing" data-processing-target="ob-b08b9ab6dca87cd6f3682e15abf24f929a90a794">
  1283. String[] levels = {"commons", "public", "affiliate", "intimate", "private", "secret"};
  1284. String[] contextTypes = {"personal", "health", "education", "work", "hobby", "financial", "other"};
  1285. static class PrivacyLevel {
  1286. public static int SECRET = 5;
  1287. public static int PRIVATE = 4;
  1288. public static int INTIMATE = 3;
  1289. public static int AFFILIATE = 2;
  1290. public static int PUBLIC = 1;
  1291. public static int COMMONS = 0;
  1292. }
  1293. static class ContextType {
  1294. public static int PERSONAL = 0;
  1295. public static int HEALTH = 1;
  1296. public static int EDUCATION = 2;
  1297. public static int WORK = 3;
  1298. public static int HOBBY = 4;
  1299. public static int FINANCIAL = 5;
  1300. public static int OTHER = 6;
  1301. }
  1302. int[][] colors = generateColors();
  1303. //generate colors to maximize contrast
  1304. int[][] generateColors()
  1305. {
  1306. int[][] pcolors = new int[contextTypes.length][levels.length];
  1307. //we start with d0 as a seed color
  1308. color seed = #FFE9BE;//seed color
  1309. colorMode(HSB);
  1310. float h_seed = hue(seed);
  1311. float s_seed = saturation(seed);
  1312. float v_seed = brightness(seed);
  1313. float levelshift = 255/levels.length;
  1314. float contextshift = 255/contextTypes.length + 1;
  1315. //cycle hue for each level
  1316. for (int i = 0; i < contextTypes.length; i++){
  1317. float h = h_seed + i * contextshift;
  1318. if(h > 255){h = h - 255;}
  1319. //cycle brightness for each context
  1320. for(int j = 0; j < levels.length; j++)
  1321. {
  1322. float v = v_seed - (j * levelshift);
  1323. float s = s_seed + (j * levelshift);
  1324. color c = color(h,s,v);
  1325. pcolors[i][j] = c;
  1326. }
  1327. }
  1328. colorMode(RGB);
  1329. return pcolors;
  1330. }
  1331. public int getColor(int privacy_level, int context_type)
  1332. {
  1333. return colors[context_type][privacy_level];
  1334. }
  1335. class Request
  1336. {
  1337. public String application;
  1338. public int contextType;
  1339. public String[] required_properties;
  1340. public String[] optional_properties;
  1341. public int plSum;
  1342. public int plMean;
  1343. public Request(String app, int contextType, String[] req, String[] opt, int sum, int mean)
  1344. {
  1345. this.application = app;
  1346. this.contextType = contextType;
  1347. this.required_properties = req;
  1348. this.optional_properties = opt;
  1349. this.plSum = sum;
  1350. this.plMean = mean;
  1351. }
  1352. }
  1353. class Diff
  1354. {
  1355. public String context;
  1356. public String[] intersect;
  1357. public String[] except;
  1358. public Diff(String ctx, String[] intersect, String[] except)
  1359. {
  1360. this.context = ctx;
  1361. this.intersect = intersect;
  1362. this.except = except;
  1363. }
  1364. }
  1365. class Property
  1366. {
  1367. public String type;
  1368. public String value;
  1369. public int pl;//privacy level
  1370. public Property(String type, String value, int pl)
  1371. {
  1372. this.type = type;
  1373. this.value = value;
  1374. this.pl = pl;
  1375. }
  1376. }
  1377. class Context
  1378. {
  1379. public String title;
  1380. public int plSum;
  1381. public int plMean;
  1382. public int contextType;
  1383. public Property[] properties;
  1384. public Context(String title, int plSum, int plMean, int contextType, Property[] properties)
  1385. {
  1386. this.title = title;
  1387. this.plSum = plSum;
  1388. this.plMean = plMean;
  1389. this.properties = properties;
  1390. this.contextType = contextType;
  1391. }
  1392. }
  1393. XMLElement doc = new XMLElement(this, 'diff.xml');
  1394. //create typed versions because this is java :-(
  1395. Request parseRequest(XMLElement xml)
  1396. {
  1397. XMLElement req = xml.getChild(0);
  1398. String name = req.getChild(0).getContent();
  1399. int contextType = req.getChild(1).getContent();
  1400. String[] required = new String[req.getChild(2).getChildCount()];
  1401. String[] optional = new String[req.getChild(3).getChildCount()];
  1402. int plSum = parseInt(req.getChild(4).getContent());
  1403. int plMean = parseInt(req.getChild(5).getContent());
  1404. for (int i = 0; i < required.length; i++) {
  1405. required[i] = req.getChild(2).getChild(i).getContent();
  1406. }
  1407. for (int i = 0; i < optional.length; i++) {
  1408. optional[i] = req.getChild(3).getChild(i).getContent();
  1409. }
  1410. Request r = new Request(name,contextType,required,optional, plSum, plMean);
  1411. return r;
  1412. }
  1413. Context[] parseProfile(XMLElement xml)
  1414. {
  1415. XMLElement profile = xml.getChild(2);
  1416. Context[] contexts = new Context[profile.getChildCount()];
  1417. for(int i = 0; i < contexts.length; i++)
  1418. {
  1419. String contextName = profile.getChild(i).getChild(0).getContent();
  1420. int contextType = parseInt(profile.getChild(i).getChild(1).getContent());
  1421. int plSum = parseInt(profile.getChild(i).getChild(2).getContent());
  1422. int plMean = parseInt(profile.getChild(i).getChild(3).getContent());
  1423. XMLElement propertiesEl = profile.getChild(i).getChild(4);
  1424. Property[] properties = new Property[propertiesEl.getChildCount()];
  1425. for(int j = 0; j < properties.length; j++)
  1426. {
  1427. Property prop = parseProperty(propertiesEl.getChild(j));
  1428. properties[j] = prop;
  1429. }
  1430. Context context = new Context(contextName, plSum, plMean, contextType, properties);
  1431. contexts[i] = context;
  1432. }
  1433. return contexts;
  1434. }
  1435. Property parseProperty(XMLElement propertyEl)
  1436. {
  1437. String propertyType = propertyEl.getChild(0).getContent();
  1438. String value = propertyEl.getChild(1).getContent();
  1439. int pl = parseInt(propertyEl.getChild(2).getContent());
  1440. return new Property(propertyType, value, pl);
  1441. }
  1442. //create typed versions because this is java :-(
  1443. Diff[] parseDiffs(xml)
  1444. {
  1445. XMLElement diffsEl = xml.getChild(1);
  1446. Diff[] diffs = new Diff[diffsEl.getChildCount()];
  1447. for(int i = 0; i < diffs.length; i++)
  1448. {
  1449. String contextName = diffsEl.getChild(i).getChild(0).getContent();
  1450. String[] intersects = new String[diffsEl.getChild(i).getChild(1).getChildCount()];
  1451. String[] except = new String[diffsEl.getChild(i).getChild(2).getChildCount()];
  1452. for(int j = 0; j < intersects.length; j++)
  1453. {
  1454. intersects[j] = diffsEl.getChild(i).getChild(1).getChild(j).getContent();
  1455. }
  1456. for(int j = 0; j < except.length; j++)
  1457. {
  1458. except[j] = diffsEl.getChild(i).getChild(2).getChild(j).getContent();
  1459. }
  1460. Diff diff = new Diff(contextName,intersects,except);
  1461. diffs[i] = diff;
  1462. }
  1463. return diffs;
  1464. }
  1465. Request request = parseRequest(doc);
  1466. Diff[] diffs = parseDiffs(doc);
  1467. Context[] contexts = parseProfile(doc);
  1468. //magic numbers
  1469. float contextSize = 500;//random(50,100); //random between 50 and 100
  1470. float requestSize = 50;
  1471. float eqd_angle = TWO_PI / contexts.length;
  1472. float PROPORTION_CENTER = 0.6;
  1473. float DISTANCE_FACTOR = 1.4;
  1474. //globals
  1475. float centerX, centerY;
  1476. float xOffset, yOffset;
  1477. float requestX, requestY;//request coordinates, they can move
  1478. boolean hitRequest = false;
  1479. boolean dragging = false;
  1480. int hitContext = -1;
  1481. int currentContext = 1;//first context is the default context
  1482. PFont font;
  1483. PFont titleFont;
  1484. void setup()
  1485. {
  1486. size(800,800);
  1487. rectMode(RADIUS);
  1488. smooth();
  1489. titleFont = createFont("HelveticaNeue", 20);
  1490. font = createFont("HelveticaNeue", 14);
  1491. centerX = width/2;
  1492. centerY = height/2;
  1493. //TODO: calculate most relevant sector for the request
  1494. PVector position = positionForSectorIndex(1);
  1495. requestX = position.x;
  1496. requestY = position.y;
  1497. }
  1498. //the position should be outside the center of the middle of the sector
  1499. PVector positionForSectorIndex(int index)
  1500. {
  1501. float angle = (PI/2) + (index * eqd_angle) + (eqd_angle/2);
  1502. float x = centerX + (contextSize/2 * DISTANCE_FACTOR) * cos(angle); //calculate xPos
  1503. float y = centerY + (contextSize/2 * DISTANCE_FACTOR) * sin(angle); //calculate yPos
  1504. return new PVector(x,y);
  1505. }
  1506. void draw()
  1507. {
  1508. background(255);
  1509. drawTitle();
  1510. }
  1511. void drawTitle()
  1512. {
  1513. //title
  1514. textFont(titleFont);
  1515. fill(150);
  1516. textAlign(CENTER);
  1517. text("Do you want to get *****d by Facebook?\nPlease drag the request on to your preferred context." , centerX, 80);
  1518. }
  1519. </script> <canvas id="ob-b08b9ab6dca87cd6f3682e15abf24f929a90a794"></canvas>
  1520. #+END_EXPORT