Change how roles are represented

main
Bob Mottram 2021-05-16 16:10:39 +01:00
parent 2a3ad46c05
commit 65b0a6b728
7 changed files with 292 additions and 148 deletions

View File

@ -123,6 +123,7 @@ from blocking import removeGlobalBlock
from blocking import isBlockedHashtag
from blocking import isBlockedDomain
from blocking import getDomainBlocklist
from roles import getActorRolesList
from roles import setRole
from roles import clearModeratorStatus
from roles import clearEditorStatus
@ -200,6 +201,8 @@ from shares import addShare
from shares import removeShare
from shares import expireShares
from categories import setHashtagCategory
from utils import getOccupationName
from utils import setOccupationName
from utils import loadTranslationsFromFile
from utils import getLocalNetworkAddresses
from utils import decodedHost
@ -4581,21 +4584,18 @@ class PubServer(BaseHTTPRequestHandler):
actorChanged = True
# Other accounts (alsoKnownAs)
occupationName = ""
if actorJson.get('hasOccupation'):
if actorJson['hasOccupation'].get('name'):
occupationName = actorJson['hasOccupation']['name']
occupationName = getOccupationName(actorJson)
if fields.get('occupationName'):
fields['occupationName'] = \
removeHtml(fields['occupationName'])
if occupationName != \
fields['occupationName']:
actorJson['hasOccupation']['name'] = \
fields['occupationName']
setOccupationName(actorJson,
fields['occupationName'])
actorChanged = True
else:
if occupationName:
actorJson['hasOccupation']['name'] = ''
setOccupationName(actorJson, '')
actorChanged = True
# Other accounts (alsoKnownAs)
@ -7373,7 +7373,7 @@ class PubServer(BaseHTTPRequestHandler):
if not actorJson:
return False
if actorJson.get('affiliation'):
if actorJson.get('hasOccupation'):
if self._requestHTTP():
getPerson = \
personLookup(domain, path.replace('/roles', ''),
@ -7394,11 +7394,7 @@ class PubServer(BaseHTTPRequestHandler):
if self.server.keyShortcuts.get(nickname):
accessKeys = self.server.keyShortcuts[nickname]
rolesList = []
if actorJson.get('affiliation'):
if isinstance(actorJson['affiliation']['roleName'],
list):
rolesList = actorJson['affiliation']['roleName']
rolesList = getActorRolesList(actorJson)
city = self._getSpoofedCity(baseDir, nickname, domain)
msg = \
htmlProfile(self.server.rssIconAtTop,
@ -7435,12 +7431,7 @@ class PubServer(BaseHTTPRequestHandler):
'show roles')
else:
if self._fetchAuthenticated():
rolesList = []
if actorJson.get('affiliation'):
if isinstance(actorJson['affiliation']['roleName'],
list):
rolesList = actorJson['affiliation']['roleName']
rolesList = getActorRolesList(actorJson)
msg = json.dumps(rolesList,
ensure_ascii=False)
msg = msg.encode('utf-8')

View File

@ -34,6 +34,8 @@ from posts import createModeration
from auth import storeBasicCredentials
from auth import removePassword
from roles import setRole
from roles import setRolesFromList
from roles import getActorRolesList
from media import processMetaData
from utils import getStatusNumber
from utils import getFullDomain
@ -201,10 +203,12 @@ def getDefaultPersonContext() -> str:
'toot': 'http://joinmastodon.org/ns#',
'value': 'schema:value',
'hasOccupation': 'schema:hasOccupation',
'affiliation': 'schema:affiliation',
'Occupation': 'schema:Occupation',
'OrganizationRole': 'schema:OrganizationRole',
'WebSite': 'schema:Project'
'occupationalCategory': 'schema:occupationalCategory',
'Role': 'schema:Role',
'WebSite': 'schema:Project',
'CategoryCode': 'schema:CategoryCode',
'CategoryCodeSet': 'schema:CategoryCodeSet'
}
@ -280,20 +284,13 @@ def _createPersonBase(baseDir: str, nickname: str, domain: str, port: int,
'following': personId + '/following',
'tts': personId + '/speaker',
'shares': personId + '/shares',
'hasOccupation': {
'@type': 'Occupation',
'name': "",
'skills': []
},
"affiliation": {
"@type": "OrganizationRole",
"roleName": [],
"affiliation": {
"@type": "WebSite",
"url": httpPrefix + '://' + domain
},
"startDate": published
},
'hasOccupation': [
{
'@type': 'Occupation',
'name': "",
'skills': []
}
],
'availability': None,
'icon': {
'mediaType': 'image/png',
@ -587,16 +584,13 @@ def personUpgradeActor(baseDir: str, personJson: {},
# if the older skills format is being used then switch
# to the new one
if not personJson.get('hasOccupation'):
personJson['hasOccupation'] = {
'@type': 'Occupation',
'name': occupationName,
'skills': []
}
updateActor = True
if isinstance(personJson['hasOccupation']['skills'], str):
skillsList = personJson['hasOccupation']['skills'].split(', ')
personJson['hasOccupation']['skills'] = skillsList
personJson['hasOccupation'] = [
{
'@type': 'Occupation',
'name': occupationName,
'skills': []
}
]
updateActor = True
# remove the old skills format
@ -606,36 +600,29 @@ def personUpgradeActor(baseDir: str, personJson: {},
# if the older roles format is being used then switch
# to the new one
if not personJson.get('affiliation'):
rolesList = []
adminName = getConfigParam(baseDir, 'admin')
if personJson['id'].endswith('/users/' + adminName):
rolesList = ["admin", "moderator", "editor"]
statusNumber, published = getStatusNumber()
personJson['affiliation'] = {
"@type": "OrganizationRole",
"roleName": rolesList,
"affiliation": {
"@type": "WebSite",
"url": personJson['id'].split('/users/')[0]
},
"startDate": published
}
if personJson.get('affiliation'):
del personJson['affiliation']
updateActor = True
if isinstance(personJson['affiliation']['roleName'], str):
rolesList = personJson['affiliation']['roleName'].split(', ')
personJson['affiliation']['roleName'] = rolesList
if not isinstance(personJson['hasOccupation'], list):
personJson['hasOccupation'] = [
{
'@type': 'Occupation',
'name': occupationName,
'skills': []
}
]
updateActor = True
# if no roles are defined then ensure that the admin
# roles are configured
if not personJson['affiliation']['roleName']:
rolesList = getActorRolesList(personJson)
if not rolesList:
adminName = getConfigParam(baseDir, 'admin')
if personJson['id'].endswith('/users/' + adminName):
personJson['affiliation']['roleName'] = \
["admin", "moderator", "editor"]
updateActor = True
rolesList = ["admin", "moderator", "editor"]
setRolesFromList(personJson, rolesList)
updateActor = True
# remove the old roles format
if personJson.get('roles'):

124
roles.py
View File

@ -9,6 +9,7 @@ __status__ = "Production"
import os
from utils import loadJson
from utils import saveJson
from utils import getStatusNumber
def _clearRoleStatus(baseDir: str, role: str) -> None:
@ -30,12 +31,10 @@ def _clearRoleStatus(baseDir: str, role: str) -> None:
actorJson = loadJson(filename)
if not actorJson:
continue
if not actorJson.get('affiliation'):
continue
rolesList = \
getRolesFromList(actorJson['affiliation']['roleName'])
rolesList = getActorRolesList(actorJson)
if role in rolesList:
rolesList.remove(role)
setRolesFromList(actorJson, rolesList)
saveJson(actorJson, filename)
@ -65,7 +64,8 @@ def clearModeratorStatus(baseDir: str) -> None:
def _addRole(baseDir: str, nickname: str, domain: str,
roleFilename: str) -> None:
"""Adds a role nickname to the file
"""Adds a role nickname to the file.
This is a file containing the nicknames of accounts having this role
"""
if ':' in domain:
domain = domain.split(':')[0]
@ -94,7 +94,8 @@ def _addRole(baseDir: str, nickname: str, domain: str,
def _removeRole(baseDir: str, nickname: str, roleFilename: str) -> None:
"""Removes a role nickname from the file
"""Removes a role nickname from the file.
This is a file containing the nicknames of accounts having this role
"""
roleFile = baseDir + '/accounts/' + roleFilename
if not os.path.isfile(roleFile):
@ -108,24 +109,99 @@ def _removeRole(baseDir: str, nickname: str, roleFilename: str) -> None:
f.write(roleNickname + '\n')
def _setActorRole(actorJson: {}, roleName: str) -> bool:
"""Sets a role for an actor
"""
if not actorJson.get('hasOccupation'):
return False
if not isinstance(actorJson['hasOccupation'], list):
return False
category = None
if 'admin' in roleName:
category = '15-1299.01'
elif 'moderator' in roleName:
category = '11-9199.02'
elif 'editor' in roleName:
category = '27-3041.00'
elif 'counselor' in roleName:
category = '23-1022.00'
if not category:
return False
for index in range(len(actorJson['hasOccupation'])):
occupationItem = actorJson['hasOccupation'][index]
if not isinstance(occupationItem, dict):
continue
if not occupationItem.get('@type'):
continue
if occupationItem['@type'] != 'Role':
continue
if occupationItem['hasOccupation']['name'] == roleName:
return True
statusNumber, published = getStatusNumber()
newRole = {
"@type": "Role",
"hasOccupation": {
"@type": "Occupation",
"name": roleName,
"occupationalCategory": {
"@type": "CategoryCode",
"inCodeSet": {
"@type": "CategoryCodeSet",
"name": "O*Net-SOC",
"dateModified": "2019",
"url": "https://www.onetonline.org/"
},
"codeValue": category,
"url": "https://www.onetonline.org/link/summary/" + category
}
},
"startDate": published
}
actorJson['hasOccupation'].append(newRole)
return True
def setRolesFromList(actorJson: {}, rolesList: []) -> None:
"""Sets roles from a list
"""
if actorJson.get('affiliation'):
actorJson['affiliation']['roleName'] = rolesList.copy()
# clear Roles from the occupation list
emptyRolesList = []
for occupationItem in actorJson['hasOccupation']:
if not isinstance(occupationItem, dict):
continue
if not occupationItem.get('@type'):
continue
if occupationItem['@type'] == 'Role':
continue
emptyRolesList.append(occupationItem)
actorJson['hasOccupation'] = emptyRolesList
# create the new list
for roleName in rolesList:
_setActorRole(actorJson, roleName)
def getRolesFromList(rolesList: []) -> []:
"""Returns a list of roles from a list
def getActorRolesList(actorJson: {}) -> []:
"""Gets a list of role names from an actor
"""
if isinstance(rolesList, list):
rolesList2 = rolesList
else:
rolesList2 = rolesList.split(',')
rolesResult = []
for roleName in rolesList2:
rolesResult.append(roleName.strip().lower())
return rolesResult
if not actorJson.get('hasOccupation'):
return []
if not isinstance(actorJson['hasOccupation'], list):
return []
rolesList = []
for occupationItem in actorJson['hasOccupation']:
if not isinstance(occupationItem, dict):
continue
if not occupationItem.get('@type'):
continue
if occupationItem['@type'] != 'Role':
continue
roleName = occupationItem['hasOccupation']['name']
if roleName not in rolesList:
rolesList.append(roleName)
return rolesList
def setRole(baseDir: str, nickname: str, domain: str,
@ -149,10 +225,9 @@ def setRole(baseDir: str, nickname: str, domain: str,
actorJson = loadJson(actorFilename)
if actorJson:
if not actorJson.get('affiliation'):
if not actorJson.get('hasOccupation'):
return False
rolesList = \
getRolesFromList(actorJson['affiliation']['roleName'])
rolesList = getActorRolesList(actorJson)
actorChanged = False
if role:
# add the role
@ -174,3 +249,10 @@ def setRole(baseDir: str, nickname: str, domain: str,
if actorChanged:
saveJson(actorJson, actorFilename)
return True
def actorHasRole(actorJson: {}, roleName: str) -> bool:
"""Returns true if the given actor has the given role
"""
rolesList = getActorRolesList(actorJson)
return roleName in rolesList

View File

@ -15,16 +15,18 @@ from utils import getFullDomain
from utils import getNicknameFromActor
from utils import getDomainFromActor
from utils import loadJson
from utils import getOccupationSkills
from utils import setOccupationSkillsList
def setSkillsFromDict(actorJson: {}, skillsDict: {}) -> []:
"""Converts a dict containing skills to a string
"""Converts a dict containing skills to a list
Returns the string version of the dictionary
"""
skillsList = []
for name, value in skillsDict.items():
skillsList.append(name + ':' + str(value))
actorJson['hasOccupation']['skills'] = skillsList
setOccupationSkillsList(actorJson, skillsList)
return skillsList
@ -47,21 +49,11 @@ def getSkillsFromList(skillsList: []) -> {}:
return skillsDict
def actorHasSkill(actorJson: {}, skillName: str) -> bool:
"""Returns true if the actor has the given skill
"""
skillsDict = \
getSkillsFromList(actorJson['hasOccupation']['skills'])
if not skillsDict:
return False
return skillsDict.get(skillName.lower())
def actorSkillValue(actorJson: {}, skillName: str) -> int:
"""Returns The skill level from an actor
"""
skillsDict = \
getSkillsFromList(actorJson['hasOccupation']['skills'])
ocSkillsList = getOccupationSkills(actorJson)
skillsDict = getSkillsFromList(ocSkillsList)
if not skillsDict:
return 0
skillName = skillName.lower()
@ -74,13 +66,8 @@ def noOfActorSkills(actorJson: {}) -> int:
"""Returns the number of skills that an actor has
"""
if actorJson.get('hasOccupation'):
skillsStr = actorJson['hasOccupation']['skills']
if isinstance(skillsStr, list):
skillsList = skillsStr
else:
skillsList = skillsStr.split(',')
if skillsList:
return len(skillsList)
skillsList = getOccupationSkills(actorJson)
return len(skillsList)
return 0
@ -95,13 +82,15 @@ def setActorSkillLevel(actorJson: {},
if not actorJson:
return True
if not actorJson.get('hasOccupation'):
actorJson['hasOccupation'] = {
'@type': 'Occupation',
'name': '',
'skills': ''
}
skillsDict = \
getSkillsFromList(actorJson['hasOccupation']['skills'])
actorJson['hasOccupation'] = [
{
'@type': 'Occupation',
'name': '',
'skills': []
}
]
ocSkillsList = getOccupationSkills(actorJson)
skillsDict = getSkillsFromList(ocSkillsList)
if skillLevelPercent > 0:
skillsDict[skill] = skillLevelPercent
else:
@ -138,7 +127,8 @@ def getSkills(baseDir: str, nickname: str, domain: str) -> []:
if actorJson:
if not actorJson.get('hasOccupation'):
return None
return getSkillsFromList(actorJson['hasOccupation']['skills'])
ocSkillsList = getOccupationSkills(actorJson)
return getSkillsFromList(ocSkillsList)
return None
@ -258,3 +248,13 @@ def sendSkillViaServer(baseDir: str, session, nickname: str, password: str,
print('DEBUG: c2s POST skill success')
return newSkillJson
def actorHasSkill(actorJson: {}, skillName: str) -> bool:
"""Returns true if the given actor has the given skill
"""
ocSkillsList = getOccupationSkills(actorJson)
for skillStr in ocSkillsList:
if skillName + ':' in skillStr:
return True
return False

View File

@ -67,11 +67,12 @@ from person import setDisplayNickname
from person import setBio
# from person import generateRSAKey
from skills import setSkillLevel
from skills import actorSkillValue
from skills import setSkillsFromDict
from skills import getSkillsFromList
from skills import actorHasSkill
from roles import setRolesFromList
from roles import getRolesFromList
from roles import setRole
from roles import actorHasRole
from auth import constantTimeStringCheck
from auth import createBasicAuthHeader
from auth import authorizeBasic
@ -3661,44 +3662,42 @@ def testSpoofGeolocation() -> None:
def testSkills() -> None:
print('testSkills')
actorJson = {
'hasOccupation': {
'@type': 'Occupation',
'name': "",
'skills': []
}
'hasOccupation': [
{
'@type': 'Occupation',
'name': "Sysop",
'skills': []
}
]
}
skillsDict = {
'bakery': 40,
'gardening': 70
}
setSkillsFromDict(actorJson, skillsDict)
assert actorJson['hasOccupation']['skills']
skillsDict = getSkillsFromList(actorJson['hasOccupation']['skills'])
assert skillsDict.get('bakery')
assert skillsDict.get('gardening')
assert skillsDict['bakery'] == 40
assert skillsDict['gardening'] == 70
assert actorHasSkill(actorJson, 'bakery')
assert actorHasSkill(actorJson, 'gardening')
assert actorSkillValue(actorJson, 'bakery') == 40
assert actorSkillValue(actorJson, 'gardening') == 70
def testRoles() -> None:
print('testRoles')
actorJson = {
'affiliation': {
"@type": "OrganizationRole",
"roleName": [],
"affiliation": {
"@type": "WebSite",
"url": "https://testinstance.org"
},
"startDate": "date goes here"
}
'hasOccupation': [
{
'@type': 'Occupation',
'name': "Sysop",
'skills': []
}
]
}
testRolesList = ["admin", "moderator"]
setRolesFromList(actorJson, testRolesList)
assert actorJson['affiliation']['roleName']
rolesList = getRolesFromList(actorJson['affiliation']['roleName'])
assert 'admin' in rolesList
assert 'moderator' in rolesList
assert actorHasRole(actorJson, "admin")
assert actorHasRole(actorJson, "moderator")
assert not actorHasRole(actorJson, "editor")
assert not actorHasRole(actorJson, "counselor")
def runAllTests():

View File

@ -2279,3 +2279,89 @@ def dmAllowedFromDomain(baseDir: str,
if sendingActorDomain + '\n' in open(dmAllowedInstancesFilename).read():
return True
return False
def getOccupationSkills(actorJson: {}) -> []:
"""Returns the list of skills for an actor
"""
if 'hasOccupation' not in actorJson:
return []
if not isinstance(actorJson['hasOccupation'], list):
return []
for occupationItem in actorJson['hasOccupation']:
if not isinstance(occupationItem, dict):
continue
if not occupationItem.get('@type'):
continue
if not occupationItem['@type'] == 'Occupation':
continue
if not occupationItem.get('skills'):
continue
if isinstance(occupationItem['skills'], list):
return occupationItem['skills']
elif isinstance(occupationItem['skills'], str):
return [occupationItem['skills']]
break
return []
def getOccupationName(actorJson: {}) -> str:
"""Returns the occupation name an actor
"""
if not actorJson.get('hasOccupation'):
return ""
if not isinstance(actorJson['hasOccupation'], list):
return ""
for occupationItem in actorJson['hasOccupation']:
if not isinstance(occupationItem, dict):
continue
if not occupationItem.get('@type'):
continue
if occupationItem['@type'] != 'Occupation':
continue
if not occupationItem.get('name'):
continue
if isinstance(occupationItem['name'], str):
return occupationItem['name']
break
return ""
def setOccupationName(actorJson: {}, name: str) -> bool:
"""Sets the occupation name of an actor
"""
if not actorJson.get('hasOccupation'):
return False
if not isinstance(actorJson['hasOccupation'], list):
return False
for index in range(len(actorJson['hasOccupation'])):
occupationItem = actorJson['hasOccupation'][index]
if not isinstance(occupationItem, dict):
continue
if not occupationItem.get('@type'):
continue
if occupationItem['@type'] != 'Occupation':
continue
occupationItem['name'] = name
return True
return False
def setOccupationSkillsList(actorJson: {}, skillsList: []) -> bool:
"""Sets the occupation skills for an actor
"""
if 'hasOccupation' not in actorJson:
return False
if not isinstance(actorJson['hasOccupation'], list):
return False
for index in range(len(actorJson['hasOccupation'])):
occupationItem = actorJson['hasOccupation'][index]
if not isinstance(occupationItem, dict):
continue
if not occupationItem.get('@type'):
continue
if occupationItem['@type'] != 'Occupation':
continue
occupationItem['skills'] = skillsList
return True
return False

View File

@ -8,6 +8,7 @@ __status__ = "Production"
import os
from pprint import pprint
from utils import getOccupationName
from utils import getLockedAccount
from utils import hasUsersPath
from utils import getFullDomain
@ -741,8 +742,7 @@ def htmlProfile(rssIconAtTop: bool,
joinedDate = profileJson['published']
occupationName = None
if profileJson.get('hasOccupation'):
if profileJson['hasOccupation'].get('name'):
occupationName = profileJson['hasOccupation']['name']
occupationName = getOccupationName(profileJson)
avatarUrl = profileJson['icon']['url']
@ -1602,8 +1602,7 @@ def htmlEditProfile(cssCache: {}, translate: {}, baseDir: str, path: str,
occupationName = ''
if actorJson.get('hasOccupation'):
if actorJson['hasOccupation'].get('name'):
occupationName = actorJson['hasOccupation']['name']
occupationName = getOccupationName(actorJson)
editProfileForm += '<label class="labels">' + \
translate['Occupation'] + ':</label><br>\n'