diff --git a/doc/developer/Moodle_Export.md b/doc/developer/Moodle_Export.md new file mode 100644 index 0000000000..263812a658 --- /dev/null +++ b/doc/developer/Moodle_Export.md @@ -0,0 +1,57 @@ +# What is Moodle? +Moodle is a Learning Management System with a goal to give teachers and students the required tools to teach and learn. It can be used to support any style of teaching and learning. + +# Moodle Backup File +A Moodle Backup File (.mbz) is a compressed archive of a Moodle course that can be used to restore a course within Moodle which preserves course contents, structure and settings, but does not include student work or grades. +This file can be used to restore a course on Moodle. + +## What is included? + +A moodle backup file includes : +- Resources (Files, Folders etc.) +- Activities (Assignments, Quizzes and Quiz questions) +- Settings (Course Format, Theme, Course Completion settings, settings for Assignments and Quizzes) + +It does **not include** : +- Student Enrollment +- Groups +- Guest Access Settings +- Contributions to Collaborative Activities made by any course member + +# Using and creating a .mbz file + +- FOR EXTRACTING + - `tar -xvzf moodle_backup.mbz` + +- FOR ZIPPING INTO .mbz + - `cd folder1` , where folder1 is the folder containing all contents + - `tar -czvf .mbz ./* ` + +# Structure of the .mbz file +A moodle course has several sections (or topics) which further has modules inside it. Modules/activities can be of various types- resources, ebooks etc. + +There are four main directories which are - activities, course, files and sections. + +The files which are the most important are - + +- **moodle_backup.xml** : This is the main file which describes various versions, course metadata like name, id, etc, titles, links to activities and sections as well as the settings used in a moodle course. +- **sections/section_{id}/section.xml** : This describes a section in the moodle course which contains id, number, name, summary, timemodified and availability json. +- **course/course.xml** : This file describes a course, course name, id, summary, startdate, enddate, coursedisplay, category etc. +- **activities/{activity_type}_{id}/inforef.xml** : This has the file IDs which refer to the files.xml file +- **activities/{activity_type}_{id}/module.xml** : This contains the module data like module name in the section, module id and other settings related to it. +- **activites/{activity_type}_{id}/resource.xml** : This describes the name, displayoptions, timemodified, etc for the resource in an activity. +- **files.xml** : This is a very important file, which links all the media content and text content together. It contains a lot of file tags with their ids which refer to type of the component, context id, time modified, source, filepath, filenames, author, content hash, mimetype, itemid, etc. + +To store a file, file is renamed as its SH1 hash (found using BASH command `sha1sum filename.ext`). And, file is stored in files/ a folder with name as first two letters of the hash. e.g. If hash of file is d3614f1cb8df5a00faaf805b5be7670ddfbebbb5 , It is stored as files/d3/d3614f1cb8df5a00faaf805b5be7670ddfbebbb5 + +Kindly check the complete file structure for more details and other files. + +A python script `convert.py` was written to create these xml files to embed an epub that was generated on CLIx Server into a moodle course. The mbz file was created after creating these xmls and zipping them together and then the mbz was restored on Moodle. + +Hence, the process can be summarised as- +1. Running the script +2. Zipping the file into an .mbz format +3. "Restoring" on Moodle + + +Note that this was tested on the demo moodle website- https://demo.moodle.net with a teacher login. \ No newline at end of file diff --git a/doc/developer/WorkingUsingCodeForUnit.mbz b/doc/developer/WorkingUsingCodeForUnit.mbz new file mode 100644 index 0000000000..78469abc97 Binary files /dev/null and b/doc/developer/WorkingUsingCodeForUnit.mbz differ diff --git a/doc/developer/convert.py b/doc/developer/convert.py new file mode 100644 index 0000000000..203a557a2d --- /dev/null +++ b/doc/developer/convert.py @@ -0,0 +1,606 @@ +import os +import subprocess +from lxml import etree + +folder = "./exportworking/" + + +def create_skeleton(): + if not os.path.exists(folder+ "activities"): + os.mkdir(folder+"activities") + if not os.path.exists(folder+ "course"): + os.mkdir(folder+"course") + if not os.path.exists(folder+ "files"): + os.mkdir(folder+"files") + if not os.path.exists(folder+ "sections"): + os.mkdir(folder+"sections") + + +def generate_dummy_files(): + completion_xml = open(folder+"completion.xml","w") + completion_xml.write('\n\n') + completion_xml.close() + + gradebook_xml = open(folder+"gradebook.xml","w") + gradebook_xml.write('\n\n') + gradebook_xml.close() + + grade_history_xml = open(folder + "grade_history.xml", "w") + grade_history_xml.write('\n\n\n\n') + grade_history_xml.close() + + groups_xml = open(folder + "groups.xml", "w") + groups_xml.write('\n\n\n\n') + groups_xml.close() + + outcomes_xml = open(folder + "outcomes.xml", "w") + outcomes_xml.write('\n\n') + outcomes_xml.close() + + questions_xml = open(folder + "questions.xml", "w") + questions_xml.write('\n\n') + questions_xml.close() + + roles_xml = open(folder + "roles.xml", "w") + roles_xml.write('\n\n') + roles_xml.close() + + scales_xml = open(folder + "scales.xml", "w") + scales_xml.write('\n\n') + scales_xml.close() + +def generate_moodle_backup_file(course_id,full_name,short_name): + + root = etree.Element("moodle_backup") + info = etree.SubElement(root, "information") + name = etree.SubElement(info, "name") + name.text = full_name + + moodle_version = etree.SubElement(info, "moodle_version") + moodle_version.text = '2017111302' + + moodle_release = etree.SubElement(info, "moodle_release") + moodle_release.text = '3.4.2 (Build: 20180319)' + + backup_version = etree.SubElement(info, "backup_version") + backup_version.text = '2017111300' + + backup_release = etree.SubElement(info, "backup_release") + backup_release.text = '3.4' + + backup_date = etree.SubElement(info, "backup_date") + backup_date.text = '1523717039' #TODO: ADD CURRENT DATE + + mnet_remoteusers = etree.SubElement(info, "mnet_remoteusers") + mnet_remoteusers.text = '0' + + include_files = etree.SubElement(info, "include_files") + include_files.text = '1' + + include_file_references_to_external_content = etree.SubElement(info, "include_file_references_to_external_content") + include_file_references_to_external_content.text = '0' + + original_wwwroot = etree.SubElement(info, "original_wwwroot") + original_wwwroot.text = '' + + original_site_identifier_hash = etree.SubElement(info, "original_site_identifier_hash") + original_site_identifier_hash.text = '' + + original_course_id = etree.SubElement(info, "original_course_id") + original_course_id.text = '3' #hardcoded + + original_course_format = etree.SubElement(info, "original_course_format") + original_course_format.text = 'topics' + + original_course_fullname = etree.SubElement(info, "original_course_fullname") + original_course_fullname.text = full_name + + original_course_shortname = etree.SubElement(info, "original_course_shortname") + original_course_shortname.text = short_name + + original_course_startdate = etree.SubElement(info, "original_course_startdate") + original_course_startdate.text = '1523505600' + + original_course_enddate = etree.SubElement(info, "original_course_enddate") + original_course_enddate.text = '1555041600' + + original_course_contextid = etree.SubElement(info, "original_course_contextid") + original_course_contextid.text = '40' + + original_system_contextid = etree.SubElement(info, "original_system_contextid") + original_system_contextid.text = '1' + + details = etree.SubElement(info, "details") + + detail = etree.SubElement(details, "detail") + detail.set('backup_id','0') + + type_ = etree.SubElement(detail, "type") + type_.text = 'course' + + format_ = etree.SubElement(detail, "format") + format_.text = 'moodle2' + + interactive = etree.SubElement(detail, "interactive") + interactive.text = '1' + + mode = etree.SubElement(detail, "mode") + mode.text = '30' + + execution = etree.SubElement(detail, "execution") + execution.text = '1' + + executiontime = etree.SubElement(detail, "executiontime") + executiontime.text = '0' + + contents = etree.SubElement(info, "contents") + activities = etree.SubElement(contents, "activities") + + activity = etree.SubElement(activities, "activity") + + module_id = etree.SubElement(activity, "moduleid") + module_id.text = '1' #TODO:Generate IDs + + section_id = etree.SubElement(activity, "sectionid") + section_id.text = '1' + + module_name = etree.SubElement(activity, "modulename") + module_name.text = 'resource' + + title = etree.SubElement(activity, "title") + title.text = full_name + + directory = etree.SubElement(activity, "directory") + directory.text = 'activities/resource_'+module_id.text + + sections = etree.SubElement(contents, "sections") + + section = etree.SubElement(sections, "section") + + section_id = etree.SubElement(section, "sectionid") + section_id.text = '1' + + title = etree.SubElement(section, "title") + title.text = 'Epub uploaded' + + directory = etree.SubElement(section, "directory") + directory.text = 'sections/section_'+section_id.text + + course = etree.SubElement(contents, "course") + + ccourse_id = etree.SubElement(course, "courseid") + ccourse_id.text = course_id + + title = etree.SubElement(course, "title") + title.text = short_name + + directory = etree.SubElement(course, "directory") + directory.text = 'course' + + + settings = etree.SubElement(info, "settings") + + setting = etree.SubElement(settings, "setting") + + level = etree.SubElement(setting, "level") + level.text = 'root' + + name = etree.SubElement(setting, "name") + name.text = 'filename' + + value = etree.SubElement(setting,"value") + value.text = 'ExportWorking.mbz' #TODO + + parts = ["users","anonymize","role_assignments","activities","blocks","filters","comments","badges","calendarevents","userscompletion","logs","grade_histories","questionbank","groups","competencies"] + + for i in parts: + setting = etree.SubElement(settings,"setting") + level = etree.SubElement(setting, "level") + level.text = 'root' + + name = etree.SubElement(setting,"name") + name.text = i + + value = etree.SubElement(setting,"value") + value.text = '1' + + + setting = etree.SubElement(settings,"setting") + + level = etree.SubElement(setting, "level") + level.text = 'section' + + section = etree.SubElement(setting, "section") + section.text = 'section_1' + + name = etree.SubElement(setting,"name") + name.text = 'section_1_included' + + value = etree.SubElement(setting,"value") + value.text = '1' + + + setting = etree.SubElement(settings,"setting") + + level = etree.SubElement(setting, "level") + level.text = 'section' + + section = etree.SubElement(setting, "section") + section.text = 'section_1' + + name = etree.SubElement(setting,"name") + name.text = 'section_1_userinfo' + + value = etree.SubElement(setting,"value") + value.text = '0' + + + setting = etree.SubElement(settings,"setting") + + level = etree.SubElement(setting, "level") + level.text = 'activity' + + activity = etree.SubElement(setting, "activity") + activity.text = 'resource_1' + + name = etree.SubElement(setting,"name") + name.text = 'resource_1_included' + + value = etree.SubElement(setting,"value") + value.text = '1' + + setting = etree.SubElement(settings,"setting") + + level = etree.SubElement(setting, "level") + level.text = 'activity' + + activity = etree.SubElement(setting, "activity") + activity.text = 'resource_1' + + name = etree.SubElement(setting,"name") + name.text = 'resource_1_userinfo' + + value = etree.SubElement(setting,"value") + value.text = '0' + + backup_file = open(folder+ "moodle_backup.xml","wb") + backup_file.write(etree.tostring(root,xml_declaration=True,encoding='UTF-8',pretty_print=True).replace(b"'", b'"')) + backup_file.close(); + +def fill_sections_folder(section_id,sname,ssummary): + + if not os.path.exists(folder+ "sections/section_"+section_id): #hardcoded + os.mkdir(folder+"sections/section_"+section_id) + + info_ref = open(folder + "sections/section_"+section_id+"/inforef.xml","w") + info_ref.write('\n\n') + + root = etree.Element("section") + root.set('id',section_id) + + number = etree.SubElement(root, "number") + number.text = '1' + + name = etree.SubElement(root, "name") + name.text = sname + + summary = etree.SubElement(root, "summary") + summary.text = ssummary + + summary_format = etree.SubElement(root, "summaryformat") + summary_format.text = '1' + + sequence = etree.SubElement(root, "sequence") + sequence.text = '1' + + visible = etree.SubElement(root, "visible") + visible.text = '1' + + availabilityjson = etree.SubElement(root, "availabilityjson") + availabilityjson.text = '{"op":"&","c":[],"showc":[]}' + + timemodified = etree.SubElement(root, "timemodified") + timemodified.text = '1523491174' #TODO + + + section_file = open(folder+ "sections/section_"+section_id+"/section.xml","wb") + section_file.write(etree.tostring(root,xml_declaration=True,encoding='UTF-8',pretty_print=True).replace(b"'", b'"')) + section_file.close() + + + + +def fill_course_folder(course_id,context_id,full_name,short_name,csummary): + calendar_xml = open(folder+"course/calendar.xml", "w") + calendar_xml.write('\n\n') + calendar_xml.close() + + competencies_xml = open(folder+"course/competencies.xml", "w") + competencies_xml.write('\n\n \n \n \n \n') + competencies_xml.close() + + completion_default_xml = open(folder+"course/completiondefault.xml", "w") + completion_default_xml.write('\n\n') + completion_default_xml.close() + + #course_xml = open(folder + "course/course.xml", "w") + #course_xml.write('\n\n') + #course_xml.close() + + filters_xml = open(folder+"course/filters.xml", "w") + filters_xml.write('\n\n \n \n \n \n') + filters_xml.close() + + inforef_xml = open(folder+ "course/inforef.xml", "w") + inforef_xml.write('\n') + inforef_xml.close() + + roles_xml = open(folder+ "course/roles.xml", "w") + roles_xml.write('\n\n \n \n \n \n') + roles_xml.close() + + root = etree.Element("course") + root.set('id',course_id) + root.set('contextid',context_id) + + dicti = {'shortname': short_name, + 'fullname': full_name, + 'idnumber': '', + 'summary': csummary, + 'summaryformat':'1', + 'format':'topics', + 'showgrades':'1', + 'newsitems':'1', + 'startdate':'1523505600', + 'enddate':'1555041600', + 'marker':'0', + 'maxbytes':'0', + 'legacyfiles':'0', + 'showreports':'0', + 'visible':'1', + 'groupmode':'0', + 'groupmodeforce':'0', + 'defaultgroupingid':'0', + 'lang':'', + 'theme':'', + 'timecreated':'1523491078', + 'timemodified':'1523712920', + 'requested':'0', + 'enablecompletion':'1', + 'completionnotify':'0', + 'hiddensections':'0', + 'coursedisplay':'0', + 'category':'', + 'tags':''} + + for key,value in dicti.items(): + + temp = etree.SubElement(root, key) + temp.text = value + + course_file = open(folder+ "course/course.xml","wb") + course_file.write(etree.tostring(root,xml_declaration=True,encoding='UTF-8',pretty_print=True).replace(b"'", b'"')) + course_file.close() + + +def fill_activities_folder(type1, activity_id, section_id): + + if not os.path.exists(folder+ "activities/"+type1+"_"+activity_id): #hardcoded + os.mkdir(folder+ "activities/"+type1+"_"+activity_id) + + current_path = folder+ "activities/"+type1+"_"+activity_id + + calender_xml = open(current_path+"/calendar.xml", "w") + calender_xml.write('\n\n') + calender_xml.close() + + competencies_xml = open(current_path+"/competencies.xml", "w") + competencies_xml.write('\n\n \n \n \n \n') + competencies_xml.close() + + filters_xml = open(current_path+"/filters.xml", "w") + filters_xml.write('\n\n \n \n \n \n') + filters_xml.close() + + grade_history_xml = open(current_path+"/grade_history.xml", "w") + grade_history_xml.write('\n\n\n\n') + grade_history_xml.close() + + grades_xml = open(current_path+"/grades.xml", "w") + grades_xml.write('') + grades_xml.close() + + roles_xml = open(current_path+"/roles.xml", "w") + roles_xml.write('\n\n \n \n \n \n') + roles_xml.close() + + #Time to make inforef + + root = etree.Element("inforef") + fileref = etree.SubElement(root,"fileref") + file = etree.SubElement(fileref,"file") + + file_ids = ['100', '101'] #Add for more file ids + + for i in file_ids: + file1 = etree.SubElement(fileref,"file") + id_ = etree.SubElement(file1, "id") + id_.text = i + + inforef_file = open(folder+ "activities/"+type1+"_"+activity_id+"/inforef.xml","wb") + inforef_file.write(etree.tostring(root,xml_declaration=True,encoding='UTF-8',pretty_print=True).replace(b"'", b'"')) + inforef_file.close() + + #Time to make module.xml + + + root = etree.Element("module") + root.set('id',activity_id) + root.set('version','2017111300') + + module_dic = {'modulename':type1, + 'sectionid':section_id, + 'sectionnumber':'1', + 'idnumber':'', + 'added':'1523491403', + 'score':'0', + 'indent':'0', + 'visible':'1', + 'visibleoncoursepage':'1', + 'visibleold':'1', + 'groupmode':'0', + 'groupingid':'0', + 'completion':'0', + 'completiongradeitemnumber':'$@NULL@$', + 'completionview':'0', + 'completionexpected':'0', + 'availability':'$@NULL@$', + 'showdescription':'0', + 'tags' : ''} + + for key,value in module_dic.items(): + temp = etree.SubElement(root, key) + temp.text = value + + module_file = open(folder+ "activities/"+type1+"_"+activity_id+"/module.xml","wb") + module_file.write(etree.tostring(root,xml_declaration=True,encoding='UTF-8',pretty_print=True).replace(b"'", b'"')) + module_file.close() + + + #Time to mkae resource.xml + root = etree.Element("activity") + root.set('id',activity_id) + root.set('moduleid', activity_id) #TODO figure out + root.set('modulename', type1) + root.set('contextid', '43') + + resource = etree.SubElement(root, type1) + resource.set('id', activity_id) + + res_dic = { 'name': 'CLIX UNIT EPUB', + 'intro':'', + 'introformat':'1', + 'tobemigrated':'0', + 'legacyfiles':'0', + 'legacyfileslast':'$@NULL@$', + 'display':'0', + 'displayoptions':'a:1:{s:10:"printintro";i:1;}', + 'filterfiles':'0', + 'revision':'2', + 'timemodified':'1523494275'} + + for key,value in res_dic.items(): + temp = etree.SubElement(resource, key) + temp.text = value + + res_file = open(folder+ "activities/"+type1+"_"+activity_id+"/resource.xml","wb") + res_file.write(etree.tostring(root, xml_declaration=True, encoding='UTF-8', pretty_print=True).replace(b"'", b'"') ) + res_file.close() + + +def configure_files(filename): + fileloc = "/home/bgargi/HBCSE/try_pandoc/le_unit-4-finding-postal-charges_ver11092017.epub" + hash1, name = subprocess.check_output("sha1sum "+fileloc, shell=True).split() + hash1 = hash1.decode('ASCII') + foldername = hash1[0:2] + #foldername = foldername.decode('ASCII') + x = folder+"files/"+foldername + if not os.path.exists(x): + os.mkdir(x) + os.system('cp '+fileloc+' '+folder+"files/"+foldername+"/"+hash1) + + root = etree.Element('files') + file = etree.SubElement(root, 'file') + file.set('id', '100') #TODO + filesize = os.path.getsize(folder+ "files/"+foldername+'/'+hash1) + filesize = str(filesize) + files_dic = {'contenthash': hash1, + 'contextid':'43', #TODO + 'component':'mod_resource', + 'filearea':'content', + 'itemid':'0', + 'filepath':'/', + 'filename': filename, + 'userid': '2', + 'filesize':filesize, + 'mimetype':'application/epub+zip', + 'status':'0', + 'timecreated':'1523491397', + 'timemodified':'1523491403', + 'source':filename, + 'author':'CLIx', + 'license':'allrightsreserved', + 'sortorder':'1', + 'repositorytype':'$@NULL@$', + 'repositoryid':'$@NULL@$', + 'reference':'$@NULL@$'} + + for key,value in files_dic.items(): + temp = etree.SubElement(file, key) + temp.text = value + + file = etree.SubElement(root, 'file') + file.set('id', '101') #TODO + files_dic = {'contenthash': 'da39a3ee5e6b4b0d3255bfef95601890afd80709', + 'contextid':'43', #TODO + 'component':'mod_resource', + 'filearea':'content', + 'itemid':'0', + 'filepath':'/', + 'filename': '.', + 'userid': '2', + 'filesize':'0', + 'mimetype':'$@NULL@$', + 'status':'0', + 'timecreated':'1523491398', + 'timemodified':'1523494145', + 'source':'$@NULL@$', + 'author':'$@NULL@$', + 'license':'$@NULL@$', + 'sortorder':'0', + 'repositorytype':'$@NULL@$', + 'repositoryid':'$@NULL@$', + 'reference':'$@NULL@$'} + + for key,value in files_dic.items(): + temp = etree.SubElement(file, key) + temp.text = value + + files_xml = open(folder+ "files.xml","wb") + files_xml.write(etree.tostring(root, xml_declaration=True, encoding='UTF-8', pretty_print=True).replace(b"'", b'"') ) + files_xml.close() + +def main(): + + short_name = 'Content Exchange' + full_name = 'Content Exchange for Moodle Export' + course_id = '6' + + section_id = '1' + section_name = 'Read read read!' + section_summary = 'Enjoy reading!' + contextid = '40' + course_summary = 'Welcome to exchange of content trial of CLIx with Moodle!' + + + create_skeleton() + + generate_dummy_files() + + generate_moodle_backup_file(course_id,full_name,short_name) + + fill_sections_folder(section_id,section_name,section_summary) + + fill_course_folder(course_id,contextid,full_name,short_name,course_summary) + #type-resourc/book etc,with id + + type1 = 'resource' + activity_id = '1' + + fill_activities_folder(type1,activity_id,'1') + + configure_files('le_unit-4-finding-postal-charges_ver11092017.epub') #pass epub file name + + #os.system("tar -czvf MoodleBackupUnit.mbz ./*") + +main() \ No newline at end of file diff --git a/doc/developer/epub_issues.md b/doc/developer/epub_issues.md new file mode 100644 index 0000000000..3736836b0e --- /dev/null +++ b/doc/developer/epub_issues.md @@ -0,0 +1,26 @@ +# Bugs found, but yet to be fixed, in the epub generated +We, the Xchange group from BITS Pilani interns 2018, used http://validator.idpf.org/ (which uses EpubCheck library) to validate epubs generated using our export_to_unit.py script and found the following issues/bugs in the epub. +Bugs in **content.opf** : +1. `, ` tags are to be removed from the generated epub, as they are no longer part of the standard. +2. Since we are not storing date and description of the epub, we need to remove ` and ` tags. +3. Format of date inside dcterms:modified should be like 2018-06-07T14:42:18Z (YYYY-MM-DDThh:mm::ssZ), and 2 extra copies of the date after dcterms:modified should be removed. +4. In meta properties ranging from ibooks:version to ibooks:scroll-axis, the property should be contained inside the meta tag, e.g. ` vertical ` +5. In manifest, IDs of items must not have any space like ``. Such spaces ought to be replaced by an underscore ( _ ). Hrefs of the items need not be changed. +6. In manifest, media-type of all xhtml items should be application/xhtml+xml. e.g. `` +7. In manifest, Certain items have names starting with a digit like '1-gender.xhtml', but their IDs should not start with a digit. Those need to be prefixed with an underscore. like '_1-gender.xhtml'. (Href should not be changed.) +8. In spine (table of contents), idref of itemref should not start with a digit like point 7 above. +9. In manifest, id of styles.css should be clix-activity-styles.css like `` +10. Since many xhtml files have scripts inside them, their declaration should have "properties scripted" like so: `` +11. In OEBPS/Text/assessment.xhtml, for allowing remote resource access, property 'remote-resources' should be declared in content.opf +12. sgc-toc.css, opensans-font.css etc should be declared in content.opf +13. All the included fonts should be declared in content.opf +14. To have ibooks support, package tag of content.opf should have prefix attribute as follows: `` + +[//]: # (list end) +Bugs in other files: +- Spaces should be removed from all file-names (esp. images). +- In file videojs-skin-colour.css, last line should not have word "io" as it currently has. +- In clix-activity-styles.css, CSS variables, which are a very recent feature, are causing trouble. +- In OEBPS/Text/*.xhtml, alt text of images having spaces in names should be properly kept. e.g. it should be `Express Parcel` instead of current `Express` +- In OEBPS/Text/*.xhtml, `` tag has been used, which is not supported in HTML5, and should be replaced with proper CSS. +- In OEBPS/Text/nav.xhtml, sgc-nav.css is needed, but is not included in CSS Styles folder. \ No newline at end of file diff --git a/gnowsys-ndf/gnowsys_ndf/ndf/templates/ndf/lms.html b/gnowsys-ndf/gnowsys_ndf/ndf/templates/ndf/lms.html index 91bd450061..6c008111da 100644 --- a/gnowsys-ndf/gnowsys_ndf/ndf/templates/ndf/lms.html +++ b/gnowsys-ndf/gnowsys_ndf/ndf/templates/ndf/lms.html @@ -17,7 +17,7 @@ {% block body_content %} {% check_is_gstaff group_id request.user as is_gstaff %} - + {% comment %} {% include "ndf/buddy.html" %} @@ -26,36 +26,32 @@ {% get_group_object group_id as group_object %}
- + {% include 'ndf/widget_photo_upload.html' with url_name='course_about' widget_for="group_banner" is_banner=True no_update_btn=True node=group_obj %}
{% firstof group_object.altnames group_object.name %}
- +
{% with group_object.member_of_names_list as group_object_member_of_names_list %}
-
- -
{% endif %} @@ -220,7 +244,7 @@

{% trans "Delete Unit:" %} {% firstof group_object.altnames group_object.nam {% if title == "raw material" %} {% include "ndf/assets.html" with title=title %} {% endif %} - + {% if title == "gallery" %} {% include "ndf/assets.html" with title=title %} {% endif %} @@ -321,7 +345,7 @@

{% trans "Delete Unit:" %} {% firstof group_object.altnames group_object.nam $('.lms_banner').backstretch(imgSrc); // setTimeout(function(){ - + // }, 1000); diff --git a/gnowsys-ndf/gnowsys_ndf/ndf/urls/ajax-urls.py b/gnowsys-ndf/gnowsys_ndf/ndf/urls/ajax-urls.py index 38460e6295..808dbf0678 100644 --- a/gnowsys-ndf/gnowsys_ndf/ndf/urls/ajax-urls.py +++ b/gnowsys-ndf/gnowsys_ndf/ndf/urls/ajax-urls.py @@ -62,6 +62,7 @@ url(r'^save_metadata', 'save_metadata', name='save_metadata'), url(r'^save_interactions', 'save_interactions', name='save_interactions'), url(r'^export_to_epub/(?P[\w-]+)', 'export_to_epub', name='export_to_epub'), + url(r'^export_to_epub','export_to_epub',name='export_to_epub'), url(r'^remove_related_oc/', 'remove_related_doc', name='remove_related_doc'), url(r'^get_admin_page_form', 'get_admin_page_form', name='get_admin_page_form'), url(r'^get_help_page_form', 'get_help_page_form', name='get_help_page_form'), diff --git a/gnowsys-ndf/gnowsys_ndf/ndf/views/ajax_views.py b/gnowsys-ndf/gnowsys_ndf/ndf/views/ajax_views.py index 34f804e4a3..951c08a368 100644 --- a/gnowsys-ndf/gnowsys_ndf/ndf/views/ajax_views.py +++ b/gnowsys-ndf/gnowsys_ndf/ndf/views/ajax_views.py @@ -6759,6 +6759,7 @@ def add_asset(request,group_id): except: group_name, group_id = get_group_name_id(group_id) group_obj = Group.get_group_name_id(group_id, get_obj=True) + topic_gst = node_collection.one({'_type': 'GSystemType', 'name': 'Topic'}) topic_nodes = node_collection.find({'member_of': {'$in': [topic_gst._id]}}) context_variables = {'group_id':group_id, 'groupid':group_id,'edit': False} @@ -6780,6 +6781,7 @@ def create_edit_asset(request,group_id): group_id = ObjectId(group_id) except: group_name, group_id = get_group_name_id(group_id) + group_obj = Group.get_group_name_id(group_id, get_obj=True) selected_topic = request.POST.get("topic_list", '') # selected_topic_list = request.POST.getlist("coll_arr[]", '') @@ -7071,20 +7073,54 @@ def save_metadata(request, group_id): return HttpResponseRedirect(reverse('asset_detail', kwargs={'group_id':ObjectId(group_id),'asset_id': ObjectId(node._id)})) # return HttpResponse('success') -def export_to_epub(request, group_id, node_id): +def export_to_epub(request, group_id, node_id=None): from gnowsys_ndf.ndf.views.export_to_epub import * response_dict = {'success': False} - try: - node_obj = node_collection.one({'_id': ObjectId(node_id)}) - epub_loc = create_epub(node_obj) - zip_file = open(epub_loc, 'rb') - response = HttpResponse(zip_file.read(), content_type="application/epub+zip") - response['Content-Disposition'] = 'attachment; filename="'+ slugify(node_obj.name) + '.epub"' - return response - except Exception as export_fail: - print "\n export_fail: ", export_fail - pass - return HttpResponseRedirect(reverse('unit_detail', kwargs={'group_id': group_id})) + + if(node_id!=None): + try: + node_obj = node_collection.one({'_id': ObjectId(node_id)}) + epub_loc = create_epub(node_obj) + zip_file = open(epub_loc, 'rb') + response = HttpResponse(zip_file.read(), content_type="application/epub+zip") + response['Content-Disposition'] = 'attachment; filename="'+ slugify(node_obj.name) + '.epub"' + return response + except Exception as export_fail: + print "\n export_fail: ", export_fail + pass + return HttpResponseRedirect(reverse('unit_detail', kwargs={'group_id': group_id})) + else: + try: + group_obj = node_collection.one({'_id': ObjectId(group_id)}) + epub_loc = create_unit_epub(group_obj) + zip_file = open(epub_loc, 'rb') + response = HttpResponse(zip_file.read(), content_type="application/epub+zip") + response['Content-Disposition'] = 'attachment; filename="' + slugify(group_obj.name) + '.epub"' + return response + except Exception as export_fail: + print "\n export_fail: ", export_fail + pass + return HttpResponseRedirect(reverse('unit_detail', kwargs={'group_id': group_id})) + + + + +#def export_unit_to_epub(request,group_id): +# +# from gnowsys_ndf.ndf.views.export_unit_to_epub import * +# response_dict = {'success': False} +# try: +# group_obj = node_collection.one({'_id': ObjectId(group_id)}) +# epub_loc = create_epub(group_obj) +# zip_file = open(epub_loc, 'rb') +# response = HttpResponse(zip_file.read(), content_type="application/epub+zip") +# response['Content-Disposition'] = 'attachment; filename="' + slugify(group_obj.name) + '.epub"' +# return response +# except Exception as export_fail: +# print "\n export_fail: ", export_fail +# pass +# return HttpResponseRedirect(reverse('unit_detail', kwargs={'group_id': group_id})) + def remove_related_doc(request, group_id): node = request.POST.get('node', None) diff --git a/gnowsys-ndf/gnowsys_ndf/ndf/views/export_to_epub.py b/gnowsys-ndf/gnowsys_ndf/ndf/views/export_to_epub.py index bc72d65969..34da00e5ac 100644 --- a/gnowsys-ndf/gnowsys_ndf/ndf/views/export_to_epub.py +++ b/gnowsys-ndf/gnowsys_ndf/ndf/views/export_to_epub.py @@ -20,7 +20,9 @@ oebps_path = None tool_mapping = {} -with open("/static/ndf/epub/tool_mapping.json", "r") as tool_paths: +BASE = os.path.dirname(os.path.abspath(__file__)) + +with open(os.path.join(BASE, "../static/ndf/epub/tool_mapping.json")) as tool_paths: global tool_mapping tool_mapping = json.loads(tool_paths.read()) @@ -402,9 +404,59 @@ def create_epub(node_obj): zipf = zipfile.ZipFile(epub_root + '.epub', 'w', zipfile.ZIP_DEFLATED) epub_dump(epub_root, zipf) zipf.close() + shutil.rmtree(epub_root) + print "Successfully created epub: ", epub_name + return str(epub_root + '.epub') + +def create_unit_epub(group_obj): + + epub_disp_name = None + epub_name = group_obj.name + if group_obj.altnames: + epub_disp_name = group_obj.altnames + else: + epub_disp_name = group_obj.name + + if not os.path.exists(GSTUDIO_EPUBS_LOC_PATH): + os.makedirs(GSTUDIO_EPUBS_LOC_PATH) + + datetimestamp = datetime.now().isoformat() + epub_name = slugify(epub_name + "_" + str(datetimestamp)) + epub_root = os.path.join(GSTUDIO_EPUBS_LOC_PATH, epub_name) + + os.makedirs(epub_root) + os.makedirs(os.path.join(epub_root, "META-INF")) + + global oebps_path + oebps_path = os.path.join(epub_root, "OEBPS") + os.makedirs(oebps_path) + + create_mimetype(epub_root) + create_container_file(os.path.join(epub_root, "META-INF")) + + create_subfolders(os.path.join(epub_root, "OEBPS"), oebps_files) + + lesson_nodes = node_collection.find({'_id': {'$in': group_obj.collection_set}}) + + for lesson in lesson_nodes: + content_list = lesson.collection_dict + build_html(os.path.join(epub_root, "OEBPS", "Text"), content_list, epub_name) + update_content_metadata(str(lesson._id), datetimestamp, epub_disp_name) + + # create_content_file(os.path.join(epub_name,"OEBPS"),content_list) + # create_ncx_file(os.path.join(epub_name,"OEBPS"),content_list) + fill_from_static() + + + print "Successfully created epub extraction: ", epub_name + zipf = zipfile.ZipFile(epub_root + '.epub', 'w', zipfile.ZIP_DEFLATED) + epub_dump(epub_root, zipf) + zipf.close() + shutil.rmtree(epub_root) print "Successfully created epub: ", epub_name return str(epub_root + '.epub') + def check_ip_validity(): import re if re.match(r'^((\d{1,2}|1\d{2}|2[0-4]\d|25[0-5])\.){3}(\d{1,2}|1\d{2}|2[0-4]\d|25[0-5])$', ip):