├── example
├── example
│ ├── __init__.py
│ ├── wsgi.py
│ ├── urls.py
│ └── settings.py
└── manage.py
├── gcm
├── migrations
│ ├── __init__.py
│ └── 0001_initial.py
├── __init__.py
├── routers.py
├── settings.py
├── serializers.py
├── admin.py
├── api.py
├── locale
│ └── pt_BR
│ │ └── LC_MESSAGES
│ │ └── django.po
├── utils.py
├── models.py
└── tests.py
├── .travis-requirements.txt
├── MANIFEST.in
├── docs
├── _themes
│ ├── kr
│ │ ├── theme.conf
│ │ ├── layout.html
│ │ ├── relations.html
│ │ └── static
│ │ │ ├── small_flask.css
│ │ │ └── flasky.css_t
│ ├── kr_small
│ │ ├── theme.conf
│ │ ├── layout.html
│ │ └── static
│ │ │ └── flasky.css_t
│ ├── README.rst
│ ├── LICENSE
│ └── flask_theme_support.py
├── index.rst
├── tutorial.rst
├── make.bat
├── Makefile
└── conf.py
├── setup.cfg
├── .travis.yml
├── .gitignore
├── setup.py
├── LICENSE
└── README.rst
/example/example/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/gcm/migrations/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/gcm/__init__.py:
--------------------------------------------------------------------------------
1 | VERSION = '1.0.0'
2 |
--------------------------------------------------------------------------------
/.travis-requirements.txt:
--------------------------------------------------------------------------------
1 | coverage>=3.7.1
2 | coveralls>=0.4.2
--------------------------------------------------------------------------------
/MANIFEST.in:
--------------------------------------------------------------------------------
1 | include LICENSE
2 | include README.rst
3 | recursive-include docs *
--------------------------------------------------------------------------------
/docs/_themes/kr/theme.conf:
--------------------------------------------------------------------------------
1 | [theme]
2 | inherit = basic
3 | stylesheet = flasky.css
4 | pygments_style = flask_theme_support.FlaskyStyle
5 |
6 | [options]
7 | touch_icon =
8 |
--------------------------------------------------------------------------------
/gcm/routers.py:
--------------------------------------------------------------------------------
1 | from rest_framework.routers import DefaultRouter
2 |
3 | from gcm.api import DevicesViewSet
4 |
5 | router = DefaultRouter(trailing_slash=False)
6 |
7 | router.register(r'devices', DevicesViewSet)
8 |
9 | urlpatterns = router.urls
10 |
--------------------------------------------------------------------------------
/docs/_themes/kr_small/theme.conf:
--------------------------------------------------------------------------------
1 | [theme]
2 | inherit = basic
3 | stylesheet = flasky.css
4 | nosidebar = true
5 | pygments_style = flask_theme_support.FlaskyStyle
6 |
7 | [options]
8 | index_logo = ''
9 | index_logo_height = 120px
10 | github_fork = ''
11 |
--------------------------------------------------------------------------------
/gcm/settings.py:
--------------------------------------------------------------------------------
1 | from django.conf import settings
2 |
3 | GCM_DEVICE_MODEL = getattr(settings, 'GCM_DEVICE_MODEL', 'gcm.Device')
4 |
5 | GCM_ANDROID_APIKEY = getattr(settings, 'GCM_ANDROID_APIKEY', None)
6 |
7 | GCM_IOS_APIKEY = getattr(settings, 'GCM_IOS_APIKEY', None)
8 |
--------------------------------------------------------------------------------
/setup.cfg:
--------------------------------------------------------------------------------
1 | [bdist_wheel]
2 | # This flag says that the code is written to work on both Python 2 and Python
3 | # 3. If at all possible, it is good practice to do this. If you cannot, you
4 | # will need to generate wheels for each Python version that you support.
5 | universal=1
--------------------------------------------------------------------------------
/example/manage.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | import os
3 |
4 | import sys
5 |
6 | if __name__ == "__main__":
7 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "example.settings")
8 |
9 | from django.core.management import execute_from_command_line
10 |
11 | execute_from_command_line(sys.argv)
12 |
--------------------------------------------------------------------------------
/gcm/serializers.py:
--------------------------------------------------------------------------------
1 | from rest_framework.serializers import ModelSerializer
2 |
3 | from gcm.utils import get_device_model
4 |
5 | Device = get_device_model()
6 |
7 |
8 | class DeviceSerializer(ModelSerializer):
9 | class Meta:
10 | model = Device
11 | exclude = ('id', 'creation_date', 'modified_date', 'is_active')
12 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: python
2 | python:
3 | - "2.7"
4 | - "3.4"
5 | env:
6 | - DJANGO_VERSION=1.7
7 | - DJANGO_VERSION=1.8
8 | install:
9 | - pip install -q Django==$DJANGO_VERSION
10 | - pip install -e .
11 | - pip install -r .travis-requirements.txt
12 | script:
13 | coverage run --source=gcm example/manage.py test gcm
14 | after_success:
15 | coveralls
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/gcm/admin.py:
--------------------------------------------------------------------------------
1 | from django.contrib import admin
2 |
3 | from gcm.utils import get_device_model
4 |
5 | Device = get_device_model()
6 |
7 |
8 | @admin.register(Device)
9 | class DeviceAdmin(admin.ModelAdmin):
10 | list_display = ['dev_id', 'dev_type', 'modified_date', 'is_active']
11 | search_fields = ('dev_id', 'dev_type')
12 | list_filter = ['is_active']
13 | date_hierarchy = 'modified_date'
14 | readonly_fields = ('dev_id', 'reg_id')
15 |
--------------------------------------------------------------------------------
/example/example/wsgi.py:
--------------------------------------------------------------------------------
1 | """
2 | WSGI config for example project.
3 |
4 | It exposes the WSGI callable as a module-level variable named ``application``.
5 |
6 | For more information on this file, see
7 | https://docs.djangoproject.com/en/1.8/howto/deployment/wsgi/
8 | """
9 |
10 | import os
11 |
12 | from django.core.wsgi import get_wsgi_application
13 |
14 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "example.settings")
15 |
16 | application = get_wsgi_application()
17 |
--------------------------------------------------------------------------------
/gcm/api.py:
--------------------------------------------------------------------------------
1 | from rest_framework.permissions import IsAuthenticated
2 | from rest_framework.viewsets import ModelViewSet
3 |
4 | from gcm.serializers import DeviceSerializer
5 | from gcm.utils import get_device_model
6 |
7 | Device = get_device_model()
8 |
9 |
10 | class DevicesViewSet(ModelViewSet):
11 | queryset = Device.objects.all()
12 | serializer_class = DeviceSerializer
13 | permission_classes = [IsAuthenticated]
14 | http_method_names = ['post', 'delete', 'options']
15 |
--------------------------------------------------------------------------------
/docs/_themes/README.rst:
--------------------------------------------------------------------------------
1 | krTheme Sphinx Style
2 | ====================
3 |
4 | This repository contains sphinx styles Kenneth Reitz uses in most of
5 | his projects. It is a drivative of Mitsuhiko's themes for Flask and Flask related
6 | projects. To use this style in your Sphinx documentation, follow
7 | this guide:
8 |
9 | 1. put this folder as _themes into your docs folder. Alternatively
10 | you can also use git submodules to check out the contents there.
11 |
12 | 2. add this to your conf.py: ::
13 |
14 | sys.path.append(os.path.abspath('_themes'))
15 | html_theme_path = ['_themes']
16 | html_theme = 'kr'
17 |
18 | The following themes exist:
19 |
20 | **kr**
21 | the standard flask documentation theme for large projects
22 |
23 | **kr_small**
24 | small one-page theme. Intended to be used by very small addon libraries.
25 |
26 |
--------------------------------------------------------------------------------
/docs/_themes/kr_small/layout.html:
--------------------------------------------------------------------------------
1 | {% extends "basic/layout.html" %}
2 | {% block header %}
3 | {{ super() }}
4 | {% if pagename == 'index' %}
5 |
6 | {% endif %}
7 | {% endblock %}
8 | {% block footer %}
9 | {% if pagename == 'index' %}
10 |
11 | {% endif %}
12 | {% endblock %}
13 | {# do not display relbars #}
14 | {% block relbar1 %}{% endblock %}
15 | {% block relbar2 %}
16 | {% if theme_github_fork %}
17 |
20 | {% endif %}
21 | {% endblock %}
22 | {% block sidebar1 %}{% endblock %}
23 | {% block sidebar2 %}{% endblock %}
24 |
--------------------------------------------------------------------------------
/docs/_themes/kr/layout.html:
--------------------------------------------------------------------------------
1 | {%- extends "basic/layout.html" %}
2 | {%- block extrahead %}
3 | {{ super() }}
4 | {% if theme_touch_icon %}
5 |
6 | {% endif %}
7 |
9 |
10 | {% endblock %}
11 | {%- block relbar2 %}{% endblock %}
12 | {%- block footer %}
13 |
16 |
19 | {%- endblock %}
20 |
--------------------------------------------------------------------------------
/docs/_themes/kr/relations.html:
--------------------------------------------------------------------------------
1 | Related Topics
2 |
24 |
--------------------------------------------------------------------------------
/example/example/urls.py:
--------------------------------------------------------------------------------
1 | """example URL Configuration
2 |
3 | The `urlpatterns` list routes URLs to views. For more information please see:
4 | https://docs.djangoproject.com/en/1.8/topics/http/urls/
5 | Examples:
6 | Function views
7 | 1. Add an import: from my_app import views
8 | 2. Add a URL to urlpatterns: url(r'^$', views.home, name='home')
9 | Class-based views
10 | 1. Add an import: from other_app.views import Home
11 | 2. Add a URL to urlpatterns: url(r'^$', Home.as_view(), name='home')
12 | Including another URLconf
13 | 1. Add an import: from blog import urls as blog_urls
14 | 2. Add a URL to urlpatterns: url(r'^blog/', include(blog_urls))
15 | """
16 | from django.conf.urls import include, url
17 | from django.contrib import admin
18 |
19 | from gcm.routers import router
20 |
21 | urlpatterns = [
22 | url(r'^admin/', include(admin.site.urls)),
23 | url(r'api/', include(router.urls)),
24 | ]
25 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Byte-compiled / optimized / DLL files
2 | __pycache__/
3 | *.py[cod]
4 |
5 | # C extensions
6 | *.so
7 |
8 | # Distribution / packaging
9 | .Python
10 | env/
11 | build/
12 | develop-eggs/
13 | dist/
14 | downloads/
15 | eggs/
16 | .eggs/
17 | lib/
18 | lib64/
19 | parts/
20 | sdist/
21 | var/
22 | *.egg-info/
23 | .installed.cfg
24 | *.egg
25 |
26 | # PyInstaller
27 | # Usually these files are written by a python script from a template
28 | # before PyInstaller builds the exe, so as to inject date/other infos into it.
29 | *.manifest
30 | *.spec
31 |
32 | # Installer logs
33 | pip-log.txt
34 | pip-delete-this-directory.txt
35 |
36 | # Unit test / coverage reports
37 | htmlcov/
38 | .tox/
39 | .coverage
40 | .coverage.*
41 | .cache
42 | nosetests.xml
43 | coverage.xml
44 | *,cover
45 |
46 | # Translations
47 | *.mo
48 | *.pot
49 |
50 | # Django stuff:
51 | *.log
52 |
53 | # Sphinx documentation
54 | docs/_build/
55 |
56 | # PyBuilder
57 | target/
58 |
59 | # PyCharm
60 | .idea
--------------------------------------------------------------------------------
/setup.py:
--------------------------------------------------------------------------------
1 | from setuptools import setup, find_packages
2 |
3 | import gcm
4 |
5 | version = gcm.VERSION
6 |
7 | requires = [
8 | 'Django>=1.7',
9 | 'djangorestframework==3.1.3',
10 | 'mock==1.0.1',
11 | 'pytz==2015.4',
12 | 'requests==2.7.0',
13 | ]
14 |
15 | setup(
16 | name='django-gcm-android-ios',
17 | version=version,
18 | author='Hugo Brilhante',
19 | author_email='hugobrilhante@gmail.com',
20 | packages=find_packages(),
21 | license='MIT',
22 | description='Send a message using GCM HTTP connection server protocol',
23 | long_description=open('docs/index.rst').read(),
24 | url='https://github.com/hugobrilhante/django-gcm-android-ios',
25 | include_package_data=True,
26 | install_requires=requires,
27 | classifiers=[
28 | 'Framework :: Django',
29 | 'Intended Audience :: Developers',
30 | 'Intended Audience :: System Administrators',
31 | 'Operating System :: OS Independent',
32 | 'Topic :: Software Development',
33 | 'Programming Language :: Python :: 2.7',
34 | 'Programming Language :: Python :: 3.4',
35 | ],
36 | )
37 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2015 Hugo Brilhante
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
23 |
--------------------------------------------------------------------------------
/gcm/locale/pt_BR/LC_MESSAGES/django.po:
--------------------------------------------------------------------------------
1 | # This file is distributed under the same license as the gcm-ios-android package.
2 | # Hugo Brilhante hugobrilhante@gmail.com, 2015.
3 | #
4 | #, fuzzy
5 | msgid ""
6 | msgstr ""
7 | "Project-Id-Version: 1.0.0\n"
8 | "Report-Msgid-Bugs-To: \n"
9 | "POT-Creation-Date: 2015-07-08 20:38+0000\n"
10 | "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
11 | "Last-Translator: Hugo Brilhante hugobrilhante@gmail.com\n"
12 | "Language-Team: LANGUAGE \n"
13 | "Language: pt_BR\n"
14 | "MIME-Version: 1.0\n"
15 | "Content-Type: text/plain; charset=UTF-8\n"
16 | "Content-Transfer-Encoding: 8bit\n"
17 | "Plural-Forms: nplurals=2; plural=(n > 1);\n"
18 |
19 | #: gcm/models.py:39
20 | msgid "Device ID"
21 | msgstr "ID do dispositivo"
22 |
23 | #: gcm/models.py:41
24 | msgid "Device Type"
25 | msgstr "Tipo do dispositivo"
26 |
27 | #: gcm/models.py:43
28 | msgid "Registration ID"
29 | msgstr "ID de registro"
30 |
31 | #: gcm/models.py:45
32 | msgid "Creation date"
33 | msgstr "Data de criação"
34 |
35 | #: gcm/models.py:47
36 | msgid "Modified date"
37 | msgstr "Data de modificação"
38 |
39 | #: gcm/models.py:49
40 | msgid "Is active?"
41 | msgstr "Está ativo?"
42 |
43 | #: gcm/models.py:58
44 | msgid "Device"
45 | msgstr "Dispositivo"
46 |
47 | #: gcm/models.py:59
48 | msgid "Devices"
49 | msgstr "Despositivos"
50 |
--------------------------------------------------------------------------------
/gcm/migrations/0001_initial.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | from __future__ import unicode_literals
3 |
4 | from django.db import models, migrations
5 |
6 |
7 | class Migration(migrations.Migration):
8 | dependencies = [
9 | ]
10 |
11 | operations = [
12 | migrations.CreateModel(
13 | name='Device',
14 | fields=[
15 | ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
16 | ('dev_id', models.CharField(verbose_name='Device ID', max_length=50, unique=True)),
17 | ('dev_type', models.CharField(blank=True, null=True, verbose_name='Device Type', max_length=255,
18 | choices=[('IOS', 'iOS'), ('ANDROID', 'Android')])),
19 | ('reg_id', models.CharField(verbose_name='Registration ID', max_length=255, unique=True)),
20 | ('creation_date', models.DateTimeField(verbose_name='Creation date', auto_now_add=True)),
21 | ('modified_date', models.DateTimeField(verbose_name='Modified date', auto_now=True)),
22 | ('is_active', models.BooleanField(verbose_name='Is active?', default=False)),
23 | ],
24 | options={
25 | 'verbose_name_plural': 'Devices',
26 | 'ordering': ['-modified_date'],
27 | 'abstract': False,
28 | 'verbose_name': 'Device',
29 | },
30 | ),
31 | ]
32 |
--------------------------------------------------------------------------------
/docs/_themes/kr/static/small_flask.css:
--------------------------------------------------------------------------------
1 | /*
2 | * small_flask.css_t
3 | * ~~~~~~~~~~~~~~~~~
4 | *
5 | * :copyright: Copyright 2010 by Armin Ronacher.
6 | * :license: Flask Design License, see LICENSE for details.
7 | */
8 |
9 | body {
10 | margin: 0;
11 | padding: 20px 30px;
12 | }
13 |
14 | div.documentwrapper {
15 | float: none;
16 | background: white;
17 | }
18 |
19 | div.sphinxsidebar {
20 | display: block;
21 | float: none;
22 | width: 102.5%;
23 | margin: 50px -30px -20px -30px;
24 | padding: 10px 20px;
25 | background: #333;
26 | color: white;
27 | }
28 |
29 | div.sphinxsidebar h3, div.sphinxsidebar h4, div.sphinxsidebar p,
30 | div.sphinxsidebar h3 a {
31 | color: white;
32 | }
33 |
34 | div.sphinxsidebar a {
35 | color: #aaa;
36 | }
37 |
38 | div.sphinxsidebar p.logo {
39 | display: none;
40 | }
41 |
42 | div.document {
43 | width: 100%;
44 | margin: 0;
45 | }
46 |
47 | div.related {
48 | display: block;
49 | margin: 0;
50 | padding: 10px 0 20px 0;
51 | }
52 |
53 | div.related ul,
54 | div.related ul li {
55 | margin: 0;
56 | padding: 0;
57 | }
58 |
59 | div.footer {
60 | display: none;
61 | }
62 |
63 | div.bodywrapper {
64 | margin: 0;
65 | }
66 |
67 | div.body {
68 | min-height: 0;
69 | padding: 0;
70 | }
71 |
72 | .rtd_doc_footer {
73 | display: none;
74 | }
75 |
76 | .document {
77 | width: auto;
78 | }
79 |
80 | .footer {
81 | width: auto;
82 | }
83 |
84 | .footer {
85 | width: auto;
86 | }
87 |
88 | .github {
89 | display: none;
90 | }
--------------------------------------------------------------------------------
/docs/_themes/LICENSE:
--------------------------------------------------------------------------------
1 | Modifications:
2 |
3 | Copyright (c) 2010 Kenneth Reitz.
4 |
5 |
6 | Original Project:
7 |
8 | Copyright (c) 2010 by Armin Ronacher.
9 |
10 |
11 | Some rights reserved.
12 |
13 | Redistribution and use in source and binary forms of the theme, with or
14 | without modification, are permitted provided that the following conditions
15 | are met:
16 |
17 | * Redistributions of source code must retain the above copyright
18 | notice, this list of conditions and the following disclaimer.
19 |
20 | * Redistributions in binary form must reproduce the above
21 | copyright notice, this list of conditions and the following
22 | disclaimer in the documentation and/or other materials provided
23 | with the distribution.
24 |
25 | * The names of the contributors may not be used to endorse or
26 | promote products derived from this software without specific
27 | prior written permission.
28 |
29 | We kindly ask you to only use these themes in an unmodified manner just
30 | for Flask and Flask-related products, not for unrelated projects. If you
31 | like the visual style and want to use it for your own projects, please
32 | consider making some larger changes to the themes (such as changing
33 | font faces, sizes, colors or margins).
34 |
35 | THIS THEME IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
36 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
37 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
38 | ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
39 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
40 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
41 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
42 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
43 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
44 | ARISING IN ANY WAY OUT OF THE USE OF THIS THEME, EVEN IF ADVISED OF THE
45 | POSSIBILITY OF SUCH DAMAGE.
46 |
--------------------------------------------------------------------------------
/docs/index.rst:
--------------------------------------------------------------------------------
1 | Django gcm android ios's documentation!
2 | =======================================
3 |
4 | django-gcm-android-ios is a simple Django app to send a message using Google Cloud Messaging HTTP connection server protocol.
5 |
6 |
7 | .. image:: https://travis-ci.org/hugobrilhante/django-gcm-android-ios.svg
8 | :target: https://travis-ci.org/hugobrilhante/django-gcm-android-ios
9 |
10 |
11 | .. image:: https://coveralls.io/repos/hugobrilhante/django-gcm-android-ios/badge.svg?branch=master&service=github
12 | :target: https://coveralls.io/github/hugobrilhante/django-gcm-android-ios?branch=master
13 |
14 | .. image:: https://img.shields.io/pypi/status/django-gcm-android-ios.svg
15 | :target: https://pypi.python.org/pypi/django-gcm-android-ios
16 |
17 | .. image:: https://img.shields.io/pypi/dm/django-gcm-android-ios.svg
18 | :target: https://pypi.python.org/pypi/django-gcm-android-ios/1.0.0#downloads
19 |
20 | .. image:: https://img.shields.io/pypi/l/django-gcm-android-ios.svg
21 | :target: https://github.com/hugobrilhante/django-gcm-android-ios/blob/master/LICENSE
22 |
23 | .. image:: https://img.shields.io/github/release/hugobrilhante/django-gcm-android-ios.svg
24 | :target: https://github.com/hugobrilhante/django-gcm-android-ios/releases/tag/1.0.0
25 |
26 | .. image:: https://img.shields.io/pypi/pyversions/django-gcm-android-ios.svg
27 | :target: https://pypi.python.org/pypi/django-gcm-android-ios
28 |
29 |
30 |
31 |
32 | Contents:
33 |
34 | .. toctree::
35 | :maxdepth: 2
36 |
37 | tutorial
38 |
39 |
40 |
41 | ======
42 | Author
43 | ======
44 |
45 | - `Hugo Brilhante `_
46 |
47 | =============
48 | Main features
49 | =============
50 | - Python: 2,7, 3.4.
51 | - Django: 1.7, 1.8.
52 | - API using Django Rest Framework `django-rest-framework `_
53 | - Requests processing http/https using the library `requests `_.
54 | - Excellent coverage tests (> 80%).
55 |
56 | =====
57 | Links
58 | =====
59 |
60 | - `Github `_
61 | - `Travis CI `_
62 | - `Coveralls `_
63 |
64 |
65 | ==========
66 | References
67 | ==========
68 |
69 | - `Google Cloud Messaging `_
70 |
71 | =======
72 | License
73 | =======
74 |
75 | .. include:: ../LICENSE
--------------------------------------------------------------------------------
/gcm/utils.py:
--------------------------------------------------------------------------------
1 | import json
2 |
3 | import requests
4 | from django.apps import apps
5 | from django.core.exceptions import ImproperlyConfigured
6 | from gcm import settings
7 |
8 |
9 | def notification_push(dev_type, to, message=None, **kwargs):
10 | """
11 | Send data from your server to your users' devices.
12 | """
13 | key = {
14 | 'ANDROID': settings.GCM_ANDROID_APIKEY,
15 | 'IOS': settings.GCM_IOS_APIKEY
16 | }
17 |
18 | if not key[dev_type]:
19 | raise ImproperlyConfigured(
20 | "You haven't set the 'GCM_{}_APIKEY' setting yet.".format(dev_type))
21 |
22 | payload = {
23 | 'ANDROID': {'to': to,
24 | 'data': {'message': message}},
25 | 'IOS': {
26 | 'to': to,
27 | 'notification': {
28 | 'body': message,
29 | },
30 | }
31 | }
32 |
33 | payload[dev_type].update(**kwargs)
34 |
35 | payload = json.dumps(payload[dev_type])
36 |
37 | headers = {'Authorization': 'key={}'.format(key[dev_type]), 'Content-Type': 'application/json'}
38 |
39 | response = requests.post(url='https://gcm-http.googleapis.com/gcm/send',
40 | data=payload,
41 | headers=headers
42 | )
43 |
44 | if response.status_code == 200:
45 |
46 | response = response.json()
47 |
48 | if response['success']:
49 | return {'success': 'Message send successfully'}
50 | elif response['canonical_ids']:
51 | return {'canonical_id': response.get('results')[0].get('registration_id')}
52 | elif response['failure']:
53 | return {'error': response.get('results')[0].get('error')}
54 |
55 | elif 400 <= response.status_code < 500:
56 | return {'error': '%s Client Error: %s' % (response.status_code, response.reason)}
57 |
58 | elif 500 <= response.status_code < 600:
59 | return {'error': '%s Server Error: %s' % (response.status_code, response.reason)}
60 |
61 |
62 | def get_device_model():
63 | """
64 | Returns the Device model that is active in this project.
65 | """
66 | try:
67 | return apps.get_model(settings.GCM_DEVICE_MODEL)
68 | except ValueError:
69 | raise ImproperlyConfigured("GCM_DEVICE_MODEL must be of the form 'app_label.model_name'")
70 | except LookupError:
71 | raise ImproperlyConfigured(
72 | "GCM_DEVICE_MODEL refers to model '%s' that has not been installed" % settings.GCM_DEVICE_MODEL
73 | )
74 |
--------------------------------------------------------------------------------
/docs/tutorial.rst:
--------------------------------------------------------------------------------
1 | Tutorial
2 | ========
3 |
4 |
5 | ============
6 | Installation
7 | ============
8 |
9 | Install o django-gcm-android-ios::
10 |
11 | pip install django-gcm-android-ios
12 |
13 | =============
14 | Configuration
15 | =============
16 |
17 | Configure django-gcm-android-ios in your settings.py file::
18 |
19 | INSTALLED_APPS = (
20 | ...
21 | 'gcm',
22 | )
23 |
24 |
25 | GCM_DEVICE_MODEL = "" # default gcm.Device
26 | GCM_IOS_APIKEY = ""
27 | GCM_ANDROID_APIKEY = ""
28 |
29 | Add django-gcm-android-ios resources to your URL router::
30 |
31 | from gcm.routers import router
32 | urlpatterns = [
33 | ...
34 | url(r'api/', include(router.urls)),
35 | ]
36 |
37 | You can easily test if the endpoint is working by doing the following in your terminal
38 |
39 | Register::
40 |
41 | curl -X POST -H "Content-Type: application/json" -H "Authorization: " -d '{
42 | "dev_id": "Device id",
43 | "dev_type": "ANDROID or IOS",
44 | "reg_id": "Register id"
45 | }' 'http://localhost:8001/api/devices'
46 |
47 | Unregister::
48 |
49 |
50 | curl -X DELETE -H "Content-Type: application/json" -H "Authorization: "
51 | 'http://localhost:8001/api/devices/'
52 |
53 | .. _Django Rest Framework: http://www.django-rest-framework.org/api-guide/authentication/
54 |
55 | .. note:: Authorization, see `Django Rest Framework`_ docs.
56 | ================
57 | Sending messages
58 | ================
59 | Using ``Django orm``::
60 |
61 | from gcm.utils import get_device_model
62 | Device = get_device_model()
63 |
64 | device = Device.objects.get(dev_id=)
65 |
66 | device.send_message('my test message', collapse_key='something')
67 |
68 | ``collapse_key`` parameter is optional (default message).
69 |
70 | If you want to send additional arguments like ``delay_while_idle`` or other, add them as named variables::
71 |
72 | device.send_message('my test message', delay_while_idle=True, time_to_live=5)
73 |
74 | .. _GCM Connection Server Reference: https://developers.google.com/cloud-messaging/server-ref
75 |
76 | .. note:: For more information, see `GCM Connection Server Reference`_ docs.
77 |
78 | Multicast message
79 |
80 | ``django-gcm-android-ios`` supports sending message to multiple devices at once::
81 |
82 | from gcm.utils import get_device_model
83 | Device = get_device_model()
84 |
85 | Device.objects.all().send_messages('my message')
86 |
87 | Device.objects.filter(is_active=True).send_messages('my message', collapse_key='something')
88 |
89 | Payload
90 |
91 | ``django-gcm-android-ios`` supports sending payload::
92 |
93 | from gcm.utils import get_device_model
94 | Device = get_device_model()
95 |
96 | device = Device.objects.get(dev_id=)
97 |
98 | device.send_message(data={ "score": "4x8", "time": "15:16.2342" }, collapse_key='something', time_to_live= 108)
99 |
100 |
--------------------------------------------------------------------------------
/README.rst:
--------------------------------------------------------------------------------
1 | ======================
2 | Django GCM Android iOS (Deprecate due Firebase Cloud Messaging)
3 | ======================
4 |
5 | django-gcm-android-ios is a simple Django app to send a message using GCM HTTP connection server protocol.
6 |
7 | Detailed documentation is in the "docs" directory.
8 |
9 | .. image:: https://travis-ci.org/hugobrilhante/django-gcm-android-ios.svg
10 | :target: https://travis-ci.org/hugobrilhante/django-gcm-android-ios
11 |
12 | .. image:: https://coveralls.io/repos/hugobrilhante/django-gcm-android-ios/badge.svg?branch=master&service=github
13 | :target: https://coveralls.io/github/hugobrilhante/django-gcm-android-ios?branch=master
14 |
15 | .. image:: https://readthedocs.org/projects/django-gcm-android-ios/badge/?version=latest
16 | :target: http://django-gcm-android-ios.readthedocs.org/en/latest/
17 | :alt: Documentation Status
18 |
19 | .. image:: https://img.shields.io/pypi/status/django-gcm-android-ios.svg
20 | :target: https://pypi.python.org/pypi/django-gcm-android-ios
21 |
22 | .. image:: https://img.shields.io/pypi/dm/django-gcm-android-ios.svg
23 | :target: https://pypi.python.org/pypi/django-gcm-android-ios/1.0.0#downloads
24 |
25 | .. image:: https://img.shields.io/pypi/l/django-gcm-android-ios.svg
26 | :target: https://github.com/hugobrilhante/django-gcm-android-ios/blob/master/LICENSE
27 |
28 | .. image:: https://img.shields.io/github/release/hugobrilhante/django-gcm-android-ios.svg
29 | :target: https://github.com/hugobrilhante/django-gcm-android-ios/releases/tag/1.0.0
30 |
31 | .. image:: https://img.shields.io/pypi/pyversions/django-gcm-android-ios.svg
32 | :target: https://pypi.python.org/pypi/django-gcm-android-ios
33 |
34 |
35 |
36 | Quick start
37 | -----------
38 |
39 | 1. Install django-gcm-android-ios::
40 |
41 | pip install django-gcm-android-ios
42 |
43 | 2. Add "gcm" to your INSTALLED_APPS setting like this::
44 |
45 | INSTALLED_APPS = (
46 | ...
47 | 'gcm',
48 | )
49 |
50 | 3. Add in setting api keys like this::
51 |
52 | GCM_DEVICE_MODEL = "DeviceModel" # default gcm.Device
53 | GCM_IOS_APIKEY = "IOS_APIKEY"
54 | GCM_ANDROID_APIKEY = "ANDROID_APIKEY"
55 |
56 |
57 | 4. Include the gcm routers in your project urls.py like this::
58 |
59 | from gcm.routers import router
60 | url(r'api/', include(router.urls))
61 |
62 | 5. Run `python manage.py migrate` to create the device models
63 |
64 |
65 | 6. To register device::
66 |
67 | curl -X POST -H "Content-Type: application/json" -H "Authorization: "
68 | -d '{
69 | "dev_id": "Device id",
70 | "dev_type": "ANDROID or IOS",
71 | "reg_id": "Register id"
72 | }' 'http://localhost:8001/api/devices'
73 |
74 | 7. To unregister device::
75 |
76 | curl -X DELETE -H "Content-Type: application/json" -H "Authorization: "
77 | 'http://localhost:8001/api/devices/id_device'
78 |
79 |
80 | .. image:: https://badges.gitter.im/Join%20Chat.svg
81 | :alt: Join the chat at https://gitter.im/hugobrilhante/django-gcm-android-ios
82 | :target: https://gitter.im/hugobrilhante/django-gcm-android-ios?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge
83 |
--------------------------------------------------------------------------------
/example/example/settings.py:
--------------------------------------------------------------------------------
1 | """
2 | Django settings for example project.
3 |
4 | Generated by 'django-admin startproject' using Django 1.8.2.
5 |
6 | For more information on this file, see
7 | https://docs.djangoproject.com/en/1.8/topics/settings/
8 |
9 | For the full list of settings and their values, see
10 | https://docs.djangoproject.com/en/1.8/ref/settings/
11 | """
12 |
13 | # Build paths inside the project like this: os.path.join(BASE_DIR, ...)
14 | import os
15 |
16 | BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
17 |
18 |
19 | # Quick-start development settings - unsuitable for production
20 | # See https://docs.djangoproject.com/en/1.8/howto/deployment/checklist/
21 |
22 | # SECURITY WARNING: keep the secret key used in production secret!
23 | SECRET_KEY = 'c5!6j@+#50v#uq66(d#^x1089o(+jue-3s_799ontu9@7$+y0j'
24 |
25 | # SECURITY WARNING: don't run with debug turned on in production!
26 | DEBUG = True
27 |
28 | ALLOWED_HOSTS = []
29 |
30 |
31 | # Application definition
32 |
33 | INSTALLED_APPS = (
34 | 'django.contrib.admin',
35 | 'django.contrib.auth',
36 | 'django.contrib.contenttypes',
37 | 'django.contrib.sessions',
38 | 'django.contrib.messages',
39 | 'django.contrib.staticfiles',
40 | 'gcm',
41 | )
42 |
43 | GCM_DEVICE_MODEL = 'gcm.Device'
44 |
45 | GCM_ANDROID_APIKEY = "AIzaSyDTOsEsbVUnm2sPVHrV2AuiBMsN9279czQ"
46 |
47 | GCM_IOS_APIKEY = "AIzaSyCE-T1kCt6yJDhj3VU_XBo_pD4ZT8O70lQ"
48 |
49 | MIDDLEWARE_CLASSES = (
50 | 'django.contrib.sessions.middleware.SessionMiddleware',
51 | 'django.middleware.common.CommonMiddleware',
52 | 'django.middleware.csrf.CsrfViewMiddleware',
53 | 'django.contrib.auth.middleware.AuthenticationMiddleware',
54 | 'django.contrib.auth.middleware.SessionAuthenticationMiddleware',
55 | 'django.contrib.messages.middleware.MessageMiddleware',
56 | 'django.middleware.clickjacking.XFrameOptionsMiddleware',
57 | )
58 |
59 | ROOT_URLCONF = 'example.urls'
60 |
61 | TEMPLATES = [
62 | {
63 | 'BACKEND': 'django.template.backends.django.DjangoTemplates',
64 | 'DIRS': [],
65 | 'APP_DIRS': True,
66 | 'OPTIONS': {
67 | 'context_processors': [
68 | 'django.template.context_processors.debug',
69 | 'django.template.context_processors.request',
70 | 'django.contrib.auth.context_processors.auth',
71 | 'django.contrib.messages.context_processors.messages',
72 | ],
73 | },
74 | },
75 | ]
76 |
77 | WSGI_APPLICATION = 'example.wsgi.application'
78 |
79 |
80 | # Database
81 | # https://docs.djangoproject.com/en/1.8/ref/settings/#databases
82 |
83 | DATABASES = {
84 | 'default': {
85 | 'ENGINE': 'django.db.backends.sqlite3',
86 | 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
87 | }
88 | }
89 |
90 |
91 | # Internationalization
92 | # https://docs.djangoproject.com/en/1.8/topics/i18n/
93 |
94 | LANGUAGE_CODE = 'en-us'
95 |
96 | TIME_ZONE = 'UTC'
97 |
98 | USE_I18N = True
99 |
100 | USE_L10N = True
101 |
102 | USE_TZ = True
103 |
104 |
105 | # Static files (CSS, JavaScript, Images)
106 | # https://docs.djangoproject.com/en/1.8/howto/static-files/
107 |
108 | STATIC_URL = '/static/'
109 |
110 | TEMPLATE_DIRS = (
111 | os.path.join(BASE_DIR, 'templates'),
112 | )
113 |
--------------------------------------------------------------------------------
/docs/_themes/flask_theme_support.py:
--------------------------------------------------------------------------------
1 | # flasky extensions. flasky pygments style based on tango style
2 | from pygments.style import Style
3 | from pygments.token import Keyword, Name, Comment, String, Error, \
4 | Number, Operator, Generic, Whitespace, Punctuation, Other, Literal
5 |
6 |
7 | class FlaskyStyle(Style):
8 | background_color = "#f8f8f8"
9 | default_style = ""
10 |
11 | styles = {
12 | # No corresponding class for the following:
13 | # Text: "", # class: ''
14 | Whitespace: "underline #f8f8f8", # class: 'w'
15 | Error: "#a40000 border:#ef2929", # class: 'err'
16 | Other: "#000000", # class 'x'
17 |
18 | Comment: "italic #8f5902", # class: 'c'
19 | Comment.Preproc: "noitalic", # class: 'cp'
20 |
21 | Keyword: "bold #004461", # class: 'k'
22 | Keyword.Constant: "bold #004461", # class: 'kc'
23 | Keyword.Declaration: "bold #004461", # class: 'kd'
24 | Keyword.Namespace: "bold #004461", # class: 'kn'
25 | Keyword.Pseudo: "bold #004461", # class: 'kp'
26 | Keyword.Reserved: "bold #004461", # class: 'kr'
27 | Keyword.Type: "bold #004461", # class: 'kt'
28 |
29 | Operator: "#582800", # class: 'o'
30 | Operator.Word: "bold #004461", # class: 'ow' - like keywords
31 |
32 | Punctuation: "bold #000000", # class: 'p'
33 |
34 | # because special names such as Name.Class, Name.Function, etc.
35 | # are not recognized as such later in the parsing, we choose them
36 | # to look the same as ordinary variables.
37 | Name: "#000000", # class: 'n'
38 | Name.Attribute: "#c4a000", # class: 'na' - to be revised
39 | Name.Builtin: "#004461", # class: 'nb'
40 | Name.Builtin.Pseudo: "#3465a4", # class: 'bp'
41 | Name.Class: "#000000", # class: 'nc' - to be revised
42 | Name.Constant: "#000000", # class: 'no' - to be revised
43 | Name.Decorator: "#888", # class: 'nd' - to be revised
44 | Name.Entity: "#ce5c00", # class: 'ni'
45 | Name.Exception: "bold #cc0000", # class: 'ne'
46 | Name.Function: "#000000", # class: 'nf'
47 | Name.Property: "#000000", # class: 'py'
48 | Name.Label: "#f57900", # class: 'nl'
49 | Name.Namespace: "#000000", # class: 'nn' - to be revised
50 | Name.Other: "#000000", # class: 'nx'
51 | Name.Tag: "bold #004461", # class: 'nt' - like a keyword
52 | Name.Variable: "#000000", # class: 'nv' - to be revised
53 | Name.Variable.Class: "#000000", # class: 'vc' - to be revised
54 | Name.Variable.Global: "#000000", # class: 'vg' - to be revised
55 | Name.Variable.Instance: "#000000", # class: 'vi' - to be revised
56 |
57 | Number: "#990000", # class: 'm'
58 |
59 | Literal: "#000000", # class: 'l'
60 | Literal.Date: "#000000", # class: 'ld'
61 |
62 | String: "#4e9a06", # class: 's'
63 | String.Backtick: "#4e9a06", # class: 'sb'
64 | String.Char: "#4e9a06", # class: 'sc'
65 | String.Doc: "italic #8f5902", # class: 'sd' - like a comment
66 | String.Double: "#4e9a06", # class: 's2'
67 | String.Escape: "#4e9a06", # class: 'se'
68 | String.Heredoc: "#4e9a06", # class: 'sh'
69 | String.Interpol: "#4e9a06", # class: 'si'
70 | String.Other: "#4e9a06", # class: 'sx'
71 | String.Regex: "#4e9a06", # class: 'sr'
72 | String.Single: "#4e9a06", # class: 's1'
73 | String.Symbol: "#4e9a06", # class: 'ss'
74 |
75 | Generic: "#000000", # class: 'g'
76 | Generic.Deleted: "#a40000", # class: 'gd'
77 | Generic.Emph: "italic #000000", # class: 'ge'
78 | Generic.Error: "#ef2929", # class: 'gr'
79 | Generic.Heading: "bold #000080", # class: 'gh'
80 | Generic.Inserted: "#00A000", # class: 'gi'
81 | Generic.Output: "#888", # class: 'go'
82 | Generic.Prompt: "#745334", # class: 'gp'
83 | Generic.Strong: "bold #000000", # class: 'gs'
84 | Generic.Subheading: "bold #800080", # class: 'gu'
85 | Generic.Traceback: "bold #a40000", # class: 'gt'
86 | }
87 |
--------------------------------------------------------------------------------
/gcm/models.py:
--------------------------------------------------------------------------------
1 | from django.db import models
2 | from django.utils.translation import ugettext_lazy as _
3 | from django.utils.encoding import python_2_unicode_compatible
4 |
5 | from gcm.utils import notification_push
6 |
7 | GCM_ERROR_MESSAGES = {'MissingRegistration': 'Check that the request contains a registration token',
8 | 'InvalidRegistration': 'Check the format of the registration token you pass to the server.',
9 | 'NotRegistered': 'The client app unregisters with GCM.',
10 | 'InvalidPackageName': 'Make sure the message was addressed to a registration token whose package'
11 | ' name matches the value passed in the request.',
12 | 'MismatchSenderId': 'A registration token is tied to a certain group of senders.',
13 | 'MessageTooBig': 'Check that the total size of the payload data included in a message does'
14 | ' not exceed GCM limits: 4096 bytes for most messages, or 2048 bytes in the case'
15 | ' of messages to topics or notification messages on iOS. This includes both'
16 | 'the keys and the values.',
17 | 'InvalidDataKey': 'Check that the payload data does not contain a key (such as from ,'
18 | ' or gcm , or any value prefixed by google ) that is used internally by GCM.',
19 | 'InvalidTtl': 'Check that the value used in time_to_live is an integer representing a'
20 | ' duration in seconds between 0 and 2,419,200 (4 weeks).',
21 | 'Unavailable': 'The server couldn\'t process the request in time.',
22 | 'InternalServerError': 'The server encountered an error while trying to process the request.',
23 | 'DeviceMessageRate': 'The rate of messages to a particular device is too high.',
24 | 'TopicsMessageRate': 'The rate of messages to subscribers to a particular topic is too high.',
25 | 'InvalidParameters': 'Check Parameters sent'}
26 |
27 | DEVICE_TYPES = (('IOS', "iOS"), ('ANDROID', "Android"))
28 |
29 |
30 | class DeviceQuerySet(models.QuerySet):
31 | def send_messages(self, message=None, **kwargs):
32 | responses = []
33 | for device in self.all():
34 | response = device.send_message(message, **kwargs)
35 | responses.append((device.id, response))
36 | return responses
37 |
38 |
39 | @python_2_unicode_compatible
40 | class AbstractDevice(models.Model):
41 | dev_id = models.CharField(
42 | verbose_name=_("Device ID"), max_length=50, unique=True, )
43 | dev_type = models.CharField(
44 | verbose_name=_("Device Type"), max_length=255, choices=DEVICE_TYPES)
45 | reg_id = models.CharField(
46 | verbose_name=_("Registration ID"), max_length=255, unique=True)
47 | creation_date = models.DateTimeField(
48 | verbose_name=_("Creation date"), auto_now_add=True)
49 | modified_date = models.DateTimeField(
50 | verbose_name=_("Modified date"), auto_now=True)
51 | is_active = models.BooleanField(
52 | verbose_name=_("Is active?"), default=True)
53 |
54 | objects = DeviceQuerySet.as_manager()
55 |
56 | def __str__(self):
57 | return self.dev_id
58 |
59 | class Meta:
60 | abstract = True
61 | verbose_name = _("Device")
62 | verbose_name_plural = _("Devices")
63 | ordering = ['-modified_date']
64 |
65 | def send_message(self, message=None, **kwargs):
66 | response = notification_push(self.dev_type, self.reg_id, message, **kwargs)
67 | if 'success' in response:
68 | return response['success']
69 | elif 'canonical_id' in response:
70 | self.reg_id = response['canonical_id']
71 | self.save()
72 | return 'Message send successfully'
73 | elif 'error' in response:
74 | if response['error'] == 'NotRegistered':
75 | self.mark_inactive()
76 | return GCM_ERROR_MESSAGES[response['error']]
77 | else:
78 | return GCM_ERROR_MESSAGES.get(response['error'], response['error'])
79 |
80 | def mark_inactive(self):
81 | self.is_active = False
82 | self.save()
83 |
84 |
85 | class Device(AbstractDevice):
86 | pass
87 |
--------------------------------------------------------------------------------
/docs/_themes/kr_small/static/flasky.css_t:
--------------------------------------------------------------------------------
1 | /*
2 | * flasky.css_t
3 | * ~~~~~~~~~~~~
4 | *
5 | * Sphinx stylesheet -- flasky theme based on nature theme.
6 | *
7 | * :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS.
8 | * :license: BSD, see LICENSE for details.
9 | *
10 | */
11 |
12 | @import url("basic.css");
13 |
14 | /* -- page layout ----------------------------------------------------------- */
15 |
16 | body {
17 | font-family: 'Georgia', serif;
18 | font-size: 17px;
19 | color: #000;
20 | background: white;
21 | margin: 0;
22 | padding: 0;
23 | }
24 |
25 | div.documentwrapper {
26 | float: left;
27 | width: 100%;
28 | }
29 |
30 | div.bodywrapper {
31 | margin: 40px auto 0 auto;
32 | width: 700px;
33 | }
34 |
35 | hr {
36 | border: 1px solid #B1B4B6;
37 | }
38 |
39 | div.body {
40 | background-color: #ffffff;
41 | color: #3E4349;
42 | padding: 0 30px 30px 30px;
43 | }
44 |
45 | img.floatingflask {
46 | padding: 0 0 10px 10px;
47 | float: right;
48 | }
49 |
50 | div.footer {
51 | text-align: right;
52 | color: #888;
53 | padding: 10px;
54 | font-size: 14px;
55 | width: 650px;
56 | margin: 0 auto 40px auto;
57 | }
58 |
59 | div.footer a {
60 | color: #888;
61 | text-decoration: underline;
62 | }
63 |
64 | div.related {
65 | line-height: 32px;
66 | color: #888;
67 | }
68 |
69 | div.related ul {
70 | padding: 0 0 0 10px;
71 | }
72 |
73 | div.related a {
74 | color: #444;
75 | }
76 |
77 | /* -- body styles ----------------------------------------------------------- */
78 |
79 | a {
80 | color: #004B6B;
81 | text-decoration: underline;
82 | }
83 |
84 | a:hover {
85 | color: #6D4100;
86 | text-decoration: underline;
87 | }
88 |
89 | div.body {
90 | padding-bottom: 40px; /* saved for footer */
91 | }
92 |
93 | div.body h1,
94 | div.body h2,
95 | div.body h3,
96 | div.body h4,
97 | div.body h5,
98 | div.body h6 {
99 | font-family: 'Garamond', 'Georgia', serif;
100 | font-weight: normal;
101 | margin: 30px 0px 10px 0px;
102 | padding: 0;
103 | }
104 |
105 | {% if theme_index_logo %}
106 | div.indexwrapper h1 {
107 | text-indent: -999999px;
108 | background: url({{ theme_index_logo }}) no-repeat center center;
109 | height: {{ theme_index_logo_height }};
110 | }
111 | {% endif %}
112 |
113 | div.body h2 { font-size: 180%; }
114 | div.body h3 { font-size: 150%; }
115 | div.body h4 { font-size: 130%; }
116 | div.body h5 { font-size: 100%; }
117 | div.body h6 { font-size: 100%; }
118 |
119 | a.headerlink {
120 | color: white;
121 | padding: 0 4px;
122 | text-decoration: none;
123 | }
124 |
125 | a.headerlink:hover {
126 | color: #444;
127 | background: #eaeaea;
128 | }
129 |
130 | div.body p, div.body dd, div.body li {
131 | line-height: 1.4em;
132 | }
133 |
134 | div.admonition {
135 | background: #fafafa;
136 | margin: 20px -30px;
137 | padding: 10px 30px;
138 | border-top: 1px solid #ccc;
139 | border-bottom: 1px solid #ccc;
140 | }
141 |
142 | div.admonition p.admonition-title {
143 | font-family: 'Garamond', 'Georgia', serif;
144 | font-weight: normal;
145 | font-size: 24px;
146 | margin: 0 0 10px 0;
147 | padding: 0;
148 | line-height: 1;
149 | }
150 |
151 | div.admonition p.last {
152 | margin-bottom: 0;
153 | }
154 |
155 | div.highlight{
156 | background-color: white;
157 | }
158 |
159 | dt:target, .highlight {
160 | background: #FAF3E8;
161 | }
162 |
163 | div.note {
164 | background-color: #eee;
165 | border: 1px solid #ccc;
166 | }
167 |
168 | div.seealso {
169 | background-color: #ffc;
170 | border: 1px solid #ff6;
171 | }
172 |
173 | div.topic {
174 | background-color: #eee;
175 | }
176 |
177 | div.warning {
178 | background-color: #ffe4e4;
179 | border: 1px solid #f66;
180 | }
181 |
182 | p.admonition-title {
183 | display: inline;
184 | }
185 |
186 | p.admonition-title:after {
187 | content: ":";
188 | }
189 |
190 | pre, tt {
191 | font-family: 'Consolas', 'Menlo', 'Deja Vu Sans Mono', 'Bitstream Vera Sans Mono', monospace;
192 | font-size: 0.85em;
193 | }
194 |
195 | img.screenshot {
196 | }
197 |
198 | tt.descname, tt.descclassname {
199 | font-size: 0.95em;
200 | }
201 |
202 | tt.descname {
203 | padding-right: 0.08em;
204 | }
205 |
206 | img.screenshot {
207 | -moz-box-shadow: 2px 2px 4px #eee;
208 | -webkit-box-shadow: 2px 2px 4px #eee;
209 | box-shadow: 2px 2px 4px #eee;
210 | }
211 |
212 | table.docutils {
213 | border: 1px solid #888;
214 | -moz-box-shadow: 2px 2px 4px #eee;
215 | -webkit-box-shadow: 2px 2px 4px #eee;
216 | box-shadow: 2px 2px 4px #eee;
217 | }
218 |
219 | table.docutils td, table.docutils th {
220 | border: 1px solid #888;
221 | padding: 0.25em 0.7em;
222 | }
223 |
224 | table.field-list, table.footnote {
225 | border: none;
226 | -moz-box-shadow: none;
227 | -webkit-box-shadow: none;
228 | box-shadow: none;
229 | }
230 |
231 | table.footnote {
232 | margin: 15px 0;
233 | width: 100%;
234 | border: 1px solid #eee;
235 | }
236 |
237 | table.field-list th {
238 | padding: 0 0.8em 0 0;
239 | }
240 |
241 | table.field-list td {
242 | padding: 0;
243 | }
244 |
245 | table.footnote td {
246 | padding: 0.5em;
247 | }
248 |
249 | dl {
250 | margin: 0;
251 | padding: 0;
252 | }
253 |
254 | dl dd {
255 | margin-left: 30px;
256 | }
257 |
258 | pre {
259 | padding: 0;
260 | margin: 15px -30px;
261 | padding: 8px;
262 | line-height: 1.3em;
263 | padding: 7px 30px;
264 | background: #eee;
265 | border-radius: 2px;
266 | -moz-border-radius: 2px;
267 | -webkit-border-radius: 2px;
268 | }
269 |
270 | dl pre {
271 | margin-left: -60px;
272 | padding-left: 60px;
273 | }
274 |
275 | tt {
276 | background-color: #ecf0f3;
277 | color: #222;
278 | /* padding: 1px 2px; */
279 | }
280 |
281 | tt.xref, a tt {
282 | background-color: #FBFBFB;
283 | }
284 |
285 | a:hover tt {
286 | background: #EEE;
287 | }
288 |
--------------------------------------------------------------------------------
/docs/make.bat:
--------------------------------------------------------------------------------
1 | @ECHO OFF
2 |
3 | REM Command file for Sphinx documentation
4 |
5 | if "%SPHINXBUILD%" == "" (
6 | set SPHINXBUILD=sphinx-build
7 | )
8 | set BUILDDIR=_build
9 | set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% .
10 | set I18NSPHINXOPTS=%SPHINXOPTS% .
11 | if NOT "%PAPER%" == "" (
12 | set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS%
13 | set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS%
14 | )
15 |
16 | if "%1" == "" goto help
17 |
18 | if "%1" == "help" (
19 | :help
20 | echo.Please use `make ^` where ^ is one of
21 | echo. html to make standalone HTML files
22 | echo. dirhtml to make HTML files named index.html in directories
23 | echo. singlehtml to make a single large HTML file
24 | echo. pickle to make pickle files
25 | echo. json to make JSON files
26 | echo. htmlhelp to make HTML files and a HTML help project
27 | echo. qthelp to make HTML files and a qthelp project
28 | echo. devhelp to make HTML files and a Devhelp project
29 | echo. epub to make an epub
30 | echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter
31 | echo. text to make text files
32 | echo. man to make manual pages
33 | echo. texinfo to make Texinfo files
34 | echo. gettext to make PO message catalogs
35 | echo. changes to make an overview over all changed/added/deprecated items
36 | echo. xml to make Docutils-native XML files
37 | echo. pseudoxml to make pseudoxml-XML files for display purposes
38 | echo. linkcheck to check all external links for integrity
39 | echo. doctest to run all doctests embedded in the documentation if enabled
40 | echo. coverage to run coverage check of the documentation if enabled
41 | goto end
42 | )
43 |
44 | if "%1" == "clean" (
45 | for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i
46 | del /q /s %BUILDDIR%\*
47 | goto end
48 | )
49 |
50 |
51 | REM Check if sphinx-build is available and fallback to Python version if any
52 | %SPHINXBUILD% 2> nul
53 | if errorlevel 9009 goto sphinx_python
54 | goto sphinx_ok
55 |
56 | :sphinx_python
57 |
58 | set SPHINXBUILD=python -m sphinx.__init__
59 | %SPHINXBUILD% 2> nul
60 | if errorlevel 9009 (
61 | echo.
62 | echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
63 | echo.installed, then set the SPHINXBUILD environment variable to point
64 | echo.to the full path of the 'sphinx-build' executable. Alternatively you
65 | echo.may add the Sphinx directory to PATH.
66 | echo.
67 | echo.If you don't have Sphinx installed, grab it from
68 | echo.http://sphinx-doc.org/
69 | exit /b 1
70 | )
71 |
72 | :sphinx_ok
73 |
74 |
75 | if "%1" == "html" (
76 | %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html
77 | if errorlevel 1 exit /b 1
78 | echo.
79 | echo.Build finished. The HTML pages are in %BUILDDIR%/html.
80 | goto end
81 | )
82 |
83 | if "%1" == "dirhtml" (
84 | %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml
85 | if errorlevel 1 exit /b 1
86 | echo.
87 | echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml.
88 | goto end
89 | )
90 |
91 | if "%1" == "singlehtml" (
92 | %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml
93 | if errorlevel 1 exit /b 1
94 | echo.
95 | echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml.
96 | goto end
97 | )
98 |
99 | if "%1" == "pickle" (
100 | %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle
101 | if errorlevel 1 exit /b 1
102 | echo.
103 | echo.Build finished; now you can process the pickle files.
104 | goto end
105 | )
106 |
107 | if "%1" == "json" (
108 | %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json
109 | if errorlevel 1 exit /b 1
110 | echo.
111 | echo.Build finished; now you can process the JSON files.
112 | goto end
113 | )
114 |
115 | if "%1" == "htmlhelp" (
116 | %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp
117 | if errorlevel 1 exit /b 1
118 | echo.
119 | echo.Build finished; now you can run HTML Help Workshop with the ^
120 | .hhp project file in %BUILDDIR%/htmlhelp.
121 | goto end
122 | )
123 |
124 | if "%1" == "qthelp" (
125 | %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp
126 | if errorlevel 1 exit /b 1
127 | echo.
128 | echo.Build finished; now you can run "qcollectiongenerator" with the ^
129 | .qhcp project file in %BUILDDIR%/qthelp, like this:
130 | echo.^> qcollectiongenerator %BUILDDIR%\qthelp\django-gcm-ios-android.qhcp
131 | echo.To view the help file:
132 | echo.^> assistant -collectionFile %BUILDDIR%\qthelp\django-gcm-ios-android.ghc
133 | goto end
134 | )
135 |
136 | if "%1" == "devhelp" (
137 | %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp
138 | if errorlevel 1 exit /b 1
139 | echo.
140 | echo.Build finished.
141 | goto end
142 | )
143 |
144 | if "%1" == "epub" (
145 | %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub
146 | if errorlevel 1 exit /b 1
147 | echo.
148 | echo.Build finished. The epub file is in %BUILDDIR%/epub.
149 | goto end
150 | )
151 |
152 | if "%1" == "latex" (
153 | %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
154 | if errorlevel 1 exit /b 1
155 | echo.
156 | echo.Build finished; the LaTeX files are in %BUILDDIR%/latex.
157 | goto end
158 | )
159 |
160 | if "%1" == "latexpdf" (
161 | %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
162 | cd %BUILDDIR%/latex
163 | make all-pdf
164 | cd %~dp0
165 | echo.
166 | echo.Build finished; the PDF files are in %BUILDDIR%/latex.
167 | goto end
168 | )
169 |
170 | if "%1" == "latexpdfja" (
171 | %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
172 | cd %BUILDDIR%/latex
173 | make all-pdf-ja
174 | cd %~dp0
175 | echo.
176 | echo.Build finished; the PDF files are in %BUILDDIR%/latex.
177 | goto end
178 | )
179 |
180 | if "%1" == "text" (
181 | %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text
182 | if errorlevel 1 exit /b 1
183 | echo.
184 | echo.Build finished. The text files are in %BUILDDIR%/text.
185 | goto end
186 | )
187 |
188 | if "%1" == "man" (
189 | %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man
190 | if errorlevel 1 exit /b 1
191 | echo.
192 | echo.Build finished. The manual pages are in %BUILDDIR%/man.
193 | goto end
194 | )
195 |
196 | if "%1" == "texinfo" (
197 | %SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo
198 | if errorlevel 1 exit /b 1
199 | echo.
200 | echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo.
201 | goto end
202 | )
203 |
204 | if "%1" == "gettext" (
205 | %SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale
206 | if errorlevel 1 exit /b 1
207 | echo.
208 | echo.Build finished. The message catalogs are in %BUILDDIR%/locale.
209 | goto end
210 | )
211 |
212 | if "%1" == "changes" (
213 | %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes
214 | if errorlevel 1 exit /b 1
215 | echo.
216 | echo.The overview file is in %BUILDDIR%/changes.
217 | goto end
218 | )
219 |
220 | if "%1" == "linkcheck" (
221 | %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck
222 | if errorlevel 1 exit /b 1
223 | echo.
224 | echo.Link check complete; look for any errors in the above output ^
225 | or in %BUILDDIR%/linkcheck/output.txt.
226 | goto end
227 | )
228 |
229 | if "%1" == "doctest" (
230 | %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest
231 | if errorlevel 1 exit /b 1
232 | echo.
233 | echo.Testing of doctests in the sources finished, look at the ^
234 | results in %BUILDDIR%/doctest/output.txt.
235 | goto end
236 | )
237 |
238 | if "%1" == "coverage" (
239 | %SPHINXBUILD% -b coverage %ALLSPHINXOPTS% %BUILDDIR%/coverage
240 | if errorlevel 1 exit /b 1
241 | echo.
242 | echo.Testing of coverage in the sources finished, look at the ^
243 | results in %BUILDDIR%/coverage/python.txt.
244 | goto end
245 | )
246 |
247 | if "%1" == "xml" (
248 | %SPHINXBUILD% -b xml %ALLSPHINXOPTS% %BUILDDIR%/xml
249 | if errorlevel 1 exit /b 1
250 | echo.
251 | echo.Build finished. The XML files are in %BUILDDIR%/xml.
252 | goto end
253 | )
254 |
255 | if "%1" == "pseudoxml" (
256 | %SPHINXBUILD% -b pseudoxml %ALLSPHINXOPTS% %BUILDDIR%/pseudoxml
257 | if errorlevel 1 exit /b 1
258 | echo.
259 | echo.Build finished. The pseudo-XML files are in %BUILDDIR%/pseudoxml.
260 | goto end
261 | )
262 |
263 | :end
264 |
--------------------------------------------------------------------------------
/docs/Makefile:
--------------------------------------------------------------------------------
1 | # Makefile for Sphinx documentation
2 | #
3 |
4 | # You can set these variables from the command line.
5 | SPHINXOPTS =
6 | SPHINXBUILD = sphinx-build
7 | PAPER =
8 | BUILDDIR = _build
9 |
10 | # User-friendly check for sphinx-build
11 | ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1)
12 | $(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/)
13 | endif
14 |
15 | # Internal variables.
16 | PAPEROPT_a4 = -D latex_paper_size=a4
17 | PAPEROPT_letter = -D latex_paper_size=letter
18 | ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
19 | # the i18n builder cannot share the environment and doctrees with the others
20 | I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
21 |
22 | .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest coverage gettext
23 |
24 | help:
25 | @echo "Please use \`make ' where is one of"
26 | @echo " html to make standalone HTML files"
27 | @echo " dirhtml to make HTML files named index.html in directories"
28 | @echo " singlehtml to make a single large HTML file"
29 | @echo " pickle to make pickle files"
30 | @echo " json to make JSON files"
31 | @echo " htmlhelp to make HTML files and a HTML help project"
32 | @echo " qthelp to make HTML files and a qthelp project"
33 | @echo " applehelp to make an Apple Help Book"
34 | @echo " devhelp to make HTML files and a Devhelp project"
35 | @echo " epub to make an epub"
36 | @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
37 | @echo " latexpdf to make LaTeX files and run them through pdflatex"
38 | @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx"
39 | @echo " text to make text files"
40 | @echo " man to make manual pages"
41 | @echo " texinfo to make Texinfo files"
42 | @echo " info to make Texinfo files and run them through makeinfo"
43 | @echo " gettext to make PO message catalogs"
44 | @echo " changes to make an overview of all changed/added/deprecated items"
45 | @echo " xml to make Docutils-native XML files"
46 | @echo " pseudoxml to make pseudoxml-XML files for display purposes"
47 | @echo " linkcheck to check all external links for integrity"
48 | @echo " doctest to run all doctests embedded in the documentation (if enabled)"
49 | @echo " coverage to run coverage check of the documentation (if enabled)"
50 |
51 | clean:
52 | rm -rf $(BUILDDIR)/*
53 |
54 | html:
55 | $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
56 | @echo
57 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
58 |
59 | dirhtml:
60 | $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
61 | @echo
62 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
63 |
64 | singlehtml:
65 | $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml
66 | @echo
67 | @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml."
68 |
69 | pickle:
70 | $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
71 | @echo
72 | @echo "Build finished; now you can process the pickle files."
73 |
74 | json:
75 | $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
76 | @echo
77 | @echo "Build finished; now you can process the JSON files."
78 |
79 | htmlhelp:
80 | $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
81 | @echo
82 | @echo "Build finished; now you can run HTML Help Workshop with the" \
83 | ".hhp project file in $(BUILDDIR)/htmlhelp."
84 |
85 | qthelp:
86 | $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
87 | @echo
88 | @echo "Build finished; now you can run "qcollectiongenerator" with the" \
89 | ".qhcp project file in $(BUILDDIR)/qthelp, like this:"
90 | @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/django-gcm-ios-android.qhcp"
91 | @echo "To view the help file:"
92 | @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/django-gcm-ios-android.qhc"
93 |
94 | applehelp:
95 | $(SPHINXBUILD) -b applehelp $(ALLSPHINXOPTS) $(BUILDDIR)/applehelp
96 | @echo
97 | @echo "Build finished. The help book is in $(BUILDDIR)/applehelp."
98 | @echo "N.B. You won't be able to view it unless you put it in" \
99 | "~/Library/Documentation/Help or install it in your application" \
100 | "bundle."
101 |
102 | devhelp:
103 | $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp
104 | @echo
105 | @echo "Build finished."
106 | @echo "To view the help file:"
107 | @echo "# mkdir -p $$HOME/.local/share/devhelp/django-gcm-ios-android"
108 | @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/django-gcm-ios-android"
109 | @echo "# devhelp"
110 |
111 | epub:
112 | $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub
113 | @echo
114 | @echo "Build finished. The epub file is in $(BUILDDIR)/epub."
115 |
116 | latex:
117 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
118 | @echo
119 | @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
120 | @echo "Run \`make' in that directory to run these through (pdf)latex" \
121 | "(use \`make latexpdf' here to do that automatically)."
122 |
123 | latexpdf:
124 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
125 | @echo "Running LaTeX files through pdflatex..."
126 | $(MAKE) -C $(BUILDDIR)/latex all-pdf
127 | @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
128 |
129 | latexpdfja:
130 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
131 | @echo "Running LaTeX files through platex and dvipdfmx..."
132 | $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja
133 | @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
134 |
135 | text:
136 | $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text
137 | @echo
138 | @echo "Build finished. The text files are in $(BUILDDIR)/text."
139 |
140 | man:
141 | $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man
142 | @echo
143 | @echo "Build finished. The manual pages are in $(BUILDDIR)/man."
144 |
145 | texinfo:
146 | $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
147 | @echo
148 | @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo."
149 | @echo "Run \`make' in that directory to run these through makeinfo" \
150 | "(use \`make info' here to do that automatically)."
151 |
152 | info:
153 | $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
154 | @echo "Running Texinfo files through makeinfo..."
155 | make -C $(BUILDDIR)/texinfo info
156 | @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo."
157 |
158 | gettext:
159 | $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale
160 | @echo
161 | @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale."
162 |
163 | changes:
164 | $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
165 | @echo
166 | @echo "The overview file is in $(BUILDDIR)/changes."
167 |
168 | linkcheck:
169 | $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
170 | @echo
171 | @echo "Link check complete; look for any errors in the above output " \
172 | "or in $(BUILDDIR)/linkcheck/output.txt."
173 |
174 | doctest:
175 | $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
176 | @echo "Testing of doctests in the sources finished, look at the " \
177 | "results in $(BUILDDIR)/doctest/output.txt."
178 |
179 | coverage:
180 | $(SPHINXBUILD) -b coverage $(ALLSPHINXOPTS) $(BUILDDIR)/coverage
181 | @echo "Testing of coverage in the sources finished, look at the " \
182 | "results in $(BUILDDIR)/coverage/python.txt."
183 |
184 | xml:
185 | $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml
186 | @echo
187 | @echo "Build finished. The XML files are in $(BUILDDIR)/xml."
188 |
189 | pseudoxml:
190 | $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml
191 | @echo
192 | @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml."
193 |
--------------------------------------------------------------------------------
/docs/_themes/kr/static/flasky.css_t:
--------------------------------------------------------------------------------
1 | /*
2 | * flasky.css_t
3 | * ~~~~~~~~~~~~
4 | *
5 | * :copyright: Copyright 2010 by Armin Ronacher. Modifications by Kenneth Reitz.
6 | * :license: Flask Design License, see LICENSE for details.
7 | */
8 |
9 | {% set page_width = '940px' %}
10 | {% set sidebar_width = '220px' %}
11 |
12 | @import url("basic.css");
13 |
14 | /* -- page layout ----------------------------------------------------------- */
15 |
16 | body {
17 | font-family: 'goudy old style', 'minion pro', 'bell mt', Georgia, 'Hiragino Mincho Pro';
18 | font-size: 17px;
19 | background-color: white;
20 | color: #000;
21 | margin: 0;
22 | padding: 0;
23 | }
24 |
25 | div.document {
26 | width: {{ page_width }};
27 | margin: 30px auto 0 auto;
28 | }
29 |
30 | div.documentwrapper {
31 | float: left;
32 | width: 100%;
33 | }
34 |
35 | div.bodywrapper {
36 | margin: 0 0 0 {{ sidebar_width }};
37 | }
38 |
39 | div.sphinxsidebar {
40 | width: {{ sidebar_width }};
41 | }
42 |
43 | hr {
44 | border: 1px solid #B1B4B6;
45 | }
46 |
47 | div.body {
48 | background-color: #ffffff;
49 | color: #3E4349;
50 | padding: 0 30px 0 30px;
51 | }
52 |
53 | img.floatingflask {
54 | padding: 0 0 10px 10px;
55 | float: right;
56 | }
57 |
58 | div.footer {
59 | width: {{ page_width }};
60 | margin: 20px auto 30px auto;
61 | font-size: 14px;
62 | color: #888;
63 | text-align: right;
64 | }
65 |
66 | div.footer a {
67 | color: #888;
68 | }
69 |
70 | div.related {
71 | display: none;
72 | }
73 |
74 | div.sphinxsidebar a {
75 | color: #444;
76 | text-decoration: none;
77 | border-bottom: 1px dotted #999;
78 | }
79 |
80 | div.sphinxsidebar a:hover {
81 | border-bottom: 1px solid #999;
82 | }
83 |
84 | div.sphinxsidebar {
85 | font-size: 14px;
86 | line-height: 1.5;
87 | }
88 |
89 | div.sphinxsidebarwrapper {
90 | padding: 18px 10px;
91 | }
92 |
93 | div.sphinxsidebarwrapper p.logo {
94 | padding: 0;
95 | margin: -10px 0 0 -20px;
96 | text-align: center;
97 | }
98 |
99 | div.sphinxsidebar h3,
100 | div.sphinxsidebar h4 {
101 | font-family: 'Garamond', 'Georgia', serif;
102 | color: #444;
103 | font-size: 24px;
104 | font-weight: normal;
105 | margin: 0 0 5px 0;
106 | padding: 0;
107 | }
108 |
109 | div.sphinxsidebar h4 {
110 | font-size: 20px;
111 | }
112 |
113 | div.sphinxsidebar h3 a {
114 | color: #444;
115 | }
116 |
117 | div.sphinxsidebar p.logo a,
118 | div.sphinxsidebar h3 a,
119 | div.sphinxsidebar p.logo a:hover,
120 | div.sphinxsidebar h3 a:hover {
121 | border: none;
122 | }
123 |
124 | div.sphinxsidebar p {
125 | color: #555;
126 | margin: 10px 0;
127 | }
128 |
129 | div.sphinxsidebar ul {
130 | margin: 10px 0;
131 | padding: 0;
132 | color: #000;
133 | }
134 |
135 | div.sphinxsidebar input {
136 | border: 1px solid #ccc;
137 | font-family: 'Georgia', serif;
138 | font-size: 1em;
139 | }
140 |
141 | /* -- body styles ----------------------------------------------------------- */
142 |
143 | a {
144 | color: #004B6B;
145 | text-decoration: underline;
146 | }
147 |
148 | a:hover {
149 | color: #6D4100;
150 | text-decoration: underline;
151 | }
152 |
153 | div.body h1,
154 | div.body h2,
155 | div.body h3,
156 | div.body h4,
157 | div.body h5,
158 | div.body h6 {
159 | font-family: 'Garamond', 'Georgia', serif;
160 | font-weight: normal;
161 | margin: 30px 0px 10px 0px;
162 | padding: 0;
163 | }
164 |
165 | div.body h1 { margin-top: 0; padding-top: 0; font-size: 240%; }
166 | div.body h2 { font-size: 180%; }
167 | div.body h3 { font-size: 150%; }
168 | div.body h4 { font-size: 130%; }
169 | div.body h5 { font-size: 100%; }
170 | div.body h6 { font-size: 100%; }
171 |
172 | a.headerlink {
173 | color: #ddd;
174 | padding: 0 4px;
175 | text-decoration: none;
176 | }
177 |
178 | a.headerlink:hover {
179 | color: #444;
180 | background: #eaeaea;
181 | }
182 |
183 | div.body p, div.body dd, div.body li {
184 | line-height: 1.4em;
185 | }
186 |
187 | div.admonition {
188 | background: #fafafa;
189 | margin: 20px -30px;
190 | padding: 10px 30px;
191 | border-top: 1px solid #ccc;
192 | border-bottom: 1px solid #ccc;
193 | }
194 |
195 | div.admonition tt.xref, div.admonition a tt {
196 | border-bottom: 1px solid #fafafa;
197 | }
198 |
199 | dd div.admonition {
200 | margin-left: -60px;
201 | padding-left: 60px;
202 | }
203 |
204 | div.admonition p.admonition-title {
205 | font-family: 'Garamond', 'Georgia', serif;
206 | font-weight: normal;
207 | font-size: 24px;
208 | margin: 0 0 10px 0;
209 | padding: 0;
210 | line-height: 1;
211 | }
212 |
213 | div.admonition p.last {
214 | margin-bottom: 0;
215 | }
216 |
217 | div.highlight {
218 | background-color: white;
219 | }
220 |
221 | dt:target, .highlight {
222 | background: #FAF3E8;
223 | }
224 |
225 | div.note {
226 | background-color: #eee;
227 | border: 1px solid #ccc;
228 | }
229 |
230 | div.seealso {
231 | background-color: #ffc;
232 | border: 1px solid #ff6;
233 | }
234 |
235 | div.topic {
236 | background-color: #eee;
237 | }
238 |
239 | p.admonition-title {
240 | display: inline;
241 | }
242 |
243 | p.admonition-title:after {
244 | content: ":";
245 | }
246 |
247 | pre, tt {
248 | font-family: 'Consolas', 'Menlo', 'Deja Vu Sans Mono', 'Bitstream Vera Sans Mono', monospace;
249 | font-size: 0.9em;
250 | }
251 |
252 | img.screenshot {
253 | }
254 |
255 | tt.descname, tt.descclassname {
256 | font-size: 0.95em;
257 | }
258 |
259 | tt.descname {
260 | padding-right: 0.08em;
261 | }
262 |
263 | img.screenshot {
264 | -moz-box-shadow: 2px 2px 4px #eee;
265 | -webkit-box-shadow: 2px 2px 4px #eee;
266 | box-shadow: 2px 2px 4px #eee;
267 | }
268 |
269 | table.docutils {
270 | border: 1px solid #888;
271 | -moz-box-shadow: 2px 2px 4px #eee;
272 | -webkit-box-shadow: 2px 2px 4px #eee;
273 | box-shadow: 2px 2px 4px #eee;
274 | }
275 |
276 | table.docutils td, table.docutils th {
277 | border: 1px solid #888;
278 | padding: 0.25em 0.7em;
279 | }
280 |
281 | table.field-list, table.footnote {
282 | border: none;
283 | -moz-box-shadow: none;
284 | -webkit-box-shadow: none;
285 | box-shadow: none;
286 | }
287 |
288 | table.footnote {
289 | margin: 15px 0;
290 | width: 100%;
291 | border: 1px solid #eee;
292 | background: #fdfdfd;
293 | font-size: 0.9em;
294 | }
295 |
296 | table.footnote + table.footnote {
297 | margin-top: -15px;
298 | border-top: none;
299 | }
300 |
301 | table.field-list th {
302 | padding: 0 0.8em 0 0;
303 | }
304 |
305 | table.field-list td {
306 | padding: 0;
307 | }
308 |
309 | table.footnote td.label {
310 | width: 0px;
311 | padding: 0.3em 0 0.3em 0.5em;
312 | }
313 |
314 | table.footnote td {
315 | padding: 0.3em 0.5em;
316 | }
317 |
318 | dl {
319 | margin: 0;
320 | padding: 0;
321 | }
322 |
323 | dl dd {
324 | margin-left: 30px;
325 | }
326 |
327 | blockquote {
328 | margin: 0 0 0 30px;
329 | padding: 0;
330 | }
331 |
332 | ul, ol {
333 | margin: 10px 0 10px 30px;
334 | padding: 0;
335 | }
336 |
337 | pre {
338 | background: #eee;
339 | padding: 7px 30px;
340 | margin: 15px -30px;
341 | line-height: 1.3em;
342 | }
343 |
344 | dl pre, blockquote pre, li pre {
345 | margin-left: -60px;
346 | padding-left: 60px;
347 | }
348 |
349 | dl dl pre {
350 | margin-left: -90px;
351 | padding-left: 90px;
352 | }
353 |
354 | tt {
355 | background-color: #ecf0f3;
356 | color: #222;
357 | /* padding: 1px 2px; */
358 | }
359 |
360 | tt.xref, a tt {
361 | background-color: #FBFBFB;
362 | border-bottom: 1px solid white;
363 | }
364 |
365 | a.reference {
366 | text-decoration: none;
367 | border-bottom: 1px dotted #004B6B;
368 | }
369 |
370 | a.reference:hover {
371 | border-bottom: 1px solid #6D4100;
372 | }
373 |
374 | a.footnote-reference {
375 | text-decoration: none;
376 | font-size: 0.7em;
377 | vertical-align: top;
378 | border-bottom: 1px dotted #004B6B;
379 | }
380 |
381 | a.footnote-reference:hover {
382 | border-bottom: 1px solid #6D4100;
383 | }
384 |
385 | a:hover tt {
386 | background: #EEE;
387 | }
388 |
389 |
390 | @media screen and (max-width: 600px) {
391 |
392 | div.sphinxsidebar {
393 | display: none;
394 | }
395 |
396 | div.document {
397 | width: 100%;
398 |
399 | }
400 |
401 | div.documentwrapper {
402 | margin-left: 0;
403 | margin-top: 0;
404 | margin-right: 0;
405 | margin-bottom: 0;
406 | }
407 |
408 | div.bodywrapper {
409 | margin-top: 0;
410 | margin-right: 0;
411 | margin-bottom: 0;
412 | margin-left: 0;
413 | }
414 |
415 | ul {
416 | margin-left: 0;
417 | }
418 |
419 | .document {
420 | width: auto;
421 | }
422 |
423 | .footer {
424 | width: auto;
425 | }
426 |
427 | .bodywrapper {
428 | margin: 0;
429 | }
430 |
431 | .footer {
432 | width: auto;
433 | }
434 |
435 | .github {
436 | display: none;
437 | }
438 |
439 | }
440 |
441 | /* misc. */
442 |
443 | .revsys-inline {
444 | display: none!important;
445 | }
--------------------------------------------------------------------------------
/docs/conf.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding: utf-8 -*-
3 | #
4 | # django-gcm-ios-android documentation build configuration file, created by
5 | # sphinx-quickstart on Fri Jul 10 13:54:46 2015.
6 | #
7 | # This file is execfile()d with the current directory set to its
8 | # containing dir.
9 | #
10 | # Note that not all possible configuration values are present in this
11 | # autogenerated file.
12 | #
13 | # All configuration values have a default; values that are commented out
14 | # serve to show the default.
15 |
16 | import os
17 |
18 | import sys
19 |
20 |
21 | # If extensions (or modules to document with autodoc) are in another directory,
22 | # add these directories to sys.path here. If the directory is relative to the
23 | # documentation root, use os.path.abspath to make it absolute, like shown here.
24 | # sys.path.insert(0, os.path.abspath('.'))
25 |
26 | # -- General configuration ------------------------------------------------
27 |
28 | # If your documentation needs a minimal Sphinx version, state it here.
29 | # needs_sphinx = '1.0'
30 |
31 | # Add any Sphinx extension module names here, as strings. They can be
32 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
33 | # ones.
34 | extensions = [
35 | 'sphinx.ext.pngmath',
36 | ]
37 |
38 | # Add any paths that contain templates here, relative to this directory.
39 | templates_path = ['_templates']
40 |
41 | # The suffix(es) of source filenames.
42 | # You can specify multiple suffix as a list of string:
43 | # source_suffix = ['.rst', '.md']
44 | source_suffix = '.rst'
45 |
46 | # The encoding of source files.
47 | # source_encoding = 'utf-8-sig'
48 |
49 | # The master toctree document.
50 | master_doc = 'index'
51 |
52 | # General information about the project.
53 | project = 'django-gcm-android-ios'
54 | copyright = '2015, Hugo Brilhante'
55 | author = 'Hugo Brilhante'
56 |
57 | # The version info for the project you're documenting, acts as replacement for
58 | # |version| and |release|, also used in various other places throughout the
59 | # built documents.
60 | #
61 | # The short X.Y version.
62 | version = '1.0.0'
63 | # The full version, including alpha/beta/rc tags.
64 | release = '1.0.0'
65 |
66 | # The language for content autogenerated by Sphinx. Refer to documentation
67 | # for a list of supported languages.
68 | #
69 | # This is also used if you do content translation via gettext catalogs.
70 | # Usually you set "language" from the command line for these cases.
71 | language = None
72 |
73 | # There are two options for replacing |today|: either, you set today to some
74 | # non-false value, then it is used:
75 | # today = ''
76 | # Else, today_fmt is used as the format for a strftime call.
77 | # today_fmt = '%B %d, %Y'
78 |
79 | # List of patterns, relative to source directory, that match files and
80 | # directories to ignore when looking for source files.
81 | exclude_patterns = ['_build']
82 |
83 | # The reST default role (used for this markup: `text`) to use for all
84 | # documents.
85 | # default_role = None
86 |
87 | # If true, '()' will be appended to :func: etc. cross-reference text.
88 | # add_function_parentheses = True
89 |
90 | # If true, the current module name will be prepended to all description
91 | # unit titles (such as .. function::).
92 | # add_module_names = True
93 |
94 | # If true, sectionauthor and moduleauthor directives will be shown in the
95 | # output. They are ignored by default.
96 | # show_authors = False
97 |
98 | # The name of the Pygments (syntax highlighting) style to use.
99 | pygments_style = 'flask_theme_support.FlaskyStyle'
100 |
101 | # A list of ignored prefixes for module index sorting.
102 | # modindex_common_prefix = []
103 |
104 | # If true, keep warnings as "system message" paragraphs in the built documents.
105 | # keep_warnings = False
106 |
107 | # If true, `todo` and `todoList` produce output, else they produce nothing.
108 | todo_include_todos = False
109 |
110 |
111 | # -- Options for HTML output ----------------------------------------------
112 |
113 | # The theme to use for HTML and HTML Help pages. See the documentation for
114 | # a list of builtin themes.
115 | # html_theme = 'alabaster'
116 |
117 | # Theme options are theme-specific and customize the look and feel of a theme
118 | # further. For a list of options available for each theme, see the
119 | # documentation.
120 | # html_theme_options = {}
121 |
122 | # Add any paths that contain custom themes here, relative to this directory.
123 | # html_theme_path = []
124 |
125 | # The name for this set of Sphinx documents. If None, it defaults to
126 | # " v documentation".
127 | # html_title = None
128 |
129 | # A shorter title for the navigation bar. Default is the same as html_title.
130 | # html_short_title = None
131 |
132 | # The name of an image file (relative to this directory) to place at the top
133 | # of the sidebar.
134 | # html_logo = None
135 |
136 | # The name of an image file (within the static path) to use as favicon of the
137 | # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
138 | # pixels large.
139 | # html_favicon = None
140 |
141 | # Add any paths that contain custom static files (such as style sheets) here,
142 | # relative to this directory. They are copied after the builtin static files,
143 | # so a file named "default.css" will overwrite the builtin "default.css".
144 | html_static_path = ['_static']
145 |
146 | # Add any extra paths that contain custom files (such as robots.txt or
147 | # .htaccess) here, relative to this directory. These files are copied
148 | # directly to the root of the documentation.
149 | # html_extra_path = []
150 |
151 | # If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
152 | # using the given strftime format.
153 | # html_last_updated_fmt = '%b %d, %Y'
154 |
155 | # If true, SmartyPants will be used to convert quotes and dashes to
156 | # typographically correct entities.
157 | # html_use_smartypants = True
158 |
159 | # Custom sidebar templates, maps document names to template names.
160 | # html_sidebars = {}
161 |
162 | # Additional templates that should be rendered to pages, maps page names to
163 | # template names.
164 | # html_additional_pages = {}
165 |
166 | # If false, no module index is generated.
167 | # html_domain_indices = True
168 |
169 | # If false, no index is generated.
170 | # html_use_index = True
171 |
172 | # If true, the index is split into individual pages for each letter.
173 | # html_split_index = False
174 |
175 | # If true, links to the reST sources are added to the pages.
176 | # html_show_sourcelink = True
177 |
178 | # If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
179 | # html_show_sphinx = True
180 |
181 | # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
182 | # html_show_copyright = True
183 |
184 | # If true, an OpenSearch description file will be output, and all pages will
185 | # contain a tag referring to it. The value of this option must be the
186 | # base URL from which the finished HTML is served.
187 | # html_use_opensearch = ''
188 |
189 | # This is the file name suffix for HTML files (e.g. ".xhtml").
190 | # html_file_suffix = None
191 |
192 | # Language to be used for generating the HTML full-text search index.
193 | # Sphinx supports the following languages:
194 | # 'da', 'de', 'en', 'es', 'fi', 'fr', 'h', 'it', 'ja'
195 | # 'nl', 'no', 'pt', 'ro', 'r', 'sv', 'tr'
196 | # html_search_language = 'en'
197 |
198 | # A dictionary with options for the search language support, empty by default.
199 | # Now only 'ja' uses this config value
200 | # html_search_options = {'type': 'default'}
201 |
202 | # The name of a javascript file (relative to the configuration directory) that
203 | # implements a search results scorer. If empty, the default will be used.
204 | # html_search_scorer = 'scorer.js'
205 |
206 | # Output file base name for HTML help builder.
207 | htmlhelp_basename = 'django-gcm-android-iosdoc'
208 |
209 | # -- Options for LaTeX output ---------------------------------------------
210 |
211 | latex_elements = {
212 | # The paper size ('letterpaper' or 'a4paper').
213 | # 'papersize': 'letterpaper',
214 |
215 | # The font size ('10pt', '11pt' or '12pt').
216 | # 'pointsize': '10pt',
217 |
218 | # Additional stuff for the LaTeX preamble.
219 | # 'preamble': '',
220 |
221 | # Latex figure (float) alignment
222 | # 'figure_align': 'htbp',
223 | }
224 |
225 | # Grouping the document tree into LaTeX files. List of tuples
226 | # (source start file, target name, title,
227 | # author, documentclass [howto, manual, or own class]).
228 | latex_documents = [
229 | (master_doc, 'django-gcm-android-ios.tex', 'django-gcm-android-ios Documentation',
230 | 'Hugo Brilhante', 'manual'),
231 | ]
232 |
233 | # The name of an image file (relative to this directory) to place at the top of
234 | # the title page.
235 | # latex_logo = None
236 |
237 | # For "manual" documents, if this is true, then toplevel headings are parts,
238 | # not chapters.
239 | # latex_use_parts = False
240 |
241 | # If true, show page references after internal links.
242 | # latex_show_pagerefs = False
243 |
244 | # If true, show URL addresses after external links.
245 | # latex_show_urls = False
246 |
247 | # Documents to append as an appendix to all manuals.
248 | # latex_appendices = []
249 |
250 | # If false, no module index is generated.
251 | # latex_domain_indices = True
252 |
253 |
254 | # -- Options for manual page output ---------------------------------------
255 |
256 | # One entry per manual page. List of tuples
257 | # (source start file, name, description, authors, manual section).
258 | man_pages = [
259 | (master_doc, 'django-gcm-android-ios', 'django-gcm-android-ios Documentation',
260 | [author], 1)
261 | ]
262 |
263 | # If true, show URL addresses after external links.
264 | # man_show_urls = False
265 |
266 |
267 | # -- Options for Texinfo output -------------------------------------------
268 |
269 | # Grouping the document tree into Texinfo files. List of tuples
270 | # (source start file, target name, title, author,
271 | # dir menu entry, description, category)
272 | texinfo_documents = [
273 | (master_doc, 'django-gcm-android-ios', 'django-gcm-android-ios Documentation',
274 | author, 'django-gcm-android-ios', 'One line description of project.',
275 | 'Miscellaneous'),
276 | ]
277 |
278 | # Documents to append as an appendix to all manuals.
279 | # texinfo_appendices = []
280 |
281 | # If false, no module index is generated.
282 | # texinfo_domain_indices = True
283 |
284 | # How to display URL addresses: 'footnote', 'no', or 'inline'.
285 | # texinfo_show_urls = 'footnote'
286 |
287 | # If true, do not generate a @detailmenu in the "Top" node's menu.
288 | # texinfo_no_detailmenu = False
289 |
290 |
291 |
292 | sys.path.append(os.path.abspath('_themes'))
293 | html_theme_path = ['_themes']
294 | html_theme = 'kr'
295 |
--------------------------------------------------------------------------------
/gcm/tests.py:
--------------------------------------------------------------------------------
1 | from mock import patch
2 |
3 | from django.contrib.auth import get_user_model
4 | from django.core.urlresolvers import reverse
5 | from django.test import TestCase, override_settings
6 | from rest_framework import status
7 | from rest_framework.test import APITestCase
8 | from gcm import utils
9 |
10 | User = get_user_model()
11 | Device = utils.get_device_model()
12 |
13 |
14 | class ApiDeviceTest(APITestCase):
15 | def setUp(self):
16 | self.user = User.objects.create(username='test', email='test@test.com')
17 | self.user.set_password('test')
18 | self.user.save()
19 | self.client.login(username='test', password='test')
20 | self.device = Device.objects.create(dev_id='123-1', dev_type='ANDROID', reg_id='123-1')
21 |
22 | def test_resgister_device(self):
23 | url = reverse('device-list')
24 | data = {'dev_id': '123-2', 'dev_type': 'ANDROID', 'reg_id': '123-2'}
25 | response = self.client.post(url, data, format='json')
26 | self.assertEqual(response.status_code, status.HTTP_201_CREATED)
27 | self.assertEqual(response.data, data)
28 |
29 | def test_unresgister_device(self):
30 | kwargs = {'pk': 1}
31 | url = reverse('device-detail', kwargs=kwargs)
32 | response = self.client.delete(url)
33 | self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT)
34 | self.assertEqual(response.data, None)
35 |
36 |
37 | class ModelDeviceTest(TestCase):
38 | def setUp(self):
39 | self.device = Device.objects.create(dev_id='123-1', dev_type='ANDROID', reg_id='123-1')
40 |
41 | self.not_registered = Device.objects.create(dev_id='123-2', dev_type='ANDROID',
42 | reg_id='fZBdS8D3a7U:APA91bEOMCWkMVR_IMirgiCPeFz2v7Ju_cLCaYyVYySa80s'
43 | 'fBaobYV360R2_1b0LtJQIrGGfdBbFiZvY1vM10voqPdljdYsFmKnyR8H8S4'
44 | '1bdah4LiwHgZCWy5DOP9xhMIDcIFpKugZV')
45 | self.mismatch_sender_id = Device.objects.create(dev_id='123-3', dev_type='ANDROID',
46 | reg_id='APA91bEOMCWkMVR_IMirgiCPeFz2v7Ju_cLCaYyVYySa80sfBaobYV360R2'
47 | '_1b0LtJQIrGGfdBbFiZvY1vM10voqPdljdYsFmKnyR8H8S41bdah4LiwHgZC'
48 | 'Wy5DOP9xhMIDcIFpKugZV')
49 |
50 | def test_mark_inactive(self):
51 | self.device.mark_inactive()
52 | self.assertEqual(Device.objects.filter(is_active=False).count(), 1)
53 |
54 | @override_settings(GCM_ANDROID_APIKEY='AIzaSyDTOsEsbVUnm2sPVHrV2AuiBMsN9279czQ', )
55 | def test_send_message_error_NotRegistered_real(self):
56 | response = self.not_registered.send_message()
57 | self.assertEqual(response, 'The client app unregisters with GCM.')
58 |
59 | @override_settings(GCM_ANDROID_APIKEY='AIzaSyDTOsEsbVUnm2sPVHrV2AuiBMsN9279czQ')
60 | def test_send_message_error_MismatchSenderId_real(self):
61 | response = self.mismatch_sender_id.send_message()
62 | self.assertEqual(response, 'A registration token is tied to a certain group of senders.')
63 |
64 | @patch.object(Device, 'send_message')
65 | def test_send_message_successfully(self, mock_send_message):
66 | mock_send_message.return_value = 'Message send successfully'
67 | response = self.device.send_message()
68 | self.assertEqual(response, 'Message send successfully')
69 |
70 | @patch.object(Device, 'send_message')
71 | def test_send_message_error_MissingRegistration(self, mock_send_message):
72 | mock_send_message.return_value = 'Check that the request contains a registration token'
73 | response = self.device.send_message()
74 | self.assertEqual(response, 'Check that the request contains a registration token')
75 |
76 | @patch.object(Device, 'send_message')
77 | def test_send_message_error_InvalidRegistration(self, mock_send_message):
78 | mock_send_message.return_value = 'Check the format of the registration token you pass to the server.'
79 | response = self.device.send_message()
80 | self.assertEqual(response, 'Check the format of the registration token you pass to the server.')
81 |
82 | @patch.object(Device, 'send_message')
83 | def test_send_message_error_NotRegistered(self, mock_send_message):
84 | mock_send_message.return_value = 'The client app unregisters with GCM.'
85 | response = self.device.send_message()
86 | self.assertEqual(response, 'The client app unregisters with GCM.')
87 |
88 | @patch.object(Device, 'send_message')
89 | def test_send_message_error_InvalidPackageName(self, mock_send_message):
90 | mock_send_message.return_value = ('Make sure the message was addressed to a registration token whose package'
91 | ' name matches the value passed in the request.')
92 | response = self.device.send_message()
93 | self.assertEqual(response, 'Make sure the message was addressed to a registration token whose package'
94 | ' name matches the value passed in the request.')
95 |
96 | @patch.object(Device, 'send_message')
97 | def test_send_message_error_MismatchSenderId(self, mock_send_message):
98 | mock_send_message.return_value = 'A registration token is tied to a certain group of senders.'
99 | response = self.device.send_message()
100 | self.assertEqual(response, 'A registration token is tied to a certain group of senders.')
101 |
102 | @patch.object(Device, 'send_message')
103 | def test_send_message_error_MessageTooBig(self, mock_send_message):
104 | mock_send_message.return_value = ('Check that the total size of the payload data included in a message does'
105 | ' not exceed GCM limits: 4096 bytes for most messages, or 2048 bytes in the case'
106 | ' of messages to topics or notification messages on iOS. This includes both'
107 | 'the keys and the values.')
108 | response = self.device.send_message()
109 | self.assertEqual(response, 'Check that the total size of the payload data included in a message does'
110 | ' not exceed GCM limits: 4096 bytes for most messages, or 2048 bytes in the case'
111 | ' of messages to topics or notification messages on iOS. This includes both'
112 | 'the keys and the values.')
113 |
114 | @patch.object(Device, 'send_message')
115 | def test_send_message_error_InvalidDataKey(self, mock_send_message):
116 | mock_send_message.return_value = ('Check that the payload data does not contain a key (such as from ,'
117 | ' or gcm , or any value prefixed by google ) that is used internally by GCM.')
118 | response = self.device.send_message()
119 | self.assertEqual(response, 'Check that the payload data does not contain a key (such as from ,'
120 | ' or gcm , or any value prefixed by google ) that is used internally by GCM.')
121 |
122 | @patch.object(Device, 'send_message')
123 | def test_send_message_error_InvalidTtl(self, mock_send_message):
124 | mock_send_message.return_value = ('Check that the value used in time_to_live is an integer representing a'
125 | ' duration in seconds between 0 and 2,419,200 (4 weeks).')
126 | response = self.device.send_message()
127 | self.assertEqual(response, 'Check that the value used in time_to_live is an integer representing a'
128 | ' duration in seconds between 0 and 2,419,200 (4 weeks).')
129 |
130 | @patch.object(Device, 'send_message')
131 | def test_send_message_error_Unavailable(self, mock_send_message):
132 | mock_send_message.return_value = 'The server couldn\'t process the request in time.'
133 | response = self.device.send_message()
134 | self.assertEqual(response, 'The server couldn\'t process the request in time.')
135 |
136 | @patch.object(Device, 'send_message')
137 | def test_send_message_error_InternalServerError(self, mock_send_message):
138 | mock_send_message.return_value = 'The server encountered an error while trying to process the request.'
139 | response = self.device.send_message()
140 | self.assertEqual(response, 'The server encountered an error while trying to process the request.')
141 |
142 | @patch.object(Device, 'send_message')
143 | def test_send_message_error_DeviceMessageRate(self, mock_send_message):
144 | mock_send_message.return_value = 'The rate of messages to a particular device is too high.'
145 | response = self.device.send_message()
146 | self.assertEqual(response, 'The rate of messages to a particular device is too high.')
147 |
148 | @patch.object(Device, 'send_message')
149 | def test_send_message_error_TopicsMessageRate(self, mock_send_message):
150 | mock_send_message.return_value = 'The rate of messages to subscribers to a particular topic is too high.'
151 | response = self.device.send_message()
152 | self.assertEqual(response, 'The rate of messages to subscribers to a particular topic is too high.')
153 |
154 | @patch.object(Device, 'send_message')
155 | def test_send_message_error_InvalidParameters(self, mock_send_message):
156 | mock_send_message.return_value = 'Check Parameters sent'
157 | response = self.device.send_message()
158 | self.assertEqual(response, 'Check Parameters sent')
159 |
160 |
161 | class UtilsTest(TestCase):
162 | def test_notification_push_real(self):
163 | response = utils.notification_push('ANDROID',
164 | 'to',
165 | 'message')
166 | self.assertEqual(response, {'error': '400 Client Error: Bad Request'})
167 |
168 | def test_get_device_model_real(self):
169 | response = utils.get_device_model()
170 | self.assertIs(response, Device)
171 |
172 | @patch('gcm.utils.notification_push')
173 | def test_notification_push_mock(self, mock_notification_push):
174 | mock_notification_push.return_value = 'Message send successfully'
175 | response = utils.notification_push('dev_type', 'to', 'message')
176 | self.assertEqual(response, 'Message send successfully')
177 |
178 | @patch('gcm.utils.get_device_model')
179 | def test_get_device_model_mock(self, mock_get_device_model):
180 | mock_get_device_model.return_value = Device
181 | response = utils.get_device_model()
182 | self.assertIs(response, Device)
183 |
--------------------------------------------------------------------------------