Commit 3da7c1c0 authored by Jonathan Wiedemann's avatar Jonathan Wiedemann
Browse files

make a better lesson structure

this will simplify creation of new lesson
parent 883a9a6c
......@@ -23,7 +23,7 @@
#
################################################
__title__="MOOC Workbench"
__title__ = "MOOC Workbench"
__author__ = "Jonathan Wiedemann"
__url__ = "http://www.freecadweb.org"
......@@ -31,9 +31,10 @@ __url__ = "http://www.freecadweb.org"
from PySide2 import QtCore, QtGui, QtWidgets
# for handling paths
import os, moocwb_locator
import os
import moocwb_locator
# for loading module from path
# for loading module from a path
from os import listdir
from os.path import isfile, join
import importlib.util
......@@ -41,42 +42,45 @@ import importlib.util
# import freecad and its gui
import FreeCAD as app
import FreeCADGui as gui
import WebGui
# import webbrowser to play video inside defaut web Browser
import webbrowser
"""# import MoocChecker
# import MoocChecker
import MoocChecker
# make sure MoocChecker is reloaded
try :
reload(MoocChecker)
except NameError:
import importlib
importlib.reload(MoocChecker)
# name MoocChecker
Check = MoocChecker"""
# for debug purposes
#DEBUG = True
DEBUG = False
DEBUG = True
# DEBUG = False
# make sure MoocChecker is reloaded
if DEBUG:
try:
reload(MoocChecker)
except NameError:
import importlib
importlib.reload(MoocChecker)
def debug():
if DEBUG:print("debug")
if DEBUG:
print("debug")
# set global timer
TIMER = QtCore.QTimer()
if DEBUG:TIMER.timeout.connect(debug)
if DEBUG:
TIMER.timeout.connect(debug)
moocWBpath = os.path.dirname(moocwb_locator.__file__)
moocWBpath_medias = os.path.join(moocWBpath, 'medias')
moocWB_icons_path = os.path.join( moocWBpath_medias, 'icons')
moocWB_path = os.path.dirname(moocwb_locator.__file__)
moocWB_medias_path = os.path.join(moocWB_path, 'medias')
moocWB_icons_path = os.path.join(moocWB_medias_path, 'icons')
class Ui_MoocPlayer(QtWidgets.QDialog):
'''The FreeCAD Player interface'''
def __init__(self, parent=gui.getMainWindow()):
super(Ui_MoocPlayer, self).__init__(parent)
self.setObjectName("MoocPlayer")
......@@ -95,9 +99,6 @@ class Ui_MoocPlayer(QtWidgets.QDialog):
self.text_resume = QtWidgets.QTextBrowser(self)
self.text_resume.setAcceptRichText(True)
self.text_resume.setObjectName("text_resume")
#self.text_resume.setOpenExternalLinks(True)
#self.text_resume.setOpenLinks(True)
## TODO: should open link in external browser
self.verticalLayout_left.addWidget(self.text_resume)
self.horizontalLayout_play = QtWidgets.QHBoxLayout(self)
self.horizontalLayout_play.setObjectName("horizontalLayout_play")
......@@ -138,7 +139,6 @@ class Ui_MoocPlayer(QtWidgets.QDialog):
self.show()
def retranslateUi(self):
self.setWindowTitle(QtWidgets.QApplication.translate("MoocPlayer", "FreeCAD Tutorials", None, -1))
self.label_resume.setText(QtWidgets.QApplication.translate("MoocPlayer", "Résumé de la leçon", None, -1))
......@@ -149,28 +149,29 @@ class Ui_MoocPlayer(QtWidgets.QDialog):
self.btn_next_step.setText(QtWidgets.QApplication.translate("MoocPlayer", "Suivant", None, -1))
self.btn_play.setText(QtWidgets.QApplication.translate("MoocPlayer", "Voir la vidéo", None, -1))
def closeEvent(self, event):
def closeEvent(self, event):
TIMER.stop()
if DEBUG:print("Closing")
if DEBUG:
print("Closing Mooc Player")
class Ui_Manager():
class Manager_MoocPlayer():
'''functions to control and update the dialog'''
def __init__(self, lesson):
self.form = Ui_MoocPlayer()
self.lesson = lesson
self.data_tutorial = lesson.MakeDataTutorial()
self.form.setWindowTitle(QtWidgets.QApplication.translate("MoocPlayer", self.data_tutorial['title'], None, -1))
self.form.setWindowTitle(self.lesson.get_title())
self.current_step_id = 0
self.total_step = len(self.data_tutorial['steps'])
self.total_step = self.lesson.get_lesson_len()
self.fill_data()
self.form.btn_play.clicked.connect(self.play_video)
self.form.btn_next_step.clicked.connect(self.forward_step)
self.form.btn_previous_step.clicked.connect(self.backward_step)
if DEBUG:print(self.data_tutorial['title'])
if DEBUG:print(self.get_label_step())
if DEBUG:
print(self.lesson.get_title())
print(self.get_label_step())
TIMER.start(200)
TIMER.timeout.connect(self.update)
......@@ -178,51 +179,61 @@ class Ui_Manager():
self.form.exec_()
def get_label_step(self):
label = "Étape " + str(self.current_step_id + 1) + " / "+ str(self.total_step)
label = "Étape " + str(self.current_step_id + 1) + " / " + str(self.total_step)
return label
def forward_step(self):
if self.current_step_id + 1 < self.total_step :
if self.current_step_id + 1 < self.total_step:
self.current_step_id += 1
if DEBUG:print(self.get_label_step())
if DEBUG:
print(self.get_label_step())
self.fill_data()
else:
if DEBUG:print("This is the end !")
if DEBUG:
print("This is the end !")
pass
def backward_step(self):
if self.current_step_id > 0 :
if self.current_step_id > 0:
self.current_step_id -= 1
if DEBUG:print(self.get_label_step())
if DEBUG:
print(self.get_label_step())
self.fill_data()
else:
if DEBUG:print("This is the begining !")
if DEBUG:
print("This is the begining !")
pass
def fill_data(self):
self.form.label_step.setText(QtWidgets.QApplication.translate("MoocPlayer", self.get_label_step(), None, -1))
step_data = self.data_tutorial['steps'][self.current_step_id]
self.form.label_step.setText(self.get_label_step())
step_data = self.lesson.get_data_step(self.current_step_id)
self.form.text_resume.setHtml(step_data["description"])
self.form.listWidget_objectifs.clear()
for obj in step_data["objectives"]:
self.form.listWidget_objectifs.addItem(obj)
self.form.listWidget_objectifs.setFixedHeight(
(self.form.listWidget_objectifs.sizeHintForRow(0)+10) *
self.form.listWidget_objectifs.count() +
10 * self.form.listWidget_objectifs.frameWidth() + 8)
(self.form.listWidget_objectifs.sizeHintForRow(0) + 10)
* self.form.listWidget_objectifs.count()
+ 10 * self.form.listWidget_objectifs.frameWidth() + 8)
def play_video(self):
link_url = self.data_tutorial['steps'][self.current_step_id]["video"]
step_data = self.lesson.get_data_step(self.current_step_id)
link_url = step_data["video"]
webbrowser.open(link_url, new=2)
def get_results(self):
results = self.lesson.CheckResult(self.current_step_id)
return results
step_data = self.lesson.get_data_step(self.current_step_id)
results = []
for func in step_data["checks"]:
results.append(eval(func))
if len(results) > 0:
return results
else:
return [0]
def update(self):
if DEBUG:print("Ui_Manager.update")
if DEBUG:
print("Manager_MoocPlayer.update")
results = self.get_results()
brush_green = QtGui.QBrush(QtGui.QColor(85, 170, 0))
brush_green.setStyle(QtCore.Qt.NoBrush)
......@@ -232,10 +243,10 @@ class Ui_Manager():
idx = 0
if results:
for x in results:
if x == 0 :
if x == 0:
self.form.listWidget_objectifs.item(idx).setForeground(brush_red)
self.form.listWidget_objectifs.item(idx).setIcon(QtGui.QIcon(os.path.join(moocWB_icons_path , 'window-close.svg')))
elif x == 1 :
elif x == 1:
self.form.listWidget_objectifs.item(idx).setForeground(brush_green)
self.form.listWidget_objectifs.item(idx).setIcon(QtGui.QIcon(os.path.join(moocWB_icons_path , 'dialog-apply.svg')))
idx += 1
......@@ -243,6 +254,7 @@ class Ui_Manager():
class Ui_MoocChooser(QtWidgets.QDialog):
'''The FreeCAD Player chooser interface'''
def setupUi(self, MoocPlayerChooser):
MoocPlayerChooser.setObjectName("MoocPlayerChooser")
MoocPlayerChooser.resize(324, 339)
......@@ -270,7 +282,7 @@ class Ui_MoocChooser(QtWidgets.QDialog):
QtCore.QObject.connect(self.buttonBox, QtCore.SIGNAL("accepted()"), MoocPlayerChooser.accept)
QtCore.QObject.connect(self.buttonBox, QtCore.SIGNAL("rejected()"), MoocPlayerChooser.reject)
QtCore.QObject.connect(self.listWidget_trainings, QtCore.SIGNAL("itemDoubleClicked(QListWidgetItem *)"), MoocPlayerChooser.launch_mooc)
QtCore.QObject.connect(self.listWidget_trainings, QtCore.SIGNAL("itemClicked(QListWidgetItem *)"), MoocPlayerChooser.get_description)
QtCore.QObject.connect(self.listWidget_trainings, QtCore.SIGNAL("itemClicked(QListWidgetItem *)"), MoocPlayerChooser.display_lesson_description)
QtCore.QMetaObject.connectSlotsByName(MoocPlayerChooser)
self.show()
......@@ -283,66 +295,61 @@ class Ui_MoocChooser(QtWidgets.QDialog):
def accept(self):
self.launch_mooc(self.listWidget_trainings.currentItem())
def get_lessons_title_list(self):
self.lessons_infos_list = []
moocWBpath = os.path.dirname(moocwb_locator.__file__)
moocWBpath_lessons = os.path.join(moocWBpath, 'lessons')
def get_lessons_list(self):
moocWB_path = os.path.dirname(moocwb_locator.__file__)
moocWBpath_lessons = os.path.join(moocWB_path, 'lessons')
onlyfiles = [f for f in listdir(moocWBpath_lessons) if isfile(join(moocWBpath_lessons, f))]
onlyfiles.sort()
#print(onlyfiles)
self.lessons_list = []
for lesson in onlyfiles:
name = lesson.split('.')[0]
path = os.path.join(moocWBpath_lessons, lesson)
spec = importlib.util.spec_from_file_location(name, path)
foo = importlib.util.module_from_spec(spec)
spec.loader.exec_module(foo)
self.lessons_infos_list.append([foo.get_title(),foo.get_description(),foo])
'''print(foo.get_title())
print(foo.get_description())
print(foo.__title__)'''
self.lessons_list.append(foo.lesson())
# return lessons_list
self.listWidget_trainings.addItem(foo.get_title())
def get_lessons_title_list(self):
self.get_lessons_list()
for lesson in self.lessons_list:
self.listWidget_trainings.addItem(lesson.get_title())
def get_description(self, item):
def display_lesson_description(self, item):
# get the row number from item
row = self.listWidget_trainings.row(item)
# display descritpion in label from lessons list
self.label_description.setText(self.lessons_infos_list[row][1])
# description is
desc = self.lessons_list[row].get_description()
# display descritpion in label_description
self.label_description.setText(desc)
def launch_mooc(self, item):
row = self.listWidget_trainings.row(item)
lesson = self.lessons_list[row]
self.close()
# import MoocPlayer
import MoocPlayer
# make sure MoocPlayer is reloaded
try :
reload(MoocPlayer)
except NameError:
import importlib
importlib.reload(MoocPlayer)
lesson = self.lessons_infos_list[row][2]
MoocPlayer.Ui_Manager(lesson)
class MoocChooser():
"class to choose the lesson"
def GetResources(self):
moocWBpath = os.path.dirname(moocwb_locator.__file__)
moocWBpath_medias = os.path.join(moocWBpath, 'medias')
moocWB_icons_path = os.path.join( moocWBpath_medias, 'icons')
return {'Pixmap' : os.path.join( moocWB_icons_path , 'mooc-player.svg'),
'MenuText': QtCore.QT_TRANSLATE_NOOP(u"Mooc","Voir un tutoriel."),
'ToolTip': QtCore.QT_TRANSLATE_NOOP(u"Mooc","Permet de choisir et voir un tutoriel interactif.")}
def IsActive(self):
return True
def Activated(self):
form = Ui_MoocChooser()
form.setupUi(form)
form.exec_()
Manager_MoocPlayer(lesson)
class Command_MoocChooser():
"class to choose the lesson"
def GetResources(self):
moocWB_path = os.path.dirname(moocwb_locator.__file__)
moocWB_medias_path = os.path.join(moocWB_path, 'medias')
moocWB_icons_path = os.path.join(moocWB_medias_path, 'icons')
return {'Pixmap': os.path.join(moocWB_icons_path, 'mooc-player.svg'),
'MenuText': QtCore.QT_TRANSLATE_NOOP(u"Mooc", "Voir un tutoriel."),
'ToolTip': QtCore.QT_TRANSLATE_NOOP(u"Mooc", "Permet de choisir et voir un tutoriel interactif.")}
def IsActive(self):
return True
def Activated(self):
form = Ui_MoocChooser()
form.setupUi(form)
form.exec_()
if app.GuiUp:
gui.addCommand('Mooc_Player', MoocChooser())
gui.addCommand('Mooc_Player', Command_MoocChooser())
......@@ -115,3 +115,62 @@ Quand l'évaluation vous satisfait vous pouvez transmettre les résultats en cli
Une fenêtre s'ouvre avec une chaine de caratère qu'il faut copier/coller dans votre espace MOOC. Vous pouvez cliquer sur le bouton **Copier** pour copier la chaine de caractère dans le presse papier. Il vous restera juste à le coller (clic droit coller ou avec le raccouris Ctrl+V).
<img src="/medias/images/CopyPasteResult.png" width=45% >
## Créer un tutoriel
Dans le dossier "lessons" de l'atelier, faites une copie du fichier "Template Lesson.py".
Renommé le avec un nouveau nom en gardant l"extension ".py".
Ensuite il faut l'éditer :
ligne 26
__author__ = "Votre nom"
ligne 41
self.data_tutorial["title"] = "Le titre comme il apparaît dans la liste."
ligne 42
self.data_tutorial["description"] = '''Mettre ICI la description du tutoriel : \
On met un slashback "\" à la fin des lignes dans le code \
et on peut mettre "\n" pour faire un retour à la ligne.'''
Ensuite il faut copié collé le code de la ligne 44 à 56 autant de fois qu'il y a d'étapes dans votre tutoriel.
Une étape complète :
# Step 1
step = {}
img1 = os.path.join(self.moocWB_icons_path, "Document-new.svg")
step["video"] = "https://open.tube/videos/embed/f5773731-9864-470b-a3d5-9e805c419f96?start=0m00s"
step["objectives"] = ["Create a new document.",]
step["checks"] = ["MoocChecker.document_presence()",]
step["description"] = """<h3>Preparation</h3>
<p><img src= %s width="25"/> Create a new document :
<ul><li>with menu <i>File</i> then <i>New.</li>
<li>with the shortcut Ctrl + N</li></ul></p> """ % (img1, )
self.data_tutorial["steps"].append(step)
Ajouter des variable pour les icônes et les images :
img1 = os.path.join(self.moocWB_icons_path, "icone.svg")
img2 = os.path.join(self.moocWB_images_path, "image.png")
Définir le lien vers la vidéo de l'étape :
step["video"] = "https://lien_vers_la_video.com?start=0m00s"
La liste des objectifs à atteindre (les lignes qui passent de rouge à vert) :
step["objectives"] = ["Objectif 1.",]
Les commandes à effectuer pour vérifier les objectifs :
step["checks"] = ["MoocChecker.document_presence()",]
Pour les commandes il faut étudier le fichier MoocChecker.py qui contient de nombreuse fonctions de vérification.
################################################
#
# MoocPlayer.py
#
# Copyright 2018 Jonathan Wiedemann <contact at freecad-france dot com>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
# MA 02110-1301, USA.
#
#
################################################
__title__ = "MOOC Workbench"
__author__ = "Jonathan Wiedemann"
__url__ = "http://www.freecadweb.org"
# for handling paths
import os
import moocwb_locator
class lesson(object):
def __init__(self):
moocWB_path = os.path.dirname(moocwb_locator.__file__)
moocWB_medias_path = os.path.join(moocWB_path, "medias")
self.moocWB_icons_path = os.path.join(moocWB_medias_path, "icons")
self.data_tutorial = {}
self.data_tutorial["title"] = "[Test] Test1"
self.data_tutorial["description"] = """One test with one objective."""
self.data_tutorial["steps"] = []
# Step 1
step = {}
img1 = os.path.join(self.moocWB_icons_path, "Document-new.svg")
step["video"] = "https://open.tube/videos/embed/f5773731-9864-470b-a3d5-9e805c419f96?start=0m00s"
step["objectives"] = ["Create a new document."]
step["checks"] = ["MoocChecker.document_presence()"]
step["description"] = """<h3>Preparation</h3>
<p><img src= %s width="25"/> Create a new document :
<ul><li>with menu <i>File</i> then <i>New.</li>
<li>with the shortcut Ctrl + N</li></ul></p> """ % (img1, )
self.data_tutorial["steps"].append(step)
def get_title(self):
return self.data_tutorial["title"]
def get_description(self):
return self.data_tutorial["description"]
def get_lesson_len(self):
return len(self.data_tutorial["steps"])
def get_data_step(self, stepid, ):
return self.data_tutorial["steps"][stepid]
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment