Skip to content

Commit a3d63b0

Browse files
authored
Merge pull request #11 from michel4j/add-track-stages
Add track stages
2 parents 1255e08 + 172ccf0 commit a3d63b0

File tree

141 files changed

+100360
-2088
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

141 files changed

+100360
-2088
lines changed

.dockerignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,4 +84,7 @@ venv.bak/
8484
.idea/
8585
junk/
8686
temp/
87+
deploy/*
88+
!deploy/*.conf
89+
!deploy/*.sh
8790

Dockerfile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ COPY deploy/run-server.sh /
77
COPY deploy/wait-for-it.sh /
88
ADD . /usonline
99

10+
1011
RUN set -ex && \
1112
apk add --no-cache --virtual libpq apache2-ssl apache2-mod-wsgi openssl bash sed py3-pip && \
1213
/usr/bin/python3 -m venv /venv && source /venv/bin/activate && \

VERSION

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
v1.0.1-22-g12d86ce
1+
v1.0.1-154-g72ce514

apps/agreements/fixtures/initial-data.yml

Lines changed: 16 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -2,63 +2,24 @@
22
pk: 1
33
fields:
44
created: 2016-07-21 16:15:43+00:00
5-
modified: 2024-12-01 19:25:13.272220+00:00
5+
modified: 2025-03-06 04:43:04.466828+00:00
66
start_date: null
77
end_date: null
88
code: 80ae507a-d622-4792-b376-f9f5b4b02ae0
9-
name: CLSI User Agreement
9+
name: User Agreement
1010
state: enabled
11-
description: All users wishing to perform experiments at the CLSI must read and
12-
accept the terms of this agreement. It outlines the conditions under which the
13-
CLSI provides access to it's facilities to the user.
14-
content: '<h4>Canadian Light Source Inc. (“CLSI”), in accordance with the AGREEMENT
15-
TO USE THE CANADIAN LIGHT SOURCE FACILITY between Canadian Light Source Inc.
16-
and your institution, permits you &nbsp;access to use of the Canadian Light
17-
Source. &nbsp;</h4><p><strong>As a condition of access</strong> to the Canadian
18-
Light Source Facility and its Services I agree to the following: &nbsp;</p><ul><li>I
19-
am an employee or authorized representative of the institution identified in
20-
my user profile.</li><li>I am requesting access to the Canadian Light Source
21-
in accordance with CLS User Access Policy.&nbsp;</li><li>I understands that
22-
I am are bound by the terms and conditions of the Agreement to Use the Canadian
23-
Light Source facility. &nbsp;</li><li>I will comply with the terms and conditions
24-
of each User Proposal Submission and Beam Time Request, including limiting access
25-
at the Facility to those persons for whom access is specified in such proposal.&nbsp;</li><li>I
26-
will comply with CLSI policies and procedures including those pertaining to
27-
Health, Safety and the Environment. &nbsp;</li><li>I will Inform CLSI in writing
28-
of the nature of the samples, materials and equipment, and the extent to which
29-
the same constitute a hazard including but not limited to biohazards, compressed
30-
gas, corrosiveness, dangerous reactive materials, flammability, oxidizer, radioactivity,
31-
toxicity or other hazards.</li><li>I will pay to CLSI the Fees pertaining to
32-
access and Services provided in the course of conduct of each User Proposal
33-
Submission and Beam Time Request.&nbsp;</li><li>That the User Institution’s
34-
policies regarding intellectual property apply to the activities conducted under
35-
the User Proposal Submissions and Beam Time Requests. &nbsp;</li><li><span style="line-height:
36-
1.4em; background-color: initial;">These policies do not extend to any technology,
37-
software, equipment, information, techniques, documentation or other materials
38-
relating to the Facility or its operation.</span><br></li><li><span style="line-height:
39-
1.4em; background-color: initial;">CLSI retains proportionate ownership of any
40-
Intellectual Property in the nature of the Services performed, analytical methods
41-
contributed to or conceived of by CLSI (but excluding resulting data), and other
42-
products generated by CLSI in performance of the Services for the User. &nbsp;</span><br></li><li><span
43-
style="line-height: 1.4em; background-color: initial;">The User warrants that,
44-
to the best of its knowledge, the activities contemplated under a User Proposal
45-
Submission and Beam Time Request will not infringe any Intellectual Property
46-
rights of third parties. &nbsp;</span><br></li><li>I will acknowledge CLSI in
47-
publications and report these publications to CLSI for public use. Publications
48-
should include the following acknowledgements:</li><li>1. “Research described
49-
in this paper was performed at the Canadian Light Source, which is supported
50-
by the Canadian Foundation for Innovation, Natural Sciences and Engineering
51-
Research Council of Canada, the University of Saskatchewan, the Government of
52-
Saskatchewan, Western Economic Diversification Canada, the National Research
53-
Council Canada, and the Canadian Institutes of Health Research.&nbsp;</li><li>I
54-
will provide to CLSI, summaries of research results that lead to knowledge transfer
55-
or technology transfer, including but not limited to: dissemination of best
56-
practices (e.g., in healthcare, education), improvements in professional practice
57-
(e.g., better teaching methods), disclosures of inventions, new product/process
58-
development, and patent applications/awards.&nbsp;</li><li>CLSI does not assume
59-
responsibility for delays that may arise in access to the Facility, reliance
60-
on Services or CLSI personnel and subsequent use of results and data generated
61-
by the research.</li><li>Both CLSI and the User agree to protect all information,
62-
that is identified as Confidential Information, and that has been disclosed
63-
by the other.&nbsp;</li></ul>'
11+
description: All users wishing to use this facility must accept the terms of this
12+
agreement. It outlines the conditions under which we provide access to you.
13+
content: "<p>By using this facility, you agree to:</p><ul><li><strong>Eligibility</strong>:
14+
You are authorized by your organization to use the facility.</li><li><strong>Rules</strong>:
15+
Follow all facility rules, including safety and proposal guidelines.</li><li><strong>Safety</strong>:
16+
Provide details of any hazardous materials you bring.</li><li><strong>Fees</strong>:
17+
Pay any applicable fees.</li><li><strong>Intellectual Property:</strong> Your
18+
organization's IP policies apply, except for facility-owned technology. Facility
19+
retains IP for its services and methods.</li><li><strong>No Infringement</strong>:
20+
Ensure your activities don't violate others' IP rights.</li><li><strong>Acknowledgement</strong>:
21+
Credit the facility in publications.</li><li><strong>Results</strong>: Share
22+
summaries of research results for knowledge transfer.</li><li><strong>No Liability</strong>:
23+
The facility is not liable for delays or research outcomes.</li><li><strong>Confidentiality</strong>:
24+
Protect confidential information.</li></ul>"
6425
roles: <reviewer><user>

apps/agreements/templates/agreements/blocks/agreements.html

Lines changed: 37 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -3,50 +3,57 @@
33
{% block panel_classes %}bg-paper{% endblock %}
44

55
{% block panel_content %}
6-
{% for agreement in agreements %}
6+
{% if agreements %}
77
<ul class="list-unstyled">
8-
<li class="tinytron bg-transparent">
9-
<h4 class="text-condensed"><strong>{{ agreement.name }}</strong></h4>
10-
<p>{{ agreement.description }}</p>
11-
<div class="row">
8+
{% for agreement in agreements %}
9+
<li class="bg-transparent row">
1210
<div class="col-xs-12">
11+
<div style="padding: 0 1em;">
12+
<h4 class="text-condensed"><strong>{{ agreement.name }}</strong></h4>
13+
<div>{{ agreement.description }}</div>
1314
<a href="{% url "accept-agreement" pk=agreement.pk %}"
1415
class="btn btn-primary btn-sm pull-right">View</a>
16+
</div>
1517
</div>
16-
</div>
17-
{% if not forloop.last %}
18-
<hr class="hr-xxs"/>
19-
{% endif %}
20-
</li>
18+
{% if not forloop.last %}
19+
<hr class="hr-xxs"/>
20+
{% endif %}
21+
</li>
22+
{% endfor %}
2123
</ul>
22-
{% endfor %}
24+
{% endif %}
2325
{% if no_institution %}
24-
<div class="tinytron">
26+
<div class="tinytron alert-danger">
2527
<h5>Institution Required</h5>
2628
<hr class="hr-xs"/>
2729
<p>In order to use the facility, all users must be affiliated with an institution which has a valid
2830
Institutional Agreement. You have not selected an institution. Please update your profile.</p>
2931
</div>
3032
{% endif %}
3133
{% if request_contact %}
32-
<div class="tinytron bg-transparent">
33-
<h5>Institutional Agreement Required</h5>
34-
<hr class="hr-xs"/>
35-
<p>In order to use the facility, all users must be affiliated with an institution which has a valid
36-
Institutional Agreement. The institution you have selected in your profile "{{ user.institution }}" does not have
37-
the appropriate agreement.
38-
</p>
39-
<p>
40-
To assist us in initiating the process, please provide the contact information of the person at your
41-
institution who is responsible for such matters.
42-
43-
</p>
44-
{% if user.institution %}
45-
<a href="#0" data-url="{% url 'request-institution-contact' user.institution.pk %}"
46-
class="btn btn-sm btn-default pull-right">
47-
<i class="bi-pencil icon-fw"></i>&nbsp;Institutional Contact
48-
</a>
49-
{% endif %}
34+
<div class="alert-warning" style="padding: 0.5em;">
35+
<div class="row">
36+
<div class="col-xs-12">
37+
<h5>Institutional Agreement Required</h5>
38+
<hr class="hr-xs"/>
39+
<p>In order to use the facility, all users must be affiliated with an institution which has a valid
40+
Institutional Agreement. The institution you have selected in your profile
41+
"{{ user.institution }}" does not have
42+
the appropriate agreement.
43+
</p>
44+
<p>
45+
To assist us in initiating the process, please provide the contact information of the person at
46+
your
47+
institution who is responsible for such matters.
48+
</p>
49+
{% if user.institution %}
50+
<a href="#0" data-url="{% url 'request-institution-contact' user.institution.pk %}"
51+
class="btn btn-sm btn-default pull-right">
52+
<i class="bi-pencil icon-fw"></i>&nbsp;Institutional Contact
53+
</a>
54+
{% endif %}
55+
</div>
56+
</div>
5057
</div>
5158
{% endif %}
5259

apps/beamlines/blocks.py

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import copy
2+
import itertools
3+
import operator
4+
import re
5+
from functools import reduce
6+
7+
from django.conf import settings
8+
from misc.blocktypes import BaseBlock, BLOCK_TYPES
9+
10+
11+
USO_FACILITY_STAFF_ROLE = getattr(settings, "USO_FACILITY_STAFF_ROLE", "staff:+")
12+
USO_FACILITY_ADMIN_ROLE = getattr(settings, "USO_FACILITY_ADMIN_ROLE", "admin:-")
13+
14+
15+
class MyFacilities(BaseBlock):
16+
block_type = BLOCK_TYPES.dashboard
17+
template_name = "beamlines/blocks/facilities.html"
18+
priority = 10
19+
20+
def render(self, context):
21+
from . import models
22+
ctx = copy.copy(context)
23+
user = context['request'].user
24+
# generate filters for staff and admin roles
25+
staff_pattern = re.compile(re.sub(r'[+-]$', '(?P<acronym>.+)$', USO_FACILITY_STAFF_ROLE))
26+
admin_pattern = re.compile(re.sub(r'[+-]$', '(?P<acronym>.+)$', USO_FACILITY_ADMIN_ROLE))
27+
staff_acronyms = list(itertools.chain(*[
28+
staff_pattern.findall(role) for role in user.get_all_roles() if staff_pattern.match(role)
29+
]))
30+
admin_acronyms = list(itertools.chain(*[
31+
admin_pattern.findall(role) for role in user.get_all_roles() if admin_pattern.match(role)
32+
]))
33+
acronyms = staff_acronyms + admin_acronyms
34+
if acronyms:
35+
filters = reduce(operator.__or__, [
36+
models.Q(acronym__iexact=acronym) for acronym in staff_acronyms
37+
], models.Q(pk__isnull=True))
38+
else:
39+
return None
40+
41+
facilities = models.Facility.objects.filter(filters)
42+
if facilities.exists():
43+
ctx.update({
44+
"facilities": facilities,
45+
})
46+
return super().render(ctx)

apps/beamlines/dynfields.py

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,8 @@ class TechnicalTags(FieldType):
2525
options = ['required', 'hide', 'nolabel']
2626
settings = ['label', 'options']
2727

28-
def clean(self, value, multi=False, validate=True):
29-
if not isinstance(value, dict):
30-
return {}
31-
if isinstance(value.get('facility'), list):
32-
value['facility'] = value['facility'][0]
33-
return value
28+
def clean(self, val, multi=False, validate=True):
29+
val = super().clean(val, multi=multi, validate=validate)
30+
if isinstance(val, str):
31+
val = int(val.strip())
32+
return val
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# Generated by Django 5.0.11 on 2025-03-06 16:44
2+
3+
import django.db.models.deletion
4+
from django.db import migrations, models
5+
6+
7+
class Migration(migrations.Migration):
8+
9+
dependencies = [
10+
('beamlines', '0003_initial'),
11+
]
12+
13+
operations = [
14+
migrations.AlterField(
15+
model_name='facility',
16+
name='parent',
17+
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='children', to='beamlines.facility', verbose_name='Parent Facility'),
18+
),
19+
]

apps/beamlines/models.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ class Facility(TimeStampedModel):
5656
source = models.CharField("Source Type", max_length=255, null=True, blank=True)
5757
state = models.CharField(max_length=20, choices=STATES, default=STATES.design)
5858
parent = models.ForeignKey(
59-
"Facility", null=True, related_name="children", verbose_name="Parent Facility", on_delete=models.SET_NULL
59+
"Facility", null=True, blank=True, related_name="children", verbose_name="Parent Facility", on_delete=models.SET_NULL
6060
)
6161
flex_schedule = models.BooleanField("Allocation Not Required", default=False)
6262
shift_size = models.IntegerField("Shift Size (hrs)", choices=SIZES, default=SIZES.eight)
@@ -126,7 +126,6 @@ def is_staff(self, user):
126126
return user.has_any_role(*_roles)
127127

128128
def is_admin(self, user):
129-
130129
_roles = self.expand_role(USO_FACILITY_ADMIN_ROLE)
131130
return user.has_any_role(*_roles)
132131

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
{% extends "misc/blocks/panel.html" %}
2+
{% load cycle_tags %}
3+
{% load beamline_tags %}
4+
{% block panel_title %}My Facilities{% endblock %}
5+
{% block panel_content %}
6+
{% if facilities.exists %}
7+
<div class="pad-wrapper">
8+
<div class="row scroll-box" style="max-height: 200px;">
9+
<ul class="list-unstyled list-ruled col-xs-12">
10+
{% for fac in facilities %}
11+
<li class="text-condensed" >
12+
<span class="pull-right">
13+
{% if user|is_admin:fac %}
14+
<span class="label label-info">Admin</span>
15+
{% elif user|is_staff:fac %}
16+
<span class="label label-success">Staff</span>
17+
{% endif %}
18+
</span>
19+
<a href='{% url "facility-detail" fac=fac.acronym %}' data-toggle="tooltip">
20+
<strong title="{{ fac.name }}">{{ fac.acronym }}</strong><span class="hidden-xs">&nbsp;&ndash;&nbsp;{{ fac.name }}</span>
21+
</a>
22+
</li>
23+
{% endfor %}
24+
</ul>
25+
</div>
26+
</div>
27+
{% endif %}
28+
{% endblock %}
29+

0 commit comments

Comments
 (0)