Skip to content
GitLab
Projects
Groups
Snippets
/
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Menu
Open sidebar
thalia
concrexit
Commits
068d974b
Verified
Commit
068d974b
authored
Jan 24, 2019
by
Sébastiaan Versteeg
Browse files
Add country field to user profiles
parent
23b79eb4
Changes
29
Expand all
Hide whitespace changes
Inline
Side-by-side
docs/utils.rst
View file @
068d974b
...
...
@@ -18,6 +18,14 @@ Subpackages
Submodules
----------
utils.countries module
----------------------
.. automodule:: utils.countries
:members:
:undoc-members:
:show-inheritance:
utils.exception\_filter module
------------------------------
...
...
website/locale/nl/LC_MESSAGES/django.mo
View file @
068d974b
No preview for this file type
website/locale/nl/LC_MESSAGES/django.po
View file @
068d974b
...
...
@@ -7,8 +7,8 @@ msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 201
8
-0
7-11 20:07
+0
2
00\n"
"PO-Revision-Date: 201
6-1
1-
1
4 22:
05
+0100\n"
"POT-Creation-Date: 201
9
-0
1-24 22:35
+0
1
00\n"
"PO-Revision-Date: 201
9-0
1-
2
4 22:
44
+0100\n"
"Last-Translator: Joost Rijneveld <joost@joostrijneveld.nl>\n"
"Language-Team: \n"
"Language: nl\n"
...
...
@@ -16,6 +16,7 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"X-Generator: Poedit 2.2\n"
#: templates/registration/logged_out.html
msgid "Logout"
...
...
@@ -180,3 +181,207 @@ msgstr "Wachtwoord vergeten"
#: templates/registration/password_reset_subject.txt
msgid "[THALIA] Password reset request"
msgstr "[THALIA] Verzoek tot wachtwoordherstel"
#: utils/countries.py
msgid "Åland Islands"
msgstr "Aland-eilanden"
#: utils/countries.py
msgid "Albania"
msgstr "Albanië"
#: utils/countries.py
msgid "Andorra"
msgstr "Andorra"
#: utils/countries.py
msgid "Austria"
msgstr "Oostenrijk"
#: utils/countries.py
msgid "Belarus"
msgstr "Wit-Rusland"
#: utils/countries.py
msgid "Belgium"
msgstr "België"
#: utils/countries.py
msgid "Bosnia and Herzegovina"
msgstr "Bosnië en Herzegovina"
#: utils/countries.py
msgid "Bulgaria"
msgstr "Bulgarije"
#: utils/countries.py
msgid "Croatia"
msgstr "Kroatië"
#: utils/countries.py
msgid "Czechia"
msgstr "Tsjechië"
#: utils/countries.py
msgid "Denmark"
msgstr "Denemarken"
#: utils/countries.py
msgid "Estonia"
msgstr "Estland"
#: utils/countries.py
msgid "Faroe Islands"
msgstr "Faeröer"
#: utils/countries.py
msgid "Finland"
msgstr "Finland"
#: utils/countries.py
msgid "France"
msgstr "Frankrijk"
#: utils/countries.py
msgid "Germany"
msgstr "Duitsland"
#: utils/countries.py
msgid "Gibraltar"
msgstr "Gibraltar"
#: utils/countries.py
msgid "Greece"
msgstr "Griekenland"
#: utils/countries.py
msgid "Guernsey"
msgstr "Guernsey"
#: utils/countries.py
msgid "Vatican City"
msgstr "Vaticaanstad"
#: utils/countries.py
msgid "Hungary"
msgstr "Hongarije"
#: utils/countries.py
msgid "Iceland"
msgstr "IJsland"
#: utils/countries.py
msgid "Ireland"
msgstr "Ierland"
#: utils/countries.py
msgid "Isle of Man"
msgstr "Man (eiland)"
#: utils/countries.py
msgid "Italy"
msgstr "Italië"
#: utils/countries.py
msgid "Jersey"
msgstr "Jersey"
#: utils/countries.py
msgid "Latvia"
msgstr "Letland"
#: utils/countries.py
msgid "Liechtenstein"
msgstr "Liechtenstein"
#: utils/countries.py
msgid "Lithuania"
msgstr "Litouwen"
#: utils/countries.py
msgid "Luxembourg"
msgstr "Luxemburg"
#: utils/countries.py
msgid "Macedonia (FYROM)"
msgstr "Macedonië (VJRM)"
#: utils/countries.py
msgid "Malta"
msgstr "Malta"
#: utils/countries.py
msgid "Moldova"
msgstr "Moldavië"
#: utils/countries.py
msgid "Monaco"
msgstr "Monaco"
#: utils/countries.py
msgid "Montenegro"
msgstr "Montenegro"
#: utils/countries.py
msgid "Netherlands"
msgstr "Nederland"
#: utils/countries.py
msgid "Norway"
msgstr "Noorwegen"
#: utils/countries.py
msgid "Poland"
msgstr "Polen"
#: utils/countries.py
msgid "Portugal"
msgstr "Portugal"
#: utils/countries.py
msgid "Romania"
msgstr "Roemenië"
#: utils/countries.py
msgid "Russian Federation"
msgstr "Rusland"
#: utils/countries.py
msgid "San Marino"
msgstr "San Marino"
#: utils/countries.py
msgid "Serbia"
msgstr "Servië"
#: utils/countries.py
msgid "Slovakia"
msgstr "Slowakije"
#: utils/countries.py
msgid "Slovenia"
msgstr "Slovenië"
#: utils/countries.py
msgid "Spain"
msgstr "Spanje"
#: utils/countries.py
msgid "Svalbard and Jan Mayen"
msgstr "Spitsbergen en Jan Mayen"
#: utils/countries.py
msgid "Sweden"
msgstr "Zweden"
#: utils/countries.py
msgid "Switzerland"
msgstr "Zwitserland"
#: utils/countries.py
msgid "Ukraine"
msgstr "Oekraïne"
#: utils/countries.py
msgid "United Kingdom"
msgstr "Verenigd Koninkrijk"
website/members/admin.py
View file @
068d974b
...
...
@@ -23,11 +23,11 @@ class MembershipInline(admin.StackedInline):
class
ProfileInline
(
admin
.
StackedInline
):
fields
=
(
'starting_year'
,
'programme'
,
'address_street'
,
'address_street2'
,
'address_postal_code'
,
'address_city'
,
'student_number'
,
'phone_number'
,
'receive_optin'
,
'receive_newsletter'
,
'birthday'
,
'show_birthday'
,
'direct_debit_authorized'
,
'bank_account'
,
'initials'
,
'nickname'
,
'display_name_preference'
,
'profile_description'
,
'website'
,
'photo'
,
'emergency_contact'
,
'address_country'
,
'student_number'
,
'phone_number'
,
'receive_optin'
,
'receive_newsletter'
,
'birthday'
,
'show_birthday'
,
'direct_debit_authorized'
,
'bank_account'
,
'initials'
,
'nickname'
,
'display_name_preference'
,
'profile_description'
,
'website'
,
'photo'
,
'emergency_contact'
,
'emergency_contact_phone_number'
,
'language'
,
'event_permissions'
)
model
=
models
.
Profile
...
...
@@ -131,7 +131,8 @@ class UserAdmin(BaseUserAdmin):
filename="addresses.csv"'
writer
=
csv
.
writer
(
response
)
writer
.
writerow
([
_
(
'First name'
),
_
(
'Last name'
),
_
(
'Address'
),
_
(
'Address line 2'
),
_
(
'Postal code'
),
_
(
'City'
)])
_
(
'Address line 2'
),
_
(
'Postal code'
),
_
(
'City'
),
_
(
'Country'
)])
for
user
in
queryset
.
exclude
(
profile
=
None
):
writer
.
writerow
([
user
.
first_name
,
user
.
last_name
,
...
...
@@ -139,6 +140,7 @@ class UserAdmin(BaseUserAdmin):
user
.
profile
.
address_street2
,
user
.
profile
.
address_postal_code
,
user
.
profile
.
address_city
,
user
.
profile
.
get_address_country_display
(),
])
return
response
address_csv_export
.
short_description
=
_
(
'Download address label for '
...
...
website/members/api/serializers.py
View file @
068d974b
...
...
@@ -118,9 +118,10 @@ class ProfileEditSerializer(serializers.ModelSerializer):
model
=
Profile
fields
=
(
'pk'
,
'email'
,
'first_name'
,
'last_name'
,
'address_street'
,
'address_street2'
,
'address_postal_code'
,
'address_city'
,
'phone_number'
,
'show_birthday'
,
'website'
,
'photo'
,
'emergency_contact'
,
'emergency_contact_phone_number'
,
'profile_description'
,
'nickname'
,
'display_name_preference'
,
'address_country'
,
'phone_number'
,
'show_birthday'
,
'website'
,
'photo'
,
'emergency_contact'
,
'emergency_contact_phone_number'
,
'profile_description'
,
'nickname'
,
'display_name_preference'
,
'language'
,
'receive_optin'
,
'receive_newsletter'
,
'display_name'
,
'avatar'
,
'birthday'
,
'starting_year'
,
'programme'
,
'membership_type'
,
'achievements'
,
'societies'
)
...
...
website/members/emails.py
View file @
068d974b
...
...
@@ -84,6 +84,8 @@ def send_information_request(dry_run=False):
'address_postal_code'
:
member
.
profile
.
address_postal_code
,
'address_city'
:
member
.
profile
.
address_city
,
'address_country'
:
member
.
profile
.
get_address_country_display
(),
'phone_number'
:
member
.
profile
.
phone_number
,
'birthday'
:
member
.
profile
.
birthday
,
'email'
:
member
.
email
,
...
...
website/members/fixtures/members.json
View file @
068d974b
...
...
@@ -82,6 +82,7 @@
"address_street2"
:
""
,
"address_postal_code"
:
"1245 TG"
,
"address_city"
:
"Nijmegen"
,
"address_country"
:
"NL"
,
"phone_number"
:
""
,
"emergency_contact"
:
""
,
"emergency_contact_phone_number"
:
""
,
...
...
@@ -108,6 +109,7 @@
"address_street2"
:
""
,
"address_postal_code"
:
"2545 TG"
,
"address_city"
:
"Nijmegen"
,
"address_country"
:
"NL"
,
"phone_number"
:
""
,
"emergency_contact"
:
""
,
"emergency_contact_phone_number"
:
""
,
...
...
@@ -134,6 +136,7 @@
"address_street2"
:
""
,
"address_postal_code"
:
"6525 TE"
,
"address_city"
:
"Nijmegen"
,
"address_country"
:
"NL"
,
"phone_number"
:
""
,
"emergency_contact"
:
""
,
"emergency_contact_phone_number"
:
""
,
...
...
@@ -160,6 +163,7 @@
"address_street2"
:
""
,
"address_postal_code"
:
"6525 TE"
,
"address_city"
:
"Nijmegen"
,
"address_country"
:
"NL"
,
"phone_number"
:
""
,
"emergency_contact"
:
""
,
"emergency_contact_phone_number"
:
""
,
...
...
website/members/forms.py
View file @
068d974b
...
...
@@ -11,8 +11,9 @@ from .models import Profile
class
ProfileForm
(
forms
.
ModelForm
):
class
Meta
:
fields
=
[
'address_street'
,
'address_street2'
,
'address_postal_code'
,
'address_city'
,
'phone_number'
,
'emergency_contact'
,
'emergency_contact_phone_number'
,
'address_postal_code'
,
'address_city'
,
'address_country'
,
'phone_number'
,
'emergency_contact'
,
'emergency_contact_phone_number'
,
'show_birthday'
,
'website'
,
'profile_description'
,
'nickname'
,
'display_name_preference'
,
'photo'
,
'language'
,
...
...
website/members/locale/nl/LC_MESSAGES/django.mo
View file @
068d974b
No preview for this file type
website/members/locale/nl/LC_MESSAGES/django.po
View file @
068d974b
...
...
@@ -7,8 +7,8 @@ msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 201
8-12-17 15
:3
1
+0100\n"
"PO-Revision-Date: 201
8-12-17 15:32
+0100\n"
"POT-Creation-Date: 201
9-01-24 22
:3
7
+0100\n"
"PO-Revision-Date: 201
9-01-24 22:45
+0100\n"
"Last-Translator: Thom Wiggers <thom@thomwiggers.nl>\n"
"Language-Team: \n"
"Language: nl\n"
...
...
@@ -74,6 +74,10 @@ msgstr "Postcode"
msgid "City"
msgstr "Woonplaats"
#: admin.py models.py
msgid "Country"
msgstr "Land"
#: admin.py
msgid "Download address label for selected users"
msgstr "Download adreslabels voor geselecteerde gebruikers"
...
...
@@ -774,6 +778,7 @@ msgid ""
" %(address_street2)s\n"
" %(address_postal_code)s\n"
" %(address_city)s\n"
" %(address_country)s\n"
"Phone: %(phone_number)s\n"
"Date of birth: %(birthday)s\n"
"Email address: %(email)s\n"
...
...
@@ -806,6 +811,8 @@ msgstr ""
" %(address_street2)s\n"
" %(address_postal_code)s\n"
" %(address_city)s\n"
"\n"
" %(address_country)s\n"
"Telefoonnummer: %(phone_number)s\n"
"Geboortedatum: %(birthday)s\n"
"Emailadres: %(email)s\n"
...
...
website/members/management/commands/conscribosync.py
View file @
068d974b
...
...
@@ -3,6 +3,7 @@ import logging
from
django.conf
import
settings
from
django.core.management.base
import
BaseCommand
from
django.template.defaultfilters
import
date
from
django.utils
import
translation
from
requests
import
HTTPError
from
members.models
import
Member
...
...
@@ -39,35 +40,36 @@ class Command(BaseCommand):
}
replace_commands
=
[]
for
member
in
Member
.
current_members
.
all
():
code
=
current_relations
.
pop
(
member
.
pk
,
None
)
profile
=
member
.
profile
with
translation
.
override
(
'nl'
):
for
member
in
Member
.
current_members
.
all
():
code
=
current_relations
.
pop
(
member
.
pk
,
None
)
profile
=
member
.
profile
fields
=
{
'website_id'
:
member
.
pk
,
'voornaam'
:
member
.
first_name
,
'naam'
:
member
.
last_name
[:
100
],
#
Conscribo
maxlength: 100
'einddatum_lidmaatschap'
:
date
(
member
.
current_membership
.
until
,
'Y-m-d'
),
'e_mailadres'
:
member
.
email
,
'eerste_adresregel'
:
profile
.
address_street
,
'tweede_adresregel'
:
profile
.
address_street2
,
'postcode'
:
profile
.
address_postal_code
,
'plaats'
:
profile
.
address_city
,
'land'
:
'Nederland'
,
'bankrekeningnummer'
:
{
fields
=
{
'website_id'
:
member
.
pk
,
'voornaam'
:
member
.
first_name
,
'naam'
:
member
.
last_name
[:
100
],
#
api
maxlength: 100
'einddatum_lidmaatschap'
:
date
(
member
.
current_membership
.
until
,
'Y-m-d'
),
'e_mailadres'
:
member
.
email
,
'eerste_adresregel'
:
profile
.
address_street
,
'tweede_adresregel'
:
profile
.
address_street2
,
'postcode'
:
profile
.
address_postal_code
,
'plaats'
:
profile
.
address_city
,
'land'
:
profile
.
get_address_country_display
()
,
'bankrekeningnummer'
:
{
'name'
:
f
'$
{
profile
.
initials
}
$
{
member
.
last_name
}
'
,
'bic'
:
''
,
'iban'
:
profile
.
bank_account
,
},
}
}
replace_commands
.
append
(
ApiCommand
(
command
=
'ReplaceRelation'
,
entityType
=
'lid_2'
,
fields
=
fields
,
code
=
code
,
))
replace_commands
.
append
(
ApiCommand
(
command
=
'ReplaceRelation'
,
entityType
=
'lid_2'
,
fields
=
fields
,
code
=
code
,
))
replace_responses
=
api
.
multi_request
(
replace_commands
)
for
response
in
replace_responses
:
...
...
website/members/migrations/0029_profile_address_country.py
0 → 100644
View file @
068d974b
# Generated by Django 2.1.5 on 2019-01-24 21:59
from
django.db
import
migrations
,
models
class
Migration
(
migrations
.
Migration
):
dependencies
=
[
(
'members'
,
'0028_auto_20181217_1518'
),
]
operations
=
[
migrations
.
AddField
(
model_name
=
'profile'
,
name
=
'address_country'
,
field
=
models
.
CharField
(
choices
=
[(
'AX'
,
'Åland Islands'
),
(
'AL'
,
'Albania'
),
(
'AD'
,
'Andorra'
),
(
'AT'
,
'Austria'
),
(
'BY'
,
'Belarus'
),
(
'BE'
,
'Belgium'
),
(
'BA'
,
'Bosnia and Herzegovina'
),
(
'BG'
,
'Bulgaria'
),
(
'HR'
,
'Croatia'
),
(
'CZ'
,
'Czechia'
),
(
'DK'
,
'Denmark'
),
(
'EE'
,
'Estonia'
),
(
'FO'
,
'Faroe Islands'
),
(
'FI'
,
'Finland'
),
(
'FR'
,
'France'
),
(
'DE'
,
'Germany'
),
(
'GI'
,
'Gibraltar'
),
(
'GR'
,
'Greece'
),
(
'GG'
,
'Guernsey'
),
(
'VA'
,
'Vatican City'
),
(
'HU'
,
'Hungary'
),
(
'IS'
,
'Iceland'
),
(
'IE'
,
'Ireland'
),
(
'IM'
,
'Isle of Man'
),
(
'IT'
,
'Italy'
),
(
'JE'
,
'Jersey'
),
(
'LV'
,
'Latvia'
),
(
'LI'
,
'Liechtenstein'
),
(
'LT'
,
'Lithuania'
),
(
'LU'
,
'Luxembourg'
),
(
'MK'
,
'Macedonia (FYROM)'
),
(
'MT'
,
'Malta'
),
(
'MD'
,
'Moldova'
),
(
'MC'
,
'Monaco'
),
(
'ME'
,
'Montenegro'
),
(
'NL'
,
'Netherlands'
),
(
'NO'
,
'Norway'
),
(
'PL'
,
'Poland'
),
(
'PT'
,
'Portugal'
),
(
'RO'
,
'Romania'
),
(
'RU'
,
'Russian Federation'
),
(
'SM'
,
'San Marino'
),
(
'RS'
,
'Serbia'
),
(
'SK'
,
'Slovakia'
),
(
'SI'
,
'Slovenia'
),
(
'ES'
,
'Spain'
),
(
'SJ'
,
'Svalbard and Jan Mayen'
),
(
'SE'
,
'Sweden'
),
(
'CH'
,
'Switzerland'
),
(
'UA'
,
'Ukraine'
),
(
'GB'
,
'United Kingdom'
)],
default
=
'NL'
,
max_length
=
2
,
null
=
True
,
verbose_name
=
'Country'
),
preserve_default
=
False
,
),
]
website/members/models.py
View file @
068d974b
...
...
@@ -19,6 +19,7 @@ from localflavor.generic.countries.sepa import IBAN_SEPA_COUNTRIES
from
localflavor.generic.models
import
IBANField
from
activemembers.models
import
MemberGroup
,
MemberGroupMembership
from
utils
import
countries
logger
=
logging
.
getLogger
(
__name__
)
...
...
@@ -265,6 +266,13 @@ class Profile(models.Model):
null
=
True
,
)
address_country
=
models
.
CharField
(
max_length
=
2
,
choices
=
countries
.
EUROPE
,
verbose_name
=
_
(
'Country'
),
null
=
True
,
)
phone_number
=
models
.
CharField
(
max_length
=
20
,
verbose_name
=
_
(
'Phone number'
),
...
...
website/members/services.py
View file @
068d974b
...
...
@@ -189,6 +189,7 @@ def execute_data_minimisation(dry_run=False):
profile
.
address_street2
=
None
profile
.
address_postal_code
=
None
profile
.
address_city
=
None
profile
.
address_country
=
None
profile
.
birthday
=
None
profile
.
emergency_contact_phone_number
=
None
profile
.
emergency_contact
=
None
...
...
website/members/templates/members/email/information_check.txt
View file @
068d974b
...
...
@@ -9,6 +9,7 @@ Address: {{ address_street }}
{{ address_street2 }}
{{ address_postal_code }}
{{ address_city }}
{{ address_country }}
Phone: {{ phone_number }}
Date of birth: {{ birthday }}
Email address: {{ email }}
...
...
website/registrations/admin.py
View file @
068d974b
...
...
@@ -56,7 +56,8 @@ class RegistrationAdmin(admin.ModelAdmin):
'fields'
:
(
'address_street'
,
'address_street2'
,
'address_postal_code'
,
'address_city'
,)
'address_city'
,
'address_country'
,)
}),
(
_
(
'University information'
),
{
'fields'
:
(
'student_number'
,
...
...
website/registrations/locale/nl/LC_MESSAGES/django.mo
View file @
068d974b
No preview for this file type
website/registrations/locale/nl/LC_MESSAGES/django.po
View file @
068d974b
This diff is collapsed.
Click to expand it.
website/registrations/migrations/0016_registration_address_country.py
0 → 100644
View file @
068d974b
# Generated by Django 2.1.5 on 2019-01-24 21:59
from
django.db
import
migrations
,
models
class
Migration
(
migrations
.
Migration
):
dependencies
=
[
(
'registrations'
,
'0015_auto_20181126_2021'
),
]
operations
=
[
migrations
.
AddField
(
model_name
=
'registration'
,
name
=
'address_country'
,
field
=
models
.
CharField
(
choices
=
[(
'AX'
,
'Åland Islands'
),
(
'AL'
,
'Albania'
),
(
'AD'
,
'Andorra'
),
(
'AT'
,
'Austria'
),
(
'BY'
,
'Belarus'
),
(
'BE'
,
'Belgium'
),
(
'BA'
,
'Bosnia and Herzegovina'
),
(
'BG'
,
'Bulgaria'
),
(
'HR'
,
'Croatia'
),
(
'CZ'
,
'Czechia'
),
(
'DK'
,
'Denmark'
),
(
'EE'
,
'Estonia'
),
(
'FO'
,
'Faroe Islands'
),
(
'FI'
,
'Finland'
),
(
'FR'
,
'France'
),
(
'DE'
,
'Germany'
),
(
'GI'
,
'Gibraltar'
),
(
'GR'
,
'Greece'
),
(
'GG'
,
'Guernsey'
),
(
'VA'
,
'Vatican City'
),
(
'HU'
,
'Hungary'
),
(
'IS'
,
'Iceland'
),
(
'IE'
,
'Ireland'
),
(
'IM'
,
'Isle of Man'
),
(
'IT'
,
'Italy'
),
(
'JE'
,
'Jersey'
),
(
'LV'
,
'Latvia'
),
(
'LI'
,
'Liechtenstein'
),
(
'LT'
,
'Lithuania'
),
(
'LU'
,
'Luxembourg'
),
(
'MK'
,
'Macedonia (FYROM)'
),
(
'MT'
,
'Malta'
),
(
'MD'
,
'Moldova'
),
(
'MC'
,
'Monaco'
),
(
'ME'
,
'Montenegro'
),
(
'NL'
,
'Netherlands'
),
(
'NO'
,
'Norway'
),
(
'PL'
,
'Poland'
),
(
'PT'
,
'Portugal'
),
(
'RO'
,
'Romania'
),
(
'RU'
,
'Russian Federation'
),
(
'SM'
,
'San Marino'
),
(
'RS'
,
'Serbia'
),
(
'SK'
,
'Slovakia'
),
(
'SI'
,
'Slovenia'
),
(
'ES'
,
'Spain'
),
(
'SJ'
,
'Svalbard and Jan Mayen'
),
(
'SE'
,
'Sweden'
),
(
'CH'
,
'Switzerland'
),
(
'UA'
,
'Ukraine'
),
(
'GB'
,
'United Kingdom'
)],
default
=
'NL'
,
max_length
=
2
,
null
=
True
,
verbose_name
=
'Country'
),
preserve_default
=
False
,
),
]
website/registrations/models.py
View file @
068d974b
...
...
@@ -12,6 +12,7 @@ from django.utils.translation import ugettext_lazy as _
from
members.models
import
Membership
,
Profile
from
registrations
import
emails
from
utils
import
countries