Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Regions #1331

Merged
merged 42 commits into from
Oct 6, 2020
Merged

Regions #1331

merged 42 commits into from
Oct 6, 2020

Conversation

rsanheim
Copy link
Contributor

@rsanheim rsanheim commented Sep 21, 2020

Story card: ch1455 https://app.clubhouse.io/simpledotorg/story/1455/create-region-schema-run-dry-run-of-region-backfill-in-prod

Because

We want a clean(er) way to represent hierarchical geographic areas in our domain model. Regions are a way to model Organization -> District (ie FacilityGroup) -> Block -> Facility, and they allow us to use the same object at each level for situations where that is what we want - for example with reporting.

This addresses

Adds a Region model and RegionType model. Adds a backfill with a dry_run mode, and this backfill will not run in production automatically via any migrations for now. We want to run it in dry run mode and then examine the log output to see what happens and what sort of data cleanup is necessary.

Future PRs will fix up the backfill to be more robust as needed, and then wire up the real backfill to run via a data migration.

@harimohanraj89 harimohanraj89 temporarily deployed to simple-review-pr-1331 September 21, 2020 18:05 Inactive
@rsanheim rsanheim temporarily deployed to simple-review-pr-1331 September 21, 2020 18:07 Inactive
@rsanheim rsanheim temporarily deployed to simple-review-pr-1331 September 21, 2020 18:08 Inactive
@rsanheim rsanheim temporarily deployed to simple-review-pr-1331 September 21, 2020 22:13 Inactive
Copy link
Contributor Author

@rsanheim rsanheim left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Notes for reviewers.

region
end

def self.backfill!
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Putting the backfill method here for easier testing.

facility_group_region = create_region_from(source: facility_group, kind: facility_group_kind, parent: org_region)

facility_group.facilities.group_by(&:zone).each do |block_name, facilities|
block_region = create_region_from(name: block_name, kind: block_kind, parent: facility_group_region)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We probably don't want to create blocks until the data is cleaned up on prod, but I wanted to include it here to see how it would work.

@@ -0,0 +1,14 @@
class RegionKind < ApplicationRecord
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This could've been named RegionType, but using the name type tends to conflict w/ Rails STI. This is basically where we define the region hiearchy for each Simple instance.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do RegionKinds have to be in the database? I'm wondering if we can just initialize them using some config hash-map instead?

Feels like creating a table and db rows for this might be overkill.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

They do have to be in the DB, for a couple reasons:

  • we need to know the canonical structure of Regions for building out the admin UI -- we could infer it from Region paths, but that would be error-prone and dangerous
  • the names of Regions will vary between countries. For example, in India we have blocks, in Bangladesh we will have upazila.

@rsanheim rsanheim temporarily deployed to simple-review-pr-1331 September 22, 2020 16:23 Inactive
end

def self.backfill!
instance_kind = RegionKind.find_by!(name: "Instance")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we use something like Root or RootRegion instead of Instance for these top level RegionKinds? Instance doesn't really convey anything to me semantically.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah we went back and forth on the name for the top level thing. I'll change it to Root.

@rsanheim rsanheim temporarily deployed to simple-review-pr-1331 September 23, 2020 16:01 Inactive
@rsanheim rsanheim marked this pull request as ready for review October 2, 2020 19:33
@rsanheim
Copy link
Contributor Author

rsanheim commented Oct 2, 2020

ch1455

so we can see the summary view of things
@@ -0,0 +1,14 @@
class RegionType < ApplicationRecord
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Definitely prefer RegionType to RegionKind, but wasn't there some concern about using Type in a model name?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

type as a field name is problematic, but model should be fine.

block_type = find_region_type("Block")
facility_type = find_region_type("Facility")

current_country_name = CountryConfig.current[:name]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We may want to ensure this only can only be run in India (especially if dry_run = false), since the Region hierarchy is likely going to be different elsewhere.
Don't want to trigger this accidentally via a migration or manually in other countries.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍 added a fail safe check

@vkrmis
Copy link
Contributor

vkrmis commented Oct 5, 2020

Mostly looks good, may want to be a little extra defensive and ensure this can only be triggered in India envs.


def set_path
self.path = "#{parent.path}.#{name}"
end
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we need to do manual parent and path stuff in this class?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because these records also need to build their path when they get created. The ltree gem doesn't do that for us, it just adds all the handy finder methods.

@@ -55,6 +55,10 @@ class CountryConfig
def self.for(abbreviation)
CONFIGS[abbreviation]
end

def self.current
Rails.application.config.country
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh... this is... nice...

friendly_id :name, use: :slugged

validates :name, presence: true
validates :slug, presence: true, uniqueness: true
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't think we can enforce uniqueness of the slug, can we? What happens if two blocks have the same name?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ugh. True.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Er, no, they need to be unique actually. FriendlyID should be able to handle name dupes and append a number to get a unique one. We need them unique so that we can use them for URLs and such.

# Labels must be less than 256 bytes long.
def name_to_path_label
name.gsub(/\W/, "_").slice(0, MAX_LABEL_LENGTH)
end
Copy link
Contributor

@harimohanraj89 harimohanraj89 Oct 5, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this only a convenience method for our backfill, or is it used by ltree under the hood as well?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just for our backfill.

@rsanheim rsanheim merged commit 7603f99 into master Oct 6, 2020
@rsanheim rsanheim deleted the regions-for-real branch October 6, 2020 14:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants