diff --git a/bin/README.md b/bin/README.md new file mode 100644 index 0000000..0e8167e --- /dev/null +++ b/bin/README.md @@ -0,0 +1,5 @@ +# Command-line Interface +Ported from epispot/cli +___ +This part of epispot is currently being ported from epispot/cli \ +Current development status: **in progress** \ No newline at end of file diff --git a/bin/covid.py b/bin/covid.py new file mode 100644 index 0000000..80cec65 --- /dev/null +++ b/bin/covid.py @@ -0,0 +1,110 @@ +from bs4 import BeautifulSoup +import requests +import re + +if __name__ != "__main__": + all_cases = [] + country_cases = [] + continent_cases = [] + last_updated_val = "" + + # Getting the data from https://www.worldometers.info/coronavirus/ via web scraping. + source = requests.get("https://www.worldometers.info/coronavirus/").text + soup = BeautifulSoup(source, "lxml") + + # Get last updated date + for page_top in soup.find_all("div", id="page-top"): + last_updated = page_top.find_next_sibling("div") + last_updated_val = last_updated.text.strip("Last updated: ") + + # Getting the cases and then added to all_cases list of dictionary + th_row = [] + table = soup.table + table_rows = table.find_all("tr") + for tr in table_rows: + th = tr.find_all("th") + td = tr.find_all("td") + if th: + for i in th: + th_text = i.text + if re.search("Country", th_text): + th_row.append("CountryOrRegion") + elif re.search("Critical", th_text): + th_row.append("Critical") + elif re.search("Cases/", th_text): + th_row.append("CasesPerOneMillion") + elif re.search("Deaths/", th_text): + th_row.append("DeathsPerOneMillion") + elif re.search("Tests/", th_text): + th_row.append("TestsPerOneMillion") + else: + th_row.append(th_text) + th_row.append("LastUpdated") + if td: + td_row = [j.text.strip() for j in td] + td_row.append(last_updated_val) + table_dict = dict(zip(th_row, td_row)) + all_cases.append(table_dict) + + # Filtering the list and create new list just for the Coutries and Continents + region_remover = ["World", "Asia", "North America", "Europe", + "South America", "Oceania", "Africa", "Total:", ""] + regions = ["Asia", "North America", "Europe", + "South America", "Oceania", "Africa"] + + for search in all_cases: + if search["CountryOrRegion"] not in region_remover: + country_cases.append(search) + if search["CountryOrRegion"] in regions: + continent_cases.append(search) + + # Methods + def get_global_cases(): + """ + Returns a dictionary of global cases + """ + result = None + for search in all_cases: + if search["CountryOrRegion"] == "World": + result = search + return result + + def get_country_cases(country=None): + """ + Returns a dictionary for sepecific country or list of dictionaries for all coutries. + + Parameter: + country = Name of a Country that has COVID-19 case. Will return None if country is not available. + """ + result = None + if country: + if not type(country) is str: + raise TypeError("Parameter is not a string!") + else: + if country.upper() == "South Korea".upper(): + country = "S. Korea".upper() + for country_search in country_cases: + if country_search["CountryOrRegion"].upper() == country.upper(): + result = country_search + return result + else: + return country_cases + + def get_continent_cases(continent=None): + """ + Returns a dictionary for sepecific continent or list of dictionaries for all continents. + + Parameter: + continent = Name of the continent that has COVID-19 case. Will return None if continent is not available. + """ + result = None + if continent: + if not type(continent) is str: + raise TypeError("Parameter is not a string!") + else: + for continent_search in continent_cases: + if continent_search["Continent"].upper() == continent.upper(): + result = continent_search + return result + else: + return continent_cases diff --git a/bin/data/list-of-countries.csv b/bin/data/list-of-countries.csv new file mode 100644 index 0000000..c8cf4bb --- /dev/null +++ b/bin/data/list-of-countries.csv @@ -0,0 +1,232 @@ +Zimbabwe +Zambia +Yemen +World +Western Sahara +Wallis and Futuna +Vietnam +Venezuela +Vatican City +Vanuatu +Uzbekistan +USA +Uruguay +Ukraine +UK +Uganda +UAE +Turks and Caicos +Turkey +Tunisia +Trinidad and Tobago +Togo +Timor-Leste +Thailand +Tanzania +Tajikistan +Taiwan +Syria +Switzerland +Sweden +Suriname +Sudan +St. Vincent Grenadines +St. Barth +Sri Lanka +Spain +South Sudan +South Africa +Somalia +Solomon Islands +Slovenia +Slovakia +Sint Maarten +Singapore +Sierra Leone +Seychelles +Serbia +Senegal +Saudi Arabia +Sao Tome and Principe +San Marino +Samoa +Saint Pierre Miquelon +Saint Martin +Saint Lucia +Saint Kitts and Nevis +S. Korea +Réunion +Rwanda +Russia +Romania +Qatar +Portugal +Poland +Philippines +Peru +Paraguay +Papua New Guinea +Panama +Palestine +Pakistan +Oman +Norway +North Macedonia +Nigeria +Niger +Nicaragua +New Zealand +New Caledonia +Netherlands +Nepal +Namibia +Myanmar +MS Zaandam +Mozambique +Morocco +Montserrat +Montenegro +Mongolia +Monaco +Moldova +Micronesia +Mexico +Mayotte +Mauritius +Mauritania +Martinique +Marshall Islands +Malta +Mali +Maldives +Malaysia +Malawi +Madagascar +Macao +Luxembourg +Lithuania +Liechtenstein +Libya +Liberia +Lesotho +Lebanon +Latvia +Laos +Kyrgyzstan +Kuwait +Kenya +Kazakhstan +Jordan +Japan +Jamaica +Ivory Coast +Italy +Israel +Isle of Man +Ireland +Iraq +Iran +Indonesia +India +Iceland +Hungary +Hong Kong +Honduras +Haiti +Guyana +Guinea-Bissau +Guinea +Guatemala +Guadeloupe +Grenada +Greenland +Greece +Gibraltar +Ghana +Germany +Georgia +Gambia +Gabon +French Polynesia +French Guiana +France +Finland +Fiji +Falkland Islands +Faeroe Islands +Ethiopia +Eswatini +Estonia +Eritrea +Equatorial Guinea +El Salvador +Egypt +Ecuador +DRC +Dominican Republic +Dominica +Djibouti +Diamond Princess +Denmark +Czechia +Cyprus +Curaçao +Cuba +Croatia +Costa Rica +Congo +Comoros +Colombia +China +Chile +Channel Islands +Chad +Cayman Islands +Caribbean Netherlands +CAR +Canada +Cameroon +Cambodia +Cabo Verde +Burundi +Burkina Faso +Bulgaria +Brunei +British Virgin Islands +Brazil +Botswana +Bosnia and Herzegovina +Bolivia +Bhutan +Bermuda +Benin +Belize +Belgium +Belarus +Barbados +Bangladesh +Bahrain +Bahamas +Azerbaijan +Austria +Australia +Aruba +Armenia +Argentina +Antigua and Barbuda +Anguilla +Angola +Andorra +Algeria +Albania +Afghanistan +The Arctic +The Caribbean +The Mediterranean +Eastern Europe +The Sahara +North Africa +The Horn of Africa +The Middle East +Southeast Asia +The Tundra diff --git a/bin/data/regions.csv b/bin/data/regions.csv new file mode 100644 index 0000000..bd852d0 --- /dev/null +++ b/bin/data/regions.csv @@ -0,0 +1,10 @@ +The Arctic,Denmark,Norway,Sweden,Finland,Russia,Canada,Iceland +The Caribbean,Bahamas,Barbados,Belize,Dominican Republic,Guyana,Haiti,Jamaica,Antigua and Barbuda,Trinidad and Tobago +The Mediterranean,Portugal,Spain,Italy,Greece,Turkey,Morocco,Algeria,Tunisia,Libya,Egypt +Eastern Europe,Finland,Belarus,Ukraine,Romania,Bulgaria,Latvia,Lithuania +The Sahara,Algeria,Libya,Egypt,Mauritiania,Mali,Niger,Chad,Sudan,South Sudan +North Africa,Algeria,Libya,Egypt,Mauritiania,Mali,Niger,Chad,Sudan,South Sudan +The Horn of Africa,Eritrea,Ethiopia,Djibouti,Somalia,Kenya,Sudan,South Sudan +The Middle East,Yemen,Oman,Saudi Arabia,Iraq,Syria,Jordan,Israel +Southeast Asia,Myanmar,Thailand,Cambodia,Vietnam,Laos +The Tundra,Denmark,Norway,Sweden,Finland,Russia,Canada,Iceland \ No newline at end of file diff --git a/bin/epi.py b/bin/epi.py new file mode 100644 index 0000000..41d7e10 --- /dev/null +++ b/bin/epi.py @@ -0,0 +1,246 @@ +""" +Run the epispot package faster with high-level commands +Uses fire to initialize a CLI +""" + +print('Initializing CLI ...') +import epispot as epi +try: + import fire +except ModuleNotFoundError as E: + errormsg = f'Please install fire via `pip install fire` in order to initialize a CLI.' + raise ModuleNotFoundError(errormsg) from E + +print('Preparing job ...') +import covid +import numpy as np +from matplotlib import pyplot as plt + + +""" +CLI functions +""" + + +def base(): + """ + Basic package information + """ + + print('\nPackage metadata: ') + print('Version: ' + epi.__version__) + print('\nFor more detailed package information, see `credits`') + + +def sources(): + """ + List credits for epi-cli contributions + """ + + print('\nepi-cli credits: ') + print(' - The epi-cli would not have been possible without the many people who have contributed to the epispot ' + 'organization on Github in order to end COVID-19.') + print(' - The epispot organization on Github: https://www.github.com/epispot') + print(' - COVID-19 data is gathered using the COVID-19-Data API along with a regions file to map certain regions ' + 'to countries. If your country or region is missing, submit an issue on Github.') + print(' COVID-19-Data API: https://github.com/jrclarete/COVID-19-Cases') + print(' - Lastly, the Fire package was used to initialize the epi-cli: ') + print(' - Fire: https://www.github.com/google/python-fire') + print(' - The main Medium blog posts that inspired the epispot package are linked below:') + print(' - https://towardsdatascience.com/infectious-disease-modelling-part-i-understanding-sir-28d60e29fdfc') + print(' - https://q9i.medium.com/reopening-safely-the-data-science-approach-289fd86ef63') + print(' - No docs have been posted at this time. Use --help to get command info.') + + +def regions(): + """ + List all available regions + """ + + print('Compiling list of regions ...') + country_list = open('data/list-of-countries.csv', 'r').readlines() + country_list = [country.replace('\n', '') for country in country_list] + + print('\nList of regions: ') + print(', '.join(country_list)) + + +def find(abbr): + """ + Finds region by abbreviation + For example: + Bra for Brazil + CR for Costa Rica + tm for The Mediterranean + Not case-sensitive + """ + + print('Loading list of regions ...') + country_list = open('data/list-of-countries.csv').readlines() + matches = [] + + print('Searching for region ...') + for country in country_list: + + country_words = country.split(' ') + + if len(country_words) > 1: + + country_abbr = [] + for word in country_words: + country_abbr.append(list(word)[0]) + + country_abbr = ''.join(country_abbr) + if country_abbr.lower() == abbr.lower(): + print('') + return country.replace('\n', '') + + else: + + country_letters = list(country.replace('\n', '')) + string_letters = list(abbr) + num_matches = 0 + + for letter in range(0, min(len(country_letters), len(string_letters))): + + if country_letters[letter].lower() == string_letters[letter].lower(): + num_matches += 1 + else: + break + + if num_matches != 0: + matches.append((country.replace('\n', ''), num_matches)) + + print('Comparing matches ...\n') + max_matching = ('[Country]', 0) + for match in matches: + if match[1] > max_matching[1]: + max_matching = match + + return max_matching[0] + + +# non-CLI +def out(region, case_info): + """ + Output for a region with case_info + """ + + print(region + ' has ' + case_info['TotalCases'] + ' total confirmed cases') + + if case_info['NewCases'] != '': + print(' ' + region + ' has ' + case_info['NewCases'] + ' new cases today') + if case_info['TotalDeaths'] != '': + print(' ' + region + ' has ' + case_info['TotalDeaths'] + ' total deaths') + if case_info['NewDeaths'] != '': + print(' ' + region + ' has ' + case_info['NewDeaths'] + ' new deaths today') + if case_info['TotalRecovered'] != '': + print(' ' + region + ' has ' + case_info['TotalRecovered'] + ' total recovered patients') + if case_info['NewRecovered'] != '': + print(' ' + region + ' has ' + case_info['NewRecovered'] + ' new recovered patients today') + if case_info['ActiveCases'] != '': + print(' ' + region + ' has ' + case_info['ActiveCases'] + ' estimated active cases') + if case_info['Critical'] != '': + print(' ' + region + ' has ' + case_info['Critical'] + ' patients in critical condition') + if case_info['CasesPerOneMillion'] != '': + print(' ' + region + ' has ' + case_info['CasesPerOneMillion'] + ' cases per 1M') + if case_info['DeathsPerOneMillion'] != '': + print(' ' + region + ' has ' + case_info['DeathsPerOneMillion'] + ' deaths per 1M') + if case_info['TotalTests'] != '': + print(' ' + region + ' has ' + case_info['TotalTests'] + ' total tests') + if case_info['TestsPerOneMillion'] != '': + print(' ' + region + ' has ' + case_info['TestsPerOneMillion'] + ' total tests per 1M') + + print('For reference, other statistics and notes are displayed below') + if case_info['Population'] != '': + print(' ' + region + ' has ' + case_info['Population'] + ' people') + if case_info['Continent'] != '': + print(' ' + region + ' is located in ' + case_info['Continent']) + if case_info['LastUpdated'] != '': + print(' Data for ' + region + ' was last updated as of ' + case_info['LastUpdated']) + + +def covid19(region='world'): + """ + Get case data for a specific region or continent + Also includes UN-distinguished areas such as the Holy See + Includes many cruise ships that were once quarantined + """ + + print('Loading regions file ...') + regions = open('data/regions.csv').readlines() + regions_list = [line.split(',')[0] for line in regions] + + print('Identifying region type ...') + if region != 'world': + case_info = covid.get_country_cases(country=region) + + if region == 'world': + + print('Preparing statistics ...') + case_info = covid.get_global_cases() + print('') + out(region, case_info) + + elif region in ['North America', 'South America', 'Europe', 'Africa', 'Asia', 'Australia']: + + print('Preparing statistics ...') + case_info = covid.get_continent_cases(continent=region) + print('') + out(region, case_info) + + elif case_info is not None: + + print('') + out(region, case_info) + + elif region in regions_list: + + print('Searching for region ...') + line_num = 0 + for line in range(len(regions)): + if region == regions[line].split(',')[0]: + line_num = line + + print('Preparing statistics ...') + region_countries = regions[line_num].split(',') + total_cases = 0 + exceptions = 0 + exception_cases = [] + + print('Locating region ...') + for region_region in region_countries[1:]: + case_info = covid.get_country_cases(country=region_region.replace('\n', '')) + if case_info is not None: + total_cases += int(case_info['TotalCases'].replace(',', '')) + else: + exceptions += 1 + exception_cases.append(region_region) + + print('') + print(region + ' has ' + str(total_cases) + ' cases') + if exceptions != 0: + print('This search raised ' + str(exceptions) + ' exceptions for the following countries') + print(exception_cases) + + else: + + print('Region not found ...') + print('Searching for an abbreviation ...') + + guess = find(region) + print('Here are results for ' + guess + ':') + case_info = covid.get_country_cases(country=guess) + + if case_info: + out(guess, case_info) + else: + print("Sorry, you can't use regional abbreviations just yet ...") + + +""" +Call CLI +""" + +if __name__ == '__main__': + fire.Fire()