Skip to content

schema_spreadsheet

Turns the dataverse schema into a spreadsheet.

SchemaSpreadsheet

Turns the dataverse schema into a spreadsheet.

Source code in src/dv_schema_models/schema_spreadsheet.py
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
class SchemaSpreadsheet:
    """Turns the dataverse schema into a spreadsheet."""

    def __init__(self, schema: DataverseSchemaResponse) -> None:
        """Initialize the SchemaSpreadsheet with a DataverseSchemaResponse."""
        self.schema = schema.data

    @staticmethod
    def _header_row() -> list[str]:
        return ["Field", "Definition", "Usage", "Repeatable (Y/N)", "Example"]

    def _title_fmt(self, workbook: xlsxwriter.Workbook):
        return workbook.add_format({"bold": True})

    def _block_fmt(self, workbook: xlsxwriter.Workbook):
        return workbook.add_format(
            {"bold": True, "align": "center", "bg_color": f"#{random.randrange(0x1000000):06x}"}
        )

    @staticmethod
    def _field_rows(field: MetadataField) -> list[Row]:
        """Turn one top-level field (and its children) into spreadsheet rows."""
        # ponytail: usage is RQ/O from isRequired; the schema has no "recommended" flag
        usage = lambda f: "RQ" if f.isRequired else "O"  # ruff:ignore[lambda-assignment]
        repeatable = lambda f: "Y" if f.multiple else "N"  # ruff:ignore[lambda-assignment]

        if not field.childFields:
            return [
                (
                    field.title,
                    field.description,
                    usage(field),
                    repeatable(field),
                    field.watermark,
                    True,
                    True,
                )
            ]
        # Compound field: parent row carries only the definition, children the rest.
        rows: list[Row] = [(field.title, field.description, "", "", "", True, False)]
        children = sorted(field.childFields.values(), key=lambda f: f.displayOrder)
        rows.extend(
            (
                child.title,
                child.description,
                usage(child),
                # A child of a repeatable compound is effectively repeatable.
                "Y" if (child.multiple or field.multiple) else "N",
                child.watermark,
                False,
                child is children[-1],
            )
            for child in children
        )
        return rows

    def _write_sheet(
        self,
        workbook: xlsxwriter.Workbook,
        sheet_name: str,
        blocks: list[MetadataBlock],
        cell_fmts: dict[tuple[bool, bool], Format],
    ) -> None:
        sheet = workbook.add_worksheet(sheet_name)
        for col, width in enumerate(COLUMN_WIDTHS):
            sheet.set_column(col, col, width)

        sheet.write_row(0, 0, self._header_row(), self._title_fmt(workbook))

        row_num = 1
        for block in blocks:
            sheet.merge_range(
                row_num, 0, row_num, 4, f"{block.displayName} Block", self._block_fmt(workbook)
            )
            row_num += 1
            for field in sorted(block.fields.values(), key=lambda f: f.displayOrder):
                for title, *cells, bold, end in self._field_rows(field):
                    sheet.write(row_num, 0, title, cell_fmts[bold, end])
                    for col, value in enumerate(cells, start=1):
                        sheet.write(row_num, col, value, cell_fmts[False, end])
                    row_num += 1

    def write(self, path: str | Path) -> Path:
        """Write an 'All' worksheet plus one per metadata block to an .xlsx file at `path`.

        Parameters
        ----------
        path
            The path to the .xlsx file to write. If it exists, it will be overwritten

        Returns
        -------
        Path
            The path to the .xlsx file that was written.

        """
        path = Path(path)
        workbook = xlsxwriter.Workbook(str(path))
        cell_fmts = {
            (bold, end): workbook.add_format(
                {"bold": bold, "bottom": 1 if end else 0, "text_wrap": True, "valign": "top"}
            )
            for bold in (True, False)
            for end in (True, False)
        }
        blocks = sorted(self.schema, key=lambda b: b.id)
        self._write_sheet(workbook, "All", blocks, cell_fmts)
        for block in blocks:
            self._write_sheet(
                workbook, block.displayName.removesuffix(" Metadata")[:31], [block], cell_fmts
            )
        workbook.close()
        return path

__init__(schema)

Initialize the SchemaSpreadsheet with a DataverseSchemaResponse.

Source code in src/dv_schema_models/schema_spreadsheet.py
20
21
22
def __init__(self, schema: DataverseSchemaResponse) -> None:
    """Initialize the SchemaSpreadsheet with a DataverseSchemaResponse."""
    self.schema = schema.data

write(path)

Write an 'All' worksheet plus one per metadata block to an .xlsx file at path.

Parameters:

Name Type Description Default
path str | Path

The path to the .xlsx file to write. If it exists, it will be overwritten

required

Returns:

Type Description
Path

The path to the .xlsx file that was written.

Source code in src/dv_schema_models/schema_spreadsheet.py
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
def write(self, path: str | Path) -> Path:
    """Write an 'All' worksheet plus one per metadata block to an .xlsx file at `path`.

    Parameters
    ----------
    path
        The path to the .xlsx file to write. If it exists, it will be overwritten

    Returns
    -------
    Path
        The path to the .xlsx file that was written.

    """
    path = Path(path)
    workbook = xlsxwriter.Workbook(str(path))
    cell_fmts = {
        (bold, end): workbook.add_format(
            {"bold": bold, "bottom": 1 if end else 0, "text_wrap": True, "valign": "top"}
        )
        for bold in (True, False)
        for end in (True, False)
    }
    blocks = sorted(self.schema, key=lambda b: b.id)
    self._write_sheet(workbook, "All", blocks, cell_fmts)
    for block in blocks:
        self._write_sheet(
            workbook, block.displayName.removesuffix(" Metadata")[:31], [block], cell_fmts
        )
    workbook.close()
    return path