# -*- 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
__version__ = "$Revision$"[11:-2]
import AccessControl
import OFS, Globals
from OFS import SimpleItem
import types
import time
import re
from TraversableWrapper import Traversable
from Globals import Persistent, PersistentMapping
from AccessControl import ClassSecurityInfo
from AccessControl import allow_module, allow_class, allow_type
from Cruft import Cruft
from common import translate, perm_edit, perm_view, mkTime
from zope.interface import implements
from interfaces import IAssignment, IUserManager, IAssignmentsContainer
from zope.component import adapter, getUtility
from zope.app.container.interfaces import IObjectAddedEvent
class Kodutoo(
OFS.SimpleItem.Item,
Persistent,
AccessControl.Role.RoleManager,
Traversable,
Cruft
):
meta_type = 'Assignment'
security = ClassSecurityInfo()
implements(IAssignment)
""" Assignment object """
def __init__(self, tyyp, kirjeldus='', normPunkte=0,kalendriID=None):
""" init """
self.tyyp=int(tyyp)
self.kirjeldus=kirjeldus
self.pealkiri = ""
#self.loppaeg=None #Esituspiiri pole määratud. no deadline
# now we always have deadline
self.loppaeg = time.time()
self.normPunkte=normPunkte
self.kalendriID=None #kalendrisyndmuse id. event id.
self.hwID = None #assignment id. for peer review only.
self.testiID = None #quiz id. for quiz type assignment only.
self.wmID=None #wordmap id
self.reviewMapping = PersistentMapping()
self.lockingFolder = None
self.juhend = ""
def lockFolder(self):
""" lock folder """
return getattr(self, 'lockingFolder', 0)
def getAssignmentID(self):
return getattr(self, 'hwID', "")
security.declareProtected(perm_edit, 'setAssignmentID')
def setAssignmentID(self,hwID):
self.hwID = hwID
def get_id(self):
return self.id
def getDeadline(self, REQUEST):
return time.strftime(translate(self,'timestamp_format',default="%H:%M %Y-%m-%d"), time.localtime(self.loppaeg))
def getGrades(self,user,detail='', individual=1):
""" doc string """
if self.tyyp == 2 and not individual:
user = 'subgroup_'+user
gradesObj = getattr(self, 'grades')
if not gradesObj.has_key(self.get_id()):
gradesObj[self.get_id()] = {}
grades = gradesObj[self.get_id()]
if not grades.has_key(user):
value = float(0)
self.setUserPoints(self.get_id(), user, detail, value)
if not gradesObj['Overall'].has_key(user):
gradesObj['Overall'][user] = {}
return grades[user][detail]
def getRawType(self):
""" return assignment type as integer """
return self.tyyp
def getType(self, REQUEST):
""" get assignment type word """
return self.getTypes(REQUEST)[self.tyyp]
def getPoints(self):
""" get assignment value in points """
return self.normPunkte
def quizID(self):
return getattr(self, 'testiID', None)
def getWmID(self):
"Wordmap id"
return self.wmID
def getTitle(self, REQUEST=None):
"Antakse kui on"
if len(self.pealkiri.strip())==0:
return "(untitled)"
return self.pealkiri
security.declareProtected(perm_edit, 'setTitle')
def setTitle(self, title):
""" set title """
self.pealkiri = title
security.declareProtected(perm_edit, 'setDescription')
def setDescription(self, desc):
""" set description """
self.kirjeldus = desc
def getGroups(self):
""" gets groupwork groups"""
if not hasattr(self, 'grupid'):
self.grupid = []
return self.grupid
def kodutooMaterjal(self,bs, tulem='',level=1):
i=1
aste=""
if not hasattr(self, 'juhend'):
self.juhend = ""
while i!=level:
aste += "--"
i=i+1
for obj in bs.list_contents_request2(criteria='getWeight'):
#s=obj.absolute_url[len(self.fle_root().absolute_url()):]
s = obj.getURL(1)
tulem += "\n"
if obj.meta_type == 'WebtopFolder' and not obj.kasWikiKaust:
# TODO: fixme
tulem = self.kodutooMaterjal(obj.getObject(),tulem,level+1)
return tulem
def getReviewers(self,uname):
""" return a list who's work users must review """
if not hasattr(self, 'reviewMapping'):
self.reviewMapping = PersistentMapping()
try:
return self.reviewMapping[uname]
except KeyError:
return []
def maxReviewers(self):
max = -1
for x in self.reviewMapping.keys():
if len(self.reviewMapping[x])>max:
max = len(self.reviewMapping[x])
return max
def countReviewer(self,uname):
count = 0
for x in self.reviewMapping.keys():
if uname in self.reviewMapping[x]:
count = count +1
return count
def numberOfAnswers(self, uname, c_id, hwObj):
""" return numbers of user responses """
try:
path = getattr(self.fle_root().fle_users, uname).webtop
path = getattr(path, 'c'+c_id)
path = getattr(path, 'testivastused')
path = getattr(path, hwObj.testiID)
except AttributeError:
return 0
except TypeError:
return 0
return len(path.objectValues())
def getLinkToAssignment(self, uname, c_id, asID, hwObj=None, tID=None):
""" return url to user's webtop. tyyp==4 """
url = ""
if hwObj:
hwType = hwObj.tyyp
else:
hwType = None
if asID is None:
return ""
try:
path = getattr(self.fle_root().fle_users, uname).webtop
path = getattr(path, 'c'+c_id)
except AttributeError:
path = None
if hwType in (1,4) and path:
try:
path = getattr(path, 'portfolio')
path = getattr(path, asID)
except AttributeError:
return ""
if hwType == 1 and path:
if not len(path.objectValues()):
path = None
if hwType == 0 and path:
try:
path = getattr(path, 'testivastused')
path = getattr(path, hwObj.testiID)
try:
if tID is not None:
path = getattr(path, path.objectValues()[tID].get_id())
except IndexError:
return ""
#test0 -> variant1
except AttributeError:
return ""
except TypeError:
return ""
if hwType==5:
try:
path=None #et ei naitaks veebilauale
teachermap=getattr(self.find_course().wordmaps, str(self.wmID))
for m in teachermap.getChildMaps():
if str(m.getOwner()).strip()==str(uname).strip():
if m.getHomeworkStatus()==2:
url=m.absolute_url()
except:
url=""
# XXX:FIX: needs testing
# AttributeError: 'NoneType' object has no attribute 'absolute_url'
try:
if path:
url = path.absolute_url()+'/'
except AttributeError:
return ""
return url
security.declareProtected(perm_edit, 'automaticMatching')
def automaticMatching(self, REQUEST, courseid, randomize, number, number2):
""" automatically divide users """
course = getattr(self.fle_root().courses, courseid)
users = course.get_all_users_id()
from random import choice
for x in users:
self.reviewMapping[x] = []
for i in range(0,number):
nameObj = choice(users)
ring = 1
stopp = 0
while (x == nameObj or self.countReviewer(nameObj)>number2-1) and stopp==0:
nameObj = choice(users)
ring = ring + 1
if ring>70:
stopp = 1
if not stopp:
self.addPeerReviewMatching(0, x, nameObj, r_add=1)
else:
#XXX: try finding available person
for j in users:
if self.countReviewer(j) < number2:
self.addPeerReviewMatching(0, x, nameObj, r_add=1)
continue
self.reviewMapping._p_changed = True
return REQUEST.RESPONSE.redirect('manage_changeAssignment.html')
security.declareProtected(perm_edit,'addPeerReviewMatching')
def addPeerReviewMatching(self, REQUEST,r_name, #with whom we are dealing with
review='', #name to add
r_add='', r_remove='', #Buttons
remove_name='' #name to remove
):
""" sort what users review what users """
if r_add or r_remove:
if not self.reviewMapping.has_key(r_name):
self.reviewMapping[r_name] = []
self.reviewMapping._p_changed = True
if r_add:
if review not in self.reviewMapping[r_name]:
self.reviewMapping[r_name].append(review)
self.reviewMapping._p_changed = True
elif r_remove and remove_name:
for x in remove_name:
try:
self.reviewMapping[r_name].remove(x)
except ValueError:
pass
self.reviewMapping._p_changed = True
else:
return self.restrictedTraverse('message_dialog_error.html')(
title="Error",
message="No buttons pressed!",
action="manage_changeAssignment.html")
if REQUEST:
return REQUEST.RESPONSE.redirect('manage_changeAssignment.html')
else:
return 1
def changeAssignmentHandler(self, REQUEST):
"""Muutuste salvestus"""
eelmine_loppaeg = self.loppaeg
eelmine_punktid = self.normPunkte
np = REQUEST.get('normPunkte', 0)
if np == '': np = 0
try:
self.normPunkte=float(re.sub(',', '.', np))
except ValueError:
self.normPunkte = float(0)
self.kirjeldus=REQUEST.kirjeldus
self.pealkiri=REQUEST.pealkiri
if hasattr(REQUEST,'hwID'):
self.setAssignmentID(REQUEST.hwID)
if hasattr(REQUEST,'juhend'):
if REQUEST.juhend == "Valimata":
self.juhend = ""
else:
self.juhend = REQUEST.juhend
else:
self.juhend = ""
if self.tyyp==0:
self.testiID=REQUEST.testiID
if self.tyyp==2:
self.grupid=[]
if hasattr(REQUEST, 'grupid'):
if type(REQUEST.grupid) is types.StringType:
self.grupid.append(REQUEST.grupid)
else:
for x in REQUEST.grupid:
self.grupid.append(x)
subgfolder = self.aq_parent.subgroups
for x in self.grupid:
tmp=getattr(subgfolder, x) #self.aq_parent.leiaGrupiKaust(x)
if not hasattr(tmp.aq_self, 'portfolio'):
tmp.add_portfolio()
#tmp.aq_self.add_portfolio()
folder_name = 'portfolio' #XXX: translate me!
tmp.portfolio.setObjPermission(20)
tmp.portfolio.setSubgroupName(x)
else:
portfolio = getattr(tmp.aq_self, 'portfolio')
if portfolio.meta_type != 'Portfolio':
portfolio.id = 'o_portfolio'
tmp._setObject(portfolio.id, portfolio)
tmp._delObject('portfolio')
tmp.add_portfolio()
tmp.portfolio.setObjPermission(20)
tmp.portfolio.setSubgroupName(x)
grupikaust = getattr(tmp, 'portfolio')
if not hasattr(grupikaust.aq_self, self.id):
ap = grupikaust.createAssignmentProxy(self.get_id(), self.get_course_id_from_req(REQUEST))
#ap.seaEsitluskaust(1)
#getattr(grupikaust.aq_self, self.id).set_name(self.pealkiri)
ap.setObjPermission(20)
ap.setSubgroupName(x)
if self.tyyp==5: #moistekaart
if self.wmID:
m=getattr(self.find_course().wordmaps, self.wmID)
m.homeworkstatus=0
self.wmID=REQUEST.get('wmid')
try:
m=getattr(self.find_course().wordmaps, self.wmID)
except:
return self.restrictedTraverse('message_dialog_error.html')(
self, REQUEST,
title='Error',
message='Map not found',
action=self.absolute_url()
)
if len(m.getChildMaps())>=0:
m=m.makeCopy()
m.id="map"+str(self.find_course().wordmaps.getNewWordMapNr())
self.find_course().wordmaps._setObject(m.id, m)
self.wmID=m.id
m.homeworkstatus=-1
if hasattr(REQUEST, 'kasLoppaeg'):
self.lockingFolder = 1
else:
self.lockingFolder = 0
# there is always a deadline, we just don't show it.
try:
self.loppaeg=mkTime(REQUEST, 'loppaeg')
except:
pass
#else:
# self.loppaeg=None
self.absolute_url()
try:
if self.grupid == []:
REQUEST.RESPONSE.redirect('../manage_assignments.html')
return
except:
pass
if self.tyyp == 2:
target = self.grupid
else:
target = []
target.append('blaah')
j = -1
wiga = 1
uusID = []
s_end = time.time()
for sisegr in target:
j = j+1
if self.tyyp == 2:
syndmused = getattr(self.subgroups, sisegr).syndmused
else:
syndmused = getattr(self, 'syndmused')
s_nimi = translate(self,"Task").encode('utf-8')
s_nimi += ": "+str(self.pealkiri)
kt_tyyp = self.getTypes(REQUEST)[self.tyyp]
if self.tyyp != 0 and self.tyyp != 3:
s_status = 1
else:
s_status = 0
try:
len(self.kalendriID)
except TypeError:
self.kalendriID = ''
if len(self.kalendriID):
try:
if self.tyyp != 2:
too_syndmus = getattr(syndmused, self.kalendriID)
else:
too_syndmus = getattr(syndmused, self.kalendriID[j])
except:
pass
try:
syndmused._delObject(too_syndmus.id)
wiga = 1
except:
pass
if wiga:
s_end = time.time()
#if hasattr(REQUEST, 'kasLoppaeg'):
try:
s_end = mkTime(REQUEST, 'loppaeg')
except:
pass
s_pikk = translate(self,'New task added').encode('utf-8')
s_pikk += "
"+self.kirjeldus+"
"
s_pikk += translate(self,'Points:').encode('utf-8')+" "+str(self.normPunkte)
if self.tyyp != 0 and self.tyyp != 3:
s_pikk += "
"+translate(self,'Deadline:').encode('utf-8')
s_pikk += time.strftime(translate(self,'timestamp_format',default="%H:%M %Y-%m-%d"), time.localtime(s_end))
s_algus = s_end
#print "adding event::", s_nimi, "pikk:", s_pikk, "status:", s_status
#print "adding event::", type(s_nimi), "pikk:", type(s_pikk), "status:", s_status
#s_nimi = s_nimi.encode('utf-8')
#s_pikk = s_pikk.encode('utf-8')
too_syndmus = syndmused.lisaSyndmusKohe(s_nimi,s_pikk,s_algus,s_end,'',self.juhend,s_status)
too_syndmus.setIconTag('images/kodune.gif')
if self.tyyp == 2:
uusID.append(too_syndmus.id)
else:
uusID = too_syndmus.id
#self.kalendriID = too_syndmus.id
self.kalendriID = uusID
REQUEST.RESPONSE.redirect('../manage_assignments.html')
def assignmentGrading_handler(self, REQUEST, save, user, points, comment):
""" assignmentGrading form handler. Saves user grade """
try:
points = float(points)
except:
return self.restrictedTraverse('message_dialog.html')(message='Invalid value for points. Must be numerical!')
if self.tyyp!=2:
self.setUserPoints(self.get_id(), user, 'points', points)
self.setUserPoints(self.get_id(), user, 'comment', comment)
if self.tyyp==2:
self.setUserPoints(self.get_id(), 'subgroup_'+user, 'points', points)
self.setUserPoints(self.get_id(), 'subgroup_'+user, 'comment', comment)
subgroups = getattr(self, 'subgroups', None)
if subgroups is None:
# TODO: problem - no subgroups around
pass
subgroup = subgroups.getSubgroupByName(user)
for x in subgroup.subgroup_members():
self.setUserPoints(self.get_id(), x, 'points', points)
self.setUserPoints(self.get_id(), x, 'comment', comment)
return REQUEST.RESPONSE.redirect(self.absolute_url()+'/manage_gradeAssignment')
Globals.InitializeClass(Kodutoo)
class KodutoodeKataloog(
OFS.Folder.Folder,
OFS.SimpleItem.Item,
Persistent,
AccessControl.Role.RoleManager,
Traversable,
Cruft
):
""" Assignments folder """
meta_type = "Assignments folder"
security = ClassSecurityInfo()
security.declareObjectPublic()
implements(IAssignmentsContainer)
# XXX:Migration needed for Persistent mapping
def __init__(self):
""" self.grades mapping example:
grades = {
'too1':{
'Vahur':{
'points':'55.3',
'comment':'comment box',
'teacherComment':'teacher comment box'
}
}
'Overall':{
'Vahur':{
'TotPoints':'90.1',
'TotPointsCom':'Total points comment',
'grade':'F',
'gradeCom':'Total grade comment',
'OverallComment':'Overall comment'
}
}
}
"""
self.grades = PersistentMapping()
self.grades['Overall'] = {}
self.grades = self.grades
self.hindeTahised=["A", "B", "C", "D", "E", "F"]
self.hindePiirid=[91, 81, 71, 61, 51]
self.tooNr=0
def getTypes(self, REQUEST):
"Assignment types"
t = [translate(self, 'Quiz'), translate(self, 'Product'), translate(self, 'Groupwork'), translate(self,'Presentation'), translate(self,'Peer review'), translate(self,'Concept map')]
return t
security.declareProtected(perm_edit,'kysiCSV')
def kysiCSV(self, REQUEST):
""" return users grades in a csv file """
REQUEST.RESPONSE.setHeader('Content-disposition','attachment; filename=grades.csv')
users = self.get_all_users()
t = "name;"
for x in self.listAssignments():
t += x.getTitle()+';'
t += "total;grade;\n"
for x in users:
t+= self.firstAndLastNG(x.get_uname())+";"
totPoints = 0
for too in self.listAssignments():
point = too.getGrades(x.get_uname(),'points')
t+=str(point)+";"
try:
totPoints += point
except TypeError:
pass
tp = self.getOverallDetails(x.get_uname(),'TotPoints')
if not tp:
tp = totPoints
tg = self.getOverallDetails(x.get_uname(),'grade')
if not tg:
tg = self.punktideleVastavHinne(None, tp)
t+= str(tp)+';';
t+= str(tg)+";\n"
return t
security.declareProtected(perm_view, 'getOverallDetails')
def getOverallDetails(self, user, detail=''):
""" return things """
gradesObj = getattr(self, 'grades')
try:
return gradesObj['Overall'][user][detail]
except KeyError:
return
security.declareProtected(perm_edit,'lisaKodutoo')
def lisaKodutoo(self, tyyp,preferred_id=''):
"Luuakse vastava tüübiga kodutöö ja väljastatakse selle eksemplar"
self.tooNr=self.tooNr+1
if preferred_id:
id = preferred_id
else:
id='too'+str(self.tooNr)
while hasattr(self,id):
self.tooNr = self.tooNr+1
id = 'too'+str(self.tooNr)
too=Kodutoo(tyyp)
too.id = id
self._setObject(too.id, too)
return too
security.declareProtected(perm_edit, 'addAssignment_handler')
def addAssignment_handler(self, REQUEST):
"Andmete salvestus"
too=self.lisaKodutoo(REQUEST.get('tyyp'))
if too.tyyp==2:
too.grupid = []
REQUEST.RESPONSE.redirect(self.absolute_url()+'/'+too.id+'/manage_changeAssignment.html')
security.declareProtected(perm_edit, 'manageAssignFormHandler')
def manageAssignFormHandler(self, REQUEST, kustuta='', salvesta='', tooID=()):
"Toiming vastavalt nupule"
if kustuta:
if type(tooID)==types.StringType:
tooID=(tooID,)
for x in tooID:
self._delObject(x)
if salvesta:
self.hindeTahised=[]
self.hindePiirid=[]
kokku=len(REQUEST.hindePiir)
veel=1
i=0
while veel and i= self.hindePiirid[i]:
return self.hindeTahised[i]
i+=1
if i