Skip to content

Commit 8c9c7b2

Browse files
authored
Added the functionality to record a GCP Billing Id attached to an event in the event model (#2375)
This feature allows addition of the gcp billing id to events for a more streamlined workflow of attaching shared billing ids to the cloud identities of the users participating in the events. This addresses the t-cairem [issue #18](T-CAIREM#18) The following key changes have been implemented: - Added a `gcp_billing_id` field in the events model & the migration for it. - Added a conditional rendering of Billing Accounts to choose from if the user has a cloud identity (only HDN users have cloud identities currently) - Implemented signals in the hdn-research-environment package to handle the changes in the EventApplication & CloudIdentity. Another part to be implemented alongside for the feature to work is : Bumping the hdn-research-environment-package(#2377).
2 parents 73c5da4 + 10870ab commit 8c9c7b2

File tree

8 files changed

+63
-5
lines changed

8 files changed

+63
-5
lines changed

physionet-django/events/apps.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
from django.apps import AppConfig
2+
from physionet.settings.base import ENABLE_CLOUD_RESEARCH_ENVIRONMENTS
23

34

45
class EventsConfig(AppConfig):
56
name = 'events'
7+
8+
def ready(self):
9+
if ENABLE_CLOUD_RESEARCH_ENVIRONMENTS:
10+
import environment.signals

physionet-django/events/fixtures/demo-events.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,4 +33,4 @@
3333
"allowed_domains": null
3434
}
3535
}
36-
]
36+
]

physionet-django/events/forms.py

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
from django import forms
2+
import logging
23

34
from events.widgets import DatePickerInput
45
from events.models import Event, EventApplication, EventAgreement, EventDataset, CohostInvitation
@@ -10,6 +11,8 @@
1011
(0, 'Decline')
1112
)
1213

14+
logger = logging.getLogger(__name__)
15+
1316

1417
class AddEventForm(forms.ModelForm):
1518
"""
@@ -30,6 +33,29 @@ def __init__(self, user, *args, **kwargs):
3033
self.host = user
3134
super(AddEventForm, self).__init__(*args, **kwargs)
3235

36+
# Add billing account choices if user has cloud identity
37+
if user and hasattr(user, 'cloud_identity'):
38+
self.fields['gcp_billing_id'] = forms.ChoiceField(
39+
required=False,
40+
label='Billing Account',
41+
help_text='Select a billing account to associate with this event.',
42+
choices=[('', 'No billing account')]
43+
)
44+
# Set initial value if updating an existing event
45+
if self.instance and self.instance.pk and self.instance.gcp_billing_id:
46+
self.initial['gcp_billing_id'] = self.instance.gcp_billing_id
47+
48+
try:
49+
# making a local import only if needed to avoid import issues in testing
50+
from environment.services import get_billing_accounts_list
51+
billing_accounts = get_billing_accounts_list(user)
52+
billing_choices = [('', 'No billing account')]
53+
for account in billing_accounts:
54+
billing_choices.append((account['id'], f"{account['name']} ({account['id']})"))
55+
self.fields['gcp_billing_id'].choices = billing_choices
56+
except Exception:
57+
logger.info("Unable to get billing accounts list.")
58+
3359
def clean(self):
3460
cleaned_data = super(AddEventForm, self).clean()
3561
if Event.objects.filter(title=cleaned_data['title'], host=self.host).exists():
@@ -44,16 +70,21 @@ def clean(self):
4470
def save(self):
4571
# Handle updating the event
4672
if self.initial:
73+
if 'gcp_billing_id' in self.cleaned_data:
74+
self.instance.gcp_billing_id = self.cleaned_data['gcp_billing_id']
4775
self.instance.save()
4876
else:
49-
Event.objects.create(title=self.cleaned_data['title'],
77+
event = Event.objects.create(title=self.cleaned_data['title'],
5078
category=self.cleaned_data['category'],
5179
host=self.host,
5280
description=self.cleaned_data['description'],
5381
start_date=self.cleaned_data['start_date'],
5482
end_date=self.cleaned_data['end_date'],
5583
allowed_domains=self.cleaned_data['allowed_domains']
5684
)
85+
if 'gcp_billing_id' in self.cleaned_data:
86+
event.gcp_billing_id = self.cleaned_data['gcp_billing_id']
87+
event.save()
5788

5889

5990
class EventApplicationResponseForm(forms.ModelForm):
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# Generated by Django 4.2.18 on 2025-03-14 19:40
2+
3+
from django.db import migrations, models
4+
5+
6+
class Migration(migrations.Migration):
7+
8+
dependencies = [
9+
("events", "0010_alter_event_options"),
10+
]
11+
12+
operations = [
13+
migrations.AddField(
14+
model_name="event",
15+
name="gcp_billing_id",
16+
field=models.CharField(blank=True, max_length=100, null=True),
17+
),
18+
]

physionet-django/events/models.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ class Event(models.Model):
2323
start_date = models.DateField(default=timezone.now)
2424
end_date = models.DateField(default=timezone.now)
2525
slug = models.SlugField(unique=True)
26+
gcp_billing_id = models.CharField(max_length=100, blank=True, null=True)
2627
allowed_domains = models.CharField(blank=True, null=True, validators=[
2728
validators.validate_domain_list], max_length=100)
2829

physionet-django/events/templates/events/event_detail.html

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,10 @@ <h4>Event Details</h4>
6464
</div>
6565
<hr>
6666

67+
{% if event.gcp_billing_id and event.host == user or user in event.get_cohosts %}
68+
<p><strong>Billing Account:</strong> {{ event.gcp_billing_id }}</p>
69+
{% endif %}
70+
6771
{% include "events/event_participation.html" %}
6872
<hr>
6973
{% if event_datasets|length != 0 %}

physionet-django/events/templates/events/event_home.html

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -120,8 +120,7 @@ <h5 class="modal-title">{{ info.title }}</h5>
120120
</div>
121121
</div>
122122
{% endfor %}
123-
{% endfor %}
124-
123+
{% endfor %}
125124

126125
<!-- Past events -->
127126

physionet-django/events/views.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,7 @@ def event_home(request):
133133

134134
event_application = form.save(commit=False)
135135
event = event_application.event
136-
# if user is not a host or a participant with cohort status, they don't have permission to accept/reject
136+
# if user is not a host or a participant with cohost status, they don't have permission to accept/reject
137137
if not (
138138
event.host == user
139139
or EventParticipant.objects.filter(

0 commit comments

Comments
 (0)