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

Feature/sckan-394-395-396 - Population set - Backend, UI, and Admin UI #420

Open
wants to merge 13 commits into
base: develop
Choose a base branch
from

Conversation

D-GopalKrishna
Copy link
Contributor

@D-GopalKrishna D-GopalKrishna commented Feb 5, 2025

Task description:

https://metacell.atlassian.net/browse/SCKAN-394
https://metacell.atlassian.net/browse/SCKAN-395
https://metacell.atlassian.net/browse/SCKAN-396 - (not the third part) - that is taken care in a separate PR.

More on the PR:

The Backend changes:

  • Add populationset - models, URLs, views, and serializers

  • add another field to KS - has_statement_been_exported

  • along with the migrations; a custom migration - migrate_has_statement_been_exported - to fill the field for everything that is exported currently

  • Two constraints:

    • Do not allow to go to EXPORTED if doesn't have populationset - by adding condition
conditions=[
            ConnectivityStatementStateService.is_valid,
            ConnectivityStatementStateService.has_populationset,
        ],

- Do not allow to change the populationset for a KS - if it had been ever in exported state
    def validate_population_change(self):
        if self.pk and self.has_statement_been_exported:
            original = ConnectivityStatement.objects.get(pk=self.pk)
            if original.population != self.population:
                raise ValidationError(
                    "Cannot change population set after the statement has been exported."
                )



The Composer UI changes:

  • update the genapi.sh
  • Add the copiedUISchema.population_id for - populationset
    • make it read only if isDisabled: statement.has_statement_been_exported is true
  • make a call to populations and set it using PopulationService - and call it onLogin.


The Admin UI changes:

  • Add PopulationSet to the admin UI.


def clean(self):
if not is_valid_population_name(self.name):
raise ValidationError(
Copy link
Contributor Author

Choose a reason for hiding this comment

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

@afonsobspinto I am not sure if throwing ValidationError is the best way we can tackle this. Do you think this is okay or can you help me find what would be right?

Copy link
Member

@afonsobspinto afonsobspinto Feb 5, 2025

Choose a reason for hiding this comment

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

My preference would be to use Django validators as part of the name field definition:

    name = models.CharField(
        max_length=20,
        validators=[is_valid_population_name],
        unique=True
    )

@@ -95,7 +96,10 @@ def create_or_update_connectivity_statement(
else:
create_invalid_note(connectivity_statement, error_message)
else:
if connectivity_statement.state != CSState.EXPORTED:
if (
Copy link
Contributor Author

@D-GopalKrishna D-GopalKrishna Feb 5, 2025

Choose a reason for hiding this comment

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

@afonsobspinto @ddelpiano - this update is for the ingestion script - I feel that would also require an update - since we do not want anything - that does not have ConnectivityStatementStateService.has_populationset to be in the exported state as per second point here - https://metacell.atlassian.net/browse/SCKAN-394

Is the assumption correct? Or should i revert this?

Copy link
Member

Choose a reason for hiding this comment

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

That seems reasonable but @ddelpiano needs to confirm this.

Implementation wise this seems wrong syntactically (because you don't seem to be calling has_populationset) and also we would need to update the script to associate the population with the connectivity statement

Copy link
Contributor Author

Choose a reason for hiding this comment

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

updated thanks! @afonsobspinto

Copy link
Member

Choose a reason for hiding this comment

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

The syntax looks correct now yes but I believe the second part is still missing.

would need to update the script to associate the population with the connectivity statement

If we are to implement this (which I believe we should) we need to understand how to get the population info from neurondm and update the function that creates / updates connectivity statements, on ingestion, to consider that new value on creation and ignore or report an error on updates with changes on such field

Copy link
Member

Choose a reason for hiding this comment

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

I discussed this point with @ddelpiano. We are waiting on the customer to reply on how can we get the populationSet from neurondm's neuron

@@ -96,3 +98,7 @@ def do_transition_to_exported(export_batch: ExportBatch, user: User):
CSState.EXPORTED, system_user, user
)
cs.save()
else:
export_batch.connectivity_statements.remove(connectivity_statement)
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 needed to remove connectivity_statement here - since - we do not want the one - where available transition doesn't have EXPORTED - to be exported,

And export batch is what is used in the export_services.py - to create a csv file.

Copy link
Member

Choose a reason for hiding this comment

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

I think it would make more sense to do this filtering on the create_export_batch function or at least save the changes done so that the database table keeps only the set of statements that can/were exported



def is_valid_population_name(name):
return re.match(r"^[a-zA-Z][a-zA-Z0-9_]{7,19}$", name) is not None
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 is as per the requirements here - https://metacell.atlassian.net/browse/SCKAN-394 - 3rd point. But I have also included - an underscore - to accommodate the fact - we also allow underscore - but not at the start.

def save(self, *args, **kwargs):
if not self.pk and self.sentence and not self.owner:
self.owner = self.sentence.owner

self.validate_population_change()
Copy link
Member

Choose a reason for hiding this comment

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

👍

@@ -567,6 +600,14 @@ class ConnectivityStatement(models.Model):
journey_path = models.JSONField(null=True, blank=True)
statement_prefix = models.TextField(null=True, blank=True)
statement_suffix = models.TextField(null=True, blank=True)
population = models.ForeignKey(
Copy link
Member

Choose a reason for hiding this comment

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

👍

@@ -196,6 +204,31 @@ class Meta:
verbose_name_plural = "Sex"


class PopulationSet(models.Model):
Copy link
Member

Choose a reason for hiding this comment

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

👍

@@ -260,6 +260,10 @@ def is_forward_connection_valid(connectivity_statement):
return True
return False

@staticmethod
def has_populationset(connectivity_statement) -> bool:
return connectivity_statement.population is not None
Copy link
Member

Choose a reason for hiding this comment

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

👍

]

operations = [
migrations.RunPython(migrate_has_statement_been_exported),
Copy link
Member

Choose a reason for hiding this comment

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

👍

conditions=[
ConnectivityStatementStateService.is_valid,
ConnectivityStatementStateService.has_populationset,
],
permission=ConnectivityStatementStateService.has_permission_to_transition_to_exported,
)
def exported(self, *args, **kwargs):
Copy link
Member

Choose a reason for hiding this comment

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

I think the transition effect should be to set the has_statement_been_exported to True:

self.has_statement_been_exported = True

@@ -95,7 +96,10 @@ def create_or_update_connectivity_statement(
else:
create_invalid_note(connectivity_statement, error_message)
else:
if connectivity_statement.state != CSState.EXPORTED:
if (
Copy link
Member

Choose a reason for hiding this comment

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

The syntax looks correct now yes but I believe the second part is still missing.

would need to update the script to associate the population with the connectivity statement

If we are to implement this (which I believe we should) we need to understand how to get the population info from neurondm and update the function that creates / updates connectivity statements, on ingestion, to consider that new value on creation and ignore or report an error on updates with changes on such field

@@ -96,3 +98,7 @@ def do_transition_to_exported(export_batch: ExportBatch, user: User):
CSState.EXPORTED, system_user, user
)
cs.save()
else:
export_batch.connectivity_statements.remove(connectivity_statement)
Copy link
Member

Choose a reason for hiding this comment

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

I think it would make more sense to do this filtering on the create_export_batch function or at least save the changes done so that the database table keeps only the set of statements that can/were exported

<App />
</Provider>
);
populations.setPopulations().then(() => {
Copy link
Member

Choose a reason for hiding this comment

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

Not related with this PR, but I don't love this nested callbacks. I think most of these fetches are independent, if that's the case we can use Promise.all instead to have them happening in parallel. It can be a tecnical debt card for later, cc @ddelpiano

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.

2 participants