Skip to content

Commit 0cdfbc5

Browse files
committed
Added new documentation and fixed typos of friends
1 parent 5b1a146 commit 0cdfbc5

File tree

7 files changed

+221
-6
lines changed

7 files changed

+221
-6
lines changed

docs/README.md

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
# Documentation
2+
3+
Here is the full documentation for the project at the moment. We have divided it into two types: documentation for
4+
configuring applications and documentation for development utilities.
5+
6+
## Configuring applications
7+
8+
## Development utilities
9+
10+
This utilities will be divided in generic utilities and app specific utilities.
11+
12+
### Generic
13+
14+
- **[BootstrapFormMixin](utility/bootstrap_form_mixin.md)**: A utility to assist in the rendering of a form using Bootstrap 5.
15+
- **[TabsViewMixin](utility/tabs_view_mixin.md)**: A utility to help the creation of necessary methods for displaying a view with tabs, which will automatically render when used in your views.
16+
- **[PermissionRequiredMixin](utility/permission_required_mixin.md)**: Improvement of the Django PermissionRequiredMixin class. Inherit this if you create new permission mixins please.
17+
18+
19+
### App specific
20+
21+
- **[Application Forms](utility/application_form.md) [Application App]**: Generic class to create types of applications (Hacker, Mentor, etc.) that integrates automatically the forms with the Application Model.
22+
- **[MessageServiceManager](utility/messages.md) [Event.Messages App]**: Explanation of how this services work and how to use it to send quick messages to the participants.

docs/utility/application_form.md

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
# Application Forms [+](/application/forms/base.py)
2+
3+
Despite having different types of application, all of them are based in a common structure, defined in the
4+
`ApplicationForm` class. Then, for each type (hacker, volunteer, mentor and sponsor) specific parameters are
5+
added. This class inherits the `BootstrapFormMixin`, so all the features of it are available.
6+
7+
Each class starts with `bootstrap_field_info`, where all the fields of the form that have to be rendered are described.
8+
First, it indicates the part of the form which will be modified (e.g. Personal Info, Hackathons,...). Then, each field
9+
is defined between brackets, detailing what information will be introduced (e.g. diet) and the space reserved in the
10+
screen. It's important to consider that the total length of a line is 12, therefore, a space of 4 would occupy 1/3 of
11+
the line. If a field is only visible for a certain previous answer, the developer should indicate the case when it will
12+
be shown(e.g. when diet is "Other", the applicant should specify their case.)
13+
14+
```python
15+
{'name': 'diet', 'space': 4},
16+
{'name': 'other_diet', 'space': 4, 'visible': {'diet': Application.DIET_OTHER}}
17+
```
18+
19+
If the information that contains the field was previously specified (e.g. diet, gender), it won't require additional
20+
methods. However, if a new one is introduced (e.g. `under_age` in `HackerForm`), the corresponding class will include a
21+
method defining the type of answer, if it's required, its possible choices (which sometimes may be defined as constants
22+
at the beginning of the file) and the default value amongst others.
23+
24+
```python
25+
under_age = forms.TypedChoiceField(
26+
required=True,
27+
label=_('How old are you?'),
28+
initial=False,
29+
coerce=lambda x: x == 'True',
30+
choices=((False, _('18 or over')), (True, _('Between 14 (included) and 18'))),
31+
widget=forms.RadioSelect
32+
)
33+
```
34+
The method `exclude_save` is used to not store the information of the specified fields. The checkboxes of those fields
35+
have to be accepted in order to participate in the event. Therefore, once the application is submitted, this field will
36+
always be `True`, so it's not necessary to consider that information anymore.
37+
38+
It's important to notice that all the text that will be printed out is surrounded by a low bash and parentheses. In
39+
future versions, this will allow to translate the displayed text in an easy way.
40+
41+
```python
42+
label = _('How old are you?')
43+
```

docs/utility/bootstrap_form_mixin.md

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
# BootstrapFormMixin [+](/app/mixins.py)
2+
BootstrapFormMixin is a class that allows an easy way to render any form type.
3+
4+
At the start of the class you will need to override the `bootstrap_field_info` where all the render information is stored.
5+
```python
6+
bootstrap_field_info = {'TITLE': {
7+
'fields': [{'name': 'FIELD_NAME', 'space': GRID_NUMBER, 'visible': VALUE_DICT},],
8+
'description': 'DESCRIPTION'},}
9+
```
10+
In this example the TITLE will be the h2 text of the formset that will contain all the fields on the array.
11+
The description is an optional text to be displayed after the TITLE.
12+
The FIELD_NAME will be the names of the fields of your form, this is required.
13+
GRID_NUMBER will be the grid (1-12) space that will fill your field in the Bootstrap row, if omitted the space will be 0.
14+
Lastly, the VALUE_DICT refers to a dictionary that will tell your field when can be visible, if omitted the field will always be visible.
15+
IMPORTANT: if you miss a field on the `bootstrap_field_info` it will set to not required.
16+
17+
An important feature of this form, is the use of the TypeHead lib for autocomplete CharFields, so the user has already some possible answers.
18+
This can be done by adding the `api_fields` .
19+
This information is stored in the `api_fields` to the Meta class inside your form, where the API url is required. Other optional fields are `restrict` and
20+
`others` field to restrict the options of the field or add the Others option. For example, in this application,
21+
it has been used to list all the possible countries and avoid custom answers.
22+
23+
```python
24+
class Meta(ApplicationForm.Meta):
25+
api_fields = {
26+
'country': {'url': static('data/countries.json'), 'restrict': True, 'others': True},
27+
'university': {'url': static('data/universities.json')},
28+
'degree': {'url': static('data/degrees.json')},
29+
}
30+
```
31+
32+
## Methods
33+
34+
### get_bootstrap_field_info
35+
36+
If you want to dynamically change the `bootstrap_field_info` you can override this method.
37+
It's important to not remove the initial call `super` to get a COPY of the `bootstrap_field_info` otherwise you will be overriding the full variable.
38+
You will also need to be cautious with `ModelForm` and understanding how forms work.
39+
40+
### set_read_only
41+
42+
This method is in order to make the form not editable. Keep in mind that you will need to disable the POST method on your view for this condition.

docs/utility/messages.md

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
# MessageServiceManager [+](/event/messages/services/manager.py)
2+
3+
This is a utility class to send messages (slack or/and more to be added) to the channels or users.
4+
This can be used to inform about some state about something to the users at the hackathon.
5+
6+
## Requirements
7+
8+
Set up MESSAGES_SERVICES for each service you want to use for your hackathon.
9+
10+
## Send message to user
11+
12+
- Import and call the constructor of the `MessageServiceManager` singleton
13+
- Call the `send_message_to_user` with the following parameters:
14+
15+
- **message (str)**: Message text to send to the user.
16+
- **user**: User instance from [django User model app](/user/models.py) of the user wanted to send a message to.
17+
- **sent_to_services** [Optional]: List of services names where you want to send the message. If not passed as argument it will be sent to all services.
18+
19+
```python
20+
from event.messages.services import MessageServiceManager
21+
from user.models import User
22+
23+
def some_view_or_another_method(request):
24+
manager = MessageServiceManager()
25+
user = User.objects.get(id=1) or request.user
26+
27+
# Message only sent to Slack
28+
manager.send_message_to_user(message='Test', user=user, sent_to_services=['SlackMessageService'])
29+
30+
# Message sent to all services
31+
manager.send_message_to_user(message='Test', user=user)
32+
```
33+
34+
## Send message to announcement channel
35+
36+
- Import and call the constructor of the `MessageServiceManager` singleton
37+
- Call the `make_announcement` with the following parameters:
38+
39+
- **message (str)**: Message text to send to the user.
40+
- **sent_to_services** [Optional]: List of services names where you want to send the message. If not passed as argument it will be sent to all services.
41+
- **error_callback** [Optional]: Function if the service throws an exception (the function will be executed asynchronous).
42+
43+
```python
44+
from event.messages.services import MessageServiceManager
45+
46+
manager = MessageServiceManager()
47+
48+
# Announcement only sent to all services
49+
manager.make_announcement(message='Announcement')
50+
51+
# Announcement only sent to Slack
52+
manager.make_announcement(message='Announcement', sent_to_services=['SlackMessageService'])
53+
54+
# Announcement only sent to Slack with a change to a model if throws error
55+
some_instance_from_a_model = None
56+
some_instance_from_a_model.status = 'Good'
57+
some_instance_from_a_model.save()
58+
59+
# Create error_callback on the go to save the instance location in memory
60+
def error_callback(e):
61+
some_instance_from_a_model.error = str(e)
62+
some_instance_from_a_model.status = 'Error'
63+
some_instance_from_a_model.save()
64+
65+
manager.make_announcement(message='Announcement', sent_to_services=['SlackMessageService'], error_callback=error_callback)
66+
```
67+
68+
## Create new service for messaging
69+
70+
We all know that Slack is the main service provided to communicate in a hackathon, but we are happy to integrate other
71+
new technologies as Discord or others. That's why you can easily create a new ServiceManager creating a class that
72+
inherits the [`ServiceAbstract`](/event/messages/services/base.py). Then you will need to override the abstract methods:
73+
`send_message` and `make_announcement`. We might integrate Discord soon.
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# PermissionRequiredMixin [+](/app/mixins.py)
2+
3+
This Mixin is mean to inherit any type of View class and expands the base [Django PermissionRequiredMixin](https://docs.djangoproject.com/en/4.1/topics/auth/default/#the-permissionrequiredmixin-mixin) class.
4+
The new functionality is that you can put a dictionary on the `permission_required` in order to change the permissions between the HTTP method: GET, POST, etc.
5+
6+
## Example
7+
8+
```python
9+
from django.views.generic import View
10+
11+
from app.mixins import PermissionRequiredMixin
12+
13+
14+
class SomeView(PermissionRequiredMixin, View):
15+
permission_required = {
16+
'GET': 'app.some_permission',
17+
'POST': 'app.some_other_permission'
18+
}
19+
```

docs/utility/tabs_view_mixin.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# TabsViewMixin [+](/app/mixins.py)
2+
3+
This is a simple Mixin to display Tab navigation on your TemplateView or any View class that inherits this class.
4+
The navigation tabs are the navigation that is displayed on top of your template container.
5+
6+
## Methods
7+
8+
### get_current_tabs
9+
This method is meant to be overridden and return an array to be with tuples. The first element of the tuple will be the text of the tab and the second will be the url, those 2 are mandatory.
10+
The 3rd is and optional variable that can be set to None to be omitted and displays a warning symbol.
11+
Finally, the 4th variable is to set the tab to active with a boolean, if None or unset the tab will be active if the path equals the url.
12+
Data can be passed to the method with the `get_context_data` kwargs at the super of your view.
13+
14+
### get_back_url
15+
Method that returns the url that will be updated to the context with the name `back`.

friends/templates/join_friends.html

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,13 @@
22
{% load i18n %}
33
{% block subtitle %}{% translate "Friends' group" %}{% endblock %}
44
{% block content %}
5-
<h1>{% translate "Friends' group" %}</h1>
5+
<h1>{% translate "Apply with friends" %}</h1>
6+
<p>{% translate "The friends group created is not required to be your final team for the hackathon." %}</p>
67
<form method="post" id="invite-form">
78
{% csrf_token %}
89
{% include 'components/bootstrap5_form.html' with form=friends_form %}
910
{% if friends_code %}
10-
<p>{% translate "Your group's code:" %} {{ friends_code.code }}</p>
11+
<p>{% translate "Your friend group's code:" %} {{ friends_code.code }}</p>
1112
<ul>
1213
{% for member in friends_code.get_members %}
1314
<li>{{ member.user.get_full_name }}</li>
@@ -16,11 +17,11 @@ <h1>{% translate "Friends' group" %}</h1>
1617
{% endif %}
1718
<div class="d-grid gap-2 col-lg-6 mx-auto mt-2">
1819
{% if friends_form %}
19-
<button class="btn btn-primary" name="action" value="join">{% translate 'Join' %}</button>
20-
<button class="btn btn-secondary" name="action" value="create">{% translate 'Create' %}</button>
20+
<button class="btn btn-primary" name="action" value="join">{% translate 'Join group' %}</button>
21+
<button class="btn btn-secondary" name="action" value="create">{% translate 'Create group' %}</button>
2122
{% else %}
22-
<button class="btn btn-danger" name="action" value="leave">{% translate 'Leave' %}</button>
23+
<button class="btn btn-danger" name="action" value="leave">{% translate 'Leave group' %}</button>
2324
{% endif %}
2425
</div>
2526
</form>
26-
{% endblock %}
27+
{% endblock %}

0 commit comments

Comments
 (0)