-
Notifications
You must be signed in to change notification settings - Fork 37
Organization Validation for Service Inventory #1668
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
JVickery-TBS
wants to merge
54
commits into
master
Choose a base branch
from
feature/ds-reference-tables
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from 9 commits
Commits
Show all changes
54 commits
Select commit
Hold shift + click to select a range
505d76e
feat(dev): ref tables;
JVickery-TBS ec4635d
Merge branch 'master' into feature/ds-reference-tables
JVickery-TBS 45fae29
Merge branch 'master' into feature/ds-reference-tables
JVickery-TBS 2e72b9c
feat(dev): ds ref tables cont;
JVickery-TBS 10256b8
feat(dev): ds ref tables cont;
JVickery-TBS fc740de
feat(dev): ds ref tables cont;
JVickery-TBS 433e216
feat(dev): ds ref tables cont;
JVickery-TBS bcf369b
feat(dev): ds fiscal year func;
JVickery-TBS 65e40a8
feat(dev): ds fiscal year func;
JVickery-TBS 9af0578
feat(misc): changelog;
JVickery-TBS 163fd73
fix(misc): syntax;
JVickery-TBS eb89a03
feat(pd): service inventory;
JVickery-TBS dfd01d8
fix(pd): service inventory;
JVickery-TBS 046e8f1
fix(pd): service inventory;
JVickery-TBS 1bd0da2
feat(dev): github action;
JVickery-TBS 6a9a1d1
fix(tests): service inventory;
JVickery-TBS 9a82bb7
fix(pd): service inventory;
JVickery-TBS 70a1eab
fix(pd): service inventory;
JVickery-TBS 8f3daf8
fixes for loading service.csv
wardi 26cac34
fixes for loading service-std.csv
wardi e842434
compatible program_id ORDER BY
wardi 4a2d1b3
feat(db): ref data hash;
JVickery-TBS 74e453d
Merge branch 'master' into feature/ds-reference-tables
JVickery-TBS a157b56
Merge pull request #1676 from open-data/ds-reference-tables-fixes
JVickery-TBS 1e2d0b1
Merge remote-tracking branch 'origin/feature/ds-reference-tables' int…
JVickery-TBS 78f5705
feat(tests): coverage;
JVickery-TBS 5b7d542
fix(syntax): lint;
JVickery-TBS 2663137
fix(test): make test;
JVickery-TBS ce3fa40
fix(test): make test;
JVickery-TBS 504b3c7
fix(test): make test;
JVickery-TBS 396d4c7
fix(test): debug;
JVickery-TBS f1e2ab5
fix(test): debug;
JVickery-TBS 92e3b31
Merge branch 'master' into feature/ds-reference-tables
JVickery-TBS 3eea3bb
Merge branch 'master' into feature/ds-reference-tables
JVickery-TBS 4147a2a
Merge branch 'master' into feature/ds-reference-tables
JVickery-TBS b2ce3a6
feat(comments,build): script fix, ref data;
JVickery-TBS 8549cdc
Merge branch 'master' into feature/ds-reference-tables
JVickery-TBS 675ad7a
fix(bin): service ref data gen;
JVickery-TBS bf92612
feat(db): max length new lines;
JVickery-TBS 183cdf3
feat(db,pd): recombinant markers, service suffixes;
JVickery-TBS ee8d7cc
Merge branch 'master' into feature/ds-reference-tables
JVickery-TBS adf0e53
refactor(db): datastore temp table;
JVickery-TBS c422648
feat(js): pd datatables;
JVickery-TBS e4cf9f1
feat(misc): changelog;
JVickery-TBS cb720c5
fix(lint): syntax;
JVickery-TBS 62e3cd3
fix(lint): syntax;
JVickery-TBS 611d7cd
refactor(dev): feedback;
JVickery-TBS bdc4ef4
feat(pd): suffix choices;
JVickery-TBS 19757bd
fix(tests): service filter;
JVickery-TBS 31c160d
fix(syntax): lint;
JVickery-TBS 5ba54f3
Merge branch 'master' into feature/ds-reference-tables
JVickery-TBS 4dd7c21
Merge branch 'master' into feature/ds-reference-tables
JVickery-TBS aa7cbb3
Merge branch 'master' into feature/ds-reference-tables
JVickery-TBS 9b6def8
feat(schema): logic;
JVickery-TBS File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Some comments aren't visible on the classic Files Changed page.
There are no files selected for viewing
This file contains hidden or 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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,218 @@ | ||
| #!/usr/bin/env python3 | ||
| """ | ||
| Compiles all the reference data for Service Inventory. | ||
|
|
||
| All the required files are in the release of https://github.com/gcperformance/service-data | ||
|
|
||
| https://github.com/gcperformance/service-data/blob/master/src/utils.py | ||
| NOTE: program_list compiles fiscal years, orgs, program_ids, and their labels | ||
| NOTE: sid_list compiles fiscal years, orgs, service_ids, and their labels | ||
| https://github.com/gcperformance/service-data/blob/master/src/export.py | ||
| NOTE: CSV files in the release use semicolon(;) as the delimiter | ||
| """ | ||
|
|
||
| import os | ||
| import re | ||
| import csv | ||
| import json | ||
| import requests | ||
|
|
||
|
|
||
| ORG_LIST_URI = 'https://open.canada.ca/data/api/action/organization_list' | ||
| RELEASE_URI = 'https://api.github.com/repos/gcperformance/service-data/releases/latest' | ||
|
|
||
| SERVICE_ID_OUTPUT_FILE = os.path.join( | ||
| os.path.split(__file__)[0], | ||
| '../ckanext/canada/tables/references/data/ref_service_service_ids.csv') | ||
| SERVICE_ID_HEADERS = ['service_id', 'label_en', 'label_fr', 'org_years'] | ||
|
|
||
| PROGRAM_ID_OUTPUT_FILE = os.path.join( | ||
| os.path.split(__file__)[0], | ||
| '../ckanext/canada/tables/references/data/ref_service_program_ids.csv') | ||
| PROGRAM_ID_HEADERS = ['program_id', 'label_en', 'label_fr', 'org_years'] | ||
|
|
||
| REQUEST_HEADERS = {'User-Agent': 'CKAN/open-gov/service/gen_ref_data'} | ||
|
|
||
| WHITE_SPACE_SUBS = re.compile(r'\xa0|\t|\n|\s+') | ||
| TIMESTAMP_MATCH = re.compile(r'^Timestamp:') | ||
|
|
||
| ORG_VARIANTS_FILENAME = 'org_var.csv' | ||
| PROGRAM_IDS_FILENAME = 'program_list.csv' | ||
| SERVICE_IDS_FILENAME = 'sid_list.csv' | ||
|
|
||
|
|
||
| def _clean_intake_text(text: str) -> str: | ||
| """ | ||
| Normalize special characters and extra spaces | ||
| """ | ||
| text = text.replace('\u200b', '') # zero width char | ||
| text = re.sub(WHITE_SPACE_SUBS, ' ', text) | ||
| return text.strip() | ||
|
|
||
|
|
||
| def _generate_data(): | ||
| """ | ||
| Generate the reference data for Service Inventory | ||
|
|
||
| Gather Program IDs and their English and French names from the directory | ||
| https://api.github.com/repos/gcperformance/service-data/releases/latest/program_list.csv | ||
|
|
||
| Gather Service IDs and their English and French names from | ||
| https://api.github.com/repos/gcperformance/service-data/releases/latest/sid_list.csv | ||
|
|
||
| NOTE: we only start in the 2018-2019 fiscal year as per Policy. | ||
|
|
||
| NOTE: we take the latest Program Names as the ones to use. | ||
| """ | ||
| # get available org abbreviations on open gov | ||
| open_orgs = [] | ||
| skipped_orgs = set() | ||
| with requests.get(ORG_LIST_URI, stream=True, headers=REQUEST_HEADERS) as response: | ||
| open_orgs = response.json()['result'] | ||
| assert open_orgs | ||
|
|
||
| # gather the csv download URIs from the latest release | ||
| org_variants_uri = None | ||
| program_ids_uri = None | ||
| service_ids_uri = None | ||
| with requests.get(RELEASE_URI, stream=False) as response: | ||
| release_assets = response.json()['assets'] | ||
| for f in release_assets: | ||
| if f['name'] == ORG_VARIANTS_FILENAME: | ||
| org_variants_uri = f['browser_download_url'] | ||
| continue | ||
| if f['name'] == PROGRAM_IDS_FILENAME: | ||
| program_ids_uri = f['browser_download_url'] | ||
| continue | ||
| if f['name'] == SERVICE_IDS_FILENAME: | ||
| service_ids_uri = f['browser_download_url'] | ||
| continue | ||
| assert org_variants_uri | ||
| assert program_ids_uri | ||
| assert service_ids_uri | ||
|
|
||
| # compile list of Open Canada org abbreviations and Service Inventory org_ids | ||
| org_id_abbr_map = {} | ||
| with requests.get(org_variants_uri, stream=True) as response: | ||
| response.encoding = 'utf-8-sig' | ||
| c = csv.DictReader((l.decode('utf-8-sig') for l in response.iter_lines())) | ||
|
JVickery-TBS marked this conversation as resolved.
Outdated
|
||
|
|
||
| assert 'org_name_variant' in c.fieldnames | ||
| assert 'org_id' in c.fieldnames | ||
|
|
||
| for row in c: | ||
| oname = _clean_intake_text(row['org_name_variant']) | ||
| if oname in open_orgs: | ||
| org_id_abbr_map[_clean_intake_text(row['org_id'])] = oname | ||
| assert org_id_abbr_map | ||
|
|
||
| # compile map of program_ids | ||
| program_id_map = {} | ||
| with requests.get(program_ids_uri, stream=True) as response: | ||
| response.encoding = 'utf-8-sig' | ||
| c = csv.DictReader((l.decode('utf-8-sig') for l in response.iter_lines()), | ||
| delimiter=';') | ||
|
|
||
| assert 'org_id' in c.fieldnames | ||
| assert 'program_id' in c.fieldnames | ||
| assert 'latest_valid_fy' in c.fieldnames | ||
| assert 'program_en' in c.fieldnames | ||
| assert 'program_fr' in c.fieldnames | ||
|
|
||
| for row in c: | ||
| if not row['program_id']: | ||
| continue | ||
| program_id = _clean_intake_text(row['program_id']) | ||
| if not program_id: | ||
| continue | ||
| if program_id not in program_id_map: | ||
| program_id_map[program_id] = {} | ||
|
|
||
| label_en = _clean_intake_text(row['program_en']) | ||
| label_fr = _clean_intake_text(row['program_fr']) | ||
| if 'label_en' not in program_id_map[program_id]: # take first occuring label | ||
| program_id_map[program_id]['label_en'] = label_en | ||
| if 'label_fr' not in program_id_map[program_id]: # take first occuring label | ||
| program_id_map[program_id]['label_fr'] = label_fr | ||
|
|
||
| org = _clean_intake_text(row['org_id']) | ||
| if org not in org_id_abbr_map: | ||
| # org not in open gov, skip | ||
| if org not in skipped_orgs: | ||
| print('Organization %s not available in Open Gov Registry. Skipping...' % org) | ||
| skipped_orgs.add(org) | ||
| continue | ||
| org = org_id_abbr_map[org] | ||
|
|
||
| if 'org_years' not in program_id_map[program_id]: | ||
| program_id_map[program_id]['org_years'] = {} | ||
| if org not in program_id_map[program_id]['org_years']: | ||
| program_id_map[program_id]['org_years'][org] = [] | ||
| year = _clean_intake_text(row['latest_valid_fy']) | ||
| if year in program_id_map[program_id]['org_years'][org]: | ||
| continue | ||
| program_id_map[program_id]['org_years'][org].append(year) | ||
| assert program_id_map | ||
|
|
||
| # write program_id ref data | ||
| with open(PROGRAM_ID_OUTPUT_FILE, 'w') as f: | ||
| writer = csv.DictWriter(f, PROGRAM_ID_HEADERS) | ||
| writer.writeheader() | ||
| for program_id, program_data in program_id_map.items(): | ||
| writer.writerow({ | ||
| 'program_id': program_id, | ||
| 'label_en': program_data['label_en'], | ||
| 'label_fr': program_data['label_fr'], | ||
| 'org_years': json.dumps(program_data['org_years']) if 'org_years' in program_data else None,}) | ||
|
|
||
| # write service_id ref data | ||
| inserted_service_ids = set() | ||
| with requests.get(service_ids_uri, stream=True) as response: | ||
| response.encoding = 'utf-8-sig' | ||
| c = csv.DictReader((l.decode('utf-8-sig') for l in response.iter_lines()), | ||
| delimiter=';') | ||
|
|
||
| assert 'service_id' in c.fieldnames | ||
| assert 'service_name_en' in c.fieldnames | ||
| assert 'service_name_fr' in c.fieldnames | ||
| assert 'org_id' in c.fieldnames | ||
| assert 'fiscal_yr_first' in c.fieldnames | ||
| assert 'fiscal_yr_latest' in c.fieldnames | ||
|
|
||
| with open(SERVICE_ID_OUTPUT_FILE, 'w') as f: | ||
| writer = csv.DictWriter(f, SERVICE_ID_HEADERS) | ||
| writer.writeheader() | ||
| for row in c: | ||
| if not row['service_id']: | ||
| continue | ||
| service_id = _clean_intake_text(row['service_id']) | ||
| if not service_id or re.search(TIMESTAMP_MATCH, service_id): | ||
| continue | ||
| if service_id in inserted_service_ids: | ||
| raise Exception('%s already added...' % service_id) | ||
|
|
||
| org = _clean_intake_text(row['org_id']) | ||
| if org not in org_id_abbr_map: | ||
| # org not in open gov, skip | ||
| if org not in skipped_orgs: | ||
| print('Organization %s not available in Open Gov Registry. Skipping...' % org) | ||
| skipped_orgs.add(org) | ||
| continue | ||
| org = org_id_abbr_map[org] | ||
|
|
||
| inserted_service_ids.add(service_id) | ||
|
|
||
| # just make same format as program_id org_years to make queries the same | ||
| org_years = {} | ||
| org_years[org] = [_clean_intake_text(row['fiscal_yr_latest'])] | ||
|
|
||
| writer.writerow({ | ||
| 'service_id': service_id, | ||
| 'label_en': _clean_intake_text(row['service_name_en']), | ||
| 'label_fr': _clean_intake_text(row['service_name_fr']), | ||
| 'org_years': json.dumps(org_years) if org else None,}) | ||
| assert inserted_service_ids | ||
|
|
||
|
|
||
| if __name__ == '__main__': | ||
| _generate_data() | ||
This file contains hidden or 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
This file contains hidden or 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
This file contains hidden or 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
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.