Skip to content

Commit

Permalink
AB#1083 Move countries to locations table
Browse files Browse the repository at this point in the history
- Add a new location type for countries and insert countries from STANAG
  1059 in the locations table; set a few old ones to 'inactive'.
- Replace people.country with a foreign key reference to locations.uuid.
- Migrate existing countries, using exact matches and some heuristics;
  keeping the old country names if we don't have an exact match, to give
  users the opportunity to correct this themselves.
  • Loading branch information
gjvoosten committed Apr 25, 2024
1 parent e4e3a7e commit c6aa67e
Show file tree
Hide file tree
Showing 53 changed files with 1,310 additions and 350 deletions.
9 changes: 6 additions & 3 deletions anet-dictionary.yml
Original file line number Diff line number Diff line change
Expand Up @@ -786,6 +786,12 @@ fields:
placeholder: Fill in the name of this location
type:
label: Type
digram:
label: Digram
placeholder: Fill in the 2-letter code for this country
trigram:
label: Trigram
placeholder: Fill in the 3-letter code for this country
description:
label: Description
placeholder: Fill in the description of this location
Expand Down Expand Up @@ -1215,9 +1221,6 @@ fields:
regular:
person:
name: Person
countries: [Afghanistan, Albania , Armenia, Australia, Austria, Azerbaijan, Belgium, Bosnia-Herzegovina, Bulgaria, Croatia, Czech Republic, Denmark, Estonia, Finland,
Georgia, Germany, Greece, Hungary, Iceland, Italy, Latvia, Lithuania, Luxembourg, Macedonia, Mongolia, Montenegro, Netherlands, New Zealand,
Norway, Poland, Portugal, Romania, Slovakia, Slovenia, Spain, Sweden, Türkiye, Ukraine, United Kingdom, United States of America]
# number of fields after Avatar in the left column for persons
# adjust this number if two columns are not balanced on the Person Page
numberOfFieldsInLeftColumn: 7
Expand Down
1 change: 0 additions & 1 deletion client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,6 @@
"handlebars": "4.7.8",
"handlebars-loader": "1.7.3",
"html-webpack-plugin": "5.6.0",
"i18n-iso-countries": "7.11.0",
"ignore-loader": "0.1.2",
"jest": "29.7.0",
"jest-environment-jsdom": "29.7.0",
Expand Down
39 changes: 39 additions & 0 deletions client/src/components/CountryDisplay.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { Icon } from "@blueprintjs/core"
import { IconNames } from "@blueprintjs/icons"
import LinkTo from "components/LinkTo"
import PropTypes from "prop-types"
import React from "react"

const CountryDisplay = ({ country, obsoleteCountry, plain }) => {
const icon = (
<Icon
icon={IconNames.FLAG}
style={{ marginLeft: 5, marginRight: 5, height: "1em" }}
/>
)
return (
<>
{country &&
(plain ? (
<span>
{icon}
{country.name}
</span>
) : (
<LinkTo modelType="Location" model={country} showIcon={false}>
{icon}
{country.name}
</LinkTo>
))}
{obsoleteCountry && <em> (old value: {obsoleteCountry})</em>}
</>
)
}

CountryDisplay.propTypes = {
country: PropTypes.object,
obsoleteCountry: PropTypes.string,
plain: PropTypes.bool
}

export default CountryDisplay
57 changes: 41 additions & 16 deletions client/src/components/SearchFilters.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { Icon } from "@blueprintjs/core"
import { IconNames } from "@blueprintjs/icons"
import { SEARCH_OBJECT_LABELS, SEARCH_OBJECT_TYPES } from "actions"
import AdvancedSelectFilter, {
deserialize as deserializeAdvancedSelectFilter
Expand All @@ -24,6 +26,7 @@ import TaskFilter, {
deserialize as deserializeTaskFilter
} from "components/advancedSearch/TaskFilter"
import {
CountryOverlayRow,
LocationOverlayRow,
PersonDetailedOverlayRow,
PositionOverlayRow,
Expand Down Expand Up @@ -104,6 +107,18 @@ const EmailFilter = {
}
}

const advancedSelectFilterCountryProps = {
overlayColumns: [
Settings.fields.location.name?.label,
Settings.fields.location.digram?.label,
Settings.fields.location.trigram?.label
],
overlayRenderRow: CountryOverlayRow,
objectType: Location,
valueKey: "name",
fields: Location.autocompleteQuery,
addon: <Icon icon={IconNames.FLAG} />
}
const advancedSelectFilterPersonProps = {
overlayColumns: ["Name", "Position", "Location", "Organization"],
overlayRenderRow: PersonDetailedOverlayRow,
Expand Down Expand Up @@ -171,12 +186,22 @@ export const searchFilters = function(includeAdminFilters) {
queryVars: {}
}
}
const locationWidgetFilters = {
all: {
label: "All",
queryVars: {}
const countryWidgetFilters = {
activeCountries: {
label: "Active countries",
queryVars: {
type: Location.LOCATION_TYPES.COUNTRY,
status: Model.STATUS.ACTIVE
}
},
allCountries: {
label: "All countries",
queryVars: { type: Location.LOCATION_TYPES.COUNTRY }
}
}
const reportLocationWidgetFilters = Location.getReportLocationFilters()
const positionLocationWidgetFilters = Location.getPositionLocationFilters()
const organizationLocationFilters = Location.getOrganizationLocationFilters()

const taskWidgetFilters = {
all: {
Expand Down Expand Up @@ -275,7 +300,7 @@ export const searchFilters = function(includeAdminFilters) {
dictProps: Settings.fields.report.location,
deserializer: deserializeAdvancedSelectFilter,
props: Object.assign({}, advancedSelectFilterLocationProps, {
filterDefs: locationWidgetFilters,
filterDefs: reportLocationWidgetFilters,
placeholder: "Filter reports by location...",
queryKey: "locationUuid"
})
Expand Down Expand Up @@ -351,7 +376,6 @@ export const searchFilters = function(includeAdminFilters) {
}
}

const countries = Settings.fields.regular.person.countries || []
const ranks = (Settings.fields.person.ranks || []).map(f => f.value)

filters[SEARCH_OBJECT_TYPES.PEOPLE] = {
Expand All @@ -369,7 +393,7 @@ export const searchFilters = function(includeAdminFilters) {
component: AdvancedSelectFilter,
deserializer: deserializeAdvancedSelectFilter,
props: Object.assign({}, advancedSelectFilterLocationProps, {
filterDefs: locationWidgetFilters,
filterDefs: positionLocationWidgetFilters,
placeholder: "Filter by location...",
queryKey: "locationUuid"
})
Expand All @@ -385,14 +409,14 @@ export const searchFilters = function(includeAdminFilters) {
}
},
Nationality: {
component: SelectFilter,
component: AdvancedSelectFilter,
dictProps: Settings.fields.person.country,
deserializer: deserializeSelectFilter,
props: {
queryKey: "country",
options: countries,
labels: countries
}
deserializer: deserializeAdvancedSelectFilter,
props: Object.assign({}, advancedSelectFilterCountryProps, {
filterDefs: countryWidgetFilters,
placeholder: "Filter by country...",
queryKey: "countryUuid"
})
},
"Has Biography?": {
component: RadioButtonFilter,
Expand Down Expand Up @@ -452,7 +476,7 @@ export const searchFilters = function(includeAdminFilters) {
dictProps: Settings.fields.organization.location,
deserializer: deserializeAdvancedSelectFilter,
props: Object.assign({}, advancedSelectFilterLocationProps, {
filterDefs: locationWidgetFilters,
filterDefs: organizationLocationFilters,
placeholder: "Filter by location...",
queryKey: "locationUuid"
})
Expand Down Expand Up @@ -504,7 +528,7 @@ export const searchFilters = function(includeAdminFilters) {
dictProps: Settings.fields.position.location,
deserializer: deserializeAdvancedSelectFilter,
props: Object.assign({}, advancedSelectFilterLocationProps, {
filterDefs: locationWidgetFilters,
filterDefs: positionLocationWidgetFilters,
placeholder: "Filter by location...",
queryKey: "locationUuid"
})
Expand Down Expand Up @@ -534,6 +558,7 @@ export const searchFilters = function(includeAdminFilters) {
const locationTypeOptions = [
Location.LOCATION_TYPES.POINT_LOCATION,
Location.LOCATION_TYPES.GEOGRAPHICAL_AREA,
Location.LOCATION_TYPES.COUNTRY,
Location.LOCATION_TYPES.VIRTUAL_LOCATION
]
filters[SEARCH_OBJECT_TYPES.LOCATIONS] = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,14 @@ export const AuthorizationGroupOverlayRow = item => (
</React.Fragment>
)

export const CountryOverlayRow = item => (
<React.Fragment key={item.uuid}>
<td>{item.name}</td>
<td>{item.digram}</td>
<td>{item.trigram}</td>
</React.Fragment>
)

export const LocationOverlayRow = item => (
<React.Fragment key={item.uuid}>
<td>
Expand Down
54 changes: 38 additions & 16 deletions client/src/components/previews/LocationPreview.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ const GQL_GET_LOCATION = gql`
lng
status
type
digram
trigram
description
}
}
Expand Down Expand Up @@ -64,28 +66,46 @@ const LocationPreview = ({ className, uuid }) => {
value={Location.humanNameOfType(location.type)}
/>

<PreviewField
label={label}
value={
<GeoLocation
coordinates={{
lat: location.lat,
lng: location.lng,
displayedCoordinate: convertLatLngToMGRS(
location.lat,
location.lng
)
}}
/>
}
/>
{Location.hasCoordinates(location) && (
<PreviewField
label={label}
value={
<GeoLocation
coordinates={{
lat: location.lat,
lng: location.lng,
displayedCoordinate: convertLatLngToMGRS(
location.lat,
location.lng
)
}}
/>
}
/>
)}

<DictionaryField
wrappedComponent={PreviewField}
dictProps={Settings.fields.location.status}
value={Location.humanNameOfStatus(location.status)}
/>

{location.type === Location.LOCATION_TYPES.COUNTRY && (
<>
<DictionaryField
wrappedComponent={PreviewField}
dictProps={Settings.fields.location.digram}
value={location.digram}
/>

<DictionaryField
wrappedComponent={PreviewField}
dictProps={Settings.fields.location.trigram}
value={location.trigram}
/>
</>
)}

{location.description && (
<DictionaryField
wrappedComponent={PreviewField}
Expand All @@ -95,7 +115,9 @@ const LocationPreview = ({ className, uuid }) => {
)}
</div>

<Leaflet markers={[marker]} mapId={`${uuid}`} />
{Location.hasCoordinates(location) && (
<Leaflet markers={[marker]} mapId={`${uuid}`} />
)}
</div>
)
}
Expand Down
14 changes: 12 additions & 2 deletions client/src/components/previews/PersonPreview.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { gql } from "@apollo/client"
import API from "api"
import AppContext from "components/AppContext"
import AvatarDisplayComponent from "components/AvatarDisplayComponent"
import CountryDisplay from "components/CountryDisplay"
import DictionaryField from "components/DictionaryField"
import EmailAddressTable from "components/EmailAddressTable"
import { PreviewField } from "components/FieldHelper"
Expand Down Expand Up @@ -30,7 +31,11 @@ const GQL_GET_PERSON = gql`
user
domainUsername
biography
country
obsoleteCountry
country {
uuid
name
}
gender
endOfTourDate
code
Expand Down Expand Up @@ -181,7 +186,12 @@ const PersonPreview = ({ className, uuid }) => {
<DictionaryField
wrappedComponent={PreviewField}
dictProps={Settings.fields.person.country}
value={person.country}
value={
<CountryDisplay
country={person.country}
obsoleteCountry={person.obsoleteCountry}
/>
}
/>

<DictionaryField
Expand Down
Loading

0 comments on commit c6aa67e

Please sign in to comment.