diff --git a/app/database/models.py b/app/database/models.py index 3427c74f..7e638d07 100644 --- a/app/database/models.py +++ b/app/database/models.py @@ -464,6 +464,14 @@ class Quote(Base): author = Column(String) +class Country(Base): + __tablename__ = "countries" + + id = Column(Integer, primary_key=True, index=True) + name = Column(String, nullable=False, unique=True) + timezone = Column(String, nullable=False) + + class Comment(Base): __tablename__ = "comments" diff --git a/app/internal/event.py b/app/internal/event.py index 962f48fb..c8867499 100644 --- a/app/internal/event.py +++ b/app/internal/event.py @@ -1,3 +1,4 @@ +import functools import logging import re from typing import List, NamedTuple, Set, Union @@ -11,7 +12,8 @@ from sqlalchemy.orm import Session from starlette.status import HTTP_400_BAD_REQUEST -from app.database.models import Event +from app.database.models import Country, Event +from app.resources.countries import countries ZOOM_REGEX = re.compile(r"https://.*?\.zoom.us/[a-z]/.[^.,\b\s]+") @@ -114,6 +116,43 @@ def get_messages( return messages +def add_countries_to_db(session: Session) -> None: + """ + Adding all new countries to the "Country" table in the database. + Information is based on the "countries" list. + (The list is located in app/resources/countries.py) + Names are described either as: + "Country Name, City Name" or + "Country Name" solely. + Timezones are described as "Continent/ City Name" + for example: + name: Israel, Jerusalem + timezone: Asia/Jerusalem + """ + for country in countries: + partial_name = country["name"] + for capital_city in country["timezones"]: + capital_city_name = capital_city.split("/")[-1] + if partial_name != capital_city_name: + name = partial_name + ", " + capital_city_name + else: + name = capital_city_name + new_country = Country(name=name, timezone=str(capital_city)) + session.merge(new_country) + session.commit() + + +@functools.lru_cache +def get_all_countries_names(session: Session) -> List[str]: + """ + Returns a cached list of the countries names. + """ + db_entity = session.query(Country).first() + if not db_entity: + add_countries_to_db(session=session) + return session.query(Country.name).all() + + async def get_location_coordinates( address: str, ) -> Union[Location, str]: diff --git a/app/resources/countries.py b/app/resources/countries.py new file mode 100644 index 00000000..a50f0ff7 --- /dev/null +++ b/app/resources/countries.py @@ -0,0 +1,639 @@ +# List was taken from: +# https://gist.github.com/stasius12/f95f2999fa351212991c43a5f067c78d + +countries = [ + {'timezones': ['Asia/Kabul'], + 'code': 'AF', 'name': 'Afghanistan'}, + {'timezones': ['Europe/Mariehamn'], + 'code': 'AX', 'name': 'Aland Islands'}, + {'timezones': ['Europe/Tirane'], + 'code': 'AL', 'name': 'Albania'}, + {'timezones': ['Africa/Algiers'], + 'code': 'DZ', 'name': 'Algeria'}, + {'timezones': ['Pacific/Pago_Pago'], + 'code': 'AS', 'name': 'American Samoa'}, + {'timezones': ['Europe/Andorra'], + 'code': 'AD', 'name': 'Andorra'}, + {'timezones': ['Africa/Luanda'], + 'code': 'AO', 'name': 'Angola'}, + {'timezones': ['America/Anguilla'], + 'code': 'AI', 'name': 'Anguilla'}, + {'timezones': [ + 'Antarctica/Casey', + 'Antarctica/Davis', 'Antarctica/DumontDUrville', + 'Antarctica/Mawson', 'Antarctica/McMurdo', + 'Antarctica/Palmer', 'Antarctica/Rothera', + 'Antarctica/Syowa', 'Antarctica/Troll', + 'Antarctica/Vostok'], + 'code': 'AQ', + 'name': 'Antarctica'}, + {'timezones': ['America/Antigua'], + 'code': 'AG', 'name': 'Antigua and Barbuda'}, + {'timezones': [ + 'America/Argentina/Buenos_Aires', + 'America/Argentina/Catamarca', + 'America/Argentina/Cordoba', + 'America/Argentina/Jujuy', + 'America/Argentina/La_Rioja', + 'America/Argentina/Mendoza', + 'America/Argentina/Rio_Gallegos', + 'America/Argentina/Salta', + 'America/Argentina/San_Juan', + 'America/Argentina/San_Luis', + 'America/Argentina/Tucuman', + 'America/Argentina/Ushuaia'], + 'code': 'AR', 'name': 'Argentina'}, + {'timezones': ['Asia/Yerevan'], + 'code': 'AM', 'name': 'Armenia'}, + {'timezones': ['America/Aruba'], + 'code': 'AW', 'name': 'Aruba'}, + {'timezones': [ + 'Antarctica/Macquarie', + 'Australia/Adelaide', 'Australia/Brisbane', + 'Australia/Broken_Hill', 'Australia/Currie', + 'Australia/Darwin', 'Australia/Eucla', + 'Australia/Hobart', 'Australia/Lindeman', + 'Australia/Lord_Howe', 'Australia/Melbourne', + 'Australia/Perth', 'Australia/Sydney'], + 'code': 'AU', 'name': 'Australia'}, + {'timezones': ['Europe/Vienna'], + 'code': 'AT', 'name': 'Austria'}, + {'timezones': ['Asia/Baku'], + 'code': 'AZ', 'name': 'Azerbaijan'}, + {'timezones': ['America/Nassau'], + 'code': 'BS', 'name': 'Bahamas'}, + {'timezones': ['Asia/Bahrain'], + 'code': 'BH', 'name': 'Bahrain'}, + {'timezones': ['Asia/Dhaka'], + 'code': 'BD', 'name': 'Bangladesh'}, + {'timezones': ['America/Barbados'], + 'code': 'BB', 'name': 'Barbados'}, + {'timezones': ['Europe/Minsk'], + 'code': 'BY', 'name': 'Belarus'}, + {'timezones': ['Europe/Brussels'], + 'code': 'BE', 'name': 'Belgium'}, + {'timezones': ['America/Belize'], + 'code': 'BZ', 'name': 'Belize'}, + {'timezones': ['Africa/Porto-Novo'], + 'code': 'BJ', 'name': 'Benin'}, + {'timezones': ['Atlantic/Bermuda'], + 'code': 'BM', 'name': 'Bermuda'}, + {'timezones': ['Asia/Thimphu'], + 'code': 'BT', 'name': 'Bhutan'}, + {'timezones': ['America/La_Paz'], + 'code': 'BO', 'name': 'Bolivia'}, + {'timezones': ['America/Kralendijk'], + 'code': 'BQ', 'name': 'Bonaire, Saint Eustatius and Saba '}, + {'timezones': ['Europe/Sarajevo'], + 'code': 'BA', 'name': 'Bosnia and Herzegovina'}, + {'timezones': ['Africa/Gaborone'], + 'code': 'BW', 'name': 'Botswana'}, + {'timezones': [ + 'America/Araguaina', + 'America/Bahia', 'America/Belem', + 'America/Boa_Vista', 'America/Campo_Grande', + 'America/Cuiaba', 'America/Eirunepe', + 'America/Fortaleza', 'America/Maceio', + 'America/Manaus', 'America/Noronha', + 'America/Porto_Velho', 'America/Recife', + 'America/Rio_Branco', 'America/Santarem', + 'America/Sao_Paulo'], + 'code': 'BR', 'name': 'Brazil'}, + {'timezones': ['Indian/Chagos'], + 'code': 'IO', 'name': 'British Indian Ocean Territory'}, + {'timezones': ['America/Tortola'], + 'code': 'VG', 'name': 'British Virgin Islands'}, + {'timezones': ['Asia/Brunei'], + 'code': 'BN', 'name': 'Brunei'}, + {'timezones': ['Europe/Sofia'], + 'code': 'BG', 'name': 'Bulgaria'}, + {'timezones': ['Africa/Ouagadougou'], + 'code': 'BF', 'name': 'Burkina Faso'}, + {'timezones': ['Africa/Bujumbura'], + 'code': 'BI', 'name': 'Burundi'}, + {'timezones': ['Asia/Phnom_Penh'], + 'code': 'KH', 'name': 'Cambodia'}, + {'timezones': ['Africa/Douala'], + 'code': 'CM', 'name': 'Cameroon'}, + {'timezones': [ + 'America/Atikokan', + 'America/Blanc-Sablon', 'America/Cambridge_Bay', + 'America/Montreal', 'America/Creston', + 'America/Dawson', 'America/Dawson_Creek', + 'America/Edmonton', 'America/Fort_Nelson', + 'America/Glace_Bay', 'America/Goose_Bay', + 'America/Halifax', 'America/Inuvik', + 'America/Iqaluit', 'America/Moncton', + 'America/Nipigon', 'America/Pangnirtung', + 'America/Rainy_River', 'America/Rankin_Inlet', + 'America/Regina', 'America/Resolute', + 'America/St_Johns', 'America/Swift_Current', + 'America/Thunder_Bay', 'America/Toronto', + 'America/Vancouver', 'America/Whitehorse', + 'America/Winnipeg', 'America/Yellowknife'], + 'code': 'CA', 'name': 'Canada'}, + {'timezones': ['Atlantic/Cape_Verde'], + 'code': 'CV', 'name': 'Cape Verde'}, + {'timezones': ['America/Cayman'], + 'code': 'KY', 'name': 'Cayman Islands'}, + {'timezones': ['Africa/Bangui'], + 'code': 'CF', 'name': 'Central African Republic'}, + {'timezones': ['Africa/Ndjamena'], + 'code': 'TD', 'name': 'Chad'}, + {'timezones': [ + 'America/Punta_Arenas', + 'America/Santiago', 'Pacific/Easter'], + 'code': 'CL', 'name': 'Chile'}, + {'timezones': [ + 'Asia/Shanghai', + 'Asia/Urumqi', 'Asia/Chungking', + 'Asia/Chongqing'], + 'code': 'CN', 'name': 'China'}, + {'timezones': ['Indian/Christmas'], + 'code': 'CX', 'name': 'Christmas Island'}, + {'timezones': ['Indian/Cocos'], + 'code': 'CC', 'name': 'Cocos Islands'}, + {'timezones': ['America/Bogota'], + 'code': 'CO', 'name': 'Colombia'}, + {'timezones': ['Indian/Comoro'], + 'code': 'KM', 'name': 'Comoros'}, + {'timezones': ['Pacific/Rarotonga'], + 'code': 'CK', 'name': 'Cook Islands'}, + {'timezones': ['America/Costa_Rica'], + 'code': 'CR', 'name': 'Costa Rica'}, + {'timezones': ['Europe/Zagreb'], + 'code': 'HR', 'name': 'Croatia'}, + {'timezones': ['America/Havana'], + 'code': 'CU', 'name': 'Cuba'}, + {'timezones': ['America/Curacao'], + 'code': 'CW', 'name': 'Curacao'}, + {'timezones': [ + 'Asia/Famagusta', + 'Asia/Nicosia'], + 'code': 'CY', 'name': 'Cyprus'}, + {'timezones': ['Europe/Prague'], + 'code': 'CZ', 'name': 'Czech Republic'}, + {'timezones': [ + 'Africa/Kinshasa', + 'Africa/Lubumbashi'], + 'code': 'CD', + 'name': 'Democratic Republic of the Congo'}, + {'timezones': ['Europe/Copenhagen'], + 'code': 'DK', 'name': 'Denmark'}, + {'timezones': ['Africa/Djibouti'], + 'code': 'DJ', 'name': 'Djibouti'}, + {'timezones': ['America/Dominica'], + 'code': 'DM', 'name': 'Dominica'}, + {'timezones': ['America/Santo_Domingo'], + 'code': 'DO', 'name': 'Dominican Republic'}, + {'timezones': ['Asia/Dili'], + 'code': 'TL', 'name': 'East Timor'}, + {'timezones': [ + 'America/Guayaquil', + 'Pacific/Galapagos'], + 'code': 'EC', + 'name': 'Ecuador'}, + {'timezones': ['Africa/Cairo'], + 'code': 'EG', 'name': 'Egypt'}, + {'timezones': ['America/El_Salvador'], + 'code': 'SV', 'name': 'El Salvador'}, + {'timezones': ['Africa/Malabo'], + 'code': 'GQ', 'name': 'Equatorial Guinea'}, + {'timezones': ['Africa/Asmara'], + 'code': 'ER', 'name': 'Eritrea'}, + {'timezones': ['Europe/Tallinn'], + 'code': 'EE', 'name': 'Estonia'}, + {'timezones': ['Africa/Addis_Ababa'], + 'code': 'ET', 'name': 'Ethiopia'}, + {'timezones': ['Atlantic/Stanley'], + 'code': 'FK', 'name': 'Falkland Islands'}, + {'timezones': ['Atlantic/Faroe'], + 'code': 'FO', 'name': 'Faroe Islands'}, + {'timezones': ['Pacific/Fiji'], + 'code': 'FJ', 'name': 'Fiji'}, + {'timezones': ['Europe/Helsinki'], + 'code': 'FI', 'name': 'Finland'}, + {'timezones': ['Europe/Paris'], + 'code': 'FR', 'name': 'France'}, + {'timezones': ['America/Cayenne'], + 'code': 'GF', 'name': 'French Guiana'}, + {'timezones': [ + 'Pacific/Gambier', + 'Pacific/Marquesas', 'Pacific/Tahiti'], + 'code': 'PF', 'name': 'French Polynesia'}, + {'timezones': ['Indian/Kerguelen'], + 'code': 'TF', 'name': 'French Southern Territories'}, + {'timezones': ['Africa/Libreville'], + 'code': 'GA', 'name': 'Gabon'}, + {'timezones': ['Africa/Banjul'], + 'code': 'GM', 'name': 'Gambia'}, + {'timezones': ['Asia/Tbilisi'], + 'code': 'GE', 'name': 'Georgia'}, + {'timezones': [ + 'Europe/Berlin', + 'Europe/Busingen'], + 'code': 'DE', 'name': 'Germany'}, + {'timezones': ['Africa/Accra'], + 'code': 'GH', 'name': 'Ghana'}, + {'timezones': ['Europe/Gibraltar'], + 'code': 'GI', 'name': 'Gibraltar'}, + {'timezones': ['Europe/Athens'], + 'code': 'GR', 'name': 'Greece'}, + {'timezones': [ + 'America/Danmarkshavn', + 'America/Godthab', 'America/Scoresbysund', + 'America/Thule'], + 'code': 'GL', 'name': 'Greenland'}, + {'timezones': ['America/Grenada'], + 'code': 'GD', 'name': 'Grenada'}, + {'timezones': ['America/Guadeloupe'], + 'code': 'GP', 'name': 'Guadeloupe'}, + {'timezones': ['Pacific/Guam'], + 'code': 'GU', 'name': 'Guam'}, + {'timezones': ['America/Guatemala'], + 'code': 'GT', 'name': 'Guatemala'}, + {'timezones': ['Europe/Guernsey'], + 'code': 'GG', 'name': 'Guernsey'}, + {'timezones': ['Africa/Conakry'], + 'code': 'GN', 'name': 'Guinea'}, + {'timezones': ['Africa/Bissau'], + 'code': 'GW', 'name': 'Guinea-Bissau'}, + {'timezones': ['America/Guyana'], + 'code': 'GY', 'name': 'Guyana'}, + {'timezones': ['America/Port-au-Prince'], + 'code': 'HT', 'name': 'Haiti'}, + {'timezones': ['America/Tegucigalpa'], + 'code': 'HN', 'name': 'Honduras'}, + {'timezones': ['Asia/Hong_Kong'], + 'code': 'HK', 'name': 'Hong Kong'}, + {'timezones': ['Europe/Budapest'], + 'code': 'HU', 'name': 'Hungary'}, + {'timezones': ['Atlantic/Reykjavik'], + 'code': 'IS', 'name': 'Iceland'}, + {'timezones': [ + 'Asia/Kolkata', + 'Asia/Calcutta'], + 'code': 'IN', 'name': 'India'}, + {'timezones': [ + 'Asia/Jakarta', + 'Asia/Jayapura', 'Asia/Makassar', + 'Asia/Pontianak'], + 'code': 'ID', 'name': 'Indonesia'}, + {'timezones': ['Asia/Tehran'], + 'code': 'IR', 'name': 'Iran'}, + {'timezones': ['Asia/Baghdad'], + 'code': 'IQ', 'name': 'Iraq'}, + {'timezones': ['Europe/Dublin'], + 'code': 'IE', 'name': 'Ireland'}, + {'timezones': ['Europe/Isle_of_Man'], + 'code': 'IM', 'name': 'Isle of Man'}, + {'timezones': ['Asia/Jerusalem'], + 'code': 'IL', 'name': 'Israel'}, + {'timezones': ['Europe/Rome'], + 'code': 'IT', 'name': 'Italy'}, + {'timezones': ['Africa/Abidjan'], + 'code': 'CI', 'name': 'Ivory Coast'}, + {'timezones': ['America/Jamaica'], + 'code': 'JM', 'name': 'Jamaica'}, + {'timezones': ['Asia/Tokyo'], + 'code': 'JP', 'name': 'Japan'}, + {'timezones': ['Europe/Jersey'], + 'code': 'JE', 'name': 'Jersey'}, + {'timezones': ['Asia/Amman'], + 'code': 'JO', 'name': 'Jordan'}, + {'timezones': [ + 'Asia/Almaty', + 'Asia/Aqtau', 'Asia/Aqtobe', + 'Asia/Atyrau', 'Asia/Oral', + 'Asia/Qyzylorda'], + 'code': 'KZ', + 'name': 'Kazakhstan'}, + {'timezones': ['Africa/Nairobi'], + 'code': 'KE', 'name': 'Kenya'}, + {'timezones': [ + 'Pacific/Enderbury', + 'Pacific/Kiritimati', 'Pacific/Tarawa'], + 'code': 'KI', 'name': 'Kiribati'}, + {'timezones': ['Asia/Kuwait'], + 'code': 'KW', 'name': 'Kuwait'}, + {'timezones': ['Asia/Bishkek'], + 'code': 'KG', 'name': 'Kyrgyzstan'}, + {'timezones': ['Asia/Vientiane'], + 'code': 'LA', 'name': 'Laos'}, + {'timezones': ['Europe/Riga'], + 'code': 'LV', 'name': 'Latvia'}, + {'timezones': ['Asia/Beirut'], + 'code': 'LB', 'name': 'Lebanon'}, + {'timezones': ['Africa/Maseru'], + 'code': 'LS', 'name': 'Lesotho'}, + {'timezones': ['Africa/Monrovia'], + 'code': 'LR', 'name': 'Liberia'}, + {'timezones': ['Africa/Tripoli'], + 'code': 'LY', 'name': 'Libya'}, + {'timezones': ['Europe/Vaduz'], + 'code': 'LI', 'name': 'Liechtenstein'}, + {'timezones': ['Europe/Vilnius'], + 'code': 'LT', 'name': 'Lithuania'}, + {'timezones': ['Europe/Luxembourg'], + 'code': 'LU', 'name': 'Luxembourg'}, + {'timezones': ['Asia/Macau'], + 'code': 'MO', 'name': 'Macao'}, + {'timezones': ['Europe/Skopje'], + 'code': 'MK', 'name': 'Macedonia'}, + {'timezones': ['Indian/Antananarivo'], + 'code': 'MG', 'name': 'Madagascar'}, + {'timezones': ['Africa/Blantyre'], + 'code': 'MW', 'name': 'Malawi'}, + {'timezones': [ + 'Asia/Kuala_Lumpur', + 'Asia/Kuching'], + 'code': 'MY', 'name': 'Malaysia'}, + {'timezones': ['Indian/Maldives'], + 'code': 'MV', 'name': 'Maldives'}, + {'timezones': ['Africa/Bamako'], + 'code': 'ML', 'name': 'Mali'}, + {'timezones': ['Europe/Malta'], + 'code': 'MT', 'name': 'Malta'}, + {'timezones': [ + 'Pacific/Kwajalein', + 'Pacific/Majuro'], + 'code': 'MH', + 'name': 'Marshall Islands'}, + {'timezones': ['America/Martinique'], + 'code': 'MQ', 'name': 'Martinique'}, + {'timezones': ['Africa/Nouakchott'], + 'code': 'MR', 'name': 'Mauritania'}, + {'timezones': ['Indian/Mauritius'], + 'code': 'MU', 'name': 'Mauritius'}, + {'timezones': ['Indian/Mayotte'], + 'code': 'YT', 'name': 'Mayotte'}, + {'timezones': [ + 'America/Bahia_Banderas', + 'America/Cancun', 'America/Chihuahua', + 'America/Hermosillo', 'America/Matamoros', + 'America/Mazatlan', 'America/Merida', + 'America/Mexico_City', 'America/Monterrey', + 'America/Ojinaga', 'America/Tijuana'], + 'code': 'MX', 'name': 'Mexico'}, + {'timezones': [ + 'Pacific/Chuuk', + 'Pacific/Kosrae', 'Pacific/Pohnpei'], + 'code': 'FM', 'name': 'Micronesia'}, + {'timezones': ['Europe/Chisinau'], + 'code': 'MD', 'name': 'Moldova'}, + {'timezones': ['Europe/Monaco'], + 'code': 'MC', 'name': 'Monaco'}, + {'timezones': [ + 'Asia/Choibalsan', + 'Asia/Hovd', 'Asia/Ulaanbaatar'], + 'code': 'MN', 'name': 'Mongolia'}, + {'timezones': ['Europe/Podgorica'], + 'code': 'ME', 'name': 'Montenegro'}, + {'timezones': ['America/Montserrat'], + 'code': 'MS', 'name': 'Montserrat'}, + {'timezones': ['Africa/Casablanca'], + 'code': 'MA', 'name': 'Morocco'}, + {'timezones': ['Africa/Maputo'], + 'code': 'MZ', 'name': 'Mozambique'}, + {'timezones': ['Africa/Windhoek'], + 'code': 'NA', 'name': 'Namibia'}, + {'timezones': ['Pacific/Nauru'], + 'code': 'NR', 'name': 'Nauru'}, + {'timezones': [ + 'Asia/Kathmandu', + 'Asia/Katmandu'], + 'code': 'NP', + 'name': 'Nepal'}, + {'timezones': ['Europe/Amsterdam'], + 'code': 'NL', 'name': 'Netherlands'}, + {'timezones': ['Pacific/Noumea'], + 'code': 'NC', 'name': 'New Caledonia'}, + {'timezones': [ + 'Pacific/Auckland', + 'Pacific/Chatham'], + 'code': 'NZ', + 'name': 'New Zealand'}, + {'timezones': ['America/Managua'], + 'code': 'NI', 'name': 'Nicaragua'}, + {'timezones': ['Africa/Niamey'], + 'code': 'NE', 'name': 'Niger'}, + {'timezones': ['Africa/Lagos'], + 'code': 'NG', 'name': 'Nigeria'}, + {'timezones': ['Pacific/Niue'], + 'code': 'NU', 'name': 'Niue'}, + {'timezones': ['Pacific/Norfolk'], + 'code': 'NF', 'name': 'Norfolk Island'}, + {'timezones': ['Asia/Pyongyang'], + 'code': 'KP', 'name': 'North Korea'}, + {'timezones': ['Pacific/Saipan'], + 'code': 'MP', 'name': 'Northern Mariana Islands'}, + {'timezones': ['Europe/Oslo'], + 'code': 'NO', 'name': 'Norway'}, + {'timezones': ['Asia/Muscat'], + 'code': 'OM', 'name': 'Oman'}, + {'timezones': ['Asia/Karachi'], + 'code': 'PK', 'name': 'Pakistan'}, + {'timezones': ['Pacific/Palau'], + 'code': 'PW', 'name': 'Palau'}, + {'timezones': ['Asia/Gaza', 'Asia/Hebron'], + 'code': 'PS', 'name': 'Palestinian Territory'}, + {'timezones': ['America/Panama'], + 'code': 'PA', 'name': 'Panama'}, + {'timezones': [ + 'Pacific/Bougainville', + 'Pacific/Port_Moresby'], + 'code': 'PG', 'name': 'Papua New Guinea'}, + {'timezones': ['America/Asuncion'], + 'code': 'PY', 'name': 'Paraguay'}, + {'timezones': ['America/Lima'], + 'code': 'PE', 'name': 'Peru'}, + {'timezones': ['Asia/Manila'], + 'code': 'PH', 'name': 'Philippines'}, + {'timezones': ['Pacific/Pitcairn'], + 'code': 'PN', 'name': 'Pitcairn'}, + {'timezones': ['Europe/Warsaw'], + 'code': 'PL', 'name': 'Poland'}, + {'timezones': [ + 'Atlantic/Azores', + 'Atlantic/Madeira', 'Europe/Lisbon'], + 'code': 'PT', 'name': 'Portugal'}, + {'timezones': ['America/Puerto_Rico'], + 'code': 'PR', 'name': 'Puerto Rico'}, + {'timezones': ['Asia/Qatar'], + 'code': 'QA', 'name': 'Qatar'}, + {'timezones': ['Africa/Brazzaville'], + 'code': 'CG', 'name': 'Republic of the Congo'}, + {'timezones': ['Indian/Reunion'], + 'code': 'RE', 'name': 'Reunion'}, + {'timezones': ['Europe/Bucharest'], + 'code': 'RO', 'name': 'Romania'}, + {'timezones': [ + 'Asia/Anadyr', 'Asia/Barnaul', + 'Asia/Chita', 'Asia/Irkutsk', 'Asia/Kamchatka', + 'Asia/Khandyga', 'Asia/Krasnoyarsk', 'Asia/Magadan', + 'Asia/Novokuznetsk', 'Asia/Novosibirsk', 'Asia/Omsk', + 'Asia/Sakhalin', 'Asia/Srednekolymsk', 'Asia/Tomsk', + 'Asia/Ust-Nera', 'Asia/Vladivostok', 'Asia/Yakutsk', + 'Asia/Yekaterinburg', 'Europe/Astrakhan', + 'Europe/Kaliningrad', 'Europe/Kirov', + 'Europe/Moscow', 'Europe/Samara', + 'Europe/Saratov', 'Europe/Simferopol', + 'Europe/Ulyanovsk', 'Europe/Volgograd'], + 'code': 'RU', 'name': 'Russia'}, + {'timezones': ['Africa/Kigali'], + 'code': 'RW', 'name': 'Rwanda'}, + {'timezones': ['America/St_Barthelemy'], + 'code': 'BL', 'name': 'Saint Barthelemy'}, + {'timezones': ['Atlantic/St_Helena'], + 'code': 'SH', 'name': 'Saint Helena'}, + {'timezones': ['America/St_Kitts'], + 'code': 'KN', 'name': 'Saint Kitts and Nevis'}, + {'timezones': ['America/St_Lucia'], + 'code': 'LC', 'name': 'Saint Lucia'}, + {'timezones': ['America/Marigot'], + 'code': 'MF', 'name': 'Saint Martin'}, + {'timezones': ['America/Miquelon'], + 'code': 'PM', 'name': 'Saint Pierre and Miquelon'}, + {'timezones': ['America/St_Vincent'], + 'code': 'VC', 'name': 'Saint Vincent and the Grenadines'}, + {'timezones': ['Pacific/Apia'], + 'code': 'WS', 'name': 'Samoa'}, + {'timezones': ['Europe/San_Marino'], + 'code': 'SM', 'name': 'San Marino'}, + {'timezones': ['Africa/Sao_Tome'], + 'code': 'ST', 'name': 'Sao Tome and Principe'}, + {'timezones': ['Asia/Riyadh'], + 'code': 'SA', 'name': 'Saudi Arabia'}, + {'timezones': ['Africa/Dakar'], + 'code': 'SN', 'name': 'Senegal'}, + {'timezones': ['Europe/Belgrade'], + 'code': 'RS', 'name': 'Serbia'}, + {'timezones': ['Indian/Mahe'], + 'code': 'SC', 'name': 'Seychelles'}, + {'timezones': ['Africa/Freetown'], + 'code': 'SL', 'name': 'Sierra Leone'}, + {'timezones': ['Asia/Singapore'], + 'code': 'SG', 'name': 'Singapore'}, + {'timezones': ['America/Lower_Princes'], + 'code': 'SX', 'name': 'Sint Maarten'}, + {'timezones': ['Europe/Bratislava'], + 'code': 'SK', 'name': 'Slovakia'}, + {'timezones': ['Europe/Ljubljana'], + 'code': 'SI', 'name': 'Slovenia'}, + {'timezones': ['Pacific/Guadalcanal'], + 'code': 'SB', 'name': 'Solomon Islands'}, + {'timezones': ['Africa/Mogadishu'], + 'code': 'SO', 'name': 'Somalia'}, + {'timezones': ['Africa/Johannesburg'], + 'code': 'ZA', 'name': 'South Africa'}, + {'timezones': ['Atlantic/South_Georgia'], + 'code': 'GS', + 'name': 'South Georgia and the South Sandwich Islands'}, + {'timezones': ['Asia/Seoul'], + 'code': 'KR', 'name': 'South Korea'}, + {'timezones': ['Africa/Juba'], + 'code': 'SS', 'name': 'South Sudan'}, + {'timezones': [ + 'Africa/Ceuta', + 'Atlantic/Canary', 'Europe/Madrid'], + 'code': 'ES', 'name': 'Spain'}, + {'timezones': ['Asia/Colombo'], + 'code': 'LK', 'name': 'Sri Lanka'}, + {'timezones': ['Africa/Khartoum'], + 'code': 'SD', 'name': 'Sudan'}, + {'timezones': ['America/Paramaribo'], + 'code': 'SR', 'name': 'Suriname'}, + {'timezones': ['Arctic/Longyearbyen'], + 'code': 'SJ', 'name': 'Svalbard and Jan Mayen'}, + {'timezones': ['Africa/Mbabane'], + 'code': 'SZ', 'name': 'Swaziland'}, + {'timezones': ['Europe/Stockholm'], + 'code': 'SE', 'name': 'Sweden'}, + {'timezones': ['Europe/Zurich'], + 'code': 'CH', 'name': 'Switzerland'}, + {'timezones': ['Asia/Damascus'], + 'code': 'SY', 'name': 'Syria'}, + {'timezones': ['Asia/Taipei'], + 'code': 'TW', 'name': 'Taiwan'}, + {'timezones': ['Asia/Dushanbe'], + 'code': 'TJ', 'name': 'Tajikistan'}, + {'timezones': ['Africa/Dar_es_Salaam'], + 'code': 'TZ', 'name': 'Tanzania'}, + {'timezones': ['Asia/Bangkok'], + 'code': 'TH', 'name': 'Thailand'}, + {'timezones': ['Africa/Lome'], + 'code': 'TG', 'name': 'Togo'}, + {'timezones': ['Pacific/Fakaofo'], + 'code': 'TK', 'name': 'Tokelau'}, + {'timezones': ['Pacific/Tongatapu'], + 'code': 'TO', 'name': 'Tonga'}, + {'timezones': ['America/Port_of_Spain'], + 'code': 'TT', 'name': 'Trinidad and Tobago'}, + {'timezones': ['Africa/Tunis'], + 'code': 'TN', 'name': 'Tunisia'}, + {'timezones': ['Europe/Istanbul'], + 'code': 'TR', 'name': 'Turkey'}, + {'timezones': ['Asia/Ashgabat'], + 'code': 'TM', 'name': 'Turkmenistan'}, + {'timezones': ['America/Grand_Turk'], + 'code': 'TC', 'name': 'Turks and Caicos Islands'}, + {'timezones': ['Pacific/Funafuti'], + 'code': 'TV', 'name': 'Tuvalu'}, + {'timezones': ['America/St_Thomas'], + 'code': 'VI', 'name': 'U.S. Virgin Islands'}, + {'timezones': ['Africa/Kampala'], + 'code': 'UG', 'name': 'Uganda'}, + {'timezones': [ + 'Europe/Kiev', + 'Europe/Uzhgorod', 'Europe/Zaporozhye'], + 'code': 'UA', 'name': 'Ukraine'}, + {'timezones': ['Asia/Dubai'], + 'code': 'AE', 'name': 'United Arab Emirates'}, + {'timezones': ['Europe/London'], + 'code': 'GB', 'name': 'United Kingdom'}, + {'timezones': [ + 'America/Adak', + 'America/Anchorage', 'America/Boise', + 'America/Chicago', 'US/Central', + 'America/Denver', 'America/Detroit', + 'US/Michigan', 'America/Indiana/Indianapolis', + 'America/Indiana/Knox', 'America/Indiana/Marengo', + 'America/Indiana/Petersburg', 'America/Indiana/Tell_City', + 'America/Indiana/Vevay', 'America/Indiana/Vincennes', + 'America/Indiana/Winamac', 'America/Juneau', + 'America/Kentucky/Louisville', 'America/Kentucky/Monticello', + 'America/Los_Angeles', 'US/Pacific', 'America/Menominee', + 'America/Metlakatla', 'America/New_York', 'US/Eastern', + 'America/Nome', 'America/North_Dakota/Beulah', + 'America/North_Dakota/Center', + 'America/North_Dakota/New_Salem', + 'America/Phoenix', 'America/Sitka', + 'America/Yakutat', 'Pacific/Honolulu'], + 'code': 'US', 'name': 'United States'}, + {'timezones': ['Pacific/Midway', 'Pacific/Wake'], + 'code': 'UM', 'name': 'United States Minor Outlying Islands'}, + {'timezones': ['America/Montevideo'], + 'code': 'UY', 'name': 'Uruguay'}, + {'timezones': ['Asia/Samarkand', 'Asia/Tashkent'], + 'code': 'UZ', 'name': 'Uzbekistan'}, + {'timezones': ['Pacific/Efate'], + 'code': 'VU', 'name': 'Vanuatu'}, + {'timezones': ['Europe/Vatican'], + 'code': 'VA', 'name': 'Vatican'}, + {'timezones': ['America/Caracas'], + 'code': 'VE', 'name': 'Venezuela'}, + {'timezones': ['Asia/Ho_Chi_Minh', 'Asia/Saigon', 'Asia/Hanoi'], + 'code': 'VN', 'name': 'Vietnam'}, + {'timezones': ['Pacific/Wallis'], + 'code': 'WF', 'name': 'Wallis and Futuna'}, + {'timezones': ['Africa/El_Aaiun'], + 'code': 'EH', 'name': 'Western Sahara'}, + {'timezones': ['Asia/Aden'], + 'code': 'YE', 'name': 'Yemen'}, + {'timezones': ['Africa/Lusaka'], + 'code': 'ZM', 'name': 'Zambia'}, + {'timezones': ['Africa/Harare'], + 'code': 'ZW', 'name': 'Zimbabwe'}, + {'timezones': ['Asia/Rangoon', 'Asia/Yangon'], + 'code': 'MM', 'name': 'Myanmar'}, +] diff --git a/app/routers/event.py b/app/routers/event.py index 03d99d2a..edc28a2f 100644 --- a/app/routers/event.py +++ b/app/routers/event.py @@ -19,7 +19,9 @@ from app.config import PICTURE_EXTENSION from app.database.models import ( + Category, Comment, + Country, Event, SharedList, SharedListItem, @@ -30,6 +32,7 @@ from app.internal import comment as cmt from app.internal.emotion import get_emotion from app.internal.event import ( + get_all_countries_names, get_invited_emails, get_location_coordinates, get_messages, @@ -37,6 +40,7 @@ raise_if_zoom_link_invalid, ) from app.internal.privacy import PrivacyKinds +from app.internal.security.dependencies import current_user from app.internal.utils import create_model, get_current_user from app.routers.categories import get_user_categories @@ -102,20 +106,28 @@ async def create_event_api(event: EventModel, session=Depends(get_db)): return {"success": True} +def get_categories_list( + user: User, + db_session: Session = Depends(get_db), +) -> List[Category]: + return get_user_categories(db_session, user.user_id) + + @router.get("/edit", include_in_schema=False) -@router.get("/edit") async def eventedit( request: Request, db_session: Session = Depends(get_db), + user: User = Depends(current_user), ) -> Response: - user_id = 1 # until issue#29 will get current user_id from session - categories_list = get_user_categories(db_session, user_id) + countries_names = get_all_countries_names(db_session) + categories_list = get_categories_list(user, db_session) return templates.TemplateResponse( "eventedit.html", { "request": request, "categories_list": categories_list, "privacy": PrivacyKinds, + "countries_names": countries_names, }, ) @@ -776,3 +788,23 @@ async def delete_comment( cmt.delete_comment(db, comment_id) path = router.url_path_for("view_comments", event_id=str(event_id)) return RedirectResponse(path, status_code=303) + + +@router.get("/timezone/country/{country_name}", include_in_schema=False) +async def check_timezone( + country_name, + request: Request, + db_session: Session = Depends(get_db), +) -> Response: + try: + country_timezone = ( + db_session.query(Country.timezone) + .filter_by(name=country_name) + .first()[0] + ) + except TypeError: + raise HTTPException( + status_code=404, + detail="The inserted country name is not found", + ) + return {"timezone": country_timezone} diff --git a/app/static/event/check_country_time.js b/app/static/event/check_country_time.js new file mode 100644 index 00000000..27605579 --- /dev/null +++ b/app/static/event/check_country_time.js @@ -0,0 +1,84 @@ +function checkCountryTime() { + const ERROR_TIME_DURATION = 3000; + const TIME_NOT_FILLED = 'Please enter the meeting date and time'; + const COUNTRY_NOT_FILLED = 'Please choose country'; + const modal_container = document.querySelector('.countries-modal-container'); + const open_modal = document.querySelector('.open-countries-modal'); + const close_modal = document.querySelector('.close-countries-modal'); + const submit_country = document.querySelector('.check-time'); + const upper_result = document.querySelector('.upper-result'); + const start_result = document.querySelector('.start-result'); + const end_result = document.querySelector('.end-result'); + const error_msg = document.querySelector('.empty-fields-error'); + const user_timezone = Intl.DateTimeFormat().resolvedOptions().timeZone; + + function getStartDate() { + const start_date = document.getElementById('start_date').value; + return start_date; + } + + function getStartTime() { + const start_time = document.getElementById('start_time').value; + return start_time; + } + + function getEndDate() { + const end_date = document.getElementById('end_date').value; + return end_date; + } + + function getEndTime() { + const end_time = document.getElementById('end_time').value; + return end_time; + } + + function displayErrorMsg(content) { + error_msg.classList.remove('empty-fields-error-disappear'); + error_msg.innerText = content; + setTimeout(() => error_msg.classList.add('empty-fields-error-disappear'), ERROR_TIME_DURATION); + } + + function convertTimes(data, chosen_country) { + const start_datetime = new Date(getStartDate() + 'T' + getStartTime()); + let converted_start_time = start_datetime.toLocaleTimeString('en-US', {timeZone: data.timezone}); + upper_result.innerText = 'Meeting Time in ' + chosen_country + ' is:'; + start_result.innerText = converted_start_time; + if (!(getEndDate() === "" || getEndTime() === "")) { + const end_datetime = new Date(getEndDate() + 'T' + getEndTime()); + let converted_end_time = end_datetime.toLocaleTimeString('en-US', {timeZone: data.timezone}); + end_result.innerText = ' until ' + converted_end_time; + } + } + + open_modal.addEventListener('click', (event) => { + event.preventDefault(); + modal_container.classList.add('modal-active'); + if (getStartDate() === '' || getStartTime() === '') { + displayErrorMsg(TIME_NOT_FILLED); + } + }); + + submit_country.addEventListener('click', (event) => { + event.preventDefault(); + if (getStartDate() === '' || getStartTime() === '') { + displayErrorMsg(TIME_NOT_FILLED); + return; + } + const chosen_country = document.getElementById('countries-datalist').value; + if (chosen_country === '') { + displayErrorMsg(COUNTRY_NOT_FILLED); + return; + } + fetch(`/event/timezone/country/${chosen_country}`) + .then(response => response.json()) + .then(data => convertTimes(data, chosen_country)) + }); + + close_modal.addEventListener('click', (event) => { + event.preventDefault(); + modal_container.classList.remove('modal-active'); + }); +} + + +document.addEventListener("DOMContentLoaded", checkCountryTime); diff --git a/app/static/event/eventedit.css b/app/static/event/eventedit.css index 0193595b..daca1757 100644 --- a/app/static/event/eventedit.css +++ b/app/static/event/eventedit.css @@ -66,6 +66,103 @@ input[type="submit"] { width: 100%; } +.countries-modal-container { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100vh; + background-color: rgba(0 , 0, 0, 0.5); + display: flex; + justify-content: center; + align-items: center; + visibility: hidden; + opacity: 0; + transition: visibility 0s, opacity 0, 5s; + font-family: 'Lucida Sans', 'Lucida Sans Regular', 'Lucida Grande', 'Lucida Sans Unicode', Geneva, Verdana, sans-serif; +} + +.modal-active { + visibility: visible; + opacity: 1; +} + +.countries-modal { + position: relative; + background-color: whitesmoke; + width: fit-content; + height: fit-content; + display: flex; + flex-direction: column; + justify-content: space-around; + padding: 2em 4em; + border-radius: 0.3em; + box-shadow: 0 0.2em 0.4em rgba(0, 0, 0, 0.2); + text-align: center; + transition: opacity 0.3s ease; + z-index: 100; +} + +.check-time { + padding: 0.7em 1em; + background-color: rgba(73, 65, 65, 0.842); + color: white; + border: none; + border-radius: 0.3em; + cursor: pointer; +} + +.check-time:hover { + background-color: rgba(112, 108, 108, 0.842); + border: none; +} + +.close-countries-modal { + position: absolute; + color: rgba(73, 65, 65, 0.842); + top: 1em; + right: 1em; + font-weight: bold; +} + +.close-countries-modal:hover { + color: brown; + cursor: pointer; +} + +#countries-datalist { + margin: 1em 0; + padding: 0.7em 1em; + border: 0.1em solid rgba(73, 65, 65, 0.842); + border-radius: 0.3em; +} + +#countries-datalist:hover { + background-color:aliceblue; +} + +.empty-fields-error { + color: brown; +} + +.empty-fields-error-disappear { + display: none; +} + +.results { + color: rgb(9, 65, 65); + margin-bottom: 1em; + font-weight: bold; +} + +.icon-credits, +.icon-credits a:link, +.icon-credits a:visited, +.icon-credits a:hover, +.icon-credits a:active { + color: rgb(202, 202, 202); +} + .shared-list-item-off { display: none; } diff --git a/app/static/images/worldwide.png b/app/static/images/worldwide.png new file mode 100644 index 00000000..c92d716d Binary files /dev/null and b/app/static/images/worldwide.png differ diff --git a/app/templates/eventedit.html b/app/templates/eventedit.html index a409b9c5..616ffa45 100644 --- a/app/templates/eventedit.html +++ b/app/templates/eventedit.html @@ -10,7 +10,7 @@ Event edit -
+