# -*- 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