Last active
May 12, 2016 04:39
-
-
Save stecman/2211f37593dfb17c4fa6 to your computer and use it in GitHub Desktop.
heyday/silverstripe-slices 0.x to 1.0 config conversion script
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python | |
# YAML config conversion script for heyday/silverstripe-slices 0.x going to 1.0 | |
# | |
# Comments and whitespace aren't preserved - this is more to help you out by doing | |
# the repeatitive part of the conversion. The converted config is written to stdout. | |
# | |
# Note that this needs a python module installed to work: | |
# | |
# $ pip install pyyaml | |
# | |
# Once that's installed, use as: | |
# | |
# $ python slice-convert.py mysite/_config/slices.yml | |
from __future__ import print_function | |
import argparse | |
import copy | |
import os | |
import sys | |
import yaml | |
parser = argparse.ArgumentParser(description="Convert heyday/silverstripe-slices 0.x config to 1.0") | |
parser.add_argument("file", nargs='+', help="Path to a YAML file with 0.x slice config inside") | |
args = parser.parse_args() | |
# Section name slices will grouped under in the converted config | |
# | |
# The previous version of the config allowed multiple section names (classes), but | |
# this new config is really designed to be a single unit that covers all slices. | |
# This can be changed by the user after generation if needed. | |
BASE_SECTION = "Slice" | |
# Section properties that should be copied over as-is and ignored from the | |
VERBATIM_PROPS = [ | |
"uploadFolder", | |
"uploadFolderFields", | |
"previewStylesheets", | |
"colorOptions" | |
] | |
# Section properties that aren't used in the new version of slices so can be left out | |
IGNORE_PROPS = [ | |
"hiddenFields", | |
"uploadFolder", | |
"uploadFolderFields" | |
] | |
# Fields that had a specific use under the 0.x version of the slices module, but can | |
# be removed completely in the new config | |
LEGACY_FIELDS = [ | |
"SecondaryIdentifier", | |
"TertiaryIdentifier" | |
] | |
# All property names have changed to be more appropriate for the new config structure | |
TEMPLATE_RENAME_MAP = { | |
"fieldLabels": "label", | |
"fieldHelp": "help", | |
"exampleFields": "exampleValue" | |
} | |
def is_verbatim_prop(key): | |
""" Check if a section property should be copied across as-is """ | |
return key in VERBATIM_PROPS | |
def get_template_names(config): | |
""" Get all template names defined in a config (all sections)""" | |
names = [] | |
for section in config: | |
for prop in config[section]: | |
for template in config[section][prop]: | |
if template != "Default" and prop not in VERBATIM_PROPS and prop not in IGNORE_PROPS: | |
names.append(template) | |
return sorted(set(names)) | |
def strip_legacy_fields(source): | |
newList = copy.deepcopy(source) | |
for value in LEGACY_FIELDS: | |
if value in newList: | |
newList.remove(value) | |
return newList | |
def get_defaults(config): | |
""" Get a dictionary of the defaults across all properties """ | |
defaults = {} | |
for section in config: | |
for prop in config[section]: | |
if "Default" in config[section][prop] and prop not in VERBATIM_PROPS and prop not in IGNORE_PROPS: | |
value = config[section][prop]["Default"] | |
if type(value) is list: | |
defaults[prop] = strip_legacy_fields(value) | |
else: | |
defaults[prop] = value | |
return defaults | |
def copy_config(config): | |
""" Create a new config, copying some keys verbatim from an existing one """ | |
newConfig = { | |
BASE_SECTION: { | |
"templates": {} | |
} | |
} | |
for section in config: | |
for prop in config[section]: | |
if is_verbatim_prop(prop): | |
newConfig[BASE_SECTION][prop] = copy.deepcopy(config[section][prop]) | |
return newConfig | |
def pivot_template_config(config, templateName, defaults={}): | |
""" Get a rearranged version of config with only the props (old names) grouped under a template name """ | |
templateConfig = defaults.copy() | |
for section in config: | |
for prop in config[section]: | |
if prop not in VERBATIM_PROPS: | |
if templateName in config[section][prop]: | |
templateConfig[prop] = config[section][prop][templateName] | |
return templateConfig | |
def upgrade_config(config): | |
""" Pivot a 0.x slice module config to a 1.0 compatible one """ | |
newConfig = copy_config(config) | |
templateCollection = newConfig[BASE_SECTION]["templates"] | |
defaults = get_defaults(config) | |
templates = get_template_names(config) | |
for templateName in templates: | |
pivoted = pivot_template_config(config, templateName, defaults) | |
templateConfig = { | |
"fields": {} | |
} | |
# Copy template nice names | |
if "secondaryIdentifierAsTemplatesMap" in pivoted: | |
templateConfig["name"] = pivoted["secondaryIdentifierAsTemplatesMap"] | |
# Copy class name map | |
if "secondaryIdentifierClassMap" in pivoted: | |
templateConfig["className"] = pivoted["secondaryIdentifierClassMap"] | |
if "tertiaryIdentifiers" in pivoted: | |
templateConfig["visualOptions"] = pivoted["tertiaryIdentifiers"] | |
# Add in show fields as empty (these don't have values to copy) | |
if "shownFields" in pivoted: | |
for field in pivoted["shownFields"]: | |
templateConfig["fields"][field] = {} | |
# Copy field specific configs into fields | |
for prop in TEMPLATE_RENAME_MAP: | |
if prop in pivoted: | |
for field in pivoted[prop]: | |
if field in LEGACY_FIELDS: | |
continue | |
if field not in templateConfig["fields"]: | |
templateConfig["fields"][field] = {} | |
templateConfig["fields"][field][TEMPLATE_RENAME_MAP[prop]] = pivoted[prop][field] | |
# Remove any fields specified as hidden | |
if "hiddenFields" in pivoted: | |
for field in pivoted["hiddenFields"]: | |
del templateConfig["fields"][field] | |
# Tidy up empty field configs | |
for field in templateConfig["fields"]: | |
fieldsConfig = templateConfig["fields"] | |
# Turn empty field configs into nulls | |
if not fieldsConfig[field]: | |
fieldsConfig[field] = None | |
# Change label-only field configs to use the label shortcut (string instead of object) | |
elif len(fieldsConfig[field].keys()) == 1 and "label" in fieldsConfig[field]: | |
fieldsConfig[field] = fieldsConfig[field]["label"] | |
# Put the converted config into the main config | |
templateCollection[templateName] = templateConfig | |
return newConfig | |
def nice_yaml_dump(data): | |
""" Dump YAML without serialising, and tidied up a little """ | |
string = yaml.safe_dump(data, default_flow_style=False, width=2, indent=" ") | |
string = string.replace(": null\n", ": ~\n") | |
return string | |
for filename in args.file: | |
if not os.path.exists(filename): | |
print("Skippping non-existent file '%s'" % filename, file=sys.stderr) | |
continue | |
file = open(filename, 'r') | |
try: | |
config = yaml.load(file) | |
except: | |
print("Failed to parse %s as YAML" % filename, file=sys.stderr) | |
continue | |
newConfig = upgrade_config(config) | |
print(nice_yaml_dump(newConfig)) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment