Skip to content

Commit bade0c5

Browse files
Merge pull request #49 from crpellegrino/adding_tom_registration_to_snex2_part_2
feat(users): enable tom_registration in snex2 with backwards db syncing.
2 parents c064189 + 40e6f20 commit bade0c5

File tree

7 files changed

+171
-25
lines changed

7 files changed

+171
-25
lines changed

custom_code/forms.py

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,14 @@
1010
from django.db.models.functions import Lower
1111
from django.core.exceptions import ValidationError
1212
from django.contrib.auth.models import Group
13+
try:
14+
from django.contrib.auth.forms import BaseUserCreationForm as UserCreationForm
15+
except ImportError:
16+
from django.contrib.auth.forms import UserCreationForm
17+
from django.contrib.auth.forms import UsernameField
18+
from django.contrib.auth.models import User, Group
19+
from django.db import transaction
20+
from crispy_forms.helper import FormHelper
1321

1422
class CustomTargetCreateForm(SiderealTargetCreateForm):
1523

@@ -59,6 +67,64 @@ def save(self, commit=True):
5967
)
6068

6169
return instance
70+
71+
class SNEx2UserCreationForm(UserCreationForm):
72+
"""
73+
Form used for creation of new users and update of existing users.
74+
"""
75+
email = forms.EmailField(required=True)
76+
groups = forms.ModelMultipleChoiceField(Group.objects.all(),
77+
required=False, widget=forms.CheckboxSelectMultiple)
78+
79+
class Meta:
80+
model = User
81+
fields = ('username', 'first_name', 'last_name', 'email', 'groups')
82+
field_classes = {'username': UsernameField}
83+
84+
def __init__(self, *args, **kwargs):
85+
super().__init__(*args, **kwargs)
86+
87+
self.helper = FormHelper()
88+
self.form_tag = False
89+
90+
def save(self, commit=True):
91+
# If any operations fail, we roll back
92+
with transaction.atomic():
93+
# Saving the MaterialRequisition first
94+
user = super(forms.ModelForm, self).save(commit=False)
95+
96+
# Because this form is used for both create and update user, and the user can be updated without modifying
97+
# the password, we check if the password field has been populated in order to set a new one.
98+
if self.cleaned_data['password1']:
99+
user.set_password(self.cleaned_data["password1"])
100+
if commit:
101+
# Saving the inline formsets
102+
user.save()
103+
self.save_m2m()
104+
105+
return user
106+
107+
# Also needs to be overridden in case any clean method are implemented
108+
def clean(self):
109+
super().clean()
110+
111+
return self.cleaned_data
112+
113+
# is_valid sets the cleaned_data attribute so we need to override that too
114+
def is_valid(self):
115+
is_valid = True
116+
is_valid &= super().is_valid()
117+
118+
return is_valid
119+
120+
# In case you're using the form for updating, you need to do this too
121+
# because nothing will be saved if you only update field in the inner formset
122+
def has_changed(self):
123+
has_changed = False
124+
125+
has_changed |= super().has_changed()
126+
127+
return has_changed
62128

63129

64130
class ReducerGroupWidget(forms.widgets.MultiWidget):

custom_code/hooks.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -945,7 +945,7 @@ def sync_users_with_snex1(user, created=False, old_username=''):
945945
Users.username==old_username
946946
).update(
947947
{'name': user.username,
948-
'pw': 'crypt$$'+user.password,
948+
# 'pw': 'crypt$$'+user.password,
949949
'firstname': user.first_name,
950950
'lastname': user.last_name,
951951
'email': user.email}

custom_code/views.py

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
from tom_targets.models import TargetList, Target, TargetExtra, TargetName
2020
from custom_code.models import TNSTarget, ScienceTags, TargetTags, ReducedDatumExtra, Papers, InterestedPersons, BrokerTarget
2121
from custom_code.filters import TNSTargetFilter, CustomTargetFilter, BrokerTargetFilter, BrokerTargetForm
22+
from custom_code.forms import SNEx2UserCreationForm
2223
from tom_targets.templatetags.targets_extras import target_extra_field
2324
from guardian.mixins import PermissionListMixin
2425
from guardian.models import GroupObjectPermission
@@ -58,6 +59,7 @@
5859
from tom_observations.cadence import get_cadence_strategy
5960
from tom_observations.facilities.lco import LCOSettings
6061
from tom_observations.views import ObservationCreateView, ObservationListView
62+
from tom_registration.registration_flows.approval_required.views import UserApprovalView
6163
import base64
6264

6365
import logging
@@ -326,6 +328,8 @@ def get_initial(self):
326328

327329
class CustomUserUpdateView(UserUpdateView):
328330

331+
form_class = SNEx2UserCreationForm
332+
329333
def get_success_url(self):
330334
"""
331335
Returns the redirect URL for a successful update. If the current user is a superuser, returns the URL for the
@@ -351,12 +355,20 @@ def dispatch(self, *args, **kwargs):
351355

352356
def form_valid(self, form):
353357
old_username = self.get_object().username
354-
print('IN NEW VIEW WITH USERNAME', old_username)
355358
super().form_valid(form)
356359
run_hook('sync_users_with_snex1', self.get_object(), False, old_username)
357360
return redirect(self.get_success_url())
358361

359362

363+
class SNEx2UserApprovalView(UserApprovalView):
364+
365+
def form_valid(self, form):
366+
response = super().form_valid(form)
367+
run_hook("sync_users_with_snex1", self.get_object(), True)
368+
369+
return response
370+
371+
360372
class CustomDataProductUploadView(DataProductUploadView):
361373

362374
form_class = CustomDataProductUploadForm

snex2/settings.py

Lines changed: 43 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -387,24 +387,44 @@
387387
# for example: OPEN_URLS = ['/', '/about']
388388
OPEN_URLS = ['/snex2/tnstargets/', '/pipeline-upload/photometry-upload/']
389389

390-
HOOKS = {
391-
'target_post_save': 'custom_code.hooks.target_post_save',
392-
'observation_change_state': 'tom_common.hooks.observation_change_state',
393-
'targetextra_post_save': 'custom_code.hooks.targetextra_post_save',
394-
'targetname_post_save': 'custom_code.hooks.targetname_post_save',
395-
'sync_observation_with_snex1': 'custom_code.hooks.sync_observation_with_snex1',
396-
'sync_sequence_with_snex1': 'custom_code.hooks.sync_sequence_with_snex1',
397-
'cancel_sequence_in_snex1': 'custom_code.hooks.cancel_sequence_in_snex1',
398-
'update_reminder_in_snex1': 'custom_code.hooks.update_reminder_in_snex1',
399-
'approve_sequence_in_snex1': 'custom_code.hooks.approve_sequence_in_snex1',
400-
'find_images_from_snex1': 'custom_code.hooks.find_images_from_snex1',
401-
'change_interest_in_snex1': 'custom_code.hooks.change_interest_in_snex1',
402-
'sync_paper_with_snex1': 'custom_code.hooks.sync_paper_with_snex1',
403-
'sync_comment_with_snex1': 'custom_code.hooks.sync_comment_with_snex1',
404-
'cancel_gw_obs': 'gw.hooks.cancel_gw_obs',
405-
'ingest_gw_galaxy_into_snex1': 'gw.hooks.ingest_gw_galaxy_into_snex1',
406-
'sync_users_with_snex1': 'custom_code.hooks.sync_users_with_snex1',
407-
}
390+
if DEBUG:
391+
HOOKS = {
392+
'target_post_save': '',
393+
'observation_change_state': '',
394+
'targetextra_post_save': '',
395+
'targetname_post_save': '',
396+
'sync_observation_with_snex1': '',
397+
'sync_sequence_with_snex1': '',
398+
'cancel_sequence_in_snex1': '',
399+
'update_reminder_in_snex1': '',
400+
'approve_sequence_in_snex1': '',
401+
'find_images_from_snex1': '',
402+
'change_interest_in_snex1': '',
403+
'sync_paper_with_snex1': '',
404+
'sync_comment_with_snex1': '',
405+
'cancel_gw_obs': '',
406+
'ingest_gw_galaxy_into_snex1': '',
407+
'sync_users_with_snex1': '',
408+
}
409+
else:
410+
HOOKS = {
411+
'target_post_save': 'custom_code.hooks.target_post_save',
412+
'observation_change_state': 'tom_common.hooks.observation_change_state',
413+
'targetextra_post_save': 'custom_code.hooks.targetextra_post_save',
414+
'targetname_post_save': 'custom_code.hooks.targetname_post_save',
415+
'sync_observation_with_snex1': 'custom_code.hooks.sync_observation_with_snex1',
416+
'sync_sequence_with_snex1': 'custom_code.hooks.sync_sequence_with_snex1',
417+
'cancel_sequence_in_snex1': 'custom_code.hooks.cancel_sequence_in_snex1',
418+
'update_reminder_in_snex1': 'custom_code.hooks.update_reminder_in_snex1',
419+
'approve_sequence_in_snex1': 'custom_code.hooks.approve_sequence_in_snex1',
420+
'find_images_from_snex1': 'custom_code.hooks.find_images_from_snex1',
421+
'change_interest_in_snex1': 'custom_code.hooks.change_interest_in_snex1',
422+
'sync_paper_with_snex1': 'custom_code.hooks.sync_paper_with_snex1',
423+
'sync_comment_with_snex1': 'custom_code.hooks.sync_comment_with_snex1',
424+
'cancel_gw_obs': 'gw.hooks.cancel_gw_obs',
425+
'ingest_gw_galaxy_into_snex1': 'gw.hooks.ingest_gw_galaxy_into_snex1',
426+
'sync_users_with_snex1': 'custom_code.hooks.sync_users_with_snex1',
427+
}
408428

409429

410430
BROKERS = {
@@ -497,7 +517,7 @@
497517
'REGISTRATION_AUTHENTICATION_BACKEND': 'django.contrib.auth.backends.AllowAllUsersModelBackend',
498518
'REGISTRATION_REDIRECT_PATTERN': 'home',
499519
'REGISTRATION_STRATEGY': 'approval_required',
500-
'SEND_APPROVAL_EMAILS': False,
520+
'SEND_APPROVAL_EMAILS': True,
501521
'APPROVAL_SUBJECT': f'Your {TOM_NAME} registration has been approved!', # Optional subject line of approval email, (Default Shown)
502522
'APPROVAL_MESSAGE': f'Your {TOM_NAME} registration has been approved. You can log in <a href="mytom.com/login">here</a>.' # Optional html-enabled body for approval email, (Default Shown)
503523
}
@@ -506,15 +526,15 @@
506526
('SNEx Secure', 'sne@lco.global')
507527
]
508528

529+
MANAGERS = [("SNe", "sne@lco.global")]
530+
EMAIL_SUBJECT_PREFIX = f'[{TOM_NAME}]'
509531
EMAIL_HOST = 'smtp.gmail.com'
510-
511532
EMAIL_PORT = 587
512-
513533
EMAIL_USE_TLS = True
514-
534+
EMAIL_USE_SSL = False
515535
EMAIL_HOST_USER = 'snex@lco.global'
516-
517536
EMAIL_HOST_PASSWORD = str(os.getenv('SNEX_EMAIL_PASSWORD', ''))
537+
EMAIL_BACKEND = "django.core.mail.backends.smtp.EmailBackend"
518538

519539
DATA_UPLOAD_MAX_MEMORY_SIZE = 7000000
520540

snex2/urls.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@
6969
path('nonlocalizedevents/sequence/<int:id>/obs/', EventSequenceGalaxiesTripletView.as_view(), name='nonlocalizedevents-sequence-triplets'),
7070
path('nonlocalizedevents/galaxies/<int:id>/obs/', GWFollowupGalaxyTripletView.as_view(), name='nonlocalizedevents-galaxies-triplets'),
7171
path('', include('tom_registration.registration_flows.approval_required.urls', namespace='registration')),
72+
path('snex2/accounts/approve/<int:pk>/', SNEx2UserApprovalView.as_view(), name="snex2-approve-user"),
7273
path('snex2/', include('custom_code.urls')),
7374
path('nonlocalizedevents/', include('tom_nonlocalizedevents.urls', namespace='nonlocalizedevents')),
7475
path('django_plotly_dash/', include('django_plotly_dash.urls')),
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
{% extends 'tom_common/base.html' %}
2+
{% load bootstrap4 static %}
3+
{% block title %}Approve user{% endblock %}
4+
{% block additional_css %}
5+
<link rel="stylesheet" href="{% static 'tom_targets/css/targets_snexclone.css' %}">
6+
{% endblock %}
7+
{% block content %}
8+
<form action="{% url 'snex2-approve-user' object.id %}" method="POST">
9+
{% csrf_token %}
10+
{% bootstrap_form form %}
11+
{% bootstrap_formset form.user_profile_formset %}
12+
{% buttons %}
13+
<button type="submit" class="btn btn-primary">
14+
Approve
15+
</button>
16+
{% endbuttons %}
17+
</form>
18+
{% endblock %}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
{% if request.user.is_superuser %}
2+
<h4>Pending Users</h4>
3+
<div class="row">
4+
<div class="col-md-12">
5+
<table class="table table-striped">
6+
<thead>
7+
<tr>
8+
<th>Username</th>
9+
<th>Name</th>
10+
<th>Email</th>
11+
<th>Approve</th>
12+
<th>Delete</th>
13+
</tr>
14+
</thead>
15+
<tbody>
16+
{% for user in users %}
17+
<tr>
18+
<td>{{ user.username }}</td>
19+
<td>{{ user.first_name }} {{ user.last_name }}</td>
20+
<td>{{ user.email }}</td>
21+
<td><a href="{% url 'snex2-approve-user' user.id %}" class="btn btn-primary">Approve</a></td>
22+
<td><a href="{% url 'user-delete' user.id %}" class="btn btn-danger">Delete</a></td>
23+
</tr>
24+
{% endfor %}
25+
</tbody>
26+
</table>
27+
</div>
28+
</div>
29+
{% endif %}

0 commit comments

Comments
 (0)