Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

events base structura and first DB objects #199

Open
wants to merge 63 commits into
base: develop
Choose a base branch
from
Open

events base structura and first DB objects #199

wants to merge 63 commits into from

Conversation

petrceska
Copy link
Contributor

@petrceska petrceska commented Sep 7, 2023

Summary by CodeRabbit

Release Notes

  • New Features

    • Enhanced user profile validation for Instagram usernames.
    • Added new forms for event management, including AddEventForm, OrganizerForm, and PlaceForm.
    • Introduced new event-related views for adding, updating, and deleting events, prices, and organizers.
    • New templates for event management, including detailed views, participant management, and event creation forms.
    • Implemented a reactive map widget for displaying geographical locations.
  • Bug Fixes

    • Improved handling of user roles and event participation states.
  • Style

    • Updated templates for better user experience and visual consistency.
  • Documentation

    • Added template tags for event management functionalities.

These updates enhance user interaction with the event management system, streamline processes, and improve overall functionality.

@erman-dev
Copy link
Contributor

Possible improvements: add the ability to add questions to the event.

petrceska and others added 15 commits September 27, 2023 23:34
# Conflicts:
#	fiesta/apps/accounts/migrations/0017_alter_userprofile_instagram.py
#	fiesta/apps/plugins/migrations/0014_alter_plugin_app_label.py
#	fiesta/apps/sections/migrations/0015_alter_sectionsconfiguration_required_gender_and_more.py
Copy link
Member

@thejoeejoee thejoeejoee left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

mostly:

  • forms: redundant form template definitions
  • styles: custom styling out of the concept of the rest of Fiesta
  • views: missing perms checks + CSFR checks

fiesta/apps/events/models/organizer.py Outdated Show resolved Hide resolved
fiesta/apps/events/models/organizer.py Outdated Show resolved Hide resolved
fiesta/apps/events/forms/organizer.py Outdated Show resolved Hide resolved
fiesta/apps/events/forms/organizer.py Outdated Show resolved Hide resolved
fiesta/apps/events/models/event.py Outdated Show resolved Hide resolved
fiesta/apps/events/views/price.py Show resolved Hide resolved
fiesta/apps/events/views/price.py Outdated Show resolved Hide resolved
fiesta/apps/events/views/price.py Show resolved Hide resolved
fiesta/apps/fiestaforms/widgets/models.py Outdated Show resolved Hide resolved
fiesta/apps/fiestaforms/widgets/models.py Outdated Show resolved Hide resolved
Co-authored-by: Josef Kolář <thejoeejoee@gmail.com>
@deawer234 deawer234 marked this pull request as ready for review December 6, 2023 13:18
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Inline review comments failed to post. This is likely due to GitHub's limits when posting large numbers of comments.

🛑 Comments failed to post (87)
fiesta/apps/events/templates/events/parts/update_place_button.html (2)

4-7: 🛠️ Refactor suggestion

Consider adding error handling and user feedback.

The button should handle potential errors during place updates and provide feedback to users.

Add error handling:

      x-data="modal($el.href)"
      x-bind="bind"
+     x-on:modal-error.window="$el.classList.add('btn-danger')"
+     x-on:modal-success.window="$el.classList.add('btn-success')"
      href="{% url "events:place-update" record.pk %}">
+     <span x-text="loading ? '{% trans "Updating..." %}' : '{% trans "Update" %}'"></span>
+     <template x-if="error">
+       <small class="text-danger" x-text="error"></small>
+     </template>

Committable suggestion was skipped due to low confidence.


4-7: ⚠️ Potential issue

Fix Bootstrap class naming and enhance button accessibility.

The button implementation has several areas for improvement:

  1. The Bootstrap class btn-outline btn-primary appears incorrect - it should be btn-outline-primary
  2. Missing accessibility attributes for better screen reader support
  3. Consider adding loading state handling for better UX

Apply these changes:

-  <a class="btn btn-sm btn-outline btn-primary"
+  <a class="btn btn-sm btn-outline-primary"
+     role="button"
+     aria-label="{% trans 'Update place' %}"
      x-data="modal($el.href)"
      x-bind="bind"
+     x-bind:class="{ 'opacity-50 cursor-wait': loading }"
+     x-bind:disabled="loading"
      href="{% url "events:place-update" record.pk %}">{% trans "Update" %}</a>
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

  <a class="btn btn-sm btn-outline-primary"
     role="button"
     aria-label="{% trans 'Update place' %}"
     x-data="modal($el.href)"
     x-bind="bind"
     x-bind:class="{ 'opacity-50 cursor-wait': loading }"
     x-bind:disabled="loading"
     href="{% url "events:place-update" record.pk %}">{% trans "Update" %}</a>
fiesta/apps/events/templates/events/parts/delete_place_button.html (1)

6-6: ⚠️ Potential issue

Fix incorrect confirmation message

The confirmation message asks about deleting a "price" but this template is for deleting places.

-hx-confirm="Are you sure you want to delete this price?"
+hx-confirm="Are you sure you want to delete this place?"
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

hx-confirm="Are you sure you want to delete this place?"
fiesta/apps/events/templates/events/parts/kick_participant_button.html (2)

3-9: ⚠️ Potential issue

Improve accessibility and semantics of the kick button

The current implementation has several areas for improvement:

  1. Using an anchor tag for an action button isn't semantically correct
  2. Missing accessibility attributes
  3. Generic confirmation message

Apply these changes to improve the implementation:

-<a href="#" 
-class="btn btn-sm btn-outline btn-primary"
+<button type="button"
+class="btn btn-sm btn-outline-primary"
+role="button"
+aria-label="{% translate 'Kick participant' %}"
hx-post="{% url "events:event-kick" pk=event.pk pkp=record.pk %}"
-hx-confirm="Are you sure you want to kick this participant ?"
+hx-confirm="{% translate 'Are you sure you want to remove this participant from the event? This action cannot be undone.' %}"
hx-swap="outerHTML">  
{% translate "Kick" %}
-</a>
+</button>
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

<button type="button"
class="btn btn-sm btn-outline-primary"
role="button"
aria-label="{% translate 'Kick participant' %}"
hx-post="{% url "events:event-kick" pk=event.pk pkp=record.pk %}"
hx-confirm="{% translate 'Are you sure you want to remove this participant from the event? This action cannot be undone.' %}"
hx-swap="outerHTML">  
{% translate "Kick" %}
</button>

3-9: 🛠️ Refactor suggestion

Consider adding success/error feedback

The current implementation doesn't provide visual feedback after the action completes.

Consider adding HX-Trigger to show a toast/notification:

<button type="button"
class="btn btn-sm btn-outline-primary"
role="button"
aria-label="{% translate 'Kick participant' %}"
hx-post="{% url "events:event-kick" pk=event.pk pkp=record.pk %}"
hx-confirm="{% translate 'Are you sure you want to remove this participant from the event? This action cannot be undone.' %}"
-hx-swap="outerHTML">
+hx-swap="outerHTML"
+hx-trigger="htmx:afterRequest"
+hx-on="htmx:afterRequest: if(event.detail.successful) showToast('{% translate "Participant removed successfully" %}')">

Committable suggestion was skipped due to low confidence.

fiesta/apps/events/templates/events/place_view.html (2)

2-2: ⚠️ Potential issue

Implement breadcrumb navigation.

The template loads the breadcrumbs tag but doesn't implement any breadcrumb navigation. This could affect user navigation and experience.

Add breadcrumb implementation after the template tags:

{% load breadcrumbs %}

+{% block breadcrumbs %}
+    {% breadcrumb "Home" "core:dashboard" %}
+    {% breadcrumb "Places" "events:places" %}
+{% endblock %}

Committable suggestion was skipped due to low confidence.


1-12: 🛠️ Refactor suggestion

Consider enhancing the template structure.

The current template only contains a single button. Consider adding:

  1. A heading to describe the page content
  2. A container/wrapper for better spacing and alignment
  3. A table or list view of existing places
 {% extends "fiestatables/page.html" %}
 {% load breadcrumbs %}

 {% load i18n %}
 {% block main %}
  {{ block.super }}

+ <div class="container my-4">
+  <div class="d-flex justify-content-between align-items-center mb-4">
+    <h2>{% trans "Places" %}</h2>
+    <a class="btn btn-sm btn-outline btn-primary"
+     x-data="modal($el.href)"
+     x-bind="bind"
+     href="{% url "events:place-add" %}">{% trans "Add" %}</a>
+  </div>
+
+  <div class="card">
+    <div class="card-body">
+      {% include "events/includes/places_table.html" %}
+    </div>
+  </div>
+ </div>
-  <a class="btn btn-sm btn-outline btn-primary"
-   x-data="modal($el.href)"
-   x-bind="bind"
-   href="{% url "events:place-add" %}">{% trans "Add" %}</a>
 {% endblock %}

Committable suggestion was skipped due to low confidence.

fiesta/apps/events/templates/events/participants_table.html (1)

7-10: ⚠️ Potential issue

Fix syntax and add safety checks in breadcrumb implementation.

Several issues need to be addressed:

  1. Add a space after 'as' in the URL template tag
  2. Add XSS protection for event.title
  3. Add handling for missing event object

Apply these changes:

-    {% url "events:event-detail" event.pk as url%}
-    {% breadcrumb_push_item_with_url event.title url%}
+    {% url "events:event-detail" event.pk as url %}
+    {% if event %}
+        {% breadcrumb_push_item_with_url event.title|escape url %}
+    {% endif %}

Committable suggestion was skipped due to low confidence.

fiesta/apps/events/templates/events/price.html (3)

1-1: ⚠️ Potential issue

Template structure needs improvement.

The template appears to be missing essential declarations:

  • DOCTYPE declaration
  • Template inheritance ({% extends %})

Add the following at the beginning of the file:

+{% extends "base.html" %}
 {% load django_tables2 %}

Committable suggestion was skipped due to low confidence.


13-16: 🛠️ Refactor suggestion

Enhance modal accessibility and security.

While the modal implementation works, consider these improvements:

  1. Add ARIA attributes for better accessibility
  2. Include CSRF protection for the modal form
  3. Add loading state handling for the button

Consider updating the button implementation:

 <a class="btn btn-success btn-sm btn-outline"
    href="{% url "events:price" event.pk %}"
    x-data="modal($el.href)"
-   x-bind="bind">{% translate "Add price" %}</a>
+   x-bind="bind"
+   role="button"
+   aria-haspopup="dialog"
+   x-bind:aria-expanded="open"
+   x-bind:disabled="loading"
+   data-csrf-token="{{ csrf_token }}">
+   <span x-show="!loading">{% translate "Add price" %}</span>
+   <span x-show="loading" class="spinner-border spinner-border-sm" role="status">
+     <span class="visually-hidden">{% translate "Loading..." %}</span>
+   </span>
+</a>
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

  <a class="btn btn-success btn-sm btn-outline"
     href="{% url "events:price" event.pk %}"
     x-data="modal($el.href)"
     x-bind="bind"
     role="button"
     aria-haspopup="dialog"
     x-bind:aria-expanded="open"
     x-bind:disabled="loading"
     data-csrf-token="{{ csrf_token }}">
     <span x-show="!loading">{% translate "Add price" %}</span>
     <span x-show="loading" class="spinner-border spinner-border-sm" role="status">
       <span class="visually-hidden">{% translate "Loading..." %}</span>
     </span>
  </a>

14-14: 💡 Codebase verification

URL pattern mismatch detected

The template uses {% url "events:price" event.pk %}, but the URL pattern in urls.py shows that the correct name for adding a price is "events:price" with a different URL structure: event-update/<uuid:pk>/price. While the name matches, the view expects a different URL structure.

  • In fiesta/apps/events/templates/events/price.html: The template should use the correct URL pattern that matches the view's URL structure.
🔗 Analysis chain

Verify URL pattern existence.

Ensure that the URL pattern 'events:price' exists and is properly configured in the URLconf.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for URL pattern definition
rg -A 5 "path.*events:price" --type py

Length of output: 37


Script:

#!/bin/bash
# Search for URL patterns in urls.py files within the events app
fd "urls.py" fiesta/apps/events/ --exec rg -l "price"

# If found, show the content of the matching files
fd "urls.py" fiesta/apps/events/ --exec cat {}

Length of output: 2467

fiesta/apps/accounts/migrations/0017_alter_userprofile_instagram.py (1)

17-17: 💡 Codebase verification

Additional Instagram username validations are recommended

The current validation only checks for allowed characters (^[\w_.]+$), but important Instagram username rules are missing:

  • Length limit (1-30 characters)
  • Cannot start or end with a period
  • Cannot have consecutive periods

Locations needing attention:

  • fiesta/apps/accounts/migrations/0017_alter_userprofile_instagram.py
  • fiesta/apps/accounts/models/profile.py (where the field is defined)

Note: The model definition shows the Instagram field is optional (blank=True), so these additional validations would only apply when a value is provided.

🔗 Analysis chain

Verify Instagram username validation rules.

The regex ^[\w_.]+$ correctly aligns with Instagram's username rules by:

  • Allowing letters, numbers, underscores, and periods
  • Removing hyphens which Instagram doesn't support

However, Instagram has additional rules we might want to consider:

  • Usernames must be between 1-30 characters
  • Cannot start or end with a period
  • Cannot have consecutive periods

Let's verify if we need these additional validations elsewhere in the codebase:


The previous search might have been too specific. These new searches will help us:

  1. Find any Instagram-related validation code with broader patterns
  2. Look for CharField definitions with validators
  3. Find all RegexValidator usage to see if there are similar patterns elsewhere

The migrations show the evolution of the Instagram field validation, but we need to check:

  1. The current model definition to see if there are additional validations
  2. Any forms that might handle Instagram validation
  3. Any custom validators that might be applied to the Instagram field

This will help us determine if the additional Instagram rules (length limits, period placement) are handled elsewhere in the codebase.


We need to find the actual UserProfile model definition since our previous searches didn't reveal it. The migrations show the field evolution, but we need to see the current model implementation to make a complete assessment of the Instagram validation rules.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for any existing Instagram username validations
rg -l "instagram.*validator" --type py

Length of output: 40


Script:

#!/bin/bash
# Let's try a broader search for Instagram-related validations
rg -i "instagram.*valid|valid.*instagram" --type py -A 3

# Also search for any CharField with validators
ast-grep --pattern 'models.CharField($$$, validators=[$_], $$$)'

# And check for any RegexValidator usage
rg "RegexValidator" --type py -A 2

Length of output: 5655


Script:

#!/bin/bash
# Let's find the actual model definition in the codebase
rg "instagram" --type py -g "models.py" -B 2 -A 2

# Also check for any forms that might handle Instagram validation
rg "instagram" --type py -g "forms.py" -B 2 -A 2

# Look for any custom validators in the utils directory
fd "validators?.py" --type f --exec cat {}

Length of output: 6742


Script:

#!/bin/bash
# Let's try to find the UserProfile model definition which should contain the instagram field
rg -l "class UserProfile" --type py

# Once found, let's look at its content
rg "class UserProfile" --type py -A 20

Length of output: 17222

fiesta/apps/events/migrations/0007_place_location.py (1)

17-17: 💡 Codebase verification

Default location should be moved to configuration

The hardcoded coordinates are indeed used in multiple places, but I found that the application already has a configurable location system in place. Specifically:

  1. The PickupSystemConfiguration model already implements configurable default locations through default_pickup_location field
  2. The events/places system should follow a similar pattern instead of using hardcoded coordinates

Key findings:

  • The hardcoded coordinates appear in migration files for both the events and pickup system
  • The pickup system has evolved to use a configurable location in PickupSystemConfiguration
  • The Place model in events app should be updated to use a similar configuration approach
🔗 Analysis chain

Verify the default location configuration

The hardcoded default coordinates (49.1922443,16.6113382) point to Brno, Czech Republic. Consider:

  1. Making this configurable through settings if the application will be deployed in different regions
  2. Whether a default location is necessary - it might be better to require explicit location setting

Let's check if these coordinates are used elsewhere in the codebase:


Let me check the content of these files to understand how these coordinates are used across the codebase.


Let me check if there are any configuration settings related to these default coordinates.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for hardcoded coordinates
rg -l "49\.1922443.*16\.6113382|16\.6113382.*49\.1922443"

Length of output: 292


Script:

#!/bin/bash
# Check the content of files containing the coordinates
rg -A 5 -B 5 "49\.1922443.*16\.6113382|16\.6113382.*49\.1922443"

Length of output: 3608


Script:

#!/bin/bash
# Search for location-related settings in settings files
fd "settings.*\.py" | xargs rg -i "location|coordinates|default.*location"

# Also check if there's any documentation about location configuration
fd "README|CONFIGURATION" -e md | xargs rg -i "location|coordinates"

Length of output: 10110

fiesta/apps/events/views/__init__.py (2)

3-3: ⚠️ Potential issue

Fix duplicate import and use consistent naming.

The AddPriceView is imported twice on the same line. Additionally, there seems to be a naming inconsistency as PriceDelete is listed in __all__ but not imported.

Apply this fix:

-from .price import AddPriceView, AddPriceView, DeletePriceView
+from .price import AddPriceView, DeletePriceView
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

from .price import AddPriceView, DeletePriceView
🧰 Tools
🪛 Ruff

3-3: Redefinition of unused AddPriceView from line 3

Remove definition: AddPriceView

(F811)


6-19: ⚠️ Potential issue

Remove undefined export and consider organizing exports.

  1. PriceDelete is listed in __all__ but is not imported.
  2. Consider organizing the exports alphabetically for better maintainability.

Apply these changes:

 __all__ = [
     "EventsIndexView",
     "AddEventView",
     "EventDetailView",
-    "UpdateEventView",
-    "PlaceView",
     "AddPlaceView",
-    "UpdatePlaceView",
-    "DeletePlaceView",
     "AddPriceView",
     "DeletePlaceView",
     "DeletePriceView",
-    "PriceDelete",
+    "PlaceView",
     "ParticipantsView",
+    "UpdateEventView",
+    "UpdatePlaceView",
 ]
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

__all__ = [
    "EventsIndexView",
    "AddEventView",
    "EventDetailView",
    "AddPlaceView",
    "AddPriceView",
    "DeletePlaceView",
    "DeletePriceView",
    "PlaceView",
    "ParticipantsView",
    "UpdateEventView",
    "UpdatePlaceView",
]
fiesta/apps/events/forms/price.py (1)

16-20: ⚠️ Potential issue

Fix typos in widget field names.

There are typos in the field names for the availability widgets. The current names "avilable_from" and "avilable_to" won't match the field names defined in the Meta.fields tuple.

Apply this fix:

         widgets = {
             "event": HiddenInput,
-            "avilable_from": DateTimeInput(attrs={'type': 'datetime-local'}),
-            "avilable_to": DateTimeInput(attrs={'type': 'datetime-local'}),
+            "available_from": DateTimeInput(attrs={'type': 'datetime-local'}),
+            "available_to": DateTimeInput(attrs={'type': 'datetime-local'}),
         }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

        widgets = {
            "event": HiddenInput,
            "available_from": DateTimeInput(attrs={'type': 'datetime-local'}),
            "available_to": DateTimeInput(attrs={'type': 'datetime-local'}),
        }
fiesta/apps/events/migrations/0010_alter_place_latitude_alter_place_longitude.py (1)

16-16: ⚠️ Potential issue

Consider removing default=0 for coordinates

Setting default=0 for latitude and longitude could be problematic as:

  • Coordinates (0,0) point to a valid location in the Gulf of Guinea
  • This makes it difficult to distinguish between unset locations and actual coordinates
  • It could lead to incorrect location display on maps

Consider either:

  1. Remove the default and handle null values in the application code, or
  2. Use null=True instead of a default value to explicitly indicate missing coordinates
-            field=models.FloatField(blank=True, default=0, verbose_name='latitude'),
+            field=models.FloatField(blank=True, null=True, verbose_name='latitude'),
-            field=models.FloatField(blank=True, default=0, verbose_name='longitude'),
+            field=models.FloatField(blank=True, null=True, verbose_name='longitude'),

Also applies to: 21-21

fiesta/apps/events/migrations/0011_remove_organizer_state_organizer_role.py (1)

17-21: 🛠️ Refactor suggestion

Consider improving role choices consistency and clarity.

The role field implementation has a few areas for improvement:

  1. The display value 'Event_leader' uses inconsistent casing and contains an underscore
  2. The abbreviation 'OC' might not be self-explanatory to all users

Consider applying these improvements:

-            field=models.CharField(choices=[('event_leader', 'Event_leader'), ('oc', 'OC')], default='oc', help_text='current role of the user on event', verbose_name='role'),
+            field=models.CharField(
+                choices=[
+                    ('event_leader', 'Event Leader'),
+                    ('oc', 'Organizing Committee')
+                ],
+                default='oc',
+                help_text='Specifies whether the user is an Event Leader or part of the Organizing Committee',
+                verbose_name='role'
+            ),
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

        migrations.AddField(
            model_name='organizer',
            name='role',
            field=models.CharField(
                choices=[
                    ('event_leader', 'Event Leader'),
                    ('oc', 'Organizing Committee')
                ],
                default='oc',
                help_text='Specifies whether the user is an Event Leader or part of the Organizing Committee',
                verbose_name='role'
            ),
        ),
fiesta/apps/events/migrations/0004_alter_participant_user.py (1)

19-19: 💡 Codebase verification

Potential issues with null user handling identified

Based on the code analysis, there are several places where null user cases are not properly handled:

  • In views/index.py, Participant.objects.filter(user=self.request.membership.user) assumes user is not null
  • In event_tags.py, is_participant template tag uses direct user comparison without null checks
  • In templates, participant counts and capacity checks (event.participants.all|length) don't account for null users
  • No explicit null handling found in participant queries and filters

Recommended fixes:

  • Add null checks in views/index.py when filtering participants
  • Update template tags to handle cases where participant.user is null
  • Review capacity calculation logic to properly account for null users
  • Add validation in participant-related queries to handle null users appropriately
🔗 Analysis chain

Verify null user handling in the application code.

Since the user field can now be null, ensure that:

  1. All queries and filters properly handle null user cases
  2. Templates have appropriate null checks
  3. Forms and serializers validate accordingly
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for participant user field usage to verify null handling
echo "Checking for participant user field usage in views..."
rg -l "participant.*user" --type py

echo "Checking for participant user field usage in templates..."
rg -l "participant.*user" --type html

echo "Checking for null checks in relevant files..."
rg "participant.*user.*(?:is None|not None)" --type py

Length of output: 793


Script:

#!/bin/bash
# Let's examine the actual usage of participant.user in the identified files
echo "Checking participant.user usage in views/index.py..."
rg -A 5 -B 5 "participant.*user" fiesta/apps/events/views/index.py

echo -e "\nChecking participant.user usage in event_tags.py..."
rg -A 5 -B 5 "participant.*user" fiesta/apps/events/templatetags/event_tags.py

echo -e "\nLet's also check for any participant queries or filters..."
ast-grep --pattern 'Participant.objects.filter($_)'
ast-grep --pattern 'Participant.objects.get($_)'

echo -e "\nChecking templates that might reference participants..."
fd -e html . | xargs rg "participant"

echo -e "\nChecking serializers and forms..."
fd -g "*serializer*.py" -g "*form*.py" . | xargs rg "participant"

Length of output: 9858

fiesta/apps/events/migrations/0009_place_latitude_place_longitude.py (2)

19-22: ⚠️ Potential issue

Consider using NULL as default instead of 0 for longitude.

Similar to latitude, using 0 as default for longitude could be misleading. Consider using null=True, default=None.

-            field=models.DecimalField(blank=True, decimal_places=6, default=0, max_digits=9, verbose_name='longitude'),
+            field=models.DecimalField(blank=True, null=True, decimal_places=6, default=None, max_digits=9, verbose_name='longitude'),
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

            model_name='place',
            name='longitude',
            field=models.DecimalField(blank=True, null=True, decimal_places=6, default=None, max_digits=9, verbose_name='longitude'),
        ),

13-17: ⚠️ Potential issue

Consider using NULL as default instead of 0 for latitude.

Setting default=0 for latitude means places without coordinates will appear to be located in the Gulf of Guinea (0°N 0°E). This could be misleading for data analysis or map displays. Consider using null=True, default=None instead to explicitly indicate missing coordinates.

-            field=models.DecimalField(blank=True, decimal_places=6, default=0, max_digits=9, verbose_name='latitude'),
+            field=models.DecimalField(blank=True, null=True, decimal_places=6, default=None, max_digits=9, verbose_name='latitude'),
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

        migrations.AddField(
            model_name='place',
            name='latitude',
            field=models.DecimalField(blank=True, null=True, decimal_places=6, default=None, max_digits=9, verbose_name='latitude'),
        ),
fiesta/apps/events/templates/events/parts/update_event_form.html (4)

10-14: ⚠️ Potential issue

Add validation for redirect URL

The redirect field implementation could be vulnerable to open redirect attacks. Consider validating the redirect URL against a whitelist of allowed destinations.

{% if redirect_field_value %}
+ {% if redirect_field_value|is_safe_url %}
  <input type="hidden"
         name="{{ redirect_field_name }}"
         value="{{ redirect_field_value }}" />
+ {% endif %}
{% endif %}

You'll need to implement the is_safe_url template filter to validate URLs against allowed hosts.

Committable suggestion was skipped due to low confidence.


19-24: 🛠️ Refactor suggestion

Enhance modal button with loading state

Consider adding loading indicators and error handling to improve user experience when adding new places.

 <a class="btn btn-primary btn-outline mr-6"
    href="{% url "events:eventplace-add" event.pk %}"
    x-data="modal($el.href)"
-   x-bind="bind">{% translate "Add new place" %}</a>
+   x-bind="bind"
+   x-on:click="loading = true"
+   x-on:modal-loaded="loading = false"
+   x-on:modal-error="loading = false; alert('{% translate "Failed to load form" %}')">
+   <span x-show="!loading">{% translate "Add new place" %}</span>
+   <span x-show="loading" class="spinner-border spinner-border-sm" role="status">
+     <span class="visually-hidden">{% translate "Loading..." %}</span>
+   </span>
+ </a>

Committable suggestion was skipped due to low confidence.


15-15: 🛠️ Refactor suggestion

Enhance form rendering with error handling

The current form rendering lacks error handling and validation feedback. Consider adding:

  • Form-level error messages
  • Field-level error display
  • Custom field rendering for better UX
- {{form}}
+ {% if form.non_field_errors %}
+   <div class="alert alert-danger">
+     {{ form.non_field_errors }}
+   </div>
+ {% endif %}
+ 
+ {% for field in form %}
+   <div class="form-group {% if field.errors %}has-error{% endif %}">
+     {{ field|as_label }}
+     {{ field|as_widget_field }}
+     {% if field.errors %}
+       <div class="text-danger">
+         {{ field.errors }}
+       </div>
+     {% endif %}
+     {% if field.help_text %}
+       <small class="form-text text-muted">{{ field.help_text }}</small>
+     {% endif %}
+   </div>
+ {% endfor %}

Committable suggestion was skipped due to low confidence.


3-3: 💡 Codebase verification

Remove unused template tags import

The as_widget_field and as_label template tags are imported but not used in this template. The form is rendered directly using {{form}} without using these field-specific formatting tags. These tags are actually used in the fiestaforms app's templates (field.html and classic.html) but not in this event form template.

  • Remove line: {% load as_widget_field as_label from fiestaforms %}
🔗 Analysis chain

Verify usage of imported template tags

The template loads as_widget_field and as_label from fiestaforms but these tags don't appear to be used in the template.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for usage of these template tags in related templates
rg -l "as_widget_field|as_label" --type html

Length of output: 233


Script:

#!/bin/bash
# Check the content of the update_event_form.html to see if these tags are actually used
cat fiesta/apps/events/templates/events/parts/update_event_form.html

Length of output: 909


Script:

#!/bin/bash
# Let's check the content of all related files to understand the usage pattern
rg -U "as_widget_field|as_label" --type html -A 5 -B 5

Length of output: 3295


Script:

#!/bin/bash
# Let's check if the form in update_event_form.html includes or extends any templates that might use these tags
rg -U "{% (?:include|extends) .*(?:classic|field)" fiesta/apps/events/templates/events/parts/update_event_form.html

Length of output: 235


Script:

#!/bin/bash
# Fix the regex syntax and check for template inheritance
rg -U "{%\s*(?:include|extends)\s+.*(?:classic|field)" fiesta/apps/events/templates/events/parts/update_event_form.html

# Also check how the form is rendered
rg -U "{{.*form.*}}" fiesta/apps/events/templates/events/parts/update_event_form.html

Length of output: 419

webpack/src/js/map-widget.js (2)

12-22: ⚠️ Potential issue

Several improvements needed for the map URL generation

The current implementation has several areas that could be improved:

  1. Missing input validation for parameters
  2. Unexplained magic number (0.05)
  3. No bounds checking for zoom level
  4. URL parameters should be properly encoded

Consider implementing these improvements:

 generateMapUrl(latitude, longitude, zoomLevel) {
+    // Validate inputs
+    if (!Number.isFinite(latitude) || latitude < -90 || latitude > 90) {
+        throw new Error('Invalid latitude: must be between -90 and 90');
+    }
+    if (!Number.isFinite(longitude) || longitude < -180 || longitude > 180) {
+        throw new Error('Invalid longitude: must be between -180 and 180');
+    }
+    if (!Number.isFinite(zoomLevel) || zoomLevel < 1 || zoomLevel > 100) {
+        throw new Error('Invalid zoom level: must be between 1 and 100');
+    }
+
+    // Calculate the view box size (0.05 represents the base delta at zoom level 1)
+    const BASE_DELTA = 0.05;
     const delta = 0.05 / zoomLevel;
     const bbox = [
         longitude - delta,  // left
         latitude - delta,   // bottom
         longitude + delta,  // right
         latitude + delta    // top
     ];
-    return `https://www.openstreetmap.org/export/embed.html?bbox=${bbox.join(',')}&layer=mapnik&marker=${latitude},${longitude}`;
+    // Ensure proper URL encoding of parameters
+    const bboxParam = encodeURIComponent(bbox.join(','));
+    const markerParam = encodeURIComponent(`${latitude},${longitude}`);
+    return `https://www.openstreetmap.org/export/embed.html?bbox=${bboxParam}&layer=mapnik&marker=${markerParam}`;
 },

Additionally, consider adding JSDoc documentation to explain the parameters and their constraints:

/**
 * Generates an OpenStreetMap URL with a marker and bounding box
 * @param {number} latitude - The latitude (-90 to 90)
 * @param {number} longitude - The longitude (-180 to 180)
 * @param {number} zoomLevel - The zoom level (1 to 100)
 * @returns {string} The generated map URL
 * @throws {Error} If any parameters are invalid
 */

3-6: ⚠️ Potential issue

Add input validation for locationData parameter

The component should validate the locationData parameter to ensure it contains the required latitude and longitude properties before using them.

Consider adding validation:

 Alpine.data('mapWidget', (locationData, initialZoomLevel = 30) => ({
+    init() {
+        if (!locationData?.latitude || !locationData?.longitude) {
+            throw new Error('locationData must contain valid latitude and longitude properties');
+        }
+        this.location = locationData;
+        this.zoomLevel = initialZoomLevel;
+        this.mapUrl = this.generateMapUrl(this.location.latitude, this.location.longitude, this.zoomLevel);
+    },
-    location: locationData,
-    zoomLevel: initialZoomLevel,
     mapUrl: '',

Committable suggestion was skipped due to low confidence.

fiesta/apps/events/forms/place.py (3)

1-4: ⚠️ Potential issue

Clean up unused imports and add future annotations.

Several imports are not being used in the code:

  • CharField
  • TextInput
  • gettext_lazy as _

Additionally, adding future annotations import would be beneficial for type hints.

Apply this diff to clean up the imports:

+from __future__ import annotations
-from django.forms import CharField, HiddenInput, TextInput
+from django.forms import HiddenInput
from apps.events.models import Place
from apps.fiestaforms.forms import BaseModelForm
-from django.utils.translation import gettext_lazy as _
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

from __future__ import annotations
from django.forms import HiddenInput
from apps.events.models import Place
from apps.fiestaforms.forms import BaseModelForm
🧰 Tools
🪛 Ruff

1-1: Missing required import: from __future__ import annotations

Insert required import: from __future__ import annotations

(I002)


1-1: django.forms.CharField imported but unused

Remove unused import

(F401)


1-1: django.forms.TextInput imported but unused

Remove unused import

(F401)


4-4: django.utils.translation.gettext_lazy imported but unused

Remove unused import: django.utils.translation.gettext_lazy

(F401)


6-14: ⚠️ Potential issue

Improve save method implementation.

The save method has a few issues:

  1. It ignores the commit parameter and always saves the instance
  2. Missing error handling for set_coordinates_from_url
  3. No type hints for better code maintainability

Apply this diff to improve the implementation:

 class PlaceForm(BaseModelForm):
-    def save(self, commit=True):
+    def save(self, commit: bool = True) -> Place:
         instance = super().save(commit=False)
         map_url = self.cleaned_data.get('map_link')
         if map_url:
-            instance.set_coordinates_from_url(map_url)
+            try:
+                instance.set_coordinates_from_url(map_url)
+            except ValueError as e:
+                self.add_error('map_link', str(e))
+                raise
 
-        instance.save()
-        return instance
+        if commit:
+            instance.save()
+        return instance
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

class PlaceForm(BaseModelForm):
    def save(self, commit: bool = True) -> Place:
        instance = super().save(commit=False)
        map_url = self.cleaned_data.get('map_link')
        if map_url:
            try:
                instance.set_coordinates_from_url(map_url)
            except ValueError as e:
                self.add_error('map_link', str(e))
                raise

        if commit:
            instance.save()
        return instance

16-29: 🛠️ Refactor suggestion

Enhance Meta class configuration.

The Meta class configuration has a few inconsistencies and could benefit from additional form field customization:

  1. latitude and longitude are defined in widgets but not in fields
  2. Missing field labels and help texts for better user experience

Consider applying these improvements:

     class Meta:
         model = Place
         fields = (
             'name',
             'description',
             'link',
             "section",
-            "map_link"
+            "map_link",
+            "latitude",
+            "longitude"
         )
+        labels = {
+            'name': _('Place Name'),
+            'description': _('Description'),
+            'link': _('Website Link'),
+            'map_link': _('Map URL'),
+        }
+        help_texts = {
+            'map_link': _('Enter a valid map URL to automatically set coordinates'),
+        }
         widgets = {
             "section": HiddenInput,
             "latitude": HiddenInput,
             "longitude": HiddenInput,
         }

Note: You'll need to restore the gettext_lazy import for the translations.

Committable suggestion was skipped due to low confidence.

fiesta/apps/events/migrations/0012_alter_organizer_user_alter_place_map_link.py (1)

21-25: 💡 Codebase verification

Consider escaping angle brackets and improving URL validation

The review comment about escaping angle brackets is valid, but there are broader concerns:

  1. The current URL validation in set_coordinates_from_url only extracts coordinates using regex r'([-\d.]+),([-\d.]+)' but doesn't validate the full Google Maps URL format.
  2. The help text should be updated to:
- help_text='Link to google maps in format google.com/maps/place/<place>/@<coordinates>/data'
+ help_text='Link to google maps in format google.com/maps/place/PLACE/@COORDINATES/data where COORDINATES should be latitude,longitude (e.g., 12.3456,-78.9012)'
  • fiesta/apps/events/models/place.py: Add full URL format validation in set_coordinates_from_url method
  • fiesta/apps/events/forms/place.py: Consider adding URL format validation in the form's clean method
🔗 Analysis chain

Consider escaping angle brackets in help_text

The help text format uses angle brackets which might be rendered as HTML tags in some contexts. Consider escaping them or using different notation.

-            field=models.CharField(blank=True, default='', help_text='Link to google maps in format google.com/maps/place/<place>/@<coordinates>/data', max_length=256, verbose_name='map link'),
+            field=models.CharField(blank=True, default='', help_text='Link to google maps in format google.com/maps/place/PLACE/@COORDINATES/data', max_length=256, verbose_name='map link'),

Additionally, consider:

  1. Adding validation for the Google Maps URL format
  2. Documenting the expected coordinate format (e.g., latitude,longitude)
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check if there's any URL validation for map_link
ast-grep --pattern 'def clean_map_link(self):
  $$$
'

Length of output: 55


Script:

#!/bin/bash
# Search for any validation related to map_link field
rg -A 5 "map_link" --type py

# Also check for any clean methods in Place model
ast-grep --pattern 'class Place$_:
  $$$
  clean($$$) {
    $$$
  }
  $$$
'

# Check if there's any form validation
rg -A 5 "class.*PlaceForm" --type py

Length of output: 7243


Script:

#!/bin/bash
# Check for set_coordinates_from_url method implementation
ast-grep --pattern 'def set_coordinates_from_url($$$):
  $$$
'

# Look for any URL validation patterns or regex
rg -A 5 "maps.*pattern|maps.*regex|validate.*map" --type py

Length of output: 588

fiesta/apps/events/models/organizer.py (1)

46-46: 🛠️ Refactor suggestion

Consider including the role in string representation.

Including the role would provide more context when this object is displayed.

-        return f"{self.user} - {self.event}"
+        return f"{self.user} ({self.get_role_display()}) - {self.event}"
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

        return f"{self.user} ({self.get_role_display()}) - {self.event}"
fiesta/apps/events/views/index.py (1)

14-22: 🛠️ Refactor suggestion

⚠️ Potential issue

Fix typo and improve method documentation and readability.

  1. There's a typo in darft_events (should be draft_events)
  2. The method would benefit from documentation explaining the context structure
  3. Consider optimizing database queries

Here's the suggested improvement:

     def get_context_data(self, **kwargs):
+        """Get context data for event listing.
+        
+        Returns:
+            dict with following keys:
+            - users_events: Events user is participating in
+            - upcoming_events: Published events user can join
+            - draft_events: Draft events in user's section
+            - participant: User's participant profile
+        """
         context = super().get_context_data(**kwargs)
+        
+        user = self.request.membership.user
+        section = self.request.in_space_of_section
 
-        context['users_events'] = Event.objects.filter(event_participants__user=self.request.membership.user).order_by('start')
-        context['upcoming_events'] = Event.objects.filter(section=self.request.in_space_of_section, state=Event.State.PUBLISHED).exclude(event_participants__user=self.request.membership.user).order_by('start')
-        context['darft_events'] = Event.objects.filter(section=self.request.in_space_of_section, state=Event.State.DRAFT).order_by('start')
-        context['participant'] = Participant.objects.filter(user=self.request.membership.user)
+        context.update({
+            'users_events': Event.objects.filter(
+                event_participants__user=user
+            ).select_related('section').order_by('start'),
+            
+            'upcoming_events': Event.objects.filter(
+                section=section,
+                state=Event.State.PUBLISHED
+            ).exclude(
+                event_participants__user=user
+            ).select_related('section').order_by('start'),
+            
+            'draft_events': Event.objects.filter(
+                section=section,
+                state=Event.State.DRAFT
+            ).select_related('section').order_by('start'),
+            
+            'participant': Participant.objects.filter(user=user).first()
+        })
         
         return context

The changes:

  1. Fixed the typo in darft_events
  2. Added comprehensive docstring
  3. Improved readability with better formatting
  4. Added select_related() to optimize database queries
  5. Used local variables for repeated values
  6. Changed participant to return single object since it's used as singular in the context
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

    def get_context_data(self, **kwargs):
        """Get context data for event listing.
        
        Returns:
            dict with following keys:
            - users_events: Events user is participating in
            - upcoming_events: Published events user can join
            - draft_events: Draft events in user's section
            - participant: User's participant profile
        """
        context = super().get_context_data(**kwargs)
        
        user = self.request.membership.user
        section = self.request.in_space_of_section

        context.update({
            'users_events': Event.objects.filter(
                event_participants__user=user
            ).select_related('section').order_by('start'),
            
            'upcoming_events': Event.objects.filter(
                section=section,
                state=Event.State.PUBLISHED
            ).exclude(
                event_participants__user=user
            ).select_related('section').order_by('start'),
            
            'draft_events': Event.objects.filter(
                section=section,
                state=Event.State.DRAFT
            ).select_related('section').order_by('start'),
            
            'participant': Participant.objects.filter(user=user).first()
        })
        
        return context
fiesta/apps/events/migrations/0002_alter_event_description_alter_event_subtitle_and_more.py (1)

23-37: 🛠️ Refactor suggestion

Consider URL validation for Place model link fields.

While the CharField with max_length=256 is appropriate for URLs, consider adding URL validation at the model level for both link and map_link fields.

Add validators to the model fields:

from django.core.validators import URLValidator

class Place(models.Model):
    link = models.CharField(
        blank=True,
        default='',
        max_length=256,
        validators=[URLValidator()],
        help_text='Link to the place',
        verbose_name='webpage link'
    )
    map_link = models.CharField(
        blank=True,
        default='',
        max_length=256,
        validators=[URLValidator()],
        help_text='Link to a position to the place on a map',
        verbose_name='map link'
    )
fiesta/apps/events/templates/events/dashboard_block.html (2)

32-42: ⚠️ Potential issue

Fix typo and enhance button accessibility.

  1. There's a typo in the endif tag (missing space).
  2. The buttons need accessibility improvements.

Apply these changes:

     {% if request.membership.is_local %}
     <div class="Dashboard__tile__actions text-right">
       <a href="{% url "events:add-event" %}"
-      class="btn btn-lg btn-outline">Create new event</a>
+      class="btn btn-lg btn-outline"
+      role="button"
+      aria-label="{% trans 'Create new event' %}">
+        {% trans "Create new event" %}
+      </a>
     </div>
     {% elif request.membership.is_international %}
     <div class="Dashboard__tile__actions text-right">
       <a href="{% url "events:index" %}"
-      class="btn btn-lg btn-outline">Show events</a>
+      class="btn btn-lg btn-outline"
+      role="button"
+      aria-label="{% trans 'Show all events' %}">
+        {% trans "Show events" %}
+      </a>
     </div>
-    {%endif %}
+    {% endif %}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

    {% if request.membership.is_local %}
    <div class="Dashboard__tile__actions text-right">
      <a href="{% url "events:add-event" %}"
      class="btn btn-lg btn-outline"
      role="button"
      aria-label="{% trans 'Create new event' %}">
        {% trans "Create new event" %}
      </a>
    </div>
    {% elif request.membership.is_international %}
    <div class="Dashboard__tile__actions text-right">
      <a href="{% url "events:index" %}"
      class="btn btn-lg btn-outline"
      role="button"
      aria-label="{% trans 'Show all events' %}">
        {% trans "Show events" %}
      </a>
    </div>
    {% endif %}

5-18: 🛠️ Refactor suggestion

Add empty state handling and format dates.

Consider the following improvements:

  1. Add a message when no events are available
  2. Format the start date using Django's date filter
 {% if request.membership.is_international %}
-    <div class="Dashboard__tile__title">Your Events</div>
+    <div class="Dashboard__tile__title">{% trans "Your Events" %}</div>

     <div class="Dashboard__tile__value">
+      {% if users_events %}
       {% for participating in users_events %}
       <div>
         <a href="{% url "events:event-detail" participating.id %}">
         <span class="text-3xl">{{ participating.title }}</span>
-        <p class="text-lg text-gray-300">{{ participating.start }}</p>
+        <p class="text-lg text-gray-300">{{ participating.start|date:"F j, Y" }}</p>
         </a>
       </div>
       {% endfor %}
+      {% else %}
+        <p class="text-lg text-gray-300">{% trans "No events found" %}</p>
+      {% endif %}
     </div>
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

    {% if request.membership.is_international %}
    <div class="Dashboard__tile__title">{% trans "Your Events" %}</div>

    <div class="Dashboard__tile__value">
      {% if users_events %}
      {% for participating in users_events %}
      <div>
        <a href="{% url "events:event-detail" participating.id %}">
        <span class="text-3xl">{{ participating.title }}</span>
        <p class="text-lg text-gray-300">{{ participating.start|date:"F j, Y" }}</p>
        </a>
      </div>
      {% endfor %}
      {% else %}
        <p class="text-lg text-gray-300">{% trans "No events found" %}</p>
      {% endif %}
    </div>
fiesta/apps/events/services/participant_validator.py (2)

17-31: 🛠️ Refactor suggestion

Enhance documentation and add transaction management.

While the validation logic is sound, the method could benefit from improved documentation and transaction handling.

Consider these improvements:

-    def register_for_event(self, user, event):
+    def register_for_event(self, user, event) -> PriceVariant:
         """
-        Method to validate registration for an event.
+        Validate user's eligibility to register for an event.
+        
+        Args:
+            user: The user attempting to register
+            event: The event to register for
+            
+        Returns:
+            PriceVariant: The applicable price variant for the registration
+            
+        Raises:
+            ValidationError: If registration is not possible due to:
+                - Event being full
+                - User already registered
+                - No applicable price variant available
         """
+        from django.db import transaction
+        
+        with transaction.atomic():
+            # Lock the event row to prevent race conditions on capacity check
+            event = event.__class__.objects.select_for_update().get(pk=event.pk)
+            
             if Participant.objects.filter(event=event, state=Participant.State.CONFIRMED).count() >= event.capacity:
                 raise ValidationError("This event is full.")

             if Participant.objects.filter(user=user, event=event).exists():
                 raise ValidationError("You are already registered for this event.")
             
             price = self.get_price(event, user)
             if price is None:
                 raise ValidationError("You can't register for this event yet.")

             return price
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

    def register_for_event(self, user, event) -> PriceVariant:
        """
        Validate user's eligibility to register for an event.
        
        Args:
            user: The user attempting to register
            event: The event to register for
            
        Returns:
            PriceVariant: The applicable price variant for the registration
            
        Raises:
            ValidationError: If registration is not possible due to:
                - Event being full
                - User already registered
                - No applicable price variant available
        """
        from django.db import transaction
        
        with transaction.atomic():
            # Lock the event row to prevent race conditions on capacity check
            event = event.__class__.objects.select_for_update().get(pk=event.pk)
            
            if Participant.objects.filter(event=event, state=Participant.State.CONFIRMED).count() >= event.capacity:
                raise ValidationError("This event is full.")

            if Participant.objects.filter(user=user, event=event).exists():
                raise ValidationError("You are already registered for this event.")
            
            price = self.get_price(event, user)
            if price is None:
                raise ValidationError("You can't register for this event yet.")

            return price

8-15: 🛠️ Refactor suggestion

Optimize database queries and improve code structure.

The method performs repeated database queries and could benefit from query optimization and better documentation.

Consider this improved implementation:

-    def get_price(self, event, user):
+    def get_price(self, event, user) -> PriceVariant | None:
+        """
+        Determine the appropriate price variant for a user registering for an event.
+        
+        Args:
+            event: The event to get price for
+            user: The user attempting to register
+            
+        Returns:
+            PriceVariant if a suitable price is found, None otherwise
+        """
+        # Get all price variants for the event at once
+        price_variants = {
+            pv.type: pv for pv in PriceVariant.objects.filter(event=event)
+        }
+        
+        # Check variants in priority order
+        if EventPriceVariantType.FREE in price_variants:
+            return price_variants[EventPriceVariantType.FREE]
+        
+        if user.is_esn_card_holder and EventPriceVariantType.WITH_ESN_CARD in price_variants:
+            return price_variants[EventPriceVariantType.WITH_ESN_CARD]
+        
+        return price_variants.get(EventPriceVariantType.STANDARD)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

    def get_price(self, event, user) -> PriceVariant | None:
        """
        Determine the appropriate price variant for a user registering for an event.
        
        Args:
            event: The event to get price for
            user: The user attempting to register
            
        Returns:
            PriceVariant if a suitable price is found, None otherwise
        """
        # Get all price variants for the event at once
        price_variants = {
            pv.type: pv for pv in PriceVariant.objects.filter(event=event)
        }
        
        # Check variants in priority order
        if EventPriceVariantType.FREE in price_variants:
            return price_variants[EventPriceVariantType.FREE]
        
        if user.is_esn_card_holder and EventPriceVariantType.WITH_ESN_CARD in price_variants:
            return price_variants[EventPriceVariantType.WITH_ESN_CARD]
        
        return price_variants.get(EventPriceVariantType.STANDARD)
🧰 Tools
🪛 Ruff

11-11: Unnecessary elif after return statement

Remove unnecessary elif

(RET505)

fiesta/apps/events/forms/organizer.py (5)

14-15: ⚠️ Potential issue

Remove empty init method.

This empty init method is redundant as it only calls super().init.

Remove these lines as they serve no purpose.


26-28: ⚠️ Potential issue

Remove duplicate init method.

This is a duplicate of the previous init method and should be removed.

Remove these lines completely.

🧰 Tools
🪛 Ruff

26-26: Redefinition of unused __init__ from line 14

(F811)


1-9: ⚠️ Potential issue

Clean up imports and add future annotations.

Several imports are unused and should be removed for better code maintainability.

Apply this diff:

+from __future__ import annotations

from apps.fiestaforms.forms import BaseModelForm
from apps.events.models import Organizer
from apps.accounts.models import User
-from django.forms import CharField, HiddenInput, MultipleChoiceField, ModelMultipleChoiceField, Select
+from django.forms import HiddenInput, ModelMultipleChoiceField, Select
from django.utils.translation import gettext_lazy as _
-from django_select2.forms import Select2MultipleWidget
-from apps.fiestaforms.widgets.models import MultipleActiveLocalMembersFromSectionWidget, PlaceWidget
+from apps.fiestaforms.widgets.models import MultipleActiveLocalMembersFromSectionWidget
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

from __future__ import annotations

from apps.fiestaforms.forms import BaseModelForm
from apps.events.models import Organizer
from apps.accounts.models import User
from django.forms import HiddenInput, ModelMultipleChoiceField, Select
from django.utils.translation import gettext_lazy as _
from apps.fiestaforms.widgets.models import MultipleActiveLocalMembersFromSectionWidget
🧰 Tools
🪛 Ruff

1-1: Missing required import: from __future__ import annotations

Insert required import: from __future__ import annotations

(I002)


5-5: django.forms.CharField imported but unused

Remove unused import

(F401)


5-5: django.forms.MultipleChoiceField imported but unused

Remove unused import

(F401)


6-6: django_select2.forms.Select2MultipleWidget imported but unused

Remove unused import: django_select2.forms.Select2MultipleWidget

(F401)


8-8: apps.fiestaforms.widgets.models.PlaceWidget imported but unused

Remove unused import: apps.fiestaforms.widgets.models.PlaceWidget

(F401)


24-24: 🛠️ Refactor suggestion

Convert role field to proper form field.

The role field should be defined as a proper form field for better form handling.

Replace with:

-    role = Select(choices=Organizer.Role.choices)
+    role = forms.ChoiceField(
+        choices=Organizer.Role.choices,
+        widget=Select,
+        label=_("Role")
+    )
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

    role = forms.ChoiceField(
        choices=Organizer.Role.choices,
        widget=Select,
        label=_("Role")
    )

30-42: ⚠️ Potential issue

Fix save method return value and add type hints.

The save method should return the created instances. Also, consider adding type hints for better code maintainability.

Apply this diff:

-    def save(self, commit=True):
+    def save(self, commit: bool = True) -> list[Organizer]:
         organizers = self.cleaned_data["add_organizer"]
         event = self.cleaned_data["event"]
         role = self.cleaned_data['role']	
 
+        created_organizers = []
         for organizer in organizers:
-            Organizer.objects.get_or_create(
+            organizer_obj, _ = Organizer.objects.get_or_create(
                 event=event,
                 user=organizer,
                 role=role
             )
+            created_organizers.append(organizer_obj)
 
-        return 
+        return created_organizers
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

    def save(self, commit: bool = True) -> list[Organizer]:
        organizers = self.cleaned_data["add_organizer"]
        event = self.cleaned_data["event"]
        role = self.cleaned_data['role']	

        created_organizers = []
        for organizer in organizers:
            organizer_obj, _ = Organizer.objects.get_or_create(
                event=event,
                user=organizer,
                role=role
            )
            created_organizers.append(organizer_obj)

        return created_organizers
fiesta/apps/accounts/models/user.py (1)

48-51: 💡 Codebase verification

Based on the evidence gathered, I can now provide a final response:

Setting is_esn_card_holder to always return False will impact event pricing functionality

The change will affect three critical pricing-related components:

  • Price variant availability checks in fiesta/apps/events/models/price_variant.py
  • Price variant selection in fiesta/apps/events/services/participant_validator.py
  • Price variant filtering in templates via fiesta/apps/events/templatetags/event_tags.py

The existence of ESN card related migrations and a dedicated esncards app (fiesta/apps/esncards/) suggests this is an active feature. Users with ESN cards will lose access to special pricing (WITH_ESN_CARD variant) as the property will always return False.

🔗 Analysis chain

Verify the impact on event pricing.

Since this property is used in EventPriceVariantType.is_available() for pricing logic, always returning False could affect event pricing for ESN card holders.

Let's verify the usage and potential impact:


Based on the search results, I can see that is_esn_card_holder is used in several critical pricing-related components:

  1. In participant_validator.py for determining which price variant to apply
  2. In event_tags.py for filtering visible price variants
  3. In price_variant.py for checking price variant availability

Let's verify if there are any existing ESN card holders or price variants in the system:


Let's check for migrations and tests with a corrected path pattern:

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check how is_esn_card_holder affects pricing logic

# Find all references to is_esn_card_holder in price variant related files
echo "=== Checking price variant logic ==="
rg -A 5 "is_esn_card_holder" --glob "fiesta/apps/events/**/*.py"

# Look for any price-related tests that might need updating
echo -e "\n=== Checking related tests ==="
rg -A 5 "ESN.*card|price.*variant" --glob "fiesta/**/test*.py"

Length of output: 2024


Script:

#!/bin/bash
# Check for ESN card price variants in tests and fixtures
echo "=== Checking test data ==="
rg -l "WITH_ESN_CARD" --glob "fiesta/**/fixtures/*.{json,yaml,yml}"

# Check for price variant types definition
echo -e "\n=== Price variant types definition ==="
rg -A 5 "class EventPriceVariantType" --glob "fiesta/**/*.py"

# Check for ESN card related migrations
echo -e "\n=== ESN card migrations ==="
fd "\.py$" -e py --glob "fiesta/**/migrations/*.py" --exec rg -l "ESN" {}

Length of output: 1037


Script:

#!/bin/bash
# Check for ESN card related migrations and tests
echo "=== Checking migrations ==="
fd -e py -p "migrations" . | xargs rg -l "ESN"

echo -e "\n=== Checking tests ==="
fd -e py -p "test" . | xargs rg -A 5 "WITH_ESN_CARD|is_esn_card_holder"

# Check if ESN card functionality is behind a feature flag
echo -e "\n=== Checking for feature flags ==="
rg -i "esn.*card.*flag|feature.*esn" --glob "fiesta/**/*.py"

# Check for any documentation about ESN cards
echo -e "\n=== Checking documentation ==="
fd -e md -e rst . | xargs rg -i "esn"

Length of output: 2223

fiesta/apps/events/models/place.py (2)

43-53: ⚠️ Potential issue

Add coordinate range validation

The latitude and longitude fields should include validators to ensure coordinates are within valid ranges:

  • Latitude: -90 to 90
  • Longitude: -180 to 180

Also, consider if 0.0 is the best default value as it represents a valid location in the Gulf of Guinea.

Here's how to add the validators:

 longitude = models.FloatField(
     verbose_name=_("longitude"),
     blank=True,
-    default=0.0
+    default=0.0,
+    validators=[
+        models.validators.MinValueValidator(-180),
+        models.validators.MaxValueValidator(180),
+    ]
 )

 latitude = models.FloatField(
     verbose_name=_("latitude"),
     blank=True,
-    default=0.0
+    default=0.0,
+    validators=[
+        models.validators.MinValueValidator(-90),
+        models.validators.MaxValueValidator(90),
+    ]
 )
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

    longitude = models.FloatField(
        verbose_name=_("longitude"),
        blank=True,
        default=0.0,
        validators=[
            models.validators.MinValueValidator(-180),
            models.validators.MaxValueValidator(180),
        ]
    )
    
    latitude = models.FloatField(
        verbose_name=_("latitude"),
        blank=True,
        default=0.0,
        validators=[
            models.validators.MinValueValidator(-90),
            models.validators.MaxValueValidator(90),
        ]
    )

66-71: 🛠️ Refactor suggestion

Improve coordinate extraction robustness and documentation

The coordinate extraction method needs improvements:

  1. The regex pattern should be more specific to match Google Maps URL format
  2. Missing docstring and type hints
  3. No validation of extracted coordinate ranges

Here's an improved version:

-    def set_coordinates_from_url(self, url):
-        match = re.search(r'([-\d.]+),([-\d.]+)', url)
-        if match:
-            self.latitude, self.longitude = map(float, match.groups())
-            return True
-        return False
+    def set_coordinates_from_url(self, url: str) -> bool:
+        """Extract and set coordinates from a Google Maps URL.
+        
+        Args:
+            url: Google Maps URL containing coordinates
+            
+        Returns:
+            bool: True if coordinates were successfully extracted and set
+        """
+        match = re.search(r'@([-\d.]+),([-\d.]+)', url)
+        if not match:
+            return False
+            
+        lat, lng = map(float, match.groups())
+        if not (-90 <= lat <= 90 and -180 <= lng <= 180):
+            return False
+            
+        self.latitude, self.longitude = lat, lng
+        return True
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

    def set_coordinates_from_url(self, url: str) -> bool:
        """Extract and set coordinates from a Google Maps URL.
        
        Args:
            url: Google Maps URL containing coordinates
            
        Returns:
            bool: True if coordinates were successfully extracted and set
        """
        match = re.search(r'@([-\d.]+),([-\d.]+)', url)
        if not match:
            return False
            
        lat, lng = map(float, match.groups())
        if not (-90 <= lat <= 90 and -180 <= lng <= 180):
            return False
            
        self.latitude, self.longitude = lat, lng
        return True
fiesta/apps/events/forms/event.py (3)

1-12: ⚠️ Potential issue

Clean up imports section

Several imports are unused and should be removed for better code maintainability.

Apply this diff to clean up the imports:

+from __future__ import annotations
 from typing import Any
 from apps.fiestaforms.forms import BaseModelForm
-from apps.events.models import Event, Organizer
+from apps.events.models import Event
-from apps.accounts.models import User
 from django.forms import CharField, HiddenInput, DateTimeInput
-from django_select2.forms import Select2MultipleWidget
 from django.utils.translation import gettext_lazy as _
-from apps.fiestaforms.widgets.models import MultipleActiveLocalMembersFromSectionWidget, PlaceWidget
-
-from apps.sections.models import SectionMembership
+from apps.fiestaforms.widgets.models import PlaceWidget
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

from __future__ import annotations
from typing import Any
from apps.fiestaforms.forms import BaseModelForm
from apps.events.models import Event
from django.forms import CharField, HiddenInput, DateTimeInput
from django.utils.translation import gettext_lazy as _
from apps.fiestaforms.widgets.models import PlaceWidget
🧰 Tools
🪛 Ruff

1-1: Missing required import: from __future__ import annotations

Insert required import: from __future__ import annotations

(I002)


4-4: apps.events.models.Organizer imported but unused

Remove unused import: apps.events.models.Organizer

(F401)


5-5: apps.accounts.models.User imported but unused

Remove unused import: apps.accounts.models.User

(F401)


7-7: django_select2.forms.Select2MultipleWidget imported but unused

Remove unused import: django_select2.forms.Select2MultipleWidget

(F401)


9-9: apps.fiestaforms.widgets.models.MultipleActiveLocalMembersFromSectionWidget imported but unused

Remove unused import: apps.fiestaforms.widgets.models.MultipleActiveLocalMembersFromSectionWidget

(F401)


11-11: apps.sections.models.SectionMembership imported but unused

Remove unused import: apps.sections.models.SectionMembership

(F401)


18-26: ⚠️ Potential issue

Add error handling for missing initial values

The initialization could raise KeyError if section or author are not provided in initial data.

Consider adding safe access:

     def __init__(self, *args, **kwargs):
         super().__init__(*args, **kwargs)
 
         self.fields["section"].disabled = True
         self.fields["author"].disabled = True
 
-        self.initial["section_name"] = self.initial["section"].name
-        self.initial["author_name"] = self.initial["author"].full_name
+        if "section" in self.initial:
+            self.initial["section_name"] = self.initial["section"].name
+        if "author" in self.initial:
+            self.initial["author_name"] = self.initial["author"].full_name
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

        self.fields["section"].disabled = True
        self.fields["author"].disabled = True

        if "section" in self.initial:
            self.initial["section_name"] = self.initial["section"].name
        if "author" in self.initial:
            self.initial["author_name"] = self.initial["author"].full_name

27-33: 🛠️ Refactor suggestion

Enhance form validation

While the price variant validation is good, consider adding these validations:

  1. Ensure event end date is after start date
  2. Validate that capacity is a positive number

Add additional validations:

     def clean(self) -> dict[str, Any]:
         clean_data = super().clean()
         state = clean_data.get("state")
         
         if state == Event.State.PUBLISHED and not clean_data.get("price_variants"):
             self.add_error("state", _("You can't publish event without price variants."))
+        
+        start = clean_data.get("start")
+        end = clean_data.get("end")
+        if start and end and start >= end:
+            self.add_error("end", _("Event end must be after start date."))
+        
+        capacity = clean_data.get("capacity")
+        if capacity is not None and capacity <= 0:
+            self.add_error("capacity", _("Capacity must be positive."))
+        
         return clean_data

Committable suggestion was skipped due to low confidence.

fiesta/apps/events/urls.py (1)

17-34: 🛠️ Refactor suggestion

Consider restructuring URLs for consistency.

The current URL structure uses different prefixes (event-detail, event-update) for related operations. Consider restructuring to maintain consistency:

  1. Use a single prefix for event-related operations
  2. Follow REST-like nested resource patterns

Example restructuring:

-    path('add-event', AddEventView.as_view(), name="add-event"),
-    path('event-detail/update/<uuid:pk>', UpdateEventView.as_view(), name="event-update"),
-    path('event-detail/<uuid:pk>', EventDetailView.as_view(), name="event-detail"),
+    path('events/new', AddEventView.as_view(), name="add-event"),
+    path('events/<uuid:pk>', EventDetailView.as_view(), name="event-detail"),
+    path('events/<uuid:pk>/edit', UpdateEventView.as_view(), name="event-update"),

Committable suggestion was skipped due to low confidence.

fiesta/apps/events/models/price_variant.py (1)

29-35: ⚠️ Potential issue

Fix time validation logic and simplify the return statement.

There are several issues in the availability check:

  1. The time validation logic is reversed:
    • For available_from, we should check if current time is before the start time
    • For available_to, we should check if current time is after the end time
  2. The return statement can be simplified
  3. Type hints would improve code clarity

Apply this diff to fix the issues:

-    def is_available(self, variant: PriceVariant, user: User):
+    def is_available(self, variant: PriceVariant, user: User) -> bool:
         to_ = variant.available_to
         from_ = variant.available_from
 
-        if from_ is not None and from_ != "" and from_ < datetime.now(UTC):
+        if from_ is not None and from_ != "" and datetime.now(UTC) < from_:
             return False
 
-        if to_ is not None and to_ != "" and to_ > datetime.now(UTC):
+        if to_ is not None and to_ != "" and datetime.now(UTC) > to_:
             return False
 
-        if variant.type == self.STANDARD or (
-                variant.type == self.WITH_ESN_CARD
-                and user.is_esn_card_holder
-        ):
-            return True
-
-        return False
+        return variant.type == self.STANDARD or (
+            variant.type == self.WITH_ESN_CARD
+            and user.is_esn_card_holder
+        )

Committable suggestion was skipped due to low confidence.

🧰 Tools
🪛 Ruff

29-35: Return the condition directly

Inline condition

(SIM103)

fiesta/apps/events/migrations/0003_participant_state_alter_organizer_event_and_more.py (3)

16-20: 🛠️ Refactor suggestion

Consider enhancing the state field implementation

While the state field addition is functional, consider these improvements:

  1. Use Django's TextChoices/IntegerChoices for better type safety and maintainability
  2. Add a database index since this field will likely be frequently queried
  3. Consider implementing state transition validation to prevent invalid state changes

Here's an improved implementation for the models.py file:

from django.db import models

class ParticipantState(models.TextChoices):
    WAITING = 'waiting', 'Waiting'
    CONFIRMED = 'confirmed', 'Confirmed'
    DELETED = 'deleted', 'Deleted'

class Participant(models.Model):
    state = models.CharField(
        max_length=20,
        choices=ParticipantState.choices,
        default=ParticipantState.WAITING,
        help_text='current state of the event',
        verbose_name='state',
        db_index=True  # Add index for better query performance
    )

46-50: ⚠️ Potential issue

Fix inconsistent null/deletion behavior in PriceVariant

The event field is configured to allow null values but uses CASCADE deletion, which is inconsistent:

  • If a price variant must belong to an event, make the field non-null
  • If it can exist without an event, use SET_NULL instead of CASCADE

Choose one of these approaches in your models.py:

# Option 1: If price variant must belong to an event
event = models.ForeignKey(
    'Event',
    null=False,  # Make it required
    on_delete=models.CASCADE,
    related_name='price_variants',
    verbose_name='event'
)

# Option 2: If price variant can exist without event
event = models.ForeignKey(
    'Event',
    null=True,
    on_delete=models.SET_NULL,  # Change deletion behavior
    related_name='price_variants',
    verbose_name='event'
)

31-45: ⚠️ Potential issue

Address inconsistencies in Participant model relationships

Several concerns in the Participant model alterations:

  1. The related_name 'users' for the user FK is confusing - it should be 'participations' to better describe the reverse relation
  2. Explicitly setting db_index=False on price FK is unusual and might impact query performance
  3. Ensure proper null handling in views/forms for SET_NULL fields

Apply these changes in your models.py:

class Participant(models.Model):
    user = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        null=True,
        on_delete=models.SET_NULL,
-       related_name='users',
+       related_name='participations',
        verbose_name='user'
    )
    price = models.ForeignKey(
        'PriceVariant',
        null=True,
        on_delete=models.SET_NULL,
-       db_index=False,
        related_name='participants',
        verbose_name='price'
    )

Committable suggestion was skipped due to low confidence.

fiesta/apps/events/templatetags/event_tags.py (4)

51-55: ⚠️ Potential issue

Fix incorrect filter parameter.

There's a bug in the filter condition - it's using role=Participant.State.CONFIRMED when it should be using state=.

@register.simple_tag(takes_context=True)
def is_participant(context, event: Event) -> bool:
    request: HttpRequest = context["request"]
-    return event.participants.filter(user=request.membership.user, role=Participant.State.CONFIRMED).exists()
+    return event.participants.filter(user=request.membership.user, state=Participant.State.CONFIRMED).exists()
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

@register.simple_tag(takes_context=True)
def is_participant(context, event: Event) -> bool:
    request: HttpRequest = context["request"]
    return event.participants.filter(user=request.membership.user, state=Participant.State.CONFIRMED).exists()

61-65: 🛠️ Refactor suggestion

Avoid code duplication with can_edit.

The can_see_participants function duplicates the exact logic from can_edit. Consider reusing the existing function to maintain DRY principles.

@register.simple_tag(takes_context=True)
def can_see_participants(context, event: Event) -> bool:
-    request: HttpRequest = context["request"]
-    return is_moc(context, event) or event.author == request.membership.user or request.membership.is_privileged or request.membership.user.has_perm('events.change_event')
+    return can_edit(context, event)

Committable suggestion was skipped due to low confidence.


1-11: ⚠️ Potential issue

Add translations for user-facing strings.

The gettext_lazy is imported but not used. Since this file contains user-facing strings (e.g., "Opened", "Almost full", "Full"), these should be wrapped with translation markers.

Apply translations to the strings:

-        return mark_safe('<span class="bg-success p-2 rounded-full">Opened</span>')
+        return mark_safe('<span class="bg-success p-2 rounded-full">{}</span>'.format(_('Opened')))

Committable suggestion was skipped due to low confidence.

🧰 Tools
🪛 Ruff

8-8: django.utils.translation.gettext_lazy imported but unused

Remove unused import: django.utils.translation.gettext_lazy

(F401)


32-40: ⚠️ Potential issue

Optimize database queries and improve maintainability.

Several improvements can be made to this function:

  1. Multiple identical database queries are inefficient
  2. Missing explicit return statement
  3. HTML styling is hardcoded
  4. Strings should be translated

Here's a suggested improvement:

@register.simple_tag(takes_context=True)
def get_event_fullness(context, event: Event):
+    confirmed_count = Participant.objects.filter(
+        event=event, state=Participant.State.CONFIRMED
+    ).count()
+    
+    STYLES = {
+        'opened': 'bg-success',
+        'almost_full': 'bg-warning',
+        'full': 'bg-error',
+    }
+    
-    if Participant.objects.filter(event=event, state=Participant.State.CONFIRMED).count() < event.capacity/2:
-        return mark_safe('<span class="bg-success p-2 rounded-full">Opened</span>')
-    elif Participant.objects.filter(event=event, state=Participant.State.CONFIRMED).count() < event.capacity and Participant.objects.filter(event=event, state=Participant.State.CONFIRMED).count() >= event.capacity/2:
-        return mark_safe('<span class="bg-warning p-2  rounded-full">Almost full</span>')
-    elif Participant.objects.filter(event=event, state=Participant.State.CONFIRMED).count() >= event.capacity:
-        return mark_safe('<span class="bg-error p-2  rounded-full">Full</span>')
+    if confirmed_count >= event.capacity:
+        status = ('full', _('Full'))
+    elif confirmed_count >= event.capacity/2:
+        status = ('almost_full', _('Almost full'))
+    else:
+        status = ('opened', _('Opened'))
+    
+    return mark_safe(
+        f'<span class="{STYLES[status[0]]} p-2 rounded-full">{status[1]}</span>'
+    )
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

@register.simple_tag(takes_context=True)
def get_event_fullness(context, event: Event):
    confirmed_count = Participant.objects.filter(
        event=event, state=Participant.State.CONFIRMED
    ).count()
    
    STYLES = {
        'opened': 'bg-success',
        'almost_full': 'bg-warning',
        'full': 'bg-error',
    }
    
    if confirmed_count >= event.capacity:
        status = ('full', _('Full'))
    elif confirmed_count >= event.capacity/2:
        status = ('almost_full', _('Almost full'))
    else:
        status = ('opened', _('Opened'))
    
    return mark_safe(
        f'<span class="{STYLES[status[0]]} p-2 rounded-full">{status[1]}</span>'
    )
🧰 Tools
🪛 Ruff

34-39: Missing explicit return at the end of function able to return non-None value

Add explicit return statement

(RET503)


36-36: Unnecessary elif after return statement

Remove unnecessary elif

(RET505)

fiesta/apps/fiestaforms/widgets/models.py (1)

41-62: 🛠️ Refactor suggestion

Consider extracting common user widget functionality to a mixin

The UserWidget and MultipleUserWidget share identical search fields and label generation logic. Consider creating a mixin to avoid code duplication:

+class UserWidgetMixin:
+    search_fields = [
+        "first_name__icontains",
+        "last_name__icontains",
+        "username__icontains",
+    ]
+
+    @classmethod
+    def label_from_instance(cls, user):
+        return f"{user.full_name} ({user.username})"
+
-class UserWidget(RemoteModelSelectWidgetMixin, ModelSelect2Widget):
-    search_fields = [
-        "first_name__icontains",
-        "last_name__icontains",
-        "username__icontains",
-    ]
-
-    @classmethod
-    def label_from_instance(cls, user):
-        return f"{user.full_name} ({user.username})"
+class UserWidget(UserWidgetMixin, RemoteModelSelectWidgetMixin, ModelSelect2Widget):
+    pass
 
-class MultipleUserWidget(RemoteModelSelectWidgetMixin, ModelSelect2MultipleWidget):
-    search_fields = [
-        "first_name__icontains",
-        "last_name__icontains",
-        "username__icontains",
-    ]
-
-    @classmethod
-    def label_from_instance(cls, user):
-        return f"{user.full_name} ({user.username})"
+class MultipleUserWidget(UserWidgetMixin, RemoteModelSelectWidgetMixin, ModelSelect2MultipleWidget):
+    pass
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

class UserWidgetMixin:
    search_fields = [
        "first_name__icontains",
        "last_name__icontains",
        "username__icontains",
    ]

    @classmethod
    def label_from_instance(cls, user):
        return f"{user.full_name} ({user.username})"

class UserWidget(UserWidgetMixin, RemoteModelSelectWidgetMixin, ModelSelect2Widget):
    pass
    
class MultipleUserWidget(UserWidgetMixin, RemoteModelSelectWidgetMixin, ModelSelect2MultipleWidget):
    pass
fiesta/apps/events/templates/events/index.html (4)

70-70: ⚠️ Potential issue

Typo: Correct darft_events to draft_events

There is a typo in the variable name darft_events. This will cause a VariableDoesNotExist error because the context variable is likely named draft_events.

Apply this diff to fix the typo:

- {% for event in darft_events %}
+ {% for event in draft_events %}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

    {% for event in draft_events %}

83-84: ⚠️ Potential issue

Invalid HTML: Avoid nesting <a> tags inside <button> elements

Nesting <a> tags inside <button> elements is invalid HTML and can lead to unexpected behavior. The <button> element is designed for form submission or button actions, while <a> tags are for hyperlinks.

To fix this, you can place the <a> tag outside the <button> or style the <a> tag to look like a button:

- <button class="btn btn-primary btn-outline grow">
-   <a href="{% url 'events:add-event' %}">Add event</a>
- </button>
- <button class="btn btn-primary btn-outline grow">
-   <a href="{% url 'events:place' %}">Places</a>
- </button>
+ <a href="{% url 'events:add-event' %}" class="btn btn-primary btn-outline grow">Add event</a>
+ <a href="{% url 'events:place' %}" class="btn btn-primary btn-outline grow">Places</a>
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

<a href="{% url 'events:add-event' %}" class="btn btn-primary btn-outline grow">Add event</a>
<a href="{% url 'events:place' %}" class="btn btn-primary btn-outline grow">Places</a>

28-28: ⚠️ Potential issue

Undefined variable participant

The variable participant is not defined in this template context. This will result in a VariableDoesNotExist error when rendering the template.

To fix this issue, ensure that the participant variable is passed to the template context in your view. Alternatively, if you intended to use event.participant, update the code accordingly:

- {% if participant.state == participant.State.WAITING %}
+ {% if event.participant.state == event.participant.State.WAITING %}

Committable suggestion was skipped due to low confidence.


31-39: ⚠️ Potential issue

Duplicate and conflicting messages to the user

In the else block, both messages "Pay now to confirm" and "You still have to come to our office and pay for the event!" are displayed. This can be confusing as they provide conflicting instructions—prompting the user to pay online and also to visit the office.

Consider removing the redundant message to avoid confusion:

{% if not request.events.configuration.online_purchases %}
  <div class="flex-auto">
    <p class="text-base-content font-semibold text-[15px] tracking-[0] leading-[normal] pb-1">
      You still have to come to our office and pay for the event!
    </p>
  </div>
{% else %}
  <div class="flex-auto">
    <p class="text-base-content font-semibold text-[15px] tracking-[0] leading-[normal] pb-1">
      Pay now to confirm
    </p>
  </div>
- <div class="flex-0">
-   <p class="text-base-content font-semibold text-[15px] tracking-[0] leading-[normal] pb-1">
-     You still have to come to our office and pay for the event!
-   </p>
- </div>
{% endif %}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

                    <p class="text-base-content font-semibold  text-[15px] tracking-[0] leading-[normal] pb-1">You still have to come to our office and pay for the event!</p>
                  </div>
                {% else %}
                  <div class="flex-auto">
                    <p class="text-base-content font-semibold  text-[15px] tracking-[0] leading-[normal] pb-1">Pay now to confirm</p>
                  </div>
fiesta/apps/events/views/organizer.py (4)

1-1: ⚠️ Potential issue

Include from __future__ import annotations for forward reference support

To enable postponed evaluation of type annotations (PEP 563) and improve compatibility with forward references, include the import from __future__ at the top of the file.

Apply this diff to add the import:

+from __future__ import annotations
 from typing import Any
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

from __future__ import annotations
from typing import Any
🧰 Tools
🪛 Ruff

1-1: Missing required import: from __future__ import annotations

Insert required import: from __future__ import annotations

(I002)


41-46: 🛠️ Refactor suggestion

Eliminate redundant Organizer retrieval in UpdateOrganizerRole

Both the dispatch and get_object methods fetch the same Organizer object, leading to redundant database queries.

Refactor to retrieve the Organizer object once and reuse it:

 def dispatch(self, request, *args, **kwargs):
     self.event = get_object_or_404(Event, pk=self.kwargs.get("pk"))
-    self.organizer = get_object_or_404(Organizer, pk=self.kwargs.get("pko"))
+    self.object = get_object_or_404(Organizer, pk=self.kwargs.get("pko"))
     return super().dispatch(request, *args, **kwargs)

 def get_object(self, queryset=None):
-    return get_object_or_404(Organizer, pk=self.kwargs.get("pko"))
+    return self.object
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

        self.event = get_object_or_404(Event, pk=self.kwargs.get("pk"))
        self.object = get_object_or_404(Organizer, pk=self.kwargs.get("pko"))
        return super().dispatch(request, *args, **kwargs)
    
    def get_object(self, queryset=None):
        return self.object

19-19: ⚠️ Potential issue

Avoid redefinition of HttpRequest

HttpRequest is already imported from django.http at line 3. Importing it again from apps.plugins.middleware.plugin could cause confusion or unexpected behavior.

Consider aliasing the second import or removing it if not necessary.

Apply this diff:

-from apps.plugins.middleware.plugin import HttpRequest
+from apps.plugins.middleware.plugin import PluginHttpRequest

If the second HttpRequest is not needed, you can safely remove the import:

-from apps.plugins.middleware.plugin import HttpRequest
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

from apps.plugins.middleware.plugin import PluginHttpRequest
🧰 Tools
🪛 Ruff

19-19: Redefinition of unused HttpRequest from line 3

(F811)


93-98: 🛠️ Refactor suggestion

Reduce duplicate queries in DeleteOrganizerView

Similarly, the dispatch and get_object methods in DeleteOrganizerView retrieve the same Organizer object.

Refactor to fetch the object once:

 def dispatch(self, request: HttpRequest, *args: Any, **kwargs: Any) -> HttpResponse:
     self.event = get_object_or_404(Event, pk=self.kwargs.get("pk"))
+    self.object = get_object_or_404(Organizer, pk=self.kwargs.get("pko"))
     return super().dispatch(request, *args, **kwargs)

 def get_object(self, queryset=None):
-    return get_object_or_404(Organizer, pk=self.kwargs.get("pko"))
+    return self.object
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

    def dispatch(self, request: HttpRequest, *args: Any, **kwargs: Any) -> HttpResponse:
        self.event = get_object_or_404(Event, pk=self.kwargs.get("pk"))
        self.object = get_object_or_404(Organizer, pk=self.kwargs.get("pko"))
        return super().dispatch(request, *args, **kwargs)

    def get_object(self, queryset=None):
        return self.object
fiesta/apps/events/views/price.py (7)

76-76: ⚠️ Potential issue

Set model to PriceVariant in UpdatePriceView

In the UpdatePriceView, the model attribute should be PriceVariant since the view is updating a PriceVariant instance.

Apply this diff:

-    model = Event
+    model = PriceVariant
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

    model = PriceVariant

105-105: ⚠️ Potential issue

Set model to PriceVariant in DeletePriceView

In the DeletePriceView, the model is incorrectly set to Event. It should be PriceVariant to properly identify and delete the price variant object.

Apply this diff:

-    model = Event
+    model = PriceVariant
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

    model = PriceVariant

41-41: ⚠️ Potential issue

Set model to PriceVariant in AddPriceView

In the AddPriceView, the model attribute is set to Event, but since the view is creating a PriceVariant, it should be set to PriceVariant for correct functionality of the CreateView.

Apply this diff:

-    model = Event
+    model = PriceVariant
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

    model = PriceVariant

1-1: ⚠️ Potential issue

Add from __future__ import annotations for improved type hinting

To enable postponed evaluation of type annotations and support forward references, add the following import at the top of the file.

Apply this diff:

+from __future__ import annotations
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

from __future__ import annotations
from typing import Any
🧰 Tools
🪛 Ruff

1-1: Missing required import: from __future__ import annotations

Insert required import: from __future__ import annotations

(I002)


63-63: ⚠️ Potential issue

Remove undefined return response statement

In the get_success_url method of AddPriceView, the return response line is unnecessary and response is not defined in this context. The method should return the URL constructed in the previous line.

Apply this diff:

         return reverse("events:event-detail", args=[self.event.pk])
-        return response
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

        return reverse("events:event-detail", args=[self.event.pk])
🧰 Tools
🪛 Ruff

63-63: Undefined name response

(F821)


19-19: ⚠️ Potential issue

Resolve redefinition of HttpRequest

HttpRequest is imported twice: from django.http at line 5 and from apps.plugins.middleware.plugin at line 19. This causes a redefinition and may lead to confusion. If you require the specialized HttpRequest from apps.plugins.middleware.plugin, consider aliasing one of them or removing the unnecessary import.

Option 1: Alias the second import if both are needed:

-from apps.plugins.middleware.plugin import HttpRequest
+from apps.plugins.middleware.plugin import HttpRequest as PluginHttpRequest

Option 2: Remove the unused import if only one is necessary:

-from apps.plugins.middleware.plugin import HttpRequest

Committable suggestion was skipped due to low confidence.

🧰 Tools
🪛 Ruff

19-19: Redefinition of unused HttpRequest from line 5

(F811)


125-127: 🛠️ Refactor suggestion

Follow POST/Redirect/GET pattern in DeletePriceView

The post method in DeletePriceView returns an empty HttpResponse after deleting the price variant. To adhere to the POST/Redirect/GET pattern and provide a better user experience, consider redirecting to the success URL after deletion.

Apply this diff:

 @transaction.atomic
 def post(self, request: HttpRequest, *args: str, **kwargs: Any) -> HttpResponse:
     self.price.delete()
-    return HttpResponse('')
+    return HttpResponseRedirect(self.get_success_url())

Remember to import HttpResponseRedirect at the top:

+from django.http import HttpResponseRedirect

Committable suggestion was skipped due to low confidence.

fiesta/apps/events/views/place.py (3)

13-13: ⚠️ Potential issue

Remove unused imports to clean up the code.

The following imports are not used in the file and can be safely removed:

  • reverse from django.urls (line 13)
  • models from django.db (line 16)
  • with_breadcrumb from ...utils.breadcrumbs (line 31)
  • get_next_redirect_url from allauth.account.utils (line 32)
  • transaction from django.db (line 33)

Apply this diff to remove the unused imports:

-from django.urls import reverse, reverse_lazy
+from django.urls import reverse_lazy
-from django.db import models
-from ...utils.breadcrumbs import with_breadcrumb
-from allauth.account.utils import get_next_redirect_url
-from django.db import transaction

Also applies to: 16-16, 31-33

🧰 Tools
🪛 Ruff

13-13: django.urls.reverse imported but unused

Remove unused import: django.urls.reverse

(F401)


97-99: ⚠️ Potential issue

Correct the method name for custom column rendering.

The method render_place__name does not correspond to any column in PlaceTable. To customize the rendering of the name column, the method should be named render_name.

Apply this diff to fix the method name:

-def render_place__name(self, value):
+def render_name(self, value):
     return str(value)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

    def render_name(self, value):
        return str(value)
    

24-24: ⚠️ Potential issue

Avoid redefining HttpRequest to prevent conflicts.

At line 24, HttpRequest is imported again from apps.plugins.middleware.plugin, which conflicts with the earlier import from django.http on line 4. This can cause confusion and unexpected behavior.

Apply this diff to remove the conflicting import:

-from apps.plugins.middleware.plugin import HttpRequest

If the HttpRequest from apps.plugins.middleware.plugin is needed, consider importing it with an alias:

+from apps.plugins.middleware.plugin import HttpRequest as PluginHttpRequest

Committable suggestion was skipped due to low confidence.

🧰 Tools
🪛 Ruff

24-24: Redefinition of unused HttpRequest from line 4

(F811)

fiesta/apps/events/views/event.py (3)

6-6: ⚠️ Potential issue

Remove unused import render_to_string

The import render_to_string from django.template.loader is not used and can be removed to tidy up the imports.

Apply this diff to remove the unused import:

-from django.template.loader import render_to_string
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.


🧰 Tools
🪛 Ruff

6-6: django.template.loader.render_to_string imported but unused

Remove unused import: django.template.loader.render_to_string

(F401)


3-3: ⚠️ Potential issue

Remove unused import django.db.models

The import django.db.models is not used in this file and can be safely removed to clean up the code.

Apply this diff to remove the unused import:

-from django.db import models
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.


🧰 Tools
🪛 Ruff

3-3: django.db.models imported but unused

Remove unused import: django.db.models

(F401)


9-9: ⚠️ Potential issue

Remove unused import settings

The import settings from django.conf is not utilized in this file and can be removed.

Apply this diff to remove the unused import:

-from django.conf import settings
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.


🧰 Tools
🪛 Ruff

9-9: django.conf.settings imported but unused

Remove unused import: django.conf.settings

(F401)

fiesta/apps/events/templates/events/event_detail.html (2)

282-292: 🛠️ Refactor suggestion

Simplify map widget initialization by using default values

You can streamline the map widget code by removing the conditional blocks and utilizing default filter to handle missing latitude and longitude values.

Apply this diff to consolidate the code:

        <div class="mt-12 w-full shrink-0 grow-0 basis-auto lg:mb-0 lg:hidden md:block">
-         {% if not event.place.latitude %}
-           <div class="lg:py-12" x-data="mapWidget({ latitude: 0, longitude: 0 })" x-init="init">
-             <iframe x-bind:src="mapUrl" class="z-[10] w-full h-80 rounded-b-lg shadow-lg dark:shadow-black/20"></iframe>
-           </div>
-         {% else %}
-           <div class="lg:py-12" x-data="mapWidget({ latitude: {{ event.place.latitude }}, longitude: {{ event.place.longitude }} })" x-init="init">
-             <iframe x-bind:src="mapUrl" class="z-[10] w-full h-80 rounded-b-lg shadow-lg dark:shadow-black/20"></iframe>
-           </div>
-         {% endif %}
+         <div class="lg:py-12" x-data="mapWidget({ latitude: {{ event.place.latitude|default:0 }}, longitude: {{ event.place.longitude|default:0 }} })" x-init="init">
+           <iframe x-bind:src="mapUrl" class="z-[10] w-full h-80 rounded-b-lg shadow-lg dark:shadow-black/20"></iframe>
+         </div>
        </div>
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

    <div class="mt-12 w-full shrink-0 grow-0 basis-auto lg:mb-0 lg:hidden md:block">
        <div class="lg:py-12" x-data="mapWidget({ latitude: {{ event.place.latitude|default:0 }}, longitude: {{ event.place.longitude|default:0 }} })" x-init="init">
          <iframe x-bind:src="mapUrl" class="z-[10] w-full h-80 rounded-b-lg shadow-lg dark:shadow-black/20"></iframe>
        </div>
    </div>

179-195: 🛠️ Refactor suggestion

⚠️ Potential issue

Refactor price display logic to avoid variable shadowing and improve readability

The current logic within the price display section reuses the price variable in a nested loop, which can lead to confusion and potential errors. Also, there is a mismatched HTML tag (</p>) without a corresponding opening tag. Consider refactoring the code to improve clarity and fix the HTML structure.

Apply this diff to correct the issues:

{% get_price_variants event as prices %}
{% if prices %}
  {% for price in prices %}
    {% if price.type == "free" %}
      <p>Free</p>
    {% elif request.user.is_esn_car_holder and price.type == "with_esn_card" %}
      <p>Your price with ESNcard: {{ price.amount }}</p>
    {% elif price.type == "standard" %}
      <p>Your price: {{ price.amount }}</p>
+     {% for esn_price in prices %}
+       {% if esn_price.type == "with_esn_card" %}
+         <p>You can pay only this price if you get our ESNcard: {{ esn_price.amount }}</p>
+       {% endif %}
+     {% endfor %}
    {% endif %}
  {% endfor %}
{% endif %}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

          {% get_price_variants event as prices %}
            {% if prices %}
              {% for price in prices %}
                {% if price.type == "free" %}
                  <p>Free</p>
                {% elif request.user.is_esn_car_holder and price.type == "with_esn_card" %}
                  <p>Your price with ESNcard: {{ price.amount }}</p>
                {% elif price.type == "standard" %}
                  <p>Your price: {{ price.amount }}</p>
                  {% for esn_price in prices %}
                    {% if esn_price.type == "with_esn_card" %}
                      <p>You can pay only this price if you get our ESNcard: {{ esn_price.amount }}</p>
                    {% endif %}
                  {% endfor %}
                {% endif %}
              {% endfor %}
            {% endif %}
          </div> 
fiesta/apps/events/models/participant.py (5)

58-59: ⚠️ Potential issue

Use cls instead of self in class method

In the class method register_for_event, the first parameter should be cls to represent the class, not self.

Apply this diff to correct the method definition:

 @classmethod
-def register_for_event(self, user, event, price):
+def register_for_event(cls, user, event, price):

Also, update references inside the method from self to cls:

- if self.objects.filter(event=event).count() >= event.capacity:
+ if cls.objects.filter(event=event).count() >= event.capacity:

- if self.objects.filter(user=user, event=event).exists():
+ if cls.objects.filter(user=user, event=event).exists():

- cls.objects.create(

Committable suggestion was skipped due to low confidence.


76-76: ⚠️ Potential issue

Use timezone-aware now() for timestamps

You're using datetime.datetime.now() without specifying a timezone, which can lead to timezone-related bugs. Django provides timezone.now() for obtaining the current time in a timezone-aware manner.

Apply this diff to use the appropriate function:

-    created=datetime.datetime.now(),
+    created=timezone.now(),

Also, ensure you have imported timezone:

+from django.utils import timezone
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

            created=timezone.now(),
🧰 Tools
🪛 Ruff

76-76: datetime.datetime.now() called without a tz argument

(DTZ005)


17-17: ⚠️ Potential issue

Avoid duplicate related_name attributes

The user field and the event field both have related_name="event_participants". This will cause conflicts in Django's ORM because both foreign keys will create reverse relations with the same name. Each related_name should be unique to prevent conflicts.

Apply this diff to fix the issue for the user field:

 user = models.ForeignKey(
     to="accounts.User",
-    related_name="event_participants",
+    related_name="participations",
     on_delete=models.SET_NULL,
     null=True,
     db_index=True,
     verbose_name=_("user"),
 )

And for the event field:

 event = models.ForeignKey(
     to="events.Event",
     on_delete=models.SET_NULL,
-    related_name="event_participants",
+    related_name="participants",
     null=True,
     db_index=True,
     verbose_name=_("event"),
 )

This change ensures that the reverse relations are uniquely named and accurately reflect their purposes.

Committable suggestion was skipped due to low confidence.


37-37: ⚠️ Potential issue

Ensure unique related_name for the price field

The price field has related_name="participants", which may conflict with the related_name used in the event field if both are set to "participants". Each related_name should be unique to prevent reverse relation conflicts.

Apply this diff to assign a unique related_name:

 price = models.ForeignKey(
     to="events.PriceVariant",
     on_delete=models.SET_NULL,
     verbose_name=_("price"),
-    related_name="participants",
+    related_name="price_participants",
     null=True,
     db_index=False,
 )

This allows for clear and conflict-free reverse relationships from the PriceVariant model.

Committable suggestion was skipped due to low confidence.


59-83: ⚠️ Potential issue

Avoid returning HttpResponse from model methods

The register_for_event method is returning HttpResponse objects, which is not appropriate in the model layer. Models should handle data logic, not HTTP responses. This coupling can lead to maintenance challenges and violates separation of concerns.

Refactor the method to raise exceptions or return status indicators instead. Here's a suggested change:

 def register_for_event(cls, user, event, price):
     """
     Class method to handle registration for an event.
     """

     if cls.objects.filter(event=event).count() >= event.capacity:
-        return HttpResponse("This event is full.")
+        raise ValueError("This event is full.")

     if cls.objects.filter(user=user, event=event).exists():
-        return HttpResponse("You are already registered for this event.")
+        raise ValueError("You are already registered for this event.")

     if price is None:
-        return HttpResponse("You can't register for this event yet.")
+        raise ValueError("You can't register for this event yet.")

     cls.objects.create(
         created=timezone.now(),
         user=user,
         event=event,
         price=price,
         state=cls.State.WAITING
     )
-    return HttpResponse("Successfully registered for this event.")
+    return True

Then, handle these exceptions in your view to return appropriate HTTP responses to the user.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

    def register_for_event(cls, user, event, price):
        """
        Class method to handle registration for an event.
        """

        if cls.objects.filter(event=event).count() >= event.capacity:
            raise ValueError("This event is full.")

        if cls.objects.filter(user=user, event=event).exists():
            raise ValueError("You are already registered for this event.")

        if price is None:
            raise ValueError("You can't register for this event yet.")

        cls.objects.create(
            created=timezone.now(),
            user=user,
            event=event,
            price=price,
            state=cls.State.WAITING
        )
        return True
🧰 Tools
🪛 Ruff

76-76: datetime.datetime.now() called without a tz argument

(DTZ005)

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🧹 Outside diff range and nitpick comments (3)
fiesta/apps/events/templates/events/parts/create_event_form.html (1)

7-8: Consider enhancing form accessibility and user feedback.

While the form setup is correct, consider adding:

  • novalidate attribute to ensure server-side validation
  • aria-label for screen readers
  • Loading indicators for HTMX requests
-<form action="{% url "events:add-event" %}" method="post" {% if form.is_multipart %}enctype="multipart/form-data" hx-encoding="multipart/form-data"{% endif %}>
+<form 
+    action="{% url "events:add-event" %}" 
+    method="post" 
+    novalidate
+    aria-label="{% trans 'Create Event Form' %}"
+    {% if form.is_multipart %}
+        enctype="multipart/form-data" 
+        hx-encoding="multipart/form-data"
+    {% endif %}
+    hx-indicator="#spinner">
+    <div id="spinner" class="htmx-indicator">
+        {% trans "Loading..." %}
+    </div>
fiesta/apps/events/views/event.py (2)

321-324: Remove extra blank lines

Clean up the extra blank lines at the end of the file.

     def get_success_url(self) -> str:
         return reverse("events:participants", args=[self.kwargs.get("pk")])
-    
-    
-    
-    

293-307: Improve error handling and response structure

The current error handling in the post method could be improved:

  1. Raw HttpResponse usage doesn't follow REST conventions
  2. Error messages are not structured consistently

Consider implementing a structured JSON response:

+from django.http import JsonResponse
+
     def post(self, request, *args, **kwargs):
         validator = ParticipantValidator()
         try:
             price = validator.register_for_event(self.request.user, self.event)
             Participant.objects.create(
                 created=datetime.datetime.now(tz=datetime.UTC),
                 user=self.request.user,
                 event=self.event,
                 price=price
             )
-            return HttpResponse("Successfully registered for this event.")
+            return JsonResponse({
+                'status': 'success',
+                'message': 'Successfully registered for this event'
+            })
         except ValidationError as e:
-            return HttpResponse(e.message)
+            return JsonResponse({
+                'status': 'error',
+                'message': str(e.message)
+            }, status=400)

This provides a consistent response structure and proper HTTP status codes for error handling.

🧰 Tools
🪛 Ruff

298-298: datetime.datetime.now() called without a tz argument

(DTZ005)

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between 7add1a8 and 351c8d8.

📒 Files selected for processing (2)
  • fiesta/apps/events/templates/events/parts/create_event_form.html (1 hunks)
  • fiesta/apps/events/views/event.py (1 hunks)
🧰 Additional context used
🪛 Ruff
fiesta/apps/events/views/event.py

1-1: Missing required import: from __future__ import annotations

Insert required import: from __future__ import annotations

(I002)


3-3: django.db.models imported but unused

Remove unused import: django.db.models

(F401)


6-6: django.template.loader.render_to_string imported but unused

Remove unused import: django.template.loader.render_to_string

(F401)


9-9: django.conf.settings imported but unused

Remove unused import: django.conf.settings

(F401)


14-14: django.views.generic.View imported but unused

Remove unused import: django.views.generic.View

(F401)


21-21: ..models.PriceVariant imported but unused

Remove unused import: ..models.PriceVariant

(F401)


26-26: Redefinition of unused HttpRequest from line 5

(F811)


28-28: apps.events.models.price_variant.EventPriceVariantType imported but unused

Remove unused import: apps.events.models.price_variant.EventPriceVariantType

(F401)


186-186: Star-arg unpacking after a keyword argument is strongly discouraged

(B026)


298-298: datetime.datetime.now() called without a tz argument

(DTZ005)

🔇 Additional comments (2)
fiesta/apps/events/templates/events/parts/create_event_form.html (2)

1-5: LGTM: Template tags are properly loaded.

The necessary template tags for forms, events, breadcrumbs, and internationalization are correctly imported.


1-17: Verify integration with event questions feature.

Based on the PR comments, there's a suggestion to add questions to events. The current form template doesn't show any provision for this functionality.

Let's check if there are any related models or forms for event questions:

Consider planning for the integration of event questions as suggested in the PR comments. This might involve:

  1. Adding a related model for event questions
  2. Extending this form to include question management
  3. Implementing dynamic question addition through HTMX

name="{{ redirect_field_name }}"
value="{{ redirect_field_value }}" />
{% endif %}
{{form}}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Enhance form rendering with proper structure and styling.

The current form rendering ({{form}}) outputs fields without structure. Consider using template filters for better control over layout and styling.

-{{form}}
+<div class="form-container">
+    {% for field in form %}
+        <div class="form-group">
+            {{ field|as_label }}
+            {{ field|as_widget_field }}
+            {% if field.errors %}
+                <div class="error-feedback">
+                    {{ field.errors }}
+                </div>
+            {% endif %}
+            {% if field.help_text %}
+                <small class="help-text">{{ field.help_text }}</small>
+            {% endif %}
+        </div>
+    {% endfor %}
+    <button type="submit" class="btn btn-primary">
+        {% trans "Create Event" %}
+    </button>
+</div>

Committable suggestion was skipped due to low confidence.

fiesta/apps/events/views/event.py Show resolved Hide resolved
Comment on lines +155 to +158
organizers_data = [{'name': organizer.user.get_full_name()} for organizer in self.event.event_organizers.all()]
print(organizers_data)
context['event'] = self.event
context['organizers_json'] = json.dumps(organizers_data)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Improve JSON serialization of organizer data

The current implementation has potential issues:

  1. Direct JSON serialization without proper error handling
  2. Missing type hints for better code maintainability

Consider this improved implementation:

-        organizers_data = [{'name': organizer.user.get_full_name()} for organizer in self.event.event_organizers.all()]
-        context['organizers_json'] = json.dumps(organizers_data)
+        try:
+            organizers_data: list[dict[str, str]] = [
+                {'name': organizer.user.get_full_name()}
+                for organizer in self.event.event_organizers.all()
+            ]
+            context['organizers_json'] = json.dumps(organizers_data, ensure_ascii=False)
+        except Exception as e:
+            logger.error(f"Failed to serialize organizer data: {e}")
+            context['organizers_json'] = '[]'

Comment on lines +281 to +307
class EventParticipantRegister(
EnsureInSectionSpaceViewMixin,
EnsureInternationalUserViewMixin,
CreateView):

model = Participant

def dispatch(self, request, *args, **kwargs):
self.event = get_object_or_404(Event, pk=self.kwargs.get("pk"))
return super().dispatch(request, *args, **kwargs)


def post(self, request, *args, **kwargs):
validator = ParticipantValidator()
try:
price = validator.register_for_event(self.request.user, self.event)
Participant.objects.create(
created=datetime.datetime.now(),
user=self.request.user,
event=self.event,
price=price
)
return HttpResponse("Sucsessfully registered for this event.")
except ValidationError as e:

return HttpResponse(e.message)

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Add CSRF protection and fix timezone usage

Several issues in the EventParticipantRegister view need to be addressed:

  1. Missing CSRF protection
  2. Using naive datetime
  3. Typo in success message

Apply these fixes:

 class EventParticipantRegister(
     EnsureInSectionSpaceViewMixin, 
     EnsureInternationalUserViewMixin,
     CreateView):
+    from django.views.decorators.csrf import csrf_protect
+    from django.utils.decorators import method_decorator
+    
+    @method_decorator(csrf_protect)
     def post(self, request, *args, **kwargs):
         validator = ParticipantValidator()
         try:
             price = validator.register_for_event(self.request.user, self.event)
             Participant.objects.create(
-            created=datetime.datetime.now(),
+            created=datetime.datetime.now(tz=datetime.UTC),
             user=self.request.user,
             event=self.event,
             price=price
         )
-            return HttpResponse("Sucsessfully registered for this event.")
+            return HttpResponse("Successfully registered for this event.")
         except ValidationError as e:
             return HttpResponse(e.message)

Committable suggestion was skipped due to low confidence.

🧰 Tools
🪛 Ruff

298-298: datetime.datetime.now() called without a tz argument

(DTZ005)

Comment on lines +1 to +40
import json
from typing import Any
from django.db import models

from django.http import HttpRequest, HttpResponse
from django.template.loader import render_to_string
from django.shortcuts import get_object_or_404
from _operator import attrgetter
from django.conf import settings


from django.contrib.postgres.search import SearchVector
from django.forms import TextInput
from django.views.generic import CreateView, DetailView, UpdateView, DeleteView, View
import django_tables2 as tables

from django.utils.translation import gettext_lazy as _
from django.urls import reverse
from django_filters import CharFilter, ChoiceFilter

from ..models import Participant, Organizer, PriceVariant
from ..models.event import Event
from apps.utils.views import AjaxViewMixin
from apps.fiestaforms.views.htmx import HtmxFormViewMixin
from django.contrib.messages.views import SuccessMessageMixin
from apps.plugins.middleware.plugin import HttpRequest
from apps.events.forms.event import AddEventForm
from apps.events.models.price_variant import EventPriceVariantType

from ...fiestatables.filters import BaseFilterSet, ProperDateFromToRangeFilter
from ...fiestatables.views.tables import FiestaTableView
from ...sections.views.mixins.membership import EnsurePrivilegedUserViewMixin, EnsureLocalUserViewMixin, EnsureInternationalUserViewMixin
from ...sections.views.mixins.section_space import EnsureInSectionSpaceViewMixin
from ...utils.breadcrumbs import with_breadcrumb, with_plugin_home_breadcrumb, with_object_breadcrumb
from allauth.account.utils import get_next_redirect_url
from django.contrib.auth import REDIRECT_FIELD_NAME
import datetime
from apps.events.services.participant_validator import ParticipantValidator
from django.core.exceptions import ValidationError

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Clean up and organize imports

Several issues with imports need to be addressed:

  1. Remove unused imports: models, render_to_string, settings, View, PriceVariant, EventPriceVariantType
  2. Fix duplicate HttpRequest import
  3. Add from __future__ import annotations for proper type hints support

Apply this diff to clean up imports:

+from __future__ import annotations

import json
import datetime
from typing import Any
-from django.db import models
from django.http import HttpRequest, HttpResponse
-from django.template.loader import render_to_string
from django.shortcuts import get_object_or_404
from _operator import attrgetter
-from django.conf import settings

from django.contrib.postgres.search import SearchVector
from django.forms import TextInput
-from django.views.generic import CreateView, DetailView, UpdateView, DeleteView, View
+from django.views.generic import CreateView, DetailView, UpdateView, DeleteView
import django_tables2 as tables
from django.utils.translation import gettext_lazy as _
from django.urls import reverse
from django_filters import CharFilter, ChoiceFilter

-from ..models import Participant, Organizer, PriceVariant
+from ..models import Participant, Organizer
from ..models.event import Event
from apps.utils.views import AjaxViewMixin
from apps.fiestaforms.views.htmx import HtmxFormViewMixin
from django.contrib.messages.views import SuccessMessageMixin
-from apps.plugins.middleware.plugin import HttpRequest
from apps.events.forms.event import AddEventForm
-from apps.events.models.price_variant import EventPriceVariantType
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
import json
from typing import Any
from django.db import models
from django.http import HttpRequest, HttpResponse
from django.template.loader import render_to_string
from django.shortcuts import get_object_or_404
from _operator import attrgetter
from django.conf import settings
from django.contrib.postgres.search import SearchVector
from django.forms import TextInput
from django.views.generic import CreateView, DetailView, UpdateView, DeleteView, View
import django_tables2 as tables
from django.utils.translation import gettext_lazy as _
from django.urls import reverse
from django_filters import CharFilter, ChoiceFilter
from ..models import Participant, Organizer, PriceVariant
from ..models.event import Event
from apps.utils.views import AjaxViewMixin
from apps.fiestaforms.views.htmx import HtmxFormViewMixin
from django.contrib.messages.views import SuccessMessageMixin
from apps.plugins.middleware.plugin import HttpRequest
from apps.events.forms.event import AddEventForm
from apps.events.models.price_variant import EventPriceVariantType
from ...fiestatables.filters import BaseFilterSet, ProperDateFromToRangeFilter
from ...fiestatables.views.tables import FiestaTableView
from ...sections.views.mixins.membership import EnsurePrivilegedUserViewMixin, EnsureLocalUserViewMixin, EnsureInternationalUserViewMixin
from ...sections.views.mixins.section_space import EnsureInSectionSpaceViewMixin
from ...utils.breadcrumbs import with_breadcrumb, with_plugin_home_breadcrumb, with_object_breadcrumb
from allauth.account.utils import get_next_redirect_url
from django.contrib.auth import REDIRECT_FIELD_NAME
import datetime
from apps.events.services.participant_validator import ParticipantValidator
from django.core.exceptions import ValidationError
from __future__ import annotations
import json
import datetime
from typing import Any
from django.http import HttpRequest, HttpResponse
from django.shortcuts import get_object_or_404
from _operator import attrgetter
from django.contrib.postgres.search import SearchVector
from django.forms import TextInput
from django.views.generic import CreateView, DetailView, UpdateView, DeleteView
import django_tables2 as tables
from django.utils.translation import gettext_lazy as _
from django.urls import reverse
from django_filters import CharFilter, ChoiceFilter
from ..models import Participant, Organizer
from ..models.event import Event
from apps.utils.views import AjaxViewMixin
from apps.fiestaforms.views.htmx import HtmxFormViewMixin
from django.contrib.messages.views import SuccessMessageMixin
from apps.events.forms.event import AddEventForm
from ...fiestatables.filters import BaseFilterSet, ProperDateFromToRangeFilter
from ...fiestatables.views.tables import FiestaTableView
from ...sections.views.mixins.membership import EnsurePrivilegedUserViewMixin, EnsureLocalUserViewMixin, EnsureInternationalUserViewMixin
from ...sections.views.mixins.section_space import EnsureInSectionSpaceViewMixin
from ...utils.breadcrumbs import with_breadcrumb, with_plugin_home_breadcrumb, with_object_breadcrumb
from allauth.account.utils import get_next_redirect_url
from django.contrib.auth import REDIRECT_FIELD_NAME
from apps.events.services.participant_validator import ParticipantValidator
from django.core.exceptions import ValidationError
🧰 Tools
🪛 Ruff

1-1: Missing required import: from __future__ import annotations

Insert required import: from __future__ import annotations

(I002)


3-3: django.db.models imported but unused

Remove unused import: django.db.models

(F401)


6-6: django.template.loader.render_to_string imported but unused

Remove unused import: django.template.loader.render_to_string

(F401)


9-9: django.conf.settings imported but unused

Remove unused import: django.conf.settings

(F401)


14-14: django.views.generic.View imported but unused

Remove unused import: django.views.generic.View

(F401)


21-21: ..models.PriceVariant imported but unused

Remove unused import: ..models.PriceVariant

(F401)


26-26: Redefinition of unused HttpRequest from line 5

(F811)


28-28: apps.events.models.price_variant.EventPriceVariantType imported but unused

Remove unused import: apps.events.models.price_variant.EventPriceVariantType

(F401)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants