diff --git a/.circleci/config.yml b/.circleci/config.yml new file mode 100644 index 0000000..18376ae --- /dev/null +++ b/.circleci/config.yml @@ -0,0 +1,81 @@ +# Python CircleCI 2.0 configuration file +# +# Check https://circleci.com/docs/2.0/language-python/ for more details +# + +version: 2 +workflows: + version: 2 + test: + jobs: + - test-3.6 + - test-2.7 +jobs: + test-3.6: + docker: + # specify the version you desire here + # use `-browsers` prefix for selenium tests, e.g. `3.6.1-browsers` + - image: circleci/python:3.6.5 + working_directory: ~/repo + + steps: + - checkout + + # Download and cache dependencies + - restore_cache: + keys: + - v1-dependencies-{{ checksum "requirements/test.txt" }} + # fallback to using the latest cache if no exact match is found + - v1-dependencies- + + - run: + name: activate virtualenv and install dependencies. + command: | + test -d venv || virtualenv -p python3 ./venv + . venv/bin/activate + pip install -r requirements/test.txt + + - save_cache: + paths: + - ./venv + key: v1-dependencies-{{ checksum "requirements/test.txt" }} + + - run: + name: Run tests + command: | + . venv/bin/activate + make run-tests + + # Based on answer https://discuss.circleci.com/t/run-tests-on-multiple-versions-of-python/15462 + test-2.7: + docker: + - image: circleci/python:2.7 + working_directory: ~/repo27 + + steps: + - checkout + + # Download and cache dependencies + - restore_cache: + keys: + - v1-27-dependencies-{{ checksum "requirements/test.txt" }} + # fallback to using the latest cache if no exact match is found + - v1-27-dependencies- + + - run: + name: activate virtualenv and install dependencies. + command: | + test -d venv || virtualenv ./venv + . venv/bin/activate + pip install -r requirements/test.txt + + - save_cache: + paths: + - ./venv + key: v1-27-dependencies-{{ checksum "requirements/test.txt" }} + + - run: + name: Run tests + command: | + . venv/bin/activate + make run-tests diff --git a/AUTHORS.txt b/AUTHORS.txt new file mode 100644 index 0000000..e69de29 diff --git a/CHANGELOG.rst b/CHANGELOG.rst new file mode 100644 index 0000000..7f1955f --- /dev/null +++ b/CHANGELOG.rst @@ -0,0 +1,19 @@ +Change Log +========== + +.. + All enhancements and patches to eox_hooks will be documented + in this file. It adheres to the structure of http://keepachangelog.com/ , + but in reStructuredText instead of Markdown (for ease of incorporation into + Sphinx documentation and the PyPI description). + + This project adheres to Semantic Versioning (http://semver.org/). +.. There should always be an "Unreleased" section for changes pending release. +Unreleased +---------- + +* + +[0.1.0] - 2020-07-08 +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + diff --git a/MANIFEST.in b/MANIFEST.in new file mode 100644 index 0000000..048fdb5 --- /dev/null +++ b/MANIFEST.in @@ -0,0 +1,5 @@ +include CHANGELOG.rst +include LICENSE.txt +include README.md +include requirements/base.in +recursive-include eox_hooks *.html *.png *.gif *js *.css *jpg *jpeg *svg *py diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..849af96 --- /dev/null +++ b/Makefile @@ -0,0 +1,43 @@ +############################################### +# +# EoxHooks commands. +# +############################################### + +# Define PIP_COMPILE_OPTS=-v to get more information during make upgrade. +PIP_COMPILE = pip-compile --rebuild --upgrade $(PIP_COMPILE_OPTS) + +.DEFAULT_GOAL := help + +help: ## display this help message + @echo "Please use \`make ' where is one of" + @grep '^[a-zA-Z]' $(MAKEFILE_LIST) | sort | awk -F ':.*?## ' 'NF==2 {printf "\033[36m %-25s\033[0m %s\n", $$1, $$2}' + +clean: ## delete most git-ignored files + find . -name '__pycache__' -exec rm -rf {} + + find . -name '*.pyc' -exec rm -f {} + + find . -name '*.pyo' -exec rm -f {} + + find . -name '*~' -exec rm -f {} + + +requirements: ## install environment requirements + pip install -r requirements/base.txt + +upgrade: export CUSTOM_COMPILE_COMMAND=make upgrade +upgrade: ## update the requirements/*.txt files with the latest packages satisfying requirements/*.in + pip install -qr requirements/pip-tools.txt + # Make sure to compile files after any other files they include! + $(PIP_COMPILE) -o requirements/pip-tools.txt requirements/pip-tools.in + $(PIP_COMPILE) -o requirements/base.txt requirements/base.in + $(PIP_COMPILE) -o requirements/test.txt requirements/test.in + +quality: clean ## check coding style with pycodestyle and pylint + pycodestyle ./eox_hooks + pylint ./eox_hooks --rcfile=./setup.cfg + isort --check-only --recursive --diff ./eox_hooks + +test-python: clean ## Run test suite. + pip install -r requirements/test.txt --exists-action w + coverage run --source ./eox_hooks manage.py test + coverage report -m --fail-under=80 + +run-tests: test-python quality \ No newline at end of file diff --git a/eox_hooks/__init__.py b/eox_hooks/__init__.py new file mode 100644 index 0000000..4d2ecde --- /dev/null +++ b/eox_hooks/__init__.py @@ -0,0 +1,7 @@ +""" +Init module for eox_hooks. +""" + +from __future__ import unicode_literals + +__version__ = '0.1.0' diff --git a/eox_hooks/apps.py b/eox_hooks/apps.py new file mode 100644 index 0000000..26329d9 --- /dev/null +++ b/eox_hooks/apps.py @@ -0,0 +1,42 @@ +""" +App configuration for eox_hooks. +""" + +from __future__ import unicode_literals + +from django.apps import AppConfig + + +class EoxHooksConfig(AppConfig): + """ + EoxHooks configuration. + """ + name = 'eox_hooks' + verbose_name = 'EoxHooks' + + plugin_app = { + 'url_config': { + 'lms.djangoapp': { + 'namespace': 'eox-hooks', + 'regex': r'^eox-hooks/', + 'relative_path': 'urls', + }, + 'cms.djangoapp': { + 'namespace': 'eox-hooks', + 'regex': r'^eox-hooks/', + 'relative_path': 'urls', + } + }, + 'settings_config': { + 'lms.djangoapp': { + 'common': {'relative_path': 'settings.common'}, + 'test': {'relative_path': 'settings.test'}, + 'production': {'relative_path': 'settings.production'}, + }, + 'cms.djangoapp': { + 'common': {'relative_path': 'settings.common'}, + 'test': {'relative_path': 'settings.test'}, + 'production': {'relative_path': 'settings.production'}, + }, + } + } diff --git a/eox_hooks/settings/__init__.py b/eox_hooks/settings/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/eox_hooks/settings/common.py b/eox_hooks/settings/common.py new file mode 100644 index 0000000..0a9fd63 --- /dev/null +++ b/eox_hooks/settings/common.py @@ -0,0 +1,41 @@ +""" +Common Django settings for eox_hooks project. + +For more information on this file, see +https://docs.djangoproject.com/en/1.11/topics/settings/ + +For the full list of settings and their values, see +https://docs.djangoproject.com/en/1.11/ref/settings/ +""" + +from __future__ import unicode_literals + +# Quick-start development settings - unsuitable for production +# See https://docs.djangoproject.com/en/1.11/howto/deployment/checklist/ + +# SECURITY WARNING: keep the secret key used in production secret! +SECRET_KEY = 'secret-key' + + +# Application definition + +INSTALLED_APPS = [] + +ROOT_URLCONF = 'eox_hooks.urls' + + +# Internationalization +# https://docs.djangoproject.com/en/1.11/topics/i18n/ + +LANGUAGE_CODE = 'en-us' + +TIME_ZONE = 'UTC' + +USE_TZ = True + + +def plugin_settings(settings): # pylint: disable=unused-argument + """ + Set of plugin settings used by the Open Edx platform. + More info: https://github.com/edx/edx-platform/blob/master/openedx/core/djangoapps/plugins/README.rst + """ diff --git a/eox_hooks/settings/production.py b/eox_hooks/settings/production.py new file mode 100644 index 0000000..2cb405d --- /dev/null +++ b/eox_hooks/settings/production.py @@ -0,0 +1,12 @@ +""" +Production Django settings for eox_hooks project. +""" + +from __future__ import unicode_literals + + +def plugin_settings(settings): # pylint: disable=unused-argument + """ + Set of plugin settings used by the Open Edx platform. + More info: https://github.com/edx/edx-platform/blob/master/openedx/core/djangoapps/plugins/README.rst + """ diff --git a/eox_hooks/settings/test.py b/eox_hooks/settings/test.py new file mode 100644 index 0000000..1bde9fc --- /dev/null +++ b/eox_hooks/settings/test.py @@ -0,0 +1,26 @@ +""" +Test Django settings for eox_hooks project. +""" + +from __future__ import unicode_literals + +from .common import * # pylint: disable=wildcard-import + +ALLOWED_HOSTS = ['*'] + +INSTALLED_APPS = [ + 'django.contrib.auth', + 'django.contrib.contenttypes', + 'eox_hooks', +] + +TIME_ZONE = 'UTC' + +# This key needs to be defined so that the check_apps_ready passes and the +# AppRegistry is loaded +DATABASES = { + 'default': { + 'ENGINE': 'django.db.backends.sqlite3', + 'NAME': 'db.sqlite3', + } +} diff --git a/eox_hooks/tests/__init__.py b/eox_hooks/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/eox_hooks/tests/test_views.py b/eox_hooks/tests/test_views.py new file mode 100644 index 0000000..61254f7 --- /dev/null +++ b/eox_hooks/tests/test_views.py @@ -0,0 +1,80 @@ +"""This file contains all the test for the general views.py file. + +Classes: + EOXInfoTestCase: Test info_view. +""" +from os.path import dirname, realpath +from subprocess import CalledProcessError, check_output + +from django.test import TestCase +from django.urls import reverse +from mock import patch +from rest_framework import status + +import eox_hooks +from eox_hooks import views + + +class EOXInfoTestCase(TestCase): + """Possible test's scenarios for info_view.""" + + def setUp(self): + """Setup common conditions for every test case""" + self.url = reverse('eox-info') + self.view_directory = dirname(realpath(views.__file__)) + + def test_view_info_accesible(self): + """ + This method tests the desired behavior of info_view when this + does not raise any exception. + + Expected behavior: + - Return expected content. + - Status code 200. + """ + git_data = check_output( + ['git', 'rev-parse', 'HEAD'], + cwd=self.view_directory, + ) + expected_result = { + 'version': eox_hooks.__version__, + 'name': 'eox-hooks', + 'git': git_data.decode().rstrip('\r\n'), + } + + response = self.client.get(self.url) + + content = response.json() + self.assertEqual(expected_result, content) + self.assertEqual(status.HTTP_200_OK, response.status_code) + + @patch('eox_hooks.views.check_output') + def test_view_info_response_data(self, check_output_mock): + """ + This method tests the desired behavior of info_view when + raise a CalledProcessError exception. + + Expected behavior: + - check_output called once with the right values. + - Return expected content. + - Status code 200. + """ + check_output_mock.side_effect = CalledProcessError( + cmd='test-error', + returncode=0, + ) + expected_result = { + 'version': eox_hooks.__version__, + 'name': 'eox-hooks', + 'git': '', + } + + response = self.client.get(self.url) + + content = response.json() + check_output_mock.assert_called_once_with( + ['git', 'rev-parse', 'HEAD'], + cwd=self.view_directory, + ) + self.assertEqual(expected_result, content) + self.assertEqual(status.HTTP_200_OK, response.status_code) diff --git a/eox_hooks/urls.py b/eox_hooks/urls.py new file mode 100644 index 0000000..a45e139 --- /dev/null +++ b/eox_hooks/urls.py @@ -0,0 +1,22 @@ +"""eox_hooks URL Configuration + +The `urlpatterns` list routes URLs to views. For more information please see: + https://docs.djangoproject.com/en/1.11/topics/http/urls/ +Examples: +Function views + 1. Add an import: from my_app import views + 2. Add a URL to urlpatterns: url(r'^$', views.home, name='home') +Class-based views + 1. Add an import: from other_app.views import Home + 2. Add a URL to urlpatterns: url(r'^$', Home.as_view(), name='home') +Including another URLconf + 1. Import the include() function: from django.conf.urls import url, include + 2. Add a URL to urlpatterns: url(r'^blog/', include('blog.urls')) +""" +from django.conf.urls import url + +from eox_hooks import views + +urlpatterns = [ + url(r'^eox-info$', views.info_view, name='eox-info'), +] diff --git a/eox_hooks/views.py b/eox_hooks/views.py new file mode 100644 index 0000000..771fbd6 --- /dev/null +++ b/eox_hooks/views.py @@ -0,0 +1,45 @@ +"""The generic views for the eox-hooks plugin project. + +Methods: + info_view: Show basic plugin information. +""" +from __future__ import unicode_literals + +from os.path import dirname, realpath +from subprocess import CalledProcessError, check_output + +from django.http import JsonResponse + +import eox_hooks + + +def info_view(request): # pylint: disable=unused-argument + """Show basic current plugin information. + + This view allows to identify the current plugin version per + instance by returning the version number or git commit identifier. + + Returns: + A dict mapping keys to the corresponding plugin version, name and git commit. + Example: + + { + 'version': 2.5.4, + 'name': 'eox-hooks', + 'git': 7862374283974923654, + } + """ + try: + working_dir = dirname(realpath(__file__)) + git_data = check_output(['git', 'rev-parse', 'HEAD'], cwd=working_dir) + git_data = git_data.decode().rstrip('\r\n') + except CalledProcessError: + git_data = '' + + return JsonResponse( + { + 'version': eox_hooks.__version__, + 'name': 'eox-hooks', + 'git': git_data, + }, + ) diff --git a/manage.py b/manage.py new file mode 100755 index 0000000..6df18bc --- /dev/null +++ b/manage.py @@ -0,0 +1,22 @@ +#!/usr/bin/env python +import os +import sys + +if __name__ == "__main__": + os.environ.setdefault("DJANGO_SETTINGS_MODULE", "eox_hooks.settings.test") + try: + from django.core.management import execute_from_command_line + except ImportError: + # The above import may fail for some other reason. Ensure that the + # issue is really that Django is missing to avoid masking other + # exceptions on Python 2. + try: + import django + except ImportError: + raise ImportError( + "Couldn't import Django. Are you sure it's installed and " + "available on your PYTHONPATH environment variable? Did you " + "forget to activate a virtual environment?" + ) + raise + execute_from_command_line(sys.argv) diff --git a/requirements/base.in b/requirements/base.in new file mode 100644 index 0000000..16e39c9 --- /dev/null +++ b/requirements/base.in @@ -0,0 +1,5 @@ +# Main requirements of your plugin application. +-c constraints.txt + +Django +djangorestframework diff --git a/requirements/base.txt b/requirements/base.txt new file mode 100644 index 0000000..eda6084 --- /dev/null +++ b/requirements/base.txt @@ -0,0 +1,9 @@ +# +# This file is autogenerated by pip-compile +# To update, run: +# +# make upgrade +# +django==1.11.29 # via -c requirements/constraints.txt, -r requirements/base.in +djangorestframework==3.6.3 # via -c requirements/constraints.txt, -r requirements/base.in +pytz==2020.1 # via django diff --git a/requirements/constraints.txt b/requirements/constraints.txt new file mode 100644 index 0000000..ad6bf8b --- /dev/null +++ b/requirements/constraints.txt @@ -0,0 +1,23 @@ +# Version constraints for pip-installation. +# +# This file doesn't install any packages. It specifies version constraints +# that will be applied if a package is needed. +# +# When pinning something here, please provide an explanation of why. Ideally, +# link to other information that will help people in the future to remove the +# pin when possible. Writing an issue against the offending project and +# linking to it here is good. + +# Django version 2.0.0 dropped support for python 2.7 +Django<2.0 + +# Already in python3 standard library +futures; python_version == "2.7" + +# TODO: Add constraint explanation +pylint==1.9.3 +pycodestyle==2.5.0 + +# Keep same platform version +djangorestframework==3.6.3 +testfixtures==6.4.3 diff --git a/requirements/pip-tools.in b/requirements/pip-tools.in new file mode 100644 index 0000000..0fe4856 --- /dev/null +++ b/requirements/pip-tools.in @@ -0,0 +1,5 @@ +# Just the dependencies to run pip-tools, mainly for the "upgrade" make target +-c constraints.txt + +pip-tools # Contains pip-compile, used to generate pip requirements files + diff --git a/requirements/pip-tools.txt b/requirements/pip-tools.txt new file mode 100644 index 0000000..1ae2c19 --- /dev/null +++ b/requirements/pip-tools.txt @@ -0,0 +1,12 @@ +# +# This file is autogenerated by pip-compile +# To update, run: +# +# make upgrade +# +click==7.1.2 # via pip-tools +pip-tools==5.2.1 # via -r requirements/pip-tools.in +six==1.15.0 # via pip-tools + +# The following packages are considered to be unsafe in a requirements file: +# pip diff --git a/requirements/test.in b/requirements/test.in new file mode 100644 index 0000000..cc99309 --- /dev/null +++ b/requirements/test.in @@ -0,0 +1,8 @@ +-c constraints.txt +-r base.txt + +pycodestyle +pylint +coverage +mock +testfixtures diff --git a/requirements/test.txt b/requirements/test.txt new file mode 100644 index 0000000..38ed279 --- /dev/null +++ b/requirements/test.txt @@ -0,0 +1,26 @@ +# +# This file is autogenerated by pip-compile +# To update, run: +# +# make upgrade +# +astroid==1.6.6 # via pylint +backports.functools-lru-cache==1.6.1 # via astroid, isort, pylint +configparser==4.0.2 # via pylint +coverage==5.2 # via -r requirements/test.in +django==1.11.29 # via -c requirements/constraints.txt, -r requirements/base.txt +djangorestframework==3.6.3 # via -c requirements/constraints.txt, -r requirements/base.txt +enum34==1.1.10 # via astroid +funcsigs==1.0.2 # via mock +futures==3.3.0 ; python_version == "2.7" # via -c requirements/constraints.txt, isort +isort==4.3.21 # via pylint +lazy-object-proxy==1.5.0 # via astroid +mccabe==0.6.1 # via pylint +mock==3.0.5 # via -r requirements/test.in +pycodestyle==2.5.0 # via -c requirements/constraints.txt, -r requirements/test.in +pylint==1.9.3 # via -c requirements/constraints.txt, -r requirements/test.in +pytz==2020.1 # via -r requirements/base.txt, django +singledispatch==3.4.0.3 # via astroid, pylint +six==1.15.0 # via astroid, mock, pylint, singledispatch +testfixtures==6.4.3 # via -c requirements/constraints.txt, -r requirements/test.in +wrapt==1.12.1 # via astroid diff --git a/setup.cfg b/setup.cfg new file mode 100644 index 0000000..e983cca --- /dev/null +++ b/setup.cfg @@ -0,0 +1,292 @@ +[isort] +default_section = THIRDPARTY +known_first_party = eox_hooks +include_trailing_comma = True +indent = ' ' +line_length = 120 +multi_line_output = 3 + +[pycodestyle] +max-line-length = 120 +statistics = True + +[pylint] +ignore=CVS +[MESSAGES CONTROL] +enable = + line-too-long, + syntax-error, + init-is-generator, + return-in-init, + function-redefined, + not-in-loop, + return-outside-function, + yield-outside-function, + return-arg-in-generator, + nonexistent-operator, + duplicate-argument-name, + abstract-class-instantiated, + bad-reversed-sequence, + continue-in-finally, + method-hidden, + access-member-before-definition, + no-method-argument, + no-self-argument, + invalid-slots-object, + assigning-non-slot, + invalid-slots, + inherit-non-class, + inconsistent-mro, + duplicate-bases, + non-iterator-returned, + unexpected-special-method-signature, + invalid-length-returned, + import-error, + used-before-assignment, + undefined-variable, + undefined-all-variable, + invalid-all-object, + no-name-in-module, + unbalance-tuple-unpacking, + unpacking-non-sequence, + bad-except-order, + raising-bad-type, + misplaced-bare-raise, + raising-non-exception, + nonimplemented-raised, + catching-non-exception, + slots-on-old-class, + super-on-old-class, + bad-super-call, + missing-super-argument, + no-member, + not-callable, + assignment-from-no-return, + no-value-for-parameter, + too-many-function-args, + unexpected-keyword-arg, + redundant-keyword-arg, + invalid-sequence-index, + invalid-slice-index, + assignment-from-none, + not-context-manager, + invalid-unary-operand-type, + unsupported-binary-operation, + repeated-keyword, + not-an-iterable, + not-a-mapping, + unsupported-membership-test, + unsubscriptable-object, + logging-unsupported-format, + logging-too-many-args, + logging-too-few-args, + bad-format-character, + truncated-format-string, + mixed-fomat-string, + format-needs-mapping, + missing-format-string-key, + too-many-format-args, + too-few-format-args, + bad-str-strip-call, + model-unicode-not-callable, + super-method-not-called, + non-parent-method-called, + test-inherits-tests, + translation-of-non-string, + redefined-variable-type, + cyclical-import, + unreachable, + dangerous-default-value, + pointless-statement, + pointless-string-statement, + expression-not-assigned, + duplicate-key, + confusing-with-statement, + using-constant-test, + lost-exception, + assert-on-tuple, + attribute-defined-outside-init, + bad-staticmethod-argument, + arguments-differ, + signature-differs, + abstract-method, + super-init-not-called, + relative-import, + import-self, + misplaced-future, + invalid-encoded-data, + global-variable-undefined, + redefined-outer-name, + redefined-builtin, + redefined-in-handler, + undefined-loop-variable, + cell-var-from-loop, + duplicate-except, + nonstandard-exception, + binary-op-exception, + property-on-old-class, + bad-format-string-key, + unused-format-string-key, + bad-format-string, + missing-format-argument-key, + unused-format-string-argument, + format-combined-specification, + missing-format-attribute, + invalid-format-index, + anomalous-backslash-in-string, + anomalous-unicode-escape-in-string, + bad-open-mode, + boolean-datetime, + fatal, + astroid-error, + parse-error, + method-check-failed, + django-not-available, + raw-checker-failed, + django-not-available-placeholder, + empty-docstring, + invalid-characters-in-docstring, + missing-docstring, + wrong-spelling-in-comment, + wrong-spelling-in-docstring, + unused-import, + unused-variable, + unused-argument, + exec-used, + eval-used, + bad-classmethod-argument, + bad-mcs-classmethod-argument, + bad-mcs-method-argument, + bad-whitespace, + consider-iterating-dictionary, + consider-using-enumerate, + literal-used-as-attribute, + multiple-imports, + multiple-statements, + old-style-class, + simplifiable-range, + singleton-comparison, + superfluous-parens, + unidiomatic-typecheck, + unneeded-not, + wrong-assert-type, + simplifiable-if-statement, + no-classmethod-decorator, + no-staticmethod-decorator, + unnecessary-pass, + unnecessary-lambda, + useless-else-on-loop, + unnecessary-semicolon, + reimported, + global-variable-not-assigned, + global-at-module-level, + bare-except, + broad-except, + logging-not-lazy, + redundant-unittest-assert, + model-missing-unicode, + model-has-unicode, + model-no-explicit-unicode, + protected-access, + deprecated-module, + deprecated-method, + too-many-nested-blocks, + too-many-statements, + too-many-boolean-expressions, + ungrouped-imports, + wrong-import-order, + wrong-import-position, + wildcard-import, + missing-final-newline, + mixed-line-endings, + trailing-newlines, + trailing-whitespace, + unexpected-line-ending-format, + mixed-indentation, + bad-option-value, + unrecognized-inline-option, + useless-suppression, + bad-inline-option, + deprecated-pragma, +disable = + bad-continuation, + invalid-name, + misplaced-comparison-constant, + file-ignored, + bad-indentation, + lowercase-l-suffix, + unused-wildcard-import, + global-statement, + no-else-return, + apply-builtin, + backtick, + basestring-builtin, + buffer-builtin, + cmp-builtin, + cmp-method, + coerce-builtin, + coerce-method, + delslice-method, + dict-iter-method, + dict-view-method, + duplicate-code, + execfile-builtin, + file-builtin, + filter-builtin-not-iterating, + fixme, + getslice-method, + hex-method, + import-star-module-level, + indexing-exception, + input-builtin, + intern-builtin, + locally-disabled, + locally-enabled, + logging-format-interpolation, + long-builtin, + long-suffix, + map-builtin-not-iterating, + metaclass-assignment, + next-method-called, + no-absolute-import, + no-init, + no-self-use, + nonzero-method, + oct-method, + old-division, + old-ne-operator, + old-octal-literal, + old-raise-syntax, + parameter-unpacking, + print-statement, + raising-string, + range-builtin-not-iterating, + raw_input-builtin, + reduce-builtin, + reload-builtin, + round-builtin, + setslice-method, + standarderror-builtin, + suppressed-message, + too-few-public-methods, + too-many-ancestors, + too-many-arguments, + too-many-branches, + too-many-instance-attributes, + too-many-lines, + too-many-locals, + too-many-public-methods, + too-many-return-statements, + unichr-builtin, + unicode-builtin, + unpacking-in-except, + using-cmp-argument, + xrange-builtin, + zip-builtin-not-iterating, + +[bumpversion] +current_version = 0.0.0 +commit = True +tag = True + +[bumpversion:file:eox_hooks/__init__.py] diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..e591088 --- /dev/null +++ b/setup.py @@ -0,0 +1,98 @@ +""" +Setup file for eox_hooks Django plugin. +""" + +from __future__ import print_function + +import os +import re + +from setuptools import setup + + +def load_requirements(*requirements_paths): + """ + Load all requirements from the specified requirements files. + Returns a list of requirement strings. + """ + requirements = set() + for path in requirements_paths: + requirements.update( + line.split('#')[0].strip() for line in open(path).readlines() + if is_requirement(line) + ) + return list(requirements) + + +def is_requirement(line): + """ + Return True if the requirement line is a package requirement; + that is, it is not blank, a comment, or editable. + """ + # Remove whitespace at the start/end of the line + line = line.strip() + + # Skip blank lines, comments, and editable installs + return not ( + line == '' or + line.startswith('-r') or + line.startswith('#') or + line.startswith('-e') or + line.startswith('git+') or + line.startswith('-c') + ) + + +def get_version(*file_paths): + """ + Extract the version string from the file at the given relative path fragments. + """ + filename = os.path.join(os.path.dirname(__file__), *file_paths) + version_file = open(filename).read() + version_match = re.search(r"^__version__ = ['\"]([^'\"]*)['\"]", + version_file, re.M) + if version_match: + return version_match.group(1) + raise RuntimeError('Unable to find version string.') + + +with open("README.rst", "r") as fh: + README = fh.read() + +VERSION = get_version('eox_hooks', '__init__.py') + + +setup( + name='eox-hooks', + version=VERSION, + author='eduNEXT', + author_email='contact@edunext.co', + description='EoxHooks', + license='AGPL', + long_description=README, + long_description_content_type='text/x-rst', + classifiers=[ + 'Development Status :: 5 - Production/Stable', + 'Framework :: Django :: 1.11', + 'Framework :: Django :: 2.2', + 'Intended Audience :: Developers', + 'License :: OSI Approved :: GNU Affero General Public License v3', + 'Operating System :: OS Independent', + 'Programming Language :: Python :: 2', + 'Programming Language :: Python :: 2.7', + 'Programming Language :: Python :: 3', + 'Programming Language :: Python :: 3.5', + 'Programming Language :: Python :: 3.8', + ], + packages=[ + 'eox_hooks', + ], + include_package_data=True, + install_requires=load_requirements('requirements/base.in'), + zip_safe=False, + entry_points={ + "lms.djangoapp": [ + 'eox_hooks = eox_hooks.apps:EoxHooksConfig', + ], + }, +)