class Experiment(models.Model): id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) external_id = models.CharField(max_length=100, db_index=True, null=True) modification_date = models.DateTimeField(auto_now=True) creation_date = models.DateTimeField(default=timezone.now) airtable_json = JSONField(null=True, blank=True) farmer = models.ForeignKey(Farmer, related_name='experiments', on_delete=models.CASCADE, null=True) approved = models.BooleanField(default=False, db_index=True) tags = ChoiceArrayField(models.CharField(max_length=255, choices=TAGS), default=list, blank=True, null=True) name = models.TextField() objectives = models.TextField(null=True, blank=True) equipment = models.TextField(null=True, blank=True) control_presence = models.BooleanField(null=True, blank=True) ongoing = models.BooleanField(null=True, blank=True) results = models.TextField(null=True, blank=True, choices=RESULTS) results_details = models.TextField(null=True, blank=True) links = ArrayField(models.TextField(), default=list, blank=True, null=True) description = models.TextField(null=True, blank=True) investment = models.TextField(null=True, blank=True) xp_type = models.TextField(null=True, blank=True, choices=XP_TYPE) surface = models.TextField(null=True, blank=True) surface_type = ChoiceArrayField(models.TextField(choices=SURFACE_TYPE), default=list, blank=True, null=True) def __str__(self): return self.name
class Farmer(models.Model): class Meta: ordering = ['name'] # These two are unique values. UUIDs were chosen initially as IDs as they # allow client ID generation, less issues when working with multiple DBs, etc. # However, they are cumbersome to use on some situations (e.g., URLs), so we # also benefit from a short sequential ID that uses a Postgres sequence. id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) sequence_number = models.IntegerField(default=get_next_increment, editable=False, unique=True) ############################################################################# external_id = models.CharField(max_length=100, db_index=True, null=True) user = models.OneToOneField(get_user_model(), on_delete=models.CASCADE, null=True) approved = models.BooleanField(default=False, db_index=True) modification_date = models.DateTimeField(auto_now=True) creation_date = models.DateTimeField(default=timezone.now) airtable_json = JSONField(null=True, blank=True) airtable_url = models.TextField(null=True) cgu_approved = models.BooleanField(default=False) name = models.TextField(null=True, blank=True) farm_name = models.TextField(null=True, blank=True) email = models.EmailField(db_index=True, null=True, blank=True) phone_number = models.CharField(max_length=50, null=True, blank=True) installation_date = models.DateField(null=True, blank=True) description = models.TextField(null=True, blank=True) cultures = models.TextField(null=True, blank=True) lat = models.DecimalField(null=True, blank=True, max_digits=9, decimal_places=6) lon = models.DecimalField(null=True, blank=True, max_digits=9, decimal_places=6) production = ChoiceArrayField(models.CharField(max_length=100, choices=PRODUCTIONS), default=list, null=True, blank=True) groups = ChoiceArrayField(models.CharField(max_length=200, choices=GROUPS), default=list, null=True, blank=True) agriculture_types = ChoiceArrayField(models.TextField(choices=TYPE_AGRICULTURE), default=list, null=True, blank=True) profile_image = models.ImageField(null=True, blank=True) postal_code = models.CharField(max_length=20, null=True, blank=True) personnel = models.TextField(null=True, blank=True) livestock_types = ChoiceArrayField(models.TextField(choices=TYPE_LIVESTOCK), default=list, null=True, blank=True) livestock_number = models.TextField(null=True, blank=True) soil_type = models.TextField(null=True, blank=True) specificities = models.TextField(null=True, blank=True) contact_possible = models.BooleanField(default=False) email_for_messages_allowed = models.BooleanField(default=True) links = ArrayField(models.TextField(), default=list, blank=True, null=True) surface = models.TextField(null=True, blank=True) surface_cultures = models.TextField(null=True, blank=True) surface_meadows = models.TextField(null=True, blank=True) output = models.TextField(null=True, blank=True) onboarding_shown = models.BooleanField(default=False) @property def approved_experiments(self): return self.experiments.filter(approved=True) @property def pending_experiments(self): return self.experiments.filter(approved=False) def save(self, force_insert=False, force_update=False, using=None, update_fields=None): if self.profile_image: self.profile_image = optimize_image(self.profile_image, self.profile_image.name) if self.email: self.email = get_user_model().objects.normalize_email(self.email) super(Farmer, self).save(force_insert, force_update, using, update_fields) @property def url_slug(self): url_name = quote(self.farm_name or self.name) return f'{url_name}--{self.sequence_number or ""}' @property def url_path(self): url_name = quote(self.farm_name or self.name) return f'/exploitation/{self.url_slug}' @property def html_link(self): """ This is used in the admin panel to link to the farmer's page """ if self.sequence_number and self.approved: unescaped_url = f'/exploitation/{self.farm_name or self.name}--{self.sequence_number}' return mark_safe(f'<a href="{self.url_path}" target="_blank">{unescaped_url}</a>') else: return 'Pas encore live' def __str__(self): return self.name
class Farmer(models.Model): id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) external_id = models.CharField(max_length=100, db_index=True, null=True) user = models.OneToOneField(get_user_model(), on_delete=models.CASCADE, null=True) approved = models.BooleanField(default=False, db_index=True) modification_date = models.DateTimeField(auto_now=True) creation_date = models.DateTimeField(default=timezone.now) airtable_json = JSONField(null=True, blank=True) airtable_url = models.TextField(null=True) name = models.TextField(null=True, blank=True) email = models.EmailField(db_index=True, null=True, blank=True) phone_number = models.CharField(max_length=50, null=True, blank=True) installation_date = models.DateField(null=True, blank=True) description = models.TextField(null=True, blank=True) cultures = models.TextField(null=True, blank=True) lat = models.DecimalField(max_digits=9, decimal_places=6) lon = models.DecimalField(max_digits=9, decimal_places=6) production = ChoiceArrayField(models.CharField(max_length=100, choices=PRODUCTIONS), default=list, null=True, blank=True) groups = ChoiceArrayField(models.CharField(max_length=200, choices=GROUPS), default=list, null=True, blank=True) agriculture_types = ChoiceArrayField(models.TextField(choices=TYPE_AGRICULTURE), default=list, null=True, blank=True) profile_image = models.ImageField(null=True, blank=True) postal_code = models.CharField(max_length=20, null=True, blank=True) personnel = models.TextField(null=True, blank=True) livestock_types = ChoiceArrayField(models.TextField(choices=TYPE_LIVESTOCK), default=list, null=True, blank=True) livestock_number = models.TextField(null=True, blank=True) soil_type = models.TextField(null=True, blank=True) specificities = models.TextField(null=True, blank=True) contact_possible = models.BooleanField(default=False) links = ArrayField(models.TextField(), default=list, blank=True, null=True) surface = models.TextField(null=True, blank=True) surface_cultures = models.TextField(null=True, blank=True) surface_meadows = models.TextField(null=True, blank=True) output = models.TextField(null=True, blank=True) @property def approved_experiments(self): return self.experiments.filter(approved=True) @property def pending_experiments(self): return self.experiments.filter(approved=False) def save(self, force_insert=False, force_update=False, using=None, update_fields=None): if self.profile_image: self.profile_image = optimize_image(self.profile_image, self.profile_image.name) super(Farmer, self).save(force_insert, force_update, using, update_fields) def __str__(self): return self.name
class Experiment(models.Model): # These two are unique values. UUIDs were chosen initially as IDs as they # allow client ID generation, less issues when working with multiple DBs, etc. # However, they are cumbersome to use on some situations (e.g., URLs), so we # also benefit from a short sequential ID that uses a Postgres sequence. id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) sequence_number = models.IntegerField(default=get_next_increment, editable=False, unique=True) ############################################################################# external_id = models.CharField(max_length=100, db_index=True, null=True) modification_date = models.DateTimeField(auto_now=True) creation_date = models.DateTimeField(default=timezone.now) airtable_json = JSONField(null=True, blank=True) farmer = models.ForeignKey(Farmer, related_name='experiments', on_delete=models.CASCADE, null=True) state = models.TextField(choices=STATES, default="Brouillon", db_index=True) tags = ChoiceArrayField(models.CharField(max_length=255, choices=TAGS), default=list, blank=True, null=True) name = models.TextField(max_length=70) short_name = models.TextField( max_length=30, null=True, blank=True, help_text='Si ce champ est présent, il sera utilisé pour l\'URL') objectives = models.TextField(null=True, blank=True) equipment = models.TextField(null=True, blank=True) control_presence = models.BooleanField(null=True, blank=True) ongoing = models.BooleanField(null=True, blank=True) results = models.TextField(null=True, blank=True, choices=RESULTS) results_details = models.TextField(null=True, blank=True) links = ArrayField(models.TextField(), default=list, blank=True, null=True) description = models.TextField(null=True, blank=True) investment = models.TextField(null=True, blank=True) xp_type = models.TextField(null=True, blank=True, choices=XP_TYPE) surface = models.TextField(null=True, blank=True) surface_type = ChoiceArrayField(models.TextField(choices=SURFACE_TYPE), default=list, blank=True, null=True) cultures = ArrayField(models.TextField(), default=list, blank=True, null=True) def __str__(self): return self.name @property def url_path(self): if not self.farmer: return '' url_name = quote(self.short_name or self.name) return f'{self.farmer.url_path}/{quote("expérience")}/{url_name}--{self.sequence_number or ""}' @property def html_link(self): if self.sequence_number and self.approved: unescaped_url = f'/exploitation/{self.farmer.farm_name or self.farmer.name}--{self.farmer.sequence_number}/expérience/{self.short_name or self.name}--{self.sequence_number}' return mark_safe( f'<a href="{self.url_path}" target="_blank">{unescaped_url}</a>' ) else: return 'Pas encore live' @property def approved(self): return self.state == 'Validé' @property def admin_link(self): return reverse('admin:data_experiment_change', args=(self.id, )) def save(self, force_insert=False, force_update=False, using=None, update_fields=None): """ We need to check if the experiment has been put as awaiting for validation to send an Asana task """ try: send_task = False if self.state == 'En attente de validation' and self.pk is not None: if self._state.adding or Experiment.objects.get( pk=self.pk).state != 'En attente de validation': task_name = "Retour d'experience '{0}' est en attente de validation".format( self.name) body = "Expérience créée par {0}. Lien pour admin : https://www.peps.beta.gouv.fr{1}".format( self.farmer.name, self.admin_link) AsanaUtils.send_task(settings.ASANA_PROJECT, task_name, body, None) except Exception as _: print( 'Error creating task in Asana for experiment awaiting validation' ) finally: super(Experiment, self).save(force_insert, force_update, using, update_fields)
class Experiment(models.Model): # These two are unique values. UUIDs were chosen initially as IDs as they # allow client ID generation, less issues when working with multiple DBs, etc. # However, they are cumbersome to use on some situations (e.g., URLs), so we # also benefit from a short sequential ID that uses a Postgres sequence. id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) sequence_number = models.IntegerField(default=get_next_increment, editable=False, unique=True) ############################################################################# external_id = models.CharField(max_length=100, db_index=True, null=True) modification_date = models.DateTimeField(auto_now=True) creation_date = models.DateTimeField(default=timezone.now) airtable_json = JSONField(null=True, blank=True) farmer = models.ForeignKey(Farmer, related_name='experiments', on_delete=models.CASCADE, null=True) approved = models.BooleanField(default=False, db_index=True) tags = ChoiceArrayField(models.CharField(max_length=255, choices=TAGS), default=list, blank=True, null=True) name = models.TextField(max_length=70) short_name = models.TextField( max_length=30, null=True, blank=True, help_text='Si ce champ est présent, il sera utilisé pour l\'URL') objectives = models.TextField(null=True, blank=True) equipment = models.TextField(null=True, blank=True) control_presence = models.BooleanField(null=True, blank=True) ongoing = models.BooleanField(null=True, blank=True) results = models.TextField(null=True, blank=True, choices=RESULTS) results_details = models.TextField(null=True, blank=True) links = ArrayField(models.TextField(), default=list, blank=True, null=True) description = models.TextField(null=True, blank=True) investment = models.TextField(null=True, blank=True) xp_type = models.TextField(null=True, blank=True, choices=XP_TYPE) surface = models.TextField(null=True, blank=True) surface_type = ChoiceArrayField(models.TextField(choices=SURFACE_TYPE), default=list, blank=True, null=True) cultures = ArrayField(models.TextField(), default=list, blank=True, null=True) def __str__(self): return self.name @property def url_path(self): if not self.farmer: return '' url_name = quote(self.short_name or self.name) return f'{self.farmer.url_path}/{quote("expérience")}/{url_name}--{self.sequence_number or ""}' @property def html_link(self): if self.sequence_number and self.approved: unescaped_url = f'/exploitation/{self.farmer.farm_name or self.farmer.name}--{self.farmer.sequence_number}/expérience/{self.short_name or self.name}--{self.sequence_number}' return mark_safe( f'<a href="{self.url_path}" target="_blank">{unescaped_url}</a>' ) else: return 'Pas encore live'