diff --git a/.annotation_safe_list.yml b/.annotation_safe_list.yml
new file mode 100644
index 0000000..45c6a18
--- /dev/null
+++ b/.annotation_safe_list.yml
@@ -0,0 +1,37 @@
+# This is a Code Annotations automatically-generated Django model safelist file.
+# These models must be annotated as follows in order to be counted in the coverage report.
+# See https://code-annotations.readthedocs.io/en/latest/safelist.html for more information.
+#
+# fake_app_1.FakeModelName:
+# ".. no_pii:": "This model has no PII"
+# fake_app_2.FakeModel2:
+# ".. choice_annotation:": foo, bar, baz
+
+admin.LogEntry:
+ ".. no_pii:": "This model has no PII"
+auth.Group:
+ ".. no_pii:": "This model has no PII"
+auth.Permission:
+ ".. no_pii:": "This model has no PII"
+contenttypes.ContentType:
+ ".. no_pii:": "This model has no PII"
+sessions.Session:
+ ".. no_pii:": "This model has no PII"
+social_django.Association:
+ ".. no_pii:": "This model has no PII"
+social_django.Code:
+ ".. pii:": "Email address"
+ ".. pii_types:": other
+ ".. pii_retirement:": local_api
+social_django.Nonce:
+ ".. no_pii:": "This model has no PII"
+social_django.Partial:
+ ".. no_pii:": "This model has no PII"
+social_django.UserSocialAuth:
+ ".. no_pii:": "This model has no PII"
+waffle.Flag:
+ ".. no_pii:": "This model has no PII"
+waffle.Sample:
+ ".. no_pii:": "This model has no PII"
+waffle.Switch:
+ ".. no_pii:": "This model has no PII"
diff --git a/.coveragerc b/.coveragerc
new file mode 100644
index 0000000..3bb5ee3
--- /dev/null
+++ b/.coveragerc
@@ -0,0 +1,10 @@
+[run]
+branch = True
+data_file = .coverage
+source=edx_course_team_api
+omit =
+ test_settings
+ *migrations*
+ *admin.py
+ *static*
+ *templates*
diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 0000000..94d3e71
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,94 @@
+# ***************************
+# ** DO NOT EDIT THIS FILE **
+# ***************************
+#
+# This file was generated by edx-lint: http://github.com/edx/edx-lint
+#
+# If you want to change this file, you have two choices, depending on whether
+# you want to make a local change that applies only to this repo, or whether
+# you want to make a central change that applies to all repos using edx-lint.
+#
+# LOCAL CHANGE:
+#
+# 1. Edit the local .editorconfig_tweaks file to add changes just to this
+# repo's file.
+#
+# 2. Run:
+#
+# $ edx_lint write .editorconfig
+#
+# 3. This will modify the local file. Submit a pull request to get it
+# checked in so that others will benefit.
+#
+#
+# CENTRAL CHANGE:
+#
+# 1. Edit the .editorconfig file in the edx-lint repo at
+# https://github.com/edx/edx-lint/blob/master/edx_lint/files/.editorconfig
+#
+# 2. install the updated version of edx-lint (in edx-lint):
+#
+# $ pip install .
+#
+# 3. Run (in edx-lint):
+#
+# # uses .editorconfig_tweaks from edx-lint for linting in edx-lint
+# # NOTE: Use Python 3.x, which no longer includes comments in the output file
+# $ edx_lint write .editorconfig
+#
+# 4. Make a new version of edx_lint, submit and review a pull request with the
+# .editorconfig update, and after merging, update the edx-lint version by
+# creating a new tag in the repo (uses pbr).
+#
+# 5. In your local repo, install the newer version of edx-lint.
+#
+# 6. Run:
+#
+# # uses local .editorconfig_tweaks
+# $ edx_lint write .editorconfig
+#
+# 7. This will modify the local file. Submit a pull request to get it
+# checked in so that others will benefit.
+#
+#
+#
+#
+#
+# STAY AWAY FROM THIS FILE!
+#
+#
+#
+#
+#
+# SERIOUSLY.
+#
+# ------------------------------
+[*]
+end_of_line = lf
+insert_final_newline = true
+charset = utf-8
+indent_style = space
+indent_size = 4
+max_line_length = 120
+trim_trailing_whitespace = true
+
+[{Makefile, *.mk}]
+indent_style = tab
+indent_size = 8
+
+[*.{yml,yaml,json}]
+indent_size = 2
+
+[*.js]
+indent_size = 2
+
+[*.diff]
+trim_trailing_whitespace = false
+
+[.git/*]
+trim_trailing_whitespace = false
+
+[*.rst]
+max_line_length = 79
+
+# 01d24285b953f74272f86b1e42a0235315085e59
diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md
new file mode 100644
index 0000000..c1cad6b
--- /dev/null
+++ b/.github/PULL_REQUEST_TEMPLATE.md
@@ -0,0 +1,39 @@
+**Description:** Describe in a couple of sentences what this PR adds
+
+**JIRA:** Link to JIRA ticket
+
+**Dependencies:** dependencies on other outstanding PRs, issues, etc.
+
+**Merge deadline:** List merge deadline (if any)
+
+**Installation instructions:** List any non-trivial installation
+instructions.
+
+**Testing instructions:**
+
+1. Open page A
+2. Do thing B
+3. Expect C to happen
+4. If D happened instead - check failed.
+
+**Reviewers:**
+- [ ] tag reviewer
+- [ ] tag reviewer
+
+**Merge checklist:**
+- [ ] All reviewers approved
+- [ ] CI build is green
+- [ ] Version bumped
+- [ ] Changelog record added
+- [ ] Documentation updated (not only docstrings)
+- [ ] Commits are squashed
+
+**Post merge:**
+- [ ] Create a tag
+- [ ] Check new version is pushed to PyPI after tag-triggered build is
+ finished.
+- [ ] Delete working branch (if not needed anymore)
+
+**Author concerns:** List any concerns about this PR - inelegant
+solutions, hacks, quick-and-dirty implementations, concerns about
+migrations, etc.
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..bb576d5
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,61 @@
+*.py[cod]
+__pycache__
+.pytest_cache
+
+# C extensions
+*.so
+
+# Packages
+*.egg
+*.egg-info
+/dist
+/build
+/eggs
+/parts
+/bin
+/var
+/sdist
+/develop-eggs
+/.installed.cfg
+/lib
+/lib64
+
+# Installer logs
+pip-log.txt
+
+# Unit test / coverage reports
+.cache/
+.pytest_cache/
+.coverage
+.coverage.*
+.tox
+coverage.xml
+htmlcov/
+
+
+
+# The Silver Searcher
+.agignore
+
+# OS X artifacts
+*.DS_Store
+
+# Logging
+log/
+logs/
+chromedriver.log
+ghostdriver.log
+
+# Complexity
+output/*.html
+output/*/index.html
+
+# Sphinx
+docs/_build
+docs/modules.rst
+docs/edx_course_team_api.rst
+docs/edx_course_team_api.*.rst
+
+# Private requirements
+requirements/private.in
+requirements/private.txt
diff --git a/.pii_annotations.yml b/.pii_annotations.yml
new file mode 100644
index 0000000..2b710e0
--- /dev/null
+++ b/.pii_annotations.yml
@@ -0,0 +1,35 @@
+source_path: ./
+report_path: pii_report
+safelist_path: .annotation_safe_list.yml
+coverage_target: 100.0
+annotations:
+ ".. no_pii:":
+ "pii_group":
+ - ".. pii:":
+ - ".. pii_types:":
+ choices:
+ - id # Unique identifier for the user which is shared across systems
+ - name # Used for any part of the user’s name
+ - username
+ - password
+ - location # Used for any part of any type address or country stored
+ - phone_number # Used for phone or fax numbers
+ - email_address
+ - birth_date # Used for any part of a stored birth date
+ - ip # IP address
+ - external_service # Used for external service ids or links such as social media links or usernames, website links, etc.
+ - biography # Any type of free-form biography field
+ - gender
+ - sex
+ - image
+ - video
+ - other
+ - ".. pii_retirement:":
+ choices:
+ - retained # Intentionally kept for legal reasons
+ - local_api # An API exists in this repository for retiring this information
+ - consumer_api # The data's consumer must implement an API for retiring this information
+ - third_party # A third party API exists to retire this data
+extensions:
+ python:
+ - py
diff --git a/.readthedocs.yml b/.readthedocs.yml
new file mode 100644
index 0000000..ef133c3
--- /dev/null
+++ b/.readthedocs.yml
@@ -0,0 +1,15 @@
+# .readthedocs.yml
+# Read the Docs configuration file
+# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details
+
+# Required
+version: 2
+
+# Build documentation in the docs/ directory with Sphinx
+sphinx:
+ configuration: docs/conf.py
+
+python:
+ version: 3.5
+ install:
+ - requirements: requirements/docs.txt
\ No newline at end of file
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 0000000..e648648
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,50 @@
+# Config file for automatic testing at travis-ci.org
+# To connect a new edX repository to Travis, see
+# https://openedx.atlassian.net/wiki/spaces/EdxOps/pages/157464369/How+to+connect+a+repository+to+Travis+CI
+#
+# Be sure to change the Travis settings in the web UI to enable both
+# "Build pushed branches" and "Build pushed pull requests".
+
+language: python
+
+python:
+ - 3.5
+ - 3.8
+
+env:
+ - TOXENV=django22
+ - TOXENV=django30
+
+matrix:
+ include:
+ - python: 3.5
+ env: TOXENV=quality
+ - python: 3.5
+ env: TOXENV=docs
+ - python: 3.5
+ env: TOXENV=pii_check
+
+cache:
+ - pip
+
+before_install:
+ - pip install --upgrade pip
+
+install:
+ - pip install -r requirements/travis.txt
+
+script:
+ - tox
+
+after_success:
+ - codecov
+
+# Set token via "travis encrypt --add deploy.password"; for details, see
+# https://docs.travis-ci.com/user/deployment/pypi
+deploy:
+ provider: pypi
+ user: "__token__"
+ distributions: sdist bdist_wheel
+ on:
+ tags: true
+ condition: '$TOXENV = quality'
diff --git a/CHANGELOG.rst b/CHANGELOG.rst
new file mode 100644
index 0000000..113079a
--- /dev/null
+++ b/CHANGELOG.rst
@@ -0,0 +1,25 @@
+Change Log
+----------
+
+..
+ All enhancements and patches to edx_course_team_api will be documented
+ in this file. It adheres to the structure of https://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 (https://semver.org/).
+
+.. There should always be an "Unreleased" section for changes pending release.
+
+Unreleased
+~~~~~~~~~~
+
+*
+
+[0.1.0] - 2020-10-26
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Added
+_____
+
+* First release on PyPI.
diff --git a/LICENSE.txt b/LICENSE.txt
new file mode 100644
index 0000000..7bc605e
--- /dev/null
+++ b/LICENSE.txt
@@ -0,0 +1,674 @@
+
+ GNU AFFERO GENERAL PUBLIC LICENSE
+ Version 3, 19 November 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc.
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The GNU Affero General Public License is a free, copyleft license for
+software and other kinds of works, specifically designed to ensure
+cooperation with the community in the case of network server software.
+
+ The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works. By contrast,
+our General Public Licenses are intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+ Developers that use our General Public Licenses protect your rights
+with two steps: (1) assert copyright on the software, and (2) offer
+you this License which gives you legal permission to copy, distribute
+and/or modify the software.
+
+ A secondary benefit of defending all users' freedom is that
+improvements made in alternate versions of the program, if they
+receive widespread use, become available for other developers to
+incorporate. Many developers of free software are heartened and
+encouraged by the resulting cooperation. However, in the case of
+software used on network servers, this result may fail to come about.
+The GNU General Public License permits making a modified version and
+letting the public access it on a server without ever releasing its
+source code to the public.
+
+ The GNU Affero General Public License is designed specifically to
+ensure that, in such cases, the modified source code becomes available
+to the community. It requires the operator of a network server to
+provide the source code of the modified version running there to the
+users of that server. Therefore, public use of a modified version, on
+a publicly accessible server, gives the public access to the source
+code of the modified version.
+
+ An older license, called the Affero General Public License and
+published by Affero, was designed to accomplish similar goals. This is
+a different license, not a version of the Affero GPL, but Affero has
+released a new version of the Affero GPL which permits relicensing under
+this license.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ TERMS AND CONDITIONS
+
+ 0. Definitions.
+
+ "This License" refers to version 3 of the GNU Affero General Public License.
+
+ "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+ "The Program" refers to any copyrightable work licensed under this
+License. Each licensee is addressed as "you". "Licensees" and
+"recipients" may be individuals or organizations.
+
+ To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy. The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+ A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+ To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy. Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+ To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies. Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+ An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License. If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+ 1. Source Code.
+
+ The "source code" for a work means the preferred form of the work
+for making modifications to it. "Object code" means any non-source
+form of a work.
+
+ A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+ The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form. A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+ The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities. However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work. For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+ The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+ The Corresponding Source for a work in source code form is that
+same work.
+
+ 2. Basic Permissions.
+
+ All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met. This License explicitly affirms your unlimited
+permission to run the unmodified Program. The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work. This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+ You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force. You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright. Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+ Conveying under any other circumstances is permitted solely under
+the conditions stated below. Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+ 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+ No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+ When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+ 4. Conveying Verbatim Copies.
+
+ You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+ You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+ 5. Conveying Modified Source Versions.
+
+ You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+ a) The work must carry prominent notices stating that you modified
+ it, and giving a relevant date.
+
+ b) The work must carry prominent notices stating that it is
+ released under this License and any conditions added under section
+ 7. This requirement modifies the requirement in section 4 to
+ "keep intact all notices".
+
+ c) You must license the entire work, as a whole, under this
+ License to anyone who comes into possession of a copy. This
+ License will therefore apply, along with any applicable section 7
+ additional terms, to the whole of the work, and all its parts,
+ regardless of how they are packaged. This License gives no
+ permission to license the work in any other way, but it does not
+ invalidate such permission if you have separately received it.
+
+ d) If the work has interactive user interfaces, each must display
+ Appropriate Legal Notices; however, if the Program has interactive
+ interfaces that do not display Appropriate Legal Notices, your
+ work need not make them do so.
+
+ A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit. Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+ 6. Conveying Non-Source Forms.
+
+ You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+ a) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by the
+ Corresponding Source fixed on a durable physical medium
+ customarily used for software interchange.
+
+ b) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by a
+ written offer, valid for at least three years and valid for as
+ long as you offer spare parts or customer support for that product
+ model, to give anyone who possesses the object code either (1) a
+ copy of the Corresponding Source for all the software in the
+ product that is covered by this License, on a durable physical
+ medium customarily used for software interchange, for a price no
+ more than your reasonable cost of physically performing this
+ conveying of source, or (2) access to copy the
+ Corresponding Source from a network server at no charge.
+
+ c) Convey individual copies of the object code with a copy of the
+ written offer to provide the Corresponding Source. This
+ alternative is allowed only occasionally and noncommercially, and
+ only if you received the object code with such an offer, in accord
+ with subsection 6b.
+
+ d) Convey the object code by offering access from a designated
+ place (gratis or for a charge), and offer equivalent access to the
+ Corresponding Source in the same way through the same place at no
+ further charge. You need not require recipients to copy the
+ Corresponding Source along with the object code. If the place to
+ copy the object code is a network server, the Corresponding Source
+ may be on a different server (operated by you or a third party)
+ that supports equivalent copying facilities, provided you maintain
+ clear directions next to the object code saying where to find the
+ Corresponding Source. Regardless of what server hosts the
+ Corresponding Source, you remain obligated to ensure that it is
+ available for as long as needed to satisfy these requirements.
+
+ e) Convey the object code using peer-to-peer transmission, provided
+ you inform other peers where the object code and Corresponding
+ Source of the work are being offered to the general public at no
+ charge under subsection 6d.
+
+ A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+ A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling. In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage. For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product. A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+ "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source. The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+ If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information. But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+ The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed. Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+ Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+ 7. Additional Terms.
+
+ "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law. If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+ When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it. (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.) You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+ Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+ a) Disclaiming warranty or limiting liability differently from the
+ terms of sections 15 and 16 of this License; or
+
+ b) Requiring preservation of specified reasonable legal notices or
+ author attributions in that material or in the Appropriate Legal
+ Notices displayed by works containing it; or
+
+ c) Prohibiting misrepresentation of the origin of that material, or
+ requiring that modified versions of such material be marked in
+ reasonable ways as different from the original version; or
+
+ d) Limiting the use for publicity purposes of names of licensors or
+ authors of the material; or
+
+ e) Declining to grant rights under trademark law for use of some
+ trade names, trademarks, or service marks; or
+
+ f) Requiring indemnification of licensors and authors of that
+ material by anyone who conveys the material (or modified versions of
+ it) with contractual assumptions of liability to the recipient, for
+ any liability that these contractual assumptions directly impose on
+ those licensors and authors.
+
+ All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10. If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term. If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+ If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+ Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+ 8. Termination.
+
+ You may not propagate or modify a covered work except as expressly
+provided under this License. Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+ However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+ Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+ Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License. If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+ 9. Acceptance Not Required for Having Copies.
+
+ You are not required to accept this License in order to receive or
+run a copy of the Program. Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance. However,
+nothing other than this License grants you permission to propagate or
+modify any covered work. These actions infringe copyright if you do
+not accept this License. Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+ 10. Automatic Licensing of Downstream Recipients.
+
+ Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License. You are not responsible
+for enforcing compliance by third parties with this License.
+
+ An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations. If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+ You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License. For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+ 11. Patents.
+
+ A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based. The
+work thus licensed is called the contributor's "contributor version".
+
+ A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version. For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+ Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+ In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement). To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+ If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients. "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+ If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+ A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License. You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+ Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+ 12. No Surrender of Others' Freedom.
+
+ If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all. For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+ 13. Remote Network Interaction; Use with the GNU General Public License.
+
+ Notwithstanding any other provision of this License, if you modify the
+Program, your modified version must prominently offer all users
+interacting with it remotely through a computer network (if your version
+supports such interaction) an opportunity to receive the Corresponding
+Source of your version by providing access to the Corresponding Source
+from a network server at no charge, through some standard or customary
+means of facilitating copying of software. This Corresponding Source
+shall include the Corresponding Source for any work covered by version 3
+of the GNU General Public License that is incorporated pursuant to the
+following paragraph.
+
+ Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU General Public License into a single
+combined work, and to convey the resulting work. The terms of this
+License will continue to apply to the part which is the covered work,
+but the work with which it is combined will remain governed by version
+3 of the GNU General Public License.
+
+ 14. Revised Versions of this License.
+
+ The Free Software Foundation may publish revised and/or new versions of
+the GNU Affero General Public License from time to time. Such new versions
+will be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the
+Program specifies that a certain numbered version of the GNU Affero General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation. If the Program does not specify a version number of the
+GNU Affero General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+ If the Program specifies that a proxy can decide which future
+versions of the GNU Affero General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+ Later license versions may give you additional or different
+permissions. However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+ 15. Disclaimer of Warranty.
+
+ THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. Limitation of Liability.
+
+ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+ 17. Interpretation of Sections 15 and 16.
+
+ If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+
+ Copyright (C)
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see .
+
+Also add information on how to contact you by electronic and paper mail.
+
+ If your software can interact with users remotely through a computer
+network, you should also make sure that it provides a way for users to
+get its source. For example, if your program is a web application, its
+interface could display a "Source" link that leads users to an archive
+of the code. There are many ways you could offer source, and different
+solutions will be better for different programs; see section 13 for the
+specific requirements.
+
+ You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU AGPL, see
+.
+
+EdX Inc. wishes to state, in clarification of the above license terms, that
+any public, independently available web service offered over the network and
+communicating with edX's copyrighted works by any form of inter-service
+communication, including but not limited to Remote Procedure Call (RPC)
+interfaces, is not a work based on our copyrighted work within the meaning
+of the license. "Corresponding Source" of this work, or works based on this
+work, as defined by the terms of this license do not include source code
+files for programs used solely to provide those public, independently
+available web services.
+
+
diff --git a/MANIFEST.in b/MANIFEST.in
new file mode 100644
index 0000000..31ea9ea
--- /dev/null
+++ b/MANIFEST.in
@@ -0,0 +1,5 @@
+include CHANGELOG.rst
+include LICENSE.txt
+include README.rst
+include requirements/base.in
+recursive-include edx_course_team_api *.html *.png *.gif *.js *.css *.jpg *.jpeg *.svg *.py
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..0b24c09
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,98 @@
+.PHONY: clean compile_translations coverage diff_cover docs dummy_translations \
+ extract_translations fake_translations help pii_check pull_translations push_translations \
+ quality requirements selfcheck test test-all upgrade validate
+
+.DEFAULT_GOAL := help
+
+# For opening files in a browser. Use like: $(BROWSER)relative/path/to/file.html
+BROWSER := python -m webbrowser file://$(CURDIR)/
+
+help: ## display this help message
+ @echo "Please use \`make ' where is one of"
+ @awk -F ':.*?## ' '/^[a-zA-Z]/ && NF==2 {printf "\033[36m %-25s\033[0m %s\n", $$1, $$2}' $(MAKEFILE_LIST) | sort
+
+clean: ## remove generated byte code, coverage reports, and build artifacts
+ find . -name '__pycache__' -exec rm -rf {} +
+ find . -name '*.pyc' -exec rm -f {} +
+ find . -name '*.pyo' -exec rm -f {} +
+ find . -name '*~' -exec rm -f {} +
+ coverage erase
+ rm -fr build/
+ rm -fr dist/
+ rm -fr *.egg-info
+
+coverage: clean ## generate and view HTML coverage report
+ pytest --cov-report html
+ $(BROWSER)htmlcov/index.html
+
+docs: ## generate Sphinx HTML documentation, including API docs
+ tox -e docs
+ $(BROWSER)docs/_build/html/index.html
+
+# Define PIP_COMPILE_OPTS=-v to get more information during make upgrade.
+PIP_COMPILE = pip-compile --rebuild --upgrade $(PIP_COMPILE_OPTS)
+
+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
+ $(PIP_COMPILE) -o requirements/doc.txt requirements/doc.in
+ $(PIP_COMPILE) -o requirements/quality.txt requirements/quality.in
+ $(PIP_COMPILE) -o requirements/travis.txt requirements/travis.in
+ $(PIP_COMPILE) -o requirements/dev.txt requirements/dev.in
+ # Let tox control the Django version for tests
+ sed '/^[dD]jango==/d' requirements/test.txt > requirements/test.tmp
+ mv requirements/test.tmp requirements/test.txt
+
+quality: ## check coding style with pycodestyle and pylint
+ tox -e quality
+
+pii_check: ## check for PII annotations on all Django models
+ tox -e pii_check
+
+requirements: ## install development environment requirements
+ pip install -qr requirements/pip-tools.txt
+ pip-sync requirements/dev.txt requirements/private.*
+
+test: clean ## run tests in the current virtualenv
+ pytest
+
+diff_cover: test ## find diff lines that need test coverage
+ diff-cover coverage.xml
+
+test-all: quality pii_check ## run tests on every supported Python/Django combination
+ tox
+
+validate: quality pii_check test ## run tests and quality checks
+
+selfcheck: ## check that the Makefile is well-formed
+ @echo "The Makefile is well-formed."
+
+## Localization targets
+
+extract_translations: ## extract strings to be translated, outputting .mo files
+ rm -rf docs/_build
+ cd edx_course_team_api && ../manage.py makemessages -l en -v1 -d django
+ cd edx_course_team_api && ../manage.py makemessages -l en -v1 -d djangojs
+
+compile_translations: ## compile translation files, outputting .po files for each supported language
+ cd edx_course_team_api && ../manage.py compilemessages
+
+detect_changed_source_translations:
+ cd edx_course_team_api && i18n_tool changed
+
+pull_translations: ## pull translations from Transifex
+ tx pull -af --mode reviewed
+
+push_translations: ## push source translation files (.po) from Transifex
+ tx push -s
+
+dummy_translations: ## generate dummy translation (.po) files
+ cd edx_course_team_api && i18n_tool dummy
+
+build_dummy_translations: extract_translations dummy_translations compile_translations ## generate and compile dummy translation files
+
+validate_translations: build_dummy_translations detect_changed_source_translations ## validate translations
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..1b9d11c
--- /dev/null
+++ b/README.md
@@ -0,0 +1,35 @@
+# edx_course_team_api
+
+Exposes API to add course team member in Studio.
+
+## Usage
+
+### Base Url
+
+`${STUDIO_URL}/sn-api`
+
+### Add course team member
+
+`POST /course-team//modify_access`
+
+The JSON body requires two parameters.
+
+- `email`: An existed user.
+- `role`: One of `staff`, `instructor`.
+
+Sample request
+
+```json
+{
+ "email": "instructor@example.com",
+ "role": "instructor"
+}
+```
+
+Sample response
+
+```json
+{
+ "message": "User is added to course-v1:IBMDeveloperSkillsNetwork+CB0103EN+v1333."
+}
+```
diff --git a/codecov.yml b/codecov.yml
new file mode 100644
index 0000000..4da4768
--- /dev/null
+++ b/codecov.yml
@@ -0,0 +1,12 @@
+coverage:
+ status:
+ project:
+ default:
+ enabled: yes
+ target: auto
+ patch:
+ default:
+ enabled: yes
+ target: 100%
+
+comment: false
diff --git a/docs/Makefile b/docs/Makefile
new file mode 100644
index 0000000..e939d81
--- /dev/null
+++ b/docs/Makefile
@@ -0,0 +1,230 @@
+# Makefile for Sphinx documentation
+#
+
+# You can set these variables from the command line.
+SPHINXOPTS =
+SPHINXBUILD = sphinx-build
+PAPER =
+BUILDDIR = _build
+
+# User-friendly check for sphinx-build
+ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1)
+$(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 https://sphinx-doc.org/)
+endif
+
+# Internal variables.
+PAPEROPT_a4 = -D latex_paper_size=a4
+PAPEROPT_letter = -D latex_paper_size=letter
+ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
+# the i18n builder cannot share the environment and doctrees with the others
+I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
+
+.PHONY: help
+help:
+ @echo "Please use \`make ' where is one of"
+ @echo " html to make standalone HTML files"
+ @echo " dirhtml to make HTML files named index.html in directories"
+ @echo " singlehtml to make a single large HTML file"
+ @echo " pickle to make pickle files"
+ @echo " json to make JSON files"
+ @echo " htmlhelp to make HTML files and a HTML help project"
+ @echo " qthelp to make HTML files and a qthelp project"
+ @echo " applehelp to make an Apple Help Book"
+ @echo " devhelp to make HTML files and a Devhelp project"
+ @echo " epub to make an epub"
+ @echo " epub3 to make an epub3"
+ @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
+ @echo " latexpdf to make LaTeX files and run them through pdflatex"
+ @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx"
+ @echo " text to make text files"
+ @echo " man to make manual pages"
+ @echo " texinfo to make Texinfo files"
+ @echo " info to make Texinfo files and run them through makeinfo"
+ @echo " gettext to make PO message catalogs"
+ @echo " changes to make an overview of all changed/added/deprecated items"
+ @echo " xml to make Docutils-native XML files"
+ @echo " pseudoxml to make pseudoxml-XML files for display purposes"
+ @echo " linkcheck to check all external links for integrity"
+ @echo " doctest to run all doctests embedded in the documentation (if enabled)"
+ @echo " coverage to run coverage check of the documentation (if enabled)"
+ @echo " dummy to check syntax errors of document sources"
+
+.PHONY: clean
+clean:
+ rm -rf $(BUILDDIR)/*
+
+.PHONY: html
+html:
+ $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
+ @echo
+ @echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
+
+.PHONY: dirhtml
+dirhtml:
+ $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
+ @echo
+ @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
+
+.PHONY: singlehtml
+singlehtml:
+ $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml
+ @echo
+ @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml."
+
+.PHONY: pickle
+pickle:
+ $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
+ @echo
+ @echo "Build finished; now you can process the pickle files."
+
+.PHONY: json
+json:
+ $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
+ @echo
+ @echo "Build finished; now you can process the JSON files."
+
+.PHONY: htmlhelp
+htmlhelp:
+ $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
+ @echo
+ @echo "Build finished; now you can run HTML Help Workshop with the" \
+ ".hhp project file in $(BUILDDIR)/htmlhelp."
+
+.PHONY: qthelp
+qthelp:
+ $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
+ @echo
+ @echo "Build finished; now you can run "qcollectiongenerator" with the" \
+ ".qhcp project file in $(BUILDDIR)/qthelp, like this:"
+ @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/edx_course_team_api.qhcp"
+ @echo "To view the help file:"
+ @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/edx_course_team_api.qhc"
+
+.PHONY: applehelp
+applehelp:
+ $(SPHINXBUILD) -b applehelp $(ALLSPHINXOPTS) $(BUILDDIR)/applehelp
+ @echo
+ @echo "Build finished. The help book is in $(BUILDDIR)/applehelp."
+ @echo "N.B. You won't be able to view it unless you put it in" \
+ "~/Library/Documentation/Help or install it in your application" \
+ "bundle."
+
+.PHONY: devhelp
+devhelp:
+ $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp
+ @echo
+ @echo "Build finished."
+ @echo "To view the help file:"
+ @echo "# mkdir -p $$HOME/.local/share/devhelp/edx_course_team_api"
+ @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/edx_course_team_api"
+ @echo "# devhelp"
+
+.PHONY: epub
+epub:
+ $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub
+ @echo
+ @echo "Build finished. The epub file is in $(BUILDDIR)/epub."
+
+.PHONY: epub3
+epub3:
+ $(SPHINXBUILD) -b epub3 $(ALLSPHINXOPTS) $(BUILDDIR)/epub3
+ @echo
+ @echo "Build finished. The epub3 file is in $(BUILDDIR)/epub3."
+
+.PHONY: latex
+latex:
+ $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
+ @echo
+ @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
+ @echo "Run \`make' in that directory to run these through (pdf)latex" \
+ "(use \`make latexpdf' here to do that automatically)."
+
+.PHONY: latexpdf
+latexpdf:
+ $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
+ @echo "Running LaTeX files through pdflatex..."
+ $(MAKE) -C $(BUILDDIR)/latex all-pdf
+ @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
+
+.PHONY: latexpdfja
+latexpdfja:
+ $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
+ @echo "Running LaTeX files through platex and dvipdfmx..."
+ $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja
+ @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
+
+.PHONY: text
+text:
+ $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text
+ @echo
+ @echo "Build finished. The text files are in $(BUILDDIR)/text."
+
+.PHONY: man
+man:
+ $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man
+ @echo
+ @echo "Build finished. The manual pages are in $(BUILDDIR)/man."
+
+.PHONY: texinfo
+texinfo:
+ $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
+ @echo
+ @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo."
+ @echo "Run \`make' in that directory to run these through makeinfo" \
+ "(use \`make info' here to do that automatically)."
+
+.PHONY: info
+info:
+ $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
+ @echo "Running Texinfo files through makeinfo..."
+ make -C $(BUILDDIR)/texinfo info
+ @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo."
+
+.PHONY: gettext
+gettext:
+ $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale
+ @echo
+ @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale."
+
+.PHONY: changes
+changes:
+ $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
+ @echo
+ @echo "The overview file is in $(BUILDDIR)/changes."
+
+.PHONY: linkcheck
+linkcheck:
+ $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
+ @echo
+ @echo "Link check complete; look for any errors in the above output " \
+ "or in $(BUILDDIR)/linkcheck/output.txt."
+
+.PHONY: doctest
+doctest:
+ $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
+ @echo "Testing of doctests in the sources finished, look at the " \
+ "results in $(BUILDDIR)/doctest/output.txt."
+
+.PHONY: coverage
+coverage:
+ $(SPHINXBUILD) -b coverage $(ALLSPHINXOPTS) $(BUILDDIR)/coverage
+ @echo "Testing of coverage in the sources finished, look at the " \
+ "results in $(BUILDDIR)/coverage/python.txt."
+
+.PHONY: xml
+xml:
+ $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml
+ @echo
+ @echo "Build finished. The XML files are in $(BUILDDIR)/xml."
+
+.PHONY: pseudoxml
+pseudoxml:
+ $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml
+ @echo
+ @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml."
+
+.PHONY: dummy
+dummy:
+ $(SPHINXBUILD) -b dummy $(ALLSPHINXOPTS) $(BUILDDIR)/dummy
+ @echo
+ @echo "Build finished. Dummy builder generates no files."
diff --git a/docs/_static/theme_overrides.css b/docs/_static/theme_overrides.css
new file mode 100644
index 0000000..aad8245
--- /dev/null
+++ b/docs/_static/theme_overrides.css
@@ -0,0 +1,10 @@
+/* override table width restrictions */
+.wy-table-responsive table td, .wy-table-responsive table th {
+ /* !important prevents the common CSS stylesheets from
+ overriding this as on RTD they are loaded after this stylesheet */
+ white-space: normal !important;
+}
+
+.wy-table-responsive {
+ overflow: visible !important;
+}
diff --git a/docs/changelog.rst b/docs/changelog.rst
new file mode 100644
index 0000000..565b052
--- /dev/null
+++ b/docs/changelog.rst
@@ -0,0 +1 @@
+.. include:: ../CHANGELOG.rst
diff --git a/docs/conf.py b/docs/conf.py
new file mode 100644
index 0000000..3d03452
--- /dev/null
+++ b/docs/conf.py
@@ -0,0 +1,502 @@
+# pylint: disable=invalid-name
+"""
+edx_course_team_api documentation build configuration file.
+
+This file is execfile()d with the current directory set to its
+containing dir.
+
+Note that not all possible configuration values are present in this
+autogenerated file.
+
+All configuration values have a default; values that are commented out
+serve to show the default.
+"""
+
+import io
+import os
+import re
+import sys
+from subprocess import check_call
+
+import edx_theme
+from django import setup as django_setup
+from django.conf import settings
+
+
+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 = io.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.')
+
+
+REPO_ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
+sys.path.append(REPO_ROOT)
+
+VERSION = get_version('../edx_course_team_api', '__init__.py')
+
+# Configure Django for autodoc usage
+settings.configure()
+django_setup()
+
+# If extensions (or modules to document with autodoc) are in another directory,
+# add these directories to sys.path here. If the directory is relative to the
+# documentation root, use os.path.abspath to make it absolute, like shown here.
+#
+# import os
+# import sys
+# sys.path.insert(0, os.path.abspath('.'))
+
+# -- General configuration ------------------------------------------------
+
+# If your documentation needs a minimal Sphinx version, state it here.
+#
+# needs_sphinx = '1.0'
+
+# Add any Sphinx extension module names here, as strings. They can be
+# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
+# ones.
+extensions = [
+ 'edx_theme',
+ 'sphinx.ext.autodoc',
+ 'sphinx.ext.doctest',
+ 'sphinx.ext.intersphinx',
+ 'sphinx.ext.ifconfig',
+ 'sphinx.ext.napoleon'
+]
+
+# A list of warning types to suppress arbitrary warning messages.
+suppress_warnings = [
+ 'image.nonlocal_uri',
+]
+
+# Add any paths that contain templates here, relative to this directory.
+templates_path = ['_templates']
+
+# The suffix(es) of source filenames.
+# You can specify multiple suffix as a list of string:
+#
+# source_suffix = ['.rst', '.md']
+source_suffix = '.rst'
+
+# The encoding of source files.
+#
+# source_encoding = 'utf-8-sig'
+
+# The top level toctree document.
+top_level_doc = 'index'
+
+# General information about the project.
+project = 'edx_course_team_api'
+copyright = edx_theme.COPYRIGHT # pylint: disable=redefined-builtin
+author = edx_theme.AUTHOR
+project_title = 'edx_course_team_api'
+documentation_title = "{project_title}".format(project_title=project_title)
+
+# The version info for the project you're documenting, acts as replacement for
+# |version| and |release|, also used in various other places throughout the
+# built documents.
+#
+# The short X.Y version.
+version = VERSION
+# The full version, including alpha/beta/rc tags.
+release = VERSION
+
+# The language for content autogenerated by Sphinx. Refer to documentation
+# for a list of supported languages.
+#
+# This is also used if you do content translation via gettext catalogs.
+# Usually you set "language" from the command line for these cases.
+language = None
+
+# There are two options for replacing |today|: either, you set today to some
+# non-false value, then it is used:
+#
+# today = ''
+#
+# Else, today_fmt is used as the format for a strftime call.
+#
+# today_fmt = '%B %d, %Y'
+
+# List of patterns, relative to source directory, that match files and
+# directories to ignore when looking for source files.
+# This patterns also effect to html_static_path and html_extra_path
+exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
+
+# The reST default role (used for this markup: `text`) to use for all
+# documents.
+#
+# default_role = None
+
+# If true, '()' will be appended to :func: etc. cross-reference text.
+#
+# add_function_parentheses = True
+
+# If true, the current module name will be prepended to all description
+# unit titles (such as .. function::).
+#
+# add_module_names = True
+
+# If true, sectionauthor and moduleauthor directives will be shown in the
+# output. They are ignored by default.
+#
+# show_authors = False
+
+# The name of the Pygments (syntax highlighting) style to use.
+pygments_style = 'sphinx'
+
+# A list of ignored prefixes for module index sorting.
+# modindex_common_prefix = []
+
+# If true, keep warnings as "system message" paragraphs in the built documents.
+# keep_warnings = False
+
+# If true, `todo` and `todoList` produce output, else they produce nothing.
+todo_include_todos = False
+
+
+# -- Options for HTML output ----------------------------------------------
+
+# The theme to use for HTML and HTML Help pages. See the documentation for
+# a list of builtin themes.
+
+html_theme = 'edx_theme'
+
+# Theme options are theme-specific and customize the look and feel of a theme
+# further. For a list of options available for each theme, see the
+# documentation.
+#
+# html_theme_options = {}
+
+# Add any paths that contain custom themes here, relative to this directory.
+html_theme_path = [edx_theme.get_html_theme_path()]
+
+# The name for this set of Sphinx documents.
+# " v documentation" by default.
+#
+# html_title = 'edx_course_team_api v0.1.0'
+
+# A shorter title for the navigation bar. Default is the same as html_title.
+#
+# html_short_title = None
+
+# The name of an image file (relative to this directory) to place at the top
+# of the sidebar.
+#
+# html_logo = None
+
+# The name of an image file (relative to this directory) to use as a favicon of
+# the docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
+# pixels large.
+#
+# html_favicon = None
+
+# Add any paths that contain custom static files (such as style sheets) here,
+# relative to this directory. They are copied after the builtin static files,
+# so a file named "default.css" will overwrite the builtin "default.css".
+html_static_path = ['_static']
+
+# Add any extra paths that contain custom files (such as robots.txt or
+# .htaccess) here, relative to this directory. These files are copied
+# directly to the root of the documentation.
+#
+# html_extra_path = []
+
+# If not None, a 'Last updated on:' timestamp is inserted at every page
+# bottom, using the given strftime format.
+# The empty string is equivalent to '%b %d, %Y'.
+#
+# html_last_updated_fmt = None
+
+# If true, SmartyPants will be used to convert quotes and dashes to
+# typographically correct entities.
+#
+# html_use_smartypants = True
+
+# Custom sidebar templates, maps document names to template names.
+#
+# html_sidebars = {}
+
+# Additional templates that should be rendered to pages, maps page names to
+# template names.
+#
+# html_additional_pages = {}
+
+# If false, no module index is generated.
+#
+# html_domain_indices = True
+
+# If false, no index is generated.
+#
+# html_use_index = True
+
+# If true, the index is split into individual pages for each letter.
+#
+# html_split_index = False
+
+# If true, links to the reST sources are added to the pages.
+#
+# html_show_sourcelink = True
+
+# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
+#
+# html_show_sphinx = True
+
+# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
+#
+# html_show_copyright = True
+
+# If true, an OpenSearch description file will be output, and all pages will
+# contain a tag referring to it. The value of this option must be the
+# base URL from which the finished HTML is served.
+#
+# html_use_opensearch = ''
+
+# This is the file name suffix for HTML files (e.g. ".xhtml").
+# html_file_suffix = None
+
+# Language to be used for generating the HTML full-text search index.
+# Sphinx supports the following languages:
+# 'da', 'de', 'en', 'es', 'fi', 'fr', 'h', 'it', 'ja'
+# 'nl', 'no', 'pt', 'ro', 'r', 'sv', 'tr', 'zh'
+#
+# html_search_language = 'en'
+
+# A dictionary with options for the search language support, empty by default.
+# 'ja' uses this config value.
+# 'zh' user can custom change `jieba` dictionary path.
+#
+# html_search_options = {'type': 'default'}
+
+# The name of a javascript file (relative to the configuration directory) that
+# implements a search results scorer. If empty, the default will be used.
+#
+# html_search_scorer = 'scorer.js'
+
+# Output file base name for HTML help builder.
+htmlhelp_basename = '{project_name}doc'.format(project_name=project)
+
+# -- Options for LaTeX output ---------------------------------------------
+
+latex_elements = {
+ # The paper size ('letterpaper' or 'a4paper').
+ #
+ # 'papersize': 'letterpaper',
+
+ # The font size ('10pt', '11pt' or '12pt').
+ #
+ # 'pointsize': '10pt',
+
+ # Additional stuff for the LaTeX preamble.
+ #
+ # 'preamble': '',
+
+ # Latex figure (float) alignment
+ #
+ # 'figure_align': 'htbp',
+}
+
+# Grouping the document tree into LaTeX files. List of tuples
+# (source start file, target name, title,
+# author, documentclass [howto, manual, or own class]).
+latex_target = '{project}.tex'.format(project=project)
+latex_documents = [
+ (top_level_doc, latex_target, documentation_title,
+ author, 'manual'),
+]
+
+# The name of an image file (relative to this directory) to place at the top of
+# the title page.
+#
+# latex_logo = None
+
+# For "manual" documents, if this is true, then toplevel headings are parts,
+# not chapters.
+#
+# latex_use_parts = False
+
+# If true, show page references after internal links.
+#
+# latex_show_pagerefs = False
+
+# If true, show URL addresses after external links.
+#
+# latex_show_urls = False
+
+# Documents to append as an appendix to all manuals.
+#
+# latex_appendices = []
+
+# It false, will not define \strong, \code, itleref, \crossref ... but only
+# \sphinxstrong, ..., \sphinxtitleref, ... To help avoid clash with user added
+# packages.
+#
+# latex_keep_old_macro_names = True
+
+# If false, no module index is generated.
+#
+# latex_domain_indices = True
+
+
+# -- Options for manual page output ---------------------------------------
+
+# One entry per manual page. List of tuples
+# (source start file, name, description, authors, manual section).
+man_pages = [
+ (top_level_doc, project_title, documentation_title,
+ [author], 1)
+]
+
+# If true, show URL addresses after external links.
+#
+# man_show_urls = False
+
+
+# -- Options for Texinfo output -------------------------------------------
+
+# Grouping the document tree into Texinfo files. List of tuples
+# (source start file, target name, title, author,
+# dir menu entry, description, category)
+texinfo_documents = [
+ (top_level_doc, project_title, documentation_title,
+ author, project_title, 'What is this project?',
+ 'Miscellaneous'),
+]
+
+# Documents to append as an appendix to all manuals.
+#
+# texinfo_appendices = []
+
+# If false, no module index is generated.
+#
+# texinfo_domain_indices = True
+
+# How to display URL addresses: 'footnote', 'no', or 'inline'.
+#
+# texinfo_show_urls = 'footnote'
+
+# If true, do not generate a @detailmenu in the "Top" node's menu.
+#
+# texinfo_no_detailmenu = False
+
+
+# -- Options for Epub output ----------------------------------------------
+
+# Bibliographic Dublin Core info.
+epub_title = project
+epub_author = author
+epub_publisher = author
+epub_copyright = copyright
+
+# The basename for the epub file. It defaults to the project name.
+# epub_basename = project
+
+# The HTML theme for the epub output. Since the default themes are not
+# optimized for small screen space, using the same theme for HTML and epub
+# output is usually not wise. This defaults to 'epub', a theme designed to save
+# visual space.
+#
+# epub_theme = 'epub'
+
+# The language of the text. It defaults to the language option
+# or 'en' if the language is not set.
+#
+# epub_language = ''
+
+# The scheme of the identifier. Typical schemes are ISBN or URL.
+# epub_scheme = ''
+
+# The unique identifier of the text. This can be a ISBN number
+# or the project homepage.
+#
+# epub_identifier = ''
+
+# A unique identification for the text.
+#
+# epub_uid = ''
+
+# A tuple containing the cover image and cover page html template filenames.
+#
+# epub_cover = ()
+
+# A sequence of (type, uri, title) tuples for the guide element of content.opf.
+#
+# epub_guide = ()
+
+# HTML files that should be inserted before the pages created by sphinx.
+# The format is a list of tuples containing the path and title.
+#
+# epub_pre_files = []
+
+# HTML files that should be inserted after the pages created by sphinx.
+# The format is a list of tuples containing the path and title.
+#
+# epub_post_files = []
+
+# A list of files that should not be packed into the epub file.
+epub_exclude_files = ['search.html']
+
+# The depth of the table of contents in toc.ncx.
+#
+# epub_tocdepth = 3
+
+# Allow duplicate toc entries.
+#
+# epub_tocdup = True
+
+# Choose between 'default' and 'includehidden'.
+#
+# epub_tocscope = 'default'
+
+# Fix unsupported image types using the Pillow.
+#
+# epub_fix_images = False
+
+# Scale large images.
+#
+# epub_max_image_width = 0
+
+# How to display URL addresses: 'footnote', 'no', or 'inline'.
+#
+# epub_show_urls = 'inline'
+
+# If false, no index is generated.
+#
+# epub_use_index = True
+
+
+# Example configuration for intersphinx: refer to the Python standard library.
+intersphinx_mapping = {
+ 'python': ('https://docs.python.org/3.6', None),
+ 'django': ('https://docs.djangoproject.com/en/1.11/', 'https://docs.djangoproject.com/en/1.11/_objects/'),
+ 'model_utils': ('https://django-model-utils.readthedocs.io/en/latest/', None),
+}
+
+
+def on_init(app): # pylint: disable=unused-argument
+ """
+ Run sphinx-apidoc after Sphinx initialization.
+
+ Read the Docs won't run tox or custom shell commands, so we need this to
+ avoid checking in the generated reStructuredText files.
+ """
+ docs_path = os.path.abspath(os.path.dirname(__file__))
+ root_path = os.path.abspath(os.path.join(docs_path, '..'))
+ apidoc_path = 'sphinx-apidoc'
+ if hasattr(sys, 'real_prefix'): # Check to see if we are in a virtualenv
+ # If we are, assemble the path manually
+ bin_path = os.path.abspath(os.path.join(sys.prefix, 'bin'))
+ apidoc_path = os.path.join(bin_path, apidoc_path)
+ check_call([apidoc_path, '-o', docs_path, os.path.join(root_path, 'edx_course_team_api'),
+ os.path.join(root_path, 'edx_course_team_api/migrations')])
+
+
+def setup(app):
+ """Sphinx extension: run sphinx-apidoc."""
+ event = 'builder-inited'
+ app.connect(event, on_init)
diff --git a/docs/decisions/0001-purpose-of-this-repo.rst b/docs/decisions/0001-purpose-of-this-repo.rst
new file mode 100644
index 0000000..3c1692d
--- /dev/null
+++ b/docs/decisions/0001-purpose-of-this-repo.rst
@@ -0,0 +1,29 @@
+1. Purpose of this Repo
+=======================
+
+Status
+------
+
+Draft
+
+Context
+-------
+
+TODO: Give context on what lead to the creation of this repo.
+ What project is the repo related to?
+
+Decision
+--------
+
+TODO: Clearly state how the context above led you to creating this repo.
+
+Consequences
+------------
+
+TODO: As a result of this repo's creation, what other things will change.
+
+Rejected Alternatives
+---------------------
+
+TODO: If applicable, list viable alternatives to creating this new repo and
+ give reasons for why they were rejected.
diff --git a/docs/getting_started.rst b/docs/getting_started.rst
new file mode 100644
index 0000000..f203bbb
--- /dev/null
+++ b/docs/getting_started.rst
@@ -0,0 +1,18 @@
+Getting Started
+===============
+
+If you have not already done so, create/activate a `virtualenv`_. Unless otherwise stated, assume all terminal code
+below is executed within the virtualenv.
+
+.. _virtualenv: https://virtualenvwrapper.readthedocs.org/en/latest/
+
+
+Install dependencies
+--------------------
+Dependencies can be installed via the command below.
+
+.. code-block:: bash
+
+ $ make requirements
+
+
diff --git a/docs/index.rst b/docs/index.rst
new file mode 100644
index 0000000..0b89c64
--- /dev/null
+++ b/docs/index.rst
@@ -0,0 +1,29 @@
+.. edx_course_team_api documentation top level file, created by
+ sphinx-quickstart on Mon Oct 26 15:17:58 2020.
+ You can adapt this file completely to your liking, but it should at least
+ contain the root `toctree` directive.
+
+edx_course_team_api
+===============
+
+What is this project?
+
+Contents:
+
+.. toctree::
+ :maxdepth: 2
+
+ readme
+ getting_started
+ testing
+ internationalization
+ modules
+ changelog
+
+
+Indices and tables
+==================
+
+* :ref:`genindex`
+* :ref:`modindex`
+* :ref:`search`
diff --git a/docs/internationalization.rst b/docs/internationalization.rst
new file mode 100644
index 0000000..500861e
--- /dev/null
+++ b/docs/internationalization.rst
@@ -0,0 +1,51 @@
+.. _chapter-i18n:
+
+Internationalization
+====================
+All user-facing text content should be marked for translation. Even if this application is only run in English, our
+open source users may choose to use another language. Marking content for translation ensures our users have
+this choice.
+
+Follow the `internationalization coding guidelines`_ in the edX Developer's Guide when developing new features.
+
+.. _internationalization coding guidelines: https://edx.readthedocs.org/projects/edx-developer-guide/en/latest/internationalization/i18n.html
+
+Updating Translations
+~~~~~~~~~~~~~~~~~~~~~
+This project uses `Transifex`_ to translate content. After new features are developed the translation source files
+should be pushed to Transifex. Our translation community will translate the content, after which we can retrieve the
+translations.
+
+.. _Transifex: https://www.transifex.com/
+
+Pushing source translation files to Transifex requires access to the edx-platform. Request access from the Open Source
+Team if you will be pushing translation files. You should also `configure the Transifex client`_ if you have not done so
+already.
+
+.. _configure the Transifex client: https://docs.transifex.com/client/config/
+
+The `make` targets listed below can be used to push or pull translations.
+
+.. list-table::
+ :widths: 25 75
+ :header-rows: 1
+
+ * - Target
+ - Description
+ * - pull_translations
+ - Pull translations from Transifex
+ * - push_translations
+ - Push source translation files to Transifex
+
+Fake Translations
+~~~~~~~~~~~~~~~~~
+As you develop features it may be helpful to know which strings have been marked for translation, and which are not.
+Use the `fake_translations` make target for this purpose. This target will extract all strings marked for translation,
+generate fake translations in the Esperanto (eo) language directory, and compile the translations.
+
+You can trigger the display of the translations by setting your browser's language to Esperanto (eo), and navigating to
+a page on the site. Instead of plain English strings, you should see specially-accented English strings that look
+like this:
+
+ Thé Fütüré øf Ønlïné Édüçätïøn Ⱡσяєм ι# Før änýøné, änýwhéré, änýtïmé Ⱡσяєм #
+
diff --git a/docs/make.bat b/docs/make.bat
new file mode 100644
index 0000000..5c619cf
--- /dev/null
+++ b/docs/make.bat
@@ -0,0 +1,281 @@
+@ECHO OFF
+
+REM Command file for Sphinx documentation
+
+if "%SPHINXBUILD%" == "" (
+ set SPHINXBUILD=sphinx-build
+)
+set BUILDDIR=_build
+set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% .
+set I18NSPHINXOPTS=%SPHINXOPTS% .
+if NOT "%PAPER%" == "" (
+ set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS%
+ set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS%
+)
+
+if "%1" == "" goto help
+
+if "%1" == "help" (
+ :help
+ echo.Please use `make ^` where ^ is one of
+ echo. html to make standalone HTML files
+ echo. dirhtml to make HTML files named index.html in directories
+ echo. singlehtml to make a single large HTML file
+ echo. pickle to make pickle files
+ echo. json to make JSON files
+ echo. htmlhelp to make HTML files and a HTML help project
+ echo. qthelp to make HTML files and a qthelp project
+ echo. devhelp to make HTML files and a Devhelp project
+ echo. epub to make an epub
+ echo. epub3 to make an epub3
+ echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter
+ echo. text to make text files
+ echo. man to make manual pages
+ echo. texinfo to make Texinfo files
+ echo. gettext to make PO message catalogs
+ echo. changes to make an overview over all changed/added/deprecated items
+ echo. xml to make Docutils-native XML files
+ echo. pseudoxml to make pseudoxml-XML files for display purposes
+ echo. linkcheck to check all external links for integrity
+ echo. doctest to run all doctests embedded in the documentation if enabled
+ echo. coverage to run coverage check of the documentation if enabled
+ echo. dummy to check syntax errors of document sources
+ goto end
+)
+
+if "%1" == "clean" (
+ for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i
+ del /q /s %BUILDDIR%\*
+ goto end
+)
+
+
+REM Check if sphinx-build is available and fallback to Python version if any
+%SPHINXBUILD% 1>NUL 2>NUL
+if errorlevel 9009 goto sphinx_python
+goto sphinx_ok
+
+:sphinx_python
+
+set SPHINXBUILD=python -m sphinx.__init__
+%SPHINXBUILD% 2> nul
+if errorlevel 9009 (
+ echo.
+ echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
+ echo.installed, then set the SPHINXBUILD environment variable to point
+ echo.to the full path of the 'sphinx-build' executable. Alternatively you
+ echo.may add the Sphinx directory to PATH.
+ echo.
+ echo.If you don't have Sphinx installed, grab it from
+ echo.https://sphinx-doc.org/
+ exit /b 1
+)
+
+:sphinx_ok
+
+
+if "%1" == "html" (
+ %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished. The HTML pages are in %BUILDDIR%/html.
+ goto end
+)
+
+if "%1" == "dirhtml" (
+ %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml.
+ goto end
+)
+
+if "%1" == "singlehtml" (
+ %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml.
+ goto end
+)
+
+if "%1" == "pickle" (
+ %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished; now you can process the pickle files.
+ goto end
+)
+
+if "%1" == "json" (
+ %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished; now you can process the JSON files.
+ goto end
+)
+
+if "%1" == "htmlhelp" (
+ %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished; now you can run HTML Help Workshop with the ^
+.hhp project file in %BUILDDIR%/htmlhelp.
+ goto end
+)
+
+if "%1" == "qthelp" (
+ %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished; now you can run "qcollectiongenerator" with the ^
+.qhcp project file in %BUILDDIR%/qthelp, like this:
+ echo.^> qcollectiongenerator %BUILDDIR%\qthelp\edx_course_team_api.qhcp
+ echo.To view the help file:
+ echo.^> assistant -collectionFile %BUILDDIR%\qthelp\edx_course_team_api.ghc
+ goto end
+)
+
+if "%1" == "devhelp" (
+ %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished.
+ goto end
+)
+
+if "%1" == "epub" (
+ %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished. The epub file is in %BUILDDIR%/epub.
+ goto end
+)
+
+if "%1" == "epub3" (
+ %SPHINXBUILD% -b epub3 %ALLSPHINXOPTS% %BUILDDIR%/epub3
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished. The epub3 file is in %BUILDDIR%/epub3.
+ goto end
+)
+
+if "%1" == "latex" (
+ %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished; the LaTeX files are in %BUILDDIR%/latex.
+ goto end
+)
+
+if "%1" == "latexpdf" (
+ %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
+ cd %BUILDDIR%/latex
+ make all-pdf
+ cd %~dp0
+ echo.
+ echo.Build finished; the PDF files are in %BUILDDIR%/latex.
+ goto end
+)
+
+if "%1" == "latexpdfja" (
+ %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
+ cd %BUILDDIR%/latex
+ make all-pdf-ja
+ cd %~dp0
+ echo.
+ echo.Build finished; the PDF files are in %BUILDDIR%/latex.
+ goto end
+)
+
+if "%1" == "text" (
+ %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished. The text files are in %BUILDDIR%/text.
+ goto end
+)
+
+if "%1" == "man" (
+ %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished. The manual pages are in %BUILDDIR%/man.
+ goto end
+)
+
+if "%1" == "texinfo" (
+ %SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo.
+ goto end
+)
+
+if "%1" == "gettext" (
+ %SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished. The message catalogs are in %BUILDDIR%/locale.
+ goto end
+)
+
+if "%1" == "changes" (
+ %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.The overview file is in %BUILDDIR%/changes.
+ goto end
+)
+
+if "%1" == "linkcheck" (
+ %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Link check complete; look for any errors in the above output ^
+or in %BUILDDIR%/linkcheck/output.txt.
+ goto end
+)
+
+if "%1" == "doctest" (
+ %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Testing of doctests in the sources finished, look at the ^
+results in %BUILDDIR%/doctest/output.txt.
+ goto end
+)
+
+if "%1" == "coverage" (
+ %SPHINXBUILD% -b coverage %ALLSPHINXOPTS% %BUILDDIR%/coverage
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Testing of coverage in the sources finished, look at the ^
+results in %BUILDDIR%/coverage/python.txt.
+ goto end
+)
+
+if "%1" == "xml" (
+ %SPHINXBUILD% -b xml %ALLSPHINXOPTS% %BUILDDIR%/xml
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished. The XML files are in %BUILDDIR%/xml.
+ goto end
+)
+
+if "%1" == "pseudoxml" (
+ %SPHINXBUILD% -b pseudoxml %ALLSPHINXOPTS% %BUILDDIR%/pseudoxml
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished. The pseudo-XML files are in %BUILDDIR%/pseudoxml.
+ goto end
+)
+
+if "%1" == "dummy" (
+ %SPHINXBUILD% -b dummy %ALLSPHINXOPTS% %BUILDDIR%/dummy
+ if errorlevel 1 exit /b 1
+ echo.
+ echo.Build finished. Dummy builder generates no files.
+ goto end
+)
+
+:end
diff --git a/docs/readme.rst b/docs/readme.rst
new file mode 100644
index 0000000..72a3355
--- /dev/null
+++ b/docs/readme.rst
@@ -0,0 +1 @@
+.. include:: ../README.rst
diff --git a/docs/testing.rst b/docs/testing.rst
new file mode 100644
index 0000000..1a6d717
--- /dev/null
+++ b/docs/testing.rst
@@ -0,0 +1,44 @@
+.. _chapter-testing:
+
+Testing
+=======
+
+edx_course_team_api has an assortment of test cases and code quality
+checks to catch potential problems during development. To run them all in the
+version of Python you chose for your virtualenv:
+
+.. code-block:: bash
+
+ $ make validate
+
+To run just the unit tests:
+
+.. code-block:: bash
+
+ $ make test
+
+To run just the unit tests and check diff coverage
+
+.. code-block:: bash
+
+ $ make diff_cover
+
+To run just the code quality checks:
+
+.. code-block:: bash
+
+ $ make quality
+
+To run the unit tests under every supported Python version and the code
+quality checks:
+
+.. code-block:: bash
+
+ $ make test-all
+
+To generate and open an HTML report of how much of the code is covered by
+test cases:
+
+.. code-block:: bash
+
+ $ make coverage
diff --git a/edx_course_team_api/__init__.py b/edx_course_team_api/__init__.py
new file mode 100644
index 0000000..eb177f4
--- /dev/null
+++ b/edx_course_team_api/__init__.py
@@ -0,0 +1,7 @@
+"""
+What is this project?.
+"""
+
+__version__ = '0.2.0'
+
+default_app_config = 'edx_course_team_api.apps.EdxCourseTeamApiConfig' # pylint: disable=invalid-name
diff --git a/edx_course_team_api/apps.py b/edx_course_team_api/apps.py
new file mode 100644
index 0000000..abbd634
--- /dev/null
+++ b/edx_course_team_api/apps.py
@@ -0,0 +1,26 @@
+"""
+edx_course_team_api Django application initialization.
+"""
+
+from django.apps import AppConfig
+
+
+class EdxCourseTeamApiConfig(AppConfig):
+ """
+ Configuration for the edx_course_team_api Django application.
+ """
+
+ name = 'edx_course_team_api'
+ plugin_app = {
+ 'url_config': {
+ 'cms.djangoapp': {
+ 'namespace': 'edx_course_team_api',
+ 'regex': r'^sn-api/course-team/',
+ },
+ },
+ 'settings_config': {
+ 'cms.djangoapp': {
+ 'common': {'relative_path': 'settings.common'},
+ },
+ },
+ }
diff --git a/edx_course_team_api/conf/locale/config.yaml b/edx_course_team_api/conf/locale/config.yaml
new file mode 100644
index 0000000..d1c618e
--- /dev/null
+++ b/edx_course_team_api/conf/locale/config.yaml
@@ -0,0 +1,85 @@
+# Configuration for i18n workflow.
+
+locales:
+ - en # English - Source Language
+ - am # Amharic
+ - ar # Arabic
+ - az # Azerbaijani
+ - bg_BG # Bulgarian (Bulgaria)
+ - bn_BD # Bengali (Bangladesh)
+ - bn_IN # Bengali (India)
+ - bs # Bosnian
+ - ca # Catalan
+ - ca@valencia # Catalan (Valencia)
+ - cs # Czech
+ - cy # Welsh
+ - da # Danish
+ - de_DE # German (Germany)
+ - el # Greek
+ - en # English
+ - en_GB # English (United Kingdom)
+ # Don't pull these until we figure out why pages randomly display in these locales,
+ # when the user's browser is in English and the user is not logged in.
+ # - en@lolcat # LOLCAT English
+ # - en@pirate # Pirate English
+ - es_419 # Spanish (Latin America)
+ - es_AR # Spanish (Argentina)
+ - es_EC # Spanish (Ecuador)
+ - es_ES # Spanish (Spain)
+ - es_MX # Spanish (Mexico)
+ - es_PE # Spanish (Peru)
+ - et_EE # Estonian (Estonia)
+ - eu_ES # Basque (Spain)
+ - fa # Persian
+ - fa_IR # Persian (Iran)
+ - fi_FI # Finnish (Finland)
+ - fil # Filipino
+ - fr # French
+ - gl # Galician
+ - gu # Gujarati
+ - he # Hebrew
+ - hi # Hindi
+ - hr # Croatian
+ - hu # Hungarian
+ - hy_AM # Armenian (Armenia)
+ - id # Indonesian
+ - it_IT # Italian (Italy)
+ - ja_JP # Japanese (Japan)
+ - kk_KZ # Kazakh (Kazakhstan)
+ - km_KH # Khmer (Cambodia)
+ - kn # Kannada
+ - ko_KR # Korean (Korea)
+ - lt_LT # Lithuanian (Lithuania)
+ - ml # Malayalam
+ - mn # Mongolian
+ - mr # Marathi
+ - ms # Malay
+ - nb # Norwegian Bokmål
+ - ne # Nepali
+ - nl_NL # Dutch (Netherlands)
+ - or # Oriya
+ - pl # Polish
+ - pt_BR # Portuguese (Brazil)
+ - pt_PT # Portuguese (Portugal)
+ - ro # Romanian
+ - ru # Russian
+ - si # Sinhala
+ - sk # Slovak
+ - sl # Slovenian
+ - sq # Albanian
+ - sr # Serbian
+ - ta # Tamil
+ - te # Telugu
+ - th # Thai
+ - tr_TR # Turkish (Turkey)
+ - uk # Ukranian
+ - ur # Urdu
+ - uz # Uzbek
+ - vi # Vietnamese
+ - zh_CN # Chinese (China)
+ - zh_HK # Chinese (Hong Kong)
+ - zh_TW # Chinese (Taiwan)
+
+# The locales used for fake-accented English, for testing.
+dummy_locales:
+ - eo
diff --git a/edx_course_team_api/models.py b/edx_course_team_api/models.py
new file mode 100644
index 0000000..0ff3af2
--- /dev/null
+++ b/edx_course_team_api/models.py
@@ -0,0 +1,3 @@
+"""
+Database models for edx_course_team_api.
+"""
diff --git a/edx_course_team_api/settings/__init__.py b/edx_course_team_api/settings/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/edx_course_team_api/settings/common.py b/edx_course_team_api/settings/common.py
new file mode 100644
index 0000000..c67e002
--- /dev/null
+++ b/edx_course_team_api/settings/common.py
@@ -0,0 +1,18 @@
+"""
+See apps.py for details on how this sort of plugin configures itself for
+integration with Open edX.
+"""
+
+import os
+
+def plugin_settings(settings):
+ """
+ Modify the provided settings object with settings specific to this plugin.
+ """
+ settings.AUTH_USERNAME = os.environ.get('AUTH_USERNAME')
+ settings.AUTH_PASSWORD = os.environ.get('AUTH_PASSWORD')
+
+ if not settings.AUTH_USERNAME or not settings.AUTH_PASSWORD:
+ raise Exception('AUTH_USERNAME or AUTH_PASSWORD environment variables not set')
+
+ settings.EMAIL = settings.AUTH_USERNAME + '@skills.network'
diff --git a/edx_course_team_api/templates/edx_courses_api/base.html b/edx_course_team_api/templates/edx_courses_api/base.html
new file mode 100644
index 0000000..d13a13e
--- /dev/null
+++ b/edx_course_team_api/templates/edx_courses_api/base.html
@@ -0,0 +1,22 @@
+
+{% comment %}
+As the developer of this package, don't place anything here if you can help it
+since this allows developers to have interoperability between your template
+structure and their own.
+
+Example: Developer melding the 2SoD pattern to fit inside with another pattern::
+
+ {% extends "base.html" %}
+ {% load static %}
+
+
+ {% block extra_js %}
+
+
+ {% block javascript %}
+
+ {% endblock javascript %}
+
+ {% endblock extra_js %}
+{% endcomment %}
+
diff --git a/edx_course_team_api/urls.py b/edx_course_team_api/urls.py
new file mode 100644
index 0000000..2b3876b
--- /dev/null
+++ b/edx_course_team_api/urls.py
@@ -0,0 +1,20 @@
+"""
+URLs for edx_course_team_api.
+"""
+from django.conf import settings
+from django.conf.urls import url
+
+from .views import CourseView
+
+urlpatterns = [
+ url(r'^{}/modify_access'.format(settings.COURSE_KEY_PATTERN), CourseView.as_view(), name='course'),
+]
+
+# Since urls.py is executed once, create service user here for server to server auth
+from django.contrib.auth.models import User
+try:
+ User.objects.get(username=settings.AUTH_USERNAME)
+except User.DoesNotExist:
+ User.objects.create_user(username=settings.AUTH_USERNAME,
+ email=settings.EMAIL,
+ password=settings.AUTH_PASSWORD, is_staff=True)
diff --git a/edx_course_team_api/views.py b/edx_course_team_api/views.py
new file mode 100644
index 0000000..fa8b02a
--- /dev/null
+++ b/edx_course_team_api/views.py
@@ -0,0 +1,70 @@
+import logging
+import json
+
+from rest_framework.views import APIView
+from rest_framework.response import Response
+from rest_framework.exceptions import ParseError
+from rest_framework import status
+from rest_framework.authentication import BasicAuthentication
+from rest_framework.permissions import IsAuthenticated
+from django.contrib.auth.models import User
+
+# edx imports
+from student import auth
+from student.models import CourseEnrollment
+from student.roles import CourseInstructorRole, CourseStaffRole
+from opaque_keys.edx.keys import CourseKey
+from cms.djangoapps.contentstore.views.user import _course_team_user
+
+
+log = logging.getLogger(__name__)
+
+USERNAME = 'admin' # the user who will be associated with new courses
+
+ROLE_TYPE_MAPPINGS = {
+ "staff": CourseStaffRole,
+ "instructor": CourseInstructorRole
+}
+ROLE_OPTIONS = list(ROLE_TYPE_MAPPINGS.keys())
+
+class CourseView(APIView):
+
+ authentication_classes = [BasicAuthentication]
+ permission_classes = [IsAuthenticated]
+
+ def post(self, request, course_key_string):
+ course_key = CourseKey.from_string(course_key_string)
+
+ email = request.data.get("email", None)
+ if not email:
+ msg = { "error": "Missing parameter 'email' in body." }
+ log.info(msg)
+ raise ParseError(msg)
+
+ role = request.data.get("role", None)
+ if not role:
+ msg = { "error": "Missing parameter 'role' in body." }
+ log.info(msg)
+ raise ParseError(msg)
+
+ if role not in ROLE_OPTIONS:
+ msg = { "error": "Parameter 'role' has to be one of '{}'".format(ROLE_OPTIONS) }
+ log.info(msg)
+ raise ParseError(msg)
+
+ try:
+ user = User.objects.get(email=email)
+ except Exception: # pylint: disable=broad-except
+ msg = {
+ "error": "Could not find user by email address '{email}'".format(email=email)
+ }
+ return Response(msg, 404)
+
+ role_type = ROLE_TYPE_MAPPINGS.get(role)(course_key)
+ auth.add_users(request.user, role_type, user)
+ CourseEnrollment.enroll(user, course_key)
+
+ msg = "'{email}' is granted '{role}' to '{course_key}'".format(email=email, role=role, course_key=course_key)
+ log.info(msg)
+
+ return Response({'message': "User is added to {}.".format(course_key)})
diff --git a/manage.py b/manage.py
new file mode 100644
index 0000000..3a41458
--- /dev/null
+++ b/manage.py
@@ -0,0 +1,29 @@
+#!/usr/bin/env python
+"""
+Django administration utility.
+"""
+
+import os
+import sys
+
+PWD = os.path.abspath(os.path.dirname(__file__))
+
+if __name__ == '__main__':
+ os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'test_settings')
+ sys.path.append(PWD)
+ try:
+ from django.core.management import execute_from_command_line # pylint: disable=wrong-import-position
+ 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 # pylint: disable=unused-import, wrong-import-position
+ 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/openedx.yaml b/openedx.yaml
new file mode 100644
index 0000000..0ccb213
--- /dev/null
+++ b/openedx.yaml
@@ -0,0 +1,24 @@
+# This file describes this Open edX repo, as described in OEP-2:
+# https://open-edx-proposals.readthedocs.io/en/latest/oep-0002-bp-repo-metadata.html#specification
+
+---
+nick: edx_course_team_api
+tags:
+ - tools
+oeps:
+ oep-2: true
+ oep-3:
+ state: false
+ reason: TODO - Implement for this application if appropriate
+ oep-5:
+ state: false
+ reason: TODO - Implement for this application if appropriate
+ oep-18: true
+ oep-30:
+ state: true
+openedx-release:
+ # The openedx-release key is described in OEP-10:
+ # https://open-edx-proposals.readthedocs.io/en/latest/oep-0010-proc-openedx-releases.html
+ # The FAQ might also be helpful: https://openedx.atlassian.net/wiki/spaces/COMM/pages/1331268879/Open+edX+Release+FAQ
+ maybe: true # Delete this "maybe" line when you have decided about Open edX inclusion.
+ ref: master
diff --git a/pylintrc b/pylintrc
new file mode 100644
index 0000000..b4b3588
--- /dev/null
+++ b/pylintrc
@@ -0,0 +1,460 @@
+# ***************************
+# ** DO NOT EDIT THIS FILE **
+# ***************************
+#
+# This file was generated by edx-lint: https://github.com/edx/edx-lint
+#
+# If you want to change this file, you have two choices, depending on whether
+# you want to make a local change that applies only to this repo, or whether
+# you want to make a central change that applies to all repos using edx-lint.
+#
+# Note: If your pylintrc file is simply out-of-date relative to the latest
+# pylintrc rules in edx-lint, ensure you have the latest edx-lint installed
+# and then follow the steps for a "LOCAL CHANGE".
+#
+# LOCAL CHANGE:
+#
+# 1. Edit the local pylintrc_tweaks file to add changes just to this
+# repo's file.
+#
+# 2. Run:
+#
+# $ edx_lint write pylintrc
+#
+# 3. This will modify the local file. Submit a pull request to get it
+# checked in so that others will benefit.
+#
+#
+# CENTRAL CHANGE:
+#
+# 1. Edit the pylintrc file in the edx-lint repo at
+# https://github.com/edx/edx-lint/blob/master/edx_lint/files/pylintrc
+#
+# 2. install the updated version of edx-lint (in edx-lint):
+#
+# $ pip install .
+#
+# 3. Run (in edx-lint):
+#
+# # uses pylintrc_tweaks from edx-lint for linting in edx-lint
+# # NOTE: Use Python 3.x, which no longer includes comments in the output file
+# $ edx_lint write pylintrc
+#
+# 4. Make a new version of edx_lint, submit and review a pull request with the
+# pylintrc update, and after merging, update the edx-lint version by
+# creating a new tag in the repo (uses pbr).
+#
+# 5. In your local repo, install the newer version of edx-lint.
+#
+# 6. Run:
+#
+# # uses local pylintrc_tweaks
+# $ edx_lint write pylintrc
+#
+# 7. This will modify the local file. Submit a pull request to get it
+# checked in so that others will benefit.
+#
+#
+#
+#
+#
+# STAY AWAY FROM THIS FILE!
+#
+#
+#
+#
+#
+# SERIOUSLY.
+#
+# ------------------------------
+[MASTER]
+ignore = migrations
+persistent = yes
+load-plugins = edx_lint.pylint,pylint_django,pylint_celery
+
+[MESSAGES CONTROL]
+enable =
+ blacklisted-name,
+ 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,
+
+ 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,
+ feature-toggle-needs-doc,
+ file-builtin,
+ filter-builtin-not-iterating,
+ fixme,
+ getslice-method,
+ hex-method,
+ illegal-waffle-usage,
+ 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,
+ ungrouped-imports,
+ unichr-builtin,
+ unicode-builtin,
+ unpacking-in-except,
+ using-cmp-argument,
+ xrange-builtin,
+ zip-builtin-not-iterating,
+
+[REPORTS]
+output-format = text
+files-output = no
+reports = no
+score = no
+
+[BASIC]
+bad-functions = map,filter,apply,input
+module-rgx = (([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$
+const-rgx = (([A-Z_][A-Z0-9_]*)|(__.*__)|log|urlpatterns)$
+class-rgx = [A-Z_][a-zA-Z0-9]+$
+function-rgx = ([a-z_][a-z0-9_]{2,40}|test_[a-z0-9_]+)$
+method-rgx = ([a-z_][a-z0-9_]{2,40}|setUp|set[Uu]pClass|tearDown|tear[Dd]ownClass|assert[A-Z]\w*|maxDiff|test_[a-z0-9_]+)$
+attr-rgx = [a-z_][a-z0-9_]{2,30}$
+argument-rgx = [a-z_][a-z0-9_]{2,30}$
+variable-rgx = [a-z_][a-z0-9_]{2,30}$
+class-attribute-rgx = ([A-Za-z_][A-Za-z0-9_]{2,30}|(__.*__))$
+inlinevar-rgx = [A-Za-z_][A-Za-z0-9_]*$
+good-names = f,i,j,k,db,ex,Run,_,__
+bad-names = foo,bar,baz,toto,tutu,tata
+no-docstring-rgx = __.*__$|test_.+|setUp$|setUpClass$|tearDown$|tearDownClass$|Meta$
+docstring-min-length = 5
+
+[FORMAT]
+max-line-length = 120
+ignore-long-lines = ^\s*(# )?((?)|(\.\. \w+: .*))$
+single-line-if-stmt = no
+no-space-check = trailing-comma,dict-separator
+max-module-lines = 1000
+indent-string = ' '
+
+[MISCELLANEOUS]
+notes = FIXME,XXX,TODO
+
+[SIMILARITIES]
+min-similarity-lines = 4
+ignore-comments = yes
+ignore-docstrings = yes
+ignore-imports = no
+
+[TYPECHECK]
+ignore-mixin-members = yes
+ignored-classes = SQLObject
+unsafe-load-any-extension = yes
+generated-members =
+ REQUEST,
+ acl_users,
+ aq_parent,
+ objects,
+ DoesNotExist,
+ can_read,
+ can_write,
+ get_url,
+ size,
+ content,
+ status_code,
+ create,
+ build,
+ fields,
+ tag,
+ org,
+ course,
+ category,
+ name,
+ revision,
+ _meta,
+
+[VARIABLES]
+init-import = no
+dummy-variables-rgx = _|dummy|unused|.*_unused
+additional-builtins =
+
+[CLASSES]
+defining-attr-methods = __init__,__new__,setUp
+valid-classmethod-first-arg = cls
+valid-metaclass-classmethod-first-arg = mcs
+
+[DESIGN]
+max-args = 5
+ignored-argument-names = _.*
+max-locals = 15
+max-returns = 6
+max-branches = 12
+max-statements = 50
+max-parents = 7
+max-attributes = 7
+min-public-methods = 2
+max-public-methods = 20
+
+[IMPORTS]
+deprecated-modules = regsub,TERMIOS,Bastion,rexec
+import-graph =
+ext-import-graph =
+int-import-graph =
+
+[EXCEPTIONS]
+overgeneral-exceptions = Exception
+
+# 8ef4b3df6ebdda615bae17bce9009a6675af6d2c
diff --git a/pylintrc_tweaks b/pylintrc_tweaks
new file mode 100644
index 0000000..4764e22
--- /dev/null
+++ b/pylintrc_tweaks
@@ -0,0 +1,4 @@
+# pylintrc tweaks for use with edx_lint.
+[MASTER]
+ignore = migrations
+load-plugins = edx_lint.pylint,pylint_django,pylint_celery
diff --git a/requirements/base.in b/requirements/base.in
new file mode 100644
index 0000000..4b614f1
--- /dev/null
+++ b/requirements/base.in
@@ -0,0 +1,5 @@
+# Core requirements for using this application
+-c constraints.txt
+
+Django>=1.11 # Web application framework
+djangorestframework>=3.9
diff --git a/requirements/constraints.txt b/requirements/constraints.txt
new file mode 100644
index 0000000..76a6314
--- /dev/null
+++ b/requirements/constraints.txt
@@ -0,0 +1,12 @@
+# 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.
+
+# code-annotations currently restricts to Django<2.3
+Django<2.3
diff --git a/requirements/dev.in b/requirements/dev.in
new file mode 100644
index 0000000..2e8c93c
--- /dev/null
+++ b/requirements/dev.in
@@ -0,0 +1,10 @@
+# Additional requirements for development of this application
+-c constraints.txt
+
+-r pip-tools.txt # pip-tools and its dependencies, for managing requirements files
+-r quality.txt # Core and quality check dependencies
+-r travis.txt # tox and related dependencies
+
+diff-cover # Changeset diff test coverage
+edx-i18n-tools # For i18n_tool dummy
+tox-battery # Makes tox aware of requirements file changes
diff --git a/requirements/doc.in b/requirements/doc.in
new file mode 100644
index 0000000..690e8e1
--- /dev/null
+++ b/requirements/doc.in
@@ -0,0 +1,9 @@
+# Requirements for documentation validation
+-c constraints.txt
+
+-r test.txt # Core and testing dependencies for this package
+
+doc8 # reStructuredText style checker
+edx_sphinx_theme # edX theme for Sphinx output
+readme_renderer # Validates README.rst for usage on PyPI
+Sphinx # Documentation builder
diff --git a/requirements/pip-tools.in b/requirements/pip-tools.in
new file mode 100644
index 0000000..3f1b64a
--- /dev/null
+++ b/requirements/pip-tools.in
@@ -0,0 +1,4 @@
+# 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..89fbde9
--- /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.1.2 # via -r requirements/pip-tools.in
+six==1.14.0 # via pip-tools
+
+# The following packages are considered to be unsafe in a requirements file:
+# pip
diff --git a/requirements/private.readme b/requirements/private.readme
new file mode 100644
index 0000000..5600a10
--- /dev/null
+++ b/requirements/private.readme
@@ -0,0 +1,15 @@
+# If there are any Python packages you want to keep in your virtualenv beyond
+# those listed in the official requirements files, create a "private.in" file
+# and list them there. Generate the corresponding "private.txt" file pinning
+# all of their indirect dependencies to specific versions as follows:
+
+# pip-compile private.in
+
+# This allows you to use "pip-sync" without removing these packages:
+
+# pip-sync requirements/*.txt
+
+# "private.in" and "private.txt" aren't checked into git to avoid merge
+# conflicts, and the presence of this file allows "private.*" to be
+# included in scripted pip-sync usage without requiring that those files be
+# created first.
diff --git a/requirements/quality.in b/requirements/quality.in
new file mode 100644
index 0000000..0bd84a6
--- /dev/null
+++ b/requirements/quality.in
@@ -0,0 +1,9 @@
+# Requirements for code quality checks
+-c constraints.txt
+
+-r test.txt # Core and testing dependencies for this package
+
+edx-lint # edX pylint rules and plugins
+isort # to standardize order of imports
+pycodestyle # PEP 8 compliance validation
+pydocstyle # PEP 257 compliance validation
diff --git a/requirements/test.in b/requirements/test.in
new file mode 100644
index 0000000..6797160
--- /dev/null
+++ b/requirements/test.in
@@ -0,0 +1,8 @@
+# Requirements for test runs.
+-c constraints.txt
+
+-r base.txt # Core dependencies for this package
+
+pytest-cov # pytest extension for code coverage statistics
+pytest-django # pytest extension for better Django support
+code-annotations # provides commands used by the pii_check make target.
diff --git a/requirements/travis.in b/requirements/travis.in
new file mode 100644
index 0000000..895a9c8
--- /dev/null
+++ b/requirements/travis.in
@@ -0,0 +1,5 @@
+# Requirements for running tests in Travis
+-c constraints.txt
+
+codecov # Code coverage reporting
+tox # Virtualenv management for tests
diff --git a/setup.cfg b/setup.cfg
new file mode 100644
index 0000000..9a902de
--- /dev/null
+++ b/setup.cfg
@@ -0,0 +1,8 @@
+[isort]
+include_trailing_comma = True
+indent = ' '
+line_length = 120
+multi_line_output = 3
+
+[wheel]
+universal = 1
diff --git a/setup.py b/setup.py
new file mode 100755
index 0000000..8aeff90
--- /dev/null
+++ b/setup.py
@@ -0,0 +1,102 @@
+#!/usr/bin/env python
+"""
+Package metadata for edx_course_team_api.
+"""
+import os
+import re
+import sys
+
+from setuptools import setup
+
+
+def get_version(*file_paths):
+ """
+ Extract the version string from the file.
+
+ Input:
+ - file_paths: relative path fragments to file with
+ version string
+ """
+ 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.')
+
+
+def load_requirements(*requirements_paths):
+ """
+ Load all requirements from the specified requirements files.
+
+ Returns:
+ list: Requirements file relative path strings
+ """
+ requirements = set()
+ for path in requirements_paths:
+ requirements.update(
+ line.split('#')[0].strip() for line in open(path).readlines()
+ if is_requirement(line.strip())
+ )
+ return list(requirements)
+
+
+def is_requirement(line):
+ """
+ Return True if the requirement line is a package requirement.
+
+ Returns:
+ bool: True if the line is not blank, a comment, a URL, or
+ an included file
+ """
+ return line and not line.startswith(('-r', '#', '-e', 'git+', '-c'))
+
+
+VERSION = get_version('edx_course_team_api', '__init__.py')
+
+if sys.argv[-1] == 'tag':
+ print("Tagging the version on github:")
+ os.system("git tag -a %s -m 'version %s'" % (VERSION, VERSION))
+ os.system("git push --tags")
+ sys.exit()
+
+README = open(os.path.join(os.path.dirname(__file__), 'README.rst')).read()
+CHANGELOG = open(os.path.join(os.path.dirname(__file__), 'CHANGELOG.rst')).read()
+
+setup(
+ name='edx_course_team_api',
+ version=VERSION,
+ description="""What is this project?""",
+ long_description=README + '\n\n' + CHANGELOG,
+ author='edX',
+ author_email='oscm@edx.org',
+ url='https://github.com/edx/edx_course_team_api',
+ packages=[
+ 'edx_course_team_api',
+ ],
+ include_package_data=True,
+ install_requires=load_requirements('requirements/base.in'),
+ license="AGPL 3.0",
+ zip_safe=False,
+ keywords='Python edx',
+ classifiers=[
+ 'Development Status :: 3 - Alpha',
+ 'Framework :: Django',
+ 'Framework :: Django :: 1.11',
+ 'Framework :: Django :: 2.0',
+ 'Framework :: Django :: 2.1',
+ 'Framework :: Django :: 2.2',
+ 'Intended Audience :: Developers',
+ 'License :: OSI Approved :: GNU Affero General Public License v3 or later (AGPLv3+)',
+ 'Natural Language :: English',
+ 'Programming Language :: Python :: 3',
+ 'Programming Language :: Python :: 3.5',
+ 'Programming Language :: Python :: 3.8',
+ ],
+ entry_points={
+ "cms.djangoapp": [
+ "edx_course_team_api = edx_course_team_api.apps:EdxCourseTeamApiConfig",
+ ],
+ }
+)
diff --git a/test_settings.py b/test_settings.py
new file mode 100644
index 0000000..58309ac
--- /dev/null
+++ b/test_settings.py
@@ -0,0 +1,41 @@
+"""
+These settings are here to use during tests, because django requires them.
+
+In a real-world use case, apps in this project are installed into other
+Django applications, so these settings will not be used.
+"""
+
+from os.path import abspath, dirname, join
+
+
+def root(*args):
+ """
+ Get the absolute path of the given path relative to the project root.
+ """
+ return join(abspath(dirname(__file__)), *args)
+
+
+DATABASES = {
+ 'default': {
+ 'ENGINE': 'django.db.backends.sqlite3',
+ 'NAME': 'default.db',
+ 'USER': '',
+ 'PASSWORD': '',
+ 'HOST': '',
+ 'PORT': '',
+ }
+}
+
+INSTALLED_APPS = (
+ 'django.contrib.auth',
+ 'django.contrib.contenttypes',
+ 'edx_course_team_api',
+)
+
+LOCALE_PATHS = [
+ root('edx_course_team_api', 'conf', 'locale'),
+]
+
+ROOT_URLCONF = 'edx_course_team_api.urls'
+
+SECRET_KEY = 'insecure-secret-key'
diff --git a/test_utils/__init__.py b/test_utils/__init__.py
new file mode 100644
index 0000000..7961e47
--- /dev/null
+++ b/test_utils/__init__.py
@@ -0,0 +1,10 @@
+"""
+Test utilities.
+
+Since pytest discourages putting __init__.py into testdirectory
+(i.e. making tests a package) one cannot import from anywhere
+under tests folder. However, some utility classes/methods might be useful
+in multiple test modules (i.e. factoryboy factories, base test classes).
+
+So this package is the place to put them.
+"""
diff --git a/tests/test_models.py b/tests/test_models.py
new file mode 100644
index 0000000..8914c94
--- /dev/null
+++ b/tests/test_models.py
@@ -0,0 +1,4 @@
+#!/usr/bin/env python
+"""
+Tests for the `edx_course_team_api` models module.
+"""
diff --git a/tox.ini b/tox.ini
new file mode 100644
index 0000000..9d617a7
--- /dev/null
+++ b/tox.ini
@@ -0,0 +1,84 @@
+[tox]
+envlist = {py35,py38}-django{22,30}
+
+[doc8]
+; D001 = Line too long
+ignore=D001
+
+[pycodestyle]
+exclude = .git,.tox,migrations
+max-line-length = 120
+
+[pydocstyle]
+; D101 = Missing docstring in public class
+; D200 = One-line docstring should fit on one line with quotes
+; D203 = 1 blank line required before class docstring
+; D212 = Multi-line docstring summary should start at the first line
+; D215 = Section underline is over-indented (numpy style)
+; D404 = First word of the docstring should not be This (numpy style)
+; D405 = Section name should be properly capitalized (numpy style)
+; D406 = Section name should end with a newline (numpy style)
+; D407 = Missing dashed underline after section (numpy style)
+; D408 = Section underline should be in the line following the section’s name (numpy style)
+; D409 = Section underline should match the length of its name (numpy style)
+; D410 = Missing blank line after section (numpy style)
+; D411 = Missing blank line before section (numpy style)
+; D412 = No blank lines allowed between a section header and its content (numpy style)
+; D413 = Missing blank line after last section (numpy style)
+; D414 = Section has no content (numpy style)
+ignore = D101,D200,D203,D212,D215,D404,D405,D406,D407,D408,D409,D410,D411,D412,D413,D414
+match-dir = (?!migrations)
+
+[pytest]
+DJANGO_SETTINGS_MODULE = test_settings
+addopts = --cov edx_course_team_api --cov-report term-missing --cov-report xml
+norecursedirs = .* docs requirements site-packages
+
+[testenv]
+deps =
+ django22: Django>=2.2,<2.3
+ django30: Django>=3.0,<3.1
+ -r{toxinidir}/requirements/test.txt
+commands =
+ pytest {posargs}
+
+[testenv:docs]
+setenv =
+ DJANGO_SETTINGS_MODULE = test_settings
+ PYTHONPATH = {toxinidir}
+whitelist_externals =
+ make
+ rm
+deps =
+ -r{toxinidir}/requirements/doc.txt
+commands =
+ doc8 --ignore-path docs/_build README.rst docs
+ rm -f docs/edx_course_team_api.rst
+ rm -f docs/modules.rst
+ make -C docs clean
+ make -C docs html
+ python setup.py check --restructuredtext --strict
+
+[testenv:quality]
+whitelist_externals =
+ make
+ rm
+ touch
+deps =
+ -r{toxinidir}/requirements/quality.txt
+commands =
+ touch tests/__init__.py
+ pylint edx_course_team_api tests test_utils manage.py setup.py
+ rm tests/__init__.py
+ pycodestyle edx_course_team_api tests manage.py setup.py
+ pydocstyle edx_course_team_api tests manage.py setup.py
+ isort --check-only --diff --recursive tests test_utils edx_course_team_api manage.py setup.py test_settings.py
+ make selfcheck
+
+[testenv:pii_check]
+setenv =
+ DJANGO_SETTINGS_MODULE = test_settings
+deps =
+ -r{toxinidir}/requirements/test.txt
+commands =
+ code_annotations django_find_annotations --config_file .pii_annotations.yml --lint --report --coverage