Skip to content

Commit 3f9d697

Browse files
Merge pull request #77 from rigdenlab/development
Updates to GDPR, Travis CI and Docker deployment
2 parents 36b2809 + 05a049e commit 3f9d697

14 files changed

+246
-68
lines changed

.travis.yml

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
language: python
22

33
python:
4-
- "3.7"
4+
- "3.6"
55

66
cache: pip
77

88
install:
99
- sudo apt-get update
10-
- python3.7 -m pip install -r requirements.txt
10+
- python3.6 -m pip install -r requirements.txt
1111

1212
script:
13-
- python3.7 tests.py
13+
- python3.6 tests.py

app.py

+8
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,14 @@ def toggle_gdpr_policy_modal(n_clicks):
5959
return callback_utils.toggle_modal(trigger)
6060

6161

62+
@app.callback(Output('create-user-button', 'disabled'),
63+
[Input('gdpr-agreement-checkbox', 'checked')],
64+
[State('create-user-button', 'disabled')])
65+
def toggle_createuser_button(checked, disabled):
66+
trigger = dash.callback_context.triggered[0]
67+
return callback_utils.toggle_createuserbutton(trigger, disabled)
68+
69+
6270
@app.callback(Output({'type': 'tutorial-modal', 'index': MATCH}, 'is_open'),
6371
[Input({'type': 'tutorial-button', 'index': MATCH}, 'n_clicks')])
6472
def toggle_tutorial_modal(n_clicks):

components/__init__.py

+42
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,42 @@ def SessionStoreModal(*args, **kwargs):
2727
return SessionStoreModal(*args, **kwargs)
2828

2929

30+
def GdprPolicySectionOne(*args, **kwargs):
31+
from components.paragraphs import GdprPolicySectionOne
32+
33+
return GdprPolicySectionOne(*args, **kwargs)
34+
35+
36+
def GdprPolicySectionTwo(*args, **kwargs):
37+
from components.paragraphs import GdprPolicySectionTwo
38+
39+
return GdprPolicySectionTwo(*args, **kwargs)
40+
41+
42+
def GdprPolicySectionThree(*args, **kwargs):
43+
from components.paragraphs import GdprPolicySectionThree
44+
45+
return GdprPolicySectionThree(*args, **kwargs)
46+
47+
48+
def GdprPolicySectionFour(*args, **kwargs):
49+
from components.paragraphs import GdprPolicySectionFour
50+
51+
return GdprPolicySectionFour(*args, **kwargs)
52+
53+
54+
def GdprPolicySectionFive(*args, **kwargs):
55+
from components.paragraphs import GdprPolicySectionFive
56+
57+
return GdprPolicySectionFive(*args, **kwargs)
58+
59+
60+
def GdprPolicySectionSix(*args, **kwargs):
61+
from components.paragraphs import GdprPolicySectionSix
62+
63+
return GdprPolicySectionSix(*args, **kwargs)
64+
65+
3066
def TutorialOneModal(*args, **kwargs):
3167
from components.modals import TutorialOneModal
3268

@@ -39,6 +75,12 @@ def TutorialTwoModal(*args, **kwargs):
3975
return TutorialTwoModal(*args, **kwargs)
4076

4177

78+
def GdprAgreementCheckbox(*args, **kwargs):
79+
from components.inputgroups import GdprAgreementCheckbox
80+
81+
return GdprAgreementCheckbox(*args, **kwargs)
82+
83+
4284
def GdprPolicyModal(*args, **kwargs):
4385
from components.modals import GdprPolicyModal
4486

components/cards.py

+2-3
Original file line numberDiff line numberDiff line change
@@ -322,11 +322,10 @@ def CreateUserCard():
322322
components.InvalidNewUserCollapse(),
323323
html.Br(),
324324
dbc.Spinner(html.Div(id='success-create-user-alert-div')),
325+
components.GdprAgreementCheckbox(),
325326
html.Br(),
326-
dbc.Button("Create new user", color="primary", block=True, id='create-user-button'),
327-
html.Br(),
327+
dbc.Button("Create new user", color="primary", block=True, id='create-user-button', disabled=True),
328328
]),
329-
dbc.CardFooter([components.GdprPolicyModal(), components.GdprPolicyAlert()])
330329
])
331330

332331

components/inputgroups.py

+13
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
import dash_bootstrap_components as dbc
2+
import dash_html_components as html
23
from loaders import AdditionalDatasetReference
34
from parsers import ContactFormats
45
from components import EmailIssueReference, UserReadableTrackNames
56
from utils.plot_utils import DefaultTrackLayout
7+
from utils import UrlIndex
68

79

810
def ContactFormatSelector():
@@ -178,3 +180,14 @@ def SuperimposeSwitch(superimpose):
178180
),
179181
], className='mr-1'
180182
)
183+
184+
185+
def GdprAgreementCheckbox():
186+
return dbc.FormGroup(
187+
[
188+
dbc.Checkbox(id="gdpr-agreement-checkbox", className="form-check-input"),
189+
dbc.Label([
190+
"I read and agree to ConPlot ",
191+
dbc.CardLink(html.U("Privacy Policy"), href=UrlIndex.PRIVACY_POLICY.value)
192+
], html_for="standalone-checkbox", className="form-check-label", style={'margin-top': 2.5})
193+
], check=True)

components/modals.py

+10-59
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from components import StartNewSessionLink, GdprRightsList, AutomaticInfoCollectedList, CustomFormatFieldsHelpList
1+
import components
22
import dash_bootstrap_components as dbc
33
import dash_html_components as html
44
from utils import UrlIndex
@@ -86,7 +86,7 @@ def SessionTimedOutModal():
8686
dbc.ModalBody([
8787
html.P("""More than 15 minutes have passed since you last interacted with the website and your session has
8888
timed-out.""", style={'text-align': "justify"}),
89-
StartNewSessionLink()
89+
components.StartNewSessionLink()
9090
]),
9191
], id='missing-fields-modal', is_open=True, backdrop='static', keyboard=False)
9292

@@ -169,7 +169,7 @@ def CustomFormatDescriptionModal():
169169
'this file is intended to be used with. Subsequent lines indicate records to be added in the '
170170
'track, each defined using three fields, which are separated by white spaces:',
171171
style={"text-align": "justify"}),
172-
CustomFormatFieldsHelpList(),
172+
components.CustomFormatFieldsHelpList(),
173173
html.P('Bellow there is a sample of the first four lines of an example custom file. As you can see, '
174174
'this sample corresponds with a file created for a protein containing 168 residues. In this '
175175
'case, three records have been created, a record with color "3" that spans between residues 1 '
@@ -192,72 +192,23 @@ def GdprPolicyModal():
192192
dbc.ModalBody([
193193
dbc.Row([
194194
html.H4('1. Introduction'),
195-
html.P('ConPlot (also referred to as ‘we’ throughout this text) is committed to protect user data and '
196-
'privacy. The purpose of this text is to provide you with information about how the data we '
197-
'collect from users of ConPlot is used or shared. We may update this Privacy Notice from time '
198-
'to time. We encourage you re-visit this text frequently and take note of the date of the last '
199-
'update on the field above. We do not use or share any of your personal information for any '
200-
'purpose unrelated to the functionality of the website; however, we do collect some '
201-
'information to help us understand how our site is being used in order to improve community '
202-
'support and to enhance the ConPlot user’s experience when visiting our site.'
203-
, style={"text-align": "justify"}),
195+
components.GdprPolicySectionOne(),
204196
html.Br(),
205197
html.H4('2. Information Automatically Collected'),
206-
html.P(['When you browse ConPlot, certain information about your visit will be collected. We '
207-
'automatically collect and store the following type of information about your visit:',
208-
AutomaticInfoCollectedList(),
209-
'This automatically collected information does not identify you personally unless you include '
210-
'personally identifying information in a support form request; see the “Get in Touch” policy '
211-
'below for details. We use this information to measure the number of visitors to our site. '
212-
'The aggregate data may be included in prospectuses and reports to funding agencies.'
213-
], style={"text-align": "justify"}),
198+
components.GdprPolicySectionTwo(),
214199
html.Br(),
215200
html.H4('3. Information You Directly Provide'),
216-
html.P('Storing, sharing sessions and any other user account related features of the ConPlot requires '
217-
'that you register for an account. You will be required to provide an email address so we can '
218-
'send you your temporary account password in case you forget this password. An anonymous email '
219-
'service can be used if you do not want to provide personally identifying information. Your '
220-
'email address will not be used to send you alerts or notifications. Any email address '
221-
'provided in this site will only be used to get in touch with you in case your forget your '
222-
'password or you request assistance from us. We do not sell or distribute email addresses to '
223-
'third parties. We also ask for an user name when creating an account. If you share a session '
224-
'with another user, this user name will be displayed with the shared session. We will not sell '
225-
'or distribute your user name or institution to third parties. When you log in, the client IP '
226-
'address is recorded. This IP address can be correlated with the address automatically '
227-
'collected as noted above. If your user profile personally identifies you, then it may be '
228-
'possible to associate you with your detailed activity on the ConPlot website.',
229-
style={"text-align": "justify"}),
201+
components.GdprPolicySectionThree(),
230202
html.Br(),
231203
html.H4('4. "Get in Touch" Form'),
232-
html.P('The header on each ConPlot site includes a “Get in Touch” link to a form where users can '
233-
'submit general inquiries, bug reports or request assistance if they forget their passwords. '
234-
'Submissions through this form are emailed to members of the Rigden Lab at the University of '
235-
'Liverpool. The form includes a field for an email address. If the email address identifies you '
236-
'personally, say if you use your institutional email, then your correspondence with us will '
237-
'likewise be linked to you. A valid email is not strictly required, although we cannot reply to '
238-
'you without one. When you submit the form, your IP address and browser version will be '
239-
'recorded for internal use. In the case of reported bugs or other site errors, this '
240-
'information may be used by technical staff to help locate your session in the server logs to '
241-
'aid in troubleshooting the issue. This does have the side effect of making it possible to '
242-
'associate an IP address with an email address which may, in turn, personally identify you. '
243-
'However, ConPlot does not publicly release this information.', style={"text-align": "justify"}),
204+
components.GdprPolicySectionFour(),
244205
html.Br(),
245206
html.H4('5. How ConPlot uses cookies'),
246-
html.P('ConPlot uses cookies to associate multiple requests by your web browser into a stateful '
247-
'session. Cookies are essential to track the state of session. Some cookies persist only for a '
248-
'single session. The information is recorded temporarily and is erased when the user quits the '
249-
'session or closes the browser. Others may be persistently stored on the hard drive of your '
250-
'computer until you manually delete them from a browser folder or until they expire, which can '
251-
'be months after they were last used. Cookies can be disabled in your browser (refer to your '
252-
'browser’s documentation for instructions); however, the majority of the website functionality '
253-
'will be unavailable if cookies are disabled.', style={"text-align": "justify"}),
207+
components.GdprPolicySectionFive(),
254208
html.Br(),
255209
html.H4('6. Your Rights based on the General Data Protection Regulation (GDPR)'),
256-
html.P(['If you wish to know more about your rights under the General Data Protection Regulation '
257-
'(GDPR), you can do this ', html.A(html.U('here'), href=UrlIndex.GDPR_WEBSITE.value),
258-
'. Here is a summary of what this includes:'],
259-
style={"text-align": "left"}),
260-
GdprRightsList(),
210+
components.GdprPolicySectionSix(),
211+
components.GdprRightsList(),
261212
], justify='center', align='center', className='m-0'),
262213
]),
263214
], id='gdpr-policy-modal', is_open=False, size='xl', scrollable=True, centered=True, autoFocus=True)

components/paragraphs.py

+90
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
import dash_html_components as html
2+
from components import AutomaticInfoCollectedList
3+
from utils import UrlIndex
4+
5+
6+
def GdprPolicySectionOne():
7+
return html.P(
8+
'ConPlot (also referred to as ‘we’ throughout this text) is committed to protect user '
9+
'data and privacy. The purpose of this text is to provide you with information about '
10+
'how the data we collect from users of ConPlot is used or shared. We may update this '
11+
'Privacy Notice from time to time. We encourage you re-visit this text frequently '
12+
'and take note of the date of the last update on the field above. We do not use or '
13+
'share any of your personal information for any purpose unrelated to the '
14+
'functionality of the website; however, we do collect some information to help us '
15+
'understand how our site is being used in order to improve community support and to '
16+
'enhance the ConPlot user’s experience when visiting our site.'
17+
, style={"text-align": "justify"})
18+
19+
20+
def GdprPolicySectionTwo():
21+
return html.P(
22+
['When you browse ConPlot, certain information about your visit will be collected. We '
23+
'automatically collect and store the following type of information about your visit:',
24+
AutomaticInfoCollectedList(),
25+
'This automatically collected information does not identify you personally unless you '
26+
'include personally identifying information in a support form request; see the “Get '
27+
'in Touch” policy below for details. We use this information to measure the number '
28+
'of visitors to our site. '
29+
'The aggregate data may be included in prospectuses and reports to funding agencies.'
30+
], style={"text-align": "justify"})
31+
32+
33+
def GdprPolicySectionThree():
34+
return html.P(
35+
'Storing, sharing sessions and any other user account related features of ConPlot require '
36+
'that you register for an account. To register, you will be required to provide an email address, '
37+
'a username and a password. By choosing to create a user account you give us permission to retain this '
38+
'information, which will be used only for verification of you as a user and for anonymous '
39+
'statistics. We require an email address so we can send you a temporary account password in '
40+
'case you forget this password. An anonymous email service can be used if you do not want to provide '
41+
'personally identifying information. Your email address will not be used to send you alerts or '
42+
'notifications. Any email address provided in this site will only be used to get in touch with you '
43+
'in case your forget your password or you request assistance from us. We do not sell or distribute '
44+
'email addresses to third parties. We also ask for a user name when creating an account. We will '
45+
'not sell or distribute your user name or institution to third parties. When you log '
46+
'in, the client IP address is recorded. If your user profile personally identifies you, then it may be '
47+
'possible to associate you with your detailed activity on the ConPlot website. After registering as a '
48+
'user, by choosing to store a session you give us permission to retain the data you provided '
49+
'within that session. This includes the contents and file names of all the files you have uploaded '
50+
'to ConPlot, the name chosen for the session and the date in which the session was stored. If you '
51+
'share a session with another user, all this data will be accessible to the other user, and '
52+
'it will be displayed together with your username. All this information is visible by members of the '
53+
'ConPlot team, however it will only be used for development purposes as we will not publicly '
54+
'release this information or share it with any third parties.', style={"text-align": "justify"})
55+
56+
57+
def GdprPolicySectionFour():
58+
return html.P(
59+
'The header on each ConPlot site includes a “Get in Touch” link to a form where users can '
60+
'submit general inquiries, bug reports or request assistance if they forget their passwords. '
61+
'Submissions through this form are emailed to members of the Rigden Lab at the University of '
62+
'Liverpool. The form includes a field for an email address. If the email address identifies you '
63+
'personally, say if you use your institutional email, then your correspondence with us will '
64+
'likewise be linked to you. A valid email is not strictly required, although we cannot reply to '
65+
'you without one. When you submit the form, your IP address and browser version will be '
66+
'recorded for internal use. In the case of reported bugs or other site errors, this '
67+
'information may be used by technical staff to help locate your session in the server logs to '
68+
'aid in troubleshooting the issue. This does have the side effect of making it possible to '
69+
'associate an IP address with an email address which may, in turn, personally identify you. '
70+
'However, ConPlot does not publicly release this information.',
71+
style={"text-align": "justify"})
72+
73+
74+
def GdprPolicySectionFive():
75+
return html.P(
76+
'ConPlot uses cookies to associate multiple requests by your web browser into a stateful '
77+
'session. Cookies are essential to track the state of session. Some cookies persist only for a '
78+
'single session. The information is recorded temporarily and is erased when the user quits the '
79+
'session or closes the browser. Others may be persistently stored on the hard drive of your '
80+
'computer until you manually delete them from a browser folder or until they expire, which can '
81+
'be months after they were last used. Cookies can be disabled in your browser (refer to your '
82+
'browser’s documentation for instructions); however, the majority of the website functionality '
83+
'will be unavailable if cookies are disabled.', style={"text-align": "justify"})
84+
85+
86+
def GdprPolicySectionSix():
87+
return html.P([
88+
'If you wish to know more about your rights under the General Data Protection Regulation '
89+
'(GDPR), you can do this ', html.A(html.U('here'), href=UrlIndex.GDPR_WEBSITE.value),
90+
'. Here is a summary of what this includes:'], style={"text-align": "left"})

layouts/__init__.py

+6
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,12 @@ def noPage(*args, **kwargs):
1111
return noPage(*args, **kwargs)
1212

1313

14+
def PrivacyPolicy(*args, **kwargs):
15+
from layouts.privacypolicy import PrivacyPolicy
16+
17+
return PrivacyPolicy(*args, **kwargs)
18+
19+
1420
def Home(*args, **kwargs):
1521
from layouts.home import Home
1622

0 commit comments

Comments
 (0)