isogr / register-system-transition

Covers GR system transition from 2013 Java-based version to 2023 static site based version
1 stars 1 forks source link

Implement change requests (proposals) #1

Closed strogonoff closed 8 months ago

strogonoff commented 1 year ago
  1. Change requests looked like this before (I don’t know whether this script implements this or it was created through other means): https://github.com/isogr/registry/tree/09bb63e1635d7ae0194b5e71b746bb1ee13ddf8b/gr-registry/change-requests

    They are in change-requests folder and each CR is a YAML file.

  2. Now they look like this: https://github.com/isogr/registry/tree/173aaae67372858744e1fdcca250780cd7738c32/gr-registry/proposals

    They are in proposals folder and each CR is a directory with main.yaml and added/clarified item data in a subdirectory.

We should try to

This is how CR data looks in Java GUI:

Screenshot 2023-02-17 at 19 32 09
strogonoff commented 1 year ago

Here is a script that takes a dataset from the old change-requests to new proposals state that I used to update this repository (the script should be run from dataset root, i.e. gr-registry folder):

#!/usr/bin/env python

"""Migrates old change requests to proposals"""

import yaml
from pathlib import Path

PROPOSALS_DIRNAME = 'proposals'
PROPOSAL_MAIN_FILENAME = 'main.yaml'
ITEM_DATA_DIRNAME = 'items'

# TODO: Implement events
# EVENTS_DIRNAME = 'events'

PROPOSAL_STATES = (
    ((lambda p: p.get('timeProposed', None) is None and p.get('timeDisposed', None) is None and p['status'] == 'pending'), 'draft'),
    ((lambda p: p.get('timeProposed', None) and p.get('timeDisposed', None) is None and p['status'] == 'pending'), 'proposed'),
    ((lambda p: p.get('timeDisposed', None) and p.get('timeDisposed', None) and p['status'] == 'final' and p['disposition'] == 'accepted'), 'accepted'),
    ((lambda p: p.get('timeDisposed', None) and p.get('timeDisposed', None) and p['status'] == 'final' and p['disposition'] == 'rejected'), 'rejected'),
)

def determine_proposal_state(p: dict) -> str:
    for func, state in PROPOSAL_STATES:
        if func(p):
            return state
    raise RuntimeError("Unable to calculate state")

def main():
    dataset_root = Path('.')

    old_cr_root = dataset_root / 'change-requests'
    if not old_cr_root.is_dir():
        raise RuntimeError("No change-requests dir found to migrate")

    proposal_root = dataset_root / PROPOSALS_DIRNAME
    proposal_root.mkdir(exist_ok=True)

    for cr_fn in old_cr_root.glob('*.yaml'):
        try:
            with open(cr_fn, 'r') as f:
                data = yaml.load(f, Loader=yaml.SafeLoader)

            cr_id = data['id']

            print(f"see file {cr_fn} (CR ID {cr_id}), {len(data['proposals'])} proposals")

            if cr_id != cr_fn.stem:
                raise RuntimeError("CR ID doesn’t match filename, data integrity violation")

            cr_path = proposal_root / cr_id
            cr_path.mkdir(exist_ok=True)

            item_data_path = cr_path / ITEM_DATA_DIRNAME
            item_data_path.mkdir(exist_ok=True)

            print(f"Will put CR data under {cr_path}, item data under {item_data_path}")

            # Update proposal
            data['state'] = determine_proposal_state(data)

            if not data.get('controlBodyDecisionEvent'):
                data['controlBodyDecisionEvent'] = ''

            data['timeEdited'] = data['timeStarted']

            items = {}
            for item_path, proposed_item in data['proposals'].items():
                # Only clarifications and additions are supposed to have item data
                if proposed_item['type'] in ('clarification', 'addition', ):
                    item_data = proposed_item['payload']
                    item_fp = item_data_path / item_path.lstrip('/')
                    item_fp.parent.mkdir(parents=True, exist_ok=True)
                    with open(item_fp, 'w') as p_f:
                        item = dict(
                            data=item_data,
                            status='valid',
                            id=Path(item_path).stem,
                        )
                        yaml.dump(item, p_f)
                    # We don’t need payload anymore
                    del proposed_item['payload']
                items[item_path] = proposed_item

            data['items'] = items

            del data['status']
            if 'disposition' in data:
                del data['disposition']
            del data['timeStarted']
            del data['proposals']

            new_cr_fname = cr_path / PROPOSAL_MAIN_FILENAME

            with open(new_cr_fname, 'w') as main_f:
                yaml.dump(data, main_f)
        except Exception:
            print('failed to process', cr_fn)
            raise

main()