Skip to content

Commit

Permalink
Merge pull request #173 from USEPA/fix_response_request_form
Browse files Browse the repository at this point in the history
use group endpoint with paginator
  • Loading branch information
tbock authored Sep 10, 2024
2 parents 17a8303 + b67ecd9 commit 3cc2943
Show file tree
Hide file tree
Showing 11 changed files with 121 additions and 76 deletions.
14 changes: 8 additions & 6 deletions AGOLAccountRequestor/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
# See https://docs.djangoproject.com/en/2.1/howto/deployment/checklist/

# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = os.environ.get('SECRET_KEY')
SECRET_KEY = os.environ.get('SECRET_KEY', 'sssssh-its-a-secret')

# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = os.environ.get('DEBUG') == 'True'
Expand Down Expand Up @@ -193,14 +193,16 @@
EMAIL_HOST = os.environ.get('EMAIL_HOST', 'localhost')
EMAIL_FROM = os.environ.get('EMAIL_FROM', '[email protected]')

ADMINS = json.loads(os.environ.get('ADMINS', '[]'))
SERVER_EMAIL = "[email protected]"

LOGGING = DEFAULT_LOGGING

LOGGING['handlers']['slack'] = {
LOGGING['handlers']['mail_admins'] = {
'level': 'ERROR',
'filters': ['require_debug_false'],
'class': 'slack_logging.SlackExceptionHandler',
'bot_token': os.environ.get('SLACK_BOT_TOKEN', ''),
'channel_id': os.environ.get('SLACK_CHANNEL', '')
"class": "django.utils.log.AdminEmailHandler",
"include_html": False,
}

LOGGING['handlers']['file'] = {
Expand All @@ -211,7 +213,7 @@
}

LOGGING['loggers']['django'] = {
'handlers': ['console', 'slack', 'file'],
'handlers': ['console', 'mail_admins', 'file'],
'level': 'INFO',
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# Generated by Django 4.2.16 on 2024-09-10 20:20
from django.core.exceptions import ObjectDoesNotExist
from django.db import migrations, models

def set_auth_groups_for_roles(apps, schema_editor):
AGOLRole = apps.get_model('accounts', 'AGOLRole')
AGOLGroup = apps.get_model('accounts', 'AGOLGroup')
try:
r = AGOLRole.objects.get(pk=11)
if r.auth_groups.count() == 0:
groups = AGOLGroup.objects.filter(agol_id=1, is_auth_group=True).exclude(id='75a06d76-49b2-426f-b2a8-92f26fc5d1d1')
r.auth_groups.set(groups)
except ObjectDoesNotExist:
pass

try:
r = AGOLRole.objects.get(pk=12)
if r.auth_groups.count() == 0:
groups = AGOLGroup.objects.filter(id='75a06d76-49b2-426f-b2a8-92f26fc5d1d1')
r.auth_groups.set(groups)
except ObjectDoesNotExist:
pass


class Migration(migrations.Migration):
dependencies = [
('accounts', '0031_protecteddataset_alter_accountrequests_username_and_more'),
]

operations = [
migrations.RunPython(set_auth_groups_for_roles, migrations.RunPython.noop)
]
5 changes: 4 additions & 1 deletion accounts/serializers.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from rest_framework.fields import ReadOnlyField
from rest_framework.serializers import ModelSerializer, CharField, PrimaryKeyRelatedField, ChoiceField, \
JSONField, BooleanField
from .models import *
Expand Down Expand Up @@ -26,9 +27,11 @@ class Meta:


class SponsorSerializer(ModelSerializer):
title = ReadOnlyField()

class Meta:
model = User
fields = ['id', 'first_name', 'last_name', 'email']
fields = ['id', 'first_name', 'last_name', 'email', 'title']


# do NOT user with Sponsor viewset b/c that is unsecured and this exposes username
Expand Down
7 changes: 4 additions & 3 deletions accounts/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

from django.shortcuts import get_list_or_404, get_object_or_404, Http404
from django_filters.rest_framework import FilterSet, BooleanFilter, DateFilter, NumberFilter, BaseCSVFilter
from django.db.models import Q, Count, F
from django.db.models import Q, Count, F, Case
from django.template.response import TemplateResponse
from django.core.exceptions import ObjectDoesNotExist
from django.urls import resolve
Expand Down Expand Up @@ -235,7 +235,7 @@ class AGOLGroupViewSet(DALAutocompleteMixin, ReadOnlyModelViewSet):
serializer_class = AGOLGroupSerializer
ordering = ['title']
permission_classes = [IsAuthenticated]
pagination_class = None
# pagination_class = None
filterset_class = AGOLGroupFilterSet
search_fields = ['title']
autocomplete_config = {'field_walk': {'role': 'roles', 'portal': 'agol'}, 'display_field': F('title')}
Expand Down Expand Up @@ -298,7 +298,8 @@ def get_serializer_class(self):


class SponsorsViewSet(DALAutocompleteMixin, ReadOnlyModelViewSet):
queryset = User.objects.filter(agol_info__sponsor=True)
queryset = User.objects.filter(agol_info__sponsor=True)\
.annotate(title=Concat(F('first_name'), Value(' '), F('last_name')))
serializer_class = SponsorSerializer
ordering = ['last_name']
permission_classes = [IsAuthenticated]
Expand Down
5 changes: 4 additions & 1 deletion ui/cypress/e2e/specs.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,8 +81,11 @@ describe('approver workflow', () => {
cy.get('input[formcontrolname="name"]').type('Test')
cy.get('mat-select[formcontrolname="default_reason"]').click();
cy.get('mat-option').contains('Emergency Response').click();
cy.get('app-tag-input').type('test');
cy.get('app-tag-input[label="Groups"]').type('test');
cy.get('mat-option').first().click();
cy.get('mat-select[formcontrolname="role"]').click();
cy.get('mat-option').first().click();
cy.wait(4000);
cy.get('mat-select[formcontrolname="authoritative_group"]').click();
cy.get('mat-option').first().click();
cy.get('mat-dialog-content button').contains('Submit').scrollIntoView().click();
Expand Down
3 changes: 3 additions & 0 deletions ui/src/app/components/tag-input/tag-input.component.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import {MatLegacyInputModule} from '@angular/material/legacy-input';
import {MatLegacyChipsModule} from '@angular/material/legacy-chips';
import {MatLegacyFormFieldModule} from '@angular/material/legacy-form-field';
import {NoopAnimationsModule} from '@angular/platform-browser/animations';
import {BaseService} from '@services/base.service';
import {Observable} from 'rxjs';

describe('TagInputComponent', () => {
let component: TagInputComponent;
Expand All @@ -34,6 +36,7 @@ describe('TagInputComponent', () => {
beforeEach(() => {
fixture = TestBed.createComponent(TagInputComponent);
component = fixture.componentInstance;
component.tagsService = {getList: () => new Observable()} as BaseService
fixture.detectChanges();
});

Expand Down
13 changes: 6 additions & 7 deletions ui/src/app/components/tag-input/tag-input.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,11 @@ export class TagInputComponent implements OnInit {
@Input() tags: TagItem[];
@Input() label: '';
@Input() hint: '';
@Input() tagsService!: BaseService;

tagsCtrl = new FormControl([]);
filteredTags: Observable<Response>;
tagsService: BaseService;
allTags: Observable<Response>;
filteredTags: Observable<any[]>;
allTags: Observable<any[]>;
// for tag autocomplete... all may not be needed
separatorKeysCodes: number[] = [ENTER, COMMA];
visible = true;
Expand All @@ -43,11 +43,10 @@ export class TagInputComponent implements OnInit {
@ViewChild('tagInput', {static: false}) tagInput: ElementRef;

constructor(public http: HttpClient, public loadingService: LoadingService) {
this.tagsService = new BaseService('v1/agol/groups/all', this.http, this.loadingService);
this.allTags = this.tagsService.getList();
}

ngOnInit() {
this.allTags = this.tagsService.getList().pipe(map(r => r.results));
this.listenToTags();
}

Expand All @@ -57,11 +56,11 @@ export class TagInputComponent implements OnInit {
debounceTime(300),
switchMap((tag: string | null) => {
if (tag) {
return this.tagsService.getList({search: tag});
return this.tagsService.getList({search: tag}).pipe(map(r => r.results));
} else {
return this.allTags;
}
}),
})
// map((response) => {
// if (isArray(response)) {
// if (this.tags && this.tags.length > 0) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {iif, Observable, Subject} from 'rxjs';
import {finalize, map, switchMap, tap} from 'rxjs/operators';
import {UserConfig, UserConfigService} from '../../auth/user-config.service';
import {environment} from "@environments/environment";

import {Response as ServiceResponse} from "../../services/base.service"
export interface AgolGroup {
id: number;
title: string;
Expand Down Expand Up @@ -103,10 +103,10 @@ export class EditAccountPropsDialogComponent implements OnInit {

getGroups(response: number) {
if (response) {
this.http.get<AgolGroup[]>(`${environment.local_service_endpoint}/v1/agol/groups/`,
this.http.get<ServiceResponse>(`${environment.local_service_endpoint}/v1/agol/groups/`,
{params: new HttpParams().set('response', response.toString())}).pipe(
tap((res) => {
this.groups.next(res);
this.groups.next(res.results);
}),
finalize(() => this.editAccountPropsForm.controls.groups.enable())
).subscribe();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ <h4 *ngIf="userConfig.config | async as c" style="align-self: self-start;">
The sponsor for which you are a delegate was automatically included as a team coordinator.
</span>
<br/><br/>
This request will be for the {{c.portal}}. If that is incorrect login with the preferred system.
This request will be for the {{ c.portal }}. If that is incorrect login with the preferred system.
</h4>
<form novalidate [formGroup]="fieldTeamCoordErForm">
<!--Emergency Response Name-->
Expand All @@ -28,45 +28,48 @@ <h4 *ngIf="userConfig.config | async as c" style="align-self: self-start;">
<mat-label>Reason / Affiliation</mat-label>
<mat-select required formControlName="default_reason" placeholder="Reason / Affiliation">
<mat-option *ngFor="let r of reasons | async" [value]="r.value">
{{r.display_name}}
{{ r.display_name }}
</mat-option>
</mat-select>
</mat-form-field>
<!--Field team coordinators-->
<mat-form-field>
<mat-label>Team Coordinators</mat-label>
<mat-select formControlName="users" multiple>
<mat-option *ngFor="let field_coordinator of field_coordinators | async"
[value]="field_coordinator.id">
<span>
<ng-container *ngIf="field_coordinator.last_name && field_coordinator.first_name; then nameBlock else emailBlock"></ng-container>
<ng-template #nameBlock>{{field_coordinator.first_name + ' ' + field_coordinator.last_name}}</ng-template>
<ng-template #emailBlock>{{field_coordinator.email}}</ng-template>
</span>
</mat-option>
</mat-select>
</mat-form-field>
<!-- <mat-form-field>-->
<!-- <mat-label>Team Coordinators</mat-label>-->
<!-- <mat-select formControlName="users" multiple>-->
<!-- <mat-option *ngFor="let field_coordinator of field_coordinators | async"-->
<!-- [value]="field_coordinator.id">-->
<!-- <span>-->
<!-- <ng-container *ngIf="field_coordinator.last_name && field_coordinator.first_name; then nameBlock else emailBlock"></ng-container>-->
<!-- <ng-template #nameBlock>{{field_coordinator.first_name + ' ' + field_coordinator.last_name}}</ng-template>-->
<!-- <ng-template #emailBlock>{{field_coordinator.email}}</ng-template>-->
<!-- </span>-->
<!-- </mat-option>-->
<!-- </mat-select>-->
<!-- </mat-form-field>-->
<app-tag-input label="Team Coordinators" [tagsService]="sponsorsService" [tags]="[]"
(tagSelected)="fieldTeamCoordErForm.controls.users.setValue($event)"
hint="Select team coordinators.">
</app-tag-input>
<!-- groups -->
<app-tag-input label="Groups" (tagSelected)="fieldTeamCoordErForm.controls.assignable_groups.setValue($event)"
[tags]="this.tags" hint="Select all applicable groups.">
[tags]="this.tags" hint="Select all applicable groups." [tagsService]="groupService">
</app-tag-input>
<!-- role -->
<!-- role -->
<mat-form-field>
<mat-select required formControlName="role" placeholder="Role" (selectionChange)="getAuthGroups($event.value)">
<mat-option *ngFor="let r of roles | async" [value]="r.id">
{{r.name}}
{{ r.name }}
</mat-option>
</mat-select>
</mat-form-field>
<!--authoritative group-->
<mat-form-field *ngIf="(userConfig.config | async)?.portal_requires_auth_group">
<mat-select formControlName="authoritative_group" placeholder="Primary Auth Group" aria-label="Primary Auth Group">
<mat-option *ngFor="let authGroupChoice of auth_groups | async" [value]="authGroupChoice.id">
{{authGroupChoice.title}}
{{ authGroupChoice.title }}
</mat-option>
</mat-select>
</mat-form-field>

</form>
<br>
<button mat-raised-button color="primary" [disabled]="!fieldTeamCoordErForm.valid" (click)="submit()">
Expand Down
Loading

0 comments on commit 3cc2943

Please sign in to comment.