Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Workshop.activity/MANIFEST5
-rw-r--r--Workshop.activity/Rating.py144
-rw-r--r--Workshop.activity/TutorialStoreCategories.py28
-rw-r--r--Workshop.activity/TutorialStoreDetails.py74
-rw-r--r--Workshop.activity/TutorialStoreHome.py109
-rw-r--r--Workshop.activity/TutorialStoreResults.py124
-rw-r--r--Workshop.activity/TutorialStoreSearch.py33
-rw-r--r--Workshop.activity/TutorialStoreSuggestion.py139
-rwxr-xr-xWorkshop.activity/TutoriusActivity.py77
-rw-r--r--Workshop.activity/Workshop.py455
-rw-r--r--Workshop.activity/WorkshopController.py127
-rw-r--r--Workshop.activity/WorkshopModel.py245
-rw-r--r--Workshop.activity/activity/activity.info8
-rw-r--r--Workshop.activity/activity/someicon.svg21
-rw-r--r--Workshop.activity/arrow_back.pngbin0 -> 310 bytes
-rw-r--r--Workshop.activity/dialogs.py237
-rw-r--r--Workshop.activity/full_star.pngbin0 -> 1031 bytes
-rw-r--r--Workshop.activity/grayed_star.pngbin0 -> 930 bytes
-rw-r--r--Workshop.activity/half_star.pngbin0 -> 890 bytes
-rw-r--r--Workshop.activity/icon.svg21
-rwxr-xr-xWorkshop.activity/setup.py3
-rw-r--r--Workshop.activity/tests/MTTests.py169
-rw-r--r--Workshop.activity/tests/mocks.py134
-rw-r--r--Workshop.activity/tests/run-tests.py20
24 files changed, 2173 insertions, 0 deletions
diff --git a/Workshop.activity/MANIFEST b/Workshop.activity/MANIFEST
new file mode 100644
index 0000000..c5c6d42
--- /dev/null
+++ b/Workshop.activity/MANIFEST
@@ -0,0 +1,5 @@
+TutoriusActivity.py
+activity/someicon.svg
+activity/activity.info
+setup.py
+MANIFEST
diff --git a/Workshop.activity/Rating.py b/Workshop.activity/Rating.py
new file mode 100644
index 0000000..a13e5a2
--- /dev/null
+++ b/Workshop.activity/Rating.py
@@ -0,0 +1,144 @@
+import gtk
+from gtk import gdk
+import logging
+
+class Rating(gtk.Widget):
+ """
+ Controls that display the rating of a tutorial using colored stars
+ """
+ def __init__(self,tutorial,controller, rating=0,editable = False):
+ """
+ Constructor
+
+ @param the controller to link the view with
+ @param tutorial The tutorial for which this rating is
+ @param rating The rating to show
+ @param editable True if the rating may be edited
+ """
+ gtk.Widget.__init__(self)
+
+ self.tutorial = tutorial
+ self.controller = controller
+ self.editable = editable
+ self.rating = rating
+
+ #star size is 24 pixels by 24 pixels
+ self.image_length = 24
+
+ def do_realize(self):
+ self.set_flags(self.flags() | gtk.REALIZED)
+
+ self.window = gtk.gdk.Window(
+ self.get_parent_window(),
+ width=self.allocation.width,
+ height=self.allocation.height,
+ window_type=gdk.WINDOW_CHILD,
+ wclass=gdk.INPUT_OUTPUT,
+ event_mask=self.get_events() | gtk.gdk.EXPOSURE_MASK
+ | gtk.gdk.BUTTON_PRESS_MASK)
+
+ self.window.set_user_data(self)
+
+ self.style.attach(self.window)
+
+ self.style.set_background(self.window, gtk.STATE_NORMAL)
+ self.window.move_resize(*self.allocation)
+
+ #load the stars
+ pixbuf = gtk.gdk.pixbuf_new_from_file('full_star.png')
+ self.full_star,mask = pixbuf.render_pixmap_and_mask()
+
+ pixbuf = gtk.gdk.pixbuf_new_from_file('half_star.png')
+ self.half_star,mask = pixbuf.render_pixmap_and_mask()
+
+ image = gtk.Image()
+ pixbuf = gtk.gdk.pixbuf_new_from_file('grayed_star.png')
+ self.empty_star,mask =pixbuf.render_pixmap_and_mask()
+
+ self.gc = self.style.fg_gc[gtk.STATE_NORMAL]
+
+ def do_unrealize(self):
+ self.window.destroy()
+
+ def do_size_request(self, requisition):
+ requisition.height = self.image_length
+ requisition.width = (self.image_length * 5)
+
+ def do_size_allocate(self, allocation):
+ self.allocation = allocation
+ if self.flags() & gtk.REALIZED:
+ self.window.move_resize(*allocation)
+
+ def do_expose_event(self, event):
+ """
+ The widget is drawn here
+ """
+ value = self.rating
+ stars = [0,0,0,0,0]
+ if value > 0:
+ for x in range(5):
+ if value -1 > 0:
+ stars[x]=1
+ elif value -1 == -0.5:
+ stars[x] = 0.5
+ break
+ else:
+ stars[x]=1
+ break
+ value -= 1
+
+ for x in range(0,5):
+ if stars[x] == 0:
+ self.window.draw_drawable(self.gc, self.empty_star, 0, 0
+ , x*self.image_length
+ , 0,-1, -1)
+ elif stars[x] == 0.5:
+ self.window.draw_drawable(self.gc, self.half_star, 0, 0
+ , x*self.image_length
+ , 0,-1, -1)
+ elif stars[x] == 1:
+ self.window.draw_drawable(self.gc, self.full_star, 0, 0
+ , x*self.image_length
+ , 0,-1, -1)
+
+ def do_button_press_event(self, event):
+ """When the button is pressed"""
+
+ # make sure it was the first button
+ if self.editable:
+ if event.button == 1:
+ #check for new stars
+ self.check_for_new_stars(event.x)
+
+ return True
+
+ def check_for_new_stars(self, xPos):
+ """
+ Computes the star number based on where the click was
+ """
+
+ new_stars = int(xPos / self.image_length)
+ half_star = xPos % self.image_length
+
+ logging.info("xpos: %d, new_stars: %d, half_star: %d",xPos,new_stars,half_star)
+ if half_star > self.image_length/2:
+ new_stars +=1
+ else:
+ new_stars = new_stars+0.5
+ logging.info("rating: %f",new_stars)
+ self.controller.rate_tutorial(self.tutorial,new_stars)
+
+ self.set_value(new_stars)
+
+ def set_value(self, value):
+ """
+ Sets the value and force a redraw
+ """
+
+ if (value >= 0):
+ self.rating = value
+ #check for the maximum
+ if (self.rating > 5):
+ self.rating = 5
+ # redraw the widget
+ self.queue_draw() \ No newline at end of file
diff --git a/Workshop.activity/TutorialStoreCategories.py b/Workshop.activity/TutorialStoreCategories.py
new file mode 100644
index 0000000..2bf843d
--- /dev/null
+++ b/Workshop.activity/TutorialStoreCategories.py
@@ -0,0 +1,28 @@
+import sys, os
+import gtk
+
+class TutorialStoreCategories:
+
+ def __init__(self):
+ categorie_math = gtk.Label('Math (8)')
+ categorie_physics = gtk.Label('Phyisics (16)')
+ categorie_history = gtk.Label('History (32)')
+ categorie_learning = gtk.Label('Learning (53)')
+
+ categorie_box = gtk.VBox(True, 5)
+ self.categorie_box_frame = gtk.Frame('Categories')
+ self.categorie_box_frame.modify_bg(gtk.STATE_NORMAL, gtk.gdk.Color(0,0,0))
+
+ categorie_box.pack_start(categorie_math, True, True, 4)
+ categorie_box.pack_start(categorie_physics, True, True, 4)
+ categorie_box.pack_start(categorie_history, True, True, 4)
+ categorie_box.pack_start(categorie_learning, True, True, 4)
+
+ self.categorie_box_frame.add(categorie_box)
+
+ categorie_math.show()
+ categorie_physics.show()
+ categorie_history.show()
+ categorie_learning.show()
+ categorie_box.show()
+ self.categorie_box_frame.show() \ No newline at end of file
diff --git a/Workshop.activity/TutorialStoreDetails.py b/Workshop.activity/TutorialStoreDetails.py
new file mode 100644
index 0000000..67b85d0
--- /dev/null
+++ b/Workshop.activity/TutorialStoreDetails.py
@@ -0,0 +1,74 @@
+import sys, os
+import gtk
+
+class TutorialStoreDetails:
+
+ def __init__(self):
+ tuto_icon = gtk.Image()
+ tuto_icon.set_from_file('icon.svg')
+
+ full_star_icon1 = gtk.Image()
+ full_star_icon1.set_from_file('full_star.svg')
+ full_star_icon2 = gtk.Image()
+ full_star_icon2.set_from_file('full_star.svg')
+ full_star_icon3 = gtk.Image()
+ full_star_icon3.set_from_file('full_star.svg')
+
+ grayed_star_icon = gtk.Image()
+ grayed_star_icon.set_from_file('grayed_star.svg')
+
+ half_star_icon= gtk.Image()
+ half_star_icon.set_from_file('half_star.svg')
+
+ title_autor_box = gtk.VBox()
+ tuto_title_label = gtk.Label('<Title>')
+ tuto_author_label = gtk.Label('by <Author>')
+ title_autor_box.pack_start(tuto_title_label)
+ title_autor_box.pack_start(tuto_author_label)
+
+ tuto_descrip_label = gtk.Label('Description 1 : Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.')
+ tuto_descrip_label.set_line_wrap(True)
+ tuto_descrip_label.set_width_chars(50)
+
+ download_button = gtk.Button('Download')
+ infos_button = gtk.Button('Informations')
+ comp_button = gtk.Button('Compatibility')
+
+ tutorial_title_bar = gtk.HBox (False, 4)
+ tutorial_title_bar.pack_start(tuto_icon)
+ tutorial_title_bar.pack_start(title_autor_box)
+ tutorial_title_bar.pack_start(full_star_icon1)
+ tutorial_title_bar.pack_start(full_star_icon2)
+ tutorial_title_bar.pack_start(full_star_icon3)
+ tutorial_title_bar.pack_start(half_star_icon)
+ tutorial_title_bar.pack_start(grayed_star_icon)
+ tutorial_button_bar = gtk.HBox(True, 25)
+ tutorial_button_bar.pack_start(download_button)
+ tutorial_button_bar.pack_start(infos_button)
+ tutorial_button_bar.pack_start(comp_button)
+
+ self.tutorial_store_details = gtk.VBox (False, 5)
+ back_button = gtk.Button('Back')
+
+ self.tutorial_store_details.pack_start(back_button)
+ self.tutorial_store_details.pack_start(tutorial_title_bar)
+ self.tutorial_store_details.pack_start(tuto_descrip_label)
+ self.tutorial_store_details.pack_start(tutorial_button_bar)
+
+
+ tuto_icon.show()
+ full_star_icon1.show()
+ full_star_icon2.show()
+ full_star_icon3.show()
+ grayed_star_icon.show()
+ half_star_icon.show()
+ tuto_title_label.show()
+ tuto_author_label.show()
+ title_autor_box.show()
+ tuto_descrip_label.show()
+ tutorial_title_bar.show()
+ tutorial_button_bar.show()
+ download_button.show()
+ infos_button.show()
+ comp_button.show()
+ self.tutorial_store_details.show()
diff --git a/Workshop.activity/TutorialStoreHome.py b/Workshop.activity/TutorialStoreHome.py
new file mode 100644
index 0000000..8a361dc
--- /dev/null
+++ b/Workshop.activity/TutorialStoreHome.py
@@ -0,0 +1,109 @@
+import logging
+import TutorialStoreCategories
+import TutorialStoreSearch
+import TutorialStoreSuggestion
+import TutorialStoreResults
+import TutorialStoreDetails
+
+import sys, os
+import gtk
+
+class TutorialStoreHome:
+ def log(self,widget,data=None):
+ logging.info('Tutorial Store Home start')
+
+ def __init__(self):
+
+ self.categories = TutorialStoreCategories.TutorialStoreCategories()
+ categories_frame = self.categories.categorie_box_frame
+
+ self.search = TutorialStoreSearch.TutorialStoreSearch()
+ tutorial_store_search = self.search.tutorial_store_search
+
+ self.search_button = self.search.search_button_access()
+
+ self.suggestion = TutorialStoreSuggestion.TutorialStoreSuggestion()
+
+ tut_store_suggestion = gtk.HBox(homogeneous=True, spacing=5)
+ tut_store_suggestion.pack_start(self.suggestion.top_five_frame, expand=False, fill=False, padding=0)
+ tut_store_suggestion.pack_start(self.suggestion.also_like_frame, expand=False, fill=False, padding=0)
+
+ self.results = TutorialStoreResults.TutorialStoreResults()
+
+ tut_store_home_base = gtk.VBox(False, 5)
+ tut_store_home_base.pack_start(tutorial_store_search, False, False, 25)
+ tut_store_home_base.pack_start(tut_store_suggestion, False, False, 0)
+
+ self.labeltest = gtk.Label('Test')
+
+ self.tutorial_store_home = gtk.HBox(False, 5)
+ self.tutorial_store_home.pack_start(categories_frame, True, True, 5)
+ self.tutorial_store_home.pack_start(tut_store_home_base, True, True, 5)
+ self.tutorial_store_home.pack_start(tut_store_home_base, True, True, 5)
+
+ tut_store_suggestion.show()
+ categories_frame.show()
+ tut_store_home_base.show()
+ self.tutorial_store_home.show()
+
+ def get_search_button(self):
+
+ return self.search_button
+
+ def get_more_button(self):
+ return self.suggestion.get_more_button()
+
+ def get_results_widget(self):
+
+ self.search = TutorialStoreSearch.TutorialStoreSearch()
+ tutorial_store_search = self.search.tutorial_store_search
+
+ self.results = TutorialStoreResults.TutorialStoreResults()
+ tutorial_store_results = self.results.tutorial_store_results
+
+ self.categories = TutorialStoreCategories.TutorialStoreCategories()
+ categories_frame = self.categories.categorie_box_frame
+
+ tut_store_home_base = gtk.VBox(False, 5)
+ tut_store_home_base.pack_start(tutorial_store_search, False, False, 25)
+ tut_store_home_base.pack_start(tutorial_store_results, False, False, 0)
+
+ self.tutorial_store_home = gtk.HBox(False, 5)
+ self.tutorial_store_home.pack_start(categories_frame, True, True, 5)
+ self.tutorial_store_home.pack_start(tut_store_home_base, True, True, 5)
+
+ tut_store_home_base.show()
+ tutorial_store_search.show()
+ tutorial_store_results.show()
+ categories_frame.show()
+ self.tutorial_store_home.show()
+
+ return self.tutorial_store_home
+
+ def get_details_widget(self):
+
+ self.search = TutorialStoreSearch.TutorialStoreSearch()
+ tutorial_store_search = self.search.tutorial_store_search
+
+ self.details = TutorialStoreDetails.TutorialStoreDetails()
+ tutorial_store_details = self.details.tutorial_store_details
+
+ self.categories = TutorialStoreCategories.TutorialStoreCategories()
+ categories_frame = self.categories.categorie_box_frame
+
+ tut_store_home_base = gtk.VBox(False, 5)
+ tut_store_home_base.pack_start(tutorial_store_search, False, False, 25)
+ tut_store_home_base.pack_start(tutorial_store_details, False, False, 0)
+
+ self.tutorial_store_home = gtk.HBox(False, 5)
+ self.tutorial_store_home.pack_start(categories_frame, True, True, 5)
+ self.tutorial_store_home.pack_start(tut_store_home_base, True, True, 5)
+
+ tut_store_home_base.show()
+ tutorial_store_search.show()
+ tutorial_store_details.show()
+ categories_frame.show()
+ self.tutorial_store_home.show()
+
+ return self.tutorial_store_home
+
diff --git a/Workshop.activity/TutorialStoreResults.py b/Workshop.activity/TutorialStoreResults.py
new file mode 100644
index 0000000..0e8e54c
--- /dev/null
+++ b/Workshop.activity/TutorialStoreResults.py
@@ -0,0 +1,124 @@
+import sys, os
+import gtk
+
+class TutorialStoreResults:
+
+ def __init__(self):
+ tuto_icon1 = gtk.Image()
+ tuto_icon1.set_from_file('icon.svg')
+ tuto_icon2 = gtk.Image()
+ tuto_icon2.set_from_file('icon.svg')
+
+ full_star_icon1 = gtk.Image()
+ full_star_icon1.set_from_file('full_star.svg')
+ full_star_icon2 = gtk.Image()
+ full_star_icon2.set_from_file('full_star.svg')
+ full_star_icon3 = gtk.Image()
+ full_star_icon3.set_from_file('full_star.svg')
+ full_star_icon4 = gtk.Image()
+ full_star_icon4.set_from_file('full_star.svg')
+ full_star_icon5 = gtk.Image()
+ full_star_icon5.set_from_file('full_star.svg')
+
+ grayed_star_icon1 = gtk.Image()
+ grayed_star_icon1.set_from_file('grayed_star.svg')
+ grayed_star_icon2 = gtk.Image()
+ grayed_star_icon2.set_from_file('grayed_star.svg')
+ grayed_star_icon3 = gtk.Image()
+ grayed_star_icon3.set_from_file('grayed_star.svg')
+ grayed_star_icon4 = gtk.Image()
+ grayed_star_icon4.set_from_file('grayed_star.svg')
+
+ half_star_icon1= gtk.Image()
+ half_star_icon1.set_from_file('half_star.svg')
+
+ tuto_title_label1 = gtk.Label('Titre 1')
+ tuto_title_label2 = gtk.Label('Titre 2')
+
+ tuto_descrip_label1 = gtk.Label('Description 1 : Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.')
+ tuto_descrip_label1.set_line_wrap(True)
+ tuto_descrip_label1.set_width_chars(50)
+ tuto_descrip_label2 = gtk.Label('Description 2 : Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.')
+ tuto_descrip_label2.set_line_wrap(True)
+ tuto_descrip_label2.set_width_chars(50)
+
+ download_button1 = gtk.Button('Download')
+ download_button2 = gtk.Button('Download')
+ details_button1 = gtk.Button('Details')
+ details_button2 = gtk.Button('Details')
+
+ show_resuls_label = gtk.Label('Showing results 1-29 of 109')
+ # TODO : Probably must be something else than a label, or find a way to listen to click event on label ...
+ next_results_label = gtk.Label('< 1 2 3 4 >')
+
+ tutorial_title_bar1 = gtk.HBox (False, 0)
+ tutorial_title_bar1.pack_start(tuto_icon1)
+ tutorial_title_bar1.pack_start(tuto_title_label1)
+ tutorial_title_bar1.pack_start(full_star_icon1)
+ tutorial_title_bar1.pack_start(full_star_icon2)
+ tutorial_title_bar1.pack_start(full_star_icon3)
+ tutorial_title_bar1.pack_start(grayed_star_icon1)
+ tutorial_title_bar1.pack_start(grayed_star_icon2)
+
+ tutorial_button_bar1 = gtk.HBox(True, 25)
+ tutorial_button_bar1.pack_start(download_button1)
+ tutorial_button_bar1.pack_start(details_button1)
+
+ tutorial1 = gtk.VBox (False, 5)
+ tutorial1.pack_start(tutorial_title_bar1)
+ tutorial1.pack_start(tuto_descrip_label1)
+ tutorial1.pack_start(tutorial_button_bar1)
+
+ tutorial_title_bar2 = gtk.HBox (False, 5)
+ tutorial_title_bar2.pack_start(tuto_icon2)
+ tutorial_title_bar2.pack_start(tuto_title_label2)
+ tutorial_title_bar2.pack_start(full_star_icon4)
+ tutorial_title_bar2.pack_start(full_star_icon5)
+ tutorial_title_bar2.pack_start(half_star_icon1)
+ tutorial_title_bar2.pack_start(grayed_star_icon3)
+ tutorial_title_bar2.pack_start(grayed_star_icon4)
+
+ tutorial_button_bar2 = gtk.HBox(True, 55)
+ tutorial_button_bar2.pack_start(download_button2)
+ tutorial_button_bar2.pack_start(details_button2)
+
+ tutorial2 = gtk.VBox (False, 5)
+ tutorial2.pack_start(tutorial_title_bar2)
+ tutorial2.pack_start(tuto_descrip_label2)
+ tutorial2.pack_start(tutorial_button_bar2)
+
+ self.tutorial_store_results = gtk.VBox(False, 10)
+ self.tutorial_store_results.pack_start(tutorial1, True, True, 10)
+ self.tutorial_store_results.pack_start(tutorial2, True, True, 10)
+ self.tutorial_store_results.pack_start(show_resuls_label)
+ self.tutorial_store_results.pack_start(next_results_label)
+
+ tuto_icon1.show()
+ tuto_icon2.show()
+ full_star_icon1.show()
+ full_star_icon2.show()
+ full_star_icon3.show()
+ full_star_icon4.show()
+ full_star_icon5.show()
+ grayed_star_icon1.show()
+ grayed_star_icon2.show()
+ grayed_star_icon3.show()
+ grayed_star_icon4.show()
+ half_star_icon1.show()
+ tuto_title_label1.show()
+ tuto_title_label2.show()
+ tuto_descrip_label1.show()
+ tuto_descrip_label2.show()
+ download_button1.show()
+ download_button2.show()
+ details_button1.show()
+ details_button2.show()
+ show_resuls_label.show()
+ next_results_label.show()
+ tutorial_title_bar1.show()
+ tutorial_title_bar2.show()
+ tutorial_button_bar1.show()
+ tutorial_button_bar2.show()
+ tutorial1.show()
+ tutorial2.show()
+ self.tutorial_store_results.show() \ No newline at end of file
diff --git a/Workshop.activity/TutorialStoreSearch.py b/Workshop.activity/TutorialStoreSearch.py
new file mode 100644
index 0000000..1663f80
--- /dev/null
+++ b/Workshop.activity/TutorialStoreSearch.py
@@ -0,0 +1,33 @@
+import sys, os
+import gtk
+
+class TutorialStoreSearch:
+
+ def __init__(self):
+ search_label = gtk.Label('Search :')
+ search_box = gtk.Entry(400)
+ in_label = gtk.Label('in')
+ search_combobox = gtk.combo_box_new_text()
+ search_combobox.insert_text(0, 'all Categories (109)')
+ search_combobox.insert_text(1, 'Math (8)')
+ search_combobox.insert_text(2, 'Physics (16)')
+ search_combobox.insert_text(3, 'History (32)')
+ search_combobox.insert_text(4, 'Learning (53)')
+ self.search_button = gtk.Button('Search')
+
+ self.tutorial_store_search = gtk.HBox(False, 5)
+ self.tutorial_store_search.pack_start(search_label, True, True, 5)
+ self.tutorial_store_search.pack_start(search_box, True, True, 5)
+ self.tutorial_store_search.pack_start(in_label, True, True, 5)
+ self.tutorial_store_search.pack_start(search_combobox, True, True, 5)
+ self.tutorial_store_search.pack_start(self.search_button, True, True, 5)
+
+ search_label.show()
+ search_box.show()
+ in_label.show()
+ search_combobox.show()
+ self.search_button.show()
+ self.tutorial_store_search.show()
+
+ def search_button_access(self):
+ return self.search_button \ No newline at end of file
diff --git a/Workshop.activity/TutorialStoreSuggestion.py b/Workshop.activity/TutorialStoreSuggestion.py
new file mode 100644
index 0000000..9164ca0
--- /dev/null
+++ b/Workshop.activity/TutorialStoreSuggestion.py
@@ -0,0 +1,139 @@
+import sys, os
+import gtk
+
+class TutorialStoreSuggestion:
+
+ def __init__(self):
+
+ tutorial1 = gtk.HBox(homogeneous=True, spacing=0)
+ tutorial2 = gtk.HBox(homogeneous=True, spacing=0)
+ tutorial3 = gtk.HBox(homogeneous=True, spacing=0)
+ tutorial4 = gtk.HBox(homogeneous=True, spacing=0)
+ tutorial5 = gtk.HBox(homogeneous=True, spacing=0)
+ tutorial6 = gtk.HBox(homogeneous=True, spacing=0)
+
+ icon1 = gtk.Image()
+ icon1.set_from_file('icon.svg')
+ icon2 = gtk.Image()
+ icon2.set_from_file('icon.svg')
+ icon3 = gtk.Image()
+ icon3.set_from_file('icon.svg')
+ icon4 = gtk.Image()
+ icon4.set_from_file('icon.svg')
+ icon5 = gtk.Image()
+ icon5.set_from_file('icon.svg')
+ icon6 = gtk.Image()
+ icon6.set_from_file('icon.svg')
+
+ label1 = gtk.Label('Tuto 1')
+ label2 = gtk.Label('Tuto 2')
+ label3 = gtk.Label('Tuto 3')
+
+ label4 = gtk.Label('Tuto 4')
+ label5 = gtk.Label('Tuto 5')
+ label6 = gtk.Label('Tuto 6')
+
+ self.more_button1 = gtk.Button('More')
+ more_button2 = gtk.Button('More')
+ more_button3 = gtk.Button('More')
+ more_button4 = gtk.Button('More')
+ more_button5 = gtk.Button('More')
+ more_button6 = gtk.Button('More')
+
+ tutorial1.pack_start(icon1, expand=True, fill=True, padding=4)
+ tutorial1.pack_start(label1, expand=True, fill=True, padding=0)
+ tutorial1.pack_start(self.more_button1, expand=False, fill=False, padding=5)
+ tutorial1_frame = gtk.Frame()
+ tutorial1_frame.add(tutorial1)
+ tutorial1_frame.modify_bg(gtk.STATE_NORMAL, gtk.gdk.Color(0,0,0))
+
+ tutorial2.pack_start(icon2, expand=True, fill=True, padding=4)
+ tutorial2.pack_start(label2, expand=True, fill=True, padding=0)
+ tutorial2.pack_start(more_button2, expand=False, fill=False, padding=5)
+ tutorial2_frame = gtk.Frame()
+ tutorial2_frame.add(tutorial2)
+ tutorial2_frame.modify_bg(gtk.STATE_NORMAL, gtk.gdk.Color(0,0,0))
+
+ tutorial3.pack_start(icon3, expand=True, fill=True, padding=4)
+ tutorial3.pack_start(label3, expand=True, fill=True, padding=0)
+ tutorial3.pack_start(more_button3, expand=False, fill=False, padding=5)
+ tutorial3_frame = gtk.Frame()
+ tutorial3_frame.add(tutorial3)
+ tutorial3_frame.modify_bg(gtk.STATE_NORMAL, gtk.gdk.Color(0,0,0))
+
+ tutorial4.pack_start(icon4, expand=True, fill=True, padding=4)
+ tutorial4.pack_start(label4, expand=True, fill=True, padding=0)
+ tutorial4.pack_start(more_button4, expand=False, fill=False, padding=5)
+ tutorial4_frame = gtk.Frame()
+ tutorial4_frame.add(tutorial4)
+ tutorial4_frame.modify_bg(gtk.STATE_NORMAL, gtk.gdk.Color(0,0,0))
+
+ tutorial5.pack_start(icon5, expand=True, fill=True, padding=4)
+ tutorial5.pack_start(label5, expand=True, fill=True, padding=0)
+ tutorial5.pack_start(more_button5, expand=False, fill=False, padding=5)
+ tutorial5_frame = gtk.Frame()
+ tutorial5_frame.add(tutorial5)
+ tutorial5_frame.modify_bg(gtk.STATE_NORMAL, gtk.gdk.Color(0,0,0))
+
+ tutorial6.pack_start(icon6, expand=True, fill=True, padding=4)
+ tutorial6.pack_start(label6, expand=True, fill=True, padding=0)
+ tutorial6.pack_start(more_button6, expand=False, fill=False, padding=5)
+ tutorial6_frame = gtk.Frame()
+ tutorial6_frame.add(tutorial6)
+ tutorial6_frame.modify_bg(gtk.STATE_NORMAL, gtk.gdk.Color(0,0,0))
+
+ top_five = gtk.VBox(homogeneous=True, spacing=0)
+ self.top_five_frame = gtk.Frame('Top 5 Most Popular')
+## top_five_frame.modify_bg(gtk.STATE_NORMAL, gtk.gdk.Color(0,0,0))
+
+ top_five.pack_start(tutorial1_frame, expand=True, fill=True, padding=0)
+ top_five.pack_start(tutorial2_frame, expand=True, fill=True, padding=0)
+ top_five.pack_start(tutorial3_frame, expand=True, fill=True, padding=0)
+
+ also_like = gtk.VBox(homogeneous=True, spacing=0)
+ self.also_like_frame = gtk.Frame('You might also like :')
+
+ also_like.pack_start(tutorial4_frame, expand=True, fill=True, padding=0)
+ also_like.pack_start(tutorial5_frame, expand=True, fill=True, padding=0)
+ also_like.pack_start(tutorial6_frame, expand=True, fill=True, padding=0)
+
+ self.top_five_frame.add(top_five)
+ self.also_like_frame.add(also_like)
+
+ label1.show()
+ label2.show()
+ label3.show()
+ label4.show()
+ label5.show()
+ label6.show()
+ icon1.show()
+ icon2.show()
+ icon3.show()
+ icon4.show()
+ icon5.show()
+ icon6.show()
+ self.more_button1.show()
+ more_button2.show()
+ more_button3.show()
+ more_button4.show()
+ more_button5.show()
+ more_button6.show()
+ tutorial1_frame.show()
+ tutorial2_frame.show()
+ tutorial3_frame.show()
+ tutorial4_frame.show()
+ tutorial5_frame.show()
+ tutorial6_frame.show()
+ tutorial1.show()
+ tutorial2.show()
+ tutorial3.show()
+ tutorial4.show()
+ tutorial5.show()
+ tutorial6.show()
+ top_five.show()
+ self.top_five_frame.show()
+ also_like.show()
+ self.also_like_frame.show()
+
+ def get_more_button(self):
+ return self.more_button1 \ No newline at end of file
diff --git a/Workshop.activity/TutoriusActivity.py b/Workshop.activity/TutoriusActivity.py
new file mode 100755
index 0000000..b262a9f
--- /dev/null
+++ b/Workshop.activity/TutoriusActivity.py
@@ -0,0 +1,77 @@
+from sugar.activity import activity
+import TutorialStoreHome
+from Workshop import WorkshopView
+import logging
+
+import sys, os
+import gtk
+from dialogs import LoginDialog
+
+class TutoriusActivity(activity.Activity):
+ def hello(self,widget,data=None):
+ logging.info('Hello world')
+
+ def callback(self, widget, button_string):
+
+ if button_string == 'search_button':
+ self.right_container.remove(self.tutorial_store_home.tutorial_store_home)
+
+ results_widget = self.tutorial_store_home.get_results_widget()
+
+ self.right_container.pack_start(results_widget)
+
+ results_widget.show()
+ self.right_container.show()
+
+ elif button_string == 'more_button':
+ self.right_container.remove(self.tutorial_store_home.tutorial_store_home)
+
+ details_widget = self.tutorial_store_home.get_details_widget()
+
+ self.right_container.pack_start(details_widget)
+
+ details_widget.show()
+ self.right_container.show()
+
+ def __init__(self,handle):
+ print "running activity init", handle
+ activity.Activity.__init__(self,handle)
+ print "actiity running"
+
+ toolbox = activity.ActivityToolbox(self)
+ self.set_toolbox(toolbox)
+ toolbox.show()
+
+ self.table = gtk.HPaned()
+ self.table.set_position(100)
+ self.left_container = gtk.VBox()
+ btn1 = gtk.Button("My tutorials")
+ btn2 = gtk.Button("Tutorial Store")
+ btn3 = gtk.Button("test button")
+
+ self.left_container.pack_start(btn1,expand=False)
+ self.left_container.pack_start(btn2,expand=False)
+ self.tutorial_store_home = TutorialStoreHome.TutorialStoreHome()
+
+ tutorial_store_search_button = self.tutorial_store_home.get_search_button()
+ tutorial_store_search_button.connect("clicked", self.callback, 'search_button')
+
+ tutorial_store_more_button = self.tutorial_store_home.get_more_button()
+ tutorial_store_more_button.connect("clicked", self.callback, 'more_button')
+
+ self.right_container = gtk.VBox()
+ #self.right_container.pack_start(self.tutorial_store_home.tutorial_store_home)
+
+ self.workshop = WorkshopView()
+
+ self.table.add1(self.left_container)
+ self.table.add2(self.workshop)
+ self.set_canvas(self.table)
+ btn3.show()
+ btn1.show()
+ btn2.show()
+ self.left_container.show()
+ self.workshop.show()
+ self.table.show()
+
+ print "AT THE END OF THE CLASS"
diff --git a/Workshop.activity/Workshop.py b/Workshop.activity/Workshop.py
new file mode 100644
index 0000000..6958d2c
--- /dev/null
+++ b/Workshop.activity/Workshop.py
@@ -0,0 +1,455 @@
+import gtk
+import gobject
+
+from WorkshopListItem import WorkshopListItem
+from Rating import Rating
+from WorkshopModel import WorkshopModel
+from WorkshopController import WorkshopController
+from dialogs import InfoDialog
+import operator
+
+class WorkshopView(gtk.Alignment):
+ """
+ Main container for the Workshop Mytutorial Part
+ """
+ def __init__(self):
+ """
+ Constructor
+ """
+ gtk.Alignment.__init__(self,0.0,0.0,1.0,1.0)
+
+ #Register Rating as a gobject
+ gobject.type_register(Rating)
+
+ #create the model and the controller
+ self.model = WorkshopModel(self)
+ self.controller = WorkshopController(self,self.model)
+
+ #Create the main view
+ self.mainView = WorkshopMain(self.controller)
+ self.detailView = None
+
+ #display the main view
+ self.add(self.mainView)
+ self.mainView.show()
+
+ #display all tutorials
+ self.controller.tutorial_query(None,None)
+
+ def set_tutorial_list(self,tutorial_list):
+ """
+ Set the list of tutorial to display in the main View
+ Refresh the View
+
+ @param tutorial_list the list of tutorial
+ """
+ self.mainView.set_tutorial_list(tutorial_list)
+
+
+ def change_sorting(self,sorting_key):
+ """
+ Sort the list of tutorial base on the sorting_key
+
+ @param sorting_key the tutorial metadata to use to sort the tutorials
+ """
+ self.mainView.change_sorting(sorting_key)
+
+ def display_detail(self,tutorial):
+ """
+ Displays the detail view of a tutorial
+
+ @param tutorial the tutorial to display
+ """
+ #hide the main view
+ self.mainView.hide()
+ self.remove(self.mainView)
+
+ #create the detail view and show it
+ self.detailView = WorkshopDetail(tutorial,self.controller)
+ self.add(self.detailView)
+ self.detailView.show()
+
+ def display_main_view(self):
+ """
+ Displays the main view of the Workshop
+ """
+ #hide the detail view
+ self.detailView.hide()
+ self.remove(self.detailView)
+
+ #display the main view
+ self.add(self.mainView)
+ self.mainView.show()
+
+ def display_info_dialog(self,tutorial):
+ """
+ Displays the infos dialog on a tutorial
+
+ @param tutorial the tutorial to edit
+ """
+ infoDialog = InfoDialog(tutorial,self.controller)
+ infoDialog.run()
+ infoDialog.destroy()
+
+ @staticmethod
+ def display_login():
+ loginDialog = LoginDialog()
+ loginDialog.run()
+ loginDialog.destroy()
+
+ def refresh_content(self):
+ """
+ Refresh the data displayed
+ """
+ #refresh the tutorial list
+ self.mainView.refresh_tutorial_display()
+
+ #refresh the detail view
+ self.detailView.refresh_content()
+
+class WorkshopMain(gtk.VBox):
+ """
+ Contains the main view for the Workshop My tutorial
+ """
+ def __init__(self,controller):
+ """Constructor
+
+ @param controller The controller to attach the view to
+ """
+ gtk.VBox.__init__(self,False,10)
+
+ self.controller = controller
+ self.tutorial_list = []
+
+ #by default tutorials are sorted by name
+ self.sorting_key = 'Name'
+
+ self.set_border_width(10)
+
+ #The searchbar is displayed at the top
+ self.search_bar = SearchBar(self.controller)
+ self.pack_start(self.search_bar,False,False)
+
+ #Add a separator after the search bar
+ sep = gtk.HSeparator()
+ self.pack_start(sep,False,False)
+
+ #create the list item container with a scroll bar if necessary
+ self.main_container = gtk.ScrolledWindow()
+ self.main_container.set_policy(gtk.POLICY_AUTOMATIC,gtk.POLICY_AUTOMATIC)
+
+ self.list_container= gtk.VBox()
+
+ self.main_container.add_with_viewport(self.list_container)
+ self.pack_start(self.main_container)
+
+ #Show the components
+ self.search_bar.show()
+ self.list_container.show()
+ self.main_container.show()
+ sep.show()
+
+
+ def change_sorting(self,sorting):
+ """
+ Changes the property by which tutorial are sorted
+
+ @param sorting The property by which tutorials will be sorted
+ """
+
+ self.sorting_key = sorting
+ self.sort_tutorial()
+
+ def sort_tutorial(self):
+ """
+ Sorts the tutorials
+ """
+
+ #if tutorials are sorted by rating they are in the reverse order
+ self.tutorial_list.sort(key=operator.attrgetter(self.sorting_key.lower()))
+ self.refresh_tutorial_display()
+
+ def set_tutorial_list(self,tutorial_list):
+ """
+ Set the list of tutorial to display
+
+ @param tutorial_list the tutorial list
+ """
+ self.tutorial_list = tutorial_list
+ self.sort_tutorial()
+
+ def refresh_tutorial_display(self):
+ """
+ Refresh the tutorial content by deleting every item and recreating them
+ """
+ #delete every tutorial list item
+ for child in self.list_container.get_children():
+ self.list_container.remove(child)
+
+ #Creates and add a new item for every tutorial
+ for tuto in self.tutorial_list:
+ item = WorkshopListItem(tuto,self.controller)
+ self.list_container.pack_start(item)
+ item.show()
+
+class SearchBar(gtk.HBox):
+ """
+ The search bar control for the Workshop My tutorial
+ """
+ def __init__(self,controller):
+ """
+ Constructor
+
+ @param controller The controller to link the view to
+ """
+ gtk.HBox.__init__(self,False,10)
+
+ self.set_border_width(5)
+ self.controller = controller
+
+ #creating and configuring the controls
+ self.search_entry = gtk.Entry(400)
+
+ self.search_button = gtk.Button("Go")
+
+ self.separator = gtk.VSeparator()
+
+ self.sort_label = gtk.Label("Sort by")
+ self.sort_combo = gtk.combo_box_new_text()
+ self.sort_combo.insert_text(0,"Name")
+ self.sort_combo.insert_text(1,"Rating")
+ self.sort_combo.set_active(0)
+
+ self.selected_sorting = self.sort_combo.get_active_text()
+
+ #Adding the controls to the view
+ self.pack_start(self.search_entry,padding=5)
+ self.pack_start(self.search_button,False,False,padding=10)
+ self.pack_start(self.separator,False,False,padding=10)
+ self.pack_start(self.sort_label,False,False,padding=5)
+ self.pack_start(self.sort_combo,)
+
+ #showing the controls
+ self.search_entry.show()
+ self.search_button.show()
+ self.separator.show()
+ self.sort_label.show()
+ self.sort_combo.show()
+
+ #connecting the events
+ self.search_button.connect("clicked",self.controller.tutorial_query,self.search_entry.get_text())
+ self.sort_combo.connect("changed",self.controller.sort_selection_changed,None)
+
+class WorkshopDetail(gtk.VBox):
+ def __init__(self,tutorial,controller):
+ """
+ Constructor
+
+ @param tutorial The tutorial to display
+ @param controller The controller to link the view with
+ """
+
+ #Used for string formatting
+ self.title_text = '<span size="xx-large">%(title)s</span>'
+ self.author_text = '<span size="large">by %(author)s</span>'
+ self.desc_text = 'Description: %(description)s'
+
+ self.controller = controller
+ self.tutorial = tutorial
+
+ gtk.VBox.__init__(self,False,10)
+ self.set_border_width(10)
+
+ #The first row contains the back button
+ first_row = gtk.HBox(False)
+ back_image = gtk.Image()
+ back_image.set_from_file('arrow_back.png')
+ self.back_button = gtk.Button("Back")
+ self.back_button.set_image(back_image)
+
+ first_row.pack_start(self.back_button,False,False)
+
+ #The second row contains the activity icon, the title label,
+ #the author label and the star rating
+ icon = gtk.Image()
+ icon.set_from_file('icon.svg')
+
+ label_holder = gtk.VBox(False,10)
+
+ self.title_label = gtk.Label("")
+ self.author_label = gtk.Label("")
+
+ #Add a small offsert for author's label alignement because it's cute
+ self.author_label.set_alignment(0.05,0.5)
+ self.title_label.set_alignment(0.0,0.5)
+
+ label_holder.pack_start(self.title_label)
+ label_holder.pack_start(self.author_label)
+
+ self.rating = Rating(tutorial,controller,rating = tutorial.rating)
+
+ second_row = gtk.HBox(False)
+ second_row.pack_start(icon,False,False)
+ second_row.pack_start(label_holder)
+ second_row.pack_end(self.rating,False,False)
+
+ #The middle of the screen contains an area for the description
+ self.desc_view = gtk.TextView()
+ self.desc_buff = gtk.TextBuffer()
+ self.desc_buff.set_text(tutorial.description)
+ self.desc_view.set_buffer(self.desc_buff)
+ self.desc_view.set_editable(False)
+ self.desc_view.set_wrap_mode(gtk.WRAP_WORD)
+ self.desc_view.set_cursor_visible(False)
+ self.desc_view.connect("realize",self.realize_cb,None)
+ self.desc_view.modify_base(gtk.STATE_NORMAL, gtk.gdk.color_parse("gray") )
+
+ #The bottom of the screen contains the button(fourth and fifth row
+ self.launch_button = gtk.Button('Launch')
+ self.launch_button.get_child().set_markup(self.title_text %{"title":"Launch"})
+ self.edit_button = gtk.Button('Edit')
+ self.edit_button.get_child().set_markup(self.title_text %{"title":"Edit"})
+ self.update_button = gtk.Button('Update')
+ self.update_button.get_child().set_markup(self.title_text %{"title":"Update"})
+ self.info_button = gtk.Button('Infos')
+ self.info_button.get_child().set_markup(self.title_text %{"title":"Infos"})
+ self.delete_button = gtk.Button('Delete')
+ self.delete_button.get_child().set_markup(self.title_text %{"title":"Delete"})
+
+ fourth_row = gtk.HBox(False,15)
+ fourth_row.pack_start(self.launch_button,False,False)
+ fourth_row.pack_start(self.edit_button,False,False)
+ fourth_row.pack_start(self.update_button,False,False)
+ fourth_row.pack_start(self.info_button,False,False)
+ fourth_row.pack_end(self.delete_button,False,False)
+
+
+ self.publish_button = gtk.Button('')
+ self.publish_button.get_child().set_markup(self.title_text %{"title":"Publish"})
+ self.unpublish_button = gtk.Button('')
+ self.unpublish_button.get_child().set_markup(self.title_text %{"title":"Unpublish"})
+
+ fifth_row = gtk.HBox(False,15)
+ fifth_row.pack_start(self.publish_button,False,False)
+ fifth_row.pack_start(self.unpublish_button,False,False)
+
+ #The description view contains all the extra space
+ self.pack_start(first_row,False,False)
+ self.pack_start(second_row,False,False)
+ self.pack_start(self.desc_view)
+ self.pack_end(fifth_row,False,False)
+ self.pack_end(fourth_row,False,False)
+
+ #show everything
+ self.back_button.show()
+ first_row.show()
+ self.title_label.show()
+ self.author_label.show()
+ self.rating.show()
+ label_holder.show()
+ second_row.show()
+ icon.show()
+ self.desc_view.show()
+ self.launch_button.show()
+ self.edit_button.show()
+ self.update_button.show()
+ self.info_button.show()
+ self.delete_button.show()
+ fourth_row.show()
+
+ self.publish_button.show()
+ self.unpublish_button.show()
+ fifth_row.show()
+
+ #set some text with markup
+ self.title_label.set_markup(self.title_text % {"title":tutorial.name})
+ self.author_label.set_markup(self.author_text % {"author":tutorial.author})
+
+ #connect the clicked events of the buttons
+ self.back_button.connect("clicked",self.controller.back_pressed,None)
+ self.publish_button.connect("clicked",self.controller.publish_tutorial,self.tutorial)
+ self.unpublish_button.connect("clicked",self.controller.unpublish_tutorial,self.tutorial)
+ self.launch_button.connect("clicked",self.controller.launch_tutorial,self.tutorial)
+ self.edit_button.connect("clicked",self.controller.edit_tutorial,self.tutorial)
+ self.update_button.connect("clicked",self.controller.update_tutorial,self.tutorial)
+ self.info_button.connect("clicked",self.controller.info_tutorial,self.tutorial)
+ self.delete_button.connect("clicked",self.controller.delete_tutorial,self.tutorial)
+
+ def realize_cb(self,widget,data=None):
+ """
+ This fucntion changes the cursor over the description view
+ So we see an arrow and not the insert text cursor
+ """
+ widget.get_window(gtk.TEXT_WINDOW_TEXT).set_cursor(gtk.gdk.Cursor(gtk.gdk.LEFT_PTR))
+
+ def refresh_content(self):
+ """
+ Refresh the labels' text based on the tutorial object
+ """
+ self.title_label.set_markup(self.title_text % {"title":self.tutorial.name})
+ self.author_label.set_markup(self.author_text % {"author":self.tutorial.author})
+ self.desc_buff.set_text(self.tutorial.description)
+
+class WorkshopListItem(gtk.Alignment):
+ """
+ A list item containing the details of a tutorial
+ """
+ def __init__(self,tutorial,controller):
+ """
+ Constructor
+
+ @param controller The controller to link the view to
+ """
+ gtk.Alignment.__init__(self,0.0,0.0,1.0,1.0)
+ self.tutorial = tutorial
+ self.controller = controller
+
+ #The table will contain everything else
+ self.table = gtk.Table(3,3,False)
+ self.table.set_row_spacing(1,10)
+
+ #Create the controls
+ self.lbl_title = gtk.Label('')
+ self.lbl_title.set_alignment(0.0,0.5)
+ self.title_text = '<span size="xx-large">%(title)s</span>'
+ self.lbl_title.set_markup(self.title_text % {'title':tutorial.name})
+
+ self.lbl_desc = gtk.Label(tutorial.description)
+ self.lbl_desc.set_line_wrap(True)
+ self.lbl_desc.set_alignment(0.0,0.5)
+
+ self.btn_launch = gtk.Button('Launch')
+
+ launch_align= gtk.Alignment(0.0,1.0,0.0,0.0)
+ launch_align.add(self.btn_launch)
+
+ self.btn_details = gtk.Button('Details')
+
+ self.icon = gtk.Image()
+ self.icon.set_from_file('icon.svg')
+
+ self.rating = Rating(tutorial,controller,tutorial.rating, True)
+
+ #Add the controls to the table
+ self.table.attach(self.icon,0,1,0,1,0,0)
+ self.table.attach(self.lbl_title,1,2,0,1,yoptions=0)
+ self.table.attach(self.lbl_desc,1,2,1,2,xoptions=gtk.FILL,yoptions=gtk.EXPAND)
+ self.table.attach(launch_align,1,2,2,3,gtk.FILL)
+ self.table.attach(self.btn_details,2,3,2,3,0,0)
+ self.table.attach(self.rating,2,3,0,2,0,0)
+
+ #show everything
+ self.table.show()
+ self.icon.show()
+ self.lbl_title.show()
+ launch_align.show()
+ self.lbl_desc.show()
+ self.btn_launch.show()
+ self.btn_details.show()
+ self.rating.show()
+
+ self.add(self.table)
+
+ #connect the buttons
+ self.btn_details.connect("clicked",self.controller.show_details,self.tutorial)
+ self.btn_launch.connect("clicked",self.controller.launch_tutorial,self.tutorial)
diff --git a/Workshop.activity/WorkshopController.py b/Workshop.activity/WorkshopController.py
new file mode 100644
index 0000000..e036e50
--- /dev/null
+++ b/Workshop.activity/WorkshopController.py
@@ -0,0 +1,127 @@
+"""
+WorkshopController
+
+This module handles user action from the workshop view
+
+"""
+import logging
+from WorkshopModel import Tutorial
+class WorkshopController():
+ def __init__(self,view,model):
+ self.view = view
+ self.model = model
+
+ def tutorial_query(self,widget,keyword):
+ """
+ Handles query from the view
+
+ @param widget the widget that sent the query
+ @param keyword the keyword for the query, empty to get all tutorials
+ """
+ self.model.query(keyword)
+
+ def sort_selection_changed(self,widget,data):
+ """
+ Handles selection changes in the sorting selection
+
+ @param widget the widget that sent the query
+ @param sort the property to use to sort tutorial
+ """
+ sorting = widget.get_active_text()
+ self.view.change_sorting(sorting)
+
+
+ def launch_tutorial(self,widget,tutorial):
+ """
+ Handles start tutorial action
+
+ @param widget the widget that triggered the action
+ @param tutorial the tutorial to launch
+ """
+ self.model.launch_tutorial(tutorial)
+
+ def show_details(self,widget,tutorial):
+ """
+ show the details for a tutorial
+
+ @param widget the widget that made the call
+ @param tutorial the tutorial to
+ """
+ self.view.display_detail(tutorial)
+
+ def back_pressed(self,widget,data):
+ """
+ When in detail view, go back to general view
+
+ @param widget the widget that made the call
+ @param data not used
+ """
+ self.view.display_main_view()
+
+ def rate_tutorial(self,tutorial,rating):
+ """
+ Change the rating for a tutorial
+
+ @param tutorial The tutorial to rate
+ @param rating The new rating
+ """
+ self.model.rate_tutorial(tutorial,rating)
+
+ def edit_tutorial(self,widget,tutorial):
+ """
+ Edit the tutorial in the detail view
+
+ @param widget the widget that made the call
+ @param tutorial the tutorial to edit
+ """
+ self.model.edit_tutorial(tutorial)
+
+ def update_tutorial(self,widget,tutorial):
+ """
+ Need to know what this do
+ """
+ pass
+
+ def info_tutorial(self,widget,tutorial):
+ """
+ Edit the infos about the tutorial
+
+ @param widget the widget that made the call
+ @param tutorial the tutorial to edit
+ """
+ self.view.display_info_dialog(tutorial)
+
+ def save_tutorial_info(self,tutorial):
+ """
+ Save the metadata of a tutorial
+
+ @param tutorial The tutorial to update containing the new metadata
+ """
+ self.model.save_metadata(tutorial)
+
+ def delete_tutorial(self,widget,tutorial):
+ """
+ Delete a tutorial
+
+ @param widget the widget that made the call
+ @param tutorial the tutorial to delete
+ """
+ self.model.delete_tutorial(tutorial)
+
+ def publish_tutorial(self,widget,tutorial):
+ """
+ Publish a tutorial
+
+ @param widget the widget that made the call
+ @param tutorial the tutorial to publish
+ """
+ self.model.publish_tutorial(tutorial)
+
+ def unpublish_tutorial(self,widget,tutorial):
+ """
+ Unpublish a tutorial
+
+ @param widget the widget that made the call
+ @param tutorial the tutorial to unpublish
+ """
+ self.model.unpublish_tutorial(tutorial)
diff --git a/Workshop.activity/WorkshopModel.py b/Workshop.activity/WorkshopModel.py
new file mode 100644
index 0000000..b085584
--- /dev/null
+++ b/Workshop.activity/WorkshopModel.py
@@ -0,0 +1,245 @@
+"""
+WorkshopModel
+
+This module is the model of the Workshop Activity
+"""
+
+from sugar.tutorius.vault import *
+from sugar.tutorius.store import *
+from dialogs import LoginDialog
+
+class Login(object):
+ def __init__(self,proxy,login_view):
+ self.proxy = proxy
+ self.login_view = login_view
+
+ def __call__(self,f):
+ def wrapper(*args):
+ if self.proxy.get_session_id() == None:
+ self.login_view.run()
+ self.login_view.destroy()
+ f(*args)
+ return wrapper
+
+
+class WorkshopModel():
+
+ store_proxy = StoreProxy("http://bobthebuilder.mine.nu/tutorius/en-US/tutorius")
+ login_view = LoginDialog()
+
+ def __init__(self,view):
+ self.view = view
+ self.login_view.set_model(self)
+
+ def login(self,username,password):
+ """
+ Log a user in the store
+
+ @param username The username of the user
+ @param password The password of the user
+ @return True if login succeeded False otherwise
+ """
+ return self.store_proxy.login(username,password)
+
+ def register(self,username,password,email):
+ """
+ Register a new user to the store
+
+ @param username The username of the new user
+ @param password The password of the user
+ @param email The email of the user
+ @return True if user is registered, False otherwise
+ """
+ return self.store_proxy.register_new_user({'nickname':username,'password':password,'email':email})
+
+ def query(self,keyword):
+ """
+ Query the vault for tutorial that are linked with the keyword
+ Update the currently managed tutorials and notifies the view that the managed tutorials have changed
+
+ @param keyword the keyword for the query
+ """
+ vault_return = Vault.query()
+ tutorial_list = []
+ for tuto in vault_return:
+ tutorial_list.append(Tutorial(tuto))
+
+ self.view.set_tutorial_list(tutorial_list)
+
+ def delete_tutorial(self,tutorial):
+ """
+ Delete a tutorial and updated the currently managed tutorials
+ Notifies the view that the manages tutorials have changed
+
+ @param tutorial the tutorial to delete
+ """
+ vault.deleteTutorial(tutorial.id)
+
+ def update_tutorial_infos(self,tutorial, new_infos):
+ """
+ Updates the metadata on a tutorial and updates the currently managed tutorials
+ Notifies the view that the managed tutorials have changed
+
+ @param tutorial the tutorial to update
+ @param new_infos a dictionnary of the new informations i.e. {"title":"tut1","author":"Max Power"}
+ """
+ pass
+
+ @Login(store_proxy,login_view)
+ def publish_tutorial(self,tutorial):
+ """
+ Publishes a tutorial
+
+ Details to come
+ """
+ if not tutorial.published_state:
+ binary = vault.get_tutorial_binary(tutorial.id)
+
+
+ @Login(store_proxy,login_view)
+ def unpublish_tutorial(self,tutorial):
+ """
+ Unpublishes a tutorial
+
+ Details to come
+ """
+ pass
+
+ def launch_tutorial(self,tutorial):
+ """
+ Lauches a tutorial
+
+ @param tutorial The tutorial to launch
+ """
+ pass
+
+ @Login(store_proxy,login_view)
+ def rate_tutorial(self,tutorial,rating):
+ """
+ Rate the tutorial
+
+ @param tutorial The tutorial to rate
+ @param rating The new rating for the tutorial
+ """
+ tutorial.rating = rating
+ self.view.refresh_content()
+
+ def edit_tutorial(self,tutorial):
+ """
+ Edit a tutorial
+
+ @param tutorial The tutorial to edit
+ """
+ pass
+
+ def save_metadata(self,tutorial):
+ """
+ Save the metadata of a tutorial
+
+ @param tutorial The tutorial to udpate containing the new metadata
+ """
+ self.view.refresh_content()
+
+
+class Tutorial():
+ """
+ Wrapper for tutorial metadata
+ """
+ def __init__(self,metadata_dict):
+ self.__original_dict = metadata_dict
+ self.__update_dict = metadata_dict
+
+ if 'name' in self.__original_dict:
+ self.__name = self.__original_dict['name']
+ else:
+ self.__description = name
+
+ if 'description' in self.__original_dict:
+ self.__description = self.__original_dict['description']
+ else:
+ self.__description = ""
+
+ if 'author' in self.__original_dict:
+ self.__author = self.__original_dict['author']
+ else:
+ self.__author = ""
+
+ if 'rating' in self.__original_dict:
+ self.__rating = float(self.__original_dict['rating'])
+ else:
+ self.__rating = 0
+
+ if 'publish_state' in self.__original_dict:
+ #I'm sorry for this
+ temp = self.__original_dict['publish_state']
+ temp = temp.lower()
+ if temp == 'false':
+ self.__published_state = False
+ elif temp == 'true':
+ self.__published_state = True
+ else:
+ self.__published_state = None
+ else:
+ self.__published_state = None
+
+ if 'guid' in self.__original_dict:
+ self.__id = self.__original_dict['guid']
+ else:
+ self.__id = ""
+
+ def get_name(self):
+ return self.__name
+
+ def set_name(self,name):
+ self.__name = name
+
+ def get_description(self):
+ return self.__description
+
+ def set_description(self,description):
+ self.__description = description
+ self.__update_dict['Description'] = description
+
+ def get_author(self):
+ return self.__author
+
+ def set_author(self,author):
+ self.__author = author
+ self.__update_dict['Author'] = author
+
+ def get_rating(self):
+ return self.__rating
+
+ def set_rating(self,rating):
+ self.__rating = rating
+ self.__update_dict['Rating'] = rating
+
+ def get_published_state(self):
+ return self.__published_state
+
+ def set_published_state(self,published_state):
+ self.__published_state = published_state
+ self.__update_dict['PublishedState'] = published_state
+
+ def get_id(self):
+ return self.__id
+
+ def set_id(self,id):
+ self.__id = id
+ self.__update_dict['TutorialId'] = id
+
+ def get_updated_metadata(self):
+ return self.__update_dict
+
+ name = property(get_name,set_name)
+ description = property(get_description,set_description)
+ author = property(get_author,set_author)
+ rating = property(get_rating,set_rating)
+ published_state = property(get_published_state,set_published_state)
+ id = property(get_id,set_id)
+ updated_metadata = property(get_updated_metadata)
+
+
+
+
+
diff --git a/Workshop.activity/activity/activity.info b/Workshop.activity/activity/activity.info
new file mode 100644
index 0000000..02dbee1
--- /dev/null
+++ b/Workshop.activity/activity/activity.info
@@ -0,0 +1,8 @@
+[Activity]
+name = Tutorius
+bundle_id = org.laptop.TutoriusActivity
+class = TutoriusActivity.TutoriusActivity
+icon = someicon
+activity_version = 1
+host_version = 1
+show_launcher = yes
diff --git a/Workshop.activity/activity/someicon.svg b/Workshop.activity/activity/someicon.svg
new file mode 100644
index 0000000..bb28f04
--- /dev/null
+++ b/Workshop.activity/activity/someicon.svg
@@ -0,0 +1,21 @@
+<?xml version="1.0" ?><!-- Created with Inkscape (http://www.inkscape.org/) --><!DOCTYPE svg PUBLIC '-//W3C//DTD SVG 1.1//EN' 'http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd' [
+ <!ENTITY stroke_color "#000000">
+ <!ENTITY fill_color "#ffffff">
+]><svg height="55px" id="svg2393" inkscape:output_extension="org.inkscape.output.svg.inkscape" inkscape:version="0.47pre1 r21720" sodipodi:docname="tutortool.svg" sodipodi:version="0.32" version="1.1" width="55px" xmlns="http://www.w3.org/2000/svg" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:svg="http://www.w3.org/2000/svg">
+ <defs id="defs2395">
+ <inkscape:perspective id="perspective2401" inkscape:persp3d-origin="16 : 10.666667 : 1" inkscape:vp_x="0 : 16 : 1" inkscape:vp_y="0 : 1000 : 0" inkscape:vp_z="32 : 16 : 1" sodipodi:type="inkscape:persp3d"/>
+ </defs>
+ <sodipodi:namedview bordercolor="#666666" borderopacity="1.0" id="base" inkscape:current-layer="layer1" inkscape:cx="3.7661233" inkscape:cy="33.132055" inkscape:document-units="px" inkscape:grid-bbox="true" inkscape:pageopacity="0.0" inkscape:pageshadow="2" inkscape:window-height="675" inkscape:window-maximized="0" inkscape:window-width="1057" inkscape:window-x="108" inkscape:window-y="45" inkscape:zoom="3.9590209" pagecolor="#ffffff" showgrid="true"/>
+ <metadata id="metadata2398">
+ <rdf:RDF>
+ <cc:Work rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g id="layer1" inkscape:groupmode="layer" inkscape:label="Layer 1" transform="translate(0,23)">
+ <path d="m 38.01548,1.5478747 c 0,7.1837999 -7.3667,13.0141283 -16.443525,13.0141283 -2.269208,0 -8.124729,3.152936 -13.9608513,4.252763 C 13.382177,14.110994 11.434521,11.926642 9.9463815,10.748864 6.9701032,8.3933076 5.1284282,5.1397735 5.1284282,1.5478747 c 0,-7.1837994 7.3666998,-13.0141297 16.4435268,-13.0141297 9.076825,0 16.443525,5.8303303 16.443525,13.0141297 z" id="path2403" sodipodi:nodetypes="cscsssc" style="fill:&fill_color;;fill-opacity:1;fill-rule:nonzero;stroke:&stroke_color;;stroke-width:1.96931934;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"/>
+ <path d="m 50.150276,6.4721386 c 0,2.621116 -1.428036,4.9953144 -3.735846,6.7142344 -1.153905,0.85946 -1.824287,2.434433 1.398853,6.784273 -6.258422,-3.991066 -8.65379,-4.001712 -10.413335,-4.001712 -7.03818,0 -12.750327,-4.254565 -12.750327,-9.4967954 0,-5.2422321 5.712147,-9.4967971 12.750327,-9.4967971 7.038182,0 12.750328,4.254565 12.750328,9.4967971 z" id="path3175" sodipodi:nodetypes="cccsssc" style="fill:&fill_color;;fill-opacity:1;fill-rule:nonzero;stroke:&stroke_color;;stroke-width:1.96931934;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"/>
+ </g>
+</svg> \ No newline at end of file
diff --git a/Workshop.activity/arrow_back.png b/Workshop.activity/arrow_back.png
new file mode 100644
index 0000000..8263674
--- /dev/null
+++ b/Workshop.activity/arrow_back.png
Binary files differ
diff --git a/Workshop.activity/dialogs.py b/Workshop.activity/dialogs.py
new file mode 100644
index 0000000..67b3fb6
--- /dev/null
+++ b/Workshop.activity/dialogs.py
@@ -0,0 +1,237 @@
+import gtk
+
+class ErrorDialog(gtk.Dialog):
+ def __init__(self,text):
+ gtk.Dialog.__init__(self,"Error",None,gtk.DIALOG_MODAL,(gtk.STOCK_OK,gtk.RESPONSE_ACCEPT))
+ self.label = gtk.Label(text)
+ self.vbox.pack_start(self.label,padding = 10)
+ self.vbox.show_all()
+
+class LoginDialog(gtk.Dialog):
+ def __init__(self):
+ gtk.Dialog.__init__(self,"Login",None,gtk.DIALOG_MODAL)
+ ok_button = gtk.Button("Login")
+ ok_button.connect("clicked",self.ok_clicked,None)
+ ok_button.show()
+ self.action_area.pack_start(ok_button)
+ self.add_button("Cancel",gtk.RESPONSE_REJECT)
+ self.set_resizable(False)
+ self.prepare_dialog()
+
+ def set_model(self,model):
+ self.model = model
+
+ def prepare_dialog(self):
+ self.user_line = gtk.HBox(False,10)
+ self.pass_line = gtk.HBox(False,10)
+ self.register_line = gtk.HBox(False)
+ self.remember_line = gtk.HBox(False)
+
+ self.username_lbl = gtk.Label("Email:")
+ self.username_entry = gtk.Entry()
+ self.username_entry.set_width_chars(40)
+
+ self.password_lbl = gtk.Label("Password:")
+ self.password_entry = gtk.Entry()
+ self.password_entry.set_visibility(False)
+ self.password_entry.set_width_chars(40)
+
+ self.register_me = gtk.LinkButton("","Register Now!")
+ self.not_registered = gtk.Label("Not Registered? ")
+
+ self.remember_user = gtk.CheckButton("Remember my username",False)
+
+ self.register_me.connect("clicked",self.click_link,None)
+
+ self.user_line.pack_start(self.username_lbl,False)
+ self.user_line.pack_end(self.username_entry,False)
+
+ self.pass_line.pack_start(self.password_lbl,False)
+ self.pass_line.pack_end(self.password_entry,False)
+
+ self.register_line.pack_end(self.register_me,False)
+ self.register_line.pack_end(self.not_registered,False)
+
+ self.remember_line.pack_start(self.remember_user,False,padding=80)
+
+ self.vbox.pack_start(self.register_line,False)
+ self.vbox.pack_start(self.user_line,False,padding=10)
+ self.vbox.pack_start(self.pass_line,False,padding=10)
+ self.vbox.pack_start(self.remember_line,False)
+
+ self.vbox.show()
+ self.user_line.show()
+ self.pass_line.show()
+ self.register_line.show()
+ self.remember_line.show()
+
+ self.username_lbl.show()
+ self.username_entry.show()
+
+ self.password_lbl.show()
+ self.password_entry.show()
+
+ self.register_me.show()
+ self.not_registered.show()
+
+ self.remember_user.show()
+
+ def ok_clicked(self,widget,data=None):
+ success = self.model.login(self.username_entry.get_text(), self.password_entry.get_text())
+ if success:
+ self.response(gtk.RESPONSE_ACCEPT)
+ else:
+ errorDialog = ErrorDialog("The password or the email address is wrong")
+ errorDialog.run()
+ errorDialog.destroy()
+
+
+ def click_link(self,widget,data=None):
+ self.register_dialog = RegisterDialog(self.model)
+ self.register_dialog.run()
+ self.register_dialog.destroy()
+
+class RegisterDialog(gtk.Dialog):
+ def __init__(self,model):
+ gtk.Dialog.__init__(self,"Register",None,gtk.DIALOG_MODAL)
+ self.model = model
+ ok_button = gtk.Button("Register")
+ ok_button.connect("clicked",self.ok_clicked,None)
+ ok_button.show()
+ self.action_area.pack_start(ok_button)
+ self.add_button("Cancel",gtk.RESPONSE_REJECT)
+ self.set_resizable(False)
+ self.create_content()
+
+ def ok_clicked(self,widget,data=None):
+ username = self.entries[2].get_text()
+ password = self.entries[3].get_text()
+ confirmation = self.entries[4].get_text()
+ email = self.entries[0].get_text()
+
+ if username.trim() == "":
+ self.show_error_dialog('You must choose a username')
+ return
+ if password.trim() == '':
+ self.show_error_dialog('You must provide a password')
+ return
+ if email.trim() == '':
+ self.show_error_dialog('You must provide a valid email address')
+ return
+ if password != confirmation:
+ self.show_error_dialog('The password and confirmation must be the same')
+ return
+
+ success = self.model.register(self.entries[2].get_text(),self.entries[3].get_text(),self.entries[0].get_text())
+ if success :
+ self.response(gtk.RESPONSE_ACCEPT)
+ else:
+ self.show_error_dialog("An error occured while registering the user")
+
+ def show_error_dialog(self,error_message):
+ errorDialog = ErrorDialog("An error occured while registering a user")
+ errorDialog.run()
+ errorDialog.destroy()
+
+ def create_content(self):
+ entry_length = 40
+ table = gtk.Table(10,4,False)
+ self.entries = []
+ labels = ["Email address","Name","Username","Password","Confirmation","Location","Web Site"]
+ required=[True,True,True,True,True,False,False]
+ hidden=[False,False,False,True,True,False,False]
+ for x in range(6):
+ lbl = gtk.Label(labels[x]+":")
+ entry = gtk.Entry()
+ self.entries.append(entry)
+ entry.set_width_chars(entry_length)
+ table.attach(lbl,0,1,x,x+1,xpadding=10)
+ table.attach(entry,1,2,x,x+1,ypadding=10)
+ if hidden[x]:
+ entry.set_visibility(False)
+ if required[x]:
+ required_lbl = gtk.Label("*")
+ table.attach(required_lbl,2,3,x,x+1)
+ required_lbl.show()
+ lbl.set_alignment(0.0,0.5)
+ lbl.show()
+ entry.show()
+
+
+ if_required = gtk.Label(" * Required Field")
+ table.attach(if_required,3,4,0,1)
+
+ under_13 = gtk.CheckButton("I am 13 years old or younger",False)
+ legal = gtk.CheckButton("I have read the",False)
+ legal_notices=gtk.LinkButton('',"legal notices")
+ and_lbl = gtk.Label("and")
+ privacy = gtk.LinkButton('',"privacy statement")
+ hbox = gtk.HBox(False,0)
+ hbox.pack_start(legal)
+ hbox.pack_start(legal_notices)
+ hbox.pack_start(and_lbl)
+ hbox.pack_start(privacy)
+
+ table.attach(under_13,1,2,7,8)
+ table.attach(hbox,1,2,8,9)
+ under_13.show()
+ legal.show()
+ legal_notices.show()
+ and_lbl.show()
+ privacy.show()
+ hbox.show()
+ if_required.show()
+ table.show()
+ self.vbox.pack_start(table)
+
+class InfoDialog(gtk.Dialog):
+ def __init__(self,tutorial,controller):
+ gtk.Dialog.__init__(self,"Tutorial Info",None,gtk.DIALOG_MODAL)
+ self.tutorial = tutorial
+ self.controller = controller
+ ok_button = gtk.Button("Save")
+ ok_button.connect("clicked",self.ok_clicked,None)
+ ok_button.show()
+ self.action_area.pack_start(ok_button)
+ self.add_button("Cancel",gtk.RESPONSE_REJECT)
+ self.set_resizable(False)
+ self.prepare_content()
+
+ def prepare_content(self):
+ table = gtk.Table(3,2,False)
+ labels = ["Tutorial Name","Author","Description"]
+ entry_length = [40,40,40]
+ self.entries = []
+
+ self.name_entry = gtk.Entry()
+ self.name_entry.set_width_chars(40)
+ self.name_entry.set_text(self.tutorial.name)
+ table.attach(self.name_entry,1,2,0,1,ypadding=10)
+
+
+ self.author_entry = gtk.Entry()
+ self.author_entry.set_width_chars(40)
+ self.author_entry.set_text(self.tutorial.author)
+ table.attach(self.author_entry,1,2,1,2,ypadding=10)
+
+ self.desc_entry = gtk.Entry()
+ self.desc_entry.set_width_chars(40)
+ self.desc_entry.set_text(self.tutorial.description)
+ table.attach(self.desc_entry,1,2,2,3,ypadding=10)
+
+
+
+ for x in range(0,3):
+ label = gtk.Label(labels[x])
+ table.attach(label,0,1,x,x+1,xpadding=10)
+ label.set_alignment(0.0,0.5)
+ table.show_all()
+ self.vbox.pack_start(table)
+
+ def ok_clicked(self,widget,data):
+ self.tutorial.author = self.author_entry.get_text()
+ self.tutorial.name = self.name_entry.get_text()
+ self.tutorial.description = self.desc_entry.get_text()
+ self.controller.save_tutorial_info(self.tutorial)
+ self.response(gtk.RESPONSE_ACCEPT)
+
diff --git a/Workshop.activity/full_star.png b/Workshop.activity/full_star.png
new file mode 100644
index 0000000..9f7d095
--- /dev/null
+++ b/Workshop.activity/full_star.png
Binary files differ
diff --git a/Workshop.activity/grayed_star.png b/Workshop.activity/grayed_star.png
new file mode 100644
index 0000000..7f8b1e1
--- /dev/null
+++ b/Workshop.activity/grayed_star.png
Binary files differ
diff --git a/Workshop.activity/half_star.png b/Workshop.activity/half_star.png
new file mode 100644
index 0000000..48ebae3
--- /dev/null
+++ b/Workshop.activity/half_star.png
Binary files differ
diff --git a/Workshop.activity/icon.svg b/Workshop.activity/icon.svg
new file mode 100644
index 0000000..bb28f04
--- /dev/null
+++ b/Workshop.activity/icon.svg
@@ -0,0 +1,21 @@
+<?xml version="1.0" ?><!-- Created with Inkscape (http://www.inkscape.org/) --><!DOCTYPE svg PUBLIC '-//W3C//DTD SVG 1.1//EN' 'http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd' [
+ <!ENTITY stroke_color "#000000">
+ <!ENTITY fill_color "#ffffff">
+]><svg height="55px" id="svg2393" inkscape:output_extension="org.inkscape.output.svg.inkscape" inkscape:version="0.47pre1 r21720" sodipodi:docname="tutortool.svg" sodipodi:version="0.32" version="1.1" width="55px" xmlns="http://www.w3.org/2000/svg" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:svg="http://www.w3.org/2000/svg">
+ <defs id="defs2395">
+ <inkscape:perspective id="perspective2401" inkscape:persp3d-origin="16 : 10.666667 : 1" inkscape:vp_x="0 : 16 : 1" inkscape:vp_y="0 : 1000 : 0" inkscape:vp_z="32 : 16 : 1" sodipodi:type="inkscape:persp3d"/>
+ </defs>
+ <sodipodi:namedview bordercolor="#666666" borderopacity="1.0" id="base" inkscape:current-layer="layer1" inkscape:cx="3.7661233" inkscape:cy="33.132055" inkscape:document-units="px" inkscape:grid-bbox="true" inkscape:pageopacity="0.0" inkscape:pageshadow="2" inkscape:window-height="675" inkscape:window-maximized="0" inkscape:window-width="1057" inkscape:window-x="108" inkscape:window-y="45" inkscape:zoom="3.9590209" pagecolor="#ffffff" showgrid="true"/>
+ <metadata id="metadata2398">
+ <rdf:RDF>
+ <cc:Work rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g id="layer1" inkscape:groupmode="layer" inkscape:label="Layer 1" transform="translate(0,23)">
+ <path d="m 38.01548,1.5478747 c 0,7.1837999 -7.3667,13.0141283 -16.443525,13.0141283 -2.269208,0 -8.124729,3.152936 -13.9608513,4.252763 C 13.382177,14.110994 11.434521,11.926642 9.9463815,10.748864 6.9701032,8.3933076 5.1284282,5.1397735 5.1284282,1.5478747 c 0,-7.1837994 7.3666998,-13.0141297 16.4435268,-13.0141297 9.076825,0 16.443525,5.8303303 16.443525,13.0141297 z" id="path2403" sodipodi:nodetypes="cscsssc" style="fill:&fill_color;;fill-opacity:1;fill-rule:nonzero;stroke:&stroke_color;;stroke-width:1.96931934;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"/>
+ <path d="m 50.150276,6.4721386 c 0,2.621116 -1.428036,4.9953144 -3.735846,6.7142344 -1.153905,0.85946 -1.824287,2.434433 1.398853,6.784273 -6.258422,-3.991066 -8.65379,-4.001712 -10.413335,-4.001712 -7.03818,0 -12.750327,-4.254565 -12.750327,-9.4967954 0,-5.2422321 5.712147,-9.4967971 12.750327,-9.4967971 7.038182,0 12.750328,4.254565 12.750328,9.4967971 z" id="path3175" sodipodi:nodetypes="cccsssc" style="fill:&fill_color;;fill-opacity:1;fill-rule:nonzero;stroke:&stroke_color;;stroke-width:1.96931934;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"/>
+ </g>
+</svg> \ No newline at end of file
diff --git a/Workshop.activity/setup.py b/Workshop.activity/setup.py
new file mode 100755
index 0000000..f95b630
--- /dev/null
+++ b/Workshop.activity/setup.py
@@ -0,0 +1,3 @@
+#! /usr/bin/env python
+from sugar.activity import bundlebuilder
+bundlebuilder.start()
diff --git a/Workshop.activity/tests/MTTests.py b/Workshop.activity/tests/MTTests.py
new file mode 100644
index 0000000..0687d92
--- /dev/null
+++ b/Workshop.activity/tests/MTTests.py
@@ -0,0 +1,169 @@
+import mocks
+from WorkshopController import WorkshopController
+from WorkshopModel import Tutorial
+from Workshop import WorkshopView
+from unittest import TestCase
+import gtk
+
+
+class WsControllerTest(TestCase):
+ def setUp(self):
+ self.model = mocks.WorkshopModelMock()
+ self.view = mocks.ViewMock()
+ self.controller = WorkshopController(self.view,self.model)
+ self.tut = Tutorial({'name':'tut', 'description':'descrip','rating':4, 'author':'Max Power'})
+
+ def test_tutorial_query(self):
+ widget = 'This is the widget'
+ keyword = 'Test Keyword'
+ function_name = 'tutorial_query'
+ self.controller.tutorial_query(widget,keyword)
+
+ assert function_name in self.model.call_list, 'tutorial_query was not called'
+ assert self.model.call_list[function_name] == keyword, "the keyword %s wasn't correct" % (keyword)
+
+ def test_sort_selection_changed(self):
+ function_name = 'change_sorting'
+ widget = gtk.combo_box_new_text()
+ widget.insert_text(0,"Name")
+ widget.insert_text(1,"Rating")
+ widget.set_active(0)
+
+ self.controller.sort_selection_changed(widget,None)
+
+ assert function_name in self.view.call_list, 'change_sorting was not called'
+ assert self.view.call_list[function_name] == "Name", 'the parameter was not correct'
+
+ self.view.clear_call_list()
+
+ widget.set_active(1)
+ self.controller.sort_selection_changed(widget,None)
+
+ assert function_name in self.view.call_list, 'change_sorting was not called'
+ assert self.view.call_list[function_name] == "Rating", 'the parameter was not correct'
+
+ def test_launch_tutorial(self):
+ function_name = 'launch_tutorial'
+ self.controller.launch_tutorial(None,self.tut)
+
+ assert function_name in self.model.call_list, 'launch_tutorial was not called'
+ assert self.model.call_list[function_name] == self.tut, 'the parameter was not correct'
+
+ def test_show_details(self):
+ function_name = 'display_detail'
+ self.controller.show_details(None,self.tut)
+
+ assert function_name in self.view.call_list, 'display_detil was not called'
+ assert self.view.call_list[function_name] == self.tut , 'The parameter was not correct'
+
+ def test_back_pressed(self):
+ function_name = 'display_main_view'
+ self.controller.back_pressed(None,None)
+
+ assert function_name in self.view.call_list, 'display_main_view was not called'
+ assert self.view.call_list[function_name] == None, 'The parameter was not correct'
+
+ def test_rate_tutorial(self):
+ rating = 3
+ function_name = 'rate_tutorial'
+ self.controller.rate_tutorial(self.tut,rating)
+
+ assert function_name in self.model.call_list, 'rate_tutorial was not called'
+ assert self.model.call_list[function_name][0] == self.tut, 'The first param was not correct'
+ assert self.model.call_list[function_name][1] == rating, 'The second param was not correct'
+
+ def test_edit_tutorial(self,):
+ function_name = 'edit_tutorial'
+
+ self.controller.edit_tutorial(None,self.tut)
+
+ assert function_name in self.model.call_list, 'edit_tutorial was not called'
+ assert self.model.call_list[function_name] == self.tut, 'The parameter was not correct'
+
+
+ def test_info_tutorial(self):
+ function_name = 'display_info_dialog'
+
+ self.controller.info_tutorial(None,self.tut)
+
+ assert function_name in self.view.call_list, 'display_info_dialog was not called'
+ assert self.view.call_list[function_name] == self.tut, 'The parameter was not correct'
+
+
+
+ def test_save_tutorial_info(self):
+ function_name = 'save_metadata'
+
+ self.controller.save_tutorial_info(self.tut)
+
+ assert function_name in self.model.call_list, 'save_metadata was not called'
+ assert self.model.call_list[function_name] == self.tut, 'The parameter was not correct'
+
+
+ def test_delete_tutorial(self):
+ function_name = 'delete_tutorial'
+
+ self.controller.delete_tutorial(None,self.tut)
+
+ assert function_name in self.model.call_list, 'delete_tutorial was not called'
+ assert self.model.call_list[function_name] == self.tut, 'The parameter was not correct'
+
+ def test_publish_tutorial(self):
+ function_name = 'publish_tutorial'
+
+ self.controller.publish_tutorial(None,self.tut)
+
+ assert function_name in self.model.call_list, 'publish_tutorial was not called'
+ assert self.model.call_list[function_name] == self.tut, 'The parameter was not correct'
+
+ def test_unpublish_tutorial(self):
+ function_name = 'unpublish_tutorial'
+
+ self.controller.unpublish_tutorial(None,self.tut)
+
+ assert function_name in self.model.call_list, 'unpublish_tutorial was not called'
+ assert self.model.call_list[function_name] == self.tut, 'The parameter was not correct'
+
+
+class WsViewTest(TestCase):
+ def setUp(self):
+ self.workshop = WorkshopView()
+
+ def test_init(self):
+ assert self.workshop.mainView is not None, "main View not initialized"
+
+ def test_set_tutorial_list(self):
+ tut1 = Tutorial({'name':'btut1','description':'tutorial 1'})
+ tut2 = Tutorial({'name':'atut2','description':'tutorial 2'})
+ tut_list = [tut1,tut2]
+
+ assert self.workshop.mainView.list_container.get_children()
+
+ self.workshop.set_tutorial_list(tut_list)
+
+ assert self.workshop.mainView.tutorial_list == tut_list, 'The tutorial list not was processed correctly'
+
+ #tutorials are sorted by name so tut2 comes first
+ assert self.workshop.mainView.tutorial_list[0] == tut2
+ assert self.workshop.mainView.tutorial_list[1] == tut1
+
+ def test_change_sorting(self):
+ tut1 = Tutorial({'name':'btut1','description':'tutorial 1','rating':5})
+ tut2 = Tutorial({'name':'atut2','description':'tutorial 2','rating':1})
+ tut_list = [tut1,tut2]
+
+ self.workshop.set_tutorial_list(tut_list)
+
+ assert self.workshop.mainView.tutorial_list[0] == tut2
+ assert self.workshop.mainView.tutorial_list[1] == tut1
+
+ self.workshop.change_sorting('rating')
+
+ ##This test fails for the moment because rating are in reverse order
+ assert self.workshop.mainView.tutorial_list[0] == tut1, 'Not sorted by rating correctly'
+ assert self.workshop.mainView.tutorial_list[1] == tut2, 'Not sorted by rating correctly'
+
+
+
+
+ \ No newline at end of file
diff --git a/Workshop.activity/tests/mocks.py b/Workshop.activity/tests/mocks.py
new file mode 100644
index 0000000..d85d8f2
--- /dev/null
+++ b/Workshop.activity/tests/mocks.py
@@ -0,0 +1,134 @@
+import WorkshopModel
+import WorkshopController
+import Workshop
+
+class ViewMock():
+ """
+ Mock for the Workshop View
+
+ This mock contains a call list in which the name of the called function are added in the order they were called
+ """
+ def __init__(self):
+ self.__call_list ={}
+
+#method for handling the call list
+ def get_call_list(self):
+ return self.__call_list
+
+ def clear_call_list(self):
+ self.__call_list = {}
+
+ call_list = property(get_call_list)
+
+#method from the WorkshopView
+ def set_tutorial_list(self,tutorial_list):
+ self.__call_list.append('set_tutorial_list')
+
+ def change_sorting(self,sorting_key):
+ self.__call_list['change_sorting'] = sorting_key
+
+ def refresh_tutorial_info(self):
+ self.__call_list.append('refresh_tutorial_info')
+
+ def toggle_tutorial_publish(self,tutorial,published):
+ self.__call_list.append('toggle_tutorial_publish')
+
+ def display_detail(self,tutorial):
+ self.__call_list['display_detail'] = tutorial
+
+ def display_main_view(self):
+ self.__call_list['display_main_view'] = None
+
+ def display_info_dialog(self,tutorial):
+ self.__call_list['display_info_dialog'] = tutorial
+
+class WorkshopControllerMock(WorkshopController.WorkshopController):
+ def __init__(self):
+ self.__call_list =[]
+
+#method for handling the call list
+ def get_call_list(self):
+ return self.__call_list
+
+ def clear_call_list(self):
+ self.__call_list = []
+
+ call_list = property(get_call_list)
+
+#method from the WorkshopCOntroller
+ def tutorial_query(self,widget,keyword):
+ self.__call_list.append('tutorial_query')
+
+ def sort_selection_changed(self,widget,sort):
+ self.__call_list.append('sort_selection_changed')
+
+ def launch_tutorial_triggered(self,widget,tutorial):
+ self.__call_list.append('launch_tutorial_triggered')
+
+ def show_details(self,widget,tutorial):
+ self.__call_list.append('show_details')
+
+ def back_pressed(self,widget,data):
+ self.__call_list.append('back_pressed')
+
+ def edit_tutorial(self,widget,data):
+ self.__call_list.append('edit_tutorial')
+
+ def rate_tutorial(self,widget,data):
+ self.__call_list.append('rate_tutorial')
+
+ def update_tutorial(self,widget,tutorial):
+ self.__call_list.append('update_tutorial')
+
+ def info_tutorial(self,widget,tutorial):
+ self.__call_list.append('info_tutorial')
+
+ def delete_tutorial(self,widget,tutorial):
+ self.__call_list.append('delete_tutorial')
+
+ def publish_tutorial(self,widget,tutorial):
+ self,__call_list.append('publish_tutorial')
+
+ def unpublish_tutorial(self,widget,tutorial):
+ self,__call_list.append('unpublish_tutorial')
+
+class WorkshopModelMock():
+ def __init__(self):
+ self.__call_list ={}
+
+#method for handling the call list
+ def get_call_list(self):
+ return self.__call_list
+
+ def clear_call_list(self):
+ self.__call_list = {}
+
+ call_list = property(get_call_list)
+
+
+ def query(self,keyword):
+ self.__call_list['tutorial_query'] = keyword
+
+ def launch_tutorial(self,tutorial):
+ self.__call_list['launch_tutorial'] = tutorial
+
+ def rate_tutorial(self,tutorial,rating):
+ self.__call_list['rate_tutorial'] = (tutorial,rating)
+
+ def edit_tutorial(self,tutorial):
+ self.__call_list['edit_tutorial'] = tutorial
+
+ def save_metadata(self,tutorial):
+ self.__call_list['save_metadata'] = tutorial
+
+ def delete_tutorial(self,tutorial):
+ self.__call_list['delete_tutorial'] = tutorial
+
+ def update_tutorial_infos(self,tutorial,new_infos):
+ self.__call_list.append('update_tutorial_infos')
+
+ def publish_tutorial(self,tutorial):
+ self.__call_list['publish_tutorial'] = tutorial
+
+ def unpublish_tutorial(self,tutorial):
+ self.__call_list['unpublish_tutorial'] = tutorial
diff --git a/Workshop.activity/tests/run-tests.py b/Workshop.activity/tests/run-tests.py
new file mode 100644
index 0000000..5df8cc1
--- /dev/null
+++ b/Workshop.activity/tests/run-tests.py
@@ -0,0 +1,20 @@
+ACTIVITY_PATH = "../"
+
+import os, sys
+sys.path.insert(0,os.path.abspath(ACTIVITY_PATH))
+import unittest
+
+if __name__ == '__main__':
+ import WorkshopModel
+ import WorkshopController
+ import Workshop
+ import mocks
+ import MTTests
+
+
+ suite = unittest.TestSuite()
+ loader = unittest.TestLoader()
+ suite.addTest(loader.loadTestsFromModule(MTTests))
+
+ runner = unittest.TextTestRunner()
+ runner.run(suite)