[100] | 1 | from decimal import Decimal
|
---|
| 2 | import json
|
---|
| 3 | from pathlib import Path
|
---|
| 4 | from typing import Dict
|
---|
| 5 | import unittest
|
---|
| 6 |
|
---|
| 7 | from pyson.ObjectMapper import ObjectMapper
|
---|
| 8 | # from unitpy.GeneralTests import GeneralTests
|
---|
| 9 |
|
---|
| 10 | from geniusweb.issuevalue.Bid import Bid
|
---|
| 11 | from geniusweb.issuevalue.DiscreteValue import DiscreteValue
|
---|
| 12 | from geniusweb.issuevalue.DiscreteValueSet import DiscreteValueSet
|
---|
| 13 | from geniusweb.issuevalue.Domain import Domain
|
---|
| 14 | from geniusweb.issuevalue.NumberValue import NumberValue
|
---|
| 15 | from geniusweb.issuevalue.ValueSet import ValueSet
|
---|
| 16 | from geniusweb.profile.Profile import Profile
|
---|
| 17 | from geniusweb.profile.utilityspace.DiscreteValueSetUtilities import DiscreteValueSetUtilities
|
---|
| 18 | from geniusweb.profile.utilityspace.LinearAdditiveUtilitySpace import LinearAdditiveUtilitySpace
|
---|
| 19 | from geniusweb.profile.utilityspace.ValueSetUtilities import ValueSetUtilities
|
---|
| 20 |
|
---|
| 21 |
|
---|
| 22 | class LinearAdditiveTest(unittest.TestCase): #, GeneralTests[LinearAdditiveUtilitySpace]):
|
---|
| 23 | pyson=ObjectMapper()
|
---|
| 24 |
|
---|
| 25 | yesno = DiscreteValueSet([DiscreteValue("yes"), DiscreteValue("no")])
|
---|
| 26 | leasecarvals = yesno
|
---|
| 27 | permcontractvals = yesno
|
---|
| 28 | carreervals=DiscreteValueSet([DiscreteValue("low"),DiscreteValue("medium"),DiscreteValue("high")])
|
---|
| 29 | ftevals=DiscreteValueSet([DiscreteValue("0.6"),DiscreteValue("0.8"),DiscreteValue("1.0")])
|
---|
| 30 | salaryvals=DiscreteValueSet([DiscreteValue("2000"),DiscreteValue("2500"),
|
---|
| 31 | DiscreteValue("3000"),DiscreteValue("3500"),DiscreteValue("4000")])
|
---|
| 32 | workfromhomevals=DiscreteValueSet([DiscreteValue("0"),DiscreteValue("1"),DiscreteValue("2")])
|
---|
| 33 |
|
---|
| 34 | jobsdomain = Domain("jobs", {"lease car":leasecarvals, "permanent contract":permcontractvals, \
|
---|
| 35 | "career development opportunities":carreervals, "fte":ftevals, \
|
---|
| 36 | "salary": salaryvals, "work from home": workfromhomevals })
|
---|
| 37 | N0=Decimal("0")
|
---|
| 38 | N1=Decimal("1")
|
---|
| 39 | N025=Decimal("0.25")
|
---|
| 40 | N03=Decimal("0.3")
|
---|
| 41 | N05=Decimal("0.5")
|
---|
| 42 | N075=Decimal("0.75")
|
---|
| 43 |
|
---|
| 44 | leasevarutils = DiscreteValueSetUtilities({DiscreteValue("no"):N0, DiscreteValue("yes"):N1})
|
---|
| 45 | leasevarutils2 = DiscreteValueSetUtilities({DiscreteValue("no"):N0, DiscreteValue("yes"):N075})
|
---|
| 46 | permcontrutils = DiscreteValueSetUtilities({DiscreteValue("no"):N0, DiscreteValue("yes"):N1})
|
---|
| 47 | carreerutils = DiscreteValueSetUtilities({DiscreteValue("high"):N1,DiscreteValue("low"):N0,DiscreteValue("medium"):N05})
|
---|
| 48 | fteutils = DiscreteValueSetUtilities({DiscreteValue("1.0"):N075,DiscreteValue("0.6"):N025,DiscreteValue("0.8"):N05})
|
---|
| 49 | salaryutils = DiscreteValueSetUtilities({DiscreteValue("4000"):N1,DiscreteValue("2500"):N025,DiscreteValue("3500"):N075,
|
---|
| 50 | DiscreteValue("2000"):N0,DiscreteValue("3000"):N03})
|
---|
| 51 | fromhomeutils = DiscreteValueSetUtilities({DiscreteValue("1"):N05,DiscreteValue("2"):Decimal("0.666666666666"),
|
---|
| 52 | DiscreteValue("0"):Decimal("0.333333333")})
|
---|
| 53 | utils:Dict[str, ValueSetUtilities] = {'lease car': leasevarutils, 'permanent contract':permcontrutils, 'career development opportunities': carreerutils,
|
---|
| 54 | 'fte':fteutils, 'salary':salaryutils, 'work from home':fromhomeutils }
|
---|
| 55 | utils2:Dict[str, ValueSetUtilities] = {'lease car': leasevarutils2, 'permanent contract':permcontrutils, 'career development opportunities': carreerutils,
|
---|
| 56 | 'fte':fteutils, 'salary':salaryutils, 'work from home':fromhomeutils }
|
---|
| 57 | weights:Dict[str, Decimal] = {"lease car":Decimal("0.06"),"permanent contract":Decimal("0.16"),
|
---|
| 58 | "career development opportunities":Decimal("0.04"),"fte":Decimal("0.32"),
|
---|
| 59 | "salary":Decimal("0.24"),"work from home":Decimal("0.18")}
|
---|
| 60 | weights2:Dict[str, Decimal] = {"lease car":Decimal("0.06"),"permanent contract":Decimal("0.16"),
|
---|
| 61 | "career development opportunities":Decimal("0.05"),"fte":Decimal("0.31"),
|
---|
| 62 | "salary":Decimal("0.24"),"work from home":Decimal("0.18")}
|
---|
| 63 |
|
---|
| 64 | jobs=LinearAdditiveUtilitySpace(jobsdomain, "jobs1",utils, weights )
|
---|
| 65 | jobs1=LinearAdditiveUtilitySpace(jobsdomain, "jobs1",utils, weights )
|
---|
| 66 | jobs2=LinearAdditiveUtilitySpace(jobsdomain, "jobs2",utils, weights )
|
---|
| 67 | jobs3=LinearAdditiveUtilitySpace(jobsdomain, "jobs",utils2, weights )
|
---|
| 68 | jobs4=LinearAdditiveUtilitySpace(jobsdomain, "jobs",utils, weights2 )
|
---|
| 69 |
|
---|
| 70 | # A real jobs profile from profiles server
|
---|
| 71 | # "reservationBid":null was added because toJson will also add this (even though it's the default value)
|
---|
| 72 | jobsstr = '{"LinearAdditiveUtilitySpace":{"issueUtilities":{' \
|
---|
| 73 | + '"lease car":{"DiscreteValueSetUtilities":{"valueUtilities":{"no":0,"yes":1}}},' \
|
---|
| 74 | + '"permanent contract":{"DiscreteValueSetUtilities":{"valueUtilities":{"no":0,"yes":1}}},' \
|
---|
| 75 | + '"career development opportunities":{"DiscreteValueSetUtilities":{"valueUtilities":{"high":1,"low":0,"medium":0.5}}},' \
|
---|
| 76 | + '"fte":{"DiscreteValueSetUtilities":{"valueUtilities":{"1.0":0.75,"0.6":0.25,"0.8":0.5}}},' \
|
---|
| 77 | + '"salary":{"DiscreteValueSetUtilities":{"valueUtilities":{"4000":1,"2500":0.25,"3500":0.75,"2000":0,"3000":0.3}}},' \
|
---|
| 78 | + '"work from home":{"DiscreteValueSetUtilities":{"valueUtilities":{"1":0.5,"2":0.666666666666,"0":0.333333333}}}},' \
|
---|
| 79 | + '"issueWeights":{"lease car":0.06,"permanent contract":0.16,' \
|
---|
| 80 | + '"career development opportunities":0.04,"fte":0.32,"salary":0.24,"work from home":0.18},' \
|
---|
| 81 | + '"domain":{"name":"jobs","issuesValues":{"lease car":{"values":["yes","no"]},"permanent contract":{"values":["yes","no"]},"career development opportunities":{"values":["low","medium","high"]},"fte":{"values":["0.6","0.8","1.0"]},"salary":{"values":["2000","2500","3000","3500","4000"]},"work from home":{"values":["0","1","2"]}}},'\
|
---|
| 82 | + '"name":"jobs1", "reservationBid":null}}'
|
---|
| 83 |
|
---|
| 84 | jobsjson = json.loads(jobsstr)
|
---|
| 85 |
|
---|
| 86 | partystr='{"LinearAdditiveUtilitySpace":{"domain":{"name":"party","issuesValues":{"Invitations":{"values":["Plain","Photo","Custom, Handmade","Custom, Printed"]},"Music":{"values":["MP3","DJ","Band"]},"Drinks":{"values":["Non-Alcoholic","Beer Only","Handmade Cocktails","Catering"]},"Cleanup":{"values":["Water and Soap","Specialized Materials","Special Equiment","Hired Help"]},"Food":{"values":["Chips and Nuts","Finger-Food","Handmade Food","Catering"]},"Location":{"values":["Party Tent","Your Dorm","Party Room","Ballroom"]}}},"name":"party1","issueUtilities":{"Invitations":{"DiscreteValueSetUtilities":{"valueUtilities":{"Plain":0.24,"Custom, Printed":0.48,"Photo":0.74,"Custom, Handmade":1}}},"Music":{"DiscreteValueSetUtilities":{"valueUtilities":{"DJ":0.99,"Band":0.35,"MP3":0.65}}},"Drinks":{"DiscreteValueSetUtilities":{"valueUtilities":{"Handmade Cocktails":0.672,"Non-Alcoholic":0.263,"Beer Only":0.98,"Catering":0.334}}},"Cleanup":{"DiscreteValueSetUtilities":{"valueUtilities":{"Special Equiment":0.32,"Water and Soap":0.65,"Specialized Materials":0.96,"Hired Help":0.31}}},"Food":{"DiscreteValueSetUtilities":{"valueUtilities":{"Finger-Food":0.52,"Handmade Food":0.51,"Catering":0.25,"Chips and Nuts":0.75}}},"Location":{"DiscreteValueSetUtilities":{"valueUtilities":{"Your Dorm":0.23,"Ballroom":0.77,"Party Room":0.99,"Party Tent":0.52}}}},"issueWeights":{"Invitations":0.05,"Music":0.19,"Drinks":0.28,"Cleanup":0.10,"Food":0.19,"Location":0.19},"reservationBid":null}}'
|
---|
| 87 | partyjson = json.loads(partystr)
|
---|
| 88 |
|
---|
| 89 | def testSerialize(self):
|
---|
| 90 | print(self.pyson.toJson(self.jobs))
|
---|
| 91 | self.maxDiff=None
|
---|
| 92 | self.assertEqual(self.jobsjson, self.pyson.toJson(self.jobs))
|
---|
| 93 |
|
---|
| 94 | def testDeserialize(self):
|
---|
| 95 | self.assertEqual(self.jobs, self.pyson.parse(self.jobsjson, Profile))
|
---|
| 96 |
|
---|
| 97 | def testDeserializeJapanTrip(self):
|
---|
| 98 | serialized = Path("test/resources/japantrip1.json").read_text("utf-8")
|
---|
| 99 | jsonobj=json.loads(serialized)
|
---|
| 100 | profile:Profile = self.pyson.parse(jsonobj, Profile);
|
---|
| 101 | self.assertEquals("japantrip1", profile.getName())
|
---|
| 102 | self.assertEquals("japantrip", profile.getDomain().getName())
|
---|
| 103 |
|
---|
| 104 | def testDeserializeParty1(self):
|
---|
| 105 | print( str(self.pyson.parse(self.partyjson, Profile)))
|
---|
| 106 |
|
---|
| 107 | def testEquals(self):
|
---|
| 108 | self.assertEqual(self.jobs,self.jobs1)
|
---|
| 109 | self.assertEqual(hash(self.jobs),hash(self.jobs1))
|
---|
| 110 | self.assertNotEqual(self.jobs,self.jobs2)
|
---|
| 111 | self.assertNotEqual(hash(self.jobs),hash(self.jobs2))
|
---|
| 112 | self.assertNotEqual(self.jobs,self.jobs3)
|
---|
| 113 | self.assertNotEqual(hash(self.jobs),hash(self.jobs3))
|
---|
| 114 | self.assertNotEqual(self.jobs,self.jobs4)
|
---|
| 115 | self.assertNotEqual(hash(self.jobs),hash(self.jobs4))
|
---|
| 116 |
|
---|
| 117 |
|
---|
| 118 | def testUtil(self):
|
---|
| 119 | bid=Bid({"lease car":DiscreteValue("yes"), "permanent contract":DiscreteValue("yes"), \
|
---|
| 120 | "career development opportunities": DiscreteValue("low"), \
|
---|
| 121 | "fte": DiscreteValue("0.8"), "salary":DiscreteValue("2500"), "work from home": DiscreteValue("1") });
|
---|
| 122 | # 0.06 * 1 + 0.16 * 1 + 0.04 * 0 + 0.32 * 0.5 + 0.24 * 0.25 + 0.18 * 0.5
|
---|
| 123 | self.assertEqual(Decimal("0.53"), self.jobs.getUtility(bid))
|
---|