Implemented Branch/Rank objects in ranks.py

This commit is contained in:
filmfreedom-org 2021-06-02 03:06:45 -05:00
parent 6e056fcef7
commit 6ff5b6b4f5
7 changed files with 1126 additions and 10 deletions

View File

@ -41,7 +41,6 @@ from . import copy_anim
from abx import ink_paint
from . import render_profile
#configfile = os.path.join(os.path.dirname(__file__), 'config.yaml')
#print("Configuration file path: ", os.path.abspath(configfile))
@ -223,6 +222,17 @@ class LunaticsSceneProperties(bpy.types.PropertyGroup):
NOTE: due to be replaced by 'ProjectProperties', using the schema data
retrieved by file_context.
"""
name_context_id = bpy.props.StringProperty(options={'HIDDEN', 'LIBRARY_EDITABLE'})
@property
def name_context(self):
if self.name_context_id in BlendFile.name_contexts:
return BlendFile.name_contexts[self.name_context_id]
else:
name_context = BlendFile.new_name_context()
self.name_context_id = str(id(name_context))
return name_context
series_id = bpy.props.EnumProperty(
items=[
('S1', 'S1', 'Series One'),

View File

@ -1344,7 +1344,7 @@ class FileContext(NameContext):
# Containers
#self.notes = []
self.name_contexts = []
self.name_contexts = {}
# Status / Settings
self.filepath = None
@ -1636,8 +1636,11 @@ class FileContext(NameContext):
namepath_segment = []
ranks = [s.rank for s in self.schemas]
i_rank = len(self.namepath)
old_rank = ranks[i_rank -1]
i_rank = len(self.namepath)
if i_rank == 0:
old_rank = None
else:
old_rank = ranks[i_rank -1]
# The new rank will be the highest rank mentioned, or the
# explicitly requested rank or
@ -1655,17 +1658,24 @@ class FileContext(NameContext):
if ranks.index(schema.rank) <= ranks.index(rank):
new_rank = schema.rank
delta_rank = ranks.index(new_rank) - ranks.index(old_rank)
if old_rank:
delta_rank = ranks.index(new_rank) - ranks.index(old_rank)
else:
# I think in this case, it's as if the old_rank number is -1?
delta_rank = ranks.index(new_rank) + 1
# Truncate to the new rank:
namepath_segment = namepath_segment[:delta_rank]
fields['rank'] = new_rank
fields['code'] = namepath_segment[-1]
self.name_contexts.append(NameContext(self, fields,
namepath_segment=namepath_segment))
return self.name_contexts[-1]
name_context = NameContext(self, fields,
namepath_segment=namepath_segment)
self.name_contexts[str(id(name_context))] = name_context
return name_context

247
abx/ranks.py Normal file
View File

@ -0,0 +1,247 @@
# ranks.py
"""
Branches and ranks objects.
Objects for representing the ranks of a hierarchy, with the
possibility of branching at nodes with redefined ranks via
the 'project_schema' directives in project YAML files.
"""
import numbers
class Branch(object):
"""
Branch represents a ranking system in the tree of schemas.
It takes the name of the project_unit where the ranking system
is overridden, and controls how all ranks defined within it
are interpreted.
"""
def __init__(self, parent, code, start, ranks):
self.parent = parent
self.code = str(code)
self.start = int(start)
self._ranks = RankList(self,[])
if parent:
for rank in parent.ranks:
if int(rank) < start:
self._ranks.append(rank)
for num, name in enumerate(ranks):
rank = Rank(self, num + start, name)
self._ranks.append(rank)
def __repr__(self):
ranklist = ', '.join([str(r) for r in self.ranks[1:]])
if self.code:
code = self.code
else:
code = 'trunk'
return "<branch '%s': %s>" % (code, ranklist)
def __contains__(self, other):
if isinstance(other, Rank) and other in self._ranks:
return True
else:
return False
def rank(self, n):
"""
Coerce int or string to rank, if it matches a rank in this Branch.
"""
if isinstance(n, int) and 0 < n < len(self._ranks):
return self._ranks[n]
elif isinstance(n, str):
for rank in self._ranks:
if str(rank) == n:
return rank
else:
raise TypeError
@property
def ranks(self):
# Read-only property.
return self._ranks
class Rank(object):
"""
Ranks are named numbers indicating the position in a hierarchy.
They can be incremented and decremented. The value 0 represents the top
rank, with increasing numbers indicating finer grades in taxonomic rank.
They can have integers added to or subtracted from them, meaning to go
down or up in rank. They cannot be added to each other, though. Note that
higher and lower rank in the real world since of more or less scope is
inverted as a number -- the lower rank has a higher number.
There are upper and lower bounds to rank, defined by the schema 'Branch'.
Coercing a rank to an integer (int()) returns the numeric rank.
Coercing a rank to a string (str()) returns the rank name.
The representation includes the branch name and rank name.
Ranks can be subtracted, returning an integer representing the difference
in rank.
"""
def __init__(self, branch, num, name):
self.num = num
self.name = name
self.branch = branch
def __index__(self):
return self.num
def __int__(self):
return self.num
def __str__(self):
return self.name
def __repr__(self):
return '<%s:%d-%s>' % (self.branch.code, self.num, self.name)
def __hash__(self):
return hash((self.branch, self.num, self.name))
def __eq__(self, other):
if isinstance(other, Rank):
if hash(self) == hash(other):
return True
else:
return False
elif isinstance(other, str):
if other == self.name:
return True
else:
return False
elif isinstance(other, int):
if other == self.num:
return True
else:
return False
else:
return False
def __gt__(self, other):
if isinstance(other, Rank):
if self.num > other.num:
return True
else:
return False
elif isinstance(other, int):
if self.num > other:
return True
else:
return False
else:
raise TypeError("Rank can't compare to %s" % type(other))
def __lt__(self, other):
if isinstance(other, Rank):
if self.num < other.num:
return True
else:
return False
elif isinstance(other, int):
if self.num < other:
return True
else:
return False
else:
raise TypeError("Rank can't compare to %s" % type(other))
def __ge__(self, other):
return (self > other or self == other)
def __le__(self, other):
return (self < other or self == other)
def __add__(self, other):
if isinstance(other, int):
if (self.num + other) < len(self.branch.ranks):
return self.branch.ranks[self.num+other]
elif (self.num + other) < 1:
return trunk
else:
return None
else:
raise TypeError("Changes in rank must be integers.")
def __radd__(self, other):
return self.__add__(other)
def __sub__(self, other):
if isinstance(other, Rank):
return (self.num - other.num)
elif isinstance(other, int):
if 0 < (self.num - other) < len(self.branch.ranks):
return self.branch.ranks[self.num-other]
elif (self.num - other) < 1:
return trunk
elif (self.num - other) > len(self.branch.ranks):
return None
else:
raise TypeError("Rank subtraction not defined for %s" % type(other))
def __rsub__(self, other):
if isinstance(other, Rank):
return (other.num - self.num)
else:
raise TypeError("Rank subtraction not defined for %s" % type(other))
class RankList(list):
"""
Convenience wrapper for a list of ranks, with simplified look-up.
This allows indexes and slices on the ranks to use Rank objects and/or
string names in addition to integers for accessing the elements of the
list.
The RankList also has to know what branch it is indexing, so it includes
a 'branch' attribute.
"""
def __init__(self, branch, ranks):
self.branch = branch
for rank in ranks:
self.append(rank)
def __getitem__(self, rank):
if isinstance(rank, Rank):
i = int(rank)
elif isinstance(rank, str):
i = [r.name for r in self].index(rank)
elif isinstance(rank, int):
i = rank
elif isinstance(rank, slice):
if rank.start is None:
j = 0
else:
j = self.__getitem__(rank.start)
if rank.stop is None:
k = len(self)
else:
k = self.__getitem__(rank.stop)
s = []
for i in range(j,k):
s.append(super().__getitem__(i))
return s
else:
raise IndexError("Type %s not a valid index for RankList" % type(rank))
return super().__getitem__(i)
# Define the trunk branch object
# This schema will make sense for any unaffiliated Blender document,
# even if it hasn't been saved as a file yet:
trunk = Branch(None, '', 0, ('', 'file', 'scene'))

View File

@ -0,0 +1,690 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="1920"
height="1080"
viewBox="0 0 1920 1080"
version="1.1"
id="svg8"
inkscape:version="1.0.2 (1.0.2+r75+1)"
sodipodi:docname="ranks_branches.svg">
<defs
id="defs2" />
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="1"
inkscape:pageshadow="2"
inkscape:zoom="1.0854167"
inkscape:cx="877.18978"
inkscape:cy="510.01846"
inkscape:document-units="px"
inkscape:current-layer="layer1"
inkscape:document-rotation="0"
showgrid="false"
units="px"
inkscape:window-width="2880"
inkscape:window-height="1620"
inkscape:window-x="455"
inkscape:window-y="44"
inkscape:window-maximized="0" />
<metadata
id="metadata5">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title />
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1">
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:32px;line-height:1;font-family:'URW Gothic';-inkscape-font-specification:'URW Gothic';text-align:center;letter-spacing:0px;word-spacing:0px;text-anchor:middle"
x="859.29395"
y="75.932449"
id="text835"><tspan
sodipodi:role="line"
x="859.29395"
y="75.932449"
id="tspan920">&quot;trunk&quot;</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:24px;line-height:1;font-family:'URW Gothic';-inkscape-font-specification:'URW Gothic';letter-spacing:0px;word-spacing:0px"
x="833.41034"
y="144.69955"
id="text841"><tspan
sodipodi:role="line"
id="tspan839"
x="833.41034"
y="144.69955">-</tspan><tspan
sodipodi:role="line"
x="833.41034"
y="168.69955"
id="tspan843">file</tspan><tspan
sodipodi:role="line"
x="833.41034"
y="192.69955"
id="tspan845">scene</tspan></text>
<text
xml:space="preserve"
style="font-size:32px;line-height:1;font-family:'URW Gothic';-inkscape-font-specification:'URW Gothic';letter-spacing:0px;word-spacing:0px"
x="814.9588"
y="282.52191"
id="text849"><tspan
sodipodi:role="line"
id="tspan847"
x="814.9588"
y="282.52191">MyProject</tspan></text>
<text
xml:space="preserve"
style="font-size:24px;line-height:1;font-family:'URW Gothic';-inkscape-font-specification:'URW Gothic';letter-spacing:0px;word-spacing:0px"
x="836.71967"
y="346.81601"
id="text871"><tspan
sodipodi:role="line"
id="tspan869"
x="836.71967"
y="346.81601"
style="fill:#b3b3b3">-</tspan><tspan
sodipodi:role="line"
x="836.71967"
y="370.81601"
id="tspan1126">project</tspan><tspan
sodipodi:role="line"
x="836.71967"
y="394.81601"
id="tspan873">series</tspan><tspan
sodipodi:role="line"
x="836.71967"
y="418.81601"
id="tspan875">episode</tspan><tspan
sodipodi:role="line"
x="836.71967"
y="442.81601"
id="tspan877">sequence</tspan><tspan
sodipodi:role="line"
x="836.71967"
y="466.81601"
id="tspan879">block</tspan><tspan
sodipodi:role="line"
x="836.71967"
y="490.81601"
id="tspan881">camera</tspan><tspan
sodipodi:role="line"
x="836.71967"
y="514.81604"
id="tspan883">shot</tspan><tspan
sodipodi:role="line"
x="836.71967"
y="538.81604"
id="tspan885">element</tspan></text>
<text
xml:space="preserve"
style="font-size:24px;line-height:1;font-family:'URW Gothic';-inkscape-font-specification:'URW Gothic';letter-spacing:0px;word-spacing:0px"
x="1324.7578"
y="703.01611"
id="text889"><tspan
sodipodi:role="line"
id="tspan887"
x="1324.7578"
y="703.01611"
style="font-size:24px;fill:#cccccc">-</tspan><tspan
sodipodi:role="line"
x="1324.7578"
y="727.01611"
style="font-size:24px;fill:#cccccc"
id="tspan1128">project</tspan><tspan
sodipodi:role="line"
x="1324.7578"
y="751.01611"
id="tspan891">library</tspan><tspan
sodipodi:role="line"
x="1324.7578"
y="775.01611"
id="tspan893">department</tspan><tspan
sodipodi:role="line"
x="1324.7578"
y="799.01611"
id="tspan895">category</tspan><tspan
sodipodi:role="line"
x="1324.7578"
y="823.01611"
id="tspan897">subcategory</tspan></text>
<text
xml:space="preserve"
style="font-size:32px;line-height:1;font-family:'URW Gothic';-inkscape-font-specification:'URW Gothic';letter-spacing:0px;word-spacing:0px"
x="1303.8707"
y="640.34381"
id="text901"><tspan
sodipodi:role="line"
id="tspan899"
x="1303.8707"
y="640.34381">Library</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:21.3333px;line-height:1;font-family:'Courier New';-inkscape-font-specification:'Courier New Bold';font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-variant-east-asian:normal;font-feature-settings:normal;font-variation-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:0px;word-spacing:0px;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;shape-margin:0;inline-size:0;opacity:1;vector-effect:none;fill:#000000;fill-opacity:1;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;stop-color:#000000;stop-opacity:1"
x="1290.4512"
y="277.65115"
id="text905"
transform="translate(13.419556,387.89022)"><tspan
sodipodi:role="line"
id="tspan903"
x="1290.4512"
y="277.65115">library.yaml</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:21.3333px;line-height:1;font-family:'Courier New';-inkscape-font-specification:'Courier New Bold';letter-spacing:0px;word-spacing:0px"
x="814.9588"
y="305.64523"
id="text909"><tspan
sodipodi:role="line"
id="tspan907"
x="814.9588"
y="305.64523">myproject.yaml</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:21.3333px;line-height:1;font-family:'Courier New';-inkscape-font-specification:'Courier New Bold';font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-variant-east-asian:normal;font-feature-settings:normal;font-variation-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:0px;word-spacing:0px;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;shape-margin:0;inline-size:0;opacity:1;vector-effect:none;fill:#000000;fill-opacity:1;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;stop-color:#000000;stop-opacity:1"
x="1326.4742"
y="67.188583"
id="text913"
transform="translate(-513.53124,35.760744)"><tspan
sodipodi:role="line"
id="tspan911"
x="1326.4742"
y="67.188583">abx/abx.yaml</tspan></text>
<text
xml:space="preserve"
style="font-size:32px;line-height:1;font-family:'URW Gothic';-inkscape-font-specification:'URW Gothic';letter-spacing:0px;word-spacing:0px"
x="121.5862"
y="640.34381"
id="text917"><tspan
sodipodi:role="line"
id="tspan915"
x="121.5862"
y="640.34381">Episodes</tspan></text>
<text
xml:space="preserve"
style="font-size:32px;line-height:1;font-family:'URW Gothic';-inkscape-font-specification:'URW Gothic';letter-spacing:0px;word-spacing:0px"
x="121.5862"
y="789.91241"
id="text921"><tspan
sodipodi:role="line"
id="tspan919"
x="121.5862"
y="789.91241">A.001-Pilot</tspan></text>
<text
xml:space="preserve"
style="font-size:32px;line-height:1;font-family:'URW Gothic';-inkscape-font-specification:'URW Gothic';letter-spacing:0px;word-spacing:0px"
x="121.5862"
y="826.12451"
id="text925"><tspan
sodipodi:role="line"
id="tspan923"
x="121.5862"
y="826.12451">Seq</tspan></text>
<text
xml:space="preserve"
style="font-size:32px;line-height:1;font-family:'URW Gothic';-inkscape-font-specification:'URW Gothic';letter-spacing:0px;word-spacing:0px"
x="121.5862"
y="862.33655"
id="text929"><tspan
sodipodi:role="line"
id="tspan927"
x="121.5862"
y="862.33655">LP-LastPoint</tspan></text>
<text
xml:space="preserve"
style="font-size:32px;line-height:1;font-family:'URW Gothic';-inkscape-font-specification:'URW Gothic';letter-spacing:0px;word-spacing:0px"
x="121.5862"
y="898.54865"
id="text933"><tspan
sodipodi:role="line"
id="tspan931"
x="121.5862"
y="898.54865">A.001-LP-1-BeginningOfEnd-anim.blend</tspan></text>
<text
xml:space="preserve"
style="font-size:32px;line-height:1;font-family:'URW Gothic';-inkscape-font-specification:'URW Gothic';letter-spacing:0px;word-spacing:0px"
x="121.5862"
y="753.70032"
id="text937"><tspan
sodipodi:role="line"
id="tspan935"
x="121.5862"
y="753.70032">A</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:600;font-stretch:normal;font-size:16px;line-height:1;font-family:'URW Gothic';-inkscape-font-specification:'URW Gothic Semi-Bold';letter-spacing:0px;word-spacing:0px"
x="814.9588"
y="330.56796"
id="text960"><tspan
sodipodi:role="line"
id="tspan958"
x="814.9588"
y="330.56796">Ranks:</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:600;font-stretch:normal;font-size:16px;line-height:1;font-family:'URW Gothic';-inkscape-font-specification:'URW Gothic Semi-Bold';letter-spacing:0px;word-spacing:0px"
x="812.7821"
y="126.22813"
id="text964"><tspan
sodipodi:role="line"
id="tspan962"
x="812.7821"
y="126.22813">Ranks:</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:600;font-stretch:normal;font-size:16px;line-height:1;font-family:'URW Gothic';-inkscape-font-specification:'URW Gothic Semi-Bold';letter-spacing:0px;word-spacing:0px"
x="1303.8707"
y="690.45233"
id="text989"><tspan
sodipodi:role="line"
id="tspan987"
x="1303.8707"
y="690.45233">Ranks:</tspan></text>
<text
xml:space="preserve"
style="font-style:oblique;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:13.3333px;line-height:1;font-family:'URW Gothic';-inkscape-font-specification:'URW Gothic Oblique';letter-spacing:0px;word-spacing:0px"
x="944.66821"
y="628.16779"
id="text993"><tspan
sodipodi:role="line"
x="944.66821"
y="628.16779"
id="tspan1006">At any level in the hierarchy, a project unit</tspan><tspan
sodipodi:role="line"
x="944.66821"
y="641.5011"
id="tspan1333">may define a new schema with a 'project_schema'</tspan><tspan
sodipodi:role="line"
x="944.66821"
y="654.83441"
id="tspan1335">key.</tspan><tspan
sodipodi:role="line"
x="944.66821"
y="668.16766"
id="tspan1337" /><tspan
sodipodi:role="line"
x="944.66821"
y="681.50098"
id="tspan1339">This key cannot change the interpretation of ranks</tspan><tspan
sodipodi:role="line"
x="944.66821"
y="694.83429"
id="tspan1341">above the current unit, but only the ones below it</tspan><tspan
sodipodi:role="line"
x="944.66821"
y="708.1676"
id="tspan1343">(and the current unit itself).</tspan><tspan
sodipodi:role="line"
x="944.66821"
y="721.50085"
id="tspan1349" /><tspan
sodipodi:role="line"
x="944.66821"
y="734.83417"
id="tspan1351">This creates a branch of the rank/schema</tspan><tspan
sodipodi:role="line"
x="944.66821"
y="748.16748"
id="tspan1353">tree, which takes the name of the project</tspan><tspan
sodipodi:role="line"
x="944.66821"
y="761.50079"
id="tspan1355">unit where the branch starts.</tspan><tspan
sodipodi:role="line"
x="944.66821"
y="774.83411"
id="tspan1345" /><tspan
sodipodi:role="line"
x="944.66821"
y="788.16736"
id="tspan1347">(And the first unit that is not inherited from</tspan><tspan
sodipodi:role="line"
x="944.66821"
y="801.50067"
id="tspan1365">its parent Branch).</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:21.3333px;line-height:1;font-family:'Courier New';-inkscape-font-specification:'Courier New Bold';letter-spacing:0px;word-spacing:0px"
x="121.5862"
y="665.03693"
id="text911"><tspan
sodipodi:role="line"
id="tspan909"
x="121.5862"
y="665.03693">episodes.yaml</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:16px;line-height:1;font-family:'Courier New';-inkscape-font-specification:'Courier New';letter-spacing:0px;word-spacing:0px"
x="189.16733"
y="684.3067"
id="text915"><tspan
sodipodi:role="line"
x="189.16733"
y="684.3067"
id="tspan917">project_unit:</tspan><tspan
sodipodi:role="line"
x="189.16733"
y="700.3067"
id="tspan924"> from_folders: True</tspan><tspan
sodipodi:role="line"
x="189.16733"
y="716.3067"
id="tspan926" /></text>
<text
xml:space="preserve"
style="font-style:oblique;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:13.3333px;line-height:1;font-family:'URW Gothic';-inkscape-font-specification:'URW Gothic Oblique';letter-spacing:0px;word-spacing:0px"
x="449.34958"
y="629.32965"
id="text944"><tspan
sodipodi:role="line"
x="449.34958"
y="629.32965"
id="tspan951">The Episodes directory is not</tspan><tspan
sodipodi:role="line"
x="449.34958"
y="642.66296"
id="tspan1028">a unit, but each of the folders under it is (they</tspan><tspan
sodipodi:role="line"
x="449.34958"
y="655.99628"
id="tspan1541">are 'series' units, all sharing the same 'series' rank</tspan><tspan
sodipodi:role="line"
x="449.34958"
y="669.32953"
id="tspan1547">in the 'myproject' branch.</tspan><tspan
sodipodi:role="line"
x="449.34958"
y="682.66284"
id="tspan1559" /><tspan
sodipodi:role="line"
x="449.34958"
y="695.99615"
id="tspan1551">Rather than using a separate YAML files for each</tspan><tspan
sodipodi:role="line"
x="449.34958"
y="709.32947"
id="tspan1567">series, this sample project uses a single file at the</tspan><tspan
sodipodi:role="line"
x="449.34958"
y="722.66272"
id="tspan1569">top with 'project_unit' key that is used as a template.</tspan><tspan
sodipodi:role="line"
x="449.34958"
y="735.99603"
id="tspan1571" /><tspan
sodipodi:role="line"
x="449.34958"
y="749.32935"
id="tspan1573">It is distinguished from a normal 'project_unit' key</tspan><tspan
sodipodi:role="line"
x="449.34958"
y="762.66266"
id="tspan1575">by providing the 'from_folders' setting, and not</tspan><tspan
sodipodi:role="line"
x="449.34958"
y="775.99597"
id="tspan1577">containing a 'code', 'name', or 'title' setting.</tspan><tspan
sodipodi:role="line"
x="449.34958"
y="789.32922"
id="tspan1579" /><tspan
sodipodi:role="line"
x="449.34958"
y="802.66254"
id="tspan1581">Instead, these values will be set by the directory names</tspan><tspan
sodipodi:role="line"
x="449.34958"
y="815.99585"
id="tspan1583">under the current directory (this also means all directories</tspan><tspan
sodipodi:role="line"
x="449.34958"
y="829.32916"
id="tspan1585">must be project units at this level).</tspan><tspan
sodipodi:role="line"
x="449.34958"
y="842.66248"
id="tspan1587" /><tspan
sodipodi:role="line"
x="449.34958"
y="855.99573"
id="tspan1589">This allows for a simple fanout without needing lots of YAML files.</tspan></text>
<text
xml:space="preserve"
style="font-style:oblique;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:13.3333px;line-height:1;font-family:'URW Gothic';-inkscape-font-specification:'URW Gothic Oblique';letter-spacing:0px;word-spacing:0px"
x="1029.5554"
y="261.4942"
id="text969"><tspan
sodipodi:role="line"
x="1029.5554"
y="261.4942"
id="tspan1135">The project folder defines a schema in a YAML file matching the project directory name.</tspan><tspan
sodipodi:role="line"
x="1029.5554"
y="274.82751"
id="tspan1139">(you can also call the file 'project.yaml' or 'kitcat.yaml' if you prefer).</tspan><tspan
sodipodi:role="line"
x="1029.5554"
y="288.1608"
id="tspan1159" /><tspan
sodipodi:role="line"
x="1029.5554"
y="301.49411"
id="tspan1161">Three keys are mandatory for this file:</tspan><tspan
sodipodi:role="line"
x="1029.5554"
y="314.82739"
id="tspan1163" /><tspan
sodipodi:role="line"
x="1029.5554"
y="328.16071"
id="tspan1165">project_root:</tspan><tspan
sodipodi:role="line"
x="1029.5554"
y="341.49399"
id="tspan1169"> The 'project_root' key only has to exist. It can be as simple as having</tspan><tspan
sodipodi:role="line"
x="1029.5554"
y="354.8273"
id="tspan1173"> a value of True. But this is an ideal place to store basic metadata about</tspan><tspan
sodipodi:role="line"
x="1029.5554"
y="368.16058"
id="tspan1177"> your organization and this project.</tspan><tspan
sodipodi:role="line"
x="1029.5554"
y="381.4939"
id="tspan1179" /><tspan
sodipodi:role="line"
x="1029.5554"
y="394.82721"
id="tspan1181">project_unit:</tspan><tspan
sodipodi:role="line"
x="1029.5554"
y="408.16049"
id="tspan1183"> The 'project_unit' key identifies the top rank of the project (the current directory). It</tspan><tspan
sodipodi:role="line"
x="1029.5554"
y="421.4938"
id="tspan1187"> should contain a bullet list with a single entry identifying the code ('myproject'), 'name',</tspan><tspan
sodipodi:role="line"
x="1029.5554"
y="434.82709"
id="tspan1191"> 'title', and other details.</tspan><tspan
sodipodi:role="line"
x="1029.5554"
y="448.1604"
id="tspan1193" /><tspan
sodipodi:role="line"
x="1029.5554"
y="461.49368"
id="tspan1195">project_schema:</tspan><tspan
sodipodi:role="line"
x="1029.5554"
y="474.827"
id="tspan1143"> The 'project_schema' key defines the default hierarchy of project assets, and</tspan><tspan
sodipodi:role="line"
x="1029.5554"
y="488.16031"
id="tspan1553"> creates the 'myproject' branch of ranks, overriding the default &quot;trunk&quot; branch.</tspan><tspan
sodipodi:role="line"
x="1029.5554"
y="501.49359"
id="tspan1555"> All project files are of rank 'project' or below (with each having a higher rank</tspan><tspan
sodipodi:role="line"
x="1029.5554"
y="514.8269"
id="tspan1557"> number). As defined here, the default ranking is organized for episode shot files.</tspan></text>
<text
xml:space="preserve"
style="font-style:oblique;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:13.3333px;line-height:1;font-family:'URW Gothic';-inkscape-font-specification:'URW Gothic Oblique';letter-spacing:0px;word-spacing:0px"
x="515.06158"
y="63.621597"
id="text1038"><tspan
sodipodi:role="line"
x="515.06158"
y="63.621597"
id="tspan1036">Before any file is loaded (filepath is ''),</tspan><tspan
sodipodi:role="line"
x="515.06158"
y="76.954895"
id="tspan1045">the rank hierarchy is controlled solely</tspan><tspan
sodipodi:role="line"
x="515.06158"
y="90.288193"
id="tspan1047">by the default schema in the ABX</tspan><tspan
sodipodi:role="line"
x="515.06158"
y="103.62149"
id="tspan1049">program folder.</tspan><tspan
sodipodi:role="line"
x="515.06158"
y="116.95479"
id="tspan1051" /><tspan
sodipodi:role="line"
x="515.06158"
y="130.2881"
id="tspan1053">It sets only the 'trunk' (rank 0),</tspan><tspan
sodipodi:role="line"
x="515.06158"
y="143.6214"
id="tspan1055">'file' (the level of the Blender file)</tspan><tspan
sodipodi:role="line"
x="515.06158"
y="156.9547"
id="tspan1057">and 'scene' (the level of a scene</tspan><tspan
sodipodi:role="line"
x="515.06158"
y="170.28799"
id="tspan1059">in thefile) as ranks.</tspan><tspan
sodipodi:role="line"
x="515.06158"
y="183.62129"
id="tspan1061" /><tspan
sodipodi:role="line"
x="515.06158"
y="196.95459"
id="tspan1063">These are imposed by the nature of</tspan><tspan
sodipodi:role="line"
x="515.06158"
y="210.28789"
id="tspan1065">the program, so we can safely use</tspan><tspan
sodipodi:role="line"
x="515.06158"
y="223.62119"
id="tspan1067">those as defaults.</tspan><tspan
sodipodi:role="line"
x="515.06158"
y="236.95448"
id="tspan1069" /><tspan
sodipodi:role="line"
x="515.06158"
y="250.2878"
id="tspan1071">Any additional structure needs to be</tspan><tspan
sodipodi:role="line"
x="515.06158"
y="263.62109"
id="tspan1073">given by a YAML file in the project</tspan><tspan
sodipodi:role="line"
x="515.06158"
y="276.95438"
id="tspan1075">directory.</tspan></text>
<path
style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 889.51282,213.88591 0,27.1624"
id="path1077"
sodipodi:nodetypes="cc" />
<text
xml:space="preserve"
style="font-style:oblique;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:13.3333px;line-height:1;font-family:'URW Gothic';-inkscape-font-specification:'URW Gothic Oblique';letter-spacing:0px;word-spacing:0px"
x="119.42168"
y="924.96472"
id="text1085"><tspan
sodipodi:role="line"
x="119.42168"
y="924.96472"
id="tspan1083">The name path for this file is derived almost</tspan><tspan
sodipodi:role="line"
x="119.42168"
y="938.29803"
id="tspan1092">entirely from the filename. Only the 'project'</tspan><tspan
sodipodi:role="line"
x="119.42168"
y="951.63135"
id="tspan1094">and 'series' ranks are specified above.</tspan><tspan
sodipodi:role="line"
x="119.42168"
y="964.9646"
id="tspan1108" /><tspan
sodipodi:role="line"
x="119.42168"
y="978.29791"
id="tspan1110">ABX currently ignores the folder names above</tspan><tspan
sodipodi:role="line"
x="119.42168"
y="991.63123"
id="tspan1112">this, even though they could be used to give</tspan><tspan
sodipodi:role="line"
x="119.42168"
y="1004.9645"
id="tspan1114">more information (e.g. this sequence is called</tspan><tspan
sodipodi:role="line"
x="119.42168"
y="1018.2978"
id="tspan1116">&quot;LastPoint&quot;, but without YAML, this is left unspecified.</tspan></text>
<path
style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="M 130.76843,682.08859 V 716.8141"
id="path1118" />
<path
style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 889.8041,553.82368 0,18.17606 H 136.617 v 32.46778"
id="path1120"
sodipodi:nodetypes="cccc" />
<path
style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 889.8041,553.82368 0,18.17606 h 421.5589 v 32.46778"
id="path1122"
sodipodi:nodetypes="cccc" />
<path
style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="M 1312.781,834.80279 V 869.5283"
id="path1124" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 28 KiB

159
tests/test_ranks.py Normal file
View File

@ -0,0 +1,159 @@
# test_ranks
"""
Test the ranks module, which implements the branch and rank semantics and math.
"""
import unittest, os
import sys
print("__file__ = ", __file__)
sys.path.append(os.path.normpath(os.path.join(__file__, '..', '..')))
from abx import ranks
class BranchTests(unittest.TestCase):
def test_trunk_branch(self):
t = ranks.trunk.rank('')
f = ranks.trunk.rank('file')
s = ranks.trunk.rank('scene')
self.assertEqual(repr(ranks.trunk), "<branch 'trunk': file, scene>")
self.assertIn(t, ranks.trunk)
self.assertIn(f, ranks.trunk)
self.assertIn(s, ranks.trunk)
def test_defining_branch(self):
b = ranks.Branch(ranks.trunk, 'myproject', 1,
('project', 'series', 'episode', 'sequence',
'block', 'shot', 'element'))
self.assertEqual(len(b._ranks), 8)
class RanksTests(unittest.TestCase):
def setUp(self):
self.b = ranks.Branch(ranks.trunk, 'myproject', 1,
('project', 'series', 'episode', 'sequence',
'block', 'shot', 'element'))
def test_rank_knows_branch(self):
sh = self.b.rank('shot')
self.assertIs(sh.branch, self.b)
self.assertEqual(sh.num, 6)
def test_rank_representation(self):
ep = self.b.rank('episode')
self.assertEqual(repr(ep), '<myproject:3-episode>')
self.assertEqual(int(ep), 3)
self.assertEqual(str(ep), 'episode')
sq = self.b.rank(4)
self.assertEqual(repr(sq), '<myproject:4-sequence>')
self.assertEqual(int(sq), 4)
self.assertEqual(str(sq), 'sequence')
def test_rank_addition(self):
ep = self.b.rank('episode')
sq = self.b.rank(4)
self.assertEqual(sq, ep + 1)
self.assertEqual(sq, 1 + ep)
def test_rank_subtraction(self):
ep = self.b.rank('episode')
sq = self.b.rank(4)
self.assertEqual(ep, sq - 1)
self.assertEqual(sq, ep - (-1))
self.assertEqual(sq - ep, 1)
self.assertEqual(ep - sq, -1)
def test_rank_increment_decrement(self):
ep = self.b.rank('episode')
sq = self.b.rank(4)
r = ep
r += 1
self.assertEqual(r, sq)
r = sq
r -= 1
self.assertEqual(r, ep)
def test_rank_comparisons_direct(self):
sh = self.b.rank('shot')
se = self.b.rank('series')
s1 = self.b.rank(2)
self.assertEqual(se, s1)
self.assertGreater(sh, se)
self.assertLess(se, sh)
def test_rank_comparisons_compound(self):
sh = self.b.rank('shot')
se = self.b.rank('series')
s1 = self.b.rank(2)
self.assertNotEqual(sh, se)
self.assertGreaterEqual(sh, se)
self.assertLessEqual(se, sh)
self.assertLessEqual(s1, se)
self.assertGreaterEqual(se, s1)
def test_rank_too_high(self):
sh = self.b.rank('shot')
el = self.b.rank('element')
r = sh + 1
s = sh + 2
t = sh + 3
self.assertEqual(r, el)
self.assertEqual(s, None)
self.assertEqual(t, None)
def test_rank_too_low(self):
se = self.b.rank('series')
pr = self.b.rank('project')
r = se - 1 # Normal - 'project' is one below 'series'
s = se - 2 # ? Should this be 'project' or 'trunk'/None?
t = se - 3 # "` "
self.assertEqual(r, pr)
self.assertEqual(s, ranks.trunk)
self.assertEqual(t, ranks.trunk)
def test_rank_slices_from_branch(self):
ranks = self.b.ranks
self.assertEqual(
ranks[1:4],
ranks[self.b.rank('project'):self.b.rank('sequence')])
self.assertEqual(
ranks[:],
ranks)
def test_ranklist_slice_access(self):
ranks = self.b.ranks
self.assertEqual(
ranks[1:4],
ranks['project':'sequence'])
self.assertEqual(
ranks[:'sequence'],
ranks[0:4])
self.assertEqual(
ranks[1:'shot'],
ranks['project':6])
self.assertEqual(
ranks[self.b.ranks['sequence']:7],
ranks['sequence':7])
self.assertEqual(
ranks.branch,
self.b)

View File

@ -63,7 +63,7 @@ class TestRenderProfile_Implementation(unittest.TestCase):
self.assertEqual(self.scene.render.resolution_percentage, 100)
self.assertEqual(self.scene.render.image_settings.compression, 50)