Skip to content

Commit

Permalink
Merge pull request #503 from lona-web-org/fscherf/setup-buckets
Browse files Browse the repository at this point in the history
setup buckets for file-uploads
  • Loading branch information
fscherf authored Nov 28, 2023
2 parents 0f5fbdb + 9361ac5 commit 33a2545
Show file tree
Hide file tree
Showing 23 changed files with 1,333 additions and 38 deletions.
Binary file added doc/content/api-reference/bucket-index.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
99 changes: 99 additions & 0 deletions doc/content/api-reference/buckets.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
is_template: False


Buckets
=======

The bucket API implements the server-side of file-uploads and can be used to
make files accessible over HTTP. It does not implement the browser-side, so it
is best used with a library like
`lona-dropzone </demos/file-upload/index.html>`_.

A bucket is basically a temporary directory, that gets created when a bucket
is initialized with a request object, and closed automatically when the view,
associated with the request, gets removed from the server.


Usage
-----

.. code-block:: python
from lona import View, Bucket, HTML, A
class BucketView(View):
def handle_request(self, request):
self.bucket = Bucket(
request=request,
on_add=self.on_add, # optional
on_delete=self.on_delete, # optional
)
return HTML(
A(
'Bucket'
href=self.bucket.get_url(), # link to the bucket
target='_blank', # open link in new tab
interactive=False,
),
)
def on_add(self, file_names):
# this method gets called whenever a file gets added (uploaded)
# `file_names` is a list of strings
pass
def on_delete(self, file_names):
# this method gets called whenever a file gets deleted
# `file_names` is a list of strings
pass
Regardless of the URL of the view that opened the bucket, all buckets are
accessible at ``/buckets/<request_id>/<bucket_id>``.

To upload a file, issue a multipart POST request to
``/buckets/<request_id>/<bucket_id>/add``.

To delete a file, issue a POST form request to
``/buckets/<request_id>/<bucket_id>/delete``. The bucket will search for the
form key ``name``.

The URL prefix (``/buckets/``) can be changed using
``settings.BUCKETS_URL_PREFIX``.

When ``Bucket.index`` is enabled, a generic frontend for listing, adding, and
deleting files for a bucket is available at
``/buckets/<request_id>/<bucket_id>``.

.. image:: bucket-index.png


Arguments
---------

.. api-doc:: lona.Bucket.__init__


Methods
-------

.. api-doc:: lona.Bucket.get_path
.. api-doc:: lona.Bucket.get_file_names
.. api-doc:: lona.Bucket.get_size
.. api-doc:: lona.Bucket.get_url
.. api-doc:: lona.Bucket.get_add_url
.. api-doc:: lona.Bucket.get_delete_url


Customization
-------------

The Bucket index page can be customized by overriding the template
``lona/bucket.html``.

.. code-block:: html
:include: ../../../lona/templates/lona/bucket.html
43 changes: 43 additions & 0 deletions doc/content/api-reference/middlewares.rst
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,12 @@ Middlewares can be live analyzed by using the
return data
def handle_http_request(self, data):
server = data.server
http_request = data.http_request
return data
def handle_connection(self, data):
server = data.server
http_request = data.http_request
Expand All @@ -53,6 +59,23 @@ Middlewares can be live analyzed by using the
return data
def on_view_stop(self, data):
server = data.server
connection = data.connection
request = data.request
view = data.view
reason = data.reason
return data
def on_view_cleanup(self, data):
server = data.server
connection = data.connection
request = data.request
view = data.view
return data
.. code-block:: python
# settings.py
Expand Down Expand Up @@ -82,6 +105,14 @@ Middleware.on_shutdown\(data\)
Gets called on server shutdown.


Middleware.handle_http_request\(data\)
--------------------------------------

Gets called with every incomming HTTP request, before any other routing or
handling happens. If ``data`` is not returned, Lona regards
``data.http_request`` as handled.


Middleware.handle_connection\(data\)
------------------------------------

Expand All @@ -105,3 +136,15 @@ If the data gets returned, the view associated with this request gets started.
If a `Response Object </api-reference/views.html#response-objects>`_
is returned, the view gets not started and the user gets the returned response
object shown.


Middleware.on_view_stop\(data\)
-------------------------------

Gets called before `View.on_stop </api-reference/views.html#lonaview-on-stop-reason>`_


Middleware.on_view_cleanup\(data\)
----------------------------------

Gets called before `View.on_cleanup </api-reference/views.html#lonaview-on-cleanup>`_
10 changes: 10 additions & 0 deletions doc/content/api-reference/settings.rst
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,16 @@ Channels
:path: lona.default_settings.CHANNEL_TASK_WORKER_CLASS


Buckets
-------

.. setting::
:name: BUCKETS_URL_PREFIX
:path: lona.default_settings.BUCKETS_URL_PREFIX

Default prefix for :link:`bucket </api-reference/buckets.rst>` urls


Middlewares
-----------

Expand Down
Binary file added doc/content/demos/file-upload/demo.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
63 changes: 63 additions & 0 deletions doc/content/demos/file-upload/demo.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
from datetime import datetime

from lona_picocss.html import ScrollerPre, HTML, Grid, Div, H1, A
from lona_picocss import install_picocss
from lona_dropzone import Dropzone

from lona import View, App

app = App(__file__)

install_picocss(app, debug=True)


@app.route('/')
class DropzoneView(View):
def handle_request(self, request):
self.dropzone = Dropzone(
request=request,
on_add=self.on_add,
on_delete=self.on_delete,
)

self.scroller = ScrollerPre(height='30em')

return HTML(
H1('Dropzone'),
Grid(
Div(
self.dropzone,
),
Div(
self.scroller,
A(
'Bucket URL',
href=self.dropzone.bucket.get_url(),
target='_blank',
interactive=False,
),
),
),
)

def on_add(self, file_names):
# this method gets called whenever a file gets added (uploaded)

self.scroller.append(
f'{datetime.now()}: on_add: {file_names=}\n',
)

self.show()

def on_delete(self, file_names):
# this method gets called whenever a file gets deleted

self.scroller.append(
f'{datetime.now()}: on_delete: {file_names=}\n',
)

self.show()


if __name__ == '__main__':
app.run()
25 changes: 25 additions & 0 deletions doc/content/demos/file-upload/index.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@


File Upload
===========

This demo showcases a file-uploads, using
:link:`lona-dropzone <https://github.com/lona-web-org/lona-dropzone>`, which
uses the :link:`bucket API </api-reference/buckets.rst>` internally.

.. image:: demo.gif


Install Dependencies
--------------------

.. code-block:: text
pip install lona lona-picocss
Source code
-----------

.. code-block:: python
:include: demo.py
2 changes: 1 addition & 1 deletion doc/content/roadmap.rst
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ Getting Feature Complete
- [ ] Add range input
- [ ] Add color input

- [ ] Add support for file uploads
- [x] Add support for file uploads
- [ ] Add support for JavaScript Modules


Expand Down
10 changes: 6 additions & 4 deletions doc/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@
['Counter', 'demos/counter/index.rst'],

['Bootstrap 5 Confirmation Popup',
'demos/bootstrap-5-confirmation-popup/index.rst'],
'demos/bootstrap-5-confirmation-popup/index.rst'],

['Function Plotter', 'demos/function-plotter/index.rst'],

Expand All @@ -65,6 +65,7 @@
['Multi User Chat', 'demos/multi-user-chat/index.rst'],
['Game Of Life', 'demos/game-of-life/index.rst'],
['Channels', 'demos/channels/index.rst'],
['File Upload', 'demos/file-upload/index.rst'],
]],

['API Reference', [
Expand All @@ -77,6 +78,7 @@
['Middlewares', 'api-reference/middlewares.rst'],
['Settings', 'api-reference/settings.rst'],
['Sessions', 'api-reference/sessions.rst'],
['Buckets', 'api-reference/buckets.rst'],
['Channels', 'api-reference/channels.rst'],
['Testing', 'api-reference/testing.rst'],
['Lona Shell', 'api-reference/lona-shell.rst'],
Expand All @@ -101,19 +103,19 @@
['Writing A Lona Form', 'cookbook/writing-a-lona-form.rst'],

['Writing A Traditional Form',
'cookbook/writing-a-traditional-form.rst'],
'cookbook/writing-a-traditional-form.rst'],

['URL Reverse Resolving', 'cookbook/url-reverse-resolving.rst'],
['Using Server State', 'cookbook/using-server-state.rst'],

['Integrating A Frontend Library',
'cookbook/integrating-a-frontend-library.rst'],
'cookbook/integrating-a-frontend-library.rst'],

['Integrating Django', 'cookbook/integrating-django.rst'],
['Limit Concurrent Views', 'cookbook/limit-concurrent-views.rst'],

['Adding A Custom Command To Lona Shell',
'cookbook/adding-a-custom-command-to-lona-shell.rst'],
'cookbook/adding-a-custom-command-to-lona-shell.rst'],
]],

['FAQ', 'faq.rst'],
Expand Down
2 changes: 2 additions & 0 deletions lona/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
from .view import View as LonaView
from .app import App as LonaApp
from .channels import Channel
from .request import Request
from .buckets import Bucket
from .view import View
from .app import App

Expand Down
Loading

0 comments on commit 33a2545

Please sign in to comment.