From 30cb56b105a25aafbf89382d75835e8dfd8ad6a6 Mon Sep 17 00:00:00 2001 From: Carl Higgs Date: Wed, 19 Jun 2024 15:10:02 +1000 Subject: [PATCH 1/3] updated policy_report.py to not use write_html in pdf header (appears to cause an error for unknown reasons); instead just writes a multi_cell text. Doesn't look as good but works for now. Addresses #446. Also improved some minor GUI styling aspects, including addressing #451, and corrected a type comparison check in generate.py --- .ghsci_version | 2 +- process/generate.py | 6 +- process/gui.py | 16 ++++-- process/subprocesses/policy_report.py | 79 +++++++++++++++++---------- 4 files changed, 65 insertions(+), 38 deletions(-) diff --git a/.ghsci_version b/.ghsci_version index 6cedcff6..4e298cc9 100644 --- a/.ghsci_version +++ b/.ghsci_version @@ -1 +1 @@ -4.5.2 +4.5.3 diff --git a/process/generate.py b/process/generate.py index 8fb093c8..3df4168e 100644 --- a/process/generate.py +++ b/process/generate.py @@ -28,8 +28,9 @@ def export_indicators(r, gpkg=True, csv=True): r.tables = r.get_tables() if r.tables == []: sys.exit( - f"\nResults don't appear to have been processed Please ensure that the analysis process has been run for this study region successfully and then try again. The specific error raised was:\n{e}\n", + f"\nResults don't appear to have been processed for {r.codename}.\n\nPlease ensure that the analysis process has been run for this study region successfully and then try again.", ) + print('\nData files') tables_not_in_database = [x for x in tables if x not in r.tables] if len(tables_not_in_database) > 0: print( @@ -68,7 +69,7 @@ def export_indicators(r, gpkg=True, csv=True): def generate(r): """List resources that have been generated for this study region.""" - if type(r) == str: + if type(r) is str: codename = r r = Region(codename) else: @@ -88,7 +89,6 @@ def generate(r): if os.path.exists(r.log): print('\nAnalysis log text file') print(os.path.basename(r.log)) - print('\nData files') export_indicators(r) # Generate data dictionary print('\nData dictionaries') diff --git a/process/gui.py b/process/gui.py index 3693b82b..ee4118eb 100644 --- a/process/gui.py +++ b/process/gui.py @@ -1082,10 +1082,10 @@ async def get_map_tooltip(): ## Title with ui.column().props('style="max-width: 900px; margin-top: 20px;"'): with ui.row().classes('fixed').style( - 'top: 0px; left: 50%;transform: translateX(-50%);', - ).style('max-width: 900px;'): + 'top: 0px; left: 50%; transform: translateX(-50%); max-width: 900px', + ): ui.label('Global Healthy and Sustainable City Indicators').style( - 'color: #6E93D6; font-size: 200%; font-weight: 300; margin-top: 20px;', + 'color: #6E93D6; font-size: 200%; font-weight: 300; margin-top: 20px', ) ui.button().props('icon=logout outline round ').classes( 'shadow-lg', @@ -1108,7 +1108,7 @@ async def get_map_tooltip(): ## Body map = ( leaflet() - .style('width:100%;height:25rem') + .style('width:100%;height:20rem') .on('click', get_map_tooltip) ) # map.set_no_location(default_location, default_zoom) @@ -1160,7 +1160,7 @@ async def get_map_tooltip(): ) # define and design the panels for the six tabs with ui.tab_panels(tabs, value='Study regions').style( - 'width:100%', + 'width:100%; max-height:80%', ): with ui.tab_panel('Study regions'): region_ui(map, selection) @@ -1179,10 +1179,16 @@ async def get_map_tooltip(): # NOTE on windows reload must be disabled to make asyncio.create_subprocess_exec work (see https://github.com/zauberzeug/nicegui/issues/486) +app.on_startup( + lambda: print( + 'GHSCI app launched for viewing in your web browser at: http://localhost:8080\nPlease wait a few moments for the app to load.', + ), +) ui.run( # reload=platform.system() != 'Windows', reload=False, title='GHSCI', show=False, favicon=r'configuration/assets/favicon.ico', + show_welcome_message=False, ) diff --git a/process/subprocesses/policy_report.py b/process/subprocesses/policy_report.py index 720636a6..ed1bd39f 100644 --- a/process/subprocesses/policy_report.py +++ b/process/subprocesses/policy_report.py @@ -12,7 +12,10 @@ def get_policy_checklist(xlsx) -> dict: try: df = pd.read_excel( - xlsx, sheet_name='Policy Checklist', header=1, usecols='A:N', + xlsx, + sheet_name='Policy Checklist', + header=1, + usecols='A:N', ) df.columns = [ 'Indicators', @@ -41,10 +44,12 @@ def get_policy_checklist(xlsx) -> dict: df.loc[:, 'Indicators'] = df.loc[:, 'Indicators'].ffill() # Only keep measures associated with indicators, replacing 'see also' reference indicators with NA df.loc[:, 'Measures'] = df.apply( - lambda x: x['Measures'] - if x['Indicators'] in policies['Indicators'].keys() - and x['Measures'] in policies['Indicators'][x['Indicators']] - else pd.NA, + lambda x: ( + x['Measures'] + if x['Indicators'] in policies['Indicators'].keys() + and x['Measures'] in policies['Indicators'][x['Indicators']] + else pd.NA + ), axis=1, ) # fill down Measures column values @@ -54,13 +59,15 @@ def get_policy_checklist(xlsx) -> dict: df['qualifier'] = ( df['Principles'] .apply( - lambda x: x.strip() - if ( - str(x).strip() == 'No' - or str(x).strip() == 'Yes' - or str(x).strip() == 'Yes, explicit mention of:' - ) - else pd.NA, + lambda x: ( + x.strip() + if ( + str(x).strip() == 'No' + or str(x).strip() == 'Yes' + or str(x).strip() == 'Yes, explicit mention of:' + ) + else pd.NA + ), ) .ffill() .fillna('') @@ -101,13 +108,16 @@ def get_policy_setting(xlsx) -> dict: df.loc[:, 'item'] = df.loc[:, 'item'].ffill() setting = {} setting['Person(s)'] = df.loc[ - df['item'] == 'Name of person(s) completing checklist:', 'value', + df['item'] == 'Name of person(s) completing checklist:', + 'value', ].values[0] setting['E-mail'] = df.loc[ - df['item'] == 'Email address(es):', 'value', + df['item'] == 'Email address(es):', + 'value', ].values[0] setting['Date'] = df.loc[ - df['item'] == 'Date completed', 'value', + df['item'] == 'Date completed', + 'value', ].values[0] try: setting['Date'] = setting['Date'].strftime('%Y-%m-%d') @@ -118,7 +128,8 @@ def get_policy_setting(xlsx) -> dict: 0 ] setting['Country'] = df.loc[ - df['location'] == 'Country', 'value', + df['location'] == 'Country', + 'value', ].values[0] setting['Levels of Government'] = ( df.loc[ @@ -155,7 +166,8 @@ def get_policy_setting(xlsx) -> dict: 'value', ].values[0] setting['Environmental disaster context']['Other'] = df.loc[ - df['location'] == 'Other (please specify)', 'value', + df['location'] == 'Other (please specify)', + 'value', ].values[0] setting['Environmental disaster context'] = '\n'.join( [ @@ -215,12 +227,16 @@ def header(self): # Printing title: self.set_font('helvetica', 'B', 24) with self.local_context(text_color=(89, 39, 226)): - self.write_html( - f'

{self.location}

', - ) - self.write_html( - 'Policy report

', + self.multi_cell( + w=180, + text=f'\n\n\n{self.location}\nPolicy summary\n', ) + # self.write_html( + # f'

{self.location}

', + # ) + # self.write_html( + # 'Policy report

', + # ) else: # Rendering logo: self.image( @@ -234,7 +250,10 @@ def header(self): with self.local_context(text_color=(89, 39, 226)): self.set_x(38) self.multi_cell( - w=134, txt=self.location, border=0, align='R', + w=134, + txt=self.location, + border=0, + align='R', ) self.set_x(0) self.set_margins(19, 32, 19) @@ -298,12 +317,14 @@ def format_policy_checklist(self, df) -> None: == sections[section]['indicators'][indicator] ] .apply( - lambda x: x.str.strip() - .replace(' ', ' ') - .replace(' ', '') - if x['Measures'] - in policies['Indicators'][x['Indicators']] - else pd.NA, + lambda x: ( + x.str.strip() + .replace(' ', ' ') + .replace(' ', '') + if x['Measures'] + in policies['Indicators'][x['Indicators']] + else pd.NA + ), axis=1, )['Measures'] .ffill() From 9d41eb64151025ae4c755e91b823394103537dfd Mon Sep 17 00:00:00 2001 From: Carl Higgs Date: Wed, 19 Jun 2024 15:28:53 +1000 Subject: [PATCH 2/3] addressed #452 --- process/subprocesses/_utils.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/process/subprocesses/_utils.py b/process/subprocesses/_utils.py index d2e19fb5..f475475f 100644 --- a/process/subprocesses/_utils.py +++ b/process/subprocesses/_utils.py @@ -1998,7 +1998,8 @@ def update_value_if_key_in_template( f'{heading} blurb' ].format(**phrases) else: - template[f'{heading}'] = '' + if phrases[f'{heading} blurb'] == '': + template[f'{heading}'] = '' return template From 806f3fecd60b75841945a883f3a477d339530736 Mon Sep 17 00:00:00 2001 From: Carl Higgs Date: Wed, 19 Jun 2024 16:02:41 +1000 Subject: [PATCH 3/3] made updates to address #453 --- process/subprocesses/ghsci.py | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/process/subprocesses/ghsci.py b/process/subprocesses/ghsci.py index 7da9568e..b405bbc1 100644 --- a/process/subprocesses/ghsci.py +++ b/process/subprocesses/ghsci.py @@ -491,8 +491,12 @@ def _region_dictionary_setup(self, folder_path): 'urban_region', data_path, ) - if r['urban_region'] is None: - return None + if r['urban_region']['data_dir'].startswith('Not required'): + r['urban_query'] = None + if r['study_region_boundary']['data'] == 'urban_query': + sys.exit( + 'Study region has been configured to use the "urban_query" parameter, but urban region data does not appear to have been defined.', + ) r['buffered_urban_study_region'] = buffered_urban_study_region r['db'] = codename.lower() r['dbComment'] = ( @@ -574,6 +578,8 @@ def _region_data_setup( region = region_config['codename'] else: region = self.codename + if data not in region_config: + region_config[data] = None if isinstance(region_config[data], str): if data not in datasets or datasets[data] is None: print( @@ -592,9 +598,7 @@ def _region_data_setup( return None data_dictionary = datasets[data][region_config[data]].copy() else: - if data == 'urban_region' and ( - data not in region_config or region_config[data] is None - ): + if data == 'urban_region' and region_config[data] is None: urban_region_checks = [ self.config['study_region_boundary'][ 'ghsl_urban_intersection' @@ -634,13 +638,15 @@ def _region_data_setup( f"{region}.yml error: The 'data_dir' entry for {data} does not appear to have been defined. This parameter is required for analysis of {region}, and is used to locate a required dataset cross-referenced in {region}.yml. Please check the configured settings before proceeding.", ) return None - if data_path is not None: + if data_path is not None and not data_dictionary[ + 'data_dir' + ].startswith('Not required'): data_dictionary['data_dir'] = ( f"{data_path}/{data_dictionary['data_dir']}" ) return data_dictionary except Exception as e: - sys.exit(e) + sys.exit(f'Data Check error with {data}: {e}') def _population_data_setup(self, r): r['population'] = self._region_data_setup(r, 'population', data_path)