diff options
author | Tim McNamara <paperless@timmcnamara.co.nz> | 2010-08-14 01:08:00 (GMT) |
---|---|---|
committer | Tim McNamara <paperless@timmcnamara.co.nz> | 2010-08-14 01:08:00 (GMT) |
commit | 4384051284e982fd504a1991d86135312f0f9e34 (patch) | |
tree | becffc1c682225df2b9a3d281162fd75de4388da |
initial commit
46 files changed, 3576 insertions, 0 deletions
diff --git a/Experior.Activity/MANIFEST b/Experior.Activity/MANIFEST new file mode 100644 index 0000000..1115bb7 --- /dev/null +++ b/Experior.Activity/MANIFEST @@ -0,0 +1,42 @@ +test_sugarbot.pyc +test_sbexecutionengine.pyc +sblog.pyc +sbexecutionengine.py +test_widgetIdentifier.py +test_sbgui.pyc +sugarbot.py +test_sbgui.py +sbgui.py +test_widgetIdentifier.pyc + +script_terminal.py +sbrpcserver.pyc +sbconfig_sample.pyc +MANIFEST +test_rpcserver.pyc +MANIFEST~ +sbrpcserver.py +sbpython_script.py +activity/activity.info +script_calculate.py +sbexecutionengine.pyc +setup.py +test_sbexecutionengine.py +sugarbot.pyc +sbdecorators.pyc +make-manifest +sbgui.pyc +widgetIdentifier.pyc +sugarbotlauncher.py +script_terminal.pyc +sblog.py +master.cfg +sbdecorators.py +sbpython.pyc +test_rpcserver.py +script_calculate.pyc +test_sugarbot.py +modded-main.py +widgetIdentifier.py +sbpython.py +sbconfig_sample.py diff --git a/Experior.Activity/activity/activity.info b/Experior.Activity/activity/activity.info new file mode 100644 index 0000000..75dbed0 --- /dev/null +++ b/Experior.Activity/activity/activity.info @@ -0,0 +1,7 @@ +[Activity] +name = sugarbot +service_name = org.laptop.sugarbot +class = sugarbot.sugarbot +icon = sugarbot +activity_version = 1 +show_launcher = yes diff --git a/Experior.Activity/activity/sugarbot.svg b/Experior.Activity/activity/sugarbot.svg new file mode 100644 index 0000000..c5ba0c2 --- /dev/null +++ b/Experior.Activity/activity/sugarbot.svg @@ -0,0 +1,64 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!-- Created with Inkscape (http://www.inkscape.org/) --> + +<svg + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + version="1.1" + width="40.310001" + height="45.539135" + id="svg2"> + <defs + id="defs4" /> + <metadata + id="metadata7"> + <rdf:RDF> + <cc:Work + rdf:about=""> + <dc:format>image/svg+xml</dc:format> + <dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> + <dc:title></dc:title> + </cc:Work> + </rdf:RDF> + </metadata> + <g + transform="translate(-260.87975,-345.59506)" + id="layer1"> + <g + transform="translate(-104.55079,4.5456865)" + id="g3080"> + <path + d="m 365.9103,385.76896 c -0.85229,-0.61039 -0.57298,-0.75127 0.86971,-0.43866 1.98057,0.42915 2.0354,0.28739 0.80564,-2.08296 -1.61231,-3.10774 -0.41136,-5.88074 3.12997,-7.22716 1.34156,-0.51006 2.4392,-1.67381 2.4392,-2.58611 0,-2.78275 2.24941,-9.35466 3.03557,-8.86878 0.40624,0.25106 0.67693,-0.31532 0.60152,-1.25863 -0.0754,-0.94331 0.38791,-1.72597 1.02958,-1.73924 0.64166,-0.0133 1.54166,-0.43377 2,-0.93445 0.48416,-0.52889 0.30957,-0.60891 -0.41667,-0.19098 -1.77933,1.02396 -1.5517,-0.90342 0.28692,-2.42933 0.84531,-0.70155 1.41101,-2.01881 1.2571,-2.92726 -0.1539,-0.90845 0.56029,-2.9339 1.58709,-4.501 1.60229,-2.4454 1.99561,-2.61932 2.77481,-1.22696 1.07102,1.91379 7.63438,5.2289 8.5465,4.31677 0.347,-0.347 0.0644,-0.63091 -0.62802,-0.63091 -0.69242,0 -2.32941,-0.84203 -3.63776,-1.87118 -2.37438,-1.86768 -2.37507,-1.87456 -0.37246,-3.6869 1.10349,-0.99865 1.76038,-2.45672 1.45975,-3.24016 -0.37394,-0.97448 -0.0572,-1.23665 1.00258,-0.82996 0.88745,0.34055 1.79231,-0.039 2.11833,-0.88866 0.60674,-1.58113 3.89544,-2.03599 4.7479,-0.65668 0.74258,1.20152 -0.46156,5.17354 -1.56839,5.17354 -0.51831,0 -0.66427,-0.45 -0.32435,-1 0.33992,-0.55 -0.30852,-1 -1.44098,-1 -1.13246,0 -2.05902,0.47656 -2.05902,1.05902 0,0.58246 0.5625,0.72107 1.25,0.30804 1.36828,-0.82204 -1.07323,2.62534 -2.75,3.88296 -0.57471,0.43105 -0.0225,1.01765 1.29842,1.3793 3.40751,0.93292 2.40846,3.87068 -1.31631,3.87068 -1.64016,0 -3.7423,-0.33872 -4.67142,-0.7527 -0.92912,-0.41399 -1.93656,-0.35265 -2.23875,0.13631 -0.60738,0.98274 2.65182,2.08153 6.23708,2.10273 1.51564,0.009 2.09145,0.44316 1.67584,1.26366 -0.34825,0.6875 0.0523,0.40773 0.89021,-0.6217 1.48824,-1.82851 1.59957,-1.82812 4.82416,0.0168 4.55328,2.60507 8.15438,9.95308 7.24733,14.78807 -0.84308,4.494 -4.24193,9.6565 -7.39133,11.22664 -2.35042,1.17181 -2.33559,1.19491 1.03001,1.60453 1.87812,0.22859 -0.52274,0.48763 -5.33524,0.57565 -4.8125,0.088 -8.75,-0.21773 -8.75,-0.67945 0,-0.46171 -1.12603,-1.35254 -2.5023,-1.9796 -3.61077,-1.64518 -6.47332,-6.93458 -6.59214,-12.18091 l -0.10191,-4.5 -1.24783,4.5 c -1.05113,3.79063 -1.01685,4.76965 0.21746,6.21055 0.87342,1.01961 1.28511,2.93828 1.0192,4.75 -0.2968,2.02222 -0.0218,3.03945 0.8217,3.03945 0.69728,0 1.47286,0.3375 1.72351,0.75 0.65352,1.07551 -11.07818,1.05281 -12.58221,-0.0243 z m 8.81074,-3.93429 c 0.34785,-1.09599 0.13775,-2.4874 -0.46688,-3.09203 -0.79912,-0.79912 -1.09934,-0.49854 -1.09934,1.10066 0,1.28889 -0.62121,2.2 -1.5,2.2 -1.81035,0 -2.01237,-2.65034 -0.25,-3.27976 0.6875,-0.24554 0.92337,-0.79716 0.52417,-1.22584 -1.01964,-1.09489 -3.77417,1.40703 -3.77417,3.42804 0,4.06418 5.32829,4.76928 6.56622,0.86893 z m 21.944,0.76881 c 8.68458,-3.62865 9.99506,-17.90688 2.11728,-23.0686 -3.19692,-2.0947 -4.22394,-1.45626 -5.09084,3.1647 -0.52905,2.82005 -0.32216,3.88426 0.88664,4.56073 2.05198,1.14835 2.02512,2.78299 -0.0457,2.78299 -1.39808,0 -1.43265,0.23397 -0.25,1.69196 0.93256,1.14966 0.97181,1.45126 0.12245,0.94098 -0.6875,-0.41304 -1.25351,-0.38692 -1.2578,0.058 -0.0114,1.184 -4.655,3.14193 -5.74653,2.42298 -0.51268,-0.33768 -1.00371,-2.63531 -1.09118,-5.10586 -0.23858,-6.73803 -1.89954,-11.03969 -3.96413,-10.2666 -1.52943,0.5727 -1.54756,0.44853 -0.19036,-1.30427 1.39261,-1.79854 1.37298,-1.83754 -0.27417,-0.54473 -0.97579,0.76588 -1.57668,1.58998 -1.33531,1.83135 0.24137,0.24137 -0.41288,2.31475 -1.45387,4.6075 -2.41,5.30792 -2.42221,9.41217 -0.0388,13.02735 4.10605,6.22802 10.57971,8.13989 17.61233,5.20148 z m -1.94593,-5.26705 c -0.54268,-1.4252 -0.42782,-1.54006 0.5598,-0.5598 0.68342,0.67834 0.99067,1.48524 0.68278,1.79313 -0.30789,0.30789 -0.86705,-0.24711 -1.24258,-1.23333 z m -12.56429,-0.68614 c 0,-0.21616 0.69837,-0.661 1.55194,-0.98855 0.88992,-0.34149 1.29132,-0.17385 0.94098,0.39301 -0.58726,0.95021 -2.49292,1.40545 -2.49292,0.59554 z m 14.50708,-1.59554 c -0.35034,-0.56686 0.0511,-0.7345 0.94098,-0.39301 1.73242,0.66479 2.04939,1.38156 0.61096,1.38156 -0.51754,0 -1.21592,-0.44485 -1.55194,-0.98855 z m -13.69458,-2.69456 c 0.72187,-0.28888 1.58437,-0.25335 1.91667,0.079 0.33229,0.33229 -0.25834,0.56864 -1.3125,0.52522 -1.16495,-0.048 -1.4019,-0.28495 -0.60417,-0.60417 z m 5.14855,-10.4527 c -0.97243,-3.39067 -1.68486,-4.47372 -2.44366,-3.71491 -0.64963,0.64962 1.73283,6.85072 2.63204,6.85072 0.39103,0 0.30626,-1.41112 -0.18838,-3.13581 z m 2.33763,0.47176 c -0.44681,-1.16435 -0.26507,-1.43321 0.60521,-0.89535 0.92626,0.57246 0.90582,0.22284 -0.0801,-1.36961 -1.07588,-1.73779 -1.38649,-1.8401 -1.6584,-0.54623 -0.35546,1.69146 0.43565,4.47524 1.2718,4.47524 0.275,0 0.21265,-0.74882 -0.13855,-1.66405 z m -6.00099,-11.00256 c -0.46859,-1.22112 -0.73371,-1.25231 -1.42154,-0.16724 -1.26394,1.99392 -1.05071,2.83806 0.56071,2.21971 0.79027,-0.30326 1.17765,-1.22687 0.86083,-2.05247 z m -3.54769,34.00433 c 0.6875,-0.27741 1.8125,-0.27741 2.5,0 0.6875,0.27741 0.125,0.50438 -1.25,0.50438 -1.375,0 -1.9375,-0.22697 -1.25,-0.50438 z m 5.25,-38.73073 c 0,-0.76616 0.68541,-1.65603 1.52314,-1.9775 1.24266,-0.47685 1.32825,-0.22033 0.46481,1.39302 -1.24059,2.31806 -1.98795,2.53779 -1.98795,0.58448 z" + id="path3094" + style="fill:#b9b9b9;fill-opacity:1" /> + <path + d="m 367.82465,386.15919 c -0.89097,-1.94287 -0.65553,-7.42819 0.37197,-8.66625 1.45948,-1.75857 5.11427,-1.89357 6.7582,-0.24964 1.52892,1.52892 1.48962,4.83919 -0.0818,6.88924 -1.24268,1.62118 -6.5363,3.14328 -7.04837,2.02665 z m 5.74778,-2.43199 c 0.98993,-0.72385 1.63844,-2.09762 1.44113,-3.05282 -0.40184,-1.94539 -2.43711,-1.83636 -2.21985,0.11892 0.0764,0.6875 -0.48611,1.25 -1.25,1.25 -1.72823,0 -1.84137,-2.67173 -0.13889,-3.27976 0.6875,-0.24554 0.92337,-0.79716 0.52417,-1.22584 -1.01964,-1.09489 -3.77417,1.40703 -3.77417,3.42804 0,1.64033 1.79846,4.07756 3.00887,4.07756 0.33488,0 1.41881,-0.59224 2.40874,-1.3161 z m 8.39489,1.63299 c 0.72187,-0.28888 1.58437,-0.25335 1.91667,0.079 0.33229,0.33229 -0.25834,0.56864 -1.3125,0.52522 -1.16495,-0.048 -1.4019,-0.28495 -0.60417,-0.60417 z m 0.57581,-2.32045 c -2.26142,-1.60291 -4.60642,-4.17035 -5.21109,-5.70541 -1.60043,-4.06293 -1.85184,-13.56616 -0.37194,-14.05946 0.66395,-0.22131 0.98999,-0.75382 0.72453,-1.18333 -0.26546,-0.42952 0.40674,-1.58583 1.49377,-2.56958 1.08703,-0.98375 1.97642,-3.04233 1.97642,-4.57463 0,-1.5323 0.44485,-3.06093 0.98855,-3.39695 0.5684,-0.35129 0.73358,0.0535 0.38865,0.95232 -0.41743,1.08781 -0.15488,1.39252 0.86321,1.00184 0.94507,-0.36266 1.31701,-1.51737 1.05051,-3.26134 -0.34062,-2.22899 -0.22545,-2.40639 0.66032,-1.01712 1.18639,1.86078 6.56348,4.81722 8.76141,4.81722 0.77296,0 1.15489,0.40531 0.84872,0.9007 -0.58132,0.94059 -6.79379,0.69173 -8.46147,-0.33896 -0.52894,-0.3269 -1.23898,-0.14573 -1.57786,0.4026 -0.35134,0.56848 1.12323,1.27511 3.4309,1.64412 2.22588,0.35594 4.04706,1.03964 4.04706,1.51935 0,0.4797 0.45,0.87219 1,0.87219 0.55,0 1,-0.675 1,-1.5 0,-1.98231 0.84879,-1.88956 4.81018,0.52563 6.21243,3.78762 8.24808,14.63881 3.93982,21.00157 -4.97289,7.34433 -13.30775,8.96911 -20.36169,3.96924 z m 14.70557,-0.54499 c 8.22962,-4.2557 9.14582,-17.97216 1.53362,-22.95987 -3.19459,-2.09317 -4.37167,-1.37218 -5.23009,3.20358 -0.53959,2.87625 -0.30156,3.90176 1.1524,4.96492 1.74646,1.27704 1.74807,1.33992 0.0343,1.33992 -1.09252,0 -1.59624,0.5261 -1.28365,1.34068 0.8169,2.1288 -0.72791,3.7195 -3.83428,3.9482 -2.73037,0.20101 -2.87111,0.008 -2.93905,-4.03888 -0.0693,-4.12712 0.78475,-5.54222 1.81056,-3 0.27741,0.6875 0.53991,0.21094 0.58333,-1.05902 0.0434,-1.26996 0.49655,-2.05092 1.00695,-1.73548 1.71279,1.05856 2.27926,-3.39109 0.64036,-5.03 -1.42448,-1.42448 -1.57418,-1.35539 -1.63175,0.75307 l -0.0634,2.32143 -0.90439,-2.25 c -0.85367,-2.12382 -2.96884,-3.166 -2.96884,-1.4628 0,0.43295 -0.93402,0.85755 -2.07559,0.94354 -1.70105,0.12814 -1.85941,-0.10414 -0.87756,-1.28719 2.0948,-2.52408 0.40076,-1.55221 -2.12254,1.2177 -1.33337,1.46369 -1.86181,2.33713 -1.17431,1.94098 1.61295,-0.9294 1.57469,-0.45341 -0.24384,3.03346 -5.95688,11.4218 6.97793,23.81942 18.58772,17.81576 z m -11.47034,-18.99011 c -0.96498,-2.51469 -0.68031,-3.16355 0.49853,-1.13634 0.58369,1.00375 0.8506,2.03566 0.59313,2.29313 -0.25747,0.25747 -0.74872,-0.26309 -1.09166,-1.15679 z m 6.37646,7.53866 c 0,-0.55 -0.7875,-0.99311 -1.75,-0.98469 -1.49555,0.0131 -1.5319,0.15626 -0.25,0.98469 1.88721,1.21961 2,1.21961 2,0 z m 5.8125,14.31689 c 0.72187,-0.28888 1.58437,-0.25335 1.91667,0.079 0.33229,0.33229 -0.25834,0.56864 -1.3125,0.52522 -1.16495,-0.048 -1.4019,-0.28495 -0.60417,-0.60417 z M 373.31271,373.0433 c 0,-1.375 0.22698,-1.9375 0.50439,-1.25 0.27741,0.6875 0.27741,1.8125 0,2.5 -0.27741,0.6875 -0.50439,0.125 -0.50439,-1.25 z m 0.92106,-4.58333 c 0.048,-1.16495 0.28494,-1.4019 0.60416,-0.60417 0.28887,0.72187 0.25335,1.58437 -0.0789,1.91667 -0.33229,0.33229 -0.56864,-0.25834 -0.52522,-1.3125 z m 15.42105,-17.41667 c -1.72598,-1.90718 -1.72862,-2.08989 -0.057,-3.93699 0.96412,-1.06534 1.45731,-2.41534 1.09597,-3 -0.36133,-0.58465 -0.22856,-1.06301 0.29505,-1.06301 1.32152,0 2.3202,2.93409 1.4773,4.34024 -0.38237,0.63787 0.17867,0.49838 1.24675,-0.30998 1.30918,-0.99083 1.15933,-0.59132 -0.45986,1.22606 l -2.40183,2.6958 2.40183,0.96055 c 2.21804,0.88706 2.24152,0.96541 0.30681,1.02394 -1.15226,0.0349 -2.9095,-0.83661 -3.90499,-1.93661 z m -3.5,-4.37756 c 0,-0.20765 0.7875,-0.99515 1.75,-1.75 1.58606,-1.24387 1.62143,-1.2085 0.37755,0.37756 -1.30636,1.66575 -2.12755,2.19548 -2.12755,1.37244 z m 10.57654,-0.79948 c 0.45359,-1.30828 0.1243,-1.55749 -1.49755,-1.13337 -2.19579,0.57422 -2.60919,-0.30463 -1.19385,-2.53798 0.95072,-1.50021 4.25585,-1.23222 4.79547,0.38883 0.19049,0.57224 -0.33582,1.92224 -1.16959,3 -1.14452,1.47947 -1.37347,1.54869 -0.93448,0.28252 z" + id="path3092" + style="fill:#a3a3a3;fill-opacity:1" /> + <path + d="m 368.22516,383.67474 c -1.54963,-2.89551 -1.12737,-5.56631 1.102,-6.97016 2.51057,-1.58091 6.83615,0.68967 6.79586,3.56728 -0.0755,5.39139 -5.56729,7.75759 -7.89786,3.40288 z m 5.72966,0.16856 c 1.66988,-1.66988 1.49734,-4.30834 -0.38382,-5.86957 -3.11745,-2.58725 -7.02399,2.42735 -4.37631,5.61761 1.46542,1.76572 3.15684,1.85525 4.76013,0.25196 z m -3.3,-2.8 c -0.33992,-0.55 -0.16803,-1 0.38197,-1 0.55,0 1.27811,0.45 1.61803,1 0.33992,0.55 0.16803,1 -0.38197,1 -0.55,0 -1.27811,-0.45 -1.61803,-1 z m 12.32801,2.21298 c -5.51305,-4.0732 -7.00044,-7.23142 -6.65819,-14.13741 0.16637,-3.35684 0.69807,-5.65722 1.23142,-5.32759 0.51625,0.31906 0.66751,-0.12643 0.33614,-0.98998 -0.34654,-0.90307 0.21851,-2.31309 1.33006,-3.31903 1.06291,-0.96192 1.93256,-3.00264 1.93256,-4.53494 0,-1.5323 0.44485,-3.06093 0.98855,-3.39695 0.56686,-0.35034 0.7345,0.0511 0.39301,0.94098 -0.38017,0.99069 -0.0357,1.55194 0.95243,1.55194 2.00027,0 3.05787,-1.69373 1.42224,-2.27769 -0.69093,-0.24667 -0.97168,-0.75716 -0.62389,-1.13441 0.64909,-0.70408 3.52032,0.73748 12.51422,6.28303 5.98424,3.68983 8.42648,7.94639 8.1934,14.2802 -0.14467,3.93123 -0.1946,4.01275 -0.51994,0.84887 -0.19795,-1.925 -0.5758,-4.47782 -0.83967,-5.67293 -0.69475,-3.1466 -5.35191,-8.32707 -7.48591,-8.32707 -1.00528,0 -1.98571,-0.47381 -2.17874,-1.0529 -0.49247,-1.4774 -8.09126,-3.07154 -9.22706,-1.93574 -0.55082,0.55082 0.73111,1.17604 3.23094,1.57578 2.27924,0.36447 3.91778,1.0288 3.64121,1.4763 -0.27657,0.4475 0.23896,1.22878 1.14562,1.73617 1.30221,0.72875 1.41804,1.3193 0.55144,2.81146 -1.04494,1.79923 -1.09848,1.78789 -1.12744,-0.23862 -0.0353,-2.46676 -2.46281,-4.84128 -3.98661,-3.89952 -0.57409,0.35481 -1.0438,0.15699 -1.0438,-0.43959 0,-2.03012 -5.75773,1.83859 -7.90346,5.31045 -3.79852,6.14614 -2.17349,14.37552 3.63837,18.42527 6.11645,4.26199 11.63381,3.70263 17.37816,-1.76184 4.0849,-3.88587 4.9474,-3.96627 2.32085,-0.21634 -2.12969,3.04056 -8.6373,6.21719 -12.64408,6.1721 -1.96303,-0.0221 -4.77078,-1.13119 -6.96183,-2.75 z m -1.82801,-23.71298 c 1.29174,-1.375 2.12363,-2.5 1.84863,-2.5 -0.275,0 -1.55689,1.125 -2.84863,2.5 -1.29175,1.375 -2.12363,2.5 -1.84863,2.5 0.275,0 1.55688,-1.125 2.84863,-2.5 z m 5.44069,14.40404 c -0.37254,-0.60278 0.11479,-1.84028 1.08296,-2.75 1.63827,-1.53939 1.72002,-1.53809 1.17936,0.0187 -0.46904,1.35055 -0.063,1.59007 2.10803,1.24364 2.56846,-0.40984 2.60197,-0.36156 0.74783,1.07729 -2.29281,1.77927 -4.17871,1.93049 -5.11818,0.4104 z m 4.22913,-4.3928 c -1.04239,-0.76832 -1.68806,-2.26684 -1.5,-3.48132 0.32749,-2.11497 0.35822,-2.11156 3.77446,0.41887 2.14272,1.58713 2.83552,2.55121 1.83333,2.55121 -0.88602,0 -1.76094,0.41855 -1.94428,0.93011 -0.18333,0.51157 -1.15691,0.32307 -2.16351,-0.41887 z m 1.08018,-17.17352 c 0.6875,-0.27741 1.8125,-0.27741 2.5,0 0.6875,0.27741 0.125,0.50438 -1.25,0.50438 -1.375,0 -1.9375,-0.22697 -1.25,-0.50438 z m -3.10231,-2.09875 c -0.90789,-1.469 2.20801,-4.50832 3.94038,-3.84355 1.56241,0.59956 1.55468,0.78936 -0.10694,2.62543 -1.89682,2.09596 -3.06206,2.46623 -3.83344,1.21812 z m 3.35231,-1.73897 c 0.71755,-0.86459 0.81474,-1.5 0.22943,-1.5 -0.5585,0 -1.27447,0.675 -1.59106,1.5 -0.31658,0.825 -0.41982,1.5 -0.22943,1.5 0.19039,0 0.90637,-0.675 1.59106,-1.5 z m -6,-1.87756 c 0,-0.20765 0.7875,-0.99515 1.75,-1.75 1.58606,-1.24387 1.62143,-1.2085 0.37755,0.37756 -1.30636,1.66575 -2.12755,2.19548 -2.12755,1.37244 z m 4.5,-2.62244 c -0.33992,-0.55 -0.14148,-1 0.44098,-1 0.58246,0 1.05902,0.45 1.05902,1 0,0.55 -0.19844,1 -0.44098,1 -0.24254,0 -0.7191,-0.45 -1.05902,-1 z m 2.5,0 c 0,-0.55 0.675,-1 1.5,-1 0.825,0 1.5,0.45 1.5,1 0,0.55 -0.675,1 -1.5,1 -0.825,0 -1.5,-0.45 -1.5,-1 z" + id="path3090" + style="fill:#8c8c8c;fill-opacity:1" /> + <path + d="m 368.39537,384.05687 c -1.78788,-2.86285 -1.52986,-3.87163 0.31615,-1.23608 0.85619,1.22238 2.18067,2.22251 2.9433,2.22251 0.76263,0 2.08711,-1.00013 2.9433,-2.22251 0.85618,-1.22238 1.5567,-1.74212 1.5567,-1.15498 0,1.48664 -3.36019,4.37749 -5.08819,4.37749 -0.7869,0 -1.98896,-0.89389 -2.67126,-1.98643 z m 16.21683,0.12351 c -5.51869,-3.36541 -8.45738,-7.80034 -8.45738,-12.76346 0,-2.46012 0.68656,-5.80059 1.52568,-7.42328 0.83912,-1.62269 1.21938,-2.95034 0.84501,-2.95034 -0.37437,0 0.12033,-0.88509 1.09932,-1.96686 0.979,-1.08178 1.77999,-3.11499 1.77999,-4.51824 0,-1.40326 0.3886,-2.79154 0.86355,-3.08508 0.47495,-0.29353 0.57804,0.21032 0.22909,1.11967 -0.47591,1.24018 -0.16067,1.55648 1.26145,1.26568 1.25015,-0.25563 1.77661,-1.05509 1.54564,-2.34715 -0.34458,-1.92769 -0.3162,-1.92864 1.75,-0.0588 1.15515,1.04539 2.10027,1.59959 2.10027,1.23154 0,-0.99277 8.0096,4.28766 11.75,7.74633 1.7875,1.65286 3.25,3.40145 3.25,3.88576 0,0.48431 -1.1786,-0.52013 -2.61912,-2.23208 -4.11404,-4.88927 -13.32636,-8.76644 -17.25102,-7.2604 -1.10249,0.42306 -0.17629,1.01708 2.87134,1.8415 3.54185,0.95812 3.86066,1.19524 1.4988,1.11471 -4.04923,-0.13805 -6.9074,1.56847 -9.41062,5.61876 -5.26279,8.51538 -0.15219,19.229 10.08909,21.15028 3.86575,0.72522 6.68056,-0.29482 10.70871,-3.88062 1.61938,-1.44156 2.23764,-1.64259 1.92225,-0.62504 -0.67791,2.18719 -7.82283,5.96504 -11.30943,5.97981 -1.65,0.007 -4.36918,-0.82224 -6.04262,-1.84273 z m -2.95738,-25.13708 c 0.99549,-1.1 1.58497,-2 1.30997,-2 -0.275,0 -1.31448,0.9 -2.30997,2 -0.99549,1.1 -1.58498,2 -1.30998,2 0.275,0 1.31449,-0.9 2.30998,-2 z m -11,22 c -0.33992,-0.55 -0.14148,-1 0.44098,-1 0.58246,0 1.05902,0.45 1.05902,1 0,0.55 -0.19844,1 -0.44098,1 -0.24254,0 -0.7191,-0.45 -1.05902,-1 z m -3.5,-1.94098 c 0,-0.51754 0.44485,-1.21592 0.98855,-1.55194 0.56686,-0.35034 0.7345,0.0511 0.39301,0.94098 -0.66479,1.73242 -1.38156,2.04939 -1.38156,0.61096 z m 8,0.19098 c 0,-0.59583 -1.2375,-1.53531 -2.75,-2.08772 -1.5125,-0.55241 -2.04851,-1.03991 -1.19113,-1.08333 2.07496,-0.10509 5.63733,2.55818 4.68956,3.50596 -0.41164,0.41163 -0.74843,0.26092 -0.74843,-0.33491 z m 12.45888,-5.31653 c -0.36253,-0.58659 -0.44468,-1.281 -0.18255,-1.54313 0.26212,-0.26213 0.74306,0.21781 1.06875,1.06653 0.67618,1.76211 0.0998,2.07206 -0.8862,0.4766 z m 2.16941,-0.72588 c -0.26935,-0.43583 0.38882,-0.8586 1.4626,-0.93948 1.07378,-0.0809 1.69105,0.2757 1.37171,0.79241 -0.70857,1.14648 -2.16978,1.22229 -2.83431,0.14707 z m 2.10955,-2.80308 c -0.39627,-0.39627 -0.16189,-1.39807 0.52084,-2.22622 1.08202,-1.3125 0.95258,-1.35188 -1.00868,-0.30683 -1.2375,0.6594 -2.25,0.78122 -2.25,0.27071 0,-1.75623 3.16583,-2.13942 5.30376,-0.64196 1.6926,1.18554 1.7978,1.49979 0.50208,1.49979 -0.90155,0 -1.79855,0.47812 -1.99334,1.0625 -0.19479,0.58437 -0.67839,0.73828 -1.07466,0.34201 z m 12.42005,-2.40451 c 0,-1.375 0.22698,-1.9375 0.50439,-1.25 0.27741,0.6875 0.27741,1.8125 0,2.5 -0.27741,0.6875 -0.50439,0.125 -0.50439,-1.25 z m -12.15789,-5.44098 c 0,-0.79254 0.40803,-1.69317 0.90674,-2.00138 0.49871,-0.30822 0.67122,0.34022 0.38337,1.44098 -0.61061,2.33499 -1.29011,2.63015 -1.29011,0.5604 z m 0.8125,-10.24213 c 0.72187,-0.28888 1.58437,-0.25335 1.91667,0.079 0.33229,0.33229 -0.25834,0.56864 -1.3125,0.52522 -1.16495,-0.048 -1.4019,-0.28495 -0.60417,-0.60417 z m -3.8125,-2.12576 c 0,-0.44488 0.64471,-1.68238 1.43268,-2.75 1.07332,-1.45424 1.72225,-1.65591 2.58713,-0.804 0.87217,0.85908 0.85085,1.32477 -0.0872,1.90451 -0.80038,0.49466 -0.98806,0.35708 -0.52811,-0.38713 0.39244,-0.63498 0.23697,-1.15451 -0.34549,-1.15451 -0.97104,0 -1.47219,1.38327 -1.17744,3.25 0.0651,0.4125 -0.33158,0.75 -0.88158,0.75 -0.55,0 -1,-0.36399 -1,-0.80887 z m -3,-3.56869 c 0,-0.20765 0.7875,-0.99515 1.75,-1.75 1.58606,-1.24387 1.62143,-1.2085 0.37755,0.37756 -1.30636,1.66575 -2.12755,2.19548 -2.12755,1.37244 z m 7,-2.62244 c 0,-0.55 0.675,-1 1.5,-1 0.825,0 1.5,0.45 1.5,1 0,0.55 -0.675,1 -1.5,1 -0.825,0 -1.5,-0.45 -1.5,-1 z" + id="path3088" + style="fill:#767676;fill-opacity:1" /> + <path + d="m 368.80981,384.21456 c -0.91025,-1.00581 -1.64131,-2.24331 -1.62458,-2.75 0.0167,-0.50669 0.50516,-0.10504 1.0854,0.89256 0.58025,0.9976 2.02907,2.20657 3.21959,2.68661 1.4653,0.59083 1.64931,0.89327 0.56959,0.93618 -0.87726,0.0349 -2.33976,-0.75954 -3.25,-1.76535 z m 20.10667,1.12148 c 1.24391,-0.23919 3.04391,-0.23011 4,0.0202 0.95609,0.25029 -0.0617,0.446 -2.26166,0.4349 -2.2,-0.0111 -2.98225,-0.21589 -1.73834,-0.45508 z m 6.97167,-1.41683 c 0.67834,-0.68342 1.48524,-0.99067 1.79313,-0.68278 0.30789,0.30788 -0.24711,0.86705 -1.23333,1.24258 -1.4252,0.54268 -1.54006,0.42782 -0.5598,-0.5598 z m -21.16904,-1.16904 c 0.37553,-0.98622 0.93469,-1.54122 1.24258,-1.23333 0.30789,0.30789 6.4e-4,1.11479 -0.68278,1.79313 -0.98762,0.98026 -1.10248,0.8654 -0.5598,-0.5598 z m 5.19412,-2.45687 c -1.43867,-1.5125 -2.87555,-4.1 -3.19307,-5.75 l -0.5773,-3 1.13074,3 c 0.6219,1.65 2.28379,4.2375 3.69307,5.75 1.40928,1.5125 2.33733,2.75 2.06233,2.75 -0.275,0 -1.67709,-1.2375 -3.11577,-2.75 z m 9.84289,-7.08609 c -0.25451,-0.41181 0.40608,-0.81419 1.46798,-0.89417 2.29656,-0.173 2.4986,0.29836 0.46274,1.0796 -0.80738,0.30982 -1.67621,0.22638 -1.93072,-0.18543 z m -13.44341,-4.16391 c 0,-1.375 0.22698,-1.9375 0.50439,-1.25 0.27741,0.6875 0.27741,1.8125 0,2.5 -0.27741,0.6875 -0.50439,0.125 -0.50439,-1.25 z m 16.61056,-0.16845 c 0.5811,-0.5811 1.64682,-0.85979 2.36826,-0.61931 0.74895,0.24964 0.29565,0.70294 -1.05655,1.05655 -1.66335,0.43497 -2.05378,0.30483 -1.31171,-0.43724 z m -13.34413,-7.97578 c 0.15312,-0.74567 0.73616,-2.48077 1.29564,-3.85577 0.55948,-1.375 0.76571,-3.16026 0.45829,-3.96724 -0.4913,-1.28966 -0.36595,-1.28966 1.03567,0 0.87704,0.80698 1.5252,1.91724 1.44035,2.46724 -0.0849,0.55 0.52073,0.85536 1.34573,0.67857 0.91235,-0.1955 1.10823,0.006 0.5,0.51498 -0.55,0.46003 -2.20467,1.88977 -3.67704,3.1772 -1.54143,1.34782 -2.55894,1.76567 -2.39864,0.98502 z m 2.70323,-2.60577 c 1.24388,-1.58606 1.20851,-1.62143 -0.37755,-0.37755 -0.9625,0.75484 -1.75,1.54234 -1.75,1.75 0,0.82303 0.82119,0.2933 2.12755,-1.37245 z m 4.87245,-4.52182 c 0,-0.46542 -0.375,-0.61445 -0.83333,-0.33119 -0.45834,0.28327 -0.83334,-0.0728 -0.83334,-0.7912 0,-1.01928 0.62245,-1.07129 2.83334,-0.23678 3.27756,1.23714 3.75105,2.57582 0.83333,2.35604 -1.1,-0.0829 -2,-0.53145 -2,-0.99687 z m -1,-7.10574 c 0,-0.20765 0.7875,-0.99515 1.75,-1.75 1.58606,-1.24387 1.62143,-1.2085 0.37755,0.37756 -1.30636,1.66575 -2.12755,2.19548 -2.12755,1.37244 z m 7,-2.62244 c 0,-0.55 0.675,-1 1.5,-1 0.825,0 1.5,0.45 1.5,1 0,0.55 -0.675,1 -1.5,1 -0.825,0 -1.5,-0.45 -1.5,-1 z" + id="path3086" + style="fill:#606060;fill-opacity:1" /> + <path + d="m 369.02726,384.2933 c -1.19261,-1.5207 -1.1759,-1.60381 0.12756,-0.63427 0.825,0.61365 2.175,1.36562 3,1.67105 1.25475,0.46453 1.23389,0.56823 -0.12756,0.63427 -0.89515,0.0434 -2.24515,-0.70855 -3,-1.67105 z m 7.69185,-8.95687 c -0.54268,-1.4252 -0.42782,-1.54006 0.5598,-0.5598 0.68342,0.67834 0.99067,1.48524 0.68278,1.79313 -0.30789,0.30789 -0.86705,-0.24711 -1.24258,-1.23333 z m 2.81733,-14.68749 c 0.11332,-0.77213 0.74491,-2.05318 1.40353,-2.84677 0.65863,-0.79359 0.8639,-1.79994 0.45617,-2.23633 -0.40772,-0.4364 0.27118,-0.26666 1.50868,0.37719 1.2375,0.64385 2.25,1.40636 2.25,1.69447 0,0.28811 -0.46971,0.23354 -1.04381,-0.12127 -1.24911,-0.772 -4.15047,1.4037 -3.42887,2.57128 0.27792,0.44968 0.0875,1.07583 -0.42322,1.39145 -0.51069,0.31563 -0.83581,-0.0579 -0.72248,-0.83002 z m 6.49082,-8.35564 c -1.24387,-1.58606 -1.2085,-1.62143 0.37756,-0.37755 0.9625,0.75484 1.75,1.54234 1.75,1.75 0,0.82303 -0.82119,0.2933 -2.12756,-1.37245 z" + id="path3084" + style="fill:#494949;fill-opacity:1" /> + <path + d="m 380.65482,358.0433 c 0.99549,-1.1 2.03497,-2 2.30997,-2 0.275,0 -0.31448,0.9 -1.30997,2 -0.99549,1.1 -2.03498,2 -2.30998,2 -0.275,0 0.31449,-0.9 1.30998,-2 z" + id="path3082" + style="fill:#333333;fill-opacity:1" /> + </g> + </g> +</svg> diff --git a/Experior.Activity/make-manifest b/Experior.Activity/make-manifest new file mode 100755 index 0000000..69e5c8a --- /dev/null +++ b/Experior.Activity/make-manifest @@ -0,0 +1,2 @@ +find . -type f | sed 's,^./,,g' > MANIFEST +chmod -R a+x *.py
\ No newline at end of file diff --git a/Experior.Activity/master.cfg b/Experior.Activity/master.cfg new file mode 100644 index 0000000..ff0d4c5 --- /dev/null +++ b/Experior.Activity/master.cfg @@ -0,0 +1,78 @@ +# -*- python -*- +# ex: set syntax=python: + +# This is a sample buildmaster config file. It must be installed as +# 'master.cfg' in your buildmaster's base directory (although the filename +# can be changed with the --basedir option to 'mktap buildbot master'). + +# It has one job: define a dictionary named BuildmasterConfig. This +# dictionary has a variety of keys to control different aspects of the +# buildmaster. They are documented in docs/config.xhtml . + +import os.path +#from buildbot.changes.freshcvs import FreshCVSSource +from buildbot.scheduler import Scheduler, Periodic +from buildbot.process import buildstep, factory +from buildbot.status import html +from buildbot.buildslave import BuildSlave +from buildbot.changes.pb import PBChangeSource +from buildbot.scheduler import Scheduler, Periodic +from buildbot.process import buildstep +from buildbot.steps.shell import ShellCommand +from buildbot.steps.source import SVN +from buildbot.status import html +s = factory.s +c = BuildmasterConfig = {} + +####### BUILDSLAVES +c['slaves'] = [BuildSlave("bot1name", "bot1passwd")] +c['slavePortnum'] = 9989 + +c['change_source'] = PBChangeSource() + +####### SCHEDULING + +periodic = Periodic("every_6_hours", ["sugarbot_trunk"], 6*60*60) +# periodic = Periodic("every_30_minutes", ["sugarbot_trunk"], 30*60) +c['schedulers'] = [periodic] + +####### SVN +source = s(SVN, mode='update', +baseURL='http://sugarbot.googlecode.com/svn/', +defaultBranch='trunk/sugarbot') + +####### NOSE +class NoseTest(ShellCommand): + name = "nose tests" + description = ["running nose tests"] + descriptionDone = [name] + +nose_tests = s(NoseTest, command="/usr/bin/nosetests") + +###### SUGARBOT +class SugarbotTest(ShellCommand): + name = "sugarbot tests" + description = ["running sugarbot tests"] + description = [name] +sugarbot_tests = s(SugarbotTest, command="/usr/bin/env python sugarbotlauncher.py") + +####### BUILDFACTORY +f = factory.BuildFactory([source,nose_tests,sugarbot_tests]) + +c['builders'] = [ +{'name':'sugarbot_trunk', +'slavename':'sugarbot', +'builddir':'test-APP-linux', +'factory':f }, +] + +c['status'] = [] + +####### STATUS +c['status'].append(html.WebStatus(http_port=8010)) + +####### PROJECT INFO +c['projectName'] = "sugarbot" +c['projectURL'] = "http://code.google.com/p/sugarbot/" +c['buildbotURL'] = "http://localhost:8010/" +c['bots']=[("sugarbot","password")] diff --git a/Experior.Activity/modded-main.py b/Experior.Activity/modded-main.py new file mode 100755 index 0000000..e700e85 --- /dev/null +++ b/Experior.Activity/modded-main.py @@ -0,0 +1,157 @@ +# Copyright (C) 2006, Red Hat, Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +import sys +import os +from ConfigParser import ConfigParser +import gettext + +# HACK we need to import numpy before gtk otherwise we traceback in +# some locales. See http://dev.laptop.org/ticket/5559. +import numpy + +import pygtk +pygtk.require('2.0') +import gtk +import gobject + +from sugar import env +from sugar import logger +from sugar.profile import get_profile + +from view.Shell import Shell +from model.shellmodel import ShellModel +from shellservice import ShellService +from hardware import hardwaremanager +from intro import intro +import logsmanager +import config + +def _start_matchbox(): + cmd = ['matchbox-window-manager'] + + cmd.extend(['-use_titlebar', 'no']) + cmd.extend(['-theme', 'sugar']) + cmd.extend(['-kbdconfig', os.path.join(config.data_path, 'kbdconfig')]) + + gobject.spawn_async(cmd, flags=gobject.SPAWN_SEARCH_PATH) + +def _save_session_info(): + # Save our DBus Session Bus address somewhere it can be found + # + # WARNING!!! this is going away at some near future point, do not rely on it + # + session_info_file = os.path.join(env.get_profile_path(), "session.info") + f = open(session_info_file, "w") + + cp = ConfigParser() + cp.add_section('Session') + cp.set('Session', 'dbus_address', os.environ['DBUS_SESSION_BUS_ADDRESS']) + cp.set('Session', 'display', gtk.gdk.display_get_default().get_name()) + cp.write(f) + + f.close() + +def _setup_translations(): + locale_path = os.path.join(config.prefix, 'share', 'locale') + domain = 'sugar' + + gettext.bindtextdomain(domain, locale_path) + gettext.textdomain(domain) + +def check_cm(bus_name): + try: + import dbus + bus = dbus.SessionBus() + bus_object = bus.get_object('org.freedesktop.DBus', '/org/freedesktop/DBus') + name = bus_object.GetNameOwner(bus_name, dbus_interface='org.freedesktop.DBus') + if name: + return True + except dbus.DBusException: + pass + return False + +def _shell_started_cb(): + # Unfreeze the display + hw_manager = hardwaremanager.get_manager() + hw_manager.set_dcon_freeze(0) + +def main(): + gobject.idle_add(_shell_started_cb) + + logsmanager.setup() + logger.start('shell') + + _save_session_info() + _start_matchbox() + _setup_translations() + + hw_manager = hardwaremanager.get_manager() + hw_manager.startup() + + icons_path = os.path.join(config.data_path, 'icons') + gtk.icon_theme_get_default().append_search_path(icons_path) + + # Do initial setup if needed + if not get_profile().is_valid(): + win = intro.IntroWindow() + win.show_all() + gtk.main() + + if os.environ.has_key("SUGAR_TP_DEBUG"): + # Allow the user time to start up telepathy connection managers + # using the Sugar DBus bus address + import time + from telepathy.client import ManagerRegistry + + registry = ManagerRegistry() + registry.LoadManagers() + + debug_flags = os.environ["SUGAR_TP_DEBUG"].split(',') + for cm_name in debug_flags: + if cm_name not in ["gabble", "salut"]: + continue + + try: + cm = registry.services[cm_name] + except KeyError: + print RuntimeError("%s connection manager not found!" % cm_name) + + while not check_cm(cm['busname']): + print "Waiting for %s on: DBUS_SESSION_BUS_ADDRESS=%s" % \ + (cm_name, os.environ["DBUS_SESSION_BUS_ADDRESS"]) + try: + time.sleep(5) + except KeyboardInterrupt: + print "Got Ctrl+C, continuing..." + break + + model = ShellModel() + shell = Shell(model) + service = ShellService(shell) + + if os.environ.has_key("SUGARBOT_EMULATOR"): + sys.path.append(os.environ['SUGARBOT_PATH']) + from sugarbotlauncher import SugarbotLauncher + sbLauncher = SugarbotLauncher(shell, model) + + try: + gtk.main() + except KeyboardInterrupt: + print 'Ctrl+C pressed, exiting...' + + session_info_file = os.path.join(env.get_profile_path(), "session.info") + os.remove(session_info_file)
\ No newline at end of file diff --git a/Experior.Activity/rm b/Experior.Activity/rm new file mode 100644 index 0000000..1d5c9ee --- /dev/null +++ b/Experior.Activity/rm @@ -0,0 +1,2 @@ +./.svn +./activity/.svn diff --git a/Experior.Activity/sbRpcServer.log b/Experior.Activity/sbRpcServer.log new file mode 100644 index 0000000..e71d6c7 --- /dev/null +++ b/Experior.Activity/sbRpcServer.log @@ -0,0 +1,46 @@ +08-07 18:48 sbRpcServer INFO Listening on port 54321 +08-07 18:48 sbRpcServer INFO Kill: True Restart: True +08-07 18:48 sbRpcServer INFO Added script script_calculate.py [Activity Calculate] +08-07 18:48 sbRpcServer INFO Added script script_terminal.py [Activity Terminal] +08-07 18:55 sbRpcServer INFO Listening on port 54321 +08-07 18:55 sbRpcServer INFO Kill: True Restart: True +08-07 18:55 sbRpcServer INFO Added script script_calculate.py [Activity Calculate] +08-07 18:55 sbRpcServer INFO Added script script_calculate.pyc [Activity Calculate] +08-07 18:55 sbRpcServer INFO Added script script_terminal.py [Activity Terminal] +08-07 18:55 sbRpcServer INFO Added script script_terminal.pyc [Activity Terminal] +08-08 11:29 sbRpcServer INFO Listening on port 54321 +08-08 11:29 sbRpcServer INFO Kill: True Restart: True +08-08 11:29 sbRpcServer INFO Added script script_calculate.py [Activity Calculate] +08-08 11:29 0 INFO Starting script_calculate.py +08-08 11:38 sbRpcServer INFO Listening on port 54321 +08-08 11:38 sbRpcServer INFO Kill: True Restart: True +08-08 11:38 sbRpcServer INFO Added script script_calculate.py [Activity Calculate] +08-08 11:38 0 INFO Starting script_calculate.py +08-08 11:39 0 INFO Starting script_calculate.py +08-08 11:40 0 INFO Starting script_calculate.py +08-08 11:40 0 INFO Starting script_calculate.py +08-08 11:42 0 INFO Starting script_calculate.py +08-08 11:43 0 INFO Getting Script: script_calculate.py +08-08 11:43 0 ERROR Execution failed: +Traceback (most recent call last): + File "/home/tim/Activities/sugarbot.activity/sbexecutionengine.py", line 86, in executePy + sugarbot_main(self.widgets) + File "Sugarbot Script: 'script_calculate.py'", line 8, in sugarbot_main + File "/home/tim/Activities/sugarbot.activity/sbpython.py", line 548, in __getitem__ + raise WidgetDoesNotExist, which +WidgetDoesNotExist: 'The widget Share with: could not be identified.' + +08-14 11:23 sbRpcServer INFO Listening on port 54321 +08-14 11:23 sbRpcServer INFO Kill: True Restart: True +08-14 11:23 sbRpcServer INFO Added script script_calculate.py [Activity Calculate] +08-14 11:24 0 INFO Starting script_calculate.py +08-14 11:24 0 INFO Getting Script: script_calculate.py +08-14 11:24 0 ERROR Execution failed: +Traceback (most recent call last): + File "/home/tim/Activities/sugarbot.activity/sbexecutionengine.py", line 86, in executePy + sugarbot_main(self.widgets) + File "Sugarbot Script: 'script_calculate.py'", line 8, in sugarbot_main + File "/home/tim/Activities/sugarbot.activity/sbpython.py", line 548, in __getitem__ + raise WidgetDoesNotExist, which +WidgetDoesNotExist: 'The widget Share with: could not be identified.' + diff --git a/Experior.Activity/sbconfig.py b/Experior.Activity/sbconfig.py new file mode 100755 index 0000000..dfd84b4 --- /dev/null +++ b/Experior.Activity/sbconfig.py @@ -0,0 +1,13 @@ +#!/usr/bin/env python +# encoding: utf-8 + +# The host that the sugarbot clients should connect to in order to retrieve +# the commands that they need to execute. +host = "localhost" + +# The port to connect over. This is the same port that the XML-RPC server +# will be listening on. +port = 54321 + +# Client name. +clientName = "testClientName"
\ No newline at end of file diff --git a/Experior.Activity/sbconfig.pyc b/Experior.Activity/sbconfig.pyc Binary files differnew file mode 100644 index 0000000..910601e --- /dev/null +++ b/Experior.Activity/sbconfig.pyc diff --git a/Experior.Activity/sbconfig_sample.py b/Experior.Activity/sbconfig_sample.py new file mode 100755 index 0000000..dfd84b4 --- /dev/null +++ b/Experior.Activity/sbconfig_sample.py @@ -0,0 +1,13 @@ +#!/usr/bin/env python +# encoding: utf-8 + +# The host that the sugarbot clients should connect to in order to retrieve +# the commands that they need to execute. +host = "localhost" + +# The port to connect over. This is the same port that the XML-RPC server +# will be listening on. +port = 54321 + +# Client name. +clientName = "testClientName"
\ No newline at end of file diff --git a/Experior.Activity/sbconfig_sample.pyc b/Experior.Activity/sbconfig_sample.pyc Binary files differnew file mode 100644 index 0000000..d88c2a6 --- /dev/null +++ b/Experior.Activity/sbconfig_sample.pyc diff --git a/Experior.Activity/sbdecorators.py b/Experior.Activity/sbdecorators.py new file mode 100755 index 0000000..fcc85c9 --- /dev/null +++ b/Experior.Activity/sbdecorators.py @@ -0,0 +1,148 @@ +#!/usr/bin/env python +# encoding: utf-8 +""" +sbpython.py + +This file is part of sugarbot. + +sugarbot is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +sugarbot is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with sugarbot. If not, see <http://www.gnu.org/licenses/>. +""" + +""" +One of three degrees of enforcement may be specified by passing +the 'debug' keyword argument to the decorator: + 0 -- NONE: No type-checking. Decorators disabled. + 1 -- MEDIUM: Print warning message to stderr. (Default) + 2 -- STRONG: Raise TypeError with message. +If 'debug' is not passed to the decorator, the default level is used. + +Example usage: + >>> NONE, MEDIUM, STRONG = 0, 1, 2 + >>> + >>> @accepts(int, int, int) + ... @returns(float) + ... def average(x, y, z): + ... return (x + y + z) / 2 + ... + >>> average(5.5, 10, 15.0) + TypeWarning: 'average' method accepts (int, int, int), but was given + (float, int, float) + 15.25 + >>> average(5, 10, 15) + TypeWarning: 'average' method returns (float), but result is (int) + 15 + +Needed to cast params as floats in function def (or simply divide by 2.0). + + >>> TYPE_CHECK = STRONG + >>> @accepts(int, debug=TYPE_CHECK) + ... @returns(int, debug=TYPE_CHECK) + ... def fib(n): + ... if n in (0, 1): return n + ... return fib(n-1) + fib(n-2) + ... + >>> fib(5.3) + Traceback (most recent call last): + ... + TypeError: 'fib' method accepts (int), but was given (float) + +""" +import sys + +def accepts(*types, **kw): + """ Function decorator. Checks that inputs given to decorated function + are of the expected type. + + Parameters: + types -- The expected types of the inputs to the decorated function. + Must specify type for each parameter. + kw -- Optional specification of 'debug' level (this is the only valid + keyword argument, no other should be given). + debug = ( 0 | 1 | 2 ) + + """ + if not kw: + # default level: MEDIUM + debug = 1 + else: + debug = kw['debug'] + try: + def decorator(f): + def newf(*args): + if debug == 0: + return f(*args) + assert len(args) == len(types) + argtypes = tuple(map(type, args)) + if argtypes != types: + msg = info(f.__name__, types, argtypes, 0) + if debug == 1: + print >> sys.stderr, 'TypeWarning: ', msg + elif debug == 2: + raise TypeError, msg + return f(*args) + newf.__name__ = f.__name__ + return newf + return decorator + except KeyError, key: + raise KeyError, key + "is not a valid keyword argument" + except TypeError, msg: + raise TypeError, msg + +def returns(ret_type, **kw): + """ Function decorator. Checks that return value of decorated function + is of the expected type. + + Parameters: + ret_type -- The expected type of the decorated function's return value. + Must specify type for each parameter. + kw -- Optional specification of 'debug' level (this is the only valid + keyword argument, no other should be given). + debug=(0 | 1 | 2) + + """ + try: + if not kw: + # default level: MEDIUM + debug = 1 + else: + debug = kw['debug'] + def decorator(f): + def newf(*args): + result = f(*args) + if debug == 0: + return result + res_type = type(result) + if res_type != ret_type: + msg = info(f.__name__, (ret_type,), (res_type,), 1) + if debug == 1: + print >> sys.stderr, 'TypeWarning: ', msg + elif debug == 2: + raise TypeError, msg + return result + newf.__name__ = f.__name__ + return newf + return decorator + except KeyError, key: + raise KeyError, key + "is not a valid keyword argument" + except TypeError, msg: + raise TypeError, msg + +def info(fname, expected, actual, flag): + """ Convenience function returns nicely formatted error/warning msg. """ + format = lambda types: ', '.join([str(t).split("'")[1] for t in types]) + expected, actual = format(expected), format(actual) + msg = "'%s' method " % fname \ + + ("accepts", "returns")[flag] + " (%s), but " % expected\ + + ("was given", "result is")[flag] + " (%s)" % actual + return msg diff --git a/Experior.Activity/sbdecorators.pyc b/Experior.Activity/sbdecorators.pyc Binary files differnew file mode 100644 index 0000000..2edb9c5 --- /dev/null +++ b/Experior.Activity/sbdecorators.pyc diff --git a/Experior.Activity/sbexecutionengine.py b/Experior.Activity/sbexecutionengine.py new file mode 100755 index 0000000..bb67fe0 --- /dev/null +++ b/Experior.Activity/sbexecutionengine.py @@ -0,0 +1,112 @@ +#!/usr/bin/env python +# encoding: utf-8 +""" +sbexecutionengine.py + +This file is part of sugarbot. + +sugarbot is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +sugarbot is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with sugarbot. If not, see <http://www.gnu.org/licenses/>. +""" +import gobject +import logging +import os +import pygtk +import sys +import threading +import time +import traceback + +pygtk.require('2.0') +import gtk + +from sbpython import * + +class sbExecutionEngine: + """ + Responsible for communications with the XML-RPC server regarding command + retrieval, as well as parsing and execution of those commands. + """ + def __init__(self, sbGUI, xmlRpcServer): + self.sbgui = sbGUI + self.rpc = xmlRpcServer + self.widgets = sbwidgets + + self.delayedExecution = False + self.executionComplete = False + self.lastCommand = None + + self.log = logging.getLogger("ExecutionEngine") + + try: + self.id = os.environ['SUGARBOT_ID'] + except KeyError: + self.log.error("Sugarbot ID is not set. Using default 0. ") + self.id = 0 + + def killSugarbot(self): + """ + Kills the sugarbot activity. + """ + self.log.info("Attempting to kill sugarbot") + sys.exit(0) + + def executePy(self): + """ + Executes the Sugarbot pâ—Šython script. + + Executes the Sugarbot python script provided by the XML-RPC server. + Regards any unhandled exceptions as fatal errors, and reports them + to the server. Also handles the termination of Sugarbot if the + auto-restart flag has been set by the XML-RPC server. + """ + self.script = self.rpc.getScript(self.id) + self.log.info("Executing Script:\n%s" % self.script) + + self.widgets.gui = self.sbgui + + # Execute the actual python script. + try: + exec self.script + + # t = threading.Thread(args=(self.widgets,)) + # t.run = sugarbot_main + # t.start() + + sugarbot_main(self.widgets) + self.rpc.success(self.id) + + # Something bad happened. Report it, and log it. + except: + text = "Execution failed:\n%s" % traceback.format_exc() + text = self.rpc.fail(text, self.id) + self.log.error( text ) + + # Regarless of the success status, check to see if we need to restart + # sugarbot or not. + finally: + restart = self.rpc.getRestartFlag() + self.log.info("Got restart flag: " + str(restart)) + + if restart: + self.killSugarbot() + + + def isComplete(self): + """ + Returns true if execution is complete. + + Returns true if execution is complete, i.e. there are no more + commands that can be executed. + """ + return self.executionComplete
\ No newline at end of file diff --git a/Experior.Activity/sbexecutionengine.pyc b/Experior.Activity/sbexecutionengine.pyc Binary files differnew file mode 100644 index 0000000..3fc714f --- /dev/null +++ b/Experior.Activity/sbexecutionengine.pyc diff --git a/Experior.Activity/sbgui.py b/Experior.Activity/sbgui.py new file mode 100755 index 0000000..3593f38 --- /dev/null +++ b/Experior.Activity/sbgui.py @@ -0,0 +1,321 @@ +#!/usr/bin/env python +# encoding: utf-8 +""" +sbgui.py + +This file is part of sugarbot. + +sugarbot is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +sugarbot is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with sugarbot. If not, see <http://www.gnu.org/licenses/>. +""" +import gobject +import os +import pygtk +import sys +import time +pygtk.require('2.0') +import gtk +import logging +import sugar + +from gobject import idle_add +from gtk import gdk +from gtk.gdk import event_handler_set +from sbexecutionengine import sbExecutionEngine +from sugar import graphics +from sugar.graphics.toolbutton import Palette +from widgetIdentifier import * + +class sbGUI(gobject.GObject): + """ + Responsible for tracking all identifiable* widgets instantiated by GTK. + + *Identifiable widgets are widgets for which a valid identifier can be + found, via any of the identifiers enumerated by widgetIdentifier.identifiers. + """ + def __init__(self,sugarbotInstance,rpcServer): + """ + Initialize internal variables, and register self as an event handler. + """ + # Necessary, due to inheritance + gobject.GObject.__init__(self) + + # Get the RPC server connection and the sugarbot instance, as well + # as the execution engine. + self.sugarbot = sugarbotInstance + self.rpc = rpcServer + self.engine = sbExecutionEngine(self,self.rpc) + self.log = logging.getLogger('sbGUI') + + # Keep track of the amount of idle time... + self.idletime = 0 + self.idletimeout= 6 + + # In order to keep track of all of the windows, we set up a + # dict of Windows. For each window, the key is its id (as provided + # by id(theWindowObject). The value of each dictionary entry is a + # dictionary of Widgets, set up the same way -- the id() of the + # widget is the key, and the + self.trackedWidgets = {} # Maps id(object) -> widget + self.names = {} # Maps identifier -> widget + + # Register one of our methods to intercept all GTK events + self.registerEventHandler() + + + def eventHandler(self,event=None): + """ Intercepts all GDK events. We then send them off to a separate + handler method (to keep things clean), and then have GTK execute + whatever the event is supposed to do. + """ + if event is not None: + gtk.main_do_event(event) + self.handleEvent(event) + + if event.type is not gtk.gdk.EXPOSE: + self.idletime = 0 + + return True + + + def getWidgetIdentifier(self,widget): + """ + Returns the Widget's unique identifier (for example, a button's + label, or a gkt.Entry's name), or None if it does not have one. + Also, this function filters out many uninitialized identifiers, + such as "GtkToolbar" or "GtkButton" + @param widget - The widget whose identifier is to be retrieved. + """ + if not isinstance(widget,gtk.Widget): + raise "_getWidgetName must take a gtk.Widget object as its argument" + + # ---- GENERIC APPROACH ---- + # Assuming that the widget was named explicitly by the developers, + # getting the name should be very straightforward, with no specialized + # cases or special name-detecting. + widId = widgetIdentifier(widget) + ident = widId.getIdentifier() + + if ident is not None: + return ident + + # ---- SPECIALIZED APPROACH ---- + # Check to see if we have an identifier for the specific type + # of widget before we iterate through all of the different identifiers + # hoping to get a hit. + if widgetIdentifier.identifiers.has_key(widget.__class__): + widId = widgetIdentifier.identifiers[widget.__class__](widget) + ident = widId.getIdentifier() + + if ident is not None: + return ident + + # ---- BRUTE FORCE ---- + # The widget was not named explicitly by the developers, and we do + # not have a case for the specific widget class. + # Iterate through all of our potential identifiers, since we + # very likely have a widget class that it inherits from. + # This method is undesirable, since the identifiers in the dictionary + # widgetIdentifiers may be in a different order each time the + # program is run, due to the non-ordered nature of dictionaries. + for identifier in widgetIdentifier.identifiers: + if isinstance(widget, identifier): + widId = widgetIdentifier.identifiers[identifier](widget) + ident = widId.getIdentifier() + + if ident is not None: + break + + # At this point, we either have a valid widget identifier, or None. + return ident + + + def addWidget(self,widget): + """ + Add a widget to be tracked internally. Widgets are tracked by their + id(). Additionally, add it to the names{} dictionary, so that we + can quickly look up widgets by the name. + """ + + # Don't do anything if we already have this widget by ID + if self.trackedWidgets.has_key(id(widget)): + return + + # Make sure we are working on a widget + if not isinstance(widget, gtk.Widget): + return + + # Containers might have children. Check them all. + if isinstance(widget, gtk.Container): # gtk.Container can have + for child in widget.get_children(): # any number of children + self.addWidget(child) + + if isinstance(widget, gtk.Bin): # gtk.Bin can only have + self.addWidget(widget.get_child()) # one child. + + if isinstance(widget, gtk.Notebook): # gtk.Notebook can have + numPages = widget.get_n_pages() # many children. + for count in range(0, numPages): + page = widget.get_nth_page(count) + self.addWidget(page) + return + + + # Get the widget's identifier & id. If the widget cannot be reliably + # identified, there is no use in tracking it. + identifier = self.getWidgetIdentifier(widget) + _id = id(widget) + if identifier is None: + return + + # Simply keep track of the widgets by ID + self.trackedWidgets[_id]=widget + + # Keep track of the widgets by identifier. + # Check to see if it's already being tracked. + if self.names.has_key(identifier): + raise KeyError, "Already tracking a widget by identifier %s" \ + % identifier + + # Track the little bugger + else: + self.log.debug("Tracking widget id %i by identifier %s" % (_id, identifier)) + self.names[identifier] = widget + + + def delWidget(self,widget): + """ + Remove a widget from internal tracking. Widgets can be removed by + their id() or by the gtk.Widget object. + + TODO: I don't think this function ever gets called. + """ + if not isinstance(widget, gtk.Widget): + raise "Called delWidget on non-Widget object" + return + + # Get id(widget) + identifier = self.getWidgetIdentifier(widget) + _id = id(widget) + + # Remove the widget from the trackedWidgets dict if it is tracked. + if self.trackedWidgets.has_key(_id): + del self.trackedWidgets[_id] + del self.names[identifier] + + else: # Tried to call delWidget on non-tracked Widget + return + + def getWidgetByName(self,widgetName): + """ + Checks to see if we are tracking a Widget with a given name. + If so, return the Widget. If not, return None. + """ + if widgetName in self.names: + return self.names[widgetName] + return None + + def handleEvent_firehose(self,event): + """ + For exploratory testing. Don't bother trying to figure out what this + does, it will change without notification. + Pretty much just outputs a firehose of events. Useful for exploratory + testing, and that's pretty much it. + """ + if not hasattr(self,'classes'): + self.classes = [] + + eventType = event.type + print "----------------------------------------" + print event.type + if event.window: + # print event.window + try: + widget = event.window.get_user_data() + print widget.__class__ + self.classes[widget.__class__] = 1 + except: + raise + print "----------------------------------------" + print "" + print "" + + def handleEvent(self,event): + """ + Handles all GDK events. We first filter them so that we know + they pertain to a window, and then we further drill down based on the + type of action. This method is used to achieve two goals: + [1] Build a database of all windows and widgets + [2] Allow us to see actions as they happen. This could lead to + recording functionality in the future. + """ + try: + # Does the event have a Window that it belongs to? + if (not event.window): + return + + # Get some information on the widget. If it doesn't work, just + # gracefully fail. Exceptions happen with the following events: + # (maybe more, but these are what I've observed): + # - GDK_OWNER_CHANGE + widget = event.window.get_user_data() + eventType = event.type + + # -------- HANDLE WIDGET INSTANTIATION -------- + # MAP events are generated when a widget is initially displayed + # on the screen. In most cases, any naming or configuration that + # is going to be performed *has been* performed. + if eventType == gdk.MAP: + self.addWidget(widget) + + # -------- HANDLE WIDGET DESTRUCTION -------- + # UNMAP events are generated when a widget + # is being taken off the screen. Generally, this will happen at + # the end of an application's execution. However, to maintain + # flexibility (e.g. the possibility of dialog windows), handle + # the UNMAP event here. + elif eventType == gdk.UNMAP: + self.delWidget(widget) + except ValueError: + pass + except: # Oops! + raise + + def idleHandler(self, event=None): + + if self.engine.isComplete(): + return False + + if self.idletime is 0: + self.idletime = time.time() + self.lasttime = self.idletime + + if self.lasttime + 1 < time.time(): + self.lasttime += 1 + + if self.idletime + self.idletimeout < time.time(): + self.engine.executePy() + self.idletime = 0 + + return True + + def registerEventHandler(self): + """ + Registers the method self.eventHandler as the function that + will receive all GDK events. This allows us to snoop on GDK. + """ + if not event_handler_set: + raise NotImplementedError + else: + gobject.idle_add(self.idleHandler) + event_handler_set(self.eventHandler) diff --git a/Experior.Activity/sbgui.pyc b/Experior.Activity/sbgui.pyc Binary files differnew file mode 100644 index 0000000..85c5d38 --- /dev/null +++ b/Experior.Activity/sbgui.pyc diff --git a/Experior.Activity/sblog.py b/Experior.Activity/sblog.py new file mode 100755 index 0000000..928c96c --- /dev/null +++ b/Experior.Activity/sblog.py @@ -0,0 +1,24 @@ +#!/usr/bin/env python +# encoding: utf-8 +""" +sblog.py + +This file is part of sugarbot. + +sugarbot is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +sugarbot is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with sugarbot. If not, see <http://www.gnu.org/licenses/>. +""" +import logging + +log = logging.getLogger("") +log.setLevel(logging.DEBUG) diff --git a/Experior.Activity/sblog.pyc b/Experior.Activity/sblog.pyc Binary files differnew file mode 100644 index 0000000..621892c --- /dev/null +++ b/Experior.Activity/sblog.pyc diff --git a/Experior.Activity/sbpython.py b/Experior.Activity/sbpython.py new file mode 100755 index 0000000..b80554f --- /dev/null +++ b/Experior.Activity/sbpython.py @@ -0,0 +1,561 @@ +#!/usr/bin/env python +# encoding: utf-8 +""" +sbpython.py + +This file is part of sugarbot. + +sugarbot is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +sugarbot is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with sugarbot. If not, see <http://www.gnu.org/licenses/>. +""" + +import sys +import time +import gtk +import logging +from gtk import gdk +import gobject +from sbdecorators import * + +import sugar +from sugar import graphics +from sugar.graphics.toolcombobox import ToolComboBox + +class NotSupportedError(NotImplementedError): + def __init__(self, widget=None, command=None): + self.widget = widget + self.command = command + def __str__(self): + return repr('The widget %s [%s] does not support the command(s) %s' \ + % (self.widget, self.widget.__class__, self.command)) + +class WidgetDoesNotExist(ValueError): + def __init__(self, widget=None): + self.widget = widget + def __str__(self): + return repr('The widget %s could not be identified.' % self.widget) + + +class wrappedWidget(object): + raiseExceptions = True + + def __init__(self, widget, name): + self.widget = widget + self.name = name + self.log = logging.getLogger('w(%s)' % name) + + def __getitem__(self, index): + """ + Returns the indexed item. + + For ComboBox and similar items, this will effectively be the n'th object. + Uses a helper function for each class type. + """ + indexMethods = \ + { + gtk.ComboBox: self.getList_GtkComboBox, + ToolComboBox: self.getList_SugarGraphicsCombo, + # gtk.Container: self.getList_Container + } + + for classType in indexMethods: + if isinstance(self.widget, classType): + return indexMethods[classType]()[index] + + def notSupportedError(self, args): + """ + Wrapper for raising NotSupportedError exceptions. + """ + if self.raiseExceptions: + raise NotSupportedError, (self.widget, args) + return None + + def supportsSignal(self, signalName): + """ + Checks to see if a GTK object supports a signal. + + Checks to see if a GTK object supports a signal, via the + gobject.signal_lookup method. Returns True if it is supported, + false otherwise. + """ + return gobject.signal_lookup(signalName, self.widget.__class__) != 0 + + def click(self): + """ + Simulates a user click. + + Simulates a user click by: + 1) Emitting a 'clicked' signal + 2) Calling the 'activate' method + """ + self.log.info("Clicking %s" % self.name) + + widgetClass = self.widget.__class__ + + clickedSignal = 'clicked' + activateMethod = 'activate' + + # Attempt to emit the 'clicked' signal. + if self.supportsSignal(clickedSignal): + self.widget.emit(clickedSignal) + return True + + # Attempt to simply 'activate' the widget. + elif hasattr(self.widget, activateMethod): + getattr(self.widget, activateMethod)() + return True + + # Fail gracefully + return self.notSupportedError((clickedSignal, activateMethod)) + + + def getText(self): + """ + Returns the text in the widget. + + Returns whatever text is being stored by the widget. This function + gives priority to Widget functions in the following order: + 1) get_text method + 2) label (same as getLabel) + 3) title (same as getTitle) + """ + self.log.info("Getting text for %s" % self.name) + + getText = "get_text" + + # Get the text from the widget + # try: return self.__simpleGetter(getText) + # except NotSupportedError: pass + if hasattr(self.widget, getText): + return getattr(self.widget, getText)() + + # Special handling for sugar ToolComboBox + if isinstance(self.widget, ToolComboBox): + logging.fatal("YES!!!") + return self.widget.combo.get_active_item()[0] + + # Try the label + try: return self.label + except NotSupportedError: pass + + # Try the title + try: return self.title + except NotSupportedError: pass + + # Fail + return self.notSupportedError((getText,"label","title")) + + def typeText(self, val): + """ + Simulates user text entry. + + Simulates a user typing into a widget. Inserts the text at the + current insertion location, via: + 1) Emits the 'insert-at-cursor' signal. + """ + self.log.info("Adding text for %s to %s" % (self.name, val)) + + terminalMethod = "feed_child" + insertAtCursor = 'insert-at-cursor' + + if self.supportsSignal(insertAtCursor): + self.widget.emit(insertAtCursor, val) + return True + + # --- TERMINAL.ACTIVITY HACK --- + # vte.Terminal-specific typing + if hasattr(self.widget, terminalMethod): + self.widget.feed_child(val) + return True + + return self.notSupportedError(insertAtCursor, terminalMethod) + + def setText(self, val): + """ + Sets the text for the widget. + + Sets the text for the widget to the user-provided string. + Uses the following procedures to attempt to set the text: + 1) Calls 'set_text' method + 2) Set the 'label' property + 3) Set the 'title' property + 4) A Terminal-specific method, 'feed_child' + 5) Set the text via 'typeText' + """ + self.log.info("Setting text for %s to %s" % (self.name, val)) + + setText = "set_text" + + # --- GENERAL APPROACH --- + # Try simply setting the text... + try: + self.__simpleSetter(setText, val) + return True + except NotSupportedError: pass + # if hasattr(self.widget, setText): + # getattr(self.widget, setText)(val) + # return True + + # Try setting the label + try: + self.label = val + return True + except NotSupportedError: pass + + # Try setting the title + try: + self.title = val + return True + except NotSupportedError: pass + + # --- TEXT APPENDING --- + # Try to just insert the text at the given point... + try: + self.typeText(val) + return True + except NotSupportedError: + pass + + # Fail + return self.notSupportedError((setText,"label","title")) + + def delete(self, numberOfTimes, deleteType): + """ + Simulates user pressing the 'delete' key. + + Simulates user pressing the 'delete' key a given number of times, + with a flexible deletion type (e.g. characters, words, lines) + """ + # Note that we must negate numberOfTimes here, because it is + # re-negated inside of the 'backspace' call. + return backspace(-numberOfTimes, deleteType) + + def backspace(self, numberOfTimes=1, deleteType=gtk.DELETE_CHARS): + """ + Simulates the backspace keypress. + + Simulates the backspace keypress. numberOfTimes times. + If numberOfTimes is negative, simulate the 'delete' keypress. + """ + backspace = 'backspace' + deleteFromCursor = 'delete-from-cursor' + + # Try the 'deleteFromCursor' approach, as it is very flexible in the + # number of ways to delete content. + if self.supportsSignal(deleteFromCursor): + self.widget.emit(deleteFromCursor, deleteType, -numberOfTimes) + + # Try the standard 'backspace' command. Note that this will not work + # for negative quantities. + elif self.supportsSignal(backspace) and numberOfTimes >= 0: + for i in range(0, numberOfTimes): + self.widget.emit(backspace) + return True + + else: + return self.notSupportedError((backspace, deleteFromCursor)) + + def doFocus(self, setFocus=None): + """ + Either sets or gets the focus for the widget. + """ + if setFocus is None: + return self.widget.flags() & gtk.HAS_FOCUS + elif setFocus: + self.widget.grab_focus() + return True + + def getInfo(self): + """ + Returns a bunch of information about the widget. + """ + def getClasses(object): + """ + Returns a list containing the class that the object is an instance of, + and all of the classes that the instance inherits from. + For example: + >>> class A: pass + >>> class B(A): pass + >>> class C(B): pass + >>> x = C() + >>> list = getClasses(x) + >>> list + [<class __main__.C at 0x6a1e0>, <class __main__.B at 0x6a1b0>, + <class __main__.A at 0x6a180>] + >>> for i in list: + ... print "Is instance of " + str(i) + "? " + str(isinstance(x,i)) + ... + Is instance of __main__.C? True + Is instance of __main__.B? True + Is instance of __main__.A? True + """ + # If the passed object is an instance of a class, it will have a + # __class__ attribute. + if hasattr(object,"__class__"): + recursionData = [object.__class__] + + # Include the lowest-level class in the heirarchy tree + # Iterate through all of the base classes + for _class in object.__class__.__bases__: + if not _class is object: + recursionData.append(_class) + recursionData.extend(getClasses(_class)) + # Otherwise, the object -is- a class, and it will have a __bases__ + # attribute. + elif hasattr(object,"__bases__"): + # Iterate through all of the base classes. Note that we do NOT add + # the class itself here, as that is only done above, on the first + # call. + for _class in object.__bases__: + recursionData.append(_class) + recursionData.extend(getClasses(_class)) + return recursionData + + # ------ BEGIN INFO() METHOD ------- + infoStr = "\n%s" % getClasses(self.widget) + + l = dir(self.widget) + for i in l: + infoStr+= "\n> %s" % i + + return infoStr + + def __simpleGetter(self, method): + """ + Wrapper for simple 'get' methods + + Wrapper for simple 'get' methods (like get_label), that fails gracefully + if the method is not available for the widget object. + """ + self.log.info("Getting %s for %s" % (method, self.name)) + if hasattr(self.widget, method): + return getattr(self.widget, method) + + return self.notSupportedError(method) + + def __simpleSetter(self, method, val): + """ + Wrapper for simple 'set' methods + + Wrapper for simple 'set' methods (like get_label), that fails + gracefully if the method is not available for the widget object. + """ + self.log.info("Setting %s to %s for %s" % (method, val, self.name)) + if hasattr(self.widget, method): + getattr(self.widget, method)(val) + return True + + return self.notSupportedError(method) + + def getTitle(self): + """ + Returns the widget's title. + """ + return self.__simpleGetter('get_title') + + def setTitle(self, val): + """ + Sets the widget's title + """ + return self.__simpleSetter('set_title', val) + + def getLabel(self): + """ + Returns the widget's label. + """ + return self.__simpleGetter('get_label') + + def setLabel(self, val): + """ + Sets the widget's label + """ + return self.__simpleSetter('set_label', val) + + def getListFormat(self, item): + """ + Converts a ComboBox or ToolComboBox's entries into a list + + Converts a ComboBox or ToolComboBox's entries into a list. This is + useful in enumerating the entries in a ComboBox for selection. + """ + retVal = [] + + if isinstance(item, ToolComboBox): + retVal = self.getList_GtkComboBox(item) + elif isinstance(item, gtk.ComboBox): + retVal = self.getList_SugarGraphicsCombo() + + return retVal + + def getList_GtkComboBox(self, combo): + """ + getListFormat handler for gtk.ComboBo objects. + """ + index = combo.get_active() + if index == -1: + index = 0 + + model = combo.get_model() + row = model.iter_nth_child(None, index) + + if not row: + return None + + listOfValues = [] + + for i in range(0, len(model)): + try: + item = model[i] + listOfValues.append(item) + except IndexError: + break + return listOfValues + + def getList_SugarGraphicsCombo(self): + """ + getListFormat handler for sugar.graphics.ComboBox objects. + """ + tempList = self.getList_GtkComboBox(self.widget.combo) + + returnList = [] + for entry in tempList: + returnList.append(entry[1]) + + return returnList + + def getSelected_SugarGraphicsCombo(self): + """ + getSelected handler for sugar.graphics.combo* objects. + """ + active = self.getSelected_ComboBox(self.widget.combo) + + if len(active) > 1: + return active[1] + elif len(active) == 1: + return active[0] + return active + + def getSelected_ComboBox(self, combo=None): + """ + getSelected handler for gtk.ComboBox objects. + """ + if combo is None: + combo = self.widget + + index = combo.get_active() + if index == -1: + index = 0 + + row = combo.get_model().iter_nth_child(None, index) + if not row: + return None + return combo.get_model()[row] + + def getSelected(self): + """ + Returns the item selected in a gtk.ComboBox or sugar.graphics.ToolComboBox + """ + self.log.info("Getting selected entry") + # Handle for the ComboBox... + if isinstance(self.widget, gtk.ComboBox): + return self.getSelected_ComboBox(self.widget) + + # Special handling for sugar ToolComboBox + if isinstance(self.widget, ToolComboBox): + return self.getSelected_SugarGraphicsCombo() + + try: return self.__simpleGetter('get_active_text') + except NotSupportedError: pass + + try: + i = self.widget.get_active() + mod = self.widget.get_model() + return mod[i] + except: + pass + + return self.notSupportedError('get_active_text', 'get_model') + + def setSelected(self, val): + """ + Sets the item selected in a gtk.ComboBox or sugar.graphics.ToolComboBox + """ + self.log.info("Setting selected entry") + # If textual: Enumerate all of the selections + # If numeric: set selected + combo = self.widget + + # Special handling for ToolComboBox + if isinstance(self.widget, ToolComboBox): + combo = self.widget.combo + + if isinstance(combo, gtk.ComboBox): + # If we were provided with a string, get the numerical offset of + # the provided entry. + if isinstance(val, str): + l = self.getListFormat(combo) + if val in l: + val = l.index(val) + + # Use the index to set the active combobox item + if isinstance(val, int): + combo.set_active(val) + return + + return self.notSupportedError('set_active') + + + text = property(getText, setText) + title = property(getTitle, setTitle) + label = property(getLabel, setLabel) + + focus = property(doFocus, doFocus) + selected = property(getSelected, setSelected) + info = property(getInfo) + + +class widgetRegistry(): + gui = None + + def __init__(self): + self.widgets = {} + self.log = logging.getLogger('wrappedWidget') + self.log.info("Instantiated widgetRegistry") + + def __getitem__(self, which): + """ + Allows us to select widgets like a dictionary. + """ + + # If we do not already have the Widget wrapped in a wrappedWidget, + # do so now. + if which not in self.widgets: + widget = self.gui.getWidgetByName(which) + + self.log.info("Fetched widget %s [%s]" % (which, which.__class__)) + + if widget is None: + raise WidgetDoesNotExist, which + + wrapped = wrappedWidget(widget, which) + self.setWidget(which, wrapped) + + # Return the wrappedWidget object. + return self.widgets[which] + + def setWidget(self, which, value): + self.widgets[which]=value + +# Create the 'sbwidgets' instance. +if not globals().has_key("sbwidgets"): + sbwidgets = widgetRegistry()
\ No newline at end of file diff --git a/Experior.Activity/sbpython.pyc b/Experior.Activity/sbpython.pyc Binary files differnew file mode 100644 index 0000000..a62324e --- /dev/null +++ b/Experior.Activity/sbpython.pyc diff --git a/Experior.Activity/sbpython_script.py b/Experior.Activity/sbpython_script.py new file mode 100755 index 0000000..e69de29 --- /dev/null +++ b/Experior.Activity/sbpython_script.py diff --git a/Experior.Activity/sbrpcserver.py b/Experior.Activity/sbrpcserver.py new file mode 100755 index 0000000..e521a18 --- /dev/null +++ b/Experior.Activity/sbrpcserver.py @@ -0,0 +1,419 @@ +#!/usr/bin/env python +# encoding: utf-8 +""" +sbrpcserver.py + +This file is part of sugarbot. + +sugarbot is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +sugarbot is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with sugarbot. If not, see <http://www.gnu.org/licenses/>. +""" +import binascii +import logging +import optparse +import os +import random +import sblog +import sys +import time +import xmlrpclib + +from SimpleXMLRPCServer import SimpleXMLRPCServer +try: + from sbconfig import host, port +except ImportError: + from sbconfig_sample import host, port + +def proxyString(): + return "http://%s:%s/" % (host, str(port)) + +def rebuildMarshalledObject(obj, dictionary): + for key in dictionary: + setattr(obj, key, dictionary[key]) + +class sugarbotSession(): + def __init__(self,ID=0): + self.id = ID + # self.responses = [] + self.responses = {} + self.currentScript = -1 + self.scriptName = "" + self.failureText = {} + # self.log = logging.getLogger("%s" % ID) + + def getSuccessValue(self): + retval = 0 + + failed = [self.responses[k] for k in self.responses if not self.responses[k]] + if failed: + retval = 1 + return retval + + log = property(lambda self: logging.getLogger(str(self.id))) + +class sbRpcServer(SimpleXMLRPCServer): + sugarbotActivityVar = 'sugarActivityName' + + """ + Sugarbot RPC Server + + Responsible for listening on the XML-RPC port (defined as + sbconfig.port). The object loads the scripts provided + on the command-line as individual text lines. It then provides those + lines of text over XML-RPC to the XML-RPC client. The client is + responsible for parsing the lines into meaningful objects/functions. + + The list of files, as well as each file's contents, are kept entirely in + memory. This may lead to issues in the future with particularly large + script files, but this is currently not a problem. + """ + def __init__(self, args=[], xmlport=port, kill=False, restart=False): + # Random port? + if xmlport is None: + xmlport = random.randint(1024,65000) + self.port = xmlport + + # Create the RPC server + SimpleXMLRPCServer.__init__(self, (host, xmlport), + allow_none=True, logRequests=False) + + # Create the logger + self.log = self.initializeLogging() + self.log.info("Listening on port %i" % xmlport) + + # A listing of the filenames for each script + self.listOfScripts = [] + + # Keep list of all of the clients + self.clients = {0: sugarbotSession(0)} + + # Misc other stuffs... + self.kill = kill + self.restart = restart + self.log.info("Kill: %s\tRestart: %s" % (kill,restart)) + + # All of the arguments should be filenames for files to parse. + for arg in args: + self.addScript(arg) + + # Register all of the functions so that callers can use them + self.registerFunctions() + + # Initialize the random seed for generating client ID's + self.initRandomSeed() + + def testConnectivity(self): + """ + This function exists so that the client can test the connection to the + XML-RPC server. The xmlrpclib throws a socket.error if a function is + called when the ServerProxy object is not connected. + This provides a simple method so that we don't have to worry + about screwing with the internal state of other stuff. + """ + pass + + def initRandomSeed(self): + """ + Initializes the random seed using os.urandom. + """ + # Initialize the random seed... + seed = 0 + try: + seed = long(binascii.hexlify(os.urandom(64)), 16) + except NotImplementedError: + seed = time.time() + random.seed(seed) + + def generateSessionID(self): + """ + In the event a client does not specify an identifier, provide one. + """ + return random.randint(0, sys.maxint) + + def initializeLogging(self): + """ + Initialize logging for the sbRpcServer object. This provides logging + of INFO and above to the terminal, and all statements go to + a file named sbRpcServer.log. + """ + if __name__ == '__main__': + logging.basicConfig(level=logging.DEBUG, + format='%(asctime)s %(name)-12s %(levelname)-8s %(message)s', + datefmt='%m-%d %H:%M', + filename='sbRpcServer.log') + + # define a Handler which writes INFO messages or higher to the sys.stderr + console = logging.StreamHandler() + console.setLevel(logging.INFO) + # set a format which is simpler for console use + formatter = logging.Formatter('%(name)-12s: %(levelname)-8s %(message)s') + # tell the handler to use this format + console.setFormatter(formatter) + # add the handler to the root logger + logging.getLogger('').addHandler(console) + + return logging.getLogger('sbRpcServer') + + def registerFunctions(self): + """ + Register the functions for use with the XML server. + Recommended internal use only. + """ + self.register_function(self.addScript) + self.register_function(self.completionStatus) + self.register_function(self.fail) + self.register_function(self.generateSessionID) + self.register_function(self.getActivityName) + self.register_function(self.getKillFlag) + self.register_function(self.getRestartFlag) + self.register_function(self.getScript) + self.register_function(self.numberOfScripts) + self.register_function(self.resetClientState) + self.register_function(self.startScript) + self.register_function(self.success) + self.register_function(self.testConnectivity) + + def numberOfScripts(self): + """ + Returns the number of scripts for execution. + """ + return len(self.listOfScripts) + + def haveClient(self,ID): + """ + Determine whether or not we have a given client in tracking. + """ + return self.clients.has_key(ID) + + def getClient(self, ID): + """ + Returns the sugarbotSession object corresponding to the provided ID. + + If such a sugarbotSession object does not exist, it is created. + """ + if not self.haveClient(ID): + self.clients[ID] = sugarbotSession(ID) + + return self.clients[ID] + + def completionStatus(self,ID=0): + """ + Returns the completion status of all of the scripts as a list. + For example, if there were a total of three scripts, and the second + script had a command that did not complete successfully, the return + value would be: + [True, False, True] + + If no failures were reported: + [True, True, True] + + All values are initialized to False. If a script is still running, + then at least one of the result-values will be False. + + Removes the client from tracking. When the client re-connects, it + will effectively have a clean slate. + """ + retval = [] + if self.haveClient(ID): + retval = self.clients[ID] + self.resetClientState(ID) + else: + retval = None + + return retval + + def resetClientState(self, ID=0): + """ + Clears a client from tracking. + + When the client re-connects, it will start with a clean slate. + """ + if self.haveClient(ID): + client = self.getClient(ID) + + # Get a completed ratio + r = client.responses + successes = len([r[k] for k in r if r[k] is True]) + completed = len(r) + overall = len(self.listOfScripts) + self.clients[ID].log.info("Disconnected [%s\%s\%s]" % \ + (successes, completed, overall)) + del self.clients[ID] + + def scriptOutOfBounds(self,ID): + """ + Checks to see if a client's script index is out-of-bounds. + """ + client = self.getClient(ID) + + if client.currentScript < 0: + return True + + if client.currentScript >= len(self.listOfScripts): + return True + + def success(self, ID=0): + """ + This method is called after the successful completion of a script + """ + client = self.getClient(ID) + client.log.info("Success (%s)" % client.scriptName) + client.responses[client.scriptName] = True + + def modifyTraceBackText(self, text, ID=0): + """ + Replaces '<string>' with the script name in a backtrace report. + + This is useful in determining _which_ script failed execution. + """ + client = self.getClient(ID) + + if "File \"<string>\"" in text: + # Get the text.. + replacementText = "Sugarbot Script: '%s'" % client.scriptName + + # Replace the string with the filename + text = text.replace("<string>", replacementText) + return text + + def fail(self, status="No status provided", ID=0): + """ + This method is called if, for some reason, the client needs to disconnect + prematurely. A status message/reason is required. + """ + client = self.getClient(ID) + + # If the client is sending us exception text, replace that select + # portions of the text to be more useful. + status = self.modifyTraceBackText(status, ID) + + # Print the error + client.log.error("%s" % status) + client.responses[client.scriptName] = False + client.failureText[client.scriptName] = status + + return status + + def startScript(self,ID=0): + """ + Increments the internal script counter, looping back to the first + script if necessary. This should be the first method that a client + calls. + """ + # Get the client. This will create it if it does not exist. + client = self.getClient(ID) + + # Next script! (Note that the value is initialized to be -1) + client.currentScript += 1 + + # Prevent overflows if, for some reason, we loop back to script 0 + if self.scriptOutOfBounds(ID): + client.currentScript = 0 + + # Update the client's script name + client.scriptName = self.listOfScripts[client.currentScript] + + # Pretty status message... + client.log.info("Starting %s" % client.scriptName) + + # Make room for another response (default to failure) + client.responses[client.scriptName] = False + + return True + + def getScript(self,ID=0): + """ + Returns the script that the client should execute. + """ + client = self.getClient(ID) + client.log.info("Getting Script: %s" % client.scriptName ) + + f = open(self.listOfScripts[client.currentScript]) + listOfLines = f.readlines() + longString = '' + for line in listOfLines: + longString += line + + f.close() + + return longString + + def getActivityName(self, ID=0, which=None): + """ + Imports from the specified script file in order to + """ + client = self.getClient(ID) + if which is None: + which = client.currentScript + + if which == -1: + raise IndexError, "Invalid script index. If calling " + \ + "getActivityNameMake without arguments, make sure that " + \ + "startScript gets called first." + + try: + moduleName = self.listOfScripts[which] + index = moduleName.find(".py") + if index is not -1: + moduleName = moduleName[:index] + + execStr = 'from %s import %s' % \ + (moduleName, self.sugarbotActivityVar) + + evalStr = '%s' % self.sugarbotActivityVar + + exec execStr + return eval(evalStr) + except: + raise + + def getKillFlag(self): + return self.kill + + def getRestartFlag(self): + return self.restart + + def addScript(self,scriptPath): + """ + Adds a script to the internal list of scripts. This causes the parser + to load the script contents into memory for fast/easy access. + """ + self.listOfScripts.append(scriptPath) + + activityName = self.getActivityName(which=len(self.listOfScripts)-1 ) + self.log.info("Added script %s [Activity %s]" \ + % (scriptPath, activityName) ) + + +def main(argv=None): + p = optparse.OptionParser() + p.add_option('--no-restart', dest='restart', action='store_false',default=True, + help='do NOT restart sugarbot after execution has completed') + p.add_option('--no-kill', dest='kill', action='store_false',default=True, + help='do NOT kill sugar after all sugarbot scripts have executed') + + (options,args) = p.parse_args() + + if len(args) < 1: + p.print_help() + return 1 + + server = sbRpcServer(args, kill=options.kill, restart=options.restart) + + try: + server.serve_forever() + except KeyboardInterrupt: + print 'Ctrl+C pressed, exiting...' + +if __name__ == '__main__': + main()
\ No newline at end of file diff --git a/Experior.Activity/sbrpcserver.pyc b/Experior.Activity/sbrpcserver.pyc Binary files differnew file mode 100644 index 0000000..4704065 --- /dev/null +++ b/Experior.Activity/sbrpcserver.pyc diff --git a/Experior.Activity/script_calculate.py b/Experior.Activity/script_calculate.py new file mode 100755 index 0000000..93f0fa4 --- /dev/null +++ b/Experior.Activity/script_calculate.py @@ -0,0 +1,35 @@ +import time +import logging + +sugarActivityName = 'Calculate' + +def sugarbot_main(widgets): + # Test 'selected' functionality. + assert widgets['Share with:'].selected == "Private" + widgets['Share with:'].selected = "My Neighborhood" + assert widgets['Share with:'].selected == "My Neighborhood" + + # Test widget fetching/assignment + one = widgets['1'] + plus = widgets['+'] + enter = widgets['enter'] + + for i in range(0,5): + # Test click + one.click() + plus.click() + one.click() + + # Test Entry text assignment + assert widgets['TextEntry'].text == '1+1' + + enter.click() + + assert len(widgets['TextEntry'].text) == 0 + + time.sleep(1) + + # More Entry text assignment + widgets['TextEntry'].text = "1+5" + assert widgets['TextEntry'].text == '1+5' + enter.click() diff --git a/Experior.Activity/script_calculate.pyc b/Experior.Activity/script_calculate.pyc Binary files differnew file mode 100644 index 0000000..e079466 --- /dev/null +++ b/Experior.Activity/script_calculate.pyc diff --git a/Experior.Activity/script_terminal.py b/Experior.Activity/script_terminal.py new file mode 100755 index 0000000..f45e6eb --- /dev/null +++ b/Experior.Activity/script_terminal.py @@ -0,0 +1,11 @@ +import time +import logging + +sugarActivityName = 'Terminal' + +def sugarbot_main(widgets): + #assert 1 + term = widgets['VteTerminal'] + term.typeText("whoami\r") + term.typeText("ls -la\r") + term.typeText("exit\r")
\ No newline at end of file diff --git a/Experior.Activity/script_terminal.pyc b/Experior.Activity/script_terminal.pyc Binary files differnew file mode 100644 index 0000000..9c63aa1 --- /dev/null +++ b/Experior.Activity/script_terminal.pyc diff --git a/Experior.Activity/setup.py b/Experior.Activity/setup.py new file mode 100755 index 0000000..a74cffd --- /dev/null +++ b/Experior.Activity/setup.py @@ -0,0 +1,41 @@ +#!/usr/bin/env python +# encoding: utf-8 +""" +setup.py + +This file is part of sugarbot. + +sugarbot is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +sugarbot is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with sugarbot. If not, see <http://www.gnu.org/licenses/>. +""" +import sys + +from sugar.activity import bundlebuilder +bundlebuilder.start() + + +#try: + #if len(sys.argv) < 2 or sys.argv[1] == "install": + #from sugar.activity import bundlebuilder + #bundlebuilder.start("sugarbot") + #elif sys.argv[1] == "test": + ## nosetests + #pass +#except ImportError: + #import os + #os.system("find ./ | sed 's,^./,sugarbot.activity/,g' > MANIFEST") + #os.system('rm sugarbot.xo') + #os.chdir('..') + #os.system('zip -r sugarbot.xo sugarbot.activity') + #os.system('mv sugarbot.xo ./sugarbot.activity') + #os.chdir('sugarbot.activity') diff --git a/Experior.Activity/sugarbot.py b/Experior.Activity/sugarbot.py new file mode 100755 index 0000000..717ffed --- /dev/null +++ b/Experior.Activity/sugarbot.py @@ -0,0 +1,207 @@ +#!/usr/bin/env python +# encoding: utf-8 +""" +sugarbot.py + +This file is part of sugarbot. + +sugarbot is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +sugarbot is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with sugarbot. If not, see <http://www.gnu.org/licenses/>. +""" +import gobject +import gtk +import logging +import os +import sblog +import socket +import sys + +from xmlrpclib import ServerProxy + +from sugar.activity import activity +#from sugar.activity import registry +from jarabe.model.bundleregistry import get_registry + +from sbrpcserver import sbRpcServer, proxyString + +# For reading GDK.Event's +from sbgui import sbGUI + +class sugarbot(activity.Activity): + def __dynamicImport(self, fullname, path=None): + """ + Handles emulation of the target Activity. + + Dynamically modifies the module-search path, and imports an object from + a module. Some fancy work is done to get that to work properly. + @param fullname: The module to import, example calculate.Calculate. + @param path: If necessary, the path to the imported module. + """ + module = None + module_name = "" + class_name = "" + + if fullname is None: + return self.__parentClass + + try: + # <hack>========================================================= + # This could probably be cleaned up or refactored into separate + # functions. However, the code is short enough that it is not + # likely worth it. + if path != None: + sys.path.append(path) + + splitted_module = fullname.rsplit('.', 1) + + if len(splitted_module) >= 2: + module_name = splitted_module[0] + class_name = splitted_module[1] + + if module_name is not None and class_name is not None and \ + len(module_name) > 0 and len(class_name) > 0: + module = __import__(module_name) + + for comp in module_name.split('.')[1:]: + module = getattr(module, comp) + # </hack>======================================================== + except ImportError: + raise ImportError, \ + "Error in sugarbot._dynamicImport(%s,%s)" % (fullname,path) + + finally: + if hasattr(module, class_name): + self.__parentClass = getattr(module, class_name) + else: # Failsafe + self.__parentClass = activity.Activity + + return self.__parentClass + + def __cloneActivity(self,sugarHandle): + """ + Starts the cloned activity, where self.__parentClass is the activity + class. This is performed by [1] inheriting from the parentClass, + and [2] calling parentClass.__init__. + """ + if self.__parentClass is None: + raise AttributeError, "self.__parentClass not defined properly." + + else: + sugarbot.__bases__ = (self.__parentClass,) + self.__parentClass.__init__(self,sugarHandle) + + def __getActivityList(self): + """ + Gets a list of activities from the registry. Stores this list in + self.__activityList. + """ + # Prevent looking up all of the activities multiple times. + if not hasattr(self,"__activityList"): + #self.__activityList = registry.get_registry().get_activities() + self.__activityList = get_registry()._bundles + return self.__activityList + + # If we didn't get any activities, something went wrong. + if len(self.__activityList) < 1: + raise "Activity list is empty. Cannot get activity info!" + + return None + + def __selectActivity(self,name): + """ + Selects an activity from the activityList by name. This allows + simpler access to the list of activities. + (For example, one can specify only 'Calculate' instead of + 'org.laptop.Calculate' or 'calculate.Calculate'). + @param name: The name of the activity (e.g. 'Calculate') + """ + # Get the list of activities if it does not already exist + activityList = self.__getActivityList() + + # Initialization of some variables... + self.__path = None + self.__importClass = None + self.__className = None + + # Iterate through the activity list + for activity in activityList: + if activity.get_name() == name: + self.__path = activity.get_path() + self.__importClass = activity.get_command().split()[-1] + self.__className = self.__importClass.split(".")[-1] + self.log.debug("Importing class %s" % name) + return + + # If we ever get here, that means we didn't find anything... + if (self.__path is None) and (self.__importClass is None) and \ + (self.__className is None): + raise NameError, "Could not find '%s' in activity list." % name + + def __initializeScript(self): + """ + Initialize the script on the XML-RPC server. + Returns the name of the Activity that should be instantiated + """ + rpc = self.__xmlRPC + + try: + self.sessID = os.environ['SUGARBOT_ID'] + except KeyError: + self.sessID = 0 + + # Start the script + if not rpc.startScript(self.sessID): + rpc.fail("Could not start the script!", self.sessID) + exit() + + # Get our activity name + activityName = rpc.getActivityName(self.sessID) + if activityName is None: + rpc.fail('Bad activity name provided', self.sessID) + exit() + + self.log.info("Activity is %s" % activityName) + return activityName + + + def __init__(self, handle): + """ + Performs setup, and runs the specified activity. + """ + self.log = logging.getLogger('sugarbot') + + # Set up threading... + gtk.gdk.threads_init() + + # Handle is set to 'None' for testing purposes via Nose. Obviously, + # if Sugar sets the handle to None, there are other problems... + if handle is None: + return + + try: + # Create the RPC connection object + self.__xmlRPC = ServerProxy(proxyString()) + + # Set up the sbGUI object for automation + self.__sbgui = sbGUI(self, self.__xmlRPC) + + # Get our activity name + activityName = self.__initializeScript() + + # Actually clone the activity + self.__selectActivity(activityName) + self.__dynamicImport(self.__importClass,self.__path) + self.__cloneActivity(handle) + except socket.error: + sys.log.error("====== COULD NOT CONNECT TO XML-RPC SERVER ======") + sys.exit(-1) diff --git a/Experior.Activity/sugarbot.pyc b/Experior.Activity/sugarbot.pyc Binary files differnew file mode 100644 index 0000000..86dddaa --- /dev/null +++ b/Experior.Activity/sugarbot.pyc diff --git a/Experior.Activity/sugarbotlauncher.py b/Experior.Activity/sugarbotlauncher.py new file mode 100755 index 0000000..29d8d7c --- /dev/null +++ b/Experior.Activity/sugarbotlauncher.py @@ -0,0 +1,298 @@ +#!/usr/bin/env python +# encoding: utf-8 +""" +sugarbot-launcher.py + +This file is part of sugarbot. + +sugarbot is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +sugarbot is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with sugarbot. If not, see <http://www.gnu.org/licenses/>. +""" +import logging +import os +import signal +import sys +import socket + +from subprocess import Popen +from time import time,sleep +from xmlrpclib import ServerProxy + +# Set the Sugarbot path env var if it has not already been set +if not os.environ.has_key('SUGARBOT_PATH'): + os.environ['SUGARBOT_PATH']=os.getcwd() + +# Add the sugarbot path to the include-path +sys.path.append(os.environ['SUGARBOT_PATH']) + +# Import sugar-specific stuff +from sbrpcserver import proxyString, rebuildMarshalledObject, sugarbotSession +try: + from sbconfig import clientName +except ImportError: + from sbconfig_sample import clientName + +class SugarProcessHandler(): + """ + Handles launching and monitoring of the Sugar process. + """ + def __init__(self): + # Set the environment variable to emulate, and the executed scripts. + os.environ['SUGARBOT_EMULATOR']='1' + + # Get the connection to the XML-RPC server + self.xml = self.connectToXMLRPC() + logging.info("Connected to XML-RPC server %s" % proxyString()) + + # Get our ID and reset the state + self.ID = self.getSugarbotClientID() + self.xml.resetClientState(self.ID) + logging.info("Using session ID %s" % self.ID) + + def getSugarbotClientID(self): + """ + Retrieves whatever ID we should be using. This is either set by the + environment variables, the configuration file, or generated by the + server. + """ + # If we have not specified a sugarbot ID, specify one now + if not os.environ.has_key('SUGARBOT_ID'): + if not clientName: + os.environ['SUGARBOT_ID']=str(xml.generateSessionID()) + else: + os.environ['SUGARBOT_ID']=clientName + + return os.environ['SUGARBOT_ID'] + + def connectToXMLRPC(self): + """ + Connects to the ML-RPC server, tests connectivity. + """ + xml = ServerProxy(proxyString()) + try: + xml.testConnectivity() + except: + logging.fatal('Could not connect to the XML-RPC server') + sys.exit(1) + + return xml + + def launchSugar(self): + """ + Launches the sugar-emulator process and waits for it to finish. + """ + # Launch the process + self.pid = Popen('sugar-emulator') + self.waitForSugarbotExecution() + + def waitForSugarbotExecution(self): + """ + Waits a certain amount of time for Sugar to quit. + + If Sugar does not die in the alloted time, it is killed. + """ + # Wait five minutes for the tests to execute? Why not. + start = time() + wait = 60*5 + done = start + wait + + # We've waited long enough. Kill it! + while self.pid.poll() is None and done > time(): + sleep(0.1) + if self.pid.poll() is None: + logging.fatal("Had to send SIGTERM to Sugar process") + os.kill(self.pid.pid, signal.SIGTERM) + + def getReturnValue(self): + """ + Determines whether all of the tests succeeded, or there was a failure. + + 0 = All tests succeeded + 1 = At least one failure + """ + # Get the completion status... + sessionDict = self.xml.completionStatus(self.ID) + if sessionDict is None: + logging.error("Could not get completion status from XMLRPC server") + return -1 + + session = sugarbotSession() + rebuildMarshalledObject(session, sessionDict) + + # Get whatever the return value should be. + retval = session.getSuccessValue() + + # Print out the completion statuses all pretty-like + status = "" + logging.info("Script completion statuses:") + for script in session.responses: + logging.info("\t%s: %s" % (session.responses[script], script)) + + # if session.failureText.has_key(script) and \ + # not session.responses[script]: + if not session.responses[script]: + logging.info("================ [%s] ================", script) + for line in session.failureText[script].split('\n'): + if len(line) > 0: + logging.info(line) + logging.info("================ [%s] ================", script) + + + # Check the number of scripts to make sure they all executed + numScripts = self.xml.numberOfScripts() + if( numScripts > len(session.responses) ): + retval = False + logging.warning("Only executed %i/%i scripts" % \ + (numScripts,len(session.responses)) ) + + return retval + +def main(): + s = SugarProcessHandler() + s.launchSugar() + + retval = s.getReturnValue() + logging.info("Returning %s" % retval) + + return retval + +if __name__ == "__main__": + main() + +else: + import pygtk + pygtk.require('2.0') + import gtk + import gobject + import time + + from view.Shell import Shell + from model.shellmodel import ShellModel + from shellservice import ShellService + + from view.frame.activitiestray import ActivitiesTray + from view.frame.activitybutton import ActivityButton + + class SugarbotLauncher: + """ + Autmates the launching and re-launching of Sugarbot from the main Sugar + GUI. This automation is handled by simulating a click on the Sugarbot + activity icon in the Sugar Pane. + """ + activityName = "sugarbot" + + def __init__(self, shell, shellModel): + gtk.gdk.event_handler_set(self.eventHandler) + self.model = shellModel + self.numberOfScripts = 0 + self.shell = shell + self.sugarbotIsRunning = False + self.timesLaunched = 0 + self.xml = ServerProxy(proxyString()) + + home = self.model.get_home() + home.connect('activity-started', self._activity_started_cb) + home.connect('activity-removed', self._activity_removed_cb) + home.connect('active-activity-changed', self._activity_active_cb) + home.connect('pending-activity-changed', self._activity_pending_cb) + + def isSugarbotActivity(self,activity): + """ + Checks to see if an 'Activity' object is the sugarbot activity. + """ + if activity._activity_info.name == self.activityName: + if self.numberOfScripts <= 0: + self.doSetup(activity) + return True + return False + + def doSetup(self, activity): + """ + Prepares for launching the activity, given its information. + """ + path = activity._activity_info.path + sys.path.append(path) + + try: + self.numberOfScripts = self.xml.numberOfScripts() + except: + logging.fatal("Could not connect to XMLRPC Server") + sys.exit(-1) + + def _activity_started_cb(self, model, activity): + if self.isSugarbotActivity(activity): + self.sugarbotIsRunning = True + self.timesLaunched += 1 + + def _activity_removed_cb(self, model, activity): + if self.isSugarbotActivity(activity): + self.sugarbotIsRunning = False + + if self.xml.getKillFlag() and \ + 1 <= self.numberOfScripts <= self.timesLaunched: + logging.info("Sugarbot execution completed successfully") + self.killSugar() + + def _activity_active_cb(self, model, activity): + pass + + def _activity_pending_cb(self, model, activity): + pass + + def killSugar(self): + """ + Sends SIGTERM to the Sugar process. + """ + try: + if os.environ.has_key('SUGAR_EMULATOR_PID'): + pid = int(os.environ['SUGAR_EMULATOR_PID']) + os.kill(pid, signal.SIGTERM) + except: + raise + + def tryToLaunchSugarbotActivity(self): + """ + Attempts to launch sugarbot. + """ + if self.sugarbotIsRunning or not self.xml.getRestartFlag(): + # time.sleep(0.1) + return + + frame = self.shell.get_frame() + frameBottomPanel = frame._bottom_panel + bottomPanelChildren = frameBottomPanel._bg.get_children() + tray = [k for k in bottomPanelChildren if isinstance(k,ActivitiesTray)] + + if tray: + trayIcons = tray[0]._tray.get_children() + else: + return + + activities = [k for k in trayIcons if isinstance(k, ActivityButton)] + sbList = [k for k in activities if \ + k._activity_info.name == self.activityName] + + if len(sbList) > 0: + sbList[0].emit('clicked') + self.sugarbotIsRunning = True + + def eventHandler(self, event): + """ + Intercepts GTK calls, to attempt to launch sugarbot. + + Attempts to launch sugarbot at every gdk.Event emitted. + """ + if event is not None: + gtk.main_do_event(event) + self.tryToLaunchSugarbotActivity() + diff --git a/Experior.Activity/svn-commit.tmp b/Experior.Activity/svn-commit.tmp new file mode 100644 index 0000000..b34bcea --- /dev/null +++ b/Experior.Activity/svn-commit.tmp @@ -0,0 +1,6 @@ +Hacky changes + +Committing changes to 0.1 release made to ge things to work. +--This line, and those below, will be ignored-- + +M sugarbot.py diff --git a/Experior.Activity/test_rpcserver.py b/Experior.Activity/test_rpcserver.py new file mode 100755 index 0000000..919e82c --- /dev/null +++ b/Experior.Activity/test_rpcserver.py @@ -0,0 +1,143 @@ +#!/usr/bin/env python +# encoding: utf-8 +""" +test_rpcserver.py + +This file is part of sugarbot. + +sugarbot is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +sugarbot is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with sugarbot. If not, see <http://www.gnu.org/licenses/>. +""" +import os +try: + import sbconfig +except ImportError: + import sbconfig_sample + +from random import randint +from sbrpcserver import * +from xmlrpclib import ServerProxy + +class test_rpcServer: + + files = ["fileOne.py", "fileTwo.py"] + activityNames = ["activityOne", "activityTwo"] + basicScript = \ +""" +def sugarbot_main(var): + assert var + +""" + scripts = [basicScript + "sugarActivityName = '%s'" % activityNames[0], + basicScript + "sugarActivityName = '%s'" % activityNames[1]] + + def __init__(self): + self.is_setup = False + + def setUp(self): + assert not self.is_setup + + # Generate a random ID + self.clientID = randint(1,1000) + + # Create the server object. + self.server = sbRpcServer([], None) + + # Create the script file and add it... + self.createScriptFiles() + for file in self.files: + self.server.addScript(file) + + self.is_setup = True + + + def tearDown(self): + assert self.is_setup + + self.server.server_close() + self.deleteScriptFiles() + + self.is_setup = False + + def createScriptFiles(self): + for i in range(0, len(self.scripts)): + f = open(self.files[i],"w+") + f.write(self.scripts[i]) + f.close() + + def deleteScriptFiles(self): + for f in self.files: + os.remove(f) + + def test_StartScript_Reset(self): + self.server.startScript(self.clientID) + + client = self.server.clients[self.clientID] + client.currentScript = 999999 + + self.server.startScript(self.clientID) + + assert client.currentScript == 0 + assert client.responses[client.scriptName] == False + + def test_StartScript_Bounds(self): + # Check to see if we go beyond the bounds + for count in range(0,1+len(self.server.listOfScripts)): + self.server.startScript() + assert self.server.clients[0].currentScript == 0 + + def test_RegisteredFunctions(self): + expectedMethods = ['addScript', + 'completionStatus', + 'fail', + 'generateSessionID', + 'getActivityName', + 'getRestartFlag', + 'getScript', + 'numberOfScripts', + 'startScript', + 'success'] + listedMethods = self.server.system_listMethods() + + for method in expectedMethods: + if not method in listedMethods: + print method + assert 0 + + assert not 'NOTREGISTERED' in listedMethods + + def test_getActivityName_IndexError(self): + try: + self.server.startScript(self.clientID) + self.server.getActivityName(self.clientID, 9999) + assert 0 + except IndexError: + pass + + def test_getActivityName_WithoutStartingScript(self): + try: + self.server.getActivityName(self.clientID) + assert 0 + except IndexError: + pass + + def test_getActivityName(self): + for activity in self.activityNames: + self.server.startScript() + assert self.server.getActivityName() == activity + + def test_multipleCallsToGetScript(self): + for script in self.scripts: + self.server.startScript() + assert self.server.getScript() == script +
\ No newline at end of file diff --git a/Experior.Activity/test_rpcserver.pyc b/Experior.Activity/test_rpcserver.pyc Binary files differnew file mode 100644 index 0000000..650aed2 --- /dev/null +++ b/Experior.Activity/test_rpcserver.pyc diff --git a/Experior.Activity/test_sbexecutionengine.py b/Experior.Activity/test_sbexecutionengine.py new file mode 100755 index 0000000..29582c0 --- /dev/null +++ b/Experior.Activity/test_sbexecutionengine.py @@ -0,0 +1,110 @@ +#!/usr/bin/env python +# encoding: utf-8 +""" +test_sbexecutionengine.py + +This file is part of sugarbot. + +sugarbot is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +sugarbot is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with sugarbot. If not, see <http://www.gnu.org/licenses/>. +""" +from sbexecutionengine import * + +logging.raiseExceptions = 0 + +class failException(Exception): pass +class successException(Exception): pass + +class test_sbExecutionEngine: + + class fakeXmlRpc: + def __init__(self): + self.assertionValue = 1 + self.restart = False + self.kill = False + self.status = False + + def success(self, clientID=None): + # raise successException + # We can't raise exceptions on success, because of the way + # sbExecutionEngine works. Any exceptions call fail() + self.status = True + + def fail(self, reason=None, clientID=None): + raise failException + + def getRestartFlag(self): + return self.restart + + def getKillFlag(self): + return self.kill + + def getScript(self, dontCare): + return \ +""" +def sugarbot_main(param): + assert param +""" + + def __init__(self): + self.is_setup = False + + def setUp(self): + assert not self.is_setup + + self.xml = self.fakeXmlRpc() + self.ee = sbExecutionEngine(None,self.xml) + + self.is_setup = True + + def tearDown(self): + assert self.is_setup + + self.is_setup = False + + def test_setComplete(self): + assert not self.ee.isComplete() + self.ee.executionComplete = True + assert self.ee.isComplete() + + def test_killSugarbot(self): + try: + self.ee.killSugarbot() + assert 0 + except SystemExit: + pass + + def test_executePy_Fail(self): + try: + self.ee.widgets.__nonzero__ = lambda: False + self.ee.executePy() + assert 0 + except failException: + pass + + def test_executePy_Success(self): + # try: + self.ee.widgets.__nonzero__ = lambda: True + self.ee.executePy() + + assert self.xml.status == True + + def test_executePy_UnexpectedException(self): + try: + def raiseException(): raise ValueError + + self.ee.widgets.__nonzero__ = raiseException + self.ee.executePy() + assert 0 + except: + pass
\ No newline at end of file diff --git a/Experior.Activity/test_sbexecutionengine.pyc b/Experior.Activity/test_sbexecutionengine.pyc Binary files differnew file mode 100644 index 0000000..363c69c --- /dev/null +++ b/Experior.Activity/test_sbexecutionengine.pyc diff --git a/Experior.Activity/test_sbgui.py b/Experior.Activity/test_sbgui.py new file mode 100755 index 0000000..f526d5f --- /dev/null +++ b/Experior.Activity/test_sbgui.py @@ -0,0 +1,84 @@ +#!/usr/bin/env python +# encoding: utf-8 +""" +test_sbgui.py + +This file is part of sugarbot. + +sugarbot is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +sugarbot is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with sugarbot. If not, see <http://www.gnu.org/licenses/>. +""" +import unittest +import sbexecutionengine + +from sbgui import * + +class test_sbgui: + class fakeExecutionEngine(): + def getCommand(self): + pass + def executeNextCommand(self): + pass + def isComplete(self): + return False + + def __init__(self): + self.is_setup = False + + def setUp(self): + assert not self.is_setup + + self.gui = sbGUI(None,None) + self.gui.engine = self.fakeExecutionEngine() + self.window = gtk.Window(gtk.WINDOW_TOPLEVEL) + + self.is_setup = True + + def tearDown(self): + assert self.is_setup + + self.is_setup = False + + def startTimer(self, timer): + self.timer = time.time() + timer + + def timeout(self): + if time.time() > self.timer: + return True + return False + + def test_CatchWidgetInstantiation(self): + wlabel = "testButton" + widget = gtk.Button(label=wlabel) + + self.window.add(widget) + widget.show() + self.window.show() + + self.startTimer(3.0) + while not self.timeout(): + gtk.main_iteration(False) + + if self.gui.getWidgetByName(wlabel) is widget: + return + assert 0 + + def test_registerEventHandler(self): + try: + self.gui.registerEventHandler() + except NotImplementedError: + assert 0 + + +if __name__ == "__main__": + unittest.main() diff --git a/Experior.Activity/test_sbgui.pyc b/Experior.Activity/test_sbgui.pyc Binary files differnew file mode 100644 index 0000000..1722516 --- /dev/null +++ b/Experior.Activity/test_sbgui.pyc diff --git a/Experior.Activity/test_sugarbot.py b/Experior.Activity/test_sugarbot.py new file mode 100755 index 0000000..33b6126 --- /dev/null +++ b/Experior.Activity/test_sugarbot.py @@ -0,0 +1,118 @@ +#!/usr/bin/env python +# encoding: utf-8 +""" +test_sugarbot.py + +This file is part of sugarbot. + +sugarbot is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +sugarbot is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with sugarbot. If not, see <http://www.gnu.org/licenses/>. +""" +from sugarbot import * + +class rpcFailure(Exception): + pass + +class test_sugarbot: + + class cloneTest(activity.Activity): + def __init__(self,x): + self.clonedProperly = True + + class fakeXmlRpc: + activity = "exampleActivity" + def startScript(self, *args): + return True + def fail(self, reason, *args): + raise rpcFailure + + class fakeXmlRpcGood(fakeXmlRpc): + def getActivityName(self, *args): + return self.activity + + class fakeXmlRpcBad(fakeXmlRpc): + def getActivityName(self, *args): + return None + + def __init__(self): + self.is_setup = False + + def setUp(self): + assert not self.is_setup + + self.sb = sugarbot(None) + + self.is_setup = True + + def tearDown(self): + assert self.is_setup + + sugarbot.__bases__ = (activity.Activity, ) + + self.is_setup = False + + def test_dynamicImport1(self): + # Attempt to import a valid module + assert gobject.GObject == \ + self.sb._sugarbot__dynamicImport("gobject.GObject") + + def test_dynamicImport2(self): + # Attempt to import a module with a bad length or None + assert activity.Activity == self.sb._sugarbot__dynamicImport("") + assert activity.Activity == self.sb._sugarbot__dynamicImport(None) + + def test_dynamicImport3(self): + # Attempt to import a module that does not exist + try: + assert activity.Activity == \ + self.sb._sugarbot__dynamicImport("asdf.1231231.##fw") + assert 0 + except ImportError: + pass + + def test_cloneActivity1(self): + self.sb._sugarbot__parentClass = None + try: + self.sb._sugarbot__cloneActivity(None) + assert 0 + except AttributeError: + pass + + def test_cloneActivity2(self): + self.sb._sugarbot__parentClass = self.cloneTest + self.sb._sugarbot__cloneActivity(None) + + assert self.cloneTest in sugarbot.__bases__ + assert self.sb.clonedProperly + + def test_cloneActivity3(self): + assert not self.cloneTest in sugarbot.__bases__ + + def test_initializeScript1(self): + fake = self.fakeXmlRpcGood() + self.sb._sugarbot__xmlRPC = fake + + assert fake.activity == self.sb._sugarbot__initializeScript() + + def test_initializeScript2(self): + fake = self.fakeXmlRpcBad() + self.sb._sugarbot__xmlRPC = fake + + try: + self.sb._sugarbot__initializeScript() + assert 0 + except rpcFailure: + pass + + + diff --git a/Experior.Activity/test_sugarbot.pyc b/Experior.Activity/test_sugarbot.pyc Binary files differnew file mode 100644 index 0000000..464463b --- /dev/null +++ b/Experior.Activity/test_sugarbot.pyc diff --git a/Experior.Activity/test_widgetIdentifier.py b/Experior.Activity/test_widgetIdentifier.py new file mode 100755 index 0000000..83f6b3f --- /dev/null +++ b/Experior.Activity/test_widgetIdentifier.py @@ -0,0 +1,254 @@ +from widgetIdentifier import * + +class test_widgetIdentifier: + def __init__(self): + self.is_setup = False + + def setUp(self): + assert not self.is_setup + + self.widget = gtk.Button() + self.name = "nameExample" + + self.widget.set_name(self.name) + + self.identifier = widgetIdentifier(self.widget) + + self.is_setup = True + + def tearDown(self): + assert self.is_setup + self.is_setup = False + + def test_init(self): + assert self.identifier._widget == self.widget + + def test_getIdentifier(self): + assert self.identifier.getIdentifier() == self.name + + def test_checkStoredIdentifier(self): + assert not self.identifier.checkStoredIdentifier() + + def test_setIdentifier(self): + assert not self.identifier.setIdentifier("GtkAnything") + + name = "x" + assert self.identifier.setIdentifier(name) == name + + def test_setWidget(self): + self.identifier.setWidget(None) + assert not self.identifier.getIdentifier() + + widget = gtk.Label("") + name = "asdf" + widget.set_name(name) + assert not self.identifier.getIdentifier() == name + + self.identifier.setWidget(widget) + assert self.identifier.getIdentifier() == name + + def test_getStoredIdentifier(self): + assert not self.identifier.getStoredIdentifier() == self.name + + self.identifier.getIdentifier() + assert self.identifier.getStoredIdentifier() == self.name + + name = "x" + self.identifier.setIdentifier(name) + assert self.identifier.getStoredIdentifier() == name + + def test_validateIdentifier(self): + assert not self.identifier.validateIdentifier(None) + assert not self.identifier.validateIdentifier("") + assert not self.identifier.validateIdentifier(1) + assert not self.identifier.validateIdentifier("GtkAnything") + assert not self.identifier.validateIdentifier("SugarToggleToolButton") + assert self.identifier.validateIdentifier("ValidName") + +class test_buttonIdentifier: + def __init__(self): + self.is_setup = False + + def setUp(self): + assert not self.is_setup + + self.name = "nameExample" + self.widget = gtk.Button() + + self.identifier = buttonIdentifier(self.widget) + + self.is_setup = True + + def tearDown(self): + assert self.is_setup + self.is_setup = False + + def test_init(self): + assert not self.identifier.getIdentifier() + + def test_getIdentifier1(self): + self.widget.set_label(self.name) + assert self.identifier.getIdentifier() == self.name + + def test_getIdentifier2(self): + self.widget.set_name(self.name) + assert self.identifier.getIdentifier() == self.name + +class test_toolButtonIdentifier: + def __init__(self): + self.is_setup = False + + def setUp(self): + assert not self.is_setup + + self.name = "nameExample" + self.widget = gtk.ToolButton() + + self.identifier = toolButtonIdentifier(self.widget) + + self.is_setup = True + + def tearDown(self): + assert self.is_setup + self.is_setup = False + + def test_init(self): + assert not self.identifier.getIdentifier() + + def test_getIdentifier1(self): + self.widget.set_name(self.name) + assert self.identifier.getIdentifier() == self.name + + def test_getIdentifier2(self): + self.widget.set_label(self.name) + assert self.identifier.getIdentifier() == self.name + + def test_getIdentifier3(self): + self.widget.set_icon_name(self.name) + assert self.identifier.getIdentifier() == self.name + + def test_getIdentifier4(self): + label = gtk.Label(self.name) + self.widget.set_label_widget(label) + assert self.identifier.getIdentifier() == self.name + +class test_comboBoxIdentifier: + def __init__(self): + self.is_setup = False + + def setUp(self): + assert not self.is_setup + + self.name = "nameExample" + self.widget = gtk.ComboBox() + + self.identifier = comboBoxIdentifier(self.widget) + + self.is_setup = True + + def tearDown(self): + assert self.is_setup + self.is_setup = False + + def test_init(self): + assert not self.identifier.getIdentifier() + + def test_getIdentifier1(self): + self.widget.set_name(self.name) + assert self.identifier.getIdentifier() == self.name + + def test_getIdentifier2(self): + self.widget.set_title(self.name) + assert self.identifier.getIdentifier() == self.name + +class test_entryIdentifier: + def __init__(self): + self.is_setup = False + + def setUp(self): + assert not self.is_setup + + self.name = "nameExample" + self.widget = gtk.Entry() + + self.identifier = entryIdentifier(self.widget) + + self.is_setup = True + + def tearDown(self): + assert self.is_setup + self.is_setup = False + + def test_init(self): + assert not self.identifier.getIdentifier() + + def test_getIdentifier1(self): + self.widget.set_name(self.name) + assert self.identifier.getIdentifier() == self.name + + def test_getIdentifier2(self): + self.widget.set_text(self.name) + assert self.identifier.getIdentifier() == self.name + +# class test_paletteIdentifier: +# def __init__(self): +# self.is_setup = False +# +# def setUp(self): +# assert not self.is_setup +# +# self.name = "nameExample" +# # self.widget = gtk.Entry() +# self.widget = Palette(self.name) +# +# self.identifier = paletteIdentifier(self.widget) +# +# self.is_setup = True +# +# def tearDown(self): +# assert self.is_setup +# self.is_setup = False +# +# def test_init(self): +# assert self.identifier.getIdentifier() +# +# def test_getIdentifier1(self): +# otherName = "someOtherName" +# self.widget.set_name(otherName) +# assert self.identifier.getIdentifier() == otherName +# +# def test_getIdentifier2(self): +# assert self.identifier.getIdentifier() == self.name + +class test_toolComboBoxIdentifier: + def __init__(self): + self.is_setup = False + + def setUp(self): + assert not self.is_setup + + self.name = "nameExample" + self.widget = ToolComboBox() + self.widget.set_property('label-text', self.name) + + # Set the widget later... + self.identifier = toolComboBoxIdentifier(None) + + self.is_setup = True + + def tearDown(self): + assert self.is_setup + self.is_setup = False + + def test_getIdentifier1(self): + otherName = "someOtherName" + self.widget.set_name(otherName) + self.identifier.setWidget(self.widget) + assert self.identifier.getIdentifier() == otherName + + def test_getIdentifier2(self): + self.identifier.setWidget(self.widget) + assert self.identifier.getIdentifier() == self.name + +if __name__ == "__main__": + unittest.main() diff --git a/Experior.Activity/test_widgetIdentifier.pyc b/Experior.Activity/test_widgetIdentifier.pyc Binary files differnew file mode 100644 index 0000000..f1385de --- /dev/null +++ b/Experior.Activity/test_widgetIdentifier.pyc diff --git a/Experior.Activity/widgetIdentifier.py b/Experior.Activity/widgetIdentifier.py new file mode 100755 index 0000000..c9eb7dd --- /dev/null +++ b/Experior.Activity/widgetIdentifier.py @@ -0,0 +1,260 @@ +#!/usr/bin/env python +# encoding: utf-8 +""" +widgetIdentifier.py + +This file is part of sugarbot. + +sugarbot is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +sugarbot is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with sugarbot. If not, see <http://www.gnu.org/licenses/>. +""" +import gobject +import sys +import os +import time +import pygtk +pygtk.require('2.0') + +import gtk +from gtk import gdk + +import sugar +from sugar import graphics +from sugar.graphics.toolbutton import Palette +from sugar.graphics.icon import Icon +from sugar.graphics.toolcombobox import ToolComboBox + +class widgetIdentifier: + """ + The widgetIdentifier class is used as a basis for classes to identify + Widgets. Ideally, all developers would call Widget.set_name() on all + widgets created by their Activities. Since this is not always + practical or worthwhile, we must rely on other methods to identify + widgets. + + This class provides some of the functionality for identifying widgets, + such as a list of strings that are default Widget names (e.g. 'GtkButton'). + """ + identifiers = { } + + def __init__(self, widget): + self.widgetAttribute = "sugarbotWidgetIdentifier" + + self.dontWant = ["GtkToolbar", "GtkToggleButton","GtkButton", + "GtkEventBox", "GtkNotebook", "GtkViewport", + "HippoCanvas", "GtkTextView", "GtkInvisible", + "GtkEntry", "GtkLabel", "GtkVBox", "GtkHBox", + "SugarIcon","SugarToolButton","GtkAlignment", + "GtkSeparatorToolItem","GtkTable","SugarToolbox", + "GtkToggleToolButton","GtkScrolledWindow","GtkCellView", + "GtkVSeparator","GtkArrow","GtkToolItem","GtkAccelLabel", + "SugarComboBox","SugarToggleToolButton","GtkHSeparator", + "GtkHButtonBox","GtkImageMenuItem","GtkSeparatorMenuItem", + "GtkSpinButton","GtkDrawingArea","GtkFrame", + "GtkColorButton", ""] + + self.dontWantPrefixes = ["Gtk", "sugar+graphics"] + self.setWidget(widget) + + def setWidget(self, widget): + self._widget = widget + # self.getIdentifier() + + def getIdentifierSub(self): + """ + Overridden by inheriting classes. + """ + return None + + def getIdentifier(self): + """ + Returns the Identifier of the Widget set with __init__, or None + if we cannot find an identifier. Do not override this function! + """ + # If we have already set the attribute for this widget, just + # retrieve it quickly. + if self.checkStoredIdentifier(): + return self.getStoredIdentifier() + + ident = None + widget = self._widget + + if hasattr(widget, "get_name"): + ident = widget.get_name() + + if not self.validateIdentifier(ident): + ident = self.getIdentifierSub() + + return self.setIdentifier(ident) + + def checkStoredIdentifier(self): + """ + Checks to see if we have previously identified this same Widget. + If we have, the Widget will have an attribute as defined by + widgetIdentifier.widgetAttribute. + """ + if hasattr(self._widget, self.widgetAttribute) \ + and getattr(self._widget, self.widgetAttribute) is not None: + return True + return False + + def getStoredIdentifier(self): + """ + If the Widget has a stored identifier (see checkStoredIdentifier), + then retrieve the stored identifier's value. + + If the Widget does not have a stored identifier, return None. + """ + if self.checkStoredIdentifier(): + return getattr(self._widget, self.widgetAttribute) + else: + return None + + def validateIdentifier(self,ident): + """ + Checks a proposed identifier against a series of criterium. For + example, empty strings, and blacklisted strings, as well as + blacklisted prefixes, may rule out an identifier for use. + """ + if ident is None: + return False + elif not isinstance(ident, str): + return False + elif len(ident) < 1: + return False + elif ident in self.dontWant: + return False + else: + for prefix in self.dontWantPrefixes: + if ident.startswith(prefix): + return False + return True + + def setIdentifier(self, ident): + """ + Sets the stored identifier for the Widget assigned by __init__. + """ + if self.validateIdentifier(ident): + setattr(self._widget, self.widgetAttribute, ident) + return ident + return None +widgetIdentifier.identifiers[gtk.Widget] = widgetIdentifier + + +class buttonIdentifier(widgetIdentifier): + def getIdentifierSub(self): + ident = None + widget = self._widget + + if hasattr(widget, "get_label"): + ident = widget.get_label() + + return ident +widgetIdentifier.identifiers[gtk.Button] = buttonIdentifier + +# class toolButtonIdentifier(widgetIdentifier): +class toolButtonIdentifier(buttonIdentifier): + def getIdentifierSub(self): + ident = buttonIdentifier.getIdentifierSub(self) + widget = self._widget + + # Get the identifier using the icon + if not self.validateIdentifier(ident): + ico = None + ico = widget.get_icon_widget() + if isinstance(ico, Icon): + ident = ico.props.icon_name + + # Label did not give us a good ident, check the icon name + if not self.validateIdentifier(ident): + ident = widget.get_icon_name() + + # Icon did not give us a good ident, try the label + if not self.validateIdentifier(ident): + label = widget.get_label_widget() + if hasattr(label,"get_text"): + ident = label.get_text() + + return ident +widgetIdentifier.identifiers[gtk.ToolButton] = toolButtonIdentifier + +class comboBoxIdentifier(widgetIdentifier): + def getIdentifierSub(self): + ident = None + widget = self._widget + + if hasattr(widget, "get_title"): + ident = self._widget.get_title() + + return ident +widgetIdentifier.identifiers[gtk.ComboBox] = comboBoxIdentifier + + +class entryIdentifier(widgetIdentifier): + def getIdentifierSub(self): + ident = None + widget = self._widget + + if not self.validateIdentifier(ident): + if hasattr(widget, "get_text"): + ident = self._widget.get_text() + + return ident +widgetIdentifier.identifiers[gtk.Entry] = entryIdentifier + +class paletteIdentifier(widgetIdentifier): + def getIdentifierSub(self): + ident = None + widget = self._widget + + if hasattr(widget, "_primary_text"): + ident = getattr(widget, "_primary_text") + + elif not self.validateIdentifier(ident): + if hasattr(widget, "props.primary_text"): + ident = getattr(widget, "props.primary_text") + + return ident +widgetIdentifier.identifiers[Palette] = paletteIdentifier + + +class toolComboBoxIdentifier(widgetIdentifier): + def getIdentifierSub(self): + ident = None + widget = self._widget + + if hasattr(widget, "_label_text"): + ident = getattr(widget, "_label_text") + print ident + + # if not self.validateIdentifier(ident): + # try: + # print "@@@" + # ident = widget.get_property("label-text") + # print ident + # except TypeError: + # raise + + # <DEPRECATED> + # if not self.validateIdentifier(ident): + # if hasattr(widget, "_label_text"): + # ident = getattr(widget, "_label_text") + # + # elif not self.validateIdentifier(ident): + # if hasattr(widget, "label"): + # label = widget.label + # ident = label.get_text() + # </DEPRECATED> + + return ident +widgetIdentifier.identifiers[ToolComboBox] = toolComboBoxIdentifier
\ No newline at end of file diff --git a/Experior.Activity/widgetIdentifier.pyc b/Experior.Activity/widgetIdentifier.pyc Binary files differnew file mode 100644 index 0000000..99312df --- /dev/null +++ b/Experior.Activity/widgetIdentifier.pyc |