Refactor, include readonly prototypes

This commit is contained in:
Griatch 2018-03-03 14:57:55 +01:00
parent d7deed0b51
commit 3e7dae6a8c

View file

@ -6,13 +6,41 @@ available in a repository for buildiers to use. Each prototype is stored
in a Script so that it can be tagged for quick sorting/finding and locked for limiting
access.
This system also takes into consideration prototypes defined and stored in modules.
Such prototypes are considered 'read-only' to the system and can only be modified
in code. To replace a default prototype, add the same-name prototype in a
custom module read later in the settings.PROTOTYPE_MODULES list. To remove a default
prototype, override its name with an empty dict.
"""
from django.conf import settings
from evennia.scripts.scripts import DefaultScript
from evennia.utils.create import create_script
from evennia.utils.utils import make_iter
from evennia.utils.utils import make_iter, all_from_module
from evennia.utils.evtable import EvTable
# prepare the available prototypes defined in modules
_READONLY_PROTOTYPES = {}
_READONLY_PROTOTYPE_MODULES = {}
for mod in settings.PROTOTYPE_MODULES:
# to remove a default prototype, override it with an empty dict.
# internally we store as (key, desc, locks, tags, prototype_dict)
prots = [(key, prot) for key, prot in all_from_module(mod).items()
if prot and isinstance(prot, dict)]
_READONLY_PROTOTYPES.update(
{key.lower():
(key.lower(),
prot['prototype_desc'] if 'prototype_desc' in prot else mod,
prot['prototype_lock'] if 'prototype_lock' in prot else "use:all()",
set(make_iter(
prot['prototype_tags']) if 'prototype_tags' in prot else ["base-prototype"]),
prot)
for key, prot in prots})
_READONLY_PROTOTYPE_MODULES.update({tup[0]: mod for tup in prots})
class PersistentPrototype(DefaultScript):
"""
@ -46,17 +74,25 @@ def store_prototype(caller, key, prototype, desc="", tags=None, locks="", delete
Raises:
PermissionError: If edit lock was not passed by caller.
"""
key_orig = key
key = key.lower()
locks = locks if locks else "use:all();edit:id({}) or edit:perm(Admin)".format(caller.id)
tags = [(tag, "persistent_prototype") for tag in make_iter(tags)]
if key in _READONLY_PROTOTYPES:
mod = _READONLY_PROTOTYPE_MODULES.get(key, "N/A")
raise PermissionError("{} is a read-only prototype "
"(defined as code in {}).".format(key_orig, mod))
stored_prototype = PersistentPrototype.objects.filter(db_key=key)
if stored_prototype:
stored_prototype = stored_prototype[0]
if not stored_prototype.access(caller, 'edit'):
PermissionError("{} does not have permission to edit prototype {}".format(caller, key))
raise PermissionError("{} does not have permission to "
"edit prototype {}".format(caller, key))
if delete:
stored_prototype.delete()
@ -77,9 +113,9 @@ def store_prototype(caller, key, prototype, desc="", tags=None, locks="", delete
return stored_prototype
def search_prototype(key=None, tags=None):
def search_persistent_prototype(key=None, tags=None):
"""
Find prototypes based on key and/or tags.
Find persistent (database-stored) prototypes based on key and/or tags.
Kwargs:
key (str): An exact or partial key to query for.
@ -87,13 +123,10 @@ def search_prototype(key=None, tags=None):
will always be applied with the 'persistent_protototype'
tag category.
Return:
matches (queryset): All found PersistentPrototypes. This will
be all prototypes if no arguments are given.
matches (queryset): All found PersistentPrototypes
Note:
This will use the tags to make a subselection before attempting
to match on the key. So if key/tags don't match up nothing will
be found.
This will not include read-only prototypes defined in modules.
"""
if tags:
@ -109,6 +142,68 @@ def search_prototype(key=None, tags=None):
return matches
def search_readonly_prototype(key=None, tags=None):
"""
Find read-only prototypes, defined in modules.
Kwargs:
key (str): An exact or partial key to query for.
tags (str or list): Tag key to query for.
Return:
matches (list): List of prototype tuples that includes
prototype metadata, on the form
`(key, desc, lockstring, taglist, prototypedict)`
"""
matches = []
if tags:
# use tags to limit selection
tagset = set(tags)
matches = {key: tup for key, tup in _READONLY_PROTOTYPES.items()
if tagset.intersection(tup[3])}
else:
matches = _READONLY_PROTOTYPES
if key:
if key in matches:
# exact match
return matches[key]
else:
# fuzzy matching
return [tup for pkey, tup in matches.items() if key in pkey]
return matches
def search_prototype(key=None, tags=None):
"""
Find prototypes based on key and/or tags.
Kwargs:
key (str): An exact or partial key to query for.
tags (str or list): Tag key or keys to query for. These
will always be applied with the 'persistent_protototype'
tag category.
Return:
matches (list): All found prototype dicts.
Note:
The available prototypes is a combination of those supplied in
PROTOTYPE_MODULES and those stored from in-game. For the latter,
this will use the tags to make a subselection before attempting
to match on the key. So if key/tags don't match up nothing will
be found.
"""
matches = []
if key and key in _READONLY_PROTOTYPES:
matches.append(_READONLY_PROTOTYPES[key][3])
else:
matches.extend([prot.attributes.get("prototype")
for prot in search_persistent_prototype(key, tags)])
return matches
def get_prototype_list(caller, key=None, tags=None, show_non_use=False, show_non_edit=True):
"""
Collate a list of found prototypes based on search criteria and access.
@ -124,20 +219,39 @@ def get_prototype_list(caller, key=None, tags=None, show_non_use=False, show_non
if no prototypes were found.
"""
prototypes = search_prototype(key, tags)
# handle read-only prototypes separately
if key and key in _READONLY_PROTOTYPES:
readonly_prototypes = _READONLY_PROTOTYPES[key]
else:
readonly_prototypes = _READONLY_PROTOTYPES.values()
# get use-permissions of readonly attributes (edit is always False)
readonly_prototypes = [
(tup[0],
tup[1],
("{}/N".format('Y'
if caller.locks.check_lockstring(caller, tup[2], access_type='use') else 'N')),
",".join(tup[3])) for tup in readonly_prototypes]
# next, handle db-stored prototypes
prototypes = search_persistent_prototype(key, tags)
if not prototypes:
return None
# gather access permissions as (key, desc, can_use, can_edit)
# gather access permissions as (key, desc, tags, can_use, can_edit)
prototypes = [(prototype.key, prototype.desc,
prototype.access(caller, "use"), prototype.access(caller, "edit"))
"{}/{}".format('Y' if prototype.access(caller, "use") else 'N',
'Y' if prototype.access(caller, "edit") else 'N'),
",".join(prototype.tags.get(category="persistent_prototype")))
for prototype in prototypes]
prototypes = prototypes + readonly_prototypes
if not show_non_use:
prototypes = [tup for tup in prototypes if tup[2]]
prototypes = [tup for tup in sorted(prototypes, key=lambda o: o[0]) if tup[2]]
if not show_non_edit:
prototypes = [tup for tup in prototypes if tup[3]]
prototypes = [tup for tup in sorted(prototypes, key=lambda o: o[0]) if tup[3]]
if not prototypes:
return None
@ -145,9 +259,9 @@ def get_prototype_list(caller, key=None, tags=None, show_non_use=False, show_non
table = []
for i in range(len(prototypes[0])):
table.append([str(tup[i]) for tup in prototypes])
table = EvTable("Key", "Desc", "Use", "Edit", table=table, crop=True, width=78)
table = EvTable("Key", "Desc", "Use/Edit", "Tags", table=table, crop=True, width=78)
table.reformat_column(0, width=28)
table.reformat_column(1, width=40)
table.reformat_column(2, width=5)
table.reformat_column(3, width=5)
table.reformat_column(2, width=11, align='r')
table.reformat_column(3, width=20)
return table