# -*- coding: utf-8 # $Id: Course.py 327 2005-09-06 21:40:47Z vahur $ # # Copyright 2001, 2002 by IVA Team and contributors # # This file is part of IVA. # # IVA 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. # # IVA 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 IVA; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA """Contains class Course, which represents one course, which has one or more CourseContexts, which in turn contain knowledge building conversations.""" __version__ = '$Revision: 327 $'[11:-2] import time, strptime import string, types import OFS, Globals from Globals import Persistent, Acquisition import AccessControl from AccessControl import ClassSecurityInfo import copy import re import Kodutoo import WordMap import Blog import viited import teated try: from Products.ZWiki.ZWikiPage import ZWikiPage USE_ZWIKI = 1 except: USE_ZWIKI = 0 from Jamming import Jamming from TraversableWrapper import Traversable from common import add_dtml, reload_dtml, intersect_bool, make_action, get_roles, get_local_roles, translate from input_checks import strip_all, is_valid_title from CourseContext import CourseContext #from ThinkingTypeSetManager import ThinkingTypeSetManager as TTSM from Thread import Thread from Cruft import Cruft from TempObjectManager import TempObjectManager from input_checks import render, normal_entry_tags_and_link from common import perm_view, perm_edit, perm_manage, perm_add_lo from Products.ZCatalog.CatalogAwareness import CatalogAware import WebtopTrash import Subgroups import Kalender from QuizManager import QuizManager # Each Course object contains (usually) one or more CourseContexts, which # represent different aspects of the course. # A Course contains information and services on one course implementation. class Course( Persistent, Traversable, Cruft, OFS.Folder.Folder, AccessControl.Role.RoleManager, OFS.SimpleItem.Item, Thread, CatalogAware, ): """Course, contained within CourseManager, represents one course.""" meta_type = 'Course' security = ClassSecurityInfo() security.declareObjectPublic() # Call courses/course_html (This way we avoid copying index_html # to each instance of Course class.) # ATTENTION: We don't exactly know how this works (for example, # do we really need REQUEST ?) # #TODO: This could be done with a URL pointing to the course_html script. #But if we need this default implementation, we could use #restrictedTraverse('course_html') to activate Zope acquisition. #FIX:bla? security.declareProtected('View', 'index_html') def index_html(self, REQUEST=None): """Default script""" return REQUEST.RESPONSE.redirect('course_html') # Parameters: # #- parent: should be a CourseManager # #- name: name of the Course # #- tts: list of ThinkingTypeSets (copies are made in manage_afterAdd) # #- teachers: list of users (of class UserInfo?) that are granted #Teacher privileges # #- etc: other textual information def __init__( self, parent, # Whatever you do, dont bind this to self. name, teachers, description='', organisation='', methods='', starting_date='', ending_date='', uniq_id='' ): """Constructor of the course.""" # Overriding all_meta_types is not not beautiful...but, hey!, it works! #self.all_meta_types = ( # {'name': 'ThinkingTypeSet', # 'action': 'get_id'},) # Cache active members. self.active_memb_cache = [] # Remove all HTML tags from parameters name = strip_all(name) description = strip_all(description) methods = strip_all(methods) organisation = strip_all(organisation) Thread.__init__(self, parent) # Takes care of id and title. for teacher in teachers: self.set_roles(teacher, ('Teacher',)) self.__name = name # name of the course self.__organisation = organisation self.__description = description self.__methods = methods self.__starting_date = starting_date self.__ending_date = ending_date self.__credits = '' self.__courseID = '' self.__quote = '' self.course_category = 0 self.default_catalog = 'courses_zcatalog' # course should have uniq id since self.id tends to change self.setUniqId(uniq_id) # This is for group folder path listings - show path up to course. self.toplevel = 1 security.declarePrivate('manage_afterAdd') #Each course should have its own copy of the ThinkingTypeSets, #as the course administrator (teacher) should be able to edit them, #create new ones and so forth. These changes must not propagate to #other courses: hence the copying of the set. def manage_afterAdd(self, item, container): """foo""" from common import roles_student, roles_tutor, roles_teacher from common import roles_admin # add subgroups manager sg = Subgroups.SubgroupManager() self._setObject(sg.id, sg) # add quizes qm = QuizManager() self._setObject(qm.id, qm) # add jamming self._setObject('jamming', Jamming('jamming')) # add wordmaps wf=WordMap.WordmapFolder() wf.id="wordmaps" self._setObject(wf.id, wf) # add assignments k=Kodutoo.KodutoodeKataloog() k.id='kodutood' self._setObject(k.id, k) self.index_object() # add blog b = Blog.Blog() b.id = "Blog" self._setObject(b.id,b) v = viited.ViideteKataloog() v.id = "viited" self._setObject(v.id,v) t = teated.teatedeKataloog() t.id = "teated" self._setObject(t.id,t) # add calendar cal = Kalender.KalendriSyndmusteKataloog() self._setObject('syndmused', cal) security.declarePrivate('manage_beforeDelete') def manage_beforeDelete(self, item, container): """ manage before delete """ self.unindex_object() security.declareProtected(perm_edit, 'add_course_form_handler') def add_course_form_handler(self, REQUEST, course_id, my_name, desc, organisation, methods, start_date, end_date, cancel='', # submit buttons add='', # tekst='', logo_upload=None, staatus=0, cCat = 0, credits='', courseID='' ): """ modify course info, override CourseManager method. """ if cancel: return REQUEST.RESPONSE.redirect('course_info?course_id=%s' % course_id) elif not add: raise 'IVA Error', 'Inknown button' if course_id != self.get_id() or not course_id: return action=apply( make_action, ['manage_course_info'] + [(x, eval(x)) for x in ('my_name', 'desc', 'organisation', 'methods', 'start_date', 'end_date')]) action += '&course_id=' + course_id my_name=my_name.strip() if not is_valid_title(my_name): return self.message_dialog_error( self, REQUEST, title='Invalid name', message='Give valid name', action=action) if my_name != self.get_name(): if my_name in [x.get_name for x in self.get_courses()]: return self.message_dialog_error( self, REQUEST, title='Invalid name', message="Name '%s' taken" % my_name, action=action) from common import convert_date # convert dates to time.time()-format errors = [] if not start_date: starting_date = 0 else: try: time_tuple = strptime.strptime(start_date, translate(self,'short_date_format',default="%Y-%m-%d",target=self.giveLanguage(REQUEST))) starting_date = convert_date(str(time_tuple[2]), # day str(time_tuple[1]), # month str(time_tuple[0])) # year except: errors.append(translate(self,'Starting date:',default="%Y-%m-%d",target=self.giveLanguage(REQUEST)),) if not end_date: ending_date = 0 else: try: time_tuple = strptime.strptime(end_date, translate(self,'short_date_format',default="%Y-%m-%d",target=self.giveLanguage(REQUEST))) ending_date = convert_date(str(time_tuple[2]), # day str(time_tuple[1]), # month str(time_tuple[0])) # year except: errors.append(translate(self,'Ending date:',default="%Y-%m-%d",target=self.giveLanguage(REQUEST)),) organisation = organisation.strip() if organisation and not is_valid_title(organisation): errors.append(translate(self,'Organization',target=self.giveLanguage(REQUEST))) if len(errors) > 0: return self.message_dialog_error( self, REQUEST, title='Invalid input', message=translate(self,'Invalid fields',target=self.giveLanguage(REQUEST)) + ": '" + \ "' , '".join(errors) + "'", action=action) logo = None try: if len(logo_upload.filename)>0: logo=logo_upload.read() else: logo = None except AttributeError: pass self.update( my_name, desc, organisation, methods, starting_date, ending_date, tekst, logo, staatus, cCat, credits, courseID ) return REQUEST.RESPONSE.redirect(self.absolute_url()+'/gf/course_info') def is_courseContext(self): """ is course context? """ return def kasTeadmuspajasKontekste(self): "jah/ei" return len(self.objectValues('CourseContext'))>0 def kasMeediapajasProjekte(self): "jah/ei" return len(self.jamming.objectValues('JamSession'))>0 def kysiOotajad(self, REQUEST=None): "Kursusele registreerunud, keda pole veel kinnitatud" return getattr(self, 'ootajad', []) security.declareProtected(perm_edit,'manage_pendingUsers_handler') def manage_pendingUsers_handler(self, REQUEST, addtocourse='',deletefromlist='',pender=''): "Lisamine või kustutus" #from Kirjakast import Kirjakast temp = getattr(self.fle_root(), 'fle_users') if not pender: return self.message_dialog_error(self, REQUEST, title="Error", message='Please select some users first', action='manage_pendingUsers') if type(pender)==types.StringType: pender=(pender,) penders=self.kysiOotajad() new_penders = [] if deletefromlist: for x in pender: puser = getattr(temp, x) for y in penders: if y == x: continue new_penders.append(y) if addtocourse: for x in pender: puser = getattr(temp, x) self.add_student(puser.get_uname()) mess_title = translate(self,'Added to course',target=self.giveLanguage(REQUEST)) mess_body = translate(self,'You have been added to course ',target=self.giveLanguage(REQUEST))+' '+self.get_name() puser.kirjad.saadaKiri(REQUEST,puser.get_uname(),puser.get_uname(),mess_title,mess_body,999999) for x in penders: if x not in pender: new_penders.append(x) self.ootajad=new_penders return REQUEST.RESPONSE.redirect('haldusleht') def ootajastKursuseLiikmeks(self, REQUEST, kasutaja): "Lisamine" self.add_student(kasutaja.get_uname()) return 1 def lisaOotaja(self, REQUEST, ootaja): "Kasutaja lisab end ootama" if self.getStatus()=='4': #automaatregistreerumine self.ootajastKursuseLiikmeks(REQUEST, ootaja) else: m=self.kysiOotajad(REQUEST) if ootaja not in m: m.append(ootaja) self.ootajad=m return 0 def kasOotaja(self, nimi): "Kas jooksev kasutaja on kursusele ootaja" m=self.kysiOotajad() kas=0 for x in m: if nimi==x: kas=1 return kas security.declareProtected(perm_view, 'get_printable_name') # No additional comments. def get_printable_name(self): """Return name of the course.""" return self.__name security.declareProtected(perm_view, 'get_bg_colour_name') def get_bg_colour_name(self): """...""" return 'gr' # security.declareProtected(perm_view, 'get_name') security.declarePublic('get_name') # No additional comments. def get_name(self): """Get course name.""" return self.__name security.declareProtected(perm_view, 'get_organisation') # No additional comments. def get_organisation(self): """Get organisation name.""" return self.__organisation security.declareProtected(perm_view, 'get_description') # No additional comments. def get_description(self): """Get description.""" return self.__description security.declareProtected(perm_view,'render_description') def render_description(self): """Render description.""" return render( self.get_description(), legal_tags=normal_entry_tags_and_link) security.declareProtected(perm_view, 'get_methods') # No additional comments. def get_methods(self): """Get info on methods.""" return self.__methods security.declareProtected(perm_view,'render_methods') def render_methods(self): """Render methods.""" return render( self.get_methods(), legal_tags=normal_entry_tags_and_link) def get_courseID(self): """ get course id """ try: return self.__courseID except AttributeError: return "" def set_courseID(self,courseID): """ set course id(the other one) """ self.__courseID = courseID def get_credits(self): """ get credits info """ try: return self.__credits except AttributeError: return "" def set_credits(self,credits): """ set credits """ self.__credits = credits # security.declareProtected(perm_view, 'get_teachers') # No additional comments. security.declarePublic('get_teachers') def get_teachers(self): """Get teachers.""" retval = [] for (user, roles) in self.get_local_roles(): if 'Teacher' in roles: retval.append(user) return retval security.declareProtected(perm_view, 'get_start_dd') # No additional comments. def get_start_dd(self): """Return starting day.""" if self.__starting_date: return time.localtime(self.__starting_date)[2] else: return '' security.declareProtected(perm_view, 'get_start_mm') # No additional comments. def get_start_mm(self): """Return starting month.""" if self.__starting_date: return time.localtime(self.__starting_date)[1] else: return '' security.declareProtected(perm_view, 'get_start_yyyy') # No additional comments. def get_start_yyyy(self): """Return starting year.""" if self.__starting_date: return time.localtime(self.__starting_date)[0] else: return '' security.declareProtected(perm_view, 'get_end_dd') # No additional comments. def get_end_dd(self): """Return ending day.""" if self.__ending_date: return time.localtime(self.__ending_date)[2] else: return '' security.declareProtected(perm_view, 'get_end_mm') # No additional comments. def get_end_mm(self): """Return ending month.""" if self.__ending_date: return time.localtime(self.__ending_date)[1] else: return '' security.declareProtected(perm_view, 'get_end_yyyy') # No additional comments. def get_end_yyyy(self): """Return ending year.""" if self.__ending_date: return time.localtime(self.__ending_date)[0] else: return '' # security.declareProtected(perm_view, 'get_printable_starting_date') security.declarePublic('get_printable_starting_date') #FIX:security check # No additional comments. def get_printable_starting_date(self, REQUEST): """Get starting date.""" return time.strftime(translate(self,'short_date_format',default="%Y-%m-%d",target=self.giveLanguage(REQUEST)), time.localtime(self.__starting_date)) # security.declareProtected(perm_view, 'get_printable_ending_date') security.declarePublic('get_printable_ending_date') #FIX:security check # No additional comments. def get_printable_ending_date(self, REQUEST): """Get ending date.""" if self.__ending_date == 0: return '' return time.strftime(translate(self,'short_date_format',default="%Y-%m-%d",target=self.giveLanguage(REQUEST)), time.localtime(self.__ending_date)) security.declarePrivate('get_start_date') def get_start_date(self): return self.__starting_date security.declarePrivate('get_end_date') def get_end_date(self): return self.__ending_date security.declareProtected(perm_view, 'get_users') def get_users(self, REQUEST): """Return dict with 2 members of attendees as UserInfo objects. 'active_d' is the list of active members. 'others_d' is the rest .. [[UIObj'active1'], [UIObj'other1', UIObj'other2', ..]]""" rv = {'active_d':[], 'others_d': []} au = str(REQUEST.AUTHENTICATED_USER) for uname in [(u[0]) for u in self.get_local_roles()]: from Errors import FleError try: o = self.fle_users.get_user_info(uname) if uname == au or uname in self.active_memb_cache: rv['active_d'].append(o) else: rv['others_d'].append(o) except FleError: pass return rv security.declarePublic('get_n_users') def get_n_users(self): return len(self.get_all_users_id()) security.declareProtected(perm_view, 'get_all_users') def get_all_users(self): """Return a list of all users on course.""" rv = [] for uname in self.get_all_users_id(): try: rv.append(self.fle_users.get_user_info(uname)) except Exception: pass return rv def kasutajateKodutoodeArhiiv(self, REQUEST): "Arhiiv, kuhu paigutatakse kasutaja esitatud materjalid" import tempfile import zipfile failinimi=tempfile.mktemp() arhiiv=zipfile.ZipFile(failinimi,"w",zipfile.ZIP_DEFLATED) kasutajad=self.get_all_users() for x in kasutajad: kc=getattr(x.webtop, 'c'+self.id) for too in self.kodutood.objectValues(): if too.tyyp==1: #materjal if hasattr(kc, 'portfolio'): if hasattr(kc.portfolio, too.id): tookataloog=getattr(kc.portfolio, too.id) tookataloog.looArhiiv(REQUEST, arhiiv, x.id+'/') if too.tyyp==2: #grupp mitu=0 punkte=-1 for kg in x.kysiKursuseSisegrupid(self): if kg.id in too.grupid: esitluskaust=getattr(self.leiaGrupiKaust(kg.id), too.id) esitluskaust.looArhiiv(REQUEST, arhiiv, kg.id+'/') arhiiv.close() file = open(failinimi,"rb") export_data=file.read() file.close() import os os.remove(failinimi) REQUEST.RESPONSE.setHeader('Content-type','application/zip') REQUEST.RESPONSE.setHeader('Content-disposition','attachment; filename=tooarhiiv.zip') return export_data def fotogaKasutajad(self): "kasutajad, kel on foto" m=[] for x in self.get_all_users(): if x.has_photo(): m.append(x) return m def fototaKasutajad(self): "kasutajad, kel pole fotot" m=[] for x in self.get_all_users(): if not x.has_photo(): m.append(x) return m security.declareProtected(perm_view, 'get_all_users_id') def get_all_users_id(self): """Return a list of all users on this course. Same thing as get_all_users, but this one returns a list of id's of UserInfo objects, not the UserInfo object reference.""" cl = [] for u in self.get_local_roles(): if 'Student' in u[1] or 'Teacher' in u[1]: cl.append(u[0]) return cl # return [u[0] for u in self.get_local_roles()] security.declareProtected(perm_view, 'get_users_with_role') def get_users_with_role(self, role): """Return a list of participants who have a specified role.""" rv = [] for e in self.get_local_roles(): if role in e[1]: # try: rv.append(self.fle_users.get_user_info(e[0])) # except Exception: # pass return rv def get_sorted_user_list(self, role): nimed = [] rolliga = self.get_users_with_role(role) for aaa in rolliga: temp = [] if aaa.get_last_name()=='' and aaa.get_first_name()=='': temp.append(aaa.get_id()) temp.append(aaa.get_id()) temp.append(aaa) nimed.append(temp) else: temp.append(aaa.get_last_name().lower()) temp.append(aaa.get_first_name().lower()) temp.append(aaa) nimed.append(temp) nimed.sort() uuserid = [] for kasutajad in nimed: uuserid.append(kasutajad[2]) return uuserid security.declareProtected(perm_edit, 'add_student') #The person is added with Student role access. # NOTE: This method is no longer needed, except in the test cases! def add_student(self, name): """Add person to the course.""" # Check that user exists... Raises exception if not. #XXX: is this really needed!? now commented out # self.fle_users.get_user_info(name) uinf = self.fle_users.get_user_info(name) uinf.user_courselist_cache.append(self.get_id()) uinf._p_changed = True self.set_roles(name, ('Student',)) self.reindex_object() security.declareProtected(perm_view, 'get_valid_roles') # No additional comments. def get_valid_roles(self): """Get the roles valid for persons added to the course.""" from CourseManager import course_level_roles valid_roles = list(course_level_roles) #valid_roles.append('Teacher') return valid_roles security.declareProtected(perm_manage, 'remove_person') def remove_person(self, person): """Remove person from the course.""" # Check that user exists... Raises exception if not. kasutaja=self.fle_users.get_user_info(person) try: kasutaja.user_courselist_cache.remove(self.get_id()) except: pass kasutaja._p_changed = True #We have to do it the hard way since set_jooksev_kursus turns me away kasutaja.jooksev_kursus=0 # We use the get_local_roles method, because we need to # see if the user has a role attached to this course object # specifically, and we don't want the roles in the acquisition # tree to interfere. if len(get_local_roles(self,person))==0: raise FleError, ("User "+person+" does not belong to this course.") self.__unset_roles((person,)) # Note: person _must_ be a sequence! def __unset_roles(self, persons): """Unset roles of one user.""" self.manage_delLocalRoles(persons) security.declareProtected(perm_view, 'has_role') # No additional comments. def has_role(self, person, role): """Return whether the user is in the specified role.""" return role in get_roles(self,person) security.declareProtected(perm_view, 'get_teacher') def get_teacher(self): """Get the name of the teacher (creator of the course).""" for user,roles in self.get_local_roles(): if 'Teacher' in roles: return user raise 'Course has no teacher!' security.declareProtected(perm_edit, 'set_roles') # Note: roles _must_ be a sequence! # Called from CourseManager.add_users_form_handler def set_roles(self, person, roles): """Set roles of one person.""" self.__unset_roles(person) self.manage_setLocalRoles(person, roles) # FIXME: input_checks: tt_set_name not checked # FIXME: input_checks: two course contexts can have identical name. security.declareProtected(perm_add_lo, 'add_course_context') # Handler for add_course_context_form def add_course_context( self, my_name, description, tt_set_name, description_long, REQUEST, use_roleplay='', # New in ZPTIVA show_only_roleplay='', # Submit buttons. publish='', cancel='', ): """Add CourseContext object.""" if publish: error_fields = [] errors = [] my_name = my_name.strip() if not is_valid_title(my_name): error_fields.append(translate(self,'title of context',target=self.giveLanguage(REQUEST))) if my_name in [x[1] for x in self.get_course_context_names()]: errors.append(translate(self,"Name '%s' taken",target=self.giveLanguage(REQUEST)) % my_name) # Variables 'description' and 'description_long' are not checked # because render_description() and render_long_description() # methods in CourseContext filter out unwanted HTML tags. if len(error_fields) > 0 or len(errors) > 0: msg = ", ".join(errors) if len(error_fields) > 0: msg = msg + "
" + 'Invalid fields' + \ ": '" + "' , '".join(error_fields) + "'" return self.message_dialog_error( self, REQUEST, title='Invalid input', message=msg, action=apply( make_action, ['add_course_context_form'] + [(x, eval(x)) for x in ('my_name', 'description', 'tt_set_name', 'description_long')])) uname=str(REQUEST.AUTHENTICATED_USER) obj = CourseContext( self, my_name, description, description_long, tt_set_name, uname,) obj.public = 1 if (show_only_roleplay)==1: obj.__roleplay_only = 1 id = obj.get_id() self._setObject(id, obj) #XXX: statistics - lisaSyndmus(konteksteLisatud. should it be somewhere? #self.lisaSyndmus(REQUEST, 'konteksteLisatud') try: obj.changeOwnership(self.acl_users.getUser(uname).__of__(self.acl_users)) obj.manage_setLocalRoles(uname,('Owner',)) except: pass if REQUEST: pagename="" if use_roleplay: pagename="course_setup_roleplay_form" REQUEST.RESPONSE.redirect('%s/%s' % (str(id),pagename)) elif cancel: return REQUEST.RESPONSE.redirect(REQUEST.URL1) else: raise "add_course_context called without 'publish' or 'cancel'" security.declareProtected(perm_view, 'get_course_context_names') # No additional comments. def get_course_context_names(self): """Return a list of CourseContext names.""" retval = {} for e in self.get_children('CourseContext'): id = e.get_id() name = e.get_name() retval[id] = name return retval.items() #security.declareProtected(perm_view, 'get_n_notes') def get_n_notes(self): """Returns a sum of all notes in all contexts.""" count = 0 for cc in self.get_course_contexts(): count += cc.get_n_notes() return count security.declareProtected(perm_view, 'get_n_unread_notes') def get_n_unread_notes(self,uname): """Returns a sum of all unread notes in all contexts.""" count = 0 for cc in self.get_course_contexts(): count += cc.get_n_unread_notes(uname) return count security.declarePublic('update') # Parameters are received from the form (apparently). def update( self, name, description, organisation, methods, starting_date, ending_date, tekst, logo=None, status=0, cCat = 0, credits='', courseID='', ): """Edit course information.""" self.__name = name self.__description = description self.__organisation = organisation self.__methods = methods self.__starting_date = starting_date self.__ending_date = ending_date self.setQuote(tekst) if logo: self.setLogo(logo) self.setStatus(status) self.setCourseCategory(int(cCat)) self.set_credits(credits) self.set_courseID(courseID) #XXX: remove self.default_catalog = 'courses_zcatalog' self.reindex_object() security.declareProtected(perm_view, 'get_course_contexts') def get_course_contexts(self): """Return a list of all course contexts in this course.""" for abc in self.get_children('CourseContext'): if not getattr(abc, 'public', None): abc.public = 1 return self.get_children('CourseContext') security.declareProtected(perm_view, 'get_course_context_ids_in_order') def get_course_context_ids_in_order(self, id_list): """Return a list of ids of all course contexts in this course.""" return [o.get_id() for o in self.get_course_contexts_in_order(id_list)] security.declareProtected(perm_edit, 'delete_course_context') def context_form_handler(self,REQUEST,cc_id,staatus='', kustuta=''): """ delete or change status """ import types if type(cc_id) is types.StringType: cc_id = ((cc_id),) #if staatus and kustuta: # raise 'IVA error','oops, both buttons athe same time in Knowledge building form' for abc in cc_id: self.hide_show_course_context(abc) #if kustuta: # for abc in cc_id: # self.delete_course_context(abc) return REQUEST.RESPONSE.redirect(self.absolute_url()+'/course_html') security.declareProtected(perm_edit, 'delete_course_context') def delete_course_context(self,id): for c in self.get_children('CourseContext'): if c.get_id() == id: self._delObject(id) return security.declareProtected(perm_edit, 'hide_show_course_context') def hide_show_course_context(self,id): for c in self.get_children('CourseContext'): if c.get_id() == id: if c.public == 1: c.public = 0 else: c.public = 1 return # FIXME: See the random comment below, random is not # FIXME: probably the order that we really want. security.declareProtected(perm_view, 'get_course_contexts_in_order') def get_course_contexts_in_order(self, id_list): """Return a list of all course contexts in this course.""" contexts = {} for c in self.get_children('CourseContext'): contexts[c.get_id()] = c try: pass except: c.public = 1 retval = [] if id_list and id_list != ['']: if type(id_list) == types.StringType: id_list = (id_list, ) # Return courses contexts in a given order. for identifier in id_list: try: retval.append(contexts[identifier]) del contexts[identifier] except KeyError: # invalid id_list pass # If we still course contexts left (id_list is shorter # than the actual number of course_contexts), append # them to list in a random order. for key in contexts.keys(): retval.append(contexts[key]) return retval security.declarePublic('may_view_course') def may_view_course(self, REQUEST): """Return boolean depending on wether user may or may not view the course.""" from AccessControl.PermissionRole import rolesForPermissionOn return intersect_bool( get_roles(self,str(REQUEST.AUTHENTICATED_USER)), rolesForPermissionOn(perm_view,self)) security.declareProtected(perm_view, 'may_add_course_context') def may_add_course_context(self, person): """Return boolean depending on wether user may or may not add a course context to the course.""" from AccessControl.PermissionRole import rolesForPermissionOn return intersect_bool( get_roles(self,person), rolesForPermissionOn(perm_add_lo,self)) security.declareProtected(perm_view, 'may_edit_course') def may_edit_course(self, person): """Return boolean depending on whether person can edit the course or not.""" from AccessControl.PermissionRole import rolesForPermissionOn return intersect_bool( get_roles(self,person), rolesForPermissionOn(perm_edit,self)) security.declareProtected(perm_view, 'active_members') #FIXME: Why not "get_active_members" def active_members(self): """Return a list of names of users who are active.""" return self.active_memb_cache security.declareProtected(perm_view, 'has_group_folder') def has_group_folder(self): """Return whether course has a group folder or not.""" return len(self.objectIds('GroupFolder'))>0 def add_folder(self, my_name): from GroupFolder import GroupFolder from GroupFolderProxy import GroupFolderProxy new_id = 'gf' fol = GroupFolder(None,my_name) fol.id=new_id self._setObject(new_id,fol) fol=fol.__of__(self) # self.make_group_folder_proxies(self.get_all_users()) proxy = GroupFolderProxy(None, # any sense? self.get_name(), self.get_id()) self.jamming._setObject('0', proxy) return fol def make_group_folder_proxies(self, users): for user in users: # user.webtop.add_link(self.get_name()+" (Shared)",self.get_url_to_object(self.gf),1) user.webtop.add_group_folder_proxy(self.get_name(), self.get_id()) def remove_folder_link(self,users): gf = self.get_child('gf') for user in users: fol = user.webtop self.__recurse_remove_folder_link(fol, gf) def __recurse_remove_folder_link(self, folder, gf): for (_id, proxy_fol) in folder.objectItems('GroupFolderProxy'): if proxy_fol.is_proxy_for(gf): folder._delObject(_id) for fol in folder.objectValues('WebtopFolder'): self.__recurse_remove_folder_link(fol, gf) security.declareProtected(perm_edit, 'teacher_import_handler') def teacher_import_handler(self, REQUEST, file='', to_import=''): """ Form handler for teacher """ kursusenr = self.get_course_id_from_req(REQUEST) if not self.kas_opetaja(REQUEST): return "Keelatud" uname = REQUEST.AUTHENTICATED_USER.getUserName() from ImportExportIMS import Importer from ImportExport import Exporter import tempfile, os filename = tempfile.mktemp() f = open(filename,"w+b") f.write(file.read()) f.close() import_data=None imported = Importer(uname,self.fle_root(),'',getattr(self.fle_root().courses, kursusenr),kasuta_jooksvat=1) imported.loadZip(filename) element = imported.loadFile('imsmanifest.xml') imported.processFile(self,element) return REQUEST.RESPONSE.redirect(self.absolute_url()+'/'+str(kursusenr)+'/gf/course_info') security.declareProtected(perm_edit, 'teacher_export') def teacher_export(self,REQUEST): """ teacher export """ return self.iva_ims_export(REQUEST) security.declareProtected(perm_edit, 'iva_ims_export') def iva_ims_export(self, REQUEST=None, testinimi='',teacherExporting=1): """ Course or a quiz export. XML format. """ from ImportExportIMS import Kirjutus from ImportExportIMS import kirjutaYldM import tempfile, os failinimi = tempfile.mktemp() # self.fle_users.export_users(REQUEST,failinimi='ivaexport.zip',kursusega='jah',palju='self.fle_root().fle_users.get_users()') if testinimi: yld = kirjutaYldM() yld.kirjutaYldmanifest(self.get_id(),'QTI test','imsqti_xmlv1p1','','qti.xml') yld.lisaFaili(failinimi) k= Kirjutus(self.fle_root(), self.get_id()) #k.looKursuseFail() #k.exportQTI(failinimi,'') parent = k.looTestiFail() k.kirjutaTestid(parent,testinimi) k.kirjutaTestidZipi(failinimi,'') #k.pakiKursusFaili(failinimi) # k.pakiTulemusFaili(failinimi,testsonly="testsonly") else: yld = kirjutaYldM() yld.kirjutaYldmanifest(self.get_id(), self.get_description(),base=str(self.get_id())+"/") yld.lisaFaili(failinimi) k = Kirjutus(self.fle_root(), self.get_id()) k.looKursuseFail() k.kirjutavCal(self.syndmused,'kursus','GROUP') k.kirjutaWebtop(self.gf,tiitel='RAAMATURIIUL') for sisegr in self.subgroups.objectValues('GroupFolder'): k.kirjutaWebtop(sisegr,'SISEGRUPP:'+sisegr.get_name()) try: k.kirjutavCal(sisegr.syndmused,sisegr.get_name(),'GROUP') except: pass if not teacherExporting: k.kirjutaKasutajaWebtop(self.get_all_users()) k.kirjutaKasutajaSyndmus(self.get_all_users()) self.fle_users.export_users(REQUEST,failinimi=failinimi,kursusega=self.get_id(),palju='kursus.get_all_users()') k.kirjutaItem(k.organization, tiitel='KURSUSE KASUTAJAD',parameters='KURSUSE KASUTAJAD') k.kirjutaResource(k.resources, 'imsent_xmlv1p1','users.xml') k.exportQTI(failinimi,str(self.get_id())+"/",teacherExporting=teacherExporting) k.exportPajad(failinimi,str(self.get_id())+"/",startonly=teacherExporting) k.exportKodutood(self.kodutood,failinimi,str(self.get_id())+"/") k.pakiKursusFaili(failinimi) if REQUEST: file = open(failinimi,"rb") export_data=file.read() file.close() os.remove(failinimi) REQUEST.RESPONSE.setHeader('Content-disposition','attachment; filename=ivaexport.zip') REQUEST.RESPONSE.setHeader('content-type','application/zip') return export_data else: import os import shutil name='iva_'+re.sub('[^a-zA-Z0-9]', '_', self.get_name()) name2 = name number = 1 searched=0 print os.path.isfile(os.path.join(Globals.INSTANCE_HOME,'var',name+'.zip')) print os.path.join(Globals.INSTANCE_HOME,'var',name+'.zip') while os.path.isfile(os.path.join(Globals.INSTANCE_HOME,'var',name+'.zip')): number = number + 1 name = name2+'_ver'+str(number) searched=1 name += '.zip' shutil.move(failinimi,os.path.join(Globals.INSTANCE_HOME,'var',name)) return self.get_name() + ' - ' + os.path.join(Globals.INSTANCE_HOME,'var',name) def write_touchgraph_data(self,obj,type,children_types=None,extra_links=None): links = '' if children_types: links = ' '.join(obj.objectIds(children_types)) if extra_links: links = ' '.join((links,extra_links)) if not links: links=' ' return obj.get_id()+'\t'+\ type+"\t"+\ self.REQUEST.BASE0+self.get_url_to_object(obj)+'\t'+\ obj.get_name()+'\t'\ +links+'\t' # But should we protect it somehow? # The Java client would then need authentication def touchgraph_data(self): """This is a public method.""" data=self.write_touchgraph_data(self,"COURSE","CourseContext") for ctx in self.objectValues("CourseContext"): data=data+ctx.touchgraph_data() return data+"[END DATA]" def getQuote(self): "Kursuse uudis/moto. Vastab tyhja kui pole" return self.__quote def setQuote(self, tekst): """ set quote """ self.__quote=tekst def setLogo(self, img): """ add course logo """ resize = 1 try: from PIL import Image except ImportError: resize = 0 import cStringIO imgp = getattr(self.fle_root().images, 'courses_imgs', None) if imgp is None: return 0 s = cStringIO.StringIO(img) if resize: im = Image.open(s) (width, height) = im.size if height == 80 and im.format == 'JPEG': pass else: if im.mode != 'RGB': im = im.convert('RGB') if height > 80: u_w = float(width)/float(height) im = im.resize((int(80*u_w),80)) s = cStringIO.StringIO() im.save(s, "JPEG", quality=100) s.seek(0) img = s.read() try: imgp._delObject('course_'+str(self.get_id())+'_image') except AttributeError: pass imgp.manage_addImage('course_'+str(self.get_id())+'_image', img, 'image') img = getattr(imgp, 'course_'+str(self.get_id())+'_image') img.ZCacheable_setManagerId('HTTPCache') img.ZCacheable_invalidate() return 1 def hasLogo(self): """ if course has its own logo """ # XXX:weird hack if not getattr(self.images.courses_imgs, 'course_'+str(self.get_id())+'_image', None): return 0 return 1 def getLogoTag(self, REQUEST=None): """ \"\"" return getattr(self.images, 'course_default') def getLogoURL(self): """ URL to course logo """ if self.hasLogo(): return getattr(self.images.courses_imgs, 'course_'+str(self.get_id())+'_image', None).absolute_url() return getattr(self.images, 'course_default').absolute_url() def paneAutomaatRegistreerumine(self, automaatRegistreerumine): """ Tunnuse salvestus """ self.automaatRegistreerumine=automaatRegistreerumine def kysiAutomaatRegistreerumine(self): """ Väljastus, puudumise korral 0 """ return getattr(self, 'automaatRegistreerumine', 0) def setStatus(self, staatus): """ set course statuse """ self.staatus=staatus def getStatus(self): """ get course status """ if getattr(self, 'staatus', None) is None: return 0 else: return self.staatus security.declareProtected(perm_view, 'search_form_handler') def search_form_handler( self, REQUEST, cancel=None, # submit buttons submit=None, # get_name = None, get_content= None ): """Search form handler.""" if (submit) and ((get_name) or (get_content)): for s in 'get_name', 'get_content': REQUEST.set(s, REQUEST[s]) if REQUEST['get_author_name'] == '___anyone___': uname = str(self.REQUEST.AUTHENTICATED_USER) if len(self.fle_users.get_user_info(uname).user_courses()) > 0: REQUEST.set('get_author_name', self.courses.get_unames_on_my_courses(REQUEST)) else: REQUEST.set('get_author_name', uname) else: REQUEST.set('get_author_name', REQUEST['get_author_name']) return self.wt_search_results(self, REQUEST) elif cancel: REQUEST.RESPONSE.redirect('index_html') else: REQUEST.RESPONSE.redirect(REQUEST['HTTP_REFERER']) security.declareProtected(perm_view,'getCourseCategory') def getCourseCategory(self): return self.course_category security.declarePrivate('setcourseCategory') def setCourseCategory(self, id): self.course_category = id self._p_changed = 1 def get_n_artefacts(self): """ number of artefacts on a course """ total = 0 for x in self.jamming.objectValues('JamSession'): total += x.get_n_artefacts() return total def total_n_unseen_jams(self,REQUEST): total = 0 for x in self.jamming.objectValues('JamSession'): total += x.get_n_unread_artefacts(REQUEST) return total def isCourseAlive(self): """ when was last input, new objects, artefacts? """ #XXX: isCourseAlive ZZZ, this is serious and need a rewrite! return 0 curtime = time.time() aeg = curtime-86400*30 #XXX: go away! if not hasattr(self, 'tootoaMuutused'): self.tootoaMuutused = {} if self.viimaneTootoaMuutus('teadmuspaja') time: time = tmp return time def lastModify_printable(self,REQUEST): """ returns printable last modification date """ return time.strftime(translate(self,'short_date_format',default="%Y-%m-%d",target=self.giveLanguage(REQUEST)),time.localtime(self.lastModify())) def lastActive(self): """ return last visit to course """ last = 0 for x in self.get_all_users(): tmp = self.Statistics.getUserCourseStat(int(self.get_id()), 'lastAccess', x.get_uname()) if tmp > last: last = tmp return last def lastActive_printable(self,REQUEST): """ return last visit to course """ return time.strftime(translate(self,'short_date_format',default="%Y-%m-%d",target=self.giveLanguage(REQUEST)),time.localtime(self.lastActive())) security.declareProtected(perm_manage, 'getUniqId') def getUniqId(self): """ return uniq id. we take it with us when exporting and put it back when importing should be random enough to avoid collision """ return self.__uniq_id security.declarePrivate('setUniqId') def setUniqId(self, uniq_id): """ set course uniq id. don't change it. method is here only for beauty """ if not uniq_id: raise 'IVA error:', 'uniq id not passed' self.__uniq_id = uniq_id return 1 Globals.default__class_init__(Course) # EOF