Skip to content

Commit

Permalink
Merge pull request #924 from ae-utbm/unique-student-card
Browse files Browse the repository at this point in the history
Make student card unique per user
  • Loading branch information
NaNoMelo authored Dec 15, 2024
2 parents 53b13e7 + 0631c77 commit 0f00387
Show file tree
Hide file tree
Showing 16 changed files with 353 additions and 434 deletions.
9 changes: 4 additions & 5 deletions core/management/commands/populate.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ def reset_index(self, *args):
# sqlite doesn't support this operation
return
sqlcmd = StringIO()
call_command("sqlsequencereset", *args, stdout=sqlcmd)
call_command("sqlsequencereset", "--no-color", *args, stdout=sqlcmd)
cursor = connection.cursor()
cursor.execute(sqlcmd.getvalue())

Expand Down Expand Up @@ -137,11 +137,10 @@ def handle(self, *args, **options):
)

self.reset_index("club")
for bar_id, bar_name in settings.SITH_COUNTER_BARS:
Counter(id=bar_id, name=bar_name, club=bar_club, type="BAR").save()
self.reset_index("counter")
counters = [
*[
Counter(id=bar_id, name=bar_name, club=bar_club, type="BAR")
for bar_id, bar_name in settings.SITH_COUNTER_BARS
],
Counter(name="Eboutic", club=main_club, type="EBOUTIC"),
Counter(name="AE", club=main_club, type="OFFICE"),
Counter(name="Vidage comptes AE", club=main_club, type="OFFICE"),
Expand Down
7 changes: 6 additions & 1 deletion core/static/core/colors.scss
Original file line number Diff line number Diff line change
Expand Up @@ -29,4 +29,9 @@ $shadow-color: rgb(223, 223, 223);

$background-button-color: hsl(0, 0%, 95%);

$deepblue: #354a5f;
$deepblue: #354a5f;

@mixin shadow {
box-shadow: rgba(60, 64, 67, 0.3) 0 1px 3px 0,
rgba(60, 64, 67, 0.15) 0 4px 8px 3px;
}
33 changes: 31 additions & 2 deletions core/static/core/style.scss
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,32 @@ body {
}
}

[tooltip] {
position: relative;
}

[tooltip]::before {
@include shadow;
opacity: 0;
z-index: 1;
content: attr(tooltip);
background: hsl(219.6, 20.8%, 96%);
color: $black-color;
border: 0.5px solid hsl(0, 0%, 50%);
;
border-radius: 5px;
padding: 5px;
top: 1em;
position: absolute;
margin-top: 5px;
white-space: nowrap;
transition: opacity 500ms ease-out;
}

[tooltip]:hover::before {
opacity: 1;
}

.ib {
display: inline-block;
padding: 1px;
Expand Down Expand Up @@ -79,8 +105,7 @@ body {
}

.shadow {
box-shadow: rgba(60, 64, 67, 0.3) 0 1px 3px 0,
rgba(60, 64, 67, 0.15) 0 4px 8px 3px;
@include shadow;
}

.w_big {
Expand Down Expand Up @@ -308,6 +333,7 @@ body {
font-size: 120%;
background-color: unset;
position: relative;

&:after {
content: '';
position: absolute;
Expand All @@ -318,14 +344,17 @@ body {
border-radius: 2px;
transition: all 0.2s ease-in-out;
}

&:hover:after {
border-bottom-color: darken($primary-neutral-light-color, 20%);
}

&.active:after {
border-bottom-color: $primary-dark-color;
}
}
}

section {
padding: 20px;
}
Expand Down
5 changes: 3 additions & 2 deletions core/templates/core/user_preferences.jinja
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,9 @@
{% endif %}


{% if student_card %}
{{ student_card }}
{% if student_card_fragment %}
<h3>{% trans %}Student card{% endtrans %}</h3>
{{ student_card_fragment }}
<p class="justify">
{% trans %}You can add a card by asking at a counter or add it yourself here. If you want to manually
add a student card yourself, you'll need a NFC reader. We store the UID of the card which is 14 characters long.{% endtrans %}
Expand Down
10 changes: 2 additions & 8 deletions core/views/user.py
Original file line number Diff line number Diff line change
Expand Up @@ -559,10 +559,6 @@ class UserPreferencesView(UserTabsMixin, CanEditMixin, UpdateView):
context_object_name = "profile"
current_tab = "prefs"

def get_object(self, queryset=None):
user = get_object_or_404(User, pk=self.kwargs["user_id"])
return user

def get_form_kwargs(self):
kwargs = super().get_form_kwargs()
pref = self.object.preferences
Expand All @@ -572,12 +568,10 @@ def get_form_kwargs(self):
def get_context_data(self, **kwargs):
kwargs = super().get_context_data(**kwargs)

if not (
hasattr(self.object, "trombi_user") and self.request.user.trombi_user.trombi
):
if not hasattr(self.object, "trombi_user"):
kwargs["trombi_form"] = UserTrombiForm()
if hasattr(self.object, "customer"):
kwargs["student_card"] = StudentCardFormView.get_template_data(
kwargs["student_card_fragment"] = StudentCardFormView.get_template_data(
self.object.customer
).render(self.request)
return kwargs
Expand Down
4 changes: 1 addition & 3 deletions counter/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,9 +50,7 @@ class StudentCardForm(forms.ModelForm):
class Meta:
model = StudentCard
fields = ["uid"]
widgets = {
"uid": NFCTextInput,
}
widgets = {"uid": NFCTextInput}

def clean(self):
cleaned_data = super().clean()
Expand Down
53 changes: 53 additions & 0 deletions counter/migrations/0026_alter_studentcard_customer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# Generated by Django 4.2.17 on 2024-12-08 13:30
from operator import attrgetter

import django.db.models.deletion
from django.db import migrations, models
from django.db.migrations.state import StateApps
from django.db.models import Count


def delete_duplicates(apps: StateApps, schema_editor):
"""Delete cards of users with more than one student cards.
For all users who have more than one registered student card, all
the cards except the last one are deleted.
"""
Customer = apps.get_model("counter", "Customer")
StudentCard = apps.get_model("counter", "StudentCard")
customers = (
Customer.objects.annotate(nb_cards=Count("student_cards"))
.filter(nb_cards__gt=1)
.prefetch_related("student_cards")
)
to_delete = [
card.id
for customer in customers
for card in sorted(customer.student_cards.all(), key=attrgetter("id"))[:-1]
]
StudentCard.objects.filter(id__in=to_delete).delete()


class Migration(migrations.Migration):
dependencies = [("counter", "0025_remove_product_parent_product_and_more")]

operations = [
migrations.RunPython(delete_duplicates, migrations.RunPython.noop),
migrations.AlterField(
model_name="studentcard",
name="customer",
field=models.OneToOneField(
on_delete=django.db.models.deletion.CASCADE,
related_name="student_card",
to="counter.customer",
verbose_name="student card",
),
),
migrations.AlterModelOptions(
name="studentcard",
options={
"verbose_name": "student card",
"verbose_name_plural": "student cards",
},
),
]
14 changes: 8 additions & 6 deletions counter/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -1138,20 +1138,22 @@ class StudentCard(models.Model):
uid = models.CharField(
_("uid"), max_length=UID_SIZE, unique=True, validators=[MinLengthValidator(4)]
)
customer = models.ForeignKey(
customer = models.OneToOneField(
Customer,
related_name="student_cards",
verbose_name=_("student cards"),
null=False,
blank=False,
related_name="student_card",
verbose_name=_("student card"),
on_delete=models.CASCADE,
)

class Meta:
verbose_name = _("student card")
verbose_name_plural = _("student cards")

def __str__(self):
return self.uid

@staticmethod
def is_valid(uid):
def is_valid(uid: str) -> bool:
return (
(uid.isupper() or uid.isnumeric())
and len(uid) == StudentCard.UID_SIZE
Expand Down
3 changes: 2 additions & 1 deletion counter/templates/counter/counter_click.jinja
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,8 @@
<p>{% trans %}Amount: {% endtrans %}{{ customer.amount }} €</p>

{% if counter.type == 'BAR' %}
{{ student_card }}
<h5>{% trans %}Student card{% endtrans %}</h3>
{{ student_card_fragment }}
{% endif %}
</div>

Expand Down
50 changes: 25 additions & 25 deletions counter/templates/counter/fragments/create_student_card.jinja
Original file line number Diff line number Diff line change
@@ -1,29 +1,29 @@
<div id="student_card_form">
<h3>{% trans %}Add a student card{% endtrans %}</h3>
<form
hx-post="{{ action }}"
hx-swap="outerHTML"
hx-target="#student_card_form"
>
{% csrf_token %}
{{ form.as_p() }}
<input type="submit" value="{% trans %}Go{% endtrans %}"/>

</form>
<h6>{% trans %}Registered cards{% endtrans %}</h6>
{% if student_cards %}

<ul>
{% for card in student_cards %}
<li>
{{ card.uid }}
<a href="{{ url('counter:delete_student_card', customer_id=customer.pk, card_id=card.id) }}">
{% trans %}Delete{% endtrans %}
</a>
</li>
{% endfor %}
</ul>
{% else %}
{% if not customer.student_card %}
<form
hx-post="{{ action }}"
hx-swap="outerHTML"
hx-target="#student_card_form"
>
{% csrf_token %}
{{ form.as_p() }}
<input type="submit" value="{% trans %}Go{% endtrans %}"/>
</form>
<em class="no-cards">{% trans %}No student card registered.{% endtrans %}</em>
{% else %}
<p>
<span tooltip="{% trans uid=customer.student_card.uid %}uid: {{ uid }} {% endtrans %}">
{% trans %}Card registered{% endtrans %}
<i class="fa fa-check" style="color: green"></i>
</span>
&nbsp; - &nbsp;
<button
hx-get="{{ url('counter:delete_student_card', customer_id=customer.pk) }}"
hx-swap="outerHTML"
hx-target="#student_card_form"
>
{% trans %}Delete{% endtrans %}
</button>
</p>
{% endif %}
</div>
15 changes: 15 additions & 0 deletions counter/templates/counter/fragments/delete_student_card.jinja
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<div id="student_card_form">
<form hx-post="{{ action }}" hx-swap="outerHTML" hx-target="#student_card_form">
{% csrf_token %}
<p>{% trans obj=object %}Are you sure you want to delete "{{ obj }}"?{% endtrans %}</p>
<input type="submit" value="{% trans %}Confirm{% endtrans %}" />
<input
hx-get="{{ action_cancel }}"
hx-swap="outerHTML"
hx-target="#student_card_form"
type="submit"
name="cancel"
value="{% trans %}Cancel{% endtrans %}"
/>
</form>
</div>
Loading

0 comments on commit 0f00387

Please sign in to comment.