diff --git a/api/admin.py b/api/admin.py new file mode 100644 index 0000000..8c38f3f --- /dev/null +++ b/api/admin.py @@ -0,0 +1,3 @@ +from django.contrib import admin + +# Register your models here. diff --git a/api/apps.py b/api/apps.py index 5c27fd5..d87006d 100644 --- a/api/apps.py +++ b/api/apps.py @@ -1,5 +1,3 @@ -from __future__ import unicode_literals - from django.apps import AppConfig diff --git a/api/migrations/0001_initial_squash.py b/api/migrations/0001_initial.py similarity index 50% rename from api/migrations/0001_initial_squash.py rename to api/migrations/0001_initial.py index fb8ee02..ca451f9 100644 --- a/api/migrations/0001_initial_squash.py +++ b/api/migrations/0001_initial.py @@ -1,33 +1,11 @@ -# -*- coding: utf-8 -*- -# Generated by Django 1.11.2 on 2018-05-27 23:31 -from __future__ import unicode_literals +# Generated by Django 2.0.6 on 2018-06-19 04:52 from django.db import migrations, models import django.db.models.deletion -# Functions from the following migrations need manual copying. -# Move them and any dependencies into this file, then update the -# RunPython operations to refer to the local versions: -# api.migrations.0002_add_test_data - class Migration(migrations.Migration): - replaces = [ - (b'api', '0001_initial'), - (b'api', '0002_add_test_data'), - (b'api', '0003_remove_server_public_secret'), - (b'api', '0004_characterhistory'), - (b'api', '0005_ginfocharacter'), - (b'api', '0006_clan'), - (b'api', 'add_clan_id'), - (b'api', '0001_server_ip_address'), - (b'api', '0002_clean_data_schema'), - (b'api', '0003_add_server_fields'), - (b'api', '0004_server_version'), - (b'api', '0005_make_location_nullable') - ] - initial = True dependencies = [ @@ -43,42 +21,36 @@ class Migration(migrations.Migration): ('is_online', models.BooleanField()), ('steam_id', models.TextField()), ('conan_id', models.TextField()), + ('conan_clan_id', models.TextField(null=True)), ('last_online', models.DateTimeField(null=True)), ('last_killed_by', models.TextField(null=True)), ('created', models.DateTimeField(auto_now_add=True)), - ('x', models.FloatField()), - ('y', models.FloatField()), - ('z', models.FloatField()), + ('x', models.FloatField(null=True)), + ('y', models.FloatField(null=True)), + ('z', models.FloatField(null=True)), ], ), migrations.CreateModel( - name='Server', + name='CharacterHistory', fields=[ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('name', models.TextField()), - ('public_secret', models.UUIDField()), - ('private_secret', models.UUIDField()), - ('last_sync', models.DateTimeField(null=True)), + ('created', models.DateTimeField(auto_now_add=True)), + ('x', models.FloatField()), + ('y', models.FloatField()), + ('z', models.FloatField()), + ('character', models.ForeignKey(on_delete=django.db.models.deletion.DO_NOTHING, related_name='history', to='api.Character')), ], ), - migrations.AddField( - model_name='character', - name='server', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='api.Server'), - ), - migrations.RemoveField( - model_name='server', - name='public_secret', - ), migrations.CreateModel( - name='CharacterHistory', + name='Clan', fields=[ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.TextField()), + ('conan_id', models.TextField()), + ('conan_owner_id', models.TextField()), + ('motd', models.TextField()), ('created', models.DateTimeField(auto_now_add=True)), - ('x', models.FloatField()), - ('y', models.FloatField()), - ('z', models.FloatField()), - ('character', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='history', to='api.Character')), + ('owner', models.ForeignKey(null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='owner', to='api.Character')), ], ), migrations.CreateModel( @@ -86,93 +58,45 @@ class Migration(migrations.Migration): fields=[ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('ginfo_marker_uid', models.TextField()), - ('character', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='ginfo_character', related_query_name='ginfo_character', to='api.Character')), + ('character', models.ForeignKey(on_delete=django.db.models.deletion.DO_NOTHING, related_name='ginfo_character', related_query_name='ginfo_character', to='api.Character')), ], ), migrations.CreateModel( - name='Clan', + name='Server', fields=[ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('name', models.TextField()), - ('conan_id', models.TextField()), - ('conan_owner_id', models.TextField()), - ('motd', models.TextField()), - ('created', models.DateTimeField(auto_now_add=True)), - ('server', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='api.Server')), + ('ip_address', models.TextField(default='')), + ('version', models.TextField(null=True)), + ('query_port', models.TextField(null=True)), + ('max_players', models.IntegerField(null=True)), + ('tick_rate', models.IntegerField(null=True)), + ('private_secret', models.UUIDField()), + ('last_sync', models.DateTimeField(null=True)), ], ), - migrations.AddField( - model_name='character', - name='conan_clan_id', - field=models.TextField(null=True), - ), - migrations.AlterField( - model_name='character', - name='server', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='characters', to='api.Server'), - ), - migrations.AddField( - model_name='server', - name='ip_address', - field=models.TextField(default=b''), - ), migrations.CreateModel( name='ServerSyncData', fields=[ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('data', models.TextField()), ('created', models.DateTimeField(auto_now_add=True)), - ('server', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='+', to='api.Server')), + ('server', models.ForeignKey(on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='api.Server')), ], ), migrations.AddField( - model_name='character', - name='clan', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='members', to='api.Clan'), - ), - migrations.AddField( - model_name='clan', - name='owner', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='owner', to='api.Character'), - ), - migrations.AlterField( model_name='clan', name='server', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='clans', to='api.Server'), - ), - migrations.AddField( - model_name='server', - name='max_players', - field=models.IntegerField(null=True), - ), - migrations.AddField( - model_name='server', - name='query_port', - field=models.TextField(null=True), + field=models.ForeignKey(on_delete=django.db.models.deletion.DO_NOTHING, related_name='clans', to='api.Server'), ), migrations.AddField( - model_name='server', - name='tick_rate', - field=models.IntegerField(null=True), - ), - migrations.AddField( - model_name='server', - name='version', - field=models.TextField(null=True), - ), - migrations.AlterField( - model_name='character', - name='x', - field=models.FloatField(null=True), - ), - migrations.AlterField( model_name='character', - name='y', - field=models.FloatField(null=True), + name='clan', + field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='members', to='api.Clan'), ), - migrations.AlterField( + migrations.AddField( model_name='character', - name='z', - field=models.FloatField(null=True), + name='server', + field=models.ForeignKey(on_delete=django.db.models.deletion.DO_NOTHING, related_name='characters', to='api.Server'), ), ] diff --git a/api/models/__init__.py b/api/models/__init__.py index 23f62a0..7b0eb64 100644 --- a/api/models/__init__.py +++ b/api/models/__init__.py @@ -1,4 +1,3 @@ -# flake8: noqa from .character import Character from .characterhistory import CharacterHistory from .server import Server diff --git a/api/models/character.py b/api/models/character.py index b33b2b2..67c0bc2 100644 --- a/api/models/character.py +++ b/api/models/character.py @@ -12,8 +12,8 @@ class Character(models.Model): last_online = models.DateTimeField(null=True) last_killed_by = models.TextField(null=True) created = models.DateTimeField(auto_now_add=True) - clan = models.ForeignKey('api.Clan', related_name='members', null=True) - server = models.ForeignKey('api.Server', related_name='characters') + clan = models.ForeignKey('api.Clan', related_name='members', null=True, on_delete=models.DO_NOTHING) + server = models.ForeignKey('api.Server', related_name='characters', on_delete=models.DO_NOTHING) x = models.FloatField(null=True) y = models.FloatField(null=True) z = models.FloatField(null=True) diff --git a/api/models/characterhistory.py b/api/models/characterhistory.py index f01f1a4..3e3c6e9 100644 --- a/api/models/characterhistory.py +++ b/api/models/characterhistory.py @@ -3,7 +3,7 @@ class CharacterHistory(models.Model): created = models.DateTimeField(auto_now_add=True) - character = models.ForeignKey('api.Character', related_name='history') + character = models.ForeignKey('api.Character', related_name='history', on_delete=models.DO_NOTHING) x = models.FloatField() y = models.FloatField() z = models.FloatField() diff --git a/api/models/clan.py b/api/models/clan.py index 1fb596c..51d4639 100644 --- a/api/models/clan.py +++ b/api/models/clan.py @@ -9,6 +9,6 @@ class Clan(models.Model): motd = models.TextField() created = models.DateTimeField(auto_now_add=True) owner = models.ForeignKey('api.Character', related_name='owner', null=True, on_delete=models.DO_NOTHING) - server = models.ForeignKey('api.Server', related_name='clans') + server = models.ForeignKey('api.Server', related_name='clans', on_delete=models.DO_NOTHING) objects = ClanManager.as_manager() diff --git a/api/models/serversyncdata.py b/api/models/serversyncdata.py index 6f62eb2..1643b0d 100644 --- a/api/models/serversyncdata.py +++ b/api/models/serversyncdata.py @@ -2,6 +2,6 @@ class ServerSyncData(models.Model): - server = models.ForeignKey('api.Server', related_name='+') + server = models.ForeignKey('api.Server', related_name='+', on_delete=models.DO_NOTHING) data = models.TextField() created = models.DateTimeField(auto_now_add=True) diff --git a/api/plugins/ginfo/firebase_pushid.py b/api/plugins/ginfo/firebase_pushid.py index b4ba738..7e4f0a6 100644 --- a/api/plugins/ginfo/firebase_pushid.py +++ b/api/plugins/ginfo/firebase_pushid.py @@ -4,7 +4,7 @@ # https://gist.github.com/mikelehen/3596a30bd69384624c11 import random import time -from exceptions import ValueError +#from exceptions import ValueError """ diff --git a/api/plugins/ginfo/ginfo.py b/api/plugins/ginfo/ginfo.py index 5c7e650..c074e9d 100644 --- a/api/plugins/ginfo/ginfo.py +++ b/api/plugins/ginfo/ginfo.py @@ -2,8 +2,8 @@ import requests -from firebase_pushid import PushID -from ginfocharacter import GinfoCharacter +from .firebase_pushid import PushID +from .ginfocharacter import GinfoCharacter class GinfoPlugin(object): @@ -116,7 +116,7 @@ def update_position(self, character, group, access_token): json_data = response.json() if "error" in json_data: # TODO: Use proper logger - print "Error from Ginfo Firebase: " + json_data["error"] + print("Error from Ginfo Firebase: " + json_data["error"]) except: pass diff --git a/api/plugins/ginfo/ginfocharacter.py b/api/plugins/ginfo/ginfocharacter.py index f593cda..06fb318 100644 --- a/api/plugins/ginfo/ginfocharacter.py +++ b/api/plugins/ginfo/ginfocharacter.py @@ -8,5 +8,6 @@ class GinfoCharacter(models.Model): Character, related_name="ginfo_character", related_query_name="ginfo_character", + on_delete=models.DO_NOTHING ) ginfo_marker_uid = models.TextField() diff --git a/api/sync/serverdata.py b/api/sync/serverdata.py index 5841d67..03ff398 100644 --- a/api/sync/serverdata.py +++ b/api/sync/serverdata.py @@ -139,14 +139,14 @@ def sync_server_data(sync_data_id, request_get_params): .first()) if sync_data is None: - print 'Dropping sync request because data is invalid' + print('Dropping sync request because data is invalid') return sync_data.delete() server = sync_data.server if server.last_sync and server.last_sync >= sync_data.created: - print "Dropping stale sync request" + print("Dropping stale sync request") return data = json.loads(sync_data.data) diff --git a/api/tests.py b/api/tests.py new file mode 100644 index 0000000..7ce503c --- /dev/null +++ b/api/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/api/urls.py b/api/urls.py index 46efc39..abfc1bf 100644 --- a/api/urls.py +++ b/api/urls.py @@ -1,4 +1,4 @@ -from django.conf.urls import url +from django.urls import path from .views import (CharacterHistoryView, CharactersView, CharacterView, ClansView, ClanView, ServersView, ServerView, @@ -6,15 +6,15 @@ ServerInfoView) urlpatterns = [ - url(r'^$', ServersView.as_view()), - url(r'^widgets/serverinfo$', ServerInfoView.as_view()), - url(r'^(?P\d+)$', ServerView.as_view()), - url(r'^(?P\d+)/sync/characters$', SyncCharactersView.as_view()), - url(r'^(?P\d+)/clans$', ClansView.as_view()), - url(r'^(?P\d+)/clans/(?P\d+)$', ClanView.as_view()), - url(r'^(?P\d+)/clans/(?P\d+)/characters$', ClanCharactersView.as_view()), - url(r'^(?P\d+)/characters$', CharactersView.as_view()), - url(r'^(?P\d+)/characters/(?P\d+)$', CharacterView.as_view()), - url(r'^(?P\d+)/characters/(?P\d+)/history$', CharacterHistoryView.as_view()), - url(r'^(?P\d+)/widgets/activeclans$', ActiveClansView.as_view()), + path(r'', ServersView.as_view()), + path(r'widgets/serverinfo/', ServerInfoView.as_view()), + path(r'/', ServerView.as_view()), + path(r'/sync/characters/', SyncCharactersView.as_view()), + path(r'/clans/', ClansView.as_view()), + path(r'/clans//', ClanView.as_view()), + path(r'/clans//characters/', ClanCharactersView.as_view()), + path(r'/characters/', CharactersView.as_view()), + path(r'/characters//', CharacterView.as_view()), + path(r'/characters//history/', CharacterHistoryView.as_view()), + path(r'/widgets/activeclans/', ActiveClansView.as_view()), ] diff --git a/api/views/__init__.py b/api/views/__init__.py index dc11f7e..7574fe6 100644 --- a/api/views/__init__.py +++ b/api/views/__init__.py @@ -1,4 +1,3 @@ -# flake8: noqa from .character import CharacterView from .characterhistory import CharacterHistoryView from .characters import CharactersView diff --git a/manage.py b/manage.py old mode 100755 new mode 100644 index fc791ff..2a6ff7d --- a/manage.py +++ b/manage.py @@ -1,25 +1,15 @@ #!/usr/bin/env python -# flake8: noqa import os import sys -sys.dont_write_bytecode = True - if __name__ == "__main__": os.environ.setdefault("DJANGO_SETTINGS_MODULE", "serverthrallapi.settings") try: from django.core.management import execute_from_command_line - except ImportError: - # The above import may fail for some other reason. Ensure that the - # issue is really that Django is missing to avoid masking other - # exceptions on Python 2. - try: - import django - except ImportError: - raise ImportError( - "Couldn't import Django. Are you sure it's installed and " - "available on your PYTHONPATH environment variable? Did you " - "forget to activate a virtual environment?" - ) - raise + except ImportError as exc: + raise ImportError( + "Couldn't import Django. Are you sure it's installed and " + "available on your PYTHONPATH environment variable? Did you " + "forget to activate a virtual environment?" + ) from exc execute_from_command_line(sys.argv) diff --git a/requirements.txt b/requirements.txt deleted file mode 100644 index ac1b583..0000000 --- a/requirements.txt +++ /dev/null @@ -1,13 +0,0 @@ -celery==4.1.0 -dj-database-url==0.4.1 -django-celery-beat==1.0.1 -django-cors-headers==2.1.0 -django==1.11.2 -flake8==3.3.0 -gunicorn==19.6.0 -isort==4.2.5 -newrelic==2.88.1.73 -psycopg2==2.6.2 -pytz==2017.2 -requests==2.18.1 -serpy==0.1.1 diff --git a/runtime.txt b/runtime.txt deleted file mode 100644 index ba85ab9..0000000 --- a/runtime.txt +++ /dev/null @@ -1 +0,0 @@ -python-2.7.13 \ No newline at end of file diff --git a/serverthrallapi/__init__.py b/serverthrallapi/__init__.py index 7f13f4f..cf2e85f 100644 --- a/serverthrallapi/__init__.py +++ b/serverthrallapi/__init__.py @@ -1,2 +1 @@ -# flake8: noqa -from .celery import app as celery_app \ No newline at end of file +from .celery import app as celery_app diff --git a/serverthrallapi/settings.py b/serverthrallapi/settings.py index 095bd87..8b30955 100644 --- a/serverthrallapi/settings.py +++ b/serverthrallapi/settings.py @@ -1,30 +1,45 @@ -import os -import json -import dj_database_url +""" +Django settings for serverthrallapi project. + +Generated by 'django-admin startproject' using Django 2.0.6. + +For more information on this file, see +https://docs.djangoproject.com/en/2.0/topics/settings/ + +For the full list of settings and their values, see +https://docs.djangoproject.com/en/2.0/ref/settings/ +""" -ENVIRONMENT = os.environ.get('ENVIRONMENT', 'DEVELOPMENT') +import os # Build paths inside the project like this: os.path.join(BASE_DIR, ...) BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) + +# Quick-start development settings - unsuitable for production +# See https://docs.djangoproject.com/en/2.0/howto/deployment/checklist/ + # SECURITY WARNING: keep the secret key used in production secret! -SECRET_KEY = '_@#qy@r25)+ae#_=%87!b2ad1q)e-7+zd#9ty&!0n7%u=6lh)g' +SECRET_KEY = 'ey55!hebc6#y$mv+m8cm2*^lf21z1j1nsxhoiaw$pkfk5lg^+5' # SECURITY WARNING: don't run with debug turned on in production! -DEBUG = ENVIRONMENT == 'DEVELOPMENT' +DEBUG = True + +ALLOWED_HOSTS = [] -ALLOWED_HOSTS = ['*'] # Application definition + INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', + 'django.contrib.staticfiles', 'django_celery_beat', 'corsheaders', - 'api' + 'api', ] CORS_ORIGIN_ALLOW_ALL = True @@ -32,10 +47,12 @@ MIDDLEWARE = [ 'django.middleware.security.SecurityMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', - 'corsheaders.middleware.CorsMiddleware', 'django.middleware.common.CommonMiddleware', + 'django.middleware.csrf.CsrfViewMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', + 'django.middleware.clickjacking.XFrameOptionsMiddleware', + 'corsheaders.middleware.CorsMiddleware', ] ROOT_URLCONF = 'serverthrallapi.urls' @@ -60,7 +77,8 @@ # Database -# https://docs.djangoproject.com/en/1.11/ref/settings/#databases +# https://docs.djangoproject.com/en/2.0/ref/settings/#databases + DATABASES = { 'default': { 'ENGINE': 'django.db.backends.sqlite3', @@ -68,27 +86,45 @@ } } -# Update database configuration with $DATABASE_URL. -db_from_env = dj_database_url.config(conn_max_age=500) -DATABASES['default'].update(db_from_env) # Password validation -# https://docs.djangoproject.com/en/1.11/ref/settings/#auth-password-validators +# https://docs.djangoproject.com/en/2.0/ref/settings/#auth-password-validators + AUTH_PASSWORD_VALIDATORS = [ - {'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', }, - {'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', }, - {'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', }, - {'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', }, + { + 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', + }, ] + # Internationalization -# https://docs.djangoproject.com/en/1.11/topics/i18n/ +# https://docs.djangoproject.com/en/2.0/topics/i18n/ + LANGUAGE_CODE = 'en-us' + TIME_ZONE = 'UTC' + USE_I18N = True + USE_L10N = True + USE_TZ = True + +# Static files (CSS, JavaScript, Images) +# https://docs.djangoproject.com/en/2.0/howto/static-files/ + +STATIC_URL = '/static/' + # Celery CELERY_TASK_ALWAYS_EAGER = DEBUG CELERY_TASK_SERIALIZER = 'json' diff --git a/serverthrallapi/urls.py b/serverthrallapi/urls.py index 3220c19..1e1118c 100644 --- a/serverthrallapi/urls.py +++ b/serverthrallapi/urls.py @@ -1,7 +1,7 @@ -from django.conf.urls import include, url from django.contrib import admin +from django.urls import include, path urlpatterns = [ - url(r'^admin/', admin.site.urls), - url(r'^api/', include('api.urls')), + path('admin/', admin.site.urls), + path('api/', include('api.urls')), ] diff --git a/serverthrallapi/wsgi.py b/serverthrallapi/wsgi.py index ebb70bf..21aa3e6 100644 --- a/serverthrallapi/wsgi.py +++ b/serverthrallapi/wsgi.py @@ -4,7 +4,7 @@ It exposes the WSGI callable as a module-level variable named ``application``. For more information on this file, see -https://docs.djangoproject.com/en/1.11/howto/deployment/wsgi/ +https://docs.djangoproject.com/en/2.0/howto/deployment/wsgi/ """ import os diff --git a/tox.ini b/tox.ini deleted file mode 100644 index e6ad30e..0000000 --- a/tox.ini +++ /dev/null @@ -1,12 +0,0 @@ -[flake8] -ignore = E221,E128,E122,E302,D100,D101,D102,D103,E241,D200,D208 -exclude = .git,virtualenv -max-complexity = 14 -max-line-length = 140 - -[pytest] -ignore = .git,__pycache__,.cache,virtualenv - -[isort] -line_length=140 -multi_line_output=4