-
Notifications
You must be signed in to change notification settings - Fork 81
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Add new AggregatedAlgorithmTagProposal class. * Make AggregatedAlgorithmTagProposal increase each time a new AlgorithmTagProposal is created. This is most likely a temporary implementation. * Change handling of modification of AggregatedAlgorithmTagProposal to happen when adding or deleting AlgorithmTagProposals. * Remove created argument from decrease_aggregated_algorithm_tag_proposal, correct increase function to only increase amount of AggregatedTag if a new AlgorithmTagProposal is created, not when modified. * Split test_tags.py into test_tags.py file, testing AlgorithmTag and DifficultyTag classes and test_tag_proposals.py file, testing AlgorithmTagProposal and DifficultyTagProposal classes. * Expand test_save_proposals_view in TestSaveProposals class to also check AggregatedAlgorithmTagProposal. * Add AggregatedDifficultyTagProposal. * Add checking AggregatedDifficultyTagProposal in test_save_poposals_view in TestTagProposals class. Fix mistaken 'assertEquals' to 'assertEqual'. * Migrate AggregatedTagProposal classes. * Fixed test_tag_proposals.py to work correctly. * Remove unnecessary import. * Add data migration from TagProposal models to AggregatedTagProposal models. * Remove unnecessary import. * Initial version of test_data_migrations.py. It should be restructured. * Change import of migration function from static to dynamic. * Change user primary keys to keys corresponding to valid users from test_users fixture. * Change usages of creates in for loops to bulk_creates. * Change exceptions raised to logged messages. * Make increase_aggregated and decrease_aggregated functions more compact. * Add atomicity to increase and decrease functions. * Avoided repeating code through making generic function that increases tag proposals for given aggregated model. * Add exception logging for decrease_aggregated_tag_proposal function. * Simplify tests testing Aggregated Tag Proposals with helper functions. * Fix comment explaining _get_tag_amounts helper function. * Remove needles whitespaces and end of lines.
- Loading branch information
Showing
6 changed files
with
573 additions
and
193 deletions.
There are no files selected for viewing
42 changes: 42 additions & 0 deletions
42
oioioi/problems/migrations/0032_aggregated_tag_proposals.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
# Generated by Django 4.2.16 on 2024-11-28 13:39 | ||
|
||
from django.db import migrations, models | ||
import django.db.models.deletion | ||
|
||
|
||
class Migration(migrations.Migration): | ||
|
||
dependencies = [ | ||
('problems', '0031_auto_20220328_1124'), | ||
] | ||
|
||
operations = [ | ||
migrations.CreateModel( | ||
name='AggregatedDifficultyTagProposal', | ||
fields=[ | ||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), | ||
('amount', models.PositiveIntegerField(default=0)), | ||
('problem', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='problems.problem')), | ||
('tag', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='problems.difficultytag')), | ||
], | ||
options={ | ||
'verbose_name': 'aggregated difficulty tag proposal', | ||
'verbose_name_plural': 'aggregated difficulty tag proposals', | ||
'unique_together': {('problem', 'tag')}, | ||
}, | ||
), | ||
migrations.CreateModel( | ||
name='AggregatedAlgorithmTagProposal', | ||
fields=[ | ||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), | ||
('amount', models.PositiveIntegerField(default=0)), | ||
('problem', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='problems.problem')), | ||
('tag', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='problems.algorithmtag')), | ||
], | ||
options={ | ||
'verbose_name': 'aggregated algorithm tag proposal', | ||
'verbose_name_plural': 'aggregated algorithm tag proposals', | ||
'unique_together': {('problem', 'tag')}, | ||
}, | ||
), | ||
] |
48 changes: 48 additions & 0 deletions
48
oioioi/problems/migrations/0033_populate_aggregated_tag_proposals.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
# Generated by Django 4.2.16 on 2024-11-28 18:50 | ||
|
||
from django.db import migrations, models | ||
|
||
def populate_aggregated_tag_proposals(apps, schema_editor): | ||
DifficultyTagProposal = apps.get_model('problems', 'DifficultyTagProposal') | ||
AggregatedDifficultyTagProposal = apps.get_model('problems', 'AggregatedDifficultyTagProposal') | ||
AlgorithmTagProposal = apps.get_model('problems', 'AlgorithmTagProposal') | ||
AggregatedAlgorithmTagProposal = apps.get_model('problems', 'AggregatedAlgorithmTagProposal') | ||
|
||
AggregatedDifficultyTagProposal.objects.all().delete() | ||
AggregatedAlgorithmTagProposal.objects.all().delete() | ||
|
||
difficulty_data = ( | ||
DifficultyTagProposal.objects.values('problem', 'tag') | ||
.annotate(amount=models.Count('id')) | ||
) | ||
AggregatedDifficultyTagProposal.objects.bulk_create([ | ||
AggregatedDifficultyTagProposal( | ||
problem_id=entry['problem'], | ||
tag_id=entry['tag'], | ||
amount=entry['amount'] | ||
) | ||
for entry in difficulty_data | ||
]) | ||
|
||
algorithm_data = ( | ||
AlgorithmTagProposal.objects.values('problem', 'tag') | ||
.annotate(amount=models.Count('id')) | ||
) | ||
AggregatedAlgorithmTagProposal.objects.bulk_create([ | ||
AggregatedAlgorithmTagProposal( | ||
problem_id=entry['problem'], | ||
tag_id=entry['tag'], | ||
amount=entry['amount'] | ||
) | ||
for entry in algorithm_data | ||
]) | ||
|
||
class Migration(migrations.Migration): | ||
|
||
dependencies = [ | ||
('problems', '0032_aggregated_tag_proposals'), | ||
] | ||
|
||
operations = [ | ||
migrations.RunPython(populate_aggregated_tag_proposals) | ||
] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,91 @@ | ||
# coding: utf-8 | ||
|
||
from django.test import TestCase | ||
from django.apps import apps | ||
from django.contrib.auth.models import User | ||
from oioioi.problems.models import ( | ||
AggregatedDifficultyTagProposal, | ||
AggregatedAlgorithmTagProposal, | ||
AlgorithmTagProposal, | ||
DifficultyTagProposal, | ||
Problem, | ||
AlgorithmTag, | ||
DifficultyTag, | ||
) | ||
import importlib | ||
|
||
# Dynamically import the function applying data migration for AggregatedTagProposals. | ||
# This is necessary, since the name of the migration file causes a syntax error when imported normally. | ||
migration_module = importlib.import_module('oioioi.problems.migrations.0033_populate_aggregated_tag_proposals') | ||
populate_aggregated_tag_proposals = getattr(migration_module, 'populate_aggregated_tag_proposals') | ||
|
||
def _get_tag_amounts(aggregated_model, problem): | ||
"""Returns a dictionary mapping tags to their amounts for a given problem.""" | ||
return { | ||
proposal.tag: proposal.amount | ||
for proposal in aggregated_model.objects.filter(problem=problem) | ||
} | ||
|
||
class PopulateAggregatedTagProposalsTest(TestCase): | ||
fixtures = [ | ||
'test_users', | ||
'test_problem_search', | ||
'test_algorithm_tags', | ||
'test_difficulty_tags', | ||
] | ||
|
||
def setUp(self): | ||
self.problem1 = Problem.objects.get(pk=1) | ||
self.problem2 = Problem.objects.get(pk=2) | ||
self.algorithm_tag1 = AlgorithmTag.objects.get(pk=1) | ||
self.algorithm_tag2 = AlgorithmTag.objects.get(pk=2) | ||
self.difficulty_tag1 = DifficultyTag.objects.get(pk=1) | ||
self.difficulty_tag2 = DifficultyTag.objects.get(pk=2) | ||
self.user1 = User.objects.get(pk=1000) | ||
self.user2 = User.objects.get(pk=1001) | ||
self.user3 = User.objects.get(pk=1002) | ||
|
||
DifficultyTagProposal.objects.bulk_create([ | ||
DifficultyTagProposal(problem=self.problem1, tag=self.difficulty_tag1, user=self.user1), | ||
DifficultyTagProposal(problem=self.problem1, tag=self.difficulty_tag1, user=self.user2), | ||
DifficultyTagProposal(problem=self.problem1, tag=self.difficulty_tag2, user=self.user3), | ||
DifficultyTagProposal(problem=self.problem2, tag=self.difficulty_tag2, user=self.user2), | ||
]) | ||
|
||
AlgorithmTagProposal.objects.bulk_create([ | ||
AlgorithmTagProposal(problem=self.problem1, tag=self.algorithm_tag1, user=self.user1), | ||
AlgorithmTagProposal(problem=self.problem1, tag=self.algorithm_tag2, user=self.user1), | ||
AlgorithmTagProposal(problem=self.problem1, tag=self.algorithm_tag1, user=self.user3), | ||
AlgorithmTagProposal(problem=self.problem2, tag=self.algorithm_tag2, user=self.user1), | ||
AlgorithmTagProposal(problem=self.problem2, tag=self.algorithm_tag1, user=self.user2), | ||
AlgorithmTagProposal(problem=self.problem2, tag=self.algorithm_tag2, user=self.user2), | ||
AlgorithmTagProposal(problem=self.problem2, tag=self.algorithm_tag2, user=self.user3), | ||
]) | ||
|
||
def test_populate_aggregated_tag_proposals(self): | ||
AggregatedAlgorithmTagProposal.objects.filter(problem=self.problem2).delete() | ||
AggregatedDifficultyTagProposal.objects.filter(problem=self.problem1).delete() | ||
|
||
populate_aggregated_tag_proposals(apps, None) | ||
|
||
self.assertEqual(AlgorithmTagProposal.objects.count(), 7) | ||
self.assertEqual(AggregatedAlgorithmTagProposal.objects.count(), 4) | ||
self.assertEqual( | ||
_get_tag_amounts(AggregatedAlgorithmTagProposal, self.problem1), | ||
{self.algorithm_tag1: 2, self.algorithm_tag2: 1} | ||
) | ||
self.assertEqual( | ||
_get_tag_amounts(AggregatedAlgorithmTagProposal, self.problem2), | ||
{self.algorithm_tag1: 1, self.algorithm_tag2: 3} | ||
) | ||
|
||
self.assertEqual(DifficultyTagProposal.objects.count(), 4) | ||
self.assertEqual(AggregatedDifficultyTagProposal.objects.count(), 3) | ||
self.assertEqual( | ||
_get_tag_amounts(AggregatedDifficultyTagProposal, self.problem1), | ||
{self.difficulty_tag1: 2, self.difficulty_tag2: 1} | ||
) | ||
self.assertEqual( | ||
_get_tag_amounts(AggregatedDifficultyTagProposal, self.problem2), | ||
{self.difficulty_tag2: 1} | ||
) |
Oops, something went wrong.