# -*- coding: utf-8
# $Id$
#
# 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$'[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 Blog
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 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.CatalogPathAwareness import CatalogAware
import WebtopTrash
import Subgroups
import Kalender
import WordMap
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.
if teachers != ('',):
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.course_locked = 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
self.createWordmaps()
# 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)
# add calendar
cal = Kalender.KalendriSyndmusteKataloog()
self._setObject('syndmused', cal)
# add tracker
self.createTracker()
# add SQL row
self.Statistics.sql_methods._sql_populate_course_table(course_list='('+str(self.getId())+')')
security.declarePrivate('manage_beforeDelete')
def manage_beforeDelete(self, item, container):
""" manage before delete """
self.unindex_object()
self.Statistics.delCourseHistory(int(self.get_id()))
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,
regStatus=0,
cCat = 0,
credits='',
courseID='',
delete_logo='',
lock_course=0,
):
""" 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,
regStatus,
cCat,
credits,
courseID,
delete_logo,
lock_course,
)
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', [])
def isLocked(self):
""" is course locked """
try:
return self.course_locked
except AttributeError:
return False
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)
return 1
def lisaOotaja(self, REQUEST, ootaja):
""" Kasutaja lisab end ootama """
if self.getRegStatus()=='2': #automaatregistreerumine
self.ootajastKursuseLiikmeks(REQUEST, ootaja)
else:
m=self.kysiOotajad(REQUEST)
if ootaja not in m:
m.append(ootaja)
elif ootaja in m:
m.remove(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
security.declareProtected(perm_edit, 'kasutajateKodutoodeArhiiv')
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:
try:
kc=getattr(x.webtop, 'c'+self.id)
except AttributeError:
continue
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:
#XXX:TODO: check this, might be incorrect
esitluskaust = getattr(kg.portfolio, 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)
# Don't add course to user's cache twice
if self.get_id() not in uinf.user_courselist_cache:
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.")
pass
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!'
# FIXME: input_checks
security.declareProtected(perm_edit, 'add_users_form_handler')
def add_users_form_handler(
self, course_id,
REQUEST,
users_None='', # checkboxes
users_Teacher='',
users_Tutor='',
users_Student='',
None_to_Teacher='', # submit buttons
Teacher_to_None='',
None_to_Tutor='',
Tutor_to_None='',
None_to_Student='',
Student_to_None='',
Tutor_to_Teacher='',
Teacher_to_Tutor='',
Student_to_Tutor='',
Tutor_to_Student='',
):
"""Adding and removing users from a given course."""
from Kirjakast import Kirjakast
temp = getattr(self.fle_root(), 'fle_users')
for x in ('None_to_Teacher', # left/right
'Teacher_to_None',
'None_to_Tutor',
'Tutor_to_None',
'None_to_Student',
'Student_to_None',
'Tutor_to_Teacher', # up/down
'Teacher_to_Tutor',
'Student_to_Tutor',
'Tutor_to_Student'):
if eval(x):
m = re.match("^(.*)_to_(.*)", x)
old_role = m.group(1)
new_role = m.group(2)
if 'Student' in new_role and not self.isCourseReady(self):
return self.message_dialog_error(
title="Error",
message="Students can be added to the course when all required course information is submitted and course status is open(not preparing, not finished). Adding teachers is OK.",
action="manage_participants")
users = "users_%s" % old_role
if eval(users):
owners = [u[0] for u in self.get_local_roles()]
if type(eval(users)) is types.StringType:
exec '%s = [%s,]' % (users, users)
if new_role == 'None':
for user in eval(users):
self.remove_person(user)
if self.has_group_folder():
self.remove_folder_link(
(self.fle_users.get_user_info(user),))
else:
for user in eval(users):
if user in owners:
new_roles = (new_role, 'Owner')
else:
new_roles = (new_role,)
uinf = self.fle_users.get_user_info(user)
if self.get_id() not in uinf.user_courselist_cache:
uinf.user_courselist_cache.append(self.get_id())
uinf._p_changed = True
self.set_roles(user, new_roles)
try:
koht = getattr(temp, user)
koht2 = getattr(koht, 'kirjad')
tlang = uinf.get_language()
teade=translate(self,'You have been added to course ',target=tlang)
koht2.saadaKiri(REQUEST,
uinf.get_uname(),
uinf.get_uname(),
teade,'\n\n\n'+\
teade+": "+\
str(self.get_name()),9999)
except:
pass
else:
return self.message_dialog_error(
self, REQUEST,
title='Error',
message='Please select some users first',
action='manage_participants')
self.reindex_object()
REQUEST.RESPONSE.redirect('manage_participants')
#'add_course_form_2_2?course_id=' + course_id))
return
# Should be never reached.
return REQUEST
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=0,
# 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 int(show_only_roleplay)==1:
#obj.__roleplay_only = 1
obj.set_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,
regStatus = 0,
cCat = 0,
credits='',
courseID='',
delete_logo = '',
lock_course=0,
):
"""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.setRegStatus(regStatus)
if cCat:
self.setCourseCategory(int(cCat))
self.set_credits(credits)
self.set_courseID(courseID)
self.course_locked = lock_course
if delete_logo:
self.removeLogo()
#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 getattr(abc, 'public', None) is 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 """
self.hide_show_course_context(cc_id)
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
c._p_changed = 1
break
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
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, '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 ImportIMS import Importer
from ImportExport import Exporter
from StringIO import StringIO
out = StringIO()
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, out=out)
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 ExportIMS import Kirjutus
from ExportIMS 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, users='None', teacherExporting=teacherExporting)
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.kirjutaKasutajaBlog(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.kirjutaBlog(self.Blog, k.organization, k.resources)
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
security.declareProtected(perm_edit, 'removeLogo')
def removeLogo(self):
""" remove logo """
imgp = getattr(self.fle_root().images, 'courses_imgs', None)
try:
imgp._delObject('course_'+str(self.get_id())+'_image')
except AttributeError:
pass
security.declareProtected(perm_edit, 'setLogo')
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
try:
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 > 300:
mod = float(300)/float(height)
width = width*mod
height = height*mod
im = im.resize((int(width),int(height)))
if width > 300:
mod = float(300)/float(width)
width = width*mod
height = height*mod
im = im.resize((int(width),int(height)))
s = cStringIO.StringIO()
try:
im.save(s, "JPEG", quality=100)
except KeyError:
im.save(s, "GIF", 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()
except:
return 0
self.setLogoThumbnail()
return 1
security.declarePrivate('setLogoThumbnail')
def setLogoThumbnail(self):
""" creates thumbnail from uploaded course logo """
resize = 1
try:
from PIL import Image
except ImportError:
resize = 0
if not resize: return 1
import cStringIO
imgp = getattr(self.fle_root().images, 'courses_imgs', None)
if imgp is None:
return 1
if 1==1:
oimg = getattr(imgp, 'course_'+str(self.get_id())+'_image', None)
try:
s = cStringIO.StringIO(oimg.data)
except TypeError:
s = cStringIO.StringIO(str(oimg.data))
if s is None: return 1
im = Image.open(s)
(width, height) = im.size
# resize image 59x59
if height > 59:
mod = float(59)/float(height)
width = width*mod
height = height*mod
im = im.resize((int(width), int(height)))
if width > 59:
mod = float(59)/float(width)
width = width*mod
height = height*mod
im = im.resize((int(width), int(height)))
s = cStringIO.StringIO()
try:
im.save(s, "JPEG", quality=100)
except KeyError:
im.save(s, "GIF", quality=100)
s.seek(0)
img = s.read()
try:
imgp._delObject('course_'+str(self.get_id())+'_image_thumbnail')
except AttributeError:
pass
imgp.manage_addImage('course_'+str(self.get_id())+'_image_thumbnail', img, 'image')
img = getattr(imgp, 'course_'+str(self.get_id())+'_image_thumbnail')
img.ZCacheable_setManagerId('HTTPCache')
img.ZCacheable_invalidate()
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 hasLogoThumbnail(self):
""" if course logo has thumbnail """
if not self.hasLogo():
return 0
if not getattr(self.images.courses_imgs, 'course_'+str(self.get_id())+'_image_thumbnail', None):
self.setLogoThumbnail()
return 0
return 1
def getLogoTag(self, REQUEST=None):
""" "
return getattr(getattr(self, self.getSkinsFolderName()).images, 'course_default.gif')
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(1)
return getattr(getattr(self, self.getSkinsFolderName()).images, 'course_default.gif').absolute_url(1)
def getLogoThumbnailURL(self):
""" URL to course logo thumbnail """
if self.hasLogoThumbnail():
return getattr(self.images.courses_imgs, 'course_'+str(self.get_id())+'_image_thumbnail', None).absolute_url(1)
return getattr(getattr(self, self.getSkinsFolderName()).images, 'course_default.gif').absolute_url(1)
def setStatus(self, sttus):
""" set course statuse """
self.staatus=sttus
def setRegStatus(self, regStatus):
""" set registration status """
self.regStatus = regStatus
def getStatus(self):
""" get course status """
if getattr(self, 'staatus', None) is None:
return 0
else:
return self.staatus
def getRegStatus(self):
""" return how students get to course """
return getattr(self, 'regStatus', 0)
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!
curtime = time.time()
aeg = curtime-86400*30
#XXX: go away!
if self.lastModify() time:
time = tmp
return float(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
security.declareProtected(perm_view, 'hasWordmaps')
def hasWordmaps(self):
""" if course has wordmaps """
return getattr(self, 'wordmaps', None)
security.declareProtected(perm_edit, 'createWordmaps')
def createWordmaps(self):
""" create wordmaps """
import WordMapTools
wf=WordMapTools.WordmapFolder()
wf.id="wordmaps"
self._setObject(wf.id, wf)
security.declarePrivate('createTracker')
def createTracker(self, REQUEST=None):
""" create tracker. Trackers came in IVA 1.0 """
from Tracker import Tracker
tracker = Tracker()
try:
self._setObject(tracker.getId(), tracker)
except:
self._delObject(tracker.getId())
self._setObject(tracker.getId(), tracker)
if REQUEST:
return self.message_dialog(message='Tracker created', action='tracker')
else:
return 0
Globals.default__class_init__(Course)
# EOF