• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

gcivil-nyu-org / wed-fall24-team1 / #71

27 Nov 2024 11:21PM UTC coverage: 90.958% (+0.8%) from 90.197%
#71

push

coveralls-python

web-flow
Merge develop into main - release 8 (#232)

Co-authored-by: Shubham Garg <sg8311@nyu.edu>

614 of 665 new or added lines in 9 files covered. (92.33%)

7 existing lines in 2 files now uncovered.

3229 of 3550 relevant lines covered (90.96%)

0.91 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

76.47
/src/services/forms.py
1
# services/forms.py
2
from decimal import Decimal
1✔
3

4
from better_profanity import profanity
1✔
5
from django import forms
1✔
6
from django.forms import formset_factory
1✔
7
from geopy import Nominatim
1✔
8
from geopy.exc import GeocoderTimedOut, GeocoderServiceError
1✔
9
from django.forms import BaseFormSet
1✔
10

11

12
class ServiceForm(forms.Form):
1✔
13
    CATEGORY_CHOICES = [
1✔
14
        ("Mental Health Center", "Mental Health Center"),
15
        ("Homeless Shelter", "Homeless Shelter"),
16
        ("Food Pantry", "Food Pantry"),
17
        ("Restroom", "Restroom"),
18
    ]
19

20
    name = forms.CharField(max_length=255)
1✔
21
    address = forms.CharField(widget=forms.Textarea)
1✔
22
    category = forms.ChoiceField(choices=CATEGORY_CHOICES)
1✔
23
    is_active = forms.BooleanField(
1✔
24
        required=False, initial=True, label="Is the service currently available?"
25
    )
26
    announcement = forms.CharField(  # Add this field
1✔
27
        widget=forms.Textarea(
28
            attrs={"rows": 3, "placeholder": "Enter your announcement here"}
29
        ),
30
        required=False,
31
        max_length=500,
32
        help_text="Use this space to inform users about temporary changes or important updates.",
33
    )
34

35
    def clean(self):
1✔
36
        cleaned_data = super().clean()
1✔
37
        address = cleaned_data.get("address")
1✔
38

39
        if address:
1✔
40
            geolocator = Nominatim(user_agent="public_service_finder")
1✔
41
            try:
1✔
42
                location = geolocator.geocode(address)
1✔
43
                if location:
1✔
44
                    cleaned_data["latitude"] = Decimal(str(location.latitude))
1✔
45
                    cleaned_data["longitude"] = Decimal(str(location.longitude))
1✔
46
                else:
47
                    self.add_error(
×
48
                        "address",
49
                        "Unable to geocode the given address. Please check if the address is correct.",
50
                    )
51
            except (GeocoderTimedOut, GeocoderServiceError):
×
52
                self.add_error(
×
53
                    "address",
54
                    "Error occurred while geocoding the address. Please try again later.",
55
                )
56

57
        # Translate category to backend value
58
        category_translation = {
1✔
59
            "Mental Health Center": "MENTAL",
60
            "Homeless Shelter": "SHELTER",
61
            "Food Pantry": "FOOD",
62
            "Restroom": "RESTROOM",
63
        }
64
        cleaned_data["category"] = category_translation[cleaned_data["category"]]
1✔
65
        if "announcement" in cleaned_data:
1✔
66
            announcement = cleaned_data["announcement"]
1✔
67
            if announcement:
1✔
NEW
68
                profanity.load_censor_words()
×
NEW
69
                cleaned_data["announcement"] = profanity.censor(announcement)
×
70

71
        return cleaned_data
1✔
72

73

74
class DescriptionItemForm(forms.Form):
1✔
75
    key = forms.CharField(max_length=100)
1✔
76
    value = forms.CharField(widget=forms.Textarea(attrs={"rows": 3}))
1✔
77

78

79
class CustomDescriptionFormSet(BaseFormSet):
1✔
80
    def clean(self):
1✔
81
        """
82
        Adds validation to check for duplicate keys in the formset.
83
        """
84
        if any(self.errors):
1✔
85
            return
×
86

87
        keys = []
1✔
88
        duplicate_keys = set()
1✔
89

90
        # First pass: collect all keys and identify duplicates
91
        for form in self.forms:
1✔
92
            if self.can_delete and self._should_delete_form(form):
1✔
93
                continue
×
94

95
            if form.cleaned_data:
1✔
96
                key = form.cleaned_data.get("key")
1✔
97
                if key:
1✔
98
                    print(f"Processing key: {key}")  # Debug print
1✔
99
                    if key in keys:
1✔
100
                        duplicate_keys.add(key)
×
101
                    keys.append(key)
1✔
102

103
        # Second pass: add errors to all forms with duplicate keys
104
        if duplicate_keys:
1✔
105
            for form in self.forms:
×
106
                if self.can_delete and self._should_delete_form(form):
×
107
                    continue
×
108

109
                if form.cleaned_data:
×
110
                    key = form.cleaned_data.get("key")
×
111
                    if key in duplicate_keys:
×
112
                        form.add_error(
×
113
                            "key",
114
                            f"Duplicate key detected: '{key}'. Each key must be unique.",
115
                        )
116

117
            # Raise the validation error with all duplicate keys listed
118
            raise forms.ValidationError(
×
119
                f"Duplicate keys found: {', '.join(duplicate_keys)}. Each key must be unique."
120
            )
121

122
        print(f"All keys found: {keys}")  # Debug print for all keys
1✔
123
        return self.cleaned_data
1✔
124

125

126
DescriptionFormSet = formset_factory(
1✔
127
    DescriptionItemForm,
128
    formset=CustomDescriptionFormSet,
129
    extra=0,
130
    can_delete=True,
131
)
132

133

134
class ReviewResponseForm(forms.Form):
1✔
135
    responseText = forms.CharField(  # Changed from 'response' to 'responseText'
1✔
136
        widget=forms.Textarea(
137
            attrs={
138
                "class": "w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500",
139
                "rows": "4",
140
                "placeholder": "Enter your response to this review...",
141
            }
142
        ),
143
        required=True,
144
    )
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc