Skip to content

Commit

Permalink
Merge branch 'develop' of https://github.com/johnpooch/diplomacy into…
Browse files Browse the repository at this point in the history
… develop
  • Loading branch information
johnpooch committed Apr 28, 2021
2 parents 93d76f5 + 81a58bf commit d18dc72
Show file tree
Hide file tree
Showing 13 changed files with 95 additions and 34 deletions.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@

A web browser version of the classic strategy board game Diplomacy.

We are massive fans of the game and have been happily using some of the
We are fans of the game and enjoy playing on the
existing web versions like [Play Diplomacy][play diplomacy],
[Backstabbr][backstabbr], and others for years. We decided to build a new
[Backstabbr][backstabbr], and others. We decided to build a new
version of the game to try to bring together and improve on some of the
features of the existing versions. We also wanted to make the project open
source so that it could be maintained by an enthusiastic community of diplomacy
Expand Down
7 changes: 7 additions & 0 deletions core/game.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,12 @@ def process_turn(turn, dry_run=False):
the turn the adjudicator. Update the turn based on the adjudicator
response.
"""
# Remove pending turnend
try:
turn.turnend.delete()
except models.TurnEnd.DoesNotExist:
pass

logger.info('Processing turn: {}'.format(turn))
turn_data = TurnSerializer(turn).data
outcome = process_game_state(turn_data)
Expand All @@ -40,6 +46,7 @@ def process_turn(turn, dry_run=False):
winning_nation = new_turn.check_for_winning_nation()
if winning_nation:
turn.game.set_winner(winning_nation)

return new_turn


Expand Down
28 changes: 28 additions & 0 deletions core/migrations/0008_auto_20210426_1518.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# Generated by Django 3.1.7 on 2021-04-26 14:18

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('core', '0007_auto_20210425_1732'),
]

operations = [
migrations.AlterField(
model_name='game',
name='build_deadline',
field=models.CharField(choices=[(None, 'None'), ('twelve_hours', '12 hours'), ('twenty_four_hours', '24 hours'), ('two_days', '2 days'), ('three_days', '3 days'), ('five_days', '5 days'), ('seven_days', '7 days')], max_length=100, null=True),
),
migrations.AlterField(
model_name='game',
name='order_deadline',
field=models.CharField(choices=[(None, 'None'), ('twelve_hours', '12 hours'), ('twenty_four_hours', '24 hours'), ('two_days', '2 days'), ('three_days', '3 days'), ('five_days', '5 days'), ('seven_days', '7 days')], max_length=100, null=True),
),
migrations.AlterField(
model_name='game',
name='retreat_deadline',
field=models.CharField(choices=[(None, 'None'), ('twelve_hours', '12 hours'), ('twenty_four_hours', '24 hours'), ('two_days', '2 days'), ('three_days', '3 days'), ('five_days', '5 days'), ('seven_days', '7 days')], max_length=100, null=True),
),
]
7 changes: 2 additions & 5 deletions core/models/game.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,19 +85,16 @@ class Game(models.Model, AutoSlug):
order_deadline = models.CharField(
null=True,
choices=DeadlineFrequency.CHOICES,
default=DeadlineFrequency.TWENTY_FOUR_HOURS,
max_length=100,
)
retreat_deadline = models.CharField(
null=False,
null=True,
choices=DeadlineFrequency.CHOICES,
default=DeadlineFrequency.TWENTY_FOUR_HOURS,
max_length=100,
)
build_deadline = models.CharField(
null=False,
null=True,
choices=DeadlineFrequency.CHOICES,
default=DeadlineFrequency.TWELVE_HOURS,
max_length=100,
)
process_on_finalized_orders = models.BooleanField(
Expand Down
7 changes: 4 additions & 3 deletions core/models/turn.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,10 @@ def new(self, **kwargs):
instance.
"""
turn = self.create(**kwargs)
td = timespan.get_timespan(turn.deadline).timedelta
turn_end_dt = timezone.now() + td
TurnEnd.objects.new(turn, turn_end_dt)
if turn.deadline:
td = timespan.get_timespan(turn.deadline).timedelta
turn_end_dt = timezone.now() + td
TurnEnd.objects.new(turn, turn_end_dt)
return turn


Expand Down
6 changes: 6 additions & 0 deletions core/signals.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
from core.models.base import DrawStatus, GameStatus
from core.models.mixins import AutoSlug
from core.utils.models import super_receiver
from project.celery import app


def set_id(instance):
Expand Down Expand Up @@ -72,6 +73,11 @@ def set_game_state_if_draw_accepted(sender, instance, **kwargs):
game.set_winners(*nations)


@receiver(signals.pre_delete, sender=models.TurnEnd)
def revoke_task_on_delete(sender, instance, **kwargs):
app.control.revoke(instance.task_id)


@receiver(reset_password_token_created)
def password_reset_token_created(sender, instance, reset_password_token, *args, **kwargs):
"""
Expand Down
6 changes: 6 additions & 0 deletions core/tests/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@


apply_async_path = 'core.tasks.process_turn.apply_async'
revoke_task_on_delete_path = 'celery.app.control.Control.revoke'


set_status_path = 'core.models.Draw.set_status'
Expand Down Expand Up @@ -55,6 +56,11 @@ def patch_process_turn_apply_async(self):
self.apply_async = apply_async.start()
self.apply_async.return_value = AsyncResult(id=self.dummy_task_id)

def patch_revoke_task_on_delete(self):
self.revoke_task_on_delete_patcher = patch(revoke_task_on_delete_path)
self.revoke_task_on_delete_patch = self.revoke_task_on_delete_patcher.start()
self.addCleanup(self.revoke_task_on_delete_patcher.stop)

def patch_set_status(self):
self.set_status_patcher = patch(set_status_path)
self.set_status_patch = self.set_status_patcher.start()
Expand Down
5 changes: 4 additions & 1 deletion core/tests/test_game.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
from django.utils import timezone

from core import factories, models
from core.models.base import GameStatus, Phase, Season
from core.models.base import DeadlineFrequency, GameStatus, Phase, Season
from core.tests import DiplomacyTestCaseMixin


Expand All @@ -18,6 +18,9 @@ def setUp(self):
variant=self.variant,
num_players=7,
created_by=self.users[0],
order_deadline=DeadlineFrequency.TWENTY_FOUR_HOURS,
retreat_deadline=DeadlineFrequency.TWELVE_HOURS,
build_deadline=DeadlineFrequency.TWELVE_HOURS,
)
self.game.participants.add(*self.users)
self.patch_process_turn_apply_async()
Expand Down
1 change: 1 addition & 0 deletions core/tests/test_set_turn_end.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ def setUp(self):
self.yesterday_string = self.yesterday.strftime(self.date_format)

self.patch_process_turn_apply_async()
self.patch_revoke_task_on_delete()

def call_command(self, slug, date_string):
call_command(self.command, slug, date_string, stdout=self.out)
Expand Down
21 changes: 16 additions & 5 deletions core/tests/test_turn.py
Original file line number Diff line number Diff line change
Expand Up @@ -135,11 +135,6 @@ def test_turn_end_none(self):
turn = self.create_test_turn(game=self.game)
self.assertIsNone(turn.turn_end)

def test_turn_end(self):
turn = self.create_test_turn(game=self.game, turn_end=True)
turn_end = models.TurnEnd.objects.get()
self.assertEqual(turn.turn_end, turn_end)

def test_deadline_order(self):
self.game.order_deadline = DeadlineFrequency.SEVEN_DAYS
self.game.save()
Expand Down Expand Up @@ -317,9 +312,12 @@ def setUp(self):
self.user = factories.UserFactory()
self.game = self.create_test_game()
self.game.participants.add(self.user)
self.patch_process_turn_apply_async()

def test_new(self):
self.assertEqual(models.TurnEnd.objects.count(), 0)
self.game.retreat_deadline = DeadlineFrequency.FIVE_DAYS
self.game.save()
turn = models.Turn.objects.new(
game=self.game,
phase=Phase.RETREAT,
Expand All @@ -329,3 +327,16 @@ def test_new(self):
turn_end = models.TurnEnd.objects.get()
self.assertEqual(turn_end.turn, turn)
self.assertSimilarTimestamp(turn_end.datetime, self.tomorrow)

def test_new_no_deadline(self):
self.assertEqual(models.TurnEnd.objects.count(), 0)
self.game.order_deadline = None
self.game.save()
turn = models.Turn.objects.new(
game=self.game,
phase=Phase.ORDER,
season=Season.SPRING,
year=1901,
)
with self.assertRaises(models.TurnEnd.DoesNotExist):
turn.turnend
6 changes: 5 additions & 1 deletion core/tests/test_turn_end.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
from core import factories, models
from core.tasks import process_turn
from core.tests import DiplomacyTestCaseMixin
from core.models.base import Phase, Season
from core.models.base import DeadlineFrequency, Phase, Season


process_path = 'core.tasks._process_turn'
Expand All @@ -23,8 +23,12 @@ def setUp(self):
variant=self.variant,
num_players=7,
created_by=self.user,
order_deadline=DeadlineFrequency.TWENTY_FOUR_HOURS,
retreat_deadline=DeadlineFrequency.TWELVE_HOURS,
build_deadline=DeadlineFrequency.TWELVE_HOURS,
)
self.patch_process_turn_apply_async()
self.patch_revoke_task_on_delete()

def create_turn(self):
return models.Turn.objects.create(
Expand Down
5 changes: 5 additions & 0 deletions service/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,7 @@ class Meta:
fields = (
'id',
'name',
'variant',
)


Expand Down Expand Up @@ -384,6 +385,10 @@ class Meta:
'piece_type',
'via_convoy',
'turn',
'outcome',
'illegal',
'illegal_code',
'illegal_verbose',
)
read_only_fields = (
'nation',
Expand Down
26 changes: 9 additions & 17 deletions service/tests/test_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@
from core import factories, models
from core.tests import DiplomacyTestCaseMixin
from core.models.base import (
DrawStatus, DrawResponse, GameStatus, OrderType, Phase, PieceType, Season,
SurrenderStatus
DeadlineFrequency, DrawStatus, DrawResponse, GameStatus, OrderType, Phase, PieceType,
Season, SurrenderStatus
)
from service import validators

Expand Down Expand Up @@ -64,12 +64,9 @@ def test_list_games_converts_camel_to_snake_query_params(self):
self.assertEqual(len(response.data), 1)


class TestGetCreateGame(BaseTestCase):
class TestCreateGame(BaseTestCase):

def test_get_create_game(self):
"""
Get create game returns a form.
"""
user = factories.UserFactory()
self.client.force_authenticate(user=user)

Expand All @@ -81,16 +78,10 @@ def test_get_create_game(self):
)

def test_get_create_game_unauthenticated(self):
"""
Cannot get create game when unauthenticated.
"""
url = reverse('create-game')
response = self.client.get(url)
self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED)


class TestCreateGame(BaseTestCase):

def test_post_invalid_game(self):
"""
Posting invalid game data causes a 400 error.
Expand All @@ -104,17 +95,15 @@ def test_post_invalid_game(self):
self.assertEqual(response.status_code, 400)

def test_post_valid_game(self):
"""
Posting valid game data creates a game instance and redirects to
`user-games` view. The user is automatically added as a participant of
the game.
"""
user = factories.UserFactory()
self.client.force_authenticate(user=user)
variant = models.Variant.objects.get(id='standard')

data = {
'name': 'Test Game',
'order_deadline': DeadlineFrequency.TWENTY_FOUR_HOURS,
'retreat_deadline': DeadlineFrequency.TWELVE_HOURS,
'build_deadline': DeadlineFrequency.TWELVE_HOURS,
}
url = reverse('create-game')
response = self.client.post(url, data, format='json')
Expand All @@ -126,6 +115,9 @@ def test_post_valid_game(self):
created_by=user,
variant=variant,
participants=user,
order_deadline=DeadlineFrequency.TWENTY_FOUR_HOURS,
retreat_deadline=DeadlineFrequency.TWELVE_HOURS,
build_deadline=DeadlineFrequency.TWELVE_HOURS,
)
)

Expand Down

0 comments on commit d18dc72

Please sign in to comment.