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/243 calculate sponsored percentage #255

Merged
Merged
Show file tree
Hide file tree
Changes from 14 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -273,8 +273,8 @@ def create_batches_for_site(site):
num_batches = random.randint(3, 8)
for i in range(num_batches):
number_of_seed = random.randint(50, 200)
plant_count = random.randint(0, number_of_seed)
survived_count = random.randint(0, plant_count)
survived_count = random.randint(100, 200)
replace_count = random.randint(0, 50)

sponsor = create_sponsor_for_batch()

Expand All @@ -284,9 +284,8 @@ def create_batches_for_site(site):
size=random.randint(20, 150),
sponsor=sponsor,
soil_condition="Good",
plant_count=plant_count,
survived_count=survived_count,
replace_count=plant_count - survived_count,
replace_count=replace_count,
total_number_seed=number_of_seed,
total_propagation=random.randint(0, number_of_seed),
)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Generated by Django 5.1 on 2024-09-26 20:15
# Generated by Django 5.1 on 2024-09-30 16:29

import canopeum_backend.models
import django.contrib.auth.models
Expand Down Expand Up @@ -232,7 +232,6 @@ class Migration(migrations.Migration):
('name', models.TextField(blank=True, null=True)),
('size', models.IntegerField(blank=True, null=True)),
('soil_condition', models.TextField(blank=True, null=True)),
('plant_count', models.IntegerField(blank=True, null=True)),
('survived_count', models.IntegerField(blank=True, null=True)),
('replace_count', models.IntegerField(blank=True, null=True)),
('total_number_seed', models.IntegerField(blank=True, null=True)),
Expand Down Expand Up @@ -316,9 +315,9 @@ class Migration(migrations.Migration):
name='Sitetreespecies',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('quantity', models.IntegerField(blank=True, null=True)),
('site', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='canopeum_backend.site')),
('tree_type', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.DO_NOTHING, to='canopeum_backend.treetype')),
('quantity', models.IntegerField()),
('site', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='canopeum_backend.site')),
('tree_type', models.ForeignKey(on_delete=django.db.models.deletion.DO_NOTHING, to='canopeum_backend.treetype')),
],
options={
'constraints': [models.UniqueConstraint(fields=('site', 'tree_type'), name='unique_tree_species_per_site')],
Expand Down
27 changes: 23 additions & 4 deletions canopeum_backend/canopeum_backend/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,22 @@
announcement = models.ForeignKey(Announcement, models.SET_NULL, blank=True, null=True)
image = models.ForeignKey(Asset, models.SET_NULL, blank=True, null=True)

def get_plant_count(self) -> int:
site_species = Sitetreespecies.objects.filter(site=self)
return sum(specie.quantity for specie in site_species)

def get_sponsor_progress(self) -> float:
total_plant_count = self.get_plant_count()
if total_plant_count == 0:
return 0

batches = Batch.objects.filter(site=self)
sponsored_plant_count = sum(batch.plant_count() for specie in batches)

Check failure on line 169 in canopeum_backend/canopeum_backend/models.py

View workflow job for this annotation

GitHub Actions / pyright

"batch" is not defined (reportUndefinedVariable)
Samuel-Therrien-Beslogic marked this conversation as resolved.
Show resolved Hide resolved
if sponsored_plant_count >= total_plant_count:
return 100

return sponsored_plant_count / total_plant_count * 100

@override
def delete(self, using=None, keep_parents=False):
# Coordinate
Expand Down Expand Up @@ -191,7 +207,6 @@
sponsor = models.ForeignKey(BatchSponsor, models.CASCADE)
size = models.IntegerField(blank=True, null=True)
soil_condition = models.TextField(blank=True, null=True)
plant_count = models.IntegerField(blank=True, null=True)
survived_count = models.IntegerField(blank=True, null=True)
replace_count = models.IntegerField(blank=True, null=True)
total_number_seed = models.IntegerField(blank=True, null=True)
Expand All @@ -218,6 +233,10 @@
tree_type = Treetype.objects.get(pk=pk)
return BatchSupportedSpecies.objects.create(tree_type=tree_type, batch=self)

def plant_count(self):
Samuel-Therrien-Beslogic marked this conversation as resolved.
Show resolved Hide resolved
batch_species = BatchSpecies.objects.filter(batch=self)
return sum(specie.quantity for specie in batch_species)


class FertilizertypeInternationalization(models.Model):
en = models.TextField(db_column="EN", blank=True, null=True)
Expand Down Expand Up @@ -362,9 +381,9 @@


class Sitetreespecies(models.Model):
site = models.ForeignKey(Site, models.CASCADE, blank=True, null=True)
tree_type = models.ForeignKey(Treetype, models.DO_NOTHING, blank=True, null=True)
quantity = models.IntegerField(blank=True, null=True)
site = models.ForeignKey(Site, models.CASCADE)
tree_type = models.ForeignKey(Treetype, models.DO_NOTHING)
quantity = models.IntegerField()

class Meta:
constraints = (
Expand Down
28 changes: 14 additions & 14 deletions canopeum_backend/canopeum_backend/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -543,9 +543,9 @@ class SiteSummarySerializer(serializers.ModelSerializer[Site]):
site_type = SiteTypeSerializer()
coordinate = CoordinatesSerializer()
plant_count = serializers.SerializerMethodField()
sponsor_progress = serializers.SerializerMethodField()
survived_count = serializers.SerializerMethodField()
propagation_count = serializers.SerializerMethodField()
progress = serializers.SerializerMethodField()
admins = SiteAdminSerializer(source="siteadmin_set", many=True)
batches = serializers.SerializerMethodField()

Expand All @@ -557,26 +557,26 @@ class Meta:
"coordinate",
"site_type",
"plant_count",
"sponsor_progress",
"survived_count",
"propagation_count",
"visitor_count",
"progress",
"admins",
"batches",
)

def get_plant_count(self, obj) -> int:
return random.randint(100, 200) # noqa: S311
def get_plant_count(self, obj: Site) -> int:
return obj.get_plant_count()

def get_sponsor_progress(self, obj: Site) -> float:
return obj.get_sponsor_progress()

def get_survived_count(self, obj) -> int:
return random.randint(50, 100) # noqa: S311

def get_propagation_count(self, obj) -> int:
return random.randint(5, 50) # noqa: S311

def get_progress(self, obj) -> float:
return random.randint(0, 10000) / 100 # noqa: S311

@extend_schema_field(BatchDetailSerializer(many=True))
def get_batches(self, obj):
batches = obj.batch_set.all().order_by("-updated_at")
Expand All @@ -587,9 +587,9 @@ class SiteSummaryDetailSerializer(serializers.ModelSerializer[Site]):
site_type = SiteTypeSerializer()
coordinate = CoordinatesSerializer()
plant_count = serializers.SerializerMethodField()
sponsor_progress = serializers.SerializerMethodField()
survived_count = serializers.SerializerMethodField()
propagation_count = serializers.SerializerMethodField()
progress = serializers.SerializerMethodField()
sponsors = serializers.SerializerMethodField()
admins = SiteAdminSerializer(source="siteadmin_set", many=True)
batches = serializers.SerializerMethodField()
Expand All @@ -603,28 +603,28 @@ class Meta:
"coordinate",
"site_type",
"plant_count",
"sponsor_progress",
"survived_count",
"propagation_count",
"visitor_count",
"sponsors",
"progress",
"admins",
"batches",
"weather",
)

def get_plant_count(self, obj) -> int:
return random.randint(100, 200) # noqa: S311
def get_plant_count(self, obj: Site) -> int:
return obj.get_plant_count()

def get_sponsor_progress(self, obj: Site) -> float:
return obj.get_sponsor_progress()

def get_survived_count(self, obj) -> int:
return random.randint(50, 100) # noqa: S311

def get_propagation_count(self, obj) -> int:
return random.randint(5, 50) # noqa: S311

def get_progress(self, obj) -> float:
return random.randint(0, 10000) / 100 # noqa: S311

@extend_schema_field(BatchSponsorSerializer(many=True))
def get_sponsors(self, obj):
batches = Batch.objects.filter(site=obj)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ const AnalyticsSiteHeader = ({ siteSummary }: Props) => {
</div>

<div className='mt-1'>
<SiteSponsorProgress progress={siteSummary.progress} />
<SiteSponsorProgress progress={siteSummary.sponsorProgress} />
</div>
</div>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@
if (siteSummaries.length === 0) return options

// total-functions/no-partial-division -- length checked above
options.average = siteSummaries.reduce(

Check warning on line 24 in canopeum_frontend/src/components/analytics/SiteSuccessRatesChart.tsx

View workflow job for this annotation

GitHub Actions / Lint

Division is partial. You should wrap it in a wrapper that returns undefined when the denominator is zero
(accumulator, current) => accumulator + current.progress,
(accumulator, current) => accumulator + current.sponsorProgress,
0,
) / siteSummaries.length

Expand All @@ -34,10 +34,10 @@
// However, bars will appear really thin, so we use stacked bars to stack
// 0-height bars on top of each other.
const strackedSerie = Array.from<number>({ length: siteSummaries.length }).fill(0)
strackedSerie[siteIndex] = site.progress
strackedSerie[siteIndex] = site.sponsorProgress

options.colors.push(
site.progress > options.average
site.sponsorProgress > options.average
? 'var(--bs-primary)'
: 'var(--bs-secondary)',
)
Expand All @@ -60,7 +60,7 @@
const renderChartTooltip = (props: ChartsAxisContentProps) => {
const selectedSerie = props.series.find(serie => serie.id === props.axisValue)
// total-functions/no-unsafe-type-assertion -- value type is known from the context
const data = selectedSerie?.data.find(value => !!value) as number | undefined

Check warning on line 63 in canopeum_frontend/src/components/analytics/SiteSuccessRatesChart.tsx

View workflow job for this annotation

GitHub Actions / Lint

This type assertion is not type-safe

return (
<div className='p-2'>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ const SiteSummaryCard = ({ site, admins, onSiteChange, onSiteEdit }: Props) => {
</div>

<div className='mt-4'>
<SiteSponsorProgress progress={site.progress} />
<SiteSponsorProgress progress={site.sponsorProgress} />
</div>
</div>
</div>
Expand Down
Loading
Loading