Skip to content

Commit b5ee0aa

Browse files
committed
Merge branch 'ft/update-docs' into develop
[SVCS-626] Closes: #341
2 parents 7304d3b + 0e1ef15 commit b5ee0aa

File tree

2 files changed

+78
-220
lines changed

2 files changed

+78
-220
lines changed

CONTRIBUTING.rst

Lines changed: 78 additions & 152 deletions
Original file line numberDiff line numberDiff line change
@@ -2,226 +2,152 @@
22
Contributing guidelines
33
***********************
44

5-
In general
6-
==========
7-
85
- `PEP 8`_, when sensible.
9-
- Test ruthlessly. Write docs for new features.
10-
- Even more important than Test-Driven Development--*Human-Driven Development*.
11-
- If you add an extension to setup.py, add it to supportedextensions.md.
12-
- Please update AUTHORS.rst when you contribute.
6+
- Test-driven: test ruthlessly and write docs for new features.
7+
- Human-driven: make sure any new logic is easy for others to understand.
8+
- If you add an extension to setup.py, add it to ``supportedextensions.md``.
9+
- Please update ``AUTHORS.rst`` when you contribute.
1310

1411
.. _`PEP 8`: http://www.python.org/dev/peps/pep-0008/
1512

16-
In particular
17-
=============
18-
19-
2013
Setting up for development
21-
--------------------------
14+
==========================
2215

23-
Clone the repo: ::
16+
Clone the repo:
17+
18+
.. code-block:: bash
2419
2520
$ git clone https://github.com/CenterForOpenScience/modular-file-renderer.git
2621
$ cd modular-file-renderer
2722
2823
Configure development environment and install the development dependencies.
2924

3025
.. note::
26+
Python 3.5 or greater, `R`_, and `pspp`_ are required.
27+
Python 3.6 is reccomended. It's recommended that a python version manager such as `pyenv`_ is used and that you use a virtual environment such as `pyenv-virtualenv`_ during development.
3128

32-
It is recommended that you use a `virtualenv`_ with `virtualenvwrapper`_ during development. Python 3.5 or greater, `R`_, and `pspp`_ are required.
33-
34-
.. _virtualenv: http://www.virtualenv.org/en/latest/
35-
.. _virtualenvwrapper: https://pypi.python.org/pypi/virtualenvwrapper
3629
.. _R: https://www.r-project.org/
3730
.. _pspp: https://www.gnu.org/software/pspp/
31+
.. _pyenv: https://github.com/pyenv/pyenv
32+
.. _pyenv-virtualenv: https://github.com/pyenv/pyenv-virtualenv
33+
34+
For Mac OS, here is an example of the commands that might be run to set up MFR. Linux users will probably do the same thing but with a different package manager. If someone wants to update this guide, please do.
3835

3936
.. code-block:: bash
4037
41-
# For Mac OS X: Install the latest version of python3.5
42-
$ brew install python3
4338
$ brew install r pspp
44-
45-
# Linux users, probably the same thing but with apt-get
46-
# If someone wants to update this guide, please do.
47-
48-
$ pip install virtualenv
49-
$ pip install virtualenvwrapper
50-
$ mkvirtualenv --python=`which python3` mfr
39+
$ pyenv virtualenv 3.6.4 mfr && echo mfr > .python-version
5140
$ pip install setuptools==30.4.0
5241
$ pip install invoke==0.13.0
5342
43+
Lastly, install MFR requirements with the development option.
44+
45+
.. code-block:: bash
5446
55-
Lastly, install mfr in development mode. ::
47+
$ inv install -d
48+
$ inv server
5649
57-
$ invoke install -d
58-
$ invoke server
59-
6050
Running tests
61-
-------------
51+
=============
52+
53+
To run all tests (requires ``pytest``)
6254

55+
.. code-block:: bash
6356
64-
To run all tests (requires pytest) ::
57+
$ inv test
6558
66-
$ invoke test
59+
You can also use ``pytest`` directly.
6760

68-
You can also use pytest directly. ::
61+
.. code-block:: bash
6962
70-
$ py.test
63+
$ py.test --cov-report term-missing --cov mfr tests
7164
7265
Writing tests
73-
-------------
66+
=============
7467

7568
Unit tests should be written for all rendering code.
7669

77-
Tests should be encapsulated within a class and written as functions, like so:
70+
Tests should be encapsulated within a class and written as functions. There are a few `pytest fixtures`_ to help you mock files. You can use them by simply including them as parameters to your test functions.
7871

7972
.. code-block:: python
8073
8174
# in test_myformat.py
8275
83-
from mfr_something import render
84-
85-
86-
def test_render_html():
87-
with open('testfile.mp4') as fp:
88-
assert render.render_html(fp) == '<p>rendered testfile.mp4</p>'
89-
90-
There are a few `pytest fixtures`_ to help you mock files. You can use them by simply including them as parameters to your test functions. For example, the ``fakefile`` fixture is a fake file-like object whose name and content you can set to any value.
76+
from mfr.extensions.my_extension.render import MyExtensionRenderer
9177
92-
The above test can be rewritten like so:
93-
94-
.. code-block:: python
95-
96-
# in test_myformat.py
78+
@pytest.fixture
79+
def metadata():
80+
return ProviderMetadata(
81+
'file_name',
82+
'.extension',
83+
'text/plain',
84+
'1234',
85+
'http://wb.osf.io/file/file_name.extension?token=1234'
86+
)
9787
98-
from mfr_something import render
88+
def test_render_html(extension, metadata, file_path, assets_url, export_url):
89+
assert MyExtensionRenderer(
90+
extension,
91+
file_metadata,
92+
file_path,
93+
assets_url
94+
).render() == '<p>Rendered file for my_extension</p>'
9995
100-
def test_render_html(fakefile):
101-
assert render.render_html(fakefile) == '<p>rendered testfile.mp4</p>'
102-
103-
.. _pytest fixtures: https://pytest.org/latest/fixture.html
96+
Check out `pytest`_ documentation to learn more about fixtures
10497

98+
.. _pytest fixtures: https://docs.pytest.org/en/latest/fixture.html
99+
.. _pytest: https://docs.pytest.org/en/latest/
105100

106101
Manual Local Testing
107-
--------------------
108-
109-
To make sure a new renderer is functioning properly, it's recommended that you try to render a file of that type locally.
110-
111-
First, change the default provider to HTTP (in `/mfr/server/settings.py`), then update the provider domain in the ``ALLOWED_PROVIDER_DOMAINS`` whitelist (a space-separated string):
112-
113-
.. code-block:: python
114-
115-
PROVIDER_NAME = config.get('PROVIDER_NAME', 'http')
116-
ALLOWED_PROVIDER_DOMAINS = config.get('ALLOWED_PROVIDER_DOMAINS', 'http://localhost:8000/')
117-
118-
Because the MFR is passed a url to render, you also need to be running an http server.
102+
====================
119103

120-
From a directory with a file you want to render:
104+
To make sure a new renderer is functioning properly, it's recommended that you try to render a file of that type locally. The easiest way to do this would be to use the ``docker-compose`` files available inside the osf repository to get the MFR running, and then it should be straightforward to interact with the service using a tool such as postman. Alternatively, if you are familiar with OSF and its services, you can run full OSF and render files directly with it.
121105

122-
.. code-block:: bash
123-
124-
python -m SimpleHTTPServer 8000
125-
126-
Or for python 3
127-
128-
.. code-block:: bash
129-
130-
python3 -m http.server 8000
131-
132-
With both the SimpleHTTPServer and the MFR server running, go to
133-
134-
.. code-block:: bash
135-
136-
http://localhost:7778/render?url=http://localhost:8000/[filename].[ext]
137-
138-
139-
Writing A File Format Package
140-
-----------------------------
106+
Writing an extension
107+
====================
141108

142-
There are two main pieces of a file format package are
143-
144-
- Your custom rendering and/or exporting code
145-
- Your :class:`FileHandler <mfr.core.FileHandler>`
109+
An extension provides a 'renderer' and/or an 'exporter', and is registered in setup.py to allow the plugin to load when it is needed. Renderers and exporters subclasses ``mfr.core.extension.BaseRenderer`` or ``mfr.core.extension.BaseExporter`` respectively. A renderer takes a file path and some file metadata and returns a string of HTML that provides a representation of the file. The logic for the rendering happens in a renderer's ``render()`` function. This is an abstract base class method, and thus is required for the implementation of a renderer. Similarly, ``BaseExporter`` has an ``export()`` method. This method should take a file and convert it to the desired output, and create the newly converted file at the ``ouput_file_path``.
146110

111+
Renderers have an abstract property ``file_required``. This is used to determine if the renderer needs the actual content of the file in order to render it. Renderers also have a property ``cache_result``; this is used to determine whether the ouput of the renderer may be cached to improve future requests for the rendered version of the file.
147112

148113
Rendering Code
149-
++++++++++++++++++++++++
150-
151-
Renderers are simply callables (functions or methods) that take a file as their first argument and return
114+
--------------
152115

153-
Here is a very simple example of function that takes a filepointer and outputs a render result with an HTML image tag.
116+
Renderers subclass ``mfr.core.extension.BaseRenderer``, and implement a render function, a ``file_required`` property, and a ``cache_result`` property.
154117

155118
.. code-block:: python
156119
157-
def render_img_tag(filepointer):
158-
filename = filepointer.name
159-
content = '<img src="{filename}" />'.format(filename=filename)
160-
return RenderResult(content)
120+
import os
161121
162-
You can also write renderers as methods.
163-
164-
.. code-block:: python
122+
from mako.lookup import TemplateLookup
165123
166-
# in mfr_video/render.py
124+
from mfr.core import extension
167125
168-
class VideoRenderer(object):
169126
170-
def render_html5_tag(self, fp):
171-
content = '<video src="{filename}"></video>'.format(filename=fp.name)
172-
return RenderResult(content)
127+
class ImageRenderer(extension.BaseRenderer):
173128
174-
def render_flash(self, fp):
175-
# ...
176-
pass
129+
TEMPLATE = TemplateLookup(
130+
directories=[
131+
os.path.join(os.path.dirname(__file__), 'templates')
132+
]).get_template('viewer.mako')
177133
134+
def render(self):
135+
return self.TEMPLATE.render(base=self.assets_url, url=self.url.geturl())
178136
179-
The FileHandler
180-
+++++++++++++++
181-
182-
A file handler is responsible for using your custom rendering and exporting code to actually render and export a file. When you call :func:`mfr.detect <mfr.detect>`, you receive a list of :class:`FileHandler <mfr.core.FileHandler>` classes.
183-
184-
Your FileHandler **must** define a ``detect`` method which, given a file object, returns whether or not it can handle the file.
185-
186-
**Your FileHandler class should be named Handler and should be defined in your `mfr_format/__init__.py` file.**
187-
188-
.. code-block:: python
189-
190-
# in mfr_image/__init__.py
191-
192-
from mfr import FileHandler, get_file_extension
193-
194-
# Your custom code
195-
from mfr_image.render import render_img_tag
196-
from mfr_image.export import ImageExporter
197-
198-
199-
class Handler(FileHandler):
200-
renderers = {
201-
'html': render_img_tag,
202-
}
203-
204-
exporters = {
205-
'png': ImageExporter().export_png,
206-
'jpg': ImageExporter().export_jpg,
207-
# ...
208-
}
209-
210-
def detect(self, fp):
211-
return get_file_extension(fp.name) in ['.jpg', '.png', ] # and so on
212-
137+
@property
138+
def file_required(self):
139+
return False
213140
141+
@property
142+
def cache_result(self):
143+
return False
214144
215145
Organization
216-
++++++++++++
217-
218-
Each package has its own directory. At a minimum, your package should include:
146+
------------
219147

220-
- ``__init__.py``: Where your :class:`FileHandler <mfr.core.FileHandler>`` subclass will live.
221-
- ``render-requirements.txt``: External dependencies for rendering functionality.
222-
- ``export-requirements.txt``: External dependencies for export functionality.
148+
Each plugin has its own directory. At a minimum, a plugin should include:
223149

224-
Apart from those files, you are free to organize your rendering and export code however you want.
150+
- ``__init__.py``: This should export the ``mfr.core.extensions.BaseExporter`` and ``mfr.core.extensions.BaseRenderer`` subclasses provided by the plugin
225151

226152
A typical extension plugin directory structure might look like this:
227153

@@ -258,7 +184,7 @@ A typical extension plugin directory structure might look like this:
258184

259185

260186
Documentation
261-
-------------
187+
=============
262188

263189
Contributions to the documentation are welcome. Documentation is written in `reStructured Text`_ (rST). A quick rST reference can be found `here <http://docutils.sourceforge.net/docs/user/rst/quickref.html>`_. Builds are powered by Sphinx_.
264190

docs/examples.rst

Lines changed: 0 additions & 68 deletions
This file was deleted.

0 commit comments

Comments
 (0)