Skip to content

Commit 0101335

Browse files
authored
Merge pull request #314 from Mosquito-Alert/fix_package_version
Bugfix: adjust MobileApp.package_version (str) to be compatible with Report.package_version (int)
2 parents 4b764a7 + a47f345 commit 0101335

File tree

8 files changed

+104
-21
lines changed

8 files changed

+104
-21
lines changed

api/tests/integration/devices/create.tavern.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ stages:
5959
locale: en-US
6060
mobile_app:
6161
package_name: test_package_name
62-
package_version: test_package_version
62+
package_version: "1.2.3+4567"
6363
response:
6464
status_code: 201
6565
json:
@@ -74,7 +74,7 @@ stages:
7474
locale: "en-US"
7575
mobile_app:
7676
package_name: "test_package_name"
77-
package_version: "test_package_version"
77+
package_version: "1.2.3+4567"
7878
user_uuid: "{app_user.pk}"
7979
last_login: !anything
8080
created_at: !re_fullmatch \d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}.\d{6}Z

api/tests/integration/devices/update.tavern.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ stages:
4646
locale: es-ES
4747
mobile_app:
4848
package_name: new_test_package_name
49-
package_version: new_test_package_version
49+
package_version: "2.3.4+5678"
5050
response:
5151
status_code: 200
5252
strict:
@@ -59,7 +59,7 @@ stages:
5959
locale: es-ES
6060
mobile_app:
6161
package_name: new_test_package_name
62-
package_version: new_test_package_version
62+
package_version: "2.3.4+5678"
6363
# Now simulate another user trying to access the first user data
6464
- id: signup
6565
type: ref

api/tests/test_views.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ def test_package_is_set_on_report_create(self, app_user, data_create_request):
7777
device_id='unique_device_id',
7878
mobile_app=MobileApp.objects.create(
7979
package_name='testapp',
80-
package_version=1234
80+
package_version="1.2.3+4567"
8181
)
8282
)
8383
)
@@ -94,7 +94,7 @@ def test_package_is_set_on_report_create(self, app_user, data_create_request):
9494
report = self.queryset.get(pk=response.data.get('uuid'))
9595

9696
assert report.package_name == 'testapp'
97-
assert report.package_version == 1234
97+
assert report.package_version == 102 # NOTE: only major and minor version are considered.
9898
assert report.app_language == 'es'
9999

100100
def test_device_is_set_on_report_create(self, app_user, data_create_request):

api/views.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -293,7 +293,8 @@ def perform_create(self, serializer):
293293
if device.mobile_app:
294294
kwargs['mobile_app'] = device.mobile_app
295295
kwargs['package_name'] = device.mobile_app.package_name
296-
kwargs['package_version'] = device.mobile_app.package_version
296+
v = device.mobile_app.package_version
297+
kwargs['package_version'] = int(f"{v.major}{v.minor:02d}")
297298
serializer.save(**kwargs)
298299

299300
def perform_update(self, serializer):

requirements/prod_20_04.pip

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ python3-memcached==1.51
5252
rawpy==0.17.1
5353
redis==2.10.3
5454
requests==2.22.0
55+
semantic-version==2.10.0
5556
scikit-learn==1.5.2
5657
scipy==1.13.1
5758
six==1.16.0
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
# Generated by Django 3.2.25 on 2025-08-22 08:18
2+
3+
from django.db import migrations
4+
import django.core.validators
5+
import re
6+
import semantic_version
7+
import semantic_version.django_fields
8+
from semantic_version import Version
9+
10+
11+
def force_semantic_version(apps, schema_editor):
12+
MobileApp = apps.get_model('tigaserver_app', 'MobileApp')
13+
14+
obj_to_update = []
15+
for mobile_app in MobileApp.objects.iterator():
16+
if not semantic_version.validate(mobile_app.package_version):
17+
if '.' in mobile_app.package_version:
18+
mobile_app.package_version = Version.coerce(mobile_app.package_version)
19+
else:
20+
# NOTE: this must be the same as in Report.save when doing the MobileApp.objects.get_or_create
21+
mobile_app.package_version = Version(
22+
major=0,
23+
minor=int(mobile_app.package_version),
24+
patch=0,
25+
build=('legacy',)
26+
)
27+
obj_to_update.append(mobile_app)
28+
29+
MobileApp.objects.bulk_update(obj_to_update, ['package_version'], batch_size=2000)
30+
31+
def undo_force_semantic_version(apps, schema_editor):
32+
MobileApp = apps.get_model('tigaserver_app', 'MobileApp')
33+
34+
obj_to_update = []
35+
for mobile_app in MobileApp.objects.filter(package_version__contains='legacy').iterator():
36+
mobile_app.package_version = str(Version.coerce(mobile_app.package_version).minor)
37+
obj_to_update.append(mobile_app)
38+
39+
MobileApp.objects.bulk_update(obj_to_update, ['package_version'], batch_size=2000)
40+
41+
class Migration(migrations.Migration):
42+
43+
dependencies = [
44+
('tigaserver_app', '0083_europecountry_reports_can_be_published'),
45+
]
46+
47+
operations = [
48+
migrations.RunPython(force_semantic_version, undo_force_semantic_version),
49+
migrations.AlterField(
50+
model_name='mobileapp',
51+
name='package_version',
52+
field=semantic_version.django_fields.VersionField(coerce=False, max_length=32, partial=False, validators=[django.core.validators.RegexValidator(code='invalid_version', regex=re.compile('^(\\d+)\\.(\\d+)\\.(\\d+)(?:-([0-9a-zA-Z.-]+))?(?:\\+([0-9a-zA-Z.-]+))?$'))]),
53+
),
54+
]

tigaserver_app/models.py

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
from django.contrib.gis.db.models.functions import Distance as DistanceFunction
2323
from django.contrib.gis.geos import GEOSGeometry
2424
from django.contrib.gis.measure import Distance as DistanceMeasure
25+
from django.core.validators import RegexValidator
2526
from django.db import transaction
2627
from django.db.models import Count, Q
2728
from django.db.models.signals import post_save
@@ -36,6 +37,8 @@
3637
from fcm_django.models import AbstractFCMDevice, DeviceType
3738
from imagekit.processors import ResizeToFit
3839
from langcodes import Language, closest_supported_match, standardize_tag as standarize_language_tag, tag_is_valid as language_tag_is_valid
40+
from semantic_version import Version
41+
from semantic_version.django_fields import VersionField
3942
from simple_history.models import HistoricalRecords
4043
from timezone_field import TimeZoneField
4144
from taggit.managers import TaggableManager
@@ -333,8 +336,16 @@ class Meta:
333336

334337

335338
class MobileApp(models.Model):
339+
# NOTE: At some point we should adjust the package_version which 'build' value is 'legacy'
340+
# since this version were creating from Report.package_version (which is an IntegerField)
341+
# and it's a number which is not related with the Mobile App pubspeck.yaml package version.
336342
package_name = models.CharField(max_length=128)
337-
package_version = models.CharField(max_length=32)
343+
package_version = VersionField(max_length=32, validators=[
344+
RegexValidator(
345+
regex=Version.version_re,
346+
code='invalid_version'
347+
)
348+
])
338349

339350
created_at = models.DateTimeField(auto_now_add=True)
340351
updated_at = models.DateTimeField(auto_now=True)
@@ -1961,11 +1972,19 @@ def save(self, *args, **kwargs):
19611972
# in order to avoid publishing on bulk_create
19621973
self.published_at = timezone.now()
19631974

1964-
# Set mobile_app
1965-
if self.package_name and self.package_version:
1975+
# Set mobile_app (case legacy API)
1976+
if not self.mobile_app and self.package_name and self.package_version:
1977+
# NOTE: changing this will require a migration that matches the logic.
19661978
self.mobile_app, _ = MobileApp.objects.get_or_create(
19671979
package_name=self.package_name,
1968-
package_version=self.package_version
1980+
package_version=str(
1981+
Version(
1982+
major=0,
1983+
minor=int(self.package_version),
1984+
patch=0,
1985+
build=('legacy',)
1986+
)
1987+
)
19691988
)
19701989
# Update device according to the information provided in the report.
19711990
with transaction.atomic():

tigaserver_app/tests/tests.py

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
import tempfile
3131
from django.core.files.uploadedfile import SimpleUploadedFile
3232
import time_machine
33+
import semantic_version
3334

3435
import io
3536
import piexif
@@ -537,7 +538,7 @@ def test_user_locale_is_updated_according_to_app_language(self):
537538

538539
def test_mobile_app_fk_is_created_if_not_exist(self):
539540
self.assertEqual(
540-
MobileApp.objects.filter(package_name='testapp', package_version='100').count(),
541+
MobileApp.objects.filter(package_name='testapp', package_version='0.100.0+legacy').count(),
541542
0
542543
)
543544
response = self.client.post(
@@ -552,20 +553,19 @@ def test_mobile_app_fk_is_created_if_not_exist(self):
552553
format="json"
553554
)
554555
self.assertEqual(response.status_code, 201)
555-
556556
self.assertEqual(
557-
MobileApp.objects.filter(package_name='testapp', package_version='100').count(),
557+
MobileApp.objects.filter(package_name='testapp', package_version='0.100.0+legacy').count(),
558558
1
559559
)
560-
mobile_app = MobileApp.objects.get(package_name='testapp', package_version='100')
560+
mobile_app = MobileApp.objects.get(package_name='testapp', package_version='0.100.0+legacy')
561561
self.assertEqual(mobile_app.package_name, 'testapp')
562-
self.assertEqual(mobile_app.package_version, '100')
562+
self.assertEqual(mobile_app.package_version, semantic_version.Version(major=0, minor=100, patch=0, build=('legacy',)))
563563

564564
report = Report.objects.get(version_UUID=self.simple_payload["version_UUID"])
565565
self.assertEqual(report.mobile_app, mobile_app)
566566

567567
def test_mobile_app_fk_is_set_correctly_if_exist(self):
568-
mobile_app = MobileApp.objects.create(package_name='testapp', package_version='100')
568+
mobile_app = MobileApp.objects.create(package_name='testapp', package_version='0.100.0+legacy')
569569
response = self.client.post(
570570
"/api/reports/",
571571
{
@@ -635,7 +635,7 @@ def test_device_with_model_null_is_updated_on_new_report(self):
635635
last_login=timezone.now()-timedelta(days=1)
636636
)
637637
self.assertIsNone(device.model)
638-
mobile_app = MobileApp.objects.create(package_name='testapp', package_version='100')
638+
mobile_app = MobileApp.objects.create(package_name='testapp', package_version='0.100.0+legacy')
639639

640640
response = self.client.post(
641641
"/api/reports/",
@@ -680,7 +680,7 @@ def test_device_with_model_is_updated_on_new_report(self):
680680
last_login=timezone.now()-timedelta(days=1)
681681
)
682682
self.assertIsNone(device.type)
683-
mobile_app = MobileApp.objects.create(package_name='testapp', package_version='100')
683+
mobile_app = MobileApp.objects.create(package_name='testapp', package_version='0.100.0+legacy')
684684

685685
response = self.client.post(
686686
"/api/reports/",
@@ -1905,7 +1905,15 @@ def test_mobile_app_is_deleted_on_save_failure(self):
19051905
)
19061906
self.assertEqual(MobileApp.objects.all().count(), 1)
19071907

1908-
mobile_app = MobileApp.objects.get(package_name=report.package_name, package_version=report.package_version)
1908+
mobile_app = MobileApp.objects.get(
1909+
package_name=report.package_name,
1910+
package_version=semantic_version.Version(
1911+
major=0,
1912+
minor=int(report.package_version),
1913+
patch=0,
1914+
build=('legacy',)
1915+
)
1916+
)
19091917

19101918
with self.assertRaises(IntegrityError) as context:
19111919
# Trying to create a new report with the same PK, which will raise.
@@ -1925,7 +1933,7 @@ def test_mobile_app_is_deleted_on_save_failure(self):
19251933
)
19261934

19271935
mobile_app.refresh_from_db()
1928-
self.assertEqual(mobile_app.package_version, "100")
1936+
self.assertEqual(str(mobile_app.package_version), "0.100.0+legacy")
19291937

19301938
def test_device_is_updated_if_previous_model_exist_and_new_model_None_also(self):
19311939
with time_machine.travel("2024-01-01 00:00:00", tick=False) as traveller:

0 commit comments

Comments
 (0)