From 6f4f04b11756086fa4ace404d4f00c856dc6f157 Mon Sep 17 00:00:00 2001 From: Alexis Metaireau Date: Tue, 18 Oct 2011 23:26:13 +0200 Subject: [PATCH] Document the API. Fix #46 --- README.rst | 2 +- budget/models.py | 7 +- budget/templates/list_bills.html | 8 +- budget/tests.py | 14 +-- docs/Makefile | 130 +++++++++++++++++++++++ docs/_themes/.gitignore | 3 + docs/_themes/pelican | 1 + docs/api.rst | 163 +++++++++++++++++++++++++++++ docs/conf.py | 20 ++++ docs/index.rst | 1 + docs/make.bat | 170 +++++++++++++++++++++++++++++++ 11 files changed, 504 insertions(+), 15 deletions(-) create mode 100644 docs/Makefile create mode 100644 docs/_themes/.gitignore create mode 160000 docs/_themes/pelican create mode 100644 docs/api.rst create mode 100644 docs/conf.py create mode 120000 docs/index.rst create mode 100644 docs/make.bat diff --git a/README.rst b/README.rst index 374dd32..e50fe6c 100644 --- a/README.rst +++ b/README.rst @@ -51,7 +51,7 @@ How about the REST API? ======================= Yep, you're right, there is a REST API with this. Head to the `api -documentation` to know more. +documentation `_ to know more. How to contribute ================= diff --git a/budget/models.py b/budget/models.py index e5d5ea9..152ad34 100644 --- a/budget/models.py +++ b/budget/models.py @@ -12,7 +12,7 @@ db = SQLAlchemy() class Project(db.Model): _to_serialize = ("id", "name", "password", "contact_email", - "members", "active_members") + "members", "active_members", "balance") id = db.Column(db.String, primary_key=True) @@ -25,7 +25,8 @@ class Project(db.Model): def active_members(self): return [m for m in self.members if m.activated] - def get_balance(self): + @property + def balance(self): balances, should_pay, should_receive = defaultdict(int), defaultdict(int), defaultdict(int) @@ -39,7 +40,7 @@ class Project(db.Model): should_receive[bill.payer] += bill.pay_each() for person in self.members: - balances[person] = round(should_receive[person] - should_pay[person], 2) + balances[person.id] = round(should_receive[person] - should_pay[person], 2) return balances diff --git a/budget/templates/list_bills.html b/budget/templates/list_bills.html index d163f7e..e284eff 100644 --- a/budget/templates/list_bills.html +++ b/budget/templates/list_bills.html @@ -56,13 +56,13 @@

{{ _("Balance") }}

- {% set balance = g.project.get_balance() %} + {% set balance = g.project.balance %} {% for member in g.project.members %} - {% if member.activated or balance[member] != 0 %} + {% if member.activated or balance[member.id] != 0 %} - diff --git a/budget/tests.py b/budget/tests.py index 452f71f..3f261fa 100644 --- a/budget/tests.py +++ b/budget/tests.py @@ -333,7 +333,7 @@ class BudgetTestCase(TestCase): 'amount': '17', }) - balance = models.Project.query.get("raclette").get_balance() + balance = models.Project.query.get("raclette").balance self.assertEqual(set(balance.values()), set([19.0, -19.0])) def test_rounding(self): @@ -369,10 +369,8 @@ class BudgetTestCase(TestCase): 'amount': '22', }) - balance = models.Project.query.get("raclette").get_balance() - balance = dict([(user.name, bal) for user, bal in balance.items()]) - self.assertDictEqual(balance, {u'tata': -8.12, u'alexis': 8.12, - u'fred': 0.0}) + balance = models.Project.query.get("raclette").balance + self.assertDictEqual(balance, {3: -8.12, 1: 8.12, 2: 0.0}) def test_edit_project(self): @@ -489,7 +487,8 @@ class APITestCase(TestCase): "contact_email": "raclette@notmyidea.org", "members": [], "password": "raclette", - "id": "raclette" + "id": "raclette", + "balance": {}, } self.assertDictEqual(json.loads(resp.data), expected) @@ -512,7 +511,8 @@ class APITestCase(TestCase): "contact_email": "yeah@notmyidea.org", "members": [], "password": "raclette", - "id": "raclette" + "id": "raclette", + "balance": {}, } self.assertDictEqual(json.loads(resp.data), expected) diff --git a/docs/Makefile b/docs/Makefile new file mode 100644 index 0000000..cf3a9fb --- /dev/null +++ b/docs/Makefile @@ -0,0 +1,130 @@ +# Makefile for Sphinx documentation +# + +# You can set these variables from the command line. +SPHINXOPTS = +SPHINXBUILD = sphinx-build +PAPER = +BUILDDIR = _build + +# Internal variables. +PAPEROPT_a4 = -D latex_paper_size=a4 +PAPEROPT_letter = -D latex_paper_size=letter +ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . + +.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest + +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 " 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 " text to make text files" + @echo " man to make manual pages" + @echo " changes to make an overview of all changed/added/deprecated items" + @echo " linkcheck to check all external links for integrity" + @echo " doctest to run all doctests embedded in the documentation (if enabled)" + +clean: + -rm -rf $(BUILDDIR)/* + +html: + $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html + @echo + @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." + +dirhtml: + $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml + @echo + @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." + +singlehtml: + $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml + @echo + @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." + +pickle: + $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle + @echo + @echo "Build finished; now you can process the pickle files." + +json: + $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json + @echo + @echo "Build finished; now you can process the JSON files." + +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." + +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/Ihatemoney.qhcp" + @echo "To view the help file:" + @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/Ihatemoney.qhc" + +devhelp: + $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp + @echo + @echo "Build finished." + @echo "To view the help file:" + @echo "# mkdir -p $$HOME/.local/share/devhelp/Ihatemoney" + @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/Ihatemoney" + @echo "# devhelp" + +epub: + $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub + @echo + @echo "Build finished. The epub file is in $(BUILDDIR)/epub." + +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)." + +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." + +text: + $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text + @echo + @echo "Build finished. The text files are in $(BUILDDIR)/text." + +man: + $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man + @echo + @echo "Build finished. The manual pages are in $(BUILDDIR)/man." + +changes: + $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes + @echo + @echo "The overview file is in $(BUILDDIR)/changes." + +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." + +doctest: + $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest + @echo "Testing of doctests in the sources finished, look at the " \ + "results in $(BUILDDIR)/doctest/output.txt." diff --git a/docs/_themes/.gitignore b/docs/_themes/.gitignore new file mode 100644 index 0000000..66b6e4c --- /dev/null +++ b/docs/_themes/.gitignore @@ -0,0 +1,3 @@ +*.pyc +*.pyo +.DS_Store diff --git a/docs/_themes/pelican b/docs/_themes/pelican new file mode 160000 index 0000000..449c214 --- /dev/null +++ b/docs/_themes/pelican @@ -0,0 +1 @@ +Subproject commit 449c2149aa36f89c2cfcf4039b9e9939cca19733 diff --git a/docs/api.rst b/docs/api.rst new file mode 100644 index 0000000..34edbdb --- /dev/null +++ b/docs/api.rst @@ -0,0 +1,163 @@ +The REST API +############ + +All of what's possible to do with the website is also possible via a web API. +This document explains how the API is organized and how you can query it. + +By default, the API talks JSON. There is no other way to speak with it +currently. + +Overall organisation +==================== + +You can access three different things: projects, members and bills. You can +also get the balance for a project. + +For the examples, I'm using curl, feel free to use whatever you want to do the +same thing, curl is not a requirement. + +Authentication +-------------- + +To interact with bills and members, and to do something else than creating +a project, you need to be authenticated. The only way to authenticate yourself +currently is using the "basic" HTTP authentication. + +If you don't want your credentials to pass in clear trought the network, you +can use the ssl endpoint at https://ihatemoney.notmyidea.org + +For instance, here is how to see the what's in a project, using curl:: + + $ curl --basic -u demo:demo http://ihatemoney.notmyidea.org/api/projects/demo + +Projects +-------- + +You can't list projects, for security reasons. But you can create, update and +delete one directly from the API. + +The URLs are `/api/projects` and `/api/projects/`. + +Creating a project +~~~~~~~~~~~~~~~~~~ + +A project needs the following arguments: + +* `name`: The project name (string) +* `id`: the project identifier (string without special chars or spaces) +* `password`: the project password / secret code (string) +* `contact_email`: the contact email + +:: + + $ curl -X POST https://ihatemoney.notmyidea.org/api/projects \ + -d 'name=yay&id=yay&password=yay&contact_email=yay@notmyidea.org' + "yay" + +As you can see, the API retuns the identifier of the project + +Getting information about the project +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Getting information about the project:: + + + $ curl --basic -u demo:demo http://ihatemoney.notmyidea.org/api/projects/demo + { + "name": "demonstration", + "contact_email": "demo@notmyidea.org", + "password": "demo", + "id": "demo", + "active_members": [{"activated": true, "id": 31, "name": "Arnaud"}, + {"activated": true, "id": 32, "name": "Alexis"}, + {"activated": true, "id": 33, "name": "Olivier"}, + {"activated": true, "id": 34, "name": "Fred"}], + "members": [{"activated": true, "id": 31, "name": "Arnaud"}, + {"activated": true, "id": 32, "name": "Alexis"}, + {"activated": true, "id": 33, "name": "Olivier"}, + {"activated": true, "id": 34, "name": "Fred"}], + } + + +Updating a project +~~~~~~~~~~~~~~~~~~ + +Updating a project is done with the `PUT` verb:: + + $ curl --basic -u yay:yay -X PUT\ + http://ihatemoney.notmyidea.org/api/projects/yay -d\ + 'name=yay&id=yay&password=yay&contact_email=youpi@notmyidea.org' + +Deleting a project +~~~~~~~~~~~~~~~~~~ + +Just send a DELETE request ont the project URI :: + + $ curl --basic -u demo:demo -X DELETE http://ihatemoney.notmyidea.org/api/projects/demo + +Members +------- + +You can get all the members with a `GET` on `/api/projects//members`:: + + $ curl --basic -u demo:demo http://ihatemoney.notmyidea.org/api/projects/demo/members\ + [{"activated": true, "id": 31, "name": "Arnaud"}, + {"activated": true, "id": 32, "name": "Alexis"}, + {"activated": true, "id": 33, "name": "Olivier"}, + {"activated": true, "id": 34, "name": "Fred"}] + +Add a member with a `POST` request on `/api/projects//members`:: + + $ curl --basic -u demo:demo -X POST\ + http://ihatemoney.notmyidea.org/api/projects/demo/members -d 'name=tatayoyo' + 35 + +You can also `PUT` a new version of a member (changing its name):: + + $ curl --basic -u demo:demo -X PUT\ + http://ihatemoney.notmyidea.org/api/projects/demo/members/36\ + -d 'name=yeaaaaah' + {"activated": true, "id": 36, "name": "yeaaaaah"} + +Delete a member with a `DELETE` request on `/api/projects//members/`:: + + $ curl --basic -u demo:demo -X DELETE\ + http://ihatemoney.notmyidea.org/api/projects/demo/members/35 + "OK + +Bills +----- + +You can get the list of bills by doing a `GET` on `/api/projects//bills` :: + + $ curl --basic -u demo:demo http://ihatemoney.notmyidea.org/api/projects/demo/bills + +Add a bill with a `POST` query on `/api/projects//bills`. you need the +following params: + +* `date`: the date of the bill. (yy-mm-dd) +* `what`: what have been payed +* `payer`: by who ? (id) +* `payed_for`: list of ids +* `amount`: amount payed + +Returns the id of the created bill :: + + $ curl --basic -u demo:demo -X POST\ + http://ihatemoney.notmyidea.org/api/projects/demo/bills\ + -d "date=2011-09-10&what=raclette&payer=31&payed_for=31&amount=200" + 80 + +You can also `PUT` a new version of the bill at +`/api/projects//bills/`:: + + $ curl --basic -u demo:demo -X PUT\ + http://ihatemoney.notmyidea.org/api/projects/demo/bills/80\ + -d "date=2011-09-10&what=raclette&payer=31&payed_for=31&amount=250" + 80 + +And you can of course `DELETE` them at `/api/projects//bills/`:: + + $ curl --basic -u demo:demo -X DELETE\ + http://ihatemoney.notmyidea.org/api/projects/demo/bills/80\ + "OK" diff --git a/docs/conf.py b/docs/conf.py new file mode 100644 index 0000000..17f693a --- /dev/null +++ b/docs/conf.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +import sys, os +templates_path = ['_templates'] +source_suffix = '.rst' +master_doc = 'index' + +project = u'I hate money' +copyright = u'2011, The \'I hate money\' team' + +version = '1.0' +release = '1.0' + +exclude_patterns = ['_build'] +pygments_style = 'sphinx' + +sys.path.append(os.path.abspath('_themes')) +html_theme_path = ['_themes'] +html_theme = 'pelican' +html_static_path = ['_static'] +html_theme_options = { 'nosidebar': True } diff --git a/docs/index.rst b/docs/index.rst new file mode 120000 index 0000000..89a0106 --- /dev/null +++ b/docs/index.rst @@ -0,0 +1 @@ +../README.rst \ No newline at end of file diff --git a/docs/make.bat b/docs/make.bat new file mode 100644 index 0000000..25a83ff --- /dev/null +++ b/docs/make.bat @@ -0,0 +1,170 @@ +@ECHO OFF + +REM Command file for Sphinx documentation + +if "%SPHINXBUILD%" == "" ( + set SPHINXBUILD=sphinx-build +) +set BUILDDIR=_build +set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% . +if NOT "%PAPER%" == "" ( + set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS% +) + +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. 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. changes to make an overview over all changed/added/deprecated items + echo. linkcheck to check all external links for integrity + echo. doctest to run all doctests embedded in the documentation if enabled + goto end +) + +if "%1" == "clean" ( + for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i + del /q /s %BUILDDIR%\* + goto end +) + +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\Ihatemoney.qhcp + echo.To view the help file: + echo.^> assistant -collectionFile %BUILDDIR%\qthelp\Ihatemoney.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" == "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" == "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" == "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 +) + +:end
{{ member.name }} - {% if balance[member] > 0 %}+{% endif %}{{ balance[member] }} + + {% if balance[member.id] > 0 %}+{% endif %}{{ balance[member.id] }} {% if member.activated %}{{ _("delete") }}{% else %}{{ _("reactivate") }}{% endif %}