diff options
author | Aleksey Lim <alsroot@sugarlabs.org> | 2013-07-07 06:02:19 (GMT) |
---|---|---|
committer | Aleksey Lim <alsroot@sugarlabs.org> | 2013-07-07 06:02:19 (GMT) |
commit | 35d777deda94fbc78324c447e16bb7abf0b11434 (patch) | |
tree | 241259c32a05c297de62e4d84d17a638d3bc4fe4 | |
parent | 0f9ae89f82a872f7449b8e2d72b7d6f63fa0180d (diff) |
Reuse node clone API command for cloning implementation from client app; process requires clone argument properly
-rw-r--r-- | doc/objects.dia | 1503 | ||||
-rw-r--r-- | sugar_network/client/cache.py | 4 | ||||
-rw-r--r-- | sugar_network/client/clones.py | 2 | ||||
-rw-r--r-- | sugar_network/client/commands.py | 2 | ||||
-rw-r--r-- | sugar_network/client/injector.py | 17 | ||||
-rw-r--r-- | sugar_network/client/solver.py | 3 | ||||
-rw-r--r-- | sugar_network/db/router.py | 30 | ||||
-rw-r--r-- | sugar_network/node/commands.py | 78 | ||||
-rw-r--r-- | sugar_network/resources/context.py | 5 | ||||
-rw-r--r-- | sugar_network/resources/implementation.py | 88 | ||||
-rw-r--r-- | sugar_network/toolkit/http.py | 52 | ||||
-rw-r--r-- | sugar_network/toolkit/spec.py | 136 | ||||
-rw-r--r-- | sugar_network/toolkit/util.py | 33 | ||||
-rw-r--r-- | tests/__init__.py | 8 | ||||
-rwxr-xr-x | tests/units/client/injector.py | 159 | ||||
-rwxr-xr-x | tests/units/client/online_commands.py | 229 | ||||
-rwxr-xr-x | tests/units/node/node.py | 122 | ||||
-rwxr-xr-x | tests/units/node/volume.py | 1 | ||||
-rwxr-xr-x | tests/units/resources/implementation.py | 106 | ||||
-rwxr-xr-x | tests/units/toolkit/spec.py | 49 |
20 files changed, 1453 insertions, 1174 deletions
diff --git a/doc/objects.dia b/doc/objects.dia index 3e75873..9444a06 100644 --- a/doc/objects.dia +++ b/doc/objects.dia @@ -2,10 +2,10 @@ <dia:diagram xmlns:dia="http://www.lysator.liu.se/~alla/dia/"> <dia:diagramdata> <dia:attribute name="background"> - <dia:color val="#ffffff"/> + <dia:color val="#ffffffff"/> </dia:attribute> <dia:attribute name="pagebreak"> - <dia:color val="#000099"/> + <dia:color val="#000099ff"/> </dia:attribute> <dia:attribute name="paper"> <dia:composite type="paper"> @@ -37,6 +37,9 @@ </dia:attribute> <dia:attribute name="grid"> <dia:composite type="grid"> + <dia:attribute name="dynamic"> + <dia:boolean val="true"/> + </dia:attribute> <dia:attribute name="width_x"> <dia:real val="0.40000000000000002"/> </dia:attribute> @@ -53,7 +56,7 @@ </dia:composite> </dia:attribute> <dia:attribute name="color"> - <dia:color val="#d8e5e5"/> + <dia:color val="#d8e5e5ff"/> </dia:attribute> <dia:attribute name="guides"> <dia:composite type="guides"> @@ -61,6 +64,25 @@ <dia:attribute name="vguides"/> </dia:composite> </dia:attribute> + <dia:attribute name="display"> + <dia:composite type="display"> + <dia:attribute name="antialiased"> + <dia:boolean val="true"/> + </dia:attribute> + <dia:attribute name="snap-to-grid"> + <dia:boolean val="true"/> + </dia:attribute> + <dia:attribute name="snap-to-object"> + <dia:boolean val="true"/> + </dia:attribute> + <dia:attribute name="show-grid"> + <dia:boolean val="false"/> + </dia:attribute> + <dia:attribute name="show-connection-points"> + <dia:boolean val="true"/> + </dia:attribute> + </dia:composite> + </dia:attribute> </dia:diagramdata> <dia:layer name="Background" visible="true" active="true"> <dia:object type="UML - Class" version="0" id="O0"> @@ -68,7 +90,7 @@ <dia:point val="30,8"/> </dia:attribute> <dia:attribute name="obj_bb"> - <dia:rectangle val="29.985,7.985;48.6675,44.415"/> + <dia:rectangle val="29.985,7.985;48.6675,42.415"/> </dia:attribute> <dia:attribute name="elem_corner"> <dia:point val="30,8"/> @@ -77,7 +99,7 @@ <dia:real val="18.6525"/> </dia:attribute> <dia:attribute name="elem_height"> - <dia:real val="36.400000000000006"/> + <dia:real val="34.400000000000006"/> </dia:attribute> <dia:attribute name="name"> <dia:string>#Context#</dia:string> @@ -118,17 +140,20 @@ <dia:attribute name="comment_tagging"> <dia:boolean val="false"/> </dia:attribute> + <dia:attribute name="allow_resizing"> + <dia:boolean val="false"/> + </dia:attribute> <dia:attribute name="line_width"> <dia:real val="0.029999999999999999"/> </dia:attribute> <dia:attribute name="line_color"> - <dia:color val="#000000"/> + <dia:color val="#000000ff"/> </dia:attribute> <dia:attribute name="fill_color"> - <dia:color val="#c1cccc"/> + <dia:color val="#c1ccccff"/> </dia:attribute> <dia:attribute name="text_color"> - <dia:color val="#000000"/> + <dia:color val="#000000ff"/> </dia:attribute> <dia:attribute name="normal_font"> <dia:font family="monospace" style="0" name="Courier"/> @@ -192,29 +217,6 @@ </dia:composite> <dia:composite type="umlattribute"> <dia:attribute name="name"> - <dia:string>#implement#</dia:string> - </dia:attribute> - <dia:attribute name="type"> - <dia:string>#[str] [R WA]#</dia:string> - </dia:attribute> - <dia:attribute name="value"> - <dia:string>#[]#</dia:string> - </dia:attribute> - <dia:attribute name="comment"> - <dia:string>#Sugar Network names that this Content associated with#</dia:string> - </dia:attribute> - <dia:attribute name="visibility"> - <dia:enum val="0"/> - </dia:attribute> - <dia:attribute name="abstract"> - <dia:boolean val="false"/> - </dia:attribute> - <dia:attribute name="class_scope"> - <dia:boolean val="false"/> - </dia:attribute> - </dia:composite> - <dia:composite type="umlattribute"> - <dia:attribute name="name"> <dia:string>#title#</dia:string> </dia:attribute> <dia:attribute name="type"> @@ -533,7 +535,7 @@ <dia:real val="11.532500000000001"/> </dia:attribute> <dia:attribute name="elem_height"> - <dia:real val="27.600000000000005"/> + <dia:real val="27.600000000000001"/> </dia:attribute> <dia:attribute name="name"> <dia:string>#User#</dia:string> @@ -574,17 +576,20 @@ <dia:attribute name="comment_tagging"> <dia:boolean val="false"/> </dia:attribute> + <dia:attribute name="allow_resizing"> + <dia:boolean val="false"/> + </dia:attribute> <dia:attribute name="line_width"> <dia:real val="0.029999999999999999"/> </dia:attribute> <dia:attribute name="line_color"> - <dia:color val="#000000"/> + <dia:color val="#000000ff"/> </dia:attribute> <dia:attribute name="fill_color"> - <dia:color val="#90ee90"/> + <dia:color val="#90ee90ff"/> </dia:attribute> <dia:attribute name="text_color"> - <dia:color val="#000000"/> + <dia:color val="#000000ff"/> </dia:attribute> <dia:attribute name="normal_font"> <dia:font family="monospace" style="0" name="Courier"/> @@ -848,7 +853,7 @@ <dia:point val="12,6"/> </dia:attribute> <dia:attribute name="elem_width"> - <dia:real val="11.755000000000001"/> + <dia:real val="11.754999999999999"/> </dia:attribute> <dia:attribute name="elem_height"> <dia:real val="20"/> @@ -892,17 +897,20 @@ <dia:attribute name="comment_tagging"> <dia:boolean val="false"/> </dia:attribute> + <dia:attribute name="allow_resizing"> + <dia:boolean val="false"/> + </dia:attribute> <dia:attribute name="line_width"> <dia:real val="0.029999999999999999"/> </dia:attribute> <dia:attribute name="line_color"> - <dia:color val="#000000"/> + <dia:color val="#000000ff"/> </dia:attribute> <dia:attribute name="fill_color"> - <dia:color val="#ffffff"/> + <dia:color val="#ffffffff"/> </dia:attribute> <dia:attribute name="text_color"> - <dia:color val="#000000"/> + <dia:color val="#000000ff"/> </dia:attribute> <dia:attribute name="normal_font"> <dia:font family="monospace" style="0" name="Courier"/> @@ -1087,6 +1095,29 @@ <dia:attribute name="templates"/> </dia:object> <dia:object type="UML - Association" version="2" id="O3"> + <dia:attribute name="obj_pos"> + <dia:point val="48.6525,33.1"/> + </dia:attribute> + <dia:attribute name="obj_bb"> + <dia:rectangle val="48.6025,32.34;54.05,68.74"/> + </dia:attribute> + <dia:attribute name="meta"> + <dia:composite type="dict"/> + </dia:attribute> + <dia:attribute name="orth_points"> + <dia:point val="48.6525,33.1"/> + <dia:point val="51,33.1"/> + <dia:point val="51,67.9"/> + <dia:point val="54,67.9"/> + </dia:attribute> + <dia:attribute name="orth_orient"> + <dia:enum val="0"/> + <dia:enum val="1"/> + <dia:enum val="0"/> + </dia:attribute> + <dia:attribute name="orth_autoroute"> + <dia:boolean val="false"/> + </dia:attribute> <dia:attribute name="name"> <dia:string>##</dia:string> </dia:attribute> @@ -1123,20 +1154,41 @@ <dia:attribute name="show_arrow_b"> <dia:boolean val="false"/> </dia:attribute> + <dia:attribute name="text_font"> + <dia:font family="monospace" style="0" name="Courier"/> + </dia:attribute> + <dia:attribute name="text_height"> + <dia:real val="0.80000000000000004"/> + </dia:attribute> + <dia:attribute name="text_colour"> + <dia:color val="#000000ff"/> + </dia:attribute> + <dia:attribute name="line_width"> + <dia:real val="0.10000000000000001"/> + </dia:attribute> + <dia:attribute name="line_colour"> + <dia:color val="#000000ff"/> + </dia:attribute> + <dia:connections> + <dia:connection handle="0" to="O0" connection="29"/> + <dia:connection handle="1" to="O14" connection="12"/> + </dia:connections> + </dia:object> + <dia:object type="UML - Association" version="2" id="O4"> <dia:attribute name="obj_pos"> - <dia:point val="48.6525,35.1"/> + <dia:point val="54,69.9"/> </dia:attribute> <dia:attribute name="obj_bb"> - <dia:rectangle val="48.6025,34.3;54.05,68.7"/> + <dia:rectangle val="46.95,67.9293;54.05,70.74"/> </dia:attribute> <dia:attribute name="meta"> <dia:composite type="dict"/> </dia:attribute> <dia:attribute name="orth_points"> - <dia:point val="48.6525,35.1"/> - <dia:point val="51,35.1"/> - <dia:point val="51,67.9"/> - <dia:point val="54,67.9"/> + <dia:point val="54,69.9"/> + <dia:point val="47,69.9"/> + <dia:point val="47,68"/> + <dia:point val="47,68"/> </dia:attribute> <dia:attribute name="orth_orient"> <dia:enum val="0"/> @@ -1146,18 +1198,6 @@ <dia:attribute name="orth_autoroute"> <dia:boolean val="false"/> </dia:attribute> - <dia:attribute name="text_colour"> - <dia:color val="#000000"/> - </dia:attribute> - <dia:attribute name="line_colour"> - <dia:color val="#000000"/> - </dia:attribute> - <dia:connections> - <dia:connection handle="0" to="O0" connection="31"/> - <dia:connection handle="1" to="O14" connection="12"/> - </dia:connections> - </dia:object> - <dia:object type="UML - Association" version="2" id="O4"> <dia:attribute name="name"> <dia:string>##</dia:string> </dia:attribute> @@ -1194,20 +1234,41 @@ <dia:attribute name="show_arrow_b"> <dia:boolean val="false"/> </dia:attribute> + <dia:attribute name="text_font"> + <dia:font family="monospace" style="0" name="Courier"/> + </dia:attribute> + <dia:attribute name="text_height"> + <dia:real val="0.80000000000000004"/> + </dia:attribute> + <dia:attribute name="text_colour"> + <dia:color val="#000000ff"/> + </dia:attribute> + <dia:attribute name="line_width"> + <dia:real val="0.10000000000000001"/> + </dia:attribute> + <dia:attribute name="line_colour"> + <dia:color val="#000000ff"/> + </dia:attribute> + <dia:connections> + <dia:connection handle="0" to="O14" connection="14"/> + <dia:connection handle="1" to="O11" connection="4"/> + </dia:connections> + </dia:object> + <dia:object type="UML - Association" version="2" id="O5"> <dia:attribute name="obj_pos"> - <dia:point val="54,69.9"/> + <dia:point val="39.3263,42.4"/> </dia:attribute> <dia:attribute name="obj_bb"> - <dia:rectangle val="46.95,67.9293;54.05,70.7"/> + <dia:rectangle val="39.2763,41.64;40.1,49.84"/> </dia:attribute> <dia:attribute name="meta"> <dia:composite type="dict"/> </dia:attribute> <dia:attribute name="orth_points"> - <dia:point val="54,69.9"/> - <dia:point val="47,69.9"/> - <dia:point val="47,68"/> - <dia:point val="47,68"/> + <dia:point val="39.3263,42.4"/> + <dia:point val="40,42.4"/> + <dia:point val="40,49"/> + <dia:point val="39.5,49"/> </dia:attribute> <dia:attribute name="orth_orient"> <dia:enum val="0"/> @@ -1217,18 +1278,6 @@ <dia:attribute name="orth_autoroute"> <dia:boolean val="false"/> </dia:attribute> - <dia:attribute name="text_colour"> - <dia:color val="#000000"/> - </dia:attribute> - <dia:attribute name="line_colour"> - <dia:color val="#000000"/> - </dia:attribute> - <dia:connections> - <dia:connection handle="0" to="O14" connection="14"/> - <dia:connection handle="1" to="O11" connection="4"/> - </dia:connections> - </dia:object> - <dia:object type="UML - Association" version="2" id="O5"> <dia:attribute name="name"> <dia:string>##</dia:string> </dia:attribute> @@ -1265,20 +1314,41 @@ <dia:attribute name="show_arrow_b"> <dia:boolean val="false"/> </dia:attribute> + <dia:attribute name="text_font"> + <dia:font family="monospace" style="0" name="Courier"/> + </dia:attribute> + <dia:attribute name="text_height"> + <dia:real val="0.80000000000000004"/> + </dia:attribute> + <dia:attribute name="text_colour"> + <dia:color val="#000000ff"/> + </dia:attribute> + <dia:attribute name="line_width"> + <dia:real val="0.10000000000000001"/> + </dia:attribute> + <dia:attribute name="line_colour"> + <dia:color val="#000000ff"/> + </dia:attribute> + <dia:connections> + <dia:connection handle="0" to="O0" connection="6"/> + <dia:connection handle="1" to="O11" connection="1"/> + </dia:connections> + </dia:object> + <dia:object type="UML - Association" version="2" id="O6"> <dia:attribute name="obj_pos"> - <dia:point val="39.3263,44.4"/> + <dia:point val="30,33.1"/> </dia:attribute> <dia:attribute name="obj_bb"> - <dia:rectangle val="39.2763,43.6;40.1,49.8"/> + <dia:rectangle val="26.385,32.34;30.05,73.14"/> </dia:attribute> <dia:attribute name="meta"> <dia:composite type="dict"/> </dia:attribute> <dia:attribute name="orth_points"> - <dia:point val="39.3263,44.4"/> - <dia:point val="40,44.4"/> - <dia:point val="40,49"/> - <dia:point val="39.5,49"/> + <dia:point val="30,33.1"/> + <dia:point val="28.2175,33.1"/> + <dia:point val="28.2175,72.3"/> + <dia:point val="26.435,72.3"/> </dia:attribute> <dia:attribute name="orth_orient"> <dia:enum val="0"/> @@ -1286,20 +1356,8 @@ <dia:enum val="0"/> </dia:attribute> <dia:attribute name="orth_autoroute"> - <dia:boolean val="false"/> - </dia:attribute> - <dia:attribute name="text_colour"> - <dia:color val="#000000"/> - </dia:attribute> - <dia:attribute name="line_colour"> - <dia:color val="#000000"/> + <dia:boolean val="true"/> </dia:attribute> - <dia:connections> - <dia:connection handle="0" to="O0" connection="6"/> - <dia:connection handle="1" to="O11" connection="1"/> - </dia:connections> - </dia:object> - <dia:object type="UML - Association" version="2" id="O6"> <dia:attribute name="name"> <dia:string>##</dia:string> </dia:attribute> @@ -1336,20 +1394,41 @@ <dia:attribute name="show_arrow_b"> <dia:boolean val="false"/> </dia:attribute> + <dia:attribute name="text_font"> + <dia:font family="monospace" style="0" name="Courier"/> + </dia:attribute> + <dia:attribute name="text_height"> + <dia:real val="0.80000000000000004"/> + </dia:attribute> + <dia:attribute name="text_colour"> + <dia:color val="#000000ff"/> + </dia:attribute> + <dia:attribute name="line_width"> + <dia:real val="0.10000000000000001"/> + </dia:attribute> + <dia:attribute name="line_colour"> + <dia:color val="#000000ff"/> + </dia:attribute> + <dia:connections> + <dia:connection handle="0" to="O0" connection="28"/> + <dia:connection handle="1" to="O8" connection="11"/> + </dia:connections> + </dia:object> + <dia:object type="UML - Association" version="2" id="O7"> <dia:attribute name="obj_pos"> - <dia:point val="30,35.1"/> + <dia:point val="26.435,75.1"/> </dia:attribute> <dia:attribute name="obj_bb"> - <dia:rectangle val="26.385,34.3;30.05,73.1"/> + <dia:rectangle val="26.385,67.24;32.0516,75.94"/> </dia:attribute> <dia:attribute name="meta"> <dia:composite type="dict"/> </dia:attribute> <dia:attribute name="orth_points"> - <dia:point val="30,35.1"/> - <dia:point val="28.2175,35.1"/> - <dia:point val="28.2175,72.3"/> - <dia:point val="26.435,72.3"/> + <dia:point val="26.435,75.1"/> + <dia:point val="30,75.1"/> + <dia:point val="30,68"/> + <dia:point val="32.0016,68"/> </dia:attribute> <dia:attribute name="orth_orient"> <dia:enum val="0"/> @@ -1357,20 +1436,8 @@ <dia:enum val="0"/> </dia:attribute> <dia:attribute name="orth_autoroute"> - <dia:boolean val="true"/> - </dia:attribute> - <dia:attribute name="text_colour"> - <dia:color val="#000000"/> - </dia:attribute> - <dia:attribute name="line_colour"> - <dia:color val="#000000"/> + <dia:boolean val="false"/> </dia:attribute> - <dia:connections> - <dia:connection handle="0" to="O0" connection="30"/> - <dia:connection handle="1" to="O8" connection="11"/> - </dia:connections> - </dia:object> - <dia:object type="UML - Association" version="2" id="O7"> <dia:attribute name="name"> <dia:string>##</dia:string> </dia:attribute> @@ -1407,34 +1474,20 @@ <dia:attribute name="show_arrow_b"> <dia:boolean val="false"/> </dia:attribute> - <dia:attribute name="obj_pos"> - <dia:point val="26.435,75.1"/> - </dia:attribute> - <dia:attribute name="obj_bb"> - <dia:rectangle val="26.385,67.2;32,75.9"/> - </dia:attribute> - <dia:attribute name="meta"> - <dia:composite type="dict"/> - </dia:attribute> - <dia:attribute name="orth_points"> - <dia:point val="26.435,75.1"/> - <dia:point val="30,75.1"/> - <dia:point val="30,68"/> - <dia:point val="31.95,68"/> - </dia:attribute> - <dia:attribute name="orth_orient"> - <dia:enum val="0"/> - <dia:enum val="1"/> - <dia:enum val="0"/> + <dia:attribute name="text_font"> + <dia:font family="monospace" style="0" name="Courier"/> </dia:attribute> - <dia:attribute name="orth_autoroute"> - <dia:boolean val="false"/> + <dia:attribute name="text_height"> + <dia:real val="0.80000000000000004"/> </dia:attribute> <dia:attribute name="text_colour"> - <dia:color val="#000000"/> + <dia:color val="#000000ff"/> + </dia:attribute> + <dia:attribute name="line_width"> + <dia:real val="0.10000000000000001"/> </dia:attribute> <dia:attribute name="line_colour"> - <dia:color val="#000000"/> + <dia:color val="#000000ff"/> </dia:attribute> <dia:connections> <dia:connection handle="0" to="O8" connection="13"/> @@ -1452,10 +1505,10 @@ <dia:point val="14,66"/> </dia:attribute> <dia:attribute name="elem_width"> - <dia:real val="12.435"/> + <dia:real val="12.434999999999999"/> </dia:attribute> <dia:attribute name="elem_height"> - <dia:real val="14.799999999999999"/> + <dia:real val="14.800000000000001"/> </dia:attribute> <dia:attribute name="name"> <dia:string>#Feedback#</dia:string> @@ -1496,17 +1549,20 @@ <dia:attribute name="comment_tagging"> <dia:boolean val="false"/> </dia:attribute> + <dia:attribute name="allow_resizing"> + <dia:boolean val="false"/> + </dia:attribute> <dia:attribute name="line_width"> <dia:real val="0.029999999999999999"/> </dia:attribute> <dia:attribute name="line_color"> - <dia:color val="#000000"/> + <dia:color val="#000000ff"/> </dia:attribute> <dia:attribute name="fill_color"> - <dia:color val="#d9e6e6"/> + <dia:color val="#d9e6e6ff"/> </dia:attribute> <dia:attribute name="text_color"> - <dia:color val="#000000"/> + <dia:color val="#000000ff"/> </dia:attribute> <dia:attribute name="normal_font"> <dia:font family="monospace" style="0" name="Courier"/> @@ -1722,17 +1778,20 @@ <dia:attribute name="comment_tagging"> <dia:boolean val="false"/> </dia:attribute> + <dia:attribute name="allow_resizing"> + <dia:boolean val="false"/> + </dia:attribute> <dia:attribute name="line_width"> <dia:real val="0.029999999999999999"/> </dia:attribute> <dia:attribute name="line_color"> - <dia:color val="#000000"/> + <dia:color val="#000000ff"/> </dia:attribute> <dia:attribute name="fill_color"> - <dia:color val="#d8e5e5"/> + <dia:color val="#d8e5e5ff"/> </dia:attribute> <dia:attribute name="text_color"> - <dia:color val="#000000"/> + <dia:color val="#000000ff"/> </dia:attribute> <dia:attribute name="normal_font"> <dia:font family="monospace" style="0" name="Courier"/> @@ -1848,6 +1907,29 @@ <dia:attribute name="templates"/> </dia:object> <dia:object type="UML - Association" version="2" id="O10"> + <dia:attribute name="obj_pos"> + <dia:point val="14,72.3"/> + </dia:attribute> + <dia:attribute name="obj_bb"> + <dia:rectangle val="9.6625,72.25;14.2,76.1"/> + </dia:attribute> + <dia:attribute name="meta"> + <dia:composite type="dict"/> + </dia:attribute> + <dia:attribute name="orth_points"> + <dia:point val="14,72.3"/> + <dia:point val="14,75"/> + <dia:point val="9.7125,75"/> + <dia:point val="9.7125,74.5"/> + </dia:attribute> + <dia:attribute name="orth_orient"> + <dia:enum val="1"/> + <dia:enum val="0"/> + <dia:enum val="1"/> + </dia:attribute> + <dia:attribute name="orth_autoroute"> + <dia:boolean val="false"/> + </dia:attribute> <dia:attribute name="name"> <dia:string>##</dia:string> </dia:attribute> @@ -1884,34 +1966,20 @@ <dia:attribute name="show_arrow_b"> <dia:boolean val="false"/> </dia:attribute> - <dia:attribute name="obj_pos"> - <dia:point val="14,72.3"/> - </dia:attribute> - <dia:attribute name="obj_bb"> - <dia:rectangle val="9.6625,72.25;14.2,76.1"/> - </dia:attribute> - <dia:attribute name="meta"> - <dia:composite type="dict"/> - </dia:attribute> - <dia:attribute name="orth_points"> - <dia:point val="14,72.3"/> - <dia:point val="14,75"/> - <dia:point val="9.7125,75"/> - <dia:point val="9.7125,74.5"/> - </dia:attribute> - <dia:attribute name="orth_orient"> - <dia:enum val="1"/> - <dia:enum val="0"/> - <dia:enum val="1"/> + <dia:attribute name="text_font"> + <dia:font family="monospace" style="0" name="Courier"/> </dia:attribute> - <dia:attribute name="orth_autoroute"> - <dia:boolean val="false"/> + <dia:attribute name="text_height"> + <dia:real val="0.80000000000000004"/> </dia:attribute> <dia:attribute name="text_colour"> - <dia:color val="#000000"/> + <dia:color val="#000000ff"/> + </dia:attribute> + <dia:attribute name="line_width"> + <dia:real val="0.10000000000000001"/> </dia:attribute> <dia:attribute name="line_colour"> - <dia:color val="#000000"/> + <dia:color val="#000000ff"/> </dia:attribute> <dia:connections> <dia:connection handle="0" to="O8" connection="10"/> @@ -1937,23 +2005,29 @@ <dia:attribute name="elem_height"> <dia:real val="38"/> </dia:attribute> - <dia:attribute name="line_width"> - <dia:real val="0.035277776420116425"/> + <dia:attribute name="name"> + <dia:string>#Common objects #</dia:string> </dia:attribute> - <dia:attribute name="line_colour"> - <dia:color val="#000000"/> + <dia:attribute name="stereotype"> + <dia:string>##</dia:string> </dia:attribute> - <dia:attribute name="fill_colour"> - <dia:color val="#f3f3f3"/> + <dia:attribute name="text_font"> + <dia:font family="monospace" style="0" name="Courier"/> + </dia:attribute> + <dia:attribute name="text_height"> + <dia:real val="0.80000000000000004"/> </dia:attribute> <dia:attribute name="text_colour"> - <dia:color val="#000000"/> + <dia:color val="#000000ff"/> </dia:attribute> - <dia:attribute name="stereotype"> - <dia:string>##</dia:string> + <dia:attribute name="line_width"> + <dia:real val="0.035277776420116425"/> </dia:attribute> - <dia:attribute name="name"> - <dia:string>#Common objects #</dia:string> + <dia:attribute name="line_colour"> + <dia:color val="#000000ff"/> + </dia:attribute> + <dia:attribute name="fill_colour"> + <dia:color val="#f3f3f3ff"/> </dia:attribute> </dia:object> <dia:object type="UML - Class" version="0" id="O12"> @@ -1961,16 +2035,16 @@ <dia:point val="33,69"/> </dia:attribute> <dia:attribute name="obj_bb"> - <dia:rectangle val="32.985,68.985;45.63,85.415"/> + <dia:rectangle val="32.985,68.985;45.645,85.415"/> </dia:attribute> <dia:attribute name="elem_corner"> <dia:point val="33,69"/> </dia:attribute> <dia:attribute name="elem_width"> - <dia:real val="12.615"/> + <dia:real val="12.629999999999999"/> </dia:attribute> <dia:attribute name="elem_height"> - <dia:real val="16.399999999999999"/> + <dia:real val="16.400000000000002"/> </dia:attribute> <dia:attribute name="name"> <dia:string>#Comment#</dia:string> @@ -2011,17 +2085,20 @@ <dia:attribute name="comment_tagging"> <dia:boolean val="false"/> </dia:attribute> + <dia:attribute name="allow_resizing"> + <dia:boolean val="false"/> + </dia:attribute> <dia:attribute name="line_width"> <dia:real val="0.029999999999999999"/> </dia:attribute> <dia:attribute name="line_color"> - <dia:color val="#000000"/> + <dia:color val="#000000ff"/> </dia:attribute> <dia:attribute name="fill_color"> - <dia:color val="#f1ffff"/> + <dia:color val="#f1ffffff"/> </dia:attribute> <dia:attribute name="text_color"> - <dia:color val="#000000"/> + <dia:color val="#000000ff"/> </dia:attribute> <dia:attribute name="normal_font"> <dia:font family="monospace" style="0" name="Courier"/> @@ -2237,17 +2314,20 @@ <dia:attribute name="comment_tagging"> <dia:boolean val="false"/> </dia:attribute> + <dia:attribute name="allow_resizing"> + <dia:boolean val="false"/> + </dia:attribute> <dia:attribute name="line_width"> <dia:real val="0.029999999999999999"/> </dia:attribute> <dia:attribute name="line_color"> - <dia:color val="#000000"/> + <dia:color val="#000000ff"/> </dia:attribute> <dia:attribute name="fill_color"> - <dia:color val="#f1ffff"/> + <dia:color val="#f1ffffff"/> </dia:attribute> <dia:attribute name="text_color"> - <dia:color val="#000000"/> + <dia:color val="#000000ff"/> </dia:attribute> <dia:attribute name="normal_font"> <dia:font family="monospace" style="0" name="Courier"/> @@ -2463,17 +2543,20 @@ <dia:attribute name="comment_tagging"> <dia:boolean val="false"/> </dia:attribute> + <dia:attribute name="allow_resizing"> + <dia:boolean val="false"/> + </dia:attribute> <dia:attribute name="line_width"> <dia:real val="0.029999999999999999"/> </dia:attribute> <dia:attribute name="line_color"> - <dia:color val="#000000"/> + <dia:color val="#000000ff"/> </dia:attribute> <dia:attribute name="fill_color"> - <dia:color val="#d8e5e5"/> + <dia:color val="#d8e5e5ff"/> </dia:attribute> <dia:attribute name="text_color"> - <dia:color val="#000000"/> + <dia:color val="#000000ff"/> </dia:attribute> <dia:attribute name="normal_font"> <dia:font family="monospace" style="0" name="Courier"/> @@ -2827,17 +2910,20 @@ <dia:attribute name="comment_tagging"> <dia:boolean val="false"/> </dia:attribute> + <dia:attribute name="allow_resizing"> + <dia:boolean val="false"/> + </dia:attribute> <dia:attribute name="line_width"> <dia:real val="0.029999999999999999"/> </dia:attribute> <dia:attribute name="line_color"> - <dia:color val="#000000"/> + <dia:color val="#000000ff"/> </dia:attribute> <dia:attribute name="fill_color"> - <dia:color val="#d8e5e5"/> + <dia:color val="#d8e5e5ff"/> </dia:attribute> <dia:attribute name="text_color"> - <dia:color val="#000000"/> + <dia:color val="#000000ff"/> </dia:attribute> <dia:attribute name="normal_font"> <dia:font family="monospace" style="0" name="Courier"/> @@ -3035,7 +3121,7 @@ <dia:real val="17.055"/> </dia:attribute> <dia:attribute name="elem_height"> - <dia:real val="16"/> + <dia:real val="16.000000000000004"/> </dia:attribute> <dia:attribute name="name"> <dia:string>#Report#</dia:string> @@ -3076,17 +3162,20 @@ <dia:attribute name="comment_tagging"> <dia:boolean val="false"/> </dia:attribute> + <dia:attribute name="allow_resizing"> + <dia:boolean val="false"/> + </dia:attribute> <dia:attribute name="line_width"> <dia:real val="0.029999999999999999"/> </dia:attribute> <dia:attribute name="line_color"> - <dia:color val="#000000"/> + <dia:color val="#000000ff"/> </dia:attribute> <dia:attribute name="fill_color"> - <dia:color val="#d8e5e5"/> + <dia:color val="#d8e5e5ff"/> </dia:attribute> <dia:attribute name="text_color"> - <dia:color val="#000000"/> + <dia:color val="#000000ff"/> </dia:attribute> <dia:attribute name="normal_font"> <dia:font family="monospace" style="0" name="Courier"/> @@ -3225,6 +3314,29 @@ <dia:attribute name="templates"/> </dia:object> <dia:object type="UML - Association" version="2" id="O17"> + <dia:attribute name="obj_pos"> + <dia:point val="14,51.5"/> + </dia:attribute> + <dia:attribute name="obj_bb"> + <dia:rectangle val="9.005,51.4293;14.2,55.94"/> + </dia:attribute> + <dia:attribute name="meta"> + <dia:composite type="dict"/> + </dia:attribute> + <dia:attribute name="orth_points"> + <dia:point val="14,51.5"/> + <dia:point val="14,51.5"/> + <dia:point val="14,55.1"/> + <dia:point val="9.055,55.1"/> + </dia:attribute> + <dia:attribute name="orth_orient"> + <dia:enum val="0"/> + <dia:enum val="1"/> + <dia:enum val="0"/> + </dia:attribute> + <dia:attribute name="orth_autoroute"> + <dia:boolean val="false"/> + </dia:attribute> <dia:attribute name="name"> <dia:string>##</dia:string> </dia:attribute> @@ -3261,20 +3373,350 @@ <dia:attribute name="show_arrow_b"> <dia:boolean val="false"/> </dia:attribute> + <dia:attribute name="text_font"> + <dia:font family="monospace" style="0" name="Courier"/> + </dia:attribute> + <dia:attribute name="text_height"> + <dia:real val="0.80000000000000004"/> + </dia:attribute> + <dia:attribute name="text_colour"> + <dia:color val="#000000ff"/> + </dia:attribute> + <dia:attribute name="line_width"> + <dia:real val="0.10000000000000001"/> + </dia:attribute> + <dia:attribute name="line_colour"> + <dia:color val="#000000ff"/> + </dia:attribute> + <dia:connections> + <dia:connection handle="0" to="O15" connection="12"/> + <dia:connection handle="1" to="O16" connection="11"/> + </dia:connections> + </dia:object> + <dia:object type="UML - Association" version="2" id="O18"> <dia:attribute name="obj_pos"> - <dia:point val="14,51.5"/> + <dia:point val="30,31.1"/> </dia:attribute> <dia:attribute name="obj_bb"> - <dia:rectangle val="9.005,51.5;14.2,55.9"/> + <dia:rectangle val="19.7562,31.05;30.2,40.8"/> </dia:attribute> <dia:attribute name="meta"> <dia:composite type="dict"/> </dia:attribute> <dia:attribute name="orth_points"> - <dia:point val="14,51.5"/> - <dia:point val="14,51.5"/> - <dia:point val="14,55.1"/> - <dia:point val="9.055,55.1"/> + <dia:point val="30,31.1"/> + <dia:point val="30,33"/> + <dia:point val="19.8062,33"/> + <dia:point val="19.8062,40"/> + </dia:attribute> + <dia:attribute name="orth_orient"> + <dia:enum val="1"/> + <dia:enum val="0"/> + <dia:enum val="1"/> + </dia:attribute> + <dia:attribute name="orth_autoroute"> + <dia:boolean val="false"/> + </dia:attribute> + <dia:attribute name="name"> + <dia:string>##</dia:string> + </dia:attribute> + <dia:attribute name="direction"> + <dia:enum val="0"/> + </dia:attribute> + <dia:attribute name="show_direction"> + <dia:boolean val="true"/> + </dia:attribute> + <dia:attribute name="assoc_type"> + <dia:enum val="0"/> + </dia:attribute> + <dia:attribute name="role_a"> + <dia:string>##</dia:string> + </dia:attribute> + <dia:attribute name="multipicity_a"> + <dia:string>##</dia:string> + </dia:attribute> + <dia:attribute name="visibility_a"> + <dia:enum val="0"/> + </dia:attribute> + <dia:attribute name="show_arrow_a"> + <dia:boolean val="false"/> + </dia:attribute> + <dia:attribute name="role_b"> + <dia:string>##</dia:string> + </dia:attribute> + <dia:attribute name="multipicity_b"> + <dia:string>#*#</dia:string> + </dia:attribute> + <dia:attribute name="visibility_b"> + <dia:enum val="0"/> + </dia:attribute> + <dia:attribute name="show_arrow_b"> + <dia:boolean val="false"/> + </dia:attribute> + <dia:attribute name="text_font"> + <dia:font family="monospace" style="0" name="Courier"/> + </dia:attribute> + <dia:attribute name="text_height"> + <dia:real val="0.80000000000000004"/> + </dia:attribute> + <dia:attribute name="text_colour"> + <dia:color val="#000000ff"/> + </dia:attribute> + <dia:attribute name="line_width"> + <dia:real val="0.10000000000000001"/> + </dia:attribute> + <dia:attribute name="line_colour"> + <dia:color val="#000000ff"/> + </dia:attribute> + <dia:connections> + <dia:connection handle="0" to="O0" connection="26"/> + <dia:connection handle="1" to="O15" connection="1"/> + </dia:connections> + </dia:object> + <dia:object type="UML - Class" version="0" id="O19"> + <dia:attribute name="obj_pos"> + <dia:point val="54,40"/> + </dia:attribute> + <dia:attribute name="obj_bb"> + <dia:rectangle val="53.985,39.985;66.45,55.615"/> + </dia:attribute> + <dia:attribute name="elem_corner"> + <dia:point val="54,40"/> + </dia:attribute> + <dia:attribute name="elem_width"> + <dia:real val="12.434999999999999"/> + </dia:attribute> + <dia:attribute name="elem_height"> + <dia:real val="15.600000000000001"/> + </dia:attribute> + <dia:attribute name="name"> + <dia:string>#Review#</dia:string> + </dia:attribute> + <dia:attribute name="stereotype"> + <dia:string>#Resource#</dia:string> + </dia:attribute> + <dia:attribute name="comment"> + <dia:string>#Users' reviews#</dia:string> + </dia:attribute> + <dia:attribute name="abstract"> + <dia:boolean val="false"/> + </dia:attribute> + <dia:attribute name="suppress_attributes"> + <dia:boolean val="false"/> + </dia:attribute> + <dia:attribute name="suppress_operations"> + <dia:boolean val="false"/> + </dia:attribute> + <dia:attribute name="visible_attributes"> + <dia:boolean val="true"/> + </dia:attribute> + <dia:attribute name="visible_operations"> + <dia:boolean val="false"/> + </dia:attribute> + <dia:attribute name="visible_comments"> + <dia:boolean val="true"/> + </dia:attribute> + <dia:attribute name="wrap_operations"> + <dia:boolean val="false"/> + </dia:attribute> + <dia:attribute name="wrap_after_char"> + <dia:int val="35"/> + </dia:attribute> + <dia:attribute name="comment_line_length"> + <dia:int val="35"/> + </dia:attribute> + <dia:attribute name="comment_tagging"> + <dia:boolean val="false"/> + </dia:attribute> + <dia:attribute name="allow_resizing"> + <dia:boolean val="false"/> + </dia:attribute> + <dia:attribute name="line_width"> + <dia:real val="0.029999999999999999"/> + </dia:attribute> + <dia:attribute name="line_color"> + <dia:color val="#000000ff"/> + </dia:attribute> + <dia:attribute name="fill_color"> + <dia:color val="#d9e6e6ff"/> + </dia:attribute> + <dia:attribute name="text_color"> + <dia:color val="#000000ff"/> + </dia:attribute> + <dia:attribute name="normal_font"> + <dia:font family="monospace" style="0" name="Courier"/> + </dia:attribute> + <dia:attribute name="abstract_font"> + <dia:font family="monospace" style="0" name="Courier"/> + </dia:attribute> + <dia:attribute name="polymorphic_font"> + <dia:font family="monospace" style="0" name="Courier"/> + </dia:attribute> + <dia:attribute name="classname_font"> + <dia:font family="sans" style="80" name="Helvetica-Bold"/> + </dia:attribute> + <dia:attribute name="abstract_classname_font"> + <dia:font family="sans" style="88" name="Helvetica-BoldOblique"/> + </dia:attribute> + <dia:attribute name="comment_font"> + <dia:font family="sans" style="8" name="Helvetica-Oblique"/> + </dia:attribute> + <dia:attribute name="normal_font_height"> + <dia:real val="0.80000000000000004"/> + </dia:attribute> + <dia:attribute name="polymorphic_font_height"> + <dia:real val="0.80000000000000004"/> + </dia:attribute> + <dia:attribute name="abstract_font_height"> + <dia:real val="1"/> + </dia:attribute> + <dia:attribute name="classname_font_height"> + <dia:real val="1"/> + </dia:attribute> + <dia:attribute name="abstract_classname_font_height"> + <dia:real val="1"/> + </dia:attribute> + <dia:attribute name="comment_font_height"> + <dia:real val="0.80000000000000004"/> + </dia:attribute> + <dia:attribute name="attributes"> + <dia:composite type="umlattribute"> + <dia:attribute name="name"> + <dia:string>#context#</dia:string> + </dia:attribute> + <dia:attribute name="type"> + <dia:string>#Context [R WN]#</dia:string> + </dia:attribute> + <dia:attribute name="value"> + <dia:string>##</dia:string> + </dia:attribute> + <dia:attribute name="comment"> + <dia:string>#The Context this Feedback belongs to#</dia:string> + </dia:attribute> + <dia:attribute name="visibility"> + <dia:enum val="0"/> + </dia:attribute> + <dia:attribute name="abstract"> + <dia:boolean val="false"/> + </dia:attribute> + <dia:attribute name="class_scope"> + <dia:boolean val="false"/> + </dia:attribute> + </dia:composite> + <dia:composite type="umlattribute"> + <dia:attribute name="name"> + <dia:string>#artifact#</dia:string> + </dia:attribute> + <dia:attribute name="type"> + <dia:string>#Artifact [R WN]#</dia:string> + </dia:attribute> + <dia:attribute name="value"> + <dia:string>#""#</dia:string> + </dia:attribute> + <dia:attribute name="comment"> + <dia:string>#If specified, the Review was created for an Artifact#</dia:string> + </dia:attribute> + <dia:attribute name="visibility"> + <dia:enum val="0"/> + </dia:attribute> + <dia:attribute name="abstract"> + <dia:boolean val="false"/> + </dia:attribute> + <dia:attribute name="class_scope"> + <dia:boolean val="false"/> + </dia:attribute> + </dia:composite> + <dia:composite type="umlattribute"> + <dia:attribute name="name"> + <dia:string>#title#</dia:string> + </dia:attribute> + <dia:attribute name="type"> + <dia:string>#str [R WN F I]#</dia:string> + </dia:attribute> + <dia:attribute name="value"> + <dia:string>##</dia:string> + </dia:attribute> + <dia:attribute name="comment"> + <dia:string>#One line summary#</dia:string> + </dia:attribute> + <dia:attribute name="visibility"> + <dia:enum val="0"/> + </dia:attribute> + <dia:attribute name="abstract"> + <dia:boolean val="false"/> + </dia:attribute> + <dia:attribute name="class_scope"> + <dia:boolean val="false"/> + </dia:attribute> + </dia:composite> + <dia:composite type="umlattribute"> + <dia:attribute name="name"> + <dia:string>#content#</dia:string> + </dia:attribute> + <dia:attribute name="type"> + <dia:string>#wikitext [R WN F I]#</dia:string> + </dia:attribute> + <dia:attribute name="value"> + <dia:string>##</dia:string> + </dia:attribute> + <dia:attribute name="comment"> + <dia:string>#The content of the Review#</dia:string> + </dia:attribute> + <dia:attribute name="visibility"> + <dia:enum val="0"/> + </dia:attribute> + <dia:attribute name="abstract"> + <dia:boolean val="false"/> + </dia:attribute> + <dia:attribute name="class_scope"> + <dia:boolean val="false"/> + </dia:attribute> + </dia:composite> + <dia:composite type="umlattribute"> + <dia:attribute name="name"> + <dia:string>#rating#</dia:string> + </dia:attribute> + <dia:attribute name="type"> + <dia:string>#int [R WN S]#</dia:string> + </dia:attribute> + <dia:attribute name="value"> + <dia:string>##</dia:string> + </dia:attribute> + <dia:attribute name="comment"> + <dia:string>#Reviewer' rating, value from 1 to 5.#</dia:string> + </dia:attribute> + <dia:attribute name="visibility"> + <dia:enum val="0"/> + </dia:attribute> + <dia:attribute name="abstract"> + <dia:boolean val="false"/> + </dia:attribute> + <dia:attribute name="class_scope"> + <dia:boolean val="false"/> + </dia:attribute> + </dia:composite> + </dia:attribute> + <dia:attribute name="operations"/> + <dia:attribute name="template"> + <dia:boolean val="false"/> + </dia:attribute> + <dia:attribute name="templates"/> + </dia:object> + <dia:object type="UML - Association" version="2" id="O20"> + <dia:attribute name="obj_pos"> + <dia:point val="48.6525,33.1"/> + </dia:attribute> + <dia:attribute name="obj_bb"> + <dia:rectangle val="48.6025,32.34;60.2675,40.84"/> + </dia:attribute> + <dia:attribute name="meta"> + <dia:composite type="dict"/> + </dia:attribute> + <dia:attribute name="orth_points"> + <dia:point val="48.6525,33.1"/> + <dia:point val="60,33.1"/> + <dia:point val="60,40"/> + <dia:point val="60.2175,40"/> </dia:attribute> <dia:attribute name="orth_orient"> <dia:enum val="0"/> @@ -3284,19 +3726,145 @@ <dia:attribute name="orth_autoroute"> <dia:boolean val="false"/> </dia:attribute> + <dia:attribute name="name"> + <dia:string>##</dia:string> + </dia:attribute> + <dia:attribute name="direction"> + <dia:enum val="0"/> + </dia:attribute> + <dia:attribute name="show_direction"> + <dia:boolean val="true"/> + </dia:attribute> + <dia:attribute name="assoc_type"> + <dia:enum val="0"/> + </dia:attribute> + <dia:attribute name="role_a"> + <dia:string>##</dia:string> + </dia:attribute> + <dia:attribute name="multipicity_a"> + <dia:string>##</dia:string> + </dia:attribute> + <dia:attribute name="visibility_a"> + <dia:enum val="0"/> + </dia:attribute> + <dia:attribute name="show_arrow_a"> + <dia:boolean val="false"/> + </dia:attribute> + <dia:attribute name="role_b"> + <dia:string>##</dia:string> + </dia:attribute> + <dia:attribute name="multipicity_b"> + <dia:string>#*#</dia:string> + </dia:attribute> + <dia:attribute name="visibility_b"> + <dia:enum val="0"/> + </dia:attribute> + <dia:attribute name="show_arrow_b"> + <dia:boolean val="false"/> + </dia:attribute> + <dia:attribute name="text_font"> + <dia:font family="monospace" style="0" name="Courier"/> + </dia:attribute> + <dia:attribute name="text_height"> + <dia:real val="0.80000000000000004"/> + </dia:attribute> <dia:attribute name="text_colour"> - <dia:color val="#000000"/> + <dia:color val="#000000ff"/> + </dia:attribute> + <dia:attribute name="line_width"> + <dia:real val="0.10000000000000001"/> </dia:attribute> <dia:attribute name="line_colour"> - <dia:color val="#000000"/> + <dia:color val="#000000ff"/> </dia:attribute> <dia:connections> - <dia:connection handle="0" to="O15" connection="12"/> - <dia:connection handle="1" to="O16" connection="11"/> + <dia:connection handle="0" to="O0" connection="29"/> + <dia:connection handle="1" to="O19" connection="1"/> + </dia:connections> + </dia:object> + <dia:object type="UML - Association" version="2" id="O21"> + <dia:attribute name="obj_pos"> + <dia:point val="61.565,58"/> + </dia:attribute> + <dia:attribute name="obj_bb"> + <dia:rectangle val="59.6325,54.84;61.615,58.84"/> + </dia:attribute> + <dia:attribute name="meta"> + <dia:composite type="dict"/> + </dia:attribute> + <dia:attribute name="orth_points"> + <dia:point val="61.565,58"/> + <dia:point val="60,58"/> + <dia:point val="60,55.6"/> + <dia:point val="60.2175,55.6"/> + </dia:attribute> + <dia:attribute name="orth_orient"> + <dia:enum val="0"/> + <dia:enum val="1"/> + <dia:enum val="0"/> + </dia:attribute> + <dia:attribute name="orth_autoroute"> + <dia:boolean val="false"/> + </dia:attribute> + <dia:attribute name="name"> + <dia:string>##</dia:string> + </dia:attribute> + <dia:attribute name="direction"> + <dia:enum val="0"/> + </dia:attribute> + <dia:attribute name="show_direction"> + <dia:boolean val="true"/> + </dia:attribute> + <dia:attribute name="assoc_type"> + <dia:enum val="0"/> + </dia:attribute> + <dia:attribute name="role_a"> + <dia:string>##</dia:string> + </dia:attribute> + <dia:attribute name="multipicity_a"> + <dia:string>##</dia:string> + </dia:attribute> + <dia:attribute name="visibility_a"> + <dia:enum val="0"/> + </dia:attribute> + <dia:attribute name="show_arrow_a"> + <dia:boolean val="false"/> + </dia:attribute> + <dia:attribute name="role_b"> + <dia:string>##</dia:string> + </dia:attribute> + <dia:attribute name="multipicity_b"> + <dia:string>#*#</dia:string> + </dia:attribute> + <dia:attribute name="visibility_b"> + <dia:enum val="0"/> + </dia:attribute> + <dia:attribute name="show_arrow_b"> + <dia:boolean val="false"/> + </dia:attribute> + <dia:attribute name="text_font"> + <dia:font family="monospace" style="0" name="Courier"/> + </dia:attribute> + <dia:attribute name="text_height"> + <dia:real val="0.80000000000000004"/> + </dia:attribute> + <dia:attribute name="text_colour"> + <dia:color val="#000000ff"/> + </dia:attribute> + <dia:attribute name="line_width"> + <dia:real val="0.10000000000000001"/> + </dia:attribute> + <dia:attribute name="line_colour"> + <dia:color val="#000000ff"/> + </dia:attribute> + <dia:connections> + <dia:connection handle="0" to="O14" connection="1"/> + <dia:connection handle="1" to="O19" connection="6"/> </dia:connections> </dia:object> <dia:group> - <dia:object type="Standard - Box" version="0" id="O18"> + <dia:attribute name="matrix"/> + <dia:object type="Standard - Box" version="0" id="O22"> <dia:attribute name="obj_pos"> <dia:point val="62,8"/> </dia:attribute> @@ -3316,7 +3884,7 @@ <dia:real val="0.0010583332689479003"/> </dia:attribute> <dia:attribute name="inner_color"> - <dia:color val="#ffffcc"/> + <dia:color val="#ffffccff"/> </dia:attribute> <dia:attribute name="show_background"> <dia:boolean val="true"/> @@ -3325,12 +3893,12 @@ <dia:real val="0.5"/> </dia:attribute> </dia:object> - <dia:object type="Standard - Text" version="1" id="O19"> + <dia:object type="Standard - Text" version="1" id="O23"> <dia:attribute name="obj_pos"> <dia:point val="63,9.3"/> </dia:attribute> <dia:attribute name="obj_bb"> - <dia:rectangle val="63,8.705;71.67,9.4525"/> + <dia:rectangle val="63,8.705;71.67,9.45"/> </dia:attribute> <dia:attribute name="text"> <dia:composite type="text"> @@ -3347,7 +3915,7 @@ <dia:point val="63,9.3"/> </dia:attribute> <dia:attribute name="color"> - <dia:color val="#000000"/> + <dia:color val="#000000ff"/> </dia:attribute> <dia:attribute name="alignment"> <dia:enum val="0"/> @@ -3358,12 +3926,12 @@ <dia:enum val="3"/> </dia:attribute> </dia:object> - <dia:object type="Standard - Text" version="1" id="O20"> + <dia:object type="Standard - Text" version="1" id="O24"> <dia:attribute name="obj_pos"> <dia:point val="63,12.3"/> </dia:attribute> <dia:attribute name="obj_bb"> - <dia:rectangle val="63,11.705;79.725,12.4525"/> + <dia:rectangle val="63,11.705;79.725,12.45"/> </dia:attribute> <dia:attribute name="text"> <dia:composite type="text"> @@ -3380,7 +3948,7 @@ <dia:point val="63,12.3"/> </dia:attribute> <dia:attribute name="color"> - <dia:color val="#000000"/> + <dia:color val="#000000ff"/> </dia:attribute> <dia:attribute name="alignment"> <dia:enum val="0"/> @@ -3391,12 +3959,12 @@ <dia:enum val="3"/> </dia:attribute> </dia:object> - <dia:object type="Standard - Text" version="1" id="O21"> + <dia:object type="Standard - Text" version="1" id="O25"> <dia:attribute name="obj_pos"> <dia:point val="63,13.3"/> </dia:attribute> <dia:attribute name="obj_bb"> - <dia:rectangle val="63,12.705;81.39,13.4525"/> + <dia:rectangle val="63,12.705;81.39,13.45"/> </dia:attribute> <dia:attribute name="text"> <dia:composite type="text"> @@ -3413,7 +3981,7 @@ <dia:point val="63,13.3"/> </dia:attribute> <dia:attribute name="color"> - <dia:color val="#000000"/> + <dia:color val="#000000ff"/> </dia:attribute> <dia:attribute name="alignment"> <dia:enum val="0"/> @@ -3424,12 +3992,12 @@ <dia:enum val="3"/> </dia:attribute> </dia:object> - <dia:object type="Standard - Text" version="1" id="O22"> + <dia:object type="Standard - Text" version="1" id="O26"> <dia:attribute name="obj_pos"> <dia:point val="63,11.3"/> </dia:attribute> <dia:attribute name="obj_bb"> - <dia:rectangle val="63,10.705;76.6375,11.4525"/> + <dia:rectangle val="63,10.705;76.6375,11.45"/> </dia:attribute> <dia:attribute name="text"> <dia:composite type="text"> @@ -3446,7 +4014,7 @@ <dia:point val="63,11.3"/> </dia:attribute> <dia:attribute name="color"> - <dia:color val="#000000"/> + <dia:color val="#000000ff"/> </dia:attribute> <dia:attribute name="alignment"> <dia:enum val="0"/> @@ -3457,12 +4025,12 @@ <dia:enum val="3"/> </dia:attribute> </dia:object> - <dia:object type="Standard - Text" version="1" id="O23"> + <dia:object type="Standard - Text" version="1" id="O27"> <dia:attribute name="obj_pos"> <dia:point val="63,14.3"/> </dia:attribute> <dia:attribute name="obj_bb"> - <dia:rectangle val="63,13.705;80.82,14.4525"/> + <dia:rectangle val="63,13.705;80.82,14.45"/> </dia:attribute> <dia:attribute name="text"> <dia:composite type="text"> @@ -3479,7 +4047,7 @@ <dia:point val="63,14.3"/> </dia:attribute> <dia:attribute name="color"> - <dia:color val="#000000"/> + <dia:color val="#000000ff"/> </dia:attribute> <dia:attribute name="alignment"> <dia:enum val="0"/> @@ -3490,12 +4058,12 @@ <dia:enum val="3"/> </dia:attribute> </dia:object> - <dia:object type="Standard - Text" version="1" id="O24"> + <dia:object type="Standard - Text" version="1" id="O28"> <dia:attribute name="obj_pos"> <dia:point val="63,10.3"/> </dia:attribute> <dia:attribute name="obj_bb"> - <dia:rectangle val="63,9.705;74.7525,10.4525"/> + <dia:rectangle val="63,9.705;74.7525,10.45"/> </dia:attribute> <dia:attribute name="text"> <dia:composite type="text"> @@ -3512,7 +4080,7 @@ <dia:point val="63,10.3"/> </dia:attribute> <dia:attribute name="color"> - <dia:color val="#000000"/> + <dia:color val="#000000ff"/> </dia:attribute> <dia:attribute name="alignment"> <dia:enum val="0"/> @@ -3523,12 +4091,12 @@ <dia:enum val="3"/> </dia:attribute> </dia:object> - <dia:object type="Standard - Text" version="1" id="O25"> + <dia:object type="Standard - Text" version="1" id="O29"> <dia:attribute name="obj_pos"> <dia:point val="63,20"/> </dia:attribute> <dia:attribute name="obj_bb"> - <dia:rectangle val="63,19.405;80.41,20.1525"/> + <dia:rectangle val="63,19.405;80.41,20.15"/> </dia:attribute> <dia:attribute name="text"> <dia:composite type="text"> @@ -3545,7 +4113,7 @@ <dia:point val="63,20"/> </dia:attribute> <dia:attribute name="color"> - <dia:color val="#000000"/> + <dia:color val="#000000ff"/> </dia:attribute> <dia:attribute name="alignment"> <dia:enum val="0"/> @@ -3556,12 +4124,12 @@ <dia:enum val="3"/> </dia:attribute> </dia:object> - <dia:object type="Standard - Text" version="1" id="O26"> + <dia:object type="Standard - Text" version="1" id="O30"> <dia:attribute name="obj_pos"> <dia:point val="63,15.3"/> </dia:attribute> <dia:attribute name="obj_bb"> - <dia:rectangle val="63,14.705;76.82,15.4525"/> + <dia:rectangle val="63,14.705;76.82,15.45"/> </dia:attribute> <dia:attribute name="text"> <dia:composite type="text"> @@ -3578,7 +4146,7 @@ <dia:point val="63,15.3"/> </dia:attribute> <dia:attribute name="color"> - <dia:color val="#000000"/> + <dia:color val="#000000ff"/> </dia:attribute> <dia:attribute name="alignment"> <dia:enum val="0"/> @@ -3589,12 +4157,12 @@ <dia:enum val="3"/> </dia:attribute> </dia:object> - <dia:object type="Standard - Text" version="1" id="O27"> + <dia:object type="Standard - Text" version="1" id="O31"> <dia:attribute name="obj_pos"> <dia:point val="63,16.3"/> </dia:attribute> <dia:attribute name="obj_bb"> - <dia:rectangle val="63,15.705;82.6725,16.4525"/> + <dia:rectangle val="63,15.705;82.6725,16.45"/> </dia:attribute> <dia:attribute name="text"> <dia:composite type="text"> @@ -3611,7 +4179,7 @@ <dia:point val="63,16.3"/> </dia:attribute> <dia:attribute name="color"> - <dia:color val="#000000"/> + <dia:color val="#000000ff"/> </dia:attribute> <dia:attribute name="alignment"> <dia:enum val="0"/> @@ -3622,17 +4190,17 @@ <dia:enum val="3"/> </dia:attribute> </dia:object> - <dia:object type="Standard - Text" version="1" id="O28"> + <dia:object type="Standard - Text" version="1" id="O32"> <dia:attribute name="obj_pos"> - <dia:point val="55,7"/> + <dia:point val="62,7"/> </dia:attribute> <dia:attribute name="obj_bb"> - <dia:rectangle val="55,6.405;83.9275,7.1525"/> + <dia:rectangle val="62,6.405;84.375,7.15"/> </dia:attribute> <dia:attribute name="text"> <dia:composite type="text"> <dia:attribute name="string"> - <dia:string>#Visit http://wiki.sugarlabs.org/go/Platform_Team/Sugar_Network/Objects_model for details.#</dia:string> + <dia:string>#See http://wiki.sugarlabs.org/go/Sugar_Network/Resources for details.#</dia:string> </dia:attribute> <dia:attribute name="font"> <dia:font family="sans" style="0" name="Helvetica"/> @@ -3641,10 +4209,10 @@ <dia:real val="0.80000000000000004"/> </dia:attribute> <dia:attribute name="pos"> - <dia:point val="55,7"/> + <dia:point val="62,7"/> </dia:attribute> <dia:attribute name="color"> - <dia:color val="#000000"/> + <dia:color val="#000000ff"/> </dia:attribute> <dia:attribute name="alignment"> <dia:enum val="0"/> @@ -3655,12 +4223,12 @@ <dia:enum val="3"/> </dia:attribute> </dia:object> - <dia:object type="Standard - Text" version="1" id="O29"> + <dia:object type="Standard - Text" version="1" id="O33"> <dia:attribute name="obj_pos"> <dia:point val="63,18.1414"/> </dia:attribute> <dia:attribute name="obj_bb"> - <dia:rectangle val="63,17.5464;73.7075,18.2939"/> + <dia:rectangle val="63,17.5464;73.7075,18.2914"/> </dia:attribute> <dia:attribute name="text"> <dia:composite type="text"> @@ -3677,7 +4245,7 @@ <dia:point val="63,18.1414"/> </dia:attribute> <dia:attribute name="color"> - <dia:color val="#000000"/> + <dia:color val="#000000ff"/> </dia:attribute> <dia:attribute name="alignment"> <dia:enum val="0"/> @@ -3688,12 +4256,12 @@ <dia:enum val="3"/> </dia:attribute> </dia:object> - <dia:object type="Standard - Text" version="1" id="O30"> + <dia:object type="Standard - Text" version="1" id="O34"> <dia:attribute name="obj_pos"> <dia:point val="63,17.2475"/> </dia:attribute> <dia:attribute name="obj_bb"> - <dia:rectangle val="63,16.6525;75.62,17.4"/> + <dia:rectangle val="63,16.6525;75.62,17.3975"/> </dia:attribute> <dia:attribute name="text"> <dia:composite type="text"> @@ -3710,7 +4278,7 @@ <dia:point val="63,17.2475"/> </dia:attribute> <dia:attribute name="color"> - <dia:color val="#000000"/> + <dia:color val="#000000ff"/> </dia:attribute> <dia:attribute name="alignment"> <dia:enum val="0"/> @@ -3722,444 +4290,5 @@ </dia:attribute> </dia:object> </dia:group> - <dia:object type="UML - Association" version="2" id="O31"> - <dia:attribute name="name"> - <dia:string>##</dia:string> - </dia:attribute> - <dia:attribute name="direction"> - <dia:enum val="0"/> - </dia:attribute> - <dia:attribute name="show_direction"> - <dia:boolean val="true"/> - </dia:attribute> - <dia:attribute name="assoc_type"> - <dia:enum val="0"/> - </dia:attribute> - <dia:attribute name="role_a"> - <dia:string>##</dia:string> - </dia:attribute> - <dia:attribute name="multipicity_a"> - <dia:string>##</dia:string> - </dia:attribute> - <dia:attribute name="visibility_a"> - <dia:enum val="0"/> - </dia:attribute> - <dia:attribute name="show_arrow_a"> - <dia:boolean val="false"/> - </dia:attribute> - <dia:attribute name="role_b"> - <dia:string>##</dia:string> - </dia:attribute> - <dia:attribute name="multipicity_b"> - <dia:string>#*#</dia:string> - </dia:attribute> - <dia:attribute name="visibility_b"> - <dia:enum val="0"/> - </dia:attribute> - <dia:attribute name="show_arrow_b"> - <dia:boolean val="false"/> - </dia:attribute> - <dia:attribute name="obj_pos"> - <dia:point val="30,33.1"/> - </dia:attribute> - <dia:attribute name="obj_bb"> - <dia:rectangle val="19.7562,32.2;30.2,40.8"/> - </dia:attribute> - <dia:attribute name="meta"> - <dia:composite type="dict"/> - </dia:attribute> - <dia:attribute name="orth_points"> - <dia:point val="30,33.1"/> - <dia:point val="30,33"/> - <dia:point val="19.8062,33"/> - <dia:point val="19.8062,40"/> - </dia:attribute> - <dia:attribute name="orth_orient"> - <dia:enum val="1"/> - <dia:enum val="0"/> - <dia:enum val="1"/> - </dia:attribute> - <dia:attribute name="orth_autoroute"> - <dia:boolean val="false"/> - </dia:attribute> - <dia:attribute name="text_colour"> - <dia:color val="#000000"/> - </dia:attribute> - <dia:attribute name="line_colour"> - <dia:color val="#000000"/> - </dia:attribute> - <dia:connections> - <dia:connection handle="0" to="O0" connection="28"/> - <dia:connection handle="1" to="O15" connection="1"/> - </dia:connections> - </dia:object> - <dia:object type="UML - Class" version="0" id="O32"> - <dia:attribute name="obj_pos"> - <dia:point val="54,40"/> - </dia:attribute> - <dia:attribute name="obj_bb"> - <dia:rectangle val="53.985,39.985;66.45,55.615"/> - </dia:attribute> - <dia:attribute name="elem_corner"> - <dia:point val="54,40"/> - </dia:attribute> - <dia:attribute name="elem_width"> - <dia:real val="12.435"/> - </dia:attribute> - <dia:attribute name="elem_height"> - <dia:real val="15.6"/> - </dia:attribute> - <dia:attribute name="name"> - <dia:string>#Review#</dia:string> - </dia:attribute> - <dia:attribute name="stereotype"> - <dia:string>#Resource#</dia:string> - </dia:attribute> - <dia:attribute name="comment"> - <dia:string>#Users' reviews#</dia:string> - </dia:attribute> - <dia:attribute name="abstract"> - <dia:boolean val="false"/> - </dia:attribute> - <dia:attribute name="suppress_attributes"> - <dia:boolean val="false"/> - </dia:attribute> - <dia:attribute name="suppress_operations"> - <dia:boolean val="false"/> - </dia:attribute> - <dia:attribute name="visible_attributes"> - <dia:boolean val="true"/> - </dia:attribute> - <dia:attribute name="visible_operations"> - <dia:boolean val="false"/> - </dia:attribute> - <dia:attribute name="visible_comments"> - <dia:boolean val="true"/> - </dia:attribute> - <dia:attribute name="wrap_operations"> - <dia:boolean val="false"/> - </dia:attribute> - <dia:attribute name="wrap_after_char"> - <dia:int val="35"/> - </dia:attribute> - <dia:attribute name="comment_line_length"> - <dia:int val="35"/> - </dia:attribute> - <dia:attribute name="comment_tagging"> - <dia:boolean val="false"/> - </dia:attribute> - <dia:attribute name="line_width"> - <dia:real val="0.029999999999999999"/> - </dia:attribute> - <dia:attribute name="line_color"> - <dia:color val="#000000"/> - </dia:attribute> - <dia:attribute name="fill_color"> - <dia:color val="#d9e6e6"/> - </dia:attribute> - <dia:attribute name="text_color"> - <dia:color val="#000000"/> - </dia:attribute> - <dia:attribute name="normal_font"> - <dia:font family="monospace" style="0" name="Courier"/> - </dia:attribute> - <dia:attribute name="abstract_font"> - <dia:font family="monospace" style="0" name="Courier"/> - </dia:attribute> - <dia:attribute name="polymorphic_font"> - <dia:font family="monospace" style="0" name="Courier"/> - </dia:attribute> - <dia:attribute name="classname_font"> - <dia:font family="sans" style="80" name="Helvetica-Bold"/> - </dia:attribute> - <dia:attribute name="abstract_classname_font"> - <dia:font family="sans" style="88" name="Helvetica-BoldOblique"/> - </dia:attribute> - <dia:attribute name="comment_font"> - <dia:font family="sans" style="8" name="Helvetica-Oblique"/> - </dia:attribute> - <dia:attribute name="normal_font_height"> - <dia:real val="0.80000000000000004"/> - </dia:attribute> - <dia:attribute name="polymorphic_font_height"> - <dia:real val="0.80000000000000004"/> - </dia:attribute> - <dia:attribute name="abstract_font_height"> - <dia:real val="1"/> - </dia:attribute> - <dia:attribute name="classname_font_height"> - <dia:real val="1"/> - </dia:attribute> - <dia:attribute name="abstract_classname_font_height"> - <dia:real val="1"/> - </dia:attribute> - <dia:attribute name="comment_font_height"> - <dia:real val="0.80000000000000004"/> - </dia:attribute> - <dia:attribute name="attributes"> - <dia:composite type="umlattribute"> - <dia:attribute name="name"> - <dia:string>#context#</dia:string> - </dia:attribute> - <dia:attribute name="type"> - <dia:string>#Context [R WN]#</dia:string> - </dia:attribute> - <dia:attribute name="value"> - <dia:string>##</dia:string> - </dia:attribute> - <dia:attribute name="comment"> - <dia:string>#The Context this Feedback belongs to#</dia:string> - </dia:attribute> - <dia:attribute name="visibility"> - <dia:enum val="0"/> - </dia:attribute> - <dia:attribute name="abstract"> - <dia:boolean val="false"/> - </dia:attribute> - <dia:attribute name="class_scope"> - <dia:boolean val="false"/> - </dia:attribute> - </dia:composite> - <dia:composite type="umlattribute"> - <dia:attribute name="name"> - <dia:string>#artifact#</dia:string> - </dia:attribute> - <dia:attribute name="type"> - <dia:string>#Artifact [R WN]#</dia:string> - </dia:attribute> - <dia:attribute name="value"> - <dia:string>#""#</dia:string> - </dia:attribute> - <dia:attribute name="comment"> - <dia:string>#If specified, the Review was created for an Artifact#</dia:string> - </dia:attribute> - <dia:attribute name="visibility"> - <dia:enum val="0"/> - </dia:attribute> - <dia:attribute name="abstract"> - <dia:boolean val="false"/> - </dia:attribute> - <dia:attribute name="class_scope"> - <dia:boolean val="false"/> - </dia:attribute> - </dia:composite> - <dia:composite type="umlattribute"> - <dia:attribute name="name"> - <dia:string>#title#</dia:string> - </dia:attribute> - <dia:attribute name="type"> - <dia:string>#str [R WN F I]#</dia:string> - </dia:attribute> - <dia:attribute name="value"> - <dia:string>##</dia:string> - </dia:attribute> - <dia:attribute name="comment"> - <dia:string>#One line summary#</dia:string> - </dia:attribute> - <dia:attribute name="visibility"> - <dia:enum val="0"/> - </dia:attribute> - <dia:attribute name="abstract"> - <dia:boolean val="false"/> - </dia:attribute> - <dia:attribute name="class_scope"> - <dia:boolean val="false"/> - </dia:attribute> - </dia:composite> - <dia:composite type="umlattribute"> - <dia:attribute name="name"> - <dia:string>#content#</dia:string> - </dia:attribute> - <dia:attribute name="type"> - <dia:string>#wikitext [R WN F I]#</dia:string> - </dia:attribute> - <dia:attribute name="value"> - <dia:string>##</dia:string> - </dia:attribute> - <dia:attribute name="comment"> - <dia:string>#The content of the Review#</dia:string> - </dia:attribute> - <dia:attribute name="visibility"> - <dia:enum val="0"/> - </dia:attribute> - <dia:attribute name="abstract"> - <dia:boolean val="false"/> - </dia:attribute> - <dia:attribute name="class_scope"> - <dia:boolean val="false"/> - </dia:attribute> - </dia:composite> - <dia:composite type="umlattribute"> - <dia:attribute name="name"> - <dia:string>#rating#</dia:string> - </dia:attribute> - <dia:attribute name="type"> - <dia:string>#int [R WN S]#</dia:string> - </dia:attribute> - <dia:attribute name="value"> - <dia:string>##</dia:string> - </dia:attribute> - <dia:attribute name="comment"> - <dia:string>#Reviewer' rating, value from 1 to 5.#</dia:string> - </dia:attribute> - <dia:attribute name="visibility"> - <dia:enum val="0"/> - </dia:attribute> - <dia:attribute name="abstract"> - <dia:boolean val="false"/> - </dia:attribute> - <dia:attribute name="class_scope"> - <dia:boolean val="false"/> - </dia:attribute> - </dia:composite> - </dia:attribute> - <dia:attribute name="operations"/> - <dia:attribute name="template"> - <dia:boolean val="false"/> - </dia:attribute> - <dia:attribute name="templates"/> - </dia:object> - <dia:object type="UML - Association" version="2" id="O33"> - <dia:attribute name="name"> - <dia:string>##</dia:string> - </dia:attribute> - <dia:attribute name="direction"> - <dia:enum val="0"/> - </dia:attribute> - <dia:attribute name="show_direction"> - <dia:boolean val="true"/> - </dia:attribute> - <dia:attribute name="assoc_type"> - <dia:enum val="0"/> - </dia:attribute> - <dia:attribute name="role_a"> - <dia:string>##</dia:string> - </dia:attribute> - <dia:attribute name="multipicity_a"> - <dia:string>##</dia:string> - </dia:attribute> - <dia:attribute name="visibility_a"> - <dia:enum val="0"/> - </dia:attribute> - <dia:attribute name="show_arrow_a"> - <dia:boolean val="false"/> - </dia:attribute> - <dia:attribute name="role_b"> - <dia:string>##</dia:string> - </dia:attribute> - <dia:attribute name="multipicity_b"> - <dia:string>#*#</dia:string> - </dia:attribute> - <dia:attribute name="visibility_b"> - <dia:enum val="0"/> - </dia:attribute> - <dia:attribute name="show_arrow_b"> - <dia:boolean val="false"/> - </dia:attribute> - <dia:attribute name="obj_pos"> - <dia:point val="48.6525,35.1"/> - </dia:attribute> - <dia:attribute name="obj_bb"> - <dia:rectangle val="48.6025,34.3;60.2675,40.8"/> - </dia:attribute> - <dia:attribute name="meta"> - <dia:composite type="dict"/> - </dia:attribute> - <dia:attribute name="orth_points"> - <dia:point val="48.6525,35.1"/> - <dia:point val="60,35.1"/> - <dia:point val="60,40"/> - <dia:point val="60.2175,40"/> - </dia:attribute> - <dia:attribute name="orth_orient"> - <dia:enum val="0"/> - <dia:enum val="1"/> - <dia:enum val="0"/> - </dia:attribute> - <dia:attribute name="orth_autoroute"> - <dia:boolean val="false"/> - </dia:attribute> - <dia:attribute name="text_colour"> - <dia:color val="#000000"/> - </dia:attribute> - <dia:attribute name="line_colour"> - <dia:color val="#000000"/> - </dia:attribute> - <dia:connections> - <dia:connection handle="0" to="O0" connection="31"/> - <dia:connection handle="1" to="O32" connection="1"/> - </dia:connections> - </dia:object> - <dia:object type="UML - Association" version="2" id="O34"> - <dia:attribute name="name"> - <dia:string>##</dia:string> - </dia:attribute> - <dia:attribute name="direction"> - <dia:enum val="0"/> - </dia:attribute> - <dia:attribute name="show_direction"> - <dia:boolean val="true"/> - </dia:attribute> - <dia:attribute name="assoc_type"> - <dia:enum val="0"/> - </dia:attribute> - <dia:attribute name="role_a"> - <dia:string>##</dia:string> - </dia:attribute> - <dia:attribute name="multipicity_a"> - <dia:string>##</dia:string> - </dia:attribute> - <dia:attribute name="visibility_a"> - <dia:enum val="0"/> - </dia:attribute> - <dia:attribute name="show_arrow_a"> - <dia:boolean val="false"/> - </dia:attribute> - <dia:attribute name="role_b"> - <dia:string>##</dia:string> - </dia:attribute> - <dia:attribute name="multipicity_b"> - <dia:string>#*#</dia:string> - </dia:attribute> - <dia:attribute name="visibility_b"> - <dia:enum val="0"/> - </dia:attribute> - <dia:attribute name="show_arrow_b"> - <dia:boolean val="false"/> - </dia:attribute> - <dia:attribute name="obj_pos"> - <dia:point val="61.565,58"/> - </dia:attribute> - <dia:attribute name="obj_bb"> - <dia:rectangle val="59.6325,54.8;61.615,58.8"/> - </dia:attribute> - <dia:attribute name="meta"> - <dia:composite type="dict"/> - </dia:attribute> - <dia:attribute name="orth_points"> - <dia:point val="61.565,58"/> - <dia:point val="60,58"/> - <dia:point val="60,55.6"/> - <dia:point val="60.2175,55.6"/> - </dia:attribute> - <dia:attribute name="orth_orient"> - <dia:enum val="0"/> - <dia:enum val="1"/> - <dia:enum val="0"/> - </dia:attribute> - <dia:attribute name="orth_autoroute"> - <dia:boolean val="false"/> - </dia:attribute> - <dia:attribute name="text_colour"> - <dia:color val="#000000"/> - </dia:attribute> - <dia:attribute name="line_colour"> - <dia:color val="#000000"/> - </dia:attribute> - <dia:connections> - <dia:connection handle="0" to="O14" connection="1"/> - <dia:connection handle="1" to="O32" connection="6"/> - </dia:connections> - </dia:object> </dia:layer> </dia:diagram> diff --git a/sugar_network/client/cache.py b/sugar_network/client/cache.py index e0d8c94..6c91b17 100644 --- a/sugar_network/client/cache.py +++ b/sugar_network/client/cache.py @@ -23,7 +23,7 @@ from os.path import exists, join, isdir from sugar_network.client import IPCClient, local_root from sugar_network.client import cache_limit, cache_lifetime from sugar_network.toolkit.bundle import Bundle -from sugar_network.toolkit import pipe, util, enforce +from sugar_network.toolkit import pipe, util, exception, enforce _logger = logging.getLogger('cache') @@ -122,7 +122,7 @@ def _list(): # Negative `unpack_size` to process large impls at first result.append((os.stat(path).st_mtime, -unpack_size, path)) except Exception: - util.exception('Cannot list %r cached implementation', path) + exception('Cannot list %r cached implementation', path) result.append((0, 0, path)) return total, sorted(result) diff --git a/sugar_network/client/clones.py b/sugar_network/client/clones.py index 1a977bd..07114f3 100644 --- a/sugar_network/client/clones.py +++ b/sugar_network/client/clones.py @@ -140,7 +140,7 @@ class _Inotify(Inotify): exception(_logger, 'Cannot read %r spec', clone_path) return - context = spec['implement'] + context = spec['context'] context_path = _context_path(context, hashed_path) _ensure_path(context_path) diff --git a/sugar_network/client/commands.py b/sugar_network/client/commands.py index 404ddf9..d20b2aa 100644 --- a/sugar_network/client/commands.py +++ b/sugar_network/client/commands.py @@ -464,7 +464,7 @@ class ClientCommands(db.CommandsProcessor, Commands, journal.Commands): else: copy = self._node_call(method='GET', document='context', guid=guid, reply=[ - 'type', 'implement', 'title', 'summary', 'description', + 'type', 'title', 'summary', 'description', 'homepage', 'mime_types', 'dependencies', ]) copy.update(props) diff --git a/sugar_network/client/injector.py b/sugar_network/client/injector.py index c180c05..02c7937 100644 --- a/sugar_network/client/injector.py +++ b/sugar_network/client/injector.py @@ -21,7 +21,7 @@ from os.path import join, exists, basename, dirname from sugar_network import client from sugar_network.client import journal, cache -from sugar_network.toolkit import http, pipe, lsb_release, util, enforce +from sugar_network.toolkit import pipe, lsb_release, util _PMS_PATHS = { @@ -152,18 +152,11 @@ def _clone_impl(context_guid, params): conn = client.IPCClient() context = conn.get(['context', context_guid], reply=['title']) + impl = conn.meta(['context', context_guid], cmd='clone', **params) - impls = conn.get(['implementation'], context=context_guid, - order_by='-version', limit=1, - reply=['guid', 'version', 'stability', 'spec'], - **params)['result'] - enforce(impls, http.NotFound, 'No implementations') - impl = impls[0] - - spec = impl['spec']['*-*'] src_path = cache.get(impl['guid'], impl) - if 'extract' in spec: - src_path = join(src_path, spec['extract']) + if 'extract' in impl: + src_path = join(src_path, impl['extract']) dst_path = util.unique_filename( client.activity_dirs.value[0], basename(src_path)) @@ -178,7 +171,7 @@ def _clone_impl(context_guid, params): 'stability': impl['stability'], 'spec': join(dst_path, 'activity', 'activity.info'), 'path': dst_path, - 'command': spec['commands']['activity']['exec'].split(), + 'command': impl['commands']['activity']['exec'].split(), }]) diff --git a/sugar_network/client/solver.py b/sugar_network/client/solver.py index 14816a6..10245a1 100644 --- a/sugar_network/client/solver.py +++ b/sugar_network/client/solver.py @@ -13,6 +13,8 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. +# pylint: disable-msg=W0611,F0401,W0201,E1101,W0232 + import sys import logging from os.path import isabs, join, dirname @@ -28,7 +30,6 @@ from zeroinstall.injector.config import Config from zeroinstall.injector.driver import Driver from zeroinstall.injector.requirements import Requirements from zeroinstall.injector.arch import machine_ranks -# pylint: disable-msg=W0611 from zeroinstall.injector.distro import try_cleanup_distro_version diff --git a/sugar_network/db/router.py b/sugar_network/db/router.py index dea8a6b..f7f4b96 100644 --- a/sugar_network/db/router.py +++ b/sugar_network/db/router.py @@ -175,18 +175,20 @@ class Router(object): result_streamed = isinstance(result, types.GeneratorType) - if request['method'] != 'HEAD': - if js_callback: - if result_streamed: - result = ''.join(result) - result_streamed = False - result = '%s(%s);' % (js_callback, json.dumps(result)) - response.content_length = len(result) - elif not result_streamed: - if response.content_type == 'application/json': - result = json.dumps(result) - if 'content-length' not in response: - response.content_length = len(result) if result else 0 + if request['method'] == 'HEAD': + result_streamed = False + result = None + elif js_callback: + if result_streamed: + result = ''.join(result) + result_streamed = False + result = '%s(%s);' % (js_callback, json.dumps(result)) + response.content_length = len(result) + elif not result_streamed: + if response.content_type == 'application/json': + result = json.dumps(result) + if 'content-length' not in response: + response.content_length = len(result) if result else 0 for key, value in response.meta.items(): response.set('X-SN-%s' % str(key), json.dumps(value)) @@ -196,7 +198,9 @@ class Router(object): start_response(response.status, response.items()) - if result_streamed: + if request['method'] == 'HEAD': + enforce(result is None, 'HEAD responses should not contain body') + elif result_streamed: for i in result: yield i elif result is not None: diff --git a/sugar_network/node/commands.py b/sugar_network/node/commands.py index 7678d25..d0555b9 100644 --- a/sugar_network/node/commands.py +++ b/sugar_network/node/commands.py @@ -1,4 +1,4 @@ -# Copyright (C) 2012 Aleksey Lim +# Copyright (C) 2012-2013 Aleksey Lim # # 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 @@ -21,6 +21,7 @@ from os.path import join, isdir, exists from sugar_network import db, node, static from sugar_network.node import auth, stats_node from sugar_network.resources.volume import Commands +from sugar_network.toolkit.spec import parse_requires, ensure_requires from sugar_network.toolkit import http, util, coroutine, exception, enforce @@ -51,7 +52,7 @@ class NodeCommands(db.VolumeCommands, Commands): @db.route('GET', '/robots.txt') def robots(self, request, response): response.content_type = 'text/plain' - return _ROBOTS_TXT + return 'User-agent: *\nDisallow: /\n' @db.route('GET', '/favicon.ico') def favicon(self, request, response): @@ -59,10 +60,6 @@ class NodeCommands(db.VolumeCommands, Commands): blob=join(static.PATH, 'favicon.ico'), mime_type='image/x-icon') - @db.volume_command(method='GET', mime_type='text/html') - def hello(self): - return _HELLO_HTML - @db.route('GET', '/packages') def route_packages(self, request, response): enforce(node.files_root.value, http.BadRequest, 'Disabled') @@ -92,6 +89,10 @@ class NodeCommands(db.VolumeCommands, Commands): else: return util.iter_file(path) + @db.volume_command(method='GET') + def hello(self, request, response): + raise http.Redirect('http://wiki.sugarlabs.org/go/Sugar_Network/API') + @db.volume_command(method='GET', cmd='stat', mime_type='application/json') def stat(self): @@ -190,17 +191,18 @@ class NodeCommands(db.VolumeCommands, Commands): @db.document_command(method='GET', cmd='clone', arguments={'requires': db.to_list}) - def clone(self, document, guid, version, requires, stability='stable'): - enforce(document == 'context', 'No way to clone') - request = db.Request(method='GET', document='implementation', - context=guid, version=version, stability=stability, - requires=requires, order_by='-version', limit=1, - reply=['guid']) - impls = self.call(request, db.Response())['result'] - enforce(impls, http.NotFound, 'No implementations found') - request = db.Request(method='GET', document='implementation', - guid=impls[0]['guid'], prop='data') - return self.call(request, db.Response()) + def clone(self, request, response): + impl = self._clone(request) + return self.get_prop('implementation', impl.guid, 'data', + request, response) + + @db.document_command(method='HEAD', cmd='clone', + arguments={'requires': db.to_list}) + def meta_clone(self, request, response): + impl = self._clone(request) + props = impl.properties(['guid', 'license', 'version', 'stability']) + response.meta.update(props) + response.meta.update(impl.meta('data')['spec']['*-*']) @db.document_command(method='GET', cmd='deplist', mime_type='application/json', arguments={'requires': db.to_list}) @@ -249,7 +251,7 @@ class NodeCommands(db.VolumeCommands, Commands): impls, __ = implementations.find(limit=db.MAX_LIMIT, context=context.guid, layer=layer) for impl in impls: - for arch, spec in impl['spec'].items(): + for arch, spec in impl.meta('data')['spec'].items(): spec['guid'] = impl.guid spec['version'] = impl['version'] spec['arch'] = arch @@ -260,7 +262,6 @@ class NodeCommands(db.VolumeCommands, Commands): requires.setdefault(i, {}) blob = implementations.get(impl.guid).meta('data') if blob: - spec['mime_type'] = blob.get('mime_type') spec['blob_size'] = blob.get('blob_size') spec['unpack_size'] = blob.get('unpack_size') versions.append(spec) @@ -352,6 +353,33 @@ class NodeCommands(db.VolumeCommands, Commands): coroutine.sleep(stats_node.stats_node_step.value) self._stats.commit() + def _clone(self, request): + enforce(request['document'] == 'context', 'No way to clone') + + requires = {} + if 'requires' in request.query: + for i in request['requires']: + requires.update(parse_requires(i)) + request.query.pop('requires') + else: + request.query['limit'] = 1 + + if 'stability' not in request.query: + request.query['stability'] = 'stable' + + impls, __ = self.volume['implementation'].find( + context=request['guid'], order_by='-version', **request.query) + impl = None + for impl in impls: + if requires: + impl_deps = impl.meta('data')['spec']['*-*']['requires'] + if not ensure_requires(impl_deps, requires): + continue + break + else: + raise http.NotFound('No implementations found') + return impl + def _load_pubkey(pubkey): pubkey = pubkey.strip() @@ -374,15 +402,3 @@ def _load_pubkey(pubkey): raise http.Forbidden(message) return str(hashlib.sha1(pubkey.split()[1]).hexdigest()), pubkey_pkcs8 - - -_HELLO_HTML = """\ -<h2>Welcome to Sugar Network API!</h2> -Consult <a href="http://wiki.sugarlabs.org/go/Platform_Team/Sugar_Network/API"> -Sugar Labs Wiki</a> to learn how it can be used. -""" - -_ROBOTS_TXT = """\ -User-agent: * -Disallow: / -""" diff --git a/sugar_network/resources/context.py b/sugar_network/resources/context.py index ee8387b..4d30776 100644 --- a/sugar_network/resources/context.py +++ b/sugar_network/resources/context.py @@ -36,11 +36,6 @@ class Context(Resource): self['layer'] = tuple(self['layer']) + ('common',) return value - @db.indexed_property(prefix='M', - full_text=True, default=[], typecast=[]) - def implement(self, value): - return value - @db.indexed_property(slot=1, prefix='S', full_text=True, localized=True) def title(self, value): return value diff --git a/sugar_network/resources/implementation.py b/sugar_network/resources/implementation.py index 4abcd02..d915473 100644 --- a/sugar_network/resources/implementation.py +++ b/sugar_network/resources/implementation.py @@ -13,39 +13,13 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. -# pylint: disable-msg=E1101,E0102,E0202 - -import os -from os.path import exists - import xapian from sugar_network import db, resources from sugar_network.resources.volume import Resource from sugar_network.toolkit.licenses import GOOD_LICENSES -from sugar_network.toolkit.bundle import Bundle -from sugar_network.toolkit import http, util, enforce - - -_ASLO_URL = 'http://download.sugarlabs.org/activities' -_ASLO_PATH = '/upload/activities' - - -def _encode_version(version): - version = util.parse_version(version) - # Convert to [(`version`, `modifier`)] - version = zip(*([iter(version)] * 2)) - major, modifier = version.pop(0) - - result = sum([(rank % 10000) * pow(10000, 3 - i) - for i, rank in enumerate((major + [0, 0])[:3])]) - result += (5 + modifier) * 1000 - if modifier and version: - minor, __ = version.pop(0) - if minor: - result += (minor[0] % 1000) - - return xapian.sortable_serialise(result) +from sugar_network.toolkit.spec import parse_version +from sugar_network.toolkit import http, enforce class Implementation(Resource): @@ -67,7 +41,8 @@ class Implementation(Resource): def license(self, value): return value - @db.indexed_property(slot=1, prefix='V', reprcast=_encode_version, + @db.indexed_property(slot=1, prefix='V', + reprcast=lambda x: _encode_version(x), permissions=db.ACCESS_CREATE | db.ACCESS_READ) def version(self, value): return value @@ -78,55 +53,28 @@ class Implementation(Resource): def stability(self, value): return value - @db.indexed_property(prefix='R', typecast=[], default=[], - permissions=db.ACCESS_CREATE | db.ACCESS_READ) - def requires(self, value): - return value - @db.indexed_property(prefix='N', full_text=True, localized=True, - permissions=db.ACCESS_CREATE | db.ACCESS_READ) + default='', permissions=db.ACCESS_CREATE | db.ACCESS_READ) def notes(self, value): return value - @db.stored_property(typecast=dict, default={}) - def spec(self, value): - return value - @db.blob_property() def data(self, value): - if value: - context = self.volume['context'].get(self['context']) - value['name'] = [context['title'], self['version']] return value - @data.setter - def data(self, value): - context = self.volume['context'].get(self['context']) - if 'activity' not in context['type']: - return value - def calc_unpack_size(path): - unpack_size = 0 - with Bundle(path, mime_type='application/zip') as bundle: - for arcname in bundle.get_names(): - unpack_size += bundle.getmember(arcname).size - value['unpack_size'] = unpack_size +def _encode_version(version): + version = parse_version(version) + # Convert to [(`version`, `modifier`)] + version = zip(*([iter(version)] * 2)) + major, modifier = version.pop(0) - if 'unpack_size' not in value: - if 'blob' in value: - calc_unpack_size(value['blob']) - elif 'url' in value: - url = value['url'] - if url.startswith(_ASLO_URL): - local_path = _ASLO_PATH + url[len(_ASLO_URL):] - if exists(local_path): - calc_unpack_size(local_path) - value['blob_size'] = os.stat(local_path).st_size - if 'unpack_size' not in value: - with util.NamedTemporaryFile() as f: - http.download(url, f.name) - value['blob_size'] = os.stat(f.name).st_size - calc_unpack_size(f.name) + result = sum([(rank % 10000) * pow(10000, 3 - i) + for i, rank in enumerate((major + [0, 0])[:3])]) + result += (5 + modifier) * 1000 + if modifier and version: + minor, __ = version.pop(0) + if minor: + result += (minor[0] % 1000) - value['mime_type'] = 'application/vnd.olpc-sugar' - return value + return xapian.sortable_serialise(result) diff --git a/sugar_network/toolkit/http.py b/sugar_network/toolkit/http.py index 9e66e23..30eb59e 100644 --- a/sugar_network/toolkit/http.py +++ b/sugar_network/toolkit/http.py @@ -132,6 +132,16 @@ class Client(object): response = self.request('GET', path_, params=kwargs) return self._decode_reply(response) + def meta(self, path_=None, **kwargs): + response = self.request('HEAD', path_, params=kwargs) + result = {} + for key, value in response.headers.items(): + if key.startswith('x-sn-'): + result[key[5:]] = json.loads(value) + else: + result[key] = value + return result + def post(self, path_=None, data_=None, **kwargs): response = self.request('POST', path_, json.dumps(data_), headers={'Content-Type': 'application/json'}, params=kwargs) @@ -196,7 +206,7 @@ class Client(object): return response content = response.content try: - error = json.loads(content) + error = json.loads(content)['error'] except Exception: # On non-JSONified fail response, assume that the error # was not sent by the application level server code, i.e., @@ -204,18 +214,13 @@ class Client(object): # If so, try to resend request. if a_try <= self._max_retries and method == 'GET': continue - _logger.trace('Request failed, ' - 'method=%s path=%r params=%r headers=%r ' - 'status_code=%s content=%s', - method, path, params, headers, - response.status_code, - '\n' + content if content else None) - response.raise_for_status() - else: - for cls in _FORWARD_STATUSES: - if response.status_code == cls.status_code: - raise cls(error['error']) - raise RuntimeError(error['error']) + error = content or 'No error message provided' + _logger.trace('Request failed, method=%s path=%r params=%r ' + 'headers=%r status_code=%s error=%s', + method, path, params, headers, response.status_code, + '\n' + error) + cls = _FORWARD_STATUSES.get(response.status_code, RuntimeError) + raise cls(error) return response @@ -277,10 +282,11 @@ class Client(object): del reply.headers['transfer-encoding'] response.update(reply.headers) - if reply.headers.get('Content-Type') == 'application/json': - return json.loads(reply.content) - else: - return reply.raw + if method != 'HEAD': + if reply.headers.get('Content-Type') == 'application/json': + return json.loads(reply.content) + else: + return reply.raw def subscribe(self, **condition): return _Subscription(self, condition) @@ -349,9 +355,9 @@ def _sign(key_path, data): return key.sign_asn1(hashlib.sha1(data).digest()).encode('hex') -_FORWARD_STATUSES = [ - BadRequest, - Forbidden, - NotFound, - ServiceUnavailable, - ] +_FORWARD_STATUSES = { + BadRequest.status_code: BadRequest, + Forbidden.status_code: Forbidden, + NotFound.status_code: NotFound, + ServiceUnavailable.status_code: ServiceUnavailable, + } diff --git a/sugar_network/toolkit/spec.py b/sugar_network/toolkit/spec.py index 91afc0d..b914c5c 100644 --- a/sugar_network/toolkit/spec.py +++ b/sugar_network/toolkit/spec.py @@ -15,6 +15,7 @@ import re import os +import sys import logging from os.path import join, exists, dirname from ConfigParser import ConfigParser @@ -27,7 +28,7 @@ EMPTY_LICENSE = 'License is not specified' _FIELDS = { # name: (required, typecast) - 'implement': (True, None), + 'context': (True, None), 'name': (True, None), 'summary': (True, None), 'description': (False, None), @@ -44,7 +45,7 @@ _STABILITIES = ('insecure', 'buggy', 'developer', 'testing', 'stable') _POLICY_URL = 'http://wiki.sugarlabs.org/go/Sugar_Network/Policy' _LIST_SEPARATOR = ';' -_RESTRICTION_RE = re.compile('(>=|<|=)\\s*([0-9.]+)') +_RESTRICTION_RE = re.compile('(<|<=|=|>|>=)\\s*([0-9.]+)') _VERSION_RE = re.compile('-([a-z]*)') _VERSION_MOD_TO_VALUE = { @@ -130,7 +131,7 @@ def format_version(version): return ''.join(version) -def format_next_version(version): +def format_next_version(version, deep=True): """Convert incremented version to string representation. Before convertation, the last version's rank will be incremented. @@ -142,10 +143,80 @@ def format_next_version(version): return None if isinstance(version, basestring): version = parse_version(version) - version[-2][-1] += 1 + if deep: + version[-2] += [1] + else: + version[-2][-1] += 1 return format_version(version) +def parse_requires(requires): + result = {} + + for dep_str in _parse_list(requires): + dep = _Dependency() + + if dep_str.startswith('[') and dep_str.endswith(']'): + dep_str = dep_str[1:-1] + dep['importance'] = 'recommended' + + parts = _RESTRICTION_RE.split(dep_str) + enforce(parts[0], 'Can parse dependency from "%s" string', dep_str) + + dep_name = parts.pop(0).strip() + if dep_name in result: + result[dep_name].update(dep) + dep = result[dep_name] + else: + result[dep_name] = dep + + not_before = None + before = None + while len(parts) >= 3: + if parts[0] == '<': + before = format_version(parts[1]) + elif parts[0] == '<=': + before = format_next_version(parts[1]) + elif parts[0] == '>': + not_before = format_next_version(parts[1]) + elif parts[0] == '>=': + not_before = format_version(parts[1]) + elif parts[0] == '=': + not_before = format_version(parts[1]) + before = format_next_version(parts[1], False) + del parts[:3] + + enforce(not parts or not parts[0].strip(), + 'Cannot parse "%s", it should be in format ' + '"<dependency> (>=|<|=) <version>"', dep_str) + + if before or not_before: + dep.setdefault('restrictions', []) + dep['restrictions'].append((not_before, before)) + + return result + + +def ensure_requires(to_consider, to_apply): + + def intersect(x, y): + l = max([parse_version(i) for i, __ in (x + y)]) + r = min([[[sys.maxint]] if i is None else parse_version(i) \ + for __, i in [x + y]]) + return l is None or r is None or l < r + + for name, cond in to_apply.items(): + dep = to_consider.get(name) + if dep is None: + return False + if 'restrictions' not in dep or 'restrictions' not in cond: + continue + if not intersect(dep['restrictions'], cond['restrictions']): + return False + + return True + + class Spec(object): def __init__(self, spec=None, root=None): @@ -217,7 +288,7 @@ class Spec(object): return self._get(section, key) def __repr__(self): - return '<Spec %s>' % self['implement'] + return '<Spec %s>' % self['context'] def _get(self, section, key): if self._config.has_option(section, key): @@ -227,7 +298,7 @@ class Spec(object): for section in sorted(self._config.sections()): bindings = _parse_bindings(self._get(section, 'binding')) self.bindings.update(bindings) - requires = _parse_requires(self._get(section, 'requires')) + requires = parse_requires(self._get(section, 'requires')) section_type = section.split(':')[0] if section_type == 'Activity': @@ -270,8 +341,8 @@ class Spec(object): self._fields[key] = value if self.activity is not None: - # TODO Switch to `implement` tag at the end - self._fields['implement'] = self.activity['bundle_id'] + # TODO Switch to `context` tag at the end + self._fields['context'] = self.activity['bundle_id'] # Do some backwards compatibility expansions for activities if not self['summary'] and self['name']: self._fields['summary'] = self['name'] @@ -403,6 +474,8 @@ class _Dependency(dict): def versions_range(self): for not_before, before in self.get('restrictions') or []: + if not_before is None or before is None: + continue i = parse_version(not_before)[0] yield format_version([i, 0]) end = parse_version(before)[0] @@ -437,53 +510,6 @@ def _parse_bindings(text): return sorted(result) -def _parse_requires(requires): - result = {} - - for dep_str in _parse_list(requires): - dep = _Dependency() - - if dep_str.startswith('[') and dep_str.endswith(']'): - dep_str = dep_str[1:-1] - dep['importance'] = 'recommended' - - parts = _RESTRICTION_RE.split(dep_str) - enforce(parts[0], 'Can parse dependency from "%s" string', dep_str) - - dep_name = parts.pop(0).strip() - if dep_name in result: - result[dep_name].update(dep) - dep = result[dep_name] - else: - result[dep_name] = dep - - not_before = None - before = None - while len(parts) >= 3: - if parts[0] == '<': - before = format_version(parts[1]) - elif parts[0] == '<=': - before = format_next_version(parts[1]) - elif parts[0] == '>': - not_before = format_next_version(parts[1]) - elif parts[0] == '>=': - not_before = format_version(parts[1]) - elif parts[0] == '=': - not_before = format_version(parts[1]) - before = format_next_version(parts[1]) - del parts[:3] - - enforce(not parts or not parts[0].strip(), - 'Cannot parse "%s", it should be in format ' - '"<dependency> (>=|<|=) <version>"', dep_str) - - if before or not_before: - dep.setdefault('restrictions', []) - dep['restrictions'].append((not_before, before)) - - return result - - def _parse_list(str_list): if not str_list: return [] diff --git a/sugar_network/toolkit/util.py b/sugar_network/toolkit/util.py index 8fc26cf..107235b 100644 --- a/sugar_network/toolkit/util.py +++ b/sugar_network/toolkit/util.py @@ -17,6 +17,7 @@ import os import json +import errno import logging import hashlib import tempfile @@ -55,6 +56,8 @@ def init_logging(debug_level): self._log(9, message, args, **kwargs) _disable_logger(['sugar_stats']) else: + logging.Logger.trace = lambda self, message, *args, **kwargs: \ + self._log(9, message, args, **kwargs) logging.Logger.heartbeat = lambda self, message, *args, **kwargs: \ self._log(8, message, args, **kwargs) @@ -320,12 +323,30 @@ def TemporaryFile(*args, **kwargs): return tempfile.TemporaryFile(*args, **kwargs) -def NamedTemporaryFile(*args, **kwargs): - if cachedir.value: - if not exists(cachedir.value): - os.makedirs(cachedir.value) - kwargs['dir'] = cachedir.value - return tempfile.NamedTemporaryFile(*args, **kwargs) +class NamedTemporaryFile(object): + + def __init__(self, *args, **kwargs): + if cachedir.value: + if not exists(cachedir.value): + os.makedirs(cachedir.value) + kwargs['dir'] = cachedir.value + self._file = tempfile.NamedTemporaryFile(*args, **kwargs) + + def close(self): + try: + self._file.close() + except OSError, error: + if error.errno != errno.ENOENT: + raise + + def __enter__(self): + return self + + def __exit__(self, exc_type, exc_value, traceback): + self.close() + + def __getattr__(self, name): + return getattr(self._file, name) class Seqno(object): diff --git a/tests/__init__.py b/tests/__init__.py index f84325b..84378c6 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -37,7 +37,7 @@ root = abspath(dirname(__file__)) tmproot = '/tmp/sugar_network.tests' tmpdir = None -monkey.patch_socket(dns=False) +monkey.patch_socket() monkey.patch_select() monkey.patch_ssl() monkey.patch_time() @@ -226,7 +226,11 @@ class Test(unittest.TestCase): def zips(self, *items): with util.NamedTemporaryFile() as f: bundle = zipfile.ZipFile(f.name, 'w') - for arcname, data in items: + for i in items: + if isinstance(i, basestring): + arcname = data = i + else: + arcname, data = i if not isinstance(data, basestring): data = '\n'.join(data) bundle.writestr(arcname, data) diff --git a/tests/units/client/injector.py b/tests/units/client/injector.py index 11e0067..256cfa4 100755 --- a/tests/units/client/injector.py +++ b/tests/units/client/injector.py @@ -60,6 +60,8 @@ Can't find all required implementations: 'version': '1', 'stability': 'stable', 'notes': '', + }) + self.node_volume['implementation'].update(impl, {'data': { 'spec': { '*-*': { 'commands': { @@ -67,12 +69,10 @@ Can't find all required implementations: 'exec': 'echo', }, }, - 'stability': 'stable', - 'size': 0, 'extract': 'topdir', }, }, - }) + }}) pipe = injector.clone(context) log_path = tests.tmpdir + '/.sugar/default/logs/%s_1.log' % context @@ -126,6 +126,8 @@ Can't find all required implementations: 'version': '1', 'stability': 'stable', 'notes': '', + }) + self.node_volume['implementation'].update(impl, {'data': { 'spec': { '*-*': { 'commands': { @@ -133,17 +135,13 @@ Can't find all required implementations: 'exec': 'true', }, }, - 'stability': 'stable', - 'size': 0, 'extract': 'topdir', }, }, - }) - blob_path = 'master/implementation/%s/%s/data' % (impl[:2], impl) - self.touch((blob_path, json.dumps({}))) - bundle = zipfile.ZipFile(blob_path + '.blob', 'w') - bundle.writestr('topdir/probe', 'probe') - bundle.close() + 'blob': StringIO(self.zips(['topdir/probe', [ + 'probe', + ]])), + }}) pipe = injector.clone_impl(context) log_path = tests.tmpdir + '/.sugar/default/logs/%s.log' % context @@ -175,6 +173,8 @@ Can't find all required implementations: 'version': '1', 'stability': 'stable', 'notes': '', + }) + self.node_volume['implementation'].update(impl, {'data': { 'spec': { '*-*': { 'commands': { @@ -182,17 +182,13 @@ Can't find all required implementations: 'exec': 'echo', }, }, - 'stability': 'stable', - 'size': 0, 'extract': 'topdir', }, }, - }) - blob_path = 'master/implementation/%s/%s/data' % (impl[:2], impl) - self.touch((blob_path, json.dumps({}))) - bundle = zipfile.ZipFile(blob_path + '.blob', 'w') - bundle.writestr('topdir/probe', 'probe') - bundle.close() + 'blob': StringIO(self.zips(['topdir/probe', [ + 'probe', + ]])), + }}) for event in injector.clone(context): pass @@ -216,6 +212,8 @@ Can't find all required implementations: 'version': '1', 'stability': 'stable', 'notes': '', + }) + self.node_volume['implementation'].update(impl, {'data': { 'spec': { '*-*': { 'commands': { @@ -223,26 +221,19 @@ Can't find all required implementations: 'exec': 'true', }, }, - 'stability': 'stable', - 'size': 0, 'extract': 'TestActivitry', }, }, - }) - - blob_path = 'master/implementation/%s/%s/data' % (impl[:2], impl) - self.touch((blob_path, json.dumps({}))) - bundle = zipfile.ZipFile(blob_path + '.blob', 'w') - bundle.writestr('TestActivitry/activity/activity.info', '\n'.join([ - '[Activity]', - 'name = TestActivitry', - 'bundle_id = %s' % context, - 'exec = true', - 'icon = icon', - 'activity_version = 1', - 'license=Public Domain', - ])) - bundle.close() + 'blob': StringIO(self.zips(['TestActivitry/activity/activity.info', [ + '[Activity]', + 'name = TestActivitry', + 'bundle_id = %s' % context, + 'exec = true', + 'icon = icon', + 'activity_version = 1', + 'license=Public Domain', + ]])), + }}) self.override(journal, 'create_activity_id', lambda: 'activity_id') pipe = injector.launch(context) @@ -265,6 +256,8 @@ Can't find all required implementations: 'version': '2', 'stability': 'stable', 'notes': '', + }) + self.node_volume['implementation'].update(impl_2, {'data': { 'spec': { '*-*': { 'commands': { @@ -272,26 +265,19 @@ Can't find all required implementations: 'exec': 'true', }, }, - 'stability': 'stable', - 'size': 0, 'extract': 'TestActivitry', }, }, - }) - - blob_path = 'master/implementation/%s/%s/data' % (impl_2[:2], impl_2) - self.touch((blob_path, json.dumps({}))) - bundle = zipfile.ZipFile(blob_path + '.blob', 'w') - bundle.writestr('TestActivitry/activity/activity.info', '\n'.join([ - '[Activity]', - 'name = TestActivitry', - 'bundle_id = %s' % context, - 'exec = true', - 'icon = icon', - 'activity_version = 2', - 'license=Public Domain', - ])) - bundle.close() + 'blob': StringIO(self.zips(['TestActivitry/activity/activity.info', [ + '[Activity]', + 'name = TestActivitry', + 'bundle_id = %s' % context, + 'exec = true', + 'icon = icon', + 'activity_version = 2', + 'license=Public Domain', + ]])), + }}) shutil.rmtree('cache', ignore_errors=True) pipe = injector.launch(context) @@ -353,6 +339,8 @@ Can't find all required implementations: 'version': '1', 'stability': 'stable', 'notes': '', + }) + self.node_volume['implementation'].update(impl, {'data': { 'spec': { '*-*': { 'commands': { @@ -360,8 +348,6 @@ Can't find all required implementations: 'exec': 'true', }, }, - 'stability': 'stable', - 'size': 0, 'extract': 'topdir', 'requires': { 'dep1': {}, @@ -369,12 +355,10 @@ Can't find all required implementations: }, }, }, - }) - blob_path = 'master/implementation/%s/%s/data' % (impl[:2], impl) - self.touch((blob_path, json.dumps({}))) - bundle = zipfile.ZipFile(blob_path + '.blob', 'w') - bundle.writestr('topdir/probe', 'probe') - bundle.close() + 'blob': StringIO(self.zips(['topdir/probe', [ + 'probe', + ]])), + }}) conn.post(['context'], { 'guid': 'dep1', @@ -553,6 +537,8 @@ Can't find all required implementations: 'version': '1', 'stability': 'stable', 'notes': '', + }) + self.node_volume['implementation'].update(impl, {'data': { 'spec': { '*-*': { 'commands': { @@ -560,21 +546,17 @@ Can't find all required implementations: 'exec': 'echo', }, }, - 'stability': 'stable', - 'size': 0, 'extract': 'topdir', }, }, - }) - blob_path = 'master/implementation/%s/%s/data' % (impl[:2], impl) - self.touch((blob_path, json.dumps({}))) - bundle = zipfile.ZipFile(blob_path + '.blob', 'w') - bundle.writestr('topdir/activity/foo', '') - bundle.writestr('topdir/bin/bar', '') - bundle.writestr('topdir/bin/probe', '') - bundle.writestr('topdir/file1', '') - bundle.writestr('topdir/test/file2', '') - bundle.close() + 'blob': StringIO(self.zips( + 'topdir/activity/foo', + 'topdir/bin/bar', + 'topdir/bin/probe', + 'topdir/file1', + 'topdir/test/file2', + )), + }}) pipe = injector.clone(context) log_path = tests.tmpdir + '/.sugar/default/logs/%s_2.log' % context @@ -601,6 +583,8 @@ Can't find all required implementations: 'version': '1', 'stability': 'stable', 'notes': '', + }) + self.node_volume['implementation'].update(impl, {'data': { 'spec': { '*-*': { 'commands': { @@ -608,17 +592,13 @@ Can't find all required implementations: 'exec': 'echo', }, }, - 'stability': 'stable', - 'size': 0, 'extract': 'topdir', }, }, - }) - blob_path = 'master/implementation/%s/%s/data' % (impl[:2], impl) - self.touch((blob_path, json.dumps({}))) - bundle = zipfile.ZipFile(blob_path + '.blob', 'w') - bundle.writestr('topdir/probe', 'probe') - bundle.close() + 'blob': StringIO(self.zips(['topdir/probe', [ + 'probe', + ]])), + }}) for event in injector.clone(context): pass @@ -663,6 +643,8 @@ Can't find all required implementations: 'version': '1', 'stability': 'stable', 'notes': '', + }) + self.node_volume['implementation'].update(impl, {'data': { 'spec': { '*-*': { 'commands': { @@ -673,6 +655,7 @@ Can't find all required implementations: 'requires': { 'dep2': {'restrictions': [['1', '2']]}, 'dep3': {}, + }, }, }, }}) @@ -740,12 +723,14 @@ Can't find all required implementations: 'summary': 'summary', 'description': 'description', }) - conn.post(['implementation'], { + impl = conn.post(['implementation'], { 'context': context, 'license': 'GPLv3+', 'version': '1', 'stability': 'stable', 'notes': '', + }) + self.node_volume['implementation'].update(impl, {'data': { 'spec': { '*-*': { 'commands': { @@ -755,6 +740,7 @@ Can't find all required implementations: }, 'requires': { 'dep': {}, + }, }, }, }}) @@ -824,6 +810,8 @@ Can't find all required implementations: 'version': '1', 'stability': 'stable', 'notes': '', + }) + self.node_volume['implementation'].update(impl, {'data': { 'spec': { '*-*': { 'commands': { @@ -833,6 +821,7 @@ Can't find all required implementations: }, 'requires': { 'sugar': {}, + }, }, }, }}) @@ -842,7 +831,7 @@ Can't find all required implementations: ], solver.solve(conn, context)) - conn.put(['implementation', impl], { + self.node_volume['implementation'].update(impl, {'data': { 'spec': { '*-*': { 'commands': { @@ -852,6 +841,7 @@ Can't find all required implementations: }, 'requires': { 'sugar': {'restrictions': [['0.80', '0.87']]}, + }, }, }, }}) @@ -890,6 +880,8 @@ Can't find all required implementations: 'version': '1', 'stability': 'stable', 'notes': '', + }) + self.node_volume['implementation'].update(impl, {'data': { 'spec': { '*-*': { 'commands': { @@ -899,6 +891,7 @@ Can't find all required implementations: }, 'requires': { 'sugar': {}, + }, }, }, }}) diff --git a/tests/units/client/online_commands.py b/tests/units/client/online_commands.py index 12c5570..692fa38 100755 --- a/tests/units/client/online_commands.py +++ b/tests/units/client/online_commands.py @@ -6,6 +6,7 @@ import json import time import shutil import zipfile +from cStringIO import StringIO from os.path import exists from __init__ import tests, src_root @@ -13,6 +14,7 @@ from __init__ import tests, src_root from sugar_network import client, db from sugar_network.client import IPCClient, journal, clones, injector, commands from sugar_network.toolkit import coroutine, http +from sugar_network.toolkit.spec import Spec from sugar_network.client.commands import ClientCommands from sugar_network.resources.volume import Volume, Resource from sugar_network.resources.user import User @@ -71,6 +73,8 @@ class OnlineCommandsTest(tests.Test): 'version': '1', 'stability': 'stable', 'notes': '', + }) + self.node_volume['implementation'].update(impl, {'data': { 'spec': { '*-*': { 'commands': { @@ -78,24 +82,19 @@ class OnlineCommandsTest(tests.Test): 'exec': 'true', }, }, - 'stability': 'stable', - 'size': 0, 'extract': 'TestActivitry', }, }, - }) - bundle = zipfile.ZipFile('bundle', 'w') - bundle.writestr('TestActivitry/activity/activity.info', '\n'.join([ - '[Activity]', - 'name = TestActivitry', - 'bundle_id = %s' % context, - 'exec = false', - 'icon = icon', - 'activity_version = 1', - 'license=Public Domain', - ])) - bundle.close() - ipc.request('PUT', ['implementation', impl, 'data'], file('bundle', 'rb').read()) + 'blob': StringIO(self.zips(['TestActivitry/activity/activity.info', [ + '[Activity]', + 'name = TestActivitry', + 'bundle_id = %s' % context, + 'exec = false', + 'icon = icon', + 'activity_version = 1', + 'license=Public Domain', + ]])), + }}) assert not exists('Activities/TestActivitry/activity/activity.info') assert not exists('Activities/TestActivitry_1/activity/activity.info') @@ -174,8 +173,27 @@ class OnlineCommandsTest(tests.Test): 'version': '1', 'stability': 'stable', 'notes': '', - 'requires': ['foo'], }) + self.node_volume['implementation'].update(impl1, {'data': { + 'blob': StringIO(self.zips(('TestActivity/activity/activity.info', [ + '[Activity]', + 'name = TestActivity', + 'bundle_id = %s' % context, + 'exec = true', + 'icon = icon', + 'activity_version = 1', + 'license=GPLv3+', + ]))), + 'spec': { + '*-*': { + 'extract': 'TestActivity', + 'commands': {'activity': {'exec': 'true'}}, + 'requires': { + 'dep1': {}, + }, + }, + }, + }}) impl2 = ipc.post(['implementation'], { 'context': context, @@ -183,33 +201,27 @@ class OnlineCommandsTest(tests.Test): 'version': '2', 'stability': 'stable', 'notes': '', - 'requires': ['bar'], + }) + self.node_volume['implementation'].update(impl2, {'data': { + 'blob': StringIO(self.zips(('TestActivity/activity/activity.info', [ + '[Activity]', + 'name = TestActivity', + 'bundle_id = %s' % context, + 'exec = true', + 'icon = icon', + 'activity_version = 2', + 'license=GPLv3+', + ]))), 'spec': { '*-*': { - 'commands': { - 'activity': { - 'exec': 'true', - }, + 'extract': 'TestActivity', + 'commands': {'activity': {'exec': 'true'}}, + 'requires': { + 'dep2': {}, }, - 'stability': 'stable', - 'size': 0, - 'extract': 'TestActivitry', - 'requires': {'dep': {}}, }, }, - }) - bundle = zipfile.ZipFile('bundle', 'w') - bundle.writestr('TestActivitry/activity/activity.info', '\n'.join([ - '[Activity]', - 'name = TestActivitry', - 'bundle_id = %s' % context, - 'exec = false', - 'icon = icon', - 'activity_version = 1', - 'license=Public Domain', - ])) - bundle.close() - ipc.request('PUT', ['implementation', impl2, 'data'], file('bundle', 'rb').read()) + }}) impl3 = ipc.post(['implementation'], { 'context': context, @@ -217,21 +229,57 @@ class OnlineCommandsTest(tests.Test): 'version': '3', 'stability': 'developer', 'notes': '', - 'requires': ['bar'], }) + self.node_volume['implementation'].update(impl3, {'data': { + 'blob': StringIO(self.zips(('TestActivity/activity/activity.info', [ + '[Activity]', + 'name = TestActivity', + 'bundle_id = %s' % context, + 'exec = true', + 'icon = icon', + 'activity_version = 3', + 'license=GPLv3+', + ]))), + 'spec': { + '*-*': { + 'extract': 'TestActivity', + 'commands': {'activity': {'exec': 'true'}}, + 'requires': { + 'dep3': {}, + }, + }, + }, + }}) - assert not exists('Activities/TestActivitry/activity/activity.info') - self.assertEqual( - {'clone': 0, 'type': ['activity']}, - ipc.get(['context', context], reply=['clone'])) + ipc.put(['context', context], 2, cmd='clone', nodeps=1, requires='dep4') + coroutine.sleep(.1) + self.assertEqual({'clone': 0}, ipc.get(['context', context], reply=['clone'])) + assert not exists('Activities/TestActivity/activity/activity.info') - ipc.put(['context', context], 2, cmd='clone', nodeps=1, stability='stable', requires='bar') - coroutine.sleep(.5) + ipc.put(['context', context], 2, cmd='clone', nodeps=1) + coroutine.sleep(.1) + self.assertEqual({'clone': 2}, ipc.get(['context', context], reply=['clone'])) + self.assertEqual('2', Spec('Activities/TestActivity/activity/activity.info')['version']) - assert exists('Activities/TestActivitry/activity/activity.info') - self.assertEqual( - {'clone': 2}, - ipc.get(['context', context], reply=['clone'])) + ipc.put(['context', context], 0, cmd='clone') + coroutine.sleep(.1) + self.assertEqual({'clone': 0}, ipc.get(['context', context], reply=['clone'])) + assert not exists('Activities/TestActivity/activity/activity.info') + + ipc.put(['context', context], 2, cmd='clone', nodeps=1, stability='developer') + coroutine.sleep(.1) + self.assertEqual({'clone': 2}, ipc.get(['context', context], reply=['clone'])) + self.assertEqual('3', Spec('Activities/TestActivity/activity/activity.info')['version']) + + ipc.put(['context', context], 0, cmd='clone') + coroutine.sleep(.1) + self.assertEqual({'clone': 0}, ipc.get(['context', context], reply=['clone'])) + assert not exists('Activities/TestActivity/activity/activity.info') + + ipc.put(['context', context], 2, cmd='clone', nodeps=1, requires='dep1') + coroutine.sleep(.1) + self.assertEqual({'clone': 2}, ipc.get(['context', context], reply=['clone'])) + self.assertEqual('1', Spec('Activities/TestActivity/activity/activity.info')['version']) def test_clone_Content(self): self.start_online_client() @@ -528,14 +576,18 @@ class OnlineCommandsTest(tests.Test): 'version': '1', 'stability': 'stable', 'notes': '', - 'spec': {'*-*': {}}, }) + self.node_volume['implementation'].update(impl1, {'data': { + 'spec': {'*-*': {}}, + }}) impl2 = ipc.post(['implementation'], { 'context': context, 'license': 'GPLv3+', 'version': '2', 'stability': 'stable', 'notes': '', + }) + self.node_volume['implementation'].update(impl2, {'data': { 'spec': {'*-*': { 'requires': { 'dep1': {}, @@ -544,7 +596,7 @@ class OnlineCommandsTest(tests.Test): 'dep4': {'restrictions': [['3', None]]}, }, }}, - }) + }}) self.assertEqual({ 'name': 'title', @@ -554,6 +606,8 @@ class OnlineCommandsTest(tests.Test): 'arch': '*-*', 'stability': 'stable', 'guid': impl1, + 'unpack_size': None, + 'blob_size': None, }, { 'version': '2', @@ -566,6 +620,8 @@ class OnlineCommandsTest(tests.Test): 'dep3': {'restrictions': [[None, '2']]}, 'dep4': {'restrictions': [['3', None]]}, }, + 'unpack_size': None, + 'blob_size': None, }, ], }, @@ -587,8 +643,10 @@ class OnlineCommandsTest(tests.Test): 'version': '1', 'stability': 'stable', 'notes': '', - 'spec': {'*-*': {}}, }) + self.node_volume['implementation'].update(impl, {'data': { + 'spec': {'*-*': {}}, + }}) artifact = ipc.post(['artifact'], { 'type': 'instance', 'context': 'context', @@ -628,7 +686,14 @@ class OnlineCommandsTest(tests.Test): self.assertEqual({ 'name': 'title', - 'implementations': [{'stability': 'stable', 'guid': impl, 'arch': '*-*', 'version': '1'}], + 'implementations': [{ + 'stability': 'stable', + 'guid': impl, + 'arch': '*-*', + 'version': '1', + 'unpack_size': None, + 'blob_size': None, + }], }, ipc.get(['context', context], cmd='feed')) self.assertEqual({ @@ -638,7 +703,14 @@ class OnlineCommandsTest(tests.Test): ipc.get(['context', context], cmd='feed', layer='foo')) self.assertEqual({ 'name': 'title', - 'implementations': [{'stability': 'stable', 'guid': impl, 'arch': '*-*', 'version': '1'}], + 'implementations': [{ + 'stability': 'stable', + 'guid': impl, + 'arch': '*-*', + 'version': '1', + 'unpack_size': None, + 'blob_size': None, + }], }, ipc.get(['context', context], cmd='feed', layer='public')) @@ -686,7 +758,14 @@ class OnlineCommandsTest(tests.Test): ipc.get(['context', context], cmd='feed', layer='foo')) self.assertEqual({ 'name': 'title', - 'implementations': [{'stability': 'stable', 'guid': impl, 'arch': '*-*', 'version': '1'}], + 'implementations': [{ + 'stability': 'stable', + 'guid': impl, + 'arch': '*-*', + 'version': '1', + 'unpack_size': None, + 'blob_size': None, + }], }, ipc.get(['context', context], cmd='feed', layer='public')) @@ -706,8 +785,10 @@ class OnlineCommandsTest(tests.Test): 'version': '2', 'stability': 'stable', 'notes': '', - 'spec': {'*-*': {}}, }) + self.node_volume['implementation'].update(impl, {'data': { + 'spec': {'*-*': {}}, + }}) self.assertEqual({ 'name': 'title', @@ -717,6 +798,8 @@ class OnlineCommandsTest(tests.Test): 'arch': '*-*', 'stability': 'stable', 'guid': impl, + 'unpack_size': None, + 'blob_size': None, }, ], }, @@ -775,8 +858,10 @@ class OnlineCommandsTest(tests.Test): 'version': '1', 'stability': 'stable', 'notes': '', - 'spec': {'*-*': {}}, }) + self.node_volume['implementation'].update(impl1, {'data': { + 'spec': {'*-*': {}}, + }}) coroutine.sleep(.5) assert injector._mtime > mtime @@ -789,6 +874,8 @@ class OnlineCommandsTest(tests.Test): 'version': '2', 'stability': 'stable', 'notes': '', + }) + self.node_volume['implementation'].update(impl2, {'data': { 'spec': {'*-*': { 'requires': { 'dep1': {}, @@ -797,7 +884,7 @@ class OnlineCommandsTest(tests.Test): 'dep4': {'restrictions': [['3', None]]}, }, }}, - }) + }}) assert injector._mtime > mtime def test_ContentDisposition(self): @@ -984,6 +1071,8 @@ class OnlineCommandsTest(tests.Test): 'version': '1', 'stability': 'stable', 'notes': '', + }) + self.node_volume['implementation'].update(impl, {'data': { 'spec': { '*-*': { 'commands': { @@ -996,19 +1085,17 @@ class OnlineCommandsTest(tests.Test): 'extract': 'TestActivitry', }, }, - }) - bundle = zipfile.ZipFile('bundle', 'w') - bundle.writestr('TestActivitry/activity/activity.info', '\n'.join([ - '[Activity]', - 'name = TestActivitry', - 'bundle_id = %s' % context1, - 'exec = false', - 'icon = icon', - 'activity_version = 1', - 'license=Public Domain', - ])) - bundle.close() - ipc.request('PUT', ['implementation', impl, 'data'], file('bundle', 'rb').read()) + 'blob': StringIO(self.zips(['TestActivitry/activity/activity.info', [ + '[Activity]', + 'name = TestActivitry', + 'bundle_id = %s' % context1, + 'exec = false', + 'icon = icon', + 'activity_version = 1', + 'license=Public Domain', + ]])), + + }}) trigger = self.wait_for_events(ipc, event='update', document='context', guid=context1) ipc.put(['context', context1], 2, cmd='clone') diff --git a/tests/units/node/node.py b/tests/units/node/node.py index 5e9eb33..a1bb46b 100755 --- a/tests/units/node/node.py +++ b/tests/units/node/node.py @@ -5,6 +5,7 @@ import os import time import json from email.utils import formatdate, parsedate +from cStringIO import StringIO from os.path import exists from __init__ import tests @@ -432,12 +433,125 @@ class NodeTest(tests.Test): 'version': '1', 'stability': 'stable', 'notes': '', - 'requires': ['foo', 'bar'], }) - blob = self.zips(('topdir/probe', 'probe')) - client.request('PUT', ['implementation', impl, 'data'], blob) + blob1 = self.zips(('topdir/probe', 'probe1')) + volume['implementation'].update(impl, {'data': { + 'blob': StringIO(blob1), + 'spec': { + '*-*': { + 'requires': { + 'dep1': {}, + }, + }, + }, + }}) + impl = client.post(['implementation'], { + 'context': context, + 'license': 'GPLv3+', + 'version': '2', + 'stability': 'stable', + 'notes': '', + }) + blob2 = self.zips(('topdir/probe', 'probe2')) + volume['implementation'].update(impl, {'data': { + 'blob': StringIO(blob2), + 'spec': { + '*-*': { + 'requires': { + 'dep2': {'restrictions': [[None, '2']]}, + 'dep3': {}, + }, + }, + }, + }}) + impl = client.post(['implementation'], { + 'context': context, + 'license': 'GPLv3+', + 'version': '3', + 'stability': 'stable', + 'notes': '', + }) + blob3 = self.zips(('topdir/probe', 'probe3')) + volume['implementation'].update(impl, {'data': { + 'blob': StringIO(blob3), + 'spec': { + '*-*': { + 'requires': { + 'dep2': {'restrictions': [['2', None]]}, + }, + }, + }, + }}) + impl = client.post(['implementation'], { + 'context': context, + 'license': 'GPLv3+', + 'version': '4', + 'stability': 'developer', + 'notes': '', + }) + blob4 = self.zips(('topdir/probe', 'probe4')) + volume['implementation'].update(impl, {'data': { + 'blob': StringIO(blob4), + 'spec': { + '*-*': { + 'requires': {}, + }, + }, + }}) + + self.assertEqual(blob3, client.get(['context', context], cmd='clone')) + self.assertEqual(blob4, client.get(['context', context], cmd='clone', stability='developer')) + self.assertEqual(blob1, client.get(['context', context], cmd='clone', version='1')) + + self.assertEqual(blob1, client.get(['context', context], cmd='clone', requires='dep1')) + self.assertEqual(blob3, client.get(['context', context], cmd='clone', requires='dep2')) + self.assertEqual(blob2, client.get(['context', context], cmd='clone', requires='dep2=1')) + self.assertEqual(blob3, client.get(['context', context], cmd='clone', requires='dep2=2')) + self.assertEqual(blob2, client.get(['context', context], cmd='clone', requires='dep3')) + + self.assertRaises(http.NotFound, client.get, ['context', context], cmd='clone', requires='dep4') + self.assertRaises(http.NotFound, client.get, ['context', context], cmd='clone', stability='foo') + + def test_release(self): + volume = self.start_master() + conn = Client() + + conn.post(['context'], { + 'guid': 'bundle_id', + 'type': 'activity', + 'title': 'title', + 'summary': 'summary', + 'description': 'description', + }) + activity_info = '\n'.join([ + '[Activity]', + 'name = TestActivitry', + 'bundle_id = bundle_id', + 'exec = true', + 'icon = icon', + 'activity_version = 1', + 'license = Public Domain', + 'stability = developer', + 'requires = sugar>=0.88; dep' + ]) + bundle = self.zips(('topdir/activity/activity.info', activity_info)) + guid = json.load(conn.request('POST', ['implementation'], bundle, params={'cmd': 'release'}).raw) + + impl = volume['implementation'].get(guid) + self.assertEqual('bundle_id', impl['context']) + self.assertEqual('1', impl['version']) + self.assertEqual('developer', impl['stability']) + self.assertEqual(['Public Domain'], impl['license']) + self.assertEqual('developer', impl['stability']) + + data = impl.meta('data') + self.assertEqual('application/vnd.olpc-sugar', data['mime_type']) + self.assertEqual(len(bundle), data['blob_size']) + self.assertEqual(len(activity_info), data.get('unpack_size')) + + + - self.assertEqual(blob, client.get(['context', context], cmd='clone', version='1', stability='stable', requires=['foo', 'bar'])) def call(cp, principal=None, content=None, **kwargs): diff --git a/tests/units/node/volume.py b/tests/units/node/volume.py index 85ec1e2..15772e1 100755 --- a/tests/units/node/volume.py +++ b/tests/units/node/volume.py @@ -352,7 +352,6 @@ class VolumeTest(tests.Test): context = call(cp, method='POST', document='context', principal='principal', content={ 'guid': 'context', - 'implement': 'guid', 'type': 'package', 'title': 'title', 'summary': 'summary', diff --git a/tests/units/resources/implementation.py b/tests/units/resources/implementation.py index 6ff73cb..176051c 100755 --- a/tests/units/resources/implementation.py +++ b/tests/units/resources/implementation.py @@ -48,7 +48,7 @@ class ImplementationTest(tests.Test): _encode_version('1-')) self.assertEqual( xapian.sortable_serialise(eval('1''0000''0000''6''000')), - _encode_version('1-post')) + _encode_version('1-r')) self.assertEqual( xapian.sortable_serialise(eval('1''0000''0000''3''001')), @@ -58,112 +58,14 @@ class ImplementationTest(tests.Test): _encode_version('1-rc2')) self.assertEqual( xapian.sortable_serialise(eval('1''0000''0000''6''003')), - _encode_version('1-post3')) + _encode_version('1-r3')) self.assertEqual( xapian.sortable_serialise(eval('1''0000''0000''6''000')), - _encode_version('1-post-2-3')) + _encode_version('1-r-2-3')) self.assertEqual( xapian.sortable_serialise(eval('1''0000''0000''6''001')), - _encode_version('1-post1.2-3')) - - def test_ActivitityFiles(self): - self.start_online_client() - client = IPCClient() - - context = client.post(['context'], { - 'type': 'content', - 'title': 'title', - 'summary': 'summary', - 'description': 'description', - }) - impl = client.post(['implementation'], { - 'context': context, - 'license': 'GPLv3+', - 'version': '1', - 'stability': 'stable', - 'notes': '', - }) - client.request('PUT', ['implementation', impl, 'data'], 'blob', {'Content-Type': 'image/png'}) - self.assertEqual('image/png', self.node_volume['implementation'].get(impl).meta('data')['mime_type']) - - client.put(['context', context, 'type'], 'activity') - client.request('PUT', ['implementation', impl, 'data'], self.zips(('topdir/probe', 'probe'))) - - data = self.node_volume['implementation'].get(impl).meta('data') - self.assertEqual('application/vnd.olpc-sugar', data['mime_type']) - self.assertNotEqual(5, data['blob_size']) - self.assertEqual(5, data.get('unpack_size')) - - def test_ActivityUrls(self): - bundle = self.zips(('topdir/probe', 'probe')) - unpack_size = len('probe') - - class Files(db.CommandsProcessor): - - @route('GET', '/bundle') - def bundle(self, request, response): - return bundle - - self.start_online_client() - client = IPCClient() - files_server = coroutine.WSGIServer(('127.0.0.1', 9999), Router(Files())) - coroutine.spawn(files_server.serve_forever) - coroutine.dispatch() - - context = client.post(['context'], { - 'type': 'activity', - 'title': 'title', - 'summary': 'summary', - 'description': 'description', - }) - impl = client.post(['implementation'], { - 'context': context, - 'license': 'GPLv3+', - 'version': '1', - 'stability': 'stable', - 'notes': '', - }) - client.put(['implementation', impl, 'data'], {'url': 'http://127.0.0.1:9999/bundle'}) - - data = self.node_volume['implementation'].get(impl).meta('data') - self.assertEqual('application/vnd.olpc-sugar', data['mime_type']) - self.assertEqual(len(bundle), data['blob_size']) - self.assertEqual(unpack_size, data.get('unpack_size')) - self.assertEqual('http://127.0.0.1:9999/bundle', data['url']) - assert 'blob' not in data - - def test_ActivityASLOUrls(self): - implementation._ASLO_PATH = '.' - bundle = self.zips(('topdir/probe', 'probe')) - with file('bundle', 'w') as f: - f.write(bundle) - unpack_size = len('probe') - - self.start_online_client() - client = IPCClient() - - context = client.post(['context'], { - 'type': 'activity', - 'title': 'title', - 'summary': 'summary', - 'description': 'description', - }) - impl = client.post(['implementation'], { - 'context': context, - 'license': 'GPLv3+', - 'version': '1', - 'stability': 'stable', - 'notes': '', - }) - client.put(['implementation', impl, 'data'], {'url': 'http://download.sugarlabs.org/activities/bundle'}) - - data = self.node_volume['implementation'].get(impl).meta('data') - self.assertEqual('application/vnd.olpc-sugar', data['mime_type']) - self.assertEqual(len(bundle), data['blob_size']) - self.assertEqual(unpack_size, data.get('unpack_size')) - self.assertEqual('http://download.sugarlabs.org/activities/bundle', data['url']) - assert 'blob' not in data + _encode_version('1-r1.2-3')) def test_WrongAuthor(self): self.start_online_client() diff --git a/tests/units/toolkit/spec.py b/tests/units/toolkit/spec.py index 1cdf36e..e34b6ec 100755 --- a/tests/units/toolkit/spec.py +++ b/tests/units/toolkit/spec.py @@ -10,7 +10,7 @@ from sugar_network.toolkit import spec class SpecTest(tests.Test): - def test_Dependency(self): + def test_Dependency_versions_range(self): self.assertEqual( [], [i for i in spec._Dependency().versions_range()]) @@ -18,6 +18,12 @@ class SpecTest(tests.Test): [], [i for i in spec._Dependency({'restrictions': []}).versions_range()]) self.assertEqual( + [], + [i for i in spec._Dependency({'restrictions': [(None, '2')]}).versions_range()]) + self.assertEqual( + [], + [i for i in spec._Dependency({'restrictions': [('1', None)]}).versions_range()]) + self.assertEqual( ['1'], [i for i in spec._Dependency({'restrictions': [('1', '2')]}).versions_range()]) self.assertEqual( @@ -36,7 +42,7 @@ class SpecTest(tests.Test): def test_parse_requires(self): self.assertEqual( {'a': {}, 'b': {}, 'c': {}}, - spec._parse_requires('a; b; c')) + spec.parse_requires('a; b; c')) self.assertEqual( { @@ -44,8 +50,10 @@ class SpecTest(tests.Test): 'b': {'restrictions': [('1.2', '1.3')]}, 'c': {'restrictions': [('2.2', None)]}, 'd': {'restrictions': [(None, '3')]}, + 'e': {'restrictions': [('4.1', None)]}, + 'f': {'restrictions': [(None, '5.1')]}, }, - spec._parse_requires('a = 1; b=1.2; c>= 2.2; d <3-3')) + spec.parse_requires('a = 1; b=1.2; c>= 2.2; d <3-3; e > 4; f<=5')) self.assertEqual( { @@ -53,7 +61,7 @@ class SpecTest(tests.Test): 'b': {}, 'c': {'importance': 'recommended', 'restrictions': [(None, '1')]}, }, - spec._parse_requires('[a]; b; [c<1]')) + spec.parse_requires('[a]; b; [c<1]')) def test_parse_bindings(self): self.assertEqual( @@ -168,6 +176,39 @@ class SpecTest(tests.Test): assert pv('2-r999') < pv('3-pre1') + def test_ensure_requires(self): + assert spec.ensure_requires(spec.parse_requires(''), spec.parse_requires('')) + + assert not spec.ensure_requires(spec.parse_requires(''), spec.parse_requires('d1')) + assert spec.ensure_requires(spec.parse_requires('d1'), spec.parse_requires('')) + assert spec.ensure_requires(spec.parse_requires('d1'), spec.parse_requires('d1')) + + assert not spec.ensure_requires(spec.parse_requires(''), spec.parse_requires('d1; d2')) + assert spec.ensure_requires(spec.parse_requires('d1; d2'), spec.parse_requires('')) + assert not spec.ensure_requires(spec.parse_requires('d1'), spec.parse_requires('d1; d2')) + assert spec.ensure_requires(spec.parse_requires('d1; d2'), spec.parse_requires('d1')) + assert spec.ensure_requires(spec.parse_requires('d1; d2'), spec.parse_requires('d1; d2')) + + assert spec.ensure_requires(spec.parse_requires('d1'), spec.parse_requires('d1 < 1')) + assert spec.ensure_requires(spec.parse_requires('d1 < 1'), spec.parse_requires('d1')) + assert spec.ensure_requires(spec.parse_requires('d1 < 1'), spec.parse_requires('d1 < 2')) + assert spec.ensure_requires(spec.parse_requires('d1 < 2'), spec.parse_requires('d1 < 1')) + + assert spec.ensure_requires(spec.parse_requires('d1'), spec.parse_requires('d1 > 1')) + assert spec.ensure_requires(spec.parse_requires('d1 > 1'), spec.parse_requires('d1')) + assert spec.ensure_requires(spec.parse_requires('d1 > 1'), spec.parse_requires('d1 > 2')) + assert spec.ensure_requires(spec.parse_requires('d1 > 2'), spec.parse_requires('d1 > 1')) + + assert spec.ensure_requires(spec.parse_requires('d1'), spec.parse_requires('d1 > 1; d1 < 2')) + assert spec.ensure_requires(spec.parse_requires('d1 > 1; d1 < 2'), spec.parse_requires('d1')) + assert spec.ensure_requires(spec.parse_requires('d1 > 1; d1 < 2'), spec.parse_requires('d1 > 0; d1 < 3')) + assert spec.ensure_requires(spec.parse_requires('d1 > 0; d1 < 3'), spec.parse_requires('d1 > 1; d1 < 2')) + + assert spec.ensure_requires(spec.parse_requires('d1 > 1; d1 <= 2'), spec.parse_requires('d1 >= 2; d1 < 3')) + assert spec.ensure_requires(spec.parse_requires('d1 >= 1; d1 < 2'), spec.parse_requires('d1 > 0; d1 <= 1')) + assert not spec.ensure_requires(spec.parse_requires('d1 > 1; d1 < 2'), spec.parse_requires('d1 > 2; d1 < 3')) + assert not spec.ensure_requires(spec.parse_requires('d1 > 1; d1 < 2'), spec.parse_requires('d1 > 0; d1 < 1')) + if __name__ == '__main__': tests.main() |