Skip to content

Commit

Permalink
Merge pull request #1 from skkallayath/develop
Browse files Browse the repository at this point in the history
Develop
  • Loading branch information
skkallayath authored Nov 25, 2017
2 parents a216a05 + 9d42cd6 commit 8460670
Show file tree
Hide file tree
Showing 75 changed files with 2,519 additions and 257 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -105,3 +105,6 @@ ENV/

# db
*.sqlite3

# uploaded icons
icons/
90 changes: 89 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,90 @@
# django-knockout-fixture-generator
Create knockout fixtures using django
Create and manage tournament fixtures (knockout).


The project uses django framework for backend and admin panel and Jquery Brackets to display the fixture.

## Setup

Clone the project
```
$ git clone https://github.com/skkallayath/django-knockout-fixture-generator
$ cd django-knockout-fixture-generator
```

Create virtual env
```
$ python3 -m virtualenv env
$ source env/bin/activate
```

Install packages from requirements.txt
```
(env) $ pip install -r requirement.txt
```

Setup databse
```
(env) $ python manage.py migrate
```

Create superuser to access admin panel
```
(env) $ python manage.py createsuperuser
```

## Run application

```
(env) $ python manage.py runserver
```


## Admin

Login to `/admin` with the credentials of superuser.

### Add Fixture

![alt text](https://raw.githubusercontent.com/skkallayath/django-knockout-fixture-generator/master/screenshots/add%20fixture.png)


### Generate Knockout Fixture

![alt text](https://raw.githubusercontent.com/skkallayath/django-knockout-fixture-generator/master/screenshots/Generate%20fixture.png)

![alt text](https://raw.githubusercontent.com/skkallayath/django-knockout-fixture-generator/master/screenshots/Fixture%20generated.png)

### Matches of a fixture

![alt text](https://raw.githubusercontent.com/skkallayath/django-knockout-fixture-generator/master/screenshots/Matches.png)


### Updating match results

![alt text](https://raw.githubusercontent.com/skkallayath/django-knockout-fixture-generator/master/screenshots/Update%20results.png)


## UI

### Home page

![alt text](https://raw.githubusercontent.com/skkallayath/django-knockout-fixture-generator/master/screenshots/fixtures%20ui.png)

### Upcoming matches

![alt text](https://raw.githubusercontent.com/skkallayath/django-knockout-fixture-generator/master/screenshots/matches%20ui.png)

### Fixture

![alt text](https://raw.githubusercontent.com/skkallayath/django-knockout-fixture-generator/master/screenshots/fixture%20ui.png)


## About

Mail me to skkallyath@gmail.com


## License

GPL © [Sharafudheen](http://sharafu.in)
96 changes: 60 additions & 36 deletions fixture/admin.py
Original file line number Diff line number Diff line change
@@ -1,50 +1,58 @@
from django.contrib import admin
from .models import Player, Fixture, Match
from .utils import genearte_random_string
from django.http import HttpResponseRedirect
import math
from datetime import timedelta
from django.contrib import messages

# Register your models here.

def create_fixture(modeladmin, request, queryset):
fixture = Fixture(name=genearte_random_string())
fixture.save()
for player in queryset:
fixture.players.add(player)
fixture.save()
create_schedules(fixture)
return HttpResponseRedirect("/admin/fixture/fixture/%d/change/" % (fixture.pk))
def generate_schedules(modeladmin, request, queryset):
for fixture in queryset:
fixture.matches.all().delete()
fixture.save()
create_schedules(fixture)
messages.add_message(request, messages.SUCCESS, 'Fixture generated for {}.'.format(fixture))

def create_schedules(fixture):
_player_list = list(fixture.players.all())
_player_list = list(fixture.players.order_by('rank'))
_len = len(_player_list)
_rounds = int(math.ceil(math.log(_len, 2)))
_number_of_players = int(math.pow(_rounds, 2))
print(_len, _number_of_players, _rounds)
if _len == 0:
return
fixture.rounds = int(math.ceil(math.log(_len, 2)))
fixture.save()

_number_of_players = int(math.pow(2, fixture.rounds))
_players = _player_list + ([None]*(_number_of_players - _len))

print(_players)
_date = fixture.start_date
_matches = []
_counter = 1
_round = 1
for i in range(_number_of_players//2):
_match = Match()
_match.fixture = fixture
_match.match_round = _round
_match.player_1 = _players[i]
_match.player_2 = _players[-1-i]
if _players[i]:
_match.player_1 = _players[i]
if _players[-1-i]:
_match.player_2 = _players[-1-i]

if _match.player_1 and _match.player_2:
_match.match_number = _counter
_match.date = _date
if _counter%fixture.matches_per_day ==0:
print("date change",_counter)
_date = _date + timedelta(days=1)
print(_counter)
_counter = _counter+1

_match.save()

_matches.append(_match)

generate_next_rounds(fixture, _matches, _round+1, _counter)

def generate_next_rounds(fixture, matches, _round, counter):
generate_next_rounds(fixture, _matches, _round+1, _counter, _date + timedelta(days=1))

def generate_next_rounds(fixture, matches, _round, counter, _date):
_matches = []
_len = len(matches)
if _len <=1:
Expand All @@ -64,48 +72,64 @@ def generate_next_rounds(fixture, matches, _round, counter):
_match.player_2 = _right_winner

_match.match_number = counter
_match.date = _date
if counter%fixture.matches_per_day == 0:
print("date change", counter)
_date = _date + timedelta(days=1)
print(counter)
counter = counter+1

_match.save()

_matches.append(_match)

generate_next_rounds(fixture, _matches, _round+1, counter)
generate_next_rounds(fixture, _matches, _round+1, counter, _date + timedelta(days=1))

create_fixture.short_description = "Create knockout fixture"
generate_schedules.short_description = "Generate knockout fixture"

class PlayerAdmin(admin.ModelAdmin):
list_display = ['name']
list_display = ['name', 'rank', 'fixture']
ordering = ['name']
actions = [create_fixture]

class MatchAdmin(admin.ModelAdmin):
readonly_fields = ('match_number','fixture','match_round', 'left_previous','right_previous','player_1', 'player_2', 'status','winning_palyer', )
fields = ('match_number', 'player_1', 'player_2','match_round', 'status','winner', 'winning_palyer', )
readonly_fields = ('description', 'name', 'match_number','fixture','match_round', 'left_previous','right_previous','player_1', 'player_2', 'status','winner',)
fields = ('date', 'name', 'description', 'fixture', 'match_number', 'player_1', 'player_2','match_round', 'status','winner', 'player_1_score', 'player_2_score', )
def has_add_permission(self, request):
return False

can_delete = False

def has_delete_permission(self, request, obj=None):
return False

def get_actions(self, request):
actions = super().get_actions(request)
if 'delete_selected' in actions:
del actions['delete_selected']
print(actions.items())
return actions

class MatchInline(admin.TabularInline):
model = Match
ordering = ['match_number']
readonly_fields = ('match_number','fixture','match_round', 'left_previous','right_previous','player_1', 'player_2', 'status','winning_palyer', )
fields = ('match_number', 'player_1', 'player_2','match_round', 'status','winner', 'winning_palyer', )
ordering = ['match_number',]
readonly_fields = ('description', 'match_number','fixture','match_round', 'left_previous','right_previous','player_1', 'player_2', 'status','winner', )
fields = ('date', 'description', 'match_number', 'player_1', 'player_2','match_round', 'status','winner','player_1_score', 'player_2_score', )
can_delete = False
def has_add_permission(self, request):
return False

class PlayerInline(admin.TabularInline):
model = Player
ordering = ['rank']
fields = ('name', 'rank', )

class FixtureAdmin(admin.ModelAdmin):
readonly_fields = ('players',)
fields = ('name', 'description', 'players', )
readonly_fields = ('rounds',)
fields = ('name', 'description', 'icon', 'start_date', 'matches_per_day', )
inlines = [
PlayerInline,
MatchInline,
]
def has_add_permission(self, request):
return False
actions = [generate_schedules]

admin.site.register(Player, PlayerAdmin)
admin.site.register(Match, MatchAdmin)

admin.site.register(Fixture, FixtureAdmin)
50 changes: 45 additions & 5 deletions fixture/migrations/0001_initial.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.7 on 2017-11-21 06:56
# Generated by Django 1.11.7 on 2017-11-23 07:55
from __future__ import unicode_literals

from django.db import migrations, models
import django.db.models.deletion


class Migration(migrations.Migration):
Expand All @@ -17,19 +18,58 @@ class Migration(migrations.Migration):
name='Fixture',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=100)),
('pub_date', models.DateTimeField(null=True, verbose_name='date published')),
('icon', models.FileField(blank=True, upload_to='icons/%Y/%m/%d/')),
('name', models.CharField(max_length=256)),
('description', models.TextField(blank=True, default='', null=True)),
],
),
migrations.CreateModel(
name='Match',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('match_number', models.IntegerField(blank=True, null=True)),
('match_round', models.IntegerField()),
('status', models.CharField(choices=[('BYE', 'BYE'), ('Not Scheduled', 'Not Scheduled'), ('Scheduled', 'Scheduled'), ('Finished', 'Finished')], default='Not Scheduled', max_length=20)),
('winner', models.CharField(blank=True, choices=[('Player 1', 'Player 1'), ('Player 2', 'Player 2')], max_length=20)),
('fixture', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='matches', to='fixture.Fixture')),
('left_previous', models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='left_next_match', to='fixture.Match')),
],
options={
'verbose_name_plural': 'Matches',
},
),
migrations.CreateModel(
name='Player',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('rank', models.IntegerField()),
('name', models.CharField(max_length=100)),
('fixture', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='players', to='fixture.Fixture')),
],
),
migrations.AddField(
model_name='fixture',
name='fixtures',
field=models.ManyToManyField(to='fixture.Player'),
model_name='match',
name='player_1',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='left_matches', to='fixture.Player'),
),
migrations.AddField(
model_name='match',
name='player_2',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='right_matches', to='fixture.Player'),
),
migrations.AddField(
model_name='match',
name='right_previous',
field=models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='right_next_match', to='fixture.Match'),
),
migrations.AddField(
model_name='match',
name='winning_palyer',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='matches_won', to='fixture.Player'),
),
migrations.AlterUniqueTogether(
name='player',
unique_together=set([('rank', 'fixture')]),
),
]
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.7 on 2017-11-21 08:15
# Generated by Django 1.11.7 on 2017-11-23 11:02
from __future__ import unicode_literals

from django.db import migrations
Expand All @@ -12,9 +12,8 @@ class Migration(migrations.Migration):
]

operations = [
migrations.RenameField(
migrations.RemoveField(
model_name='fixture',
old_name='fixtures',
new_name='players',
name='icon',
),
]
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.7 on 2017-11-21 20:21
# Generated by Django 1.11.7 on 2017-11-23 11:05
from __future__ import unicode_literals

from django.db import migrations, models
Expand All @@ -8,13 +8,14 @@
class Migration(migrations.Migration):

dependencies = [
('fixture', '0009_auto_20171122_0126'),
('fixture', '0002_remove_fixture_icon'),
]

operations = [
migrations.AddField(
model_name='fixture',
name='description',
field=models.CharField(blank=True, default='', max_length=1024),
name='icon',
field=models.ImageField(default=None, upload_to='icons', verbose_name='image'),
preserve_default=False,
),
]
Loading

0 comments on commit 8460670

Please sign in to comment.