1
0
mirror of https://gitlab.crans.org/bde/nk20 synced 2025-08-02 21:54:24 +02:00

Compare commits

...

12 Commits

Author SHA1 Message Date
aeltheos
b811b58f2d Merge branch 'nix-shell' into 'main'
Nix shell

See merge request bde/nk20!201
2025-07-22 23:29:57 +02:00
quark
12477b33cb Merge branch 'fix_activity_form' into 'main'
fix organizer field error

See merge request bde/nk20!333
2025-07-22 18:55:27 +02:00
quark
8c3ae338ea fix organizer field error 2025-07-22 18:20:05 +02:00
ehouarn
4975c1ab6f Merge branch 'translations' into 'main'
Translations

See merge request bde/nk20!332
2025-07-19 18:29:18 +02:00
Ehouarn
61999a31a5 Wei details 2025-07-19 18:04:14 +02:00
ehouarn
b217f7ceec Merge branch 'wei' into 'main'
Wei

See merge request bde/nk20!331
2025-07-19 17:27:20 +02:00
Ehouarn
2755a5f7ab Minor fail 2025-07-19 17:10:25 +02:00
Ehouarn
9ab4df94e6 Minor fixes 2025-07-19 16:55:07 +02:00
Ehouarn
edb6abfff5 Add fee field to WEIRegistration to be able to sort on validation status 2025-07-19 16:24:25 +02:00
Ehouarn
03c1bb41b6 First of many 2025-07-18 23:49:34 +02:00
Yoann Beaugnon
dde1baa25c typo 2022-08-21 19:50:53 +02:00
Yoann Beaugnon
7a7ee47e0b Add two shell.nix to enable easier development on nixos. 2022-08-21 19:46:11 +02:00
14 changed files with 208 additions and 72 deletions

1
.gitignore vendored
View File

@@ -48,7 +48,6 @@ backups/
env/ env/
venv/ venv/
db.sqlite3 db.sqlite3
shell.nix
# ansibles customs host # ansibles customs host
ansible/host_vars/*.yaml ansible/host_vars/*.yaml

View File

@@ -32,7 +32,7 @@ class ActivityForm(forms.ModelForm):
def clean_organizer(self): def clean_organizer(self):
organizer = self.cleaned_data['organizer'] organizer = self.cleaned_data['organizer']
if not organizer.note.is_active: if not organizer.note.is_active:
self.add_error('organiser', _('The note of this club is inactive.')) self.add_error('organizer', _('The note of this club is inactive.'))
return organizer return organizer
def clean_date_end(self): def clean_date_end(self):

View File

@@ -6,7 +6,7 @@ from django.conf import settings
from django.db.models.signals import post_save from django.db.models.signals import post_save
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from .signals import save_user_profile from .signals import save_user_profile, update_wei_registration_fee_on_membership_creation, update_wei_registration_fee_on_club_change
class MemberConfig(AppConfig): class MemberConfig(AppConfig):
@@ -17,7 +17,16 @@ class MemberConfig(AppConfig):
""" """
Define app internal signals to interact with other apps Define app internal signals to interact with other apps
""" """
from .models import Membership, Club
post_save.connect( post_save.connect(
save_user_profile, save_user_profile,
sender=settings.AUTH_USER_MODEL, sender=settings.AUTH_USER_MODEL,
) )
post_save.connect(
update_wei_registration_fee_on_membership_creation,
sender=Membership
)
post_save.connect(
update_wei_registration_fee_on_club_change,
sender=Club
)

View File

@@ -13,3 +13,25 @@ def save_user_profile(instance, created, raw, **_kwargs):
instance.profile.email_confirmed = True instance.profile.email_confirmed = True
instance.profile.registration_valid = True instance.profile.registration_valid = True
instance.profile.save() instance.profile.save()
def update_wei_registration_fee_on_membership_creation(sender, instance, created, **kwargs):
if created:
from wei.models import WEIRegistration
if instance.club.id == 1 or instance.club.id == 2:
registrations = WEIRegistration.objects.filter(
user=instance.user,
wei__year=instance.date_start.year,
)
for r in registrations:
r.save()
def update_wei_registration_fee_on_club_change(sender, instance, **kwargs):
from wei.models import WEIRegistration
if instance.id == 1 or instance.id == 2:
registrations = WEIRegistration.objects.filter(
wei__year=instance.membership_start.year,
)
for r in registrations:
r.save()

View File

@@ -1,11 +1,11 @@
# Copyright (C) 2018-2025 by BDE ENS Paris-Saclay # Copyright (C) 2018-2025 by BDE ENS Paris-Saclay
# SPDX-License-Identifier: GPL-3.0-or-later # SPDX-License-Identifier: GPL-3.0-or-later
from .registration import WEIForm, WEIRegistrationForm, WEIRegistration1AForm, WEIRegistration2AForm, WEIMembership1AForm, \ from .registration import WEIForm, WEIRegistrationForm, WEIMembership1AForm, \
WEIMembershipForm, BusForm, BusTeamForm WEIMembershipForm, BusForm, BusTeamForm
from .surveys import WEISurvey, WEISurveyInformation, WEISurveyAlgorithm, CurrentSurvey from .surveys import WEISurvey, WEISurveyInformation, WEISurveyAlgorithm, CurrentSurvey
__all__ = [ __all__ = [
'WEIForm', 'WEIRegistrationForm', 'WEIRegistration1AForm', 'WEIRegistration2AForm', 'WEIMembership1AForm', 'WEIMembershipForm', 'BusForm', 'BusTeamForm', 'WEIForm', 'WEIRegistrationForm', 'WEIMembership1AForm', 'WEIMembershipForm', 'BusForm', 'BusTeamForm',
'WEISurvey', 'WEISurveyInformation', 'WEISurveyAlgorithm', 'CurrentSurvey', 'WEISurvey', 'WEISurveyInformation', 'WEISurveyAlgorithm', 'CurrentSurvey',
] ]

View File

@@ -44,7 +44,7 @@ class WEIRegistrationForm(forms.ModelForm):
fields = [ fields = [
'user', 'soge_credit', 'birth_date', 'gender', 'clothing_size', 'user', 'soge_credit', 'birth_date', 'gender', 'clothing_size',
'health_issues', 'emergency_contact_name', 'emergency_contact_phone', 'health_issues', 'emergency_contact_name', 'emergency_contact_phone',
'first_year', 'information_json', 'deposit_check' 'first_year', 'information_json', 'deposit_check', 'deposit_type'
] ]
widgets = { widgets = {
"user": Autocomplete( "user": Autocomplete(
@@ -62,27 +62,14 @@ class WEIRegistrationForm(forms.ModelForm):
"deposit_check": forms.BooleanField( "deposit_check": forms.BooleanField(
required=False, required=False,
), ),
}
class WEIRegistration2AForm(WEIRegistrationForm):
class Meta(WEIRegistrationForm.Meta):
fields = WEIRegistrationForm.Meta.fields + ['deposit_type']
widgets = WEIRegistrationForm.Meta.widgets.copy()
widgets.update({
"deposit_type": forms.RadioSelect(), "deposit_type": forms.RadioSelect(),
}) }
class WEIRegistration1AForm(WEIRegistrationForm):
class Meta(WEIRegistrationForm.Meta):
fields = WEIRegistrationForm.Meta.fields
class WEIChooseBusForm(forms.Form): class WEIChooseBusForm(forms.Form):
bus = forms.ModelMultipleChoiceField( bus = forms.ModelMultipleChoiceField(
queryset=Bus.objects, queryset=Bus.objects,
label=_("bus"), label=_("Bus"),
help_text=_("This choice is not definitive. The WEI organizers are free to attribute for you a bus and a team," help_text=_("This choice is not definitive. The WEI organizers are free to attribute for you a bus and a team,"
+ " in particular if you are a free eletron."), + " in particular if you are a free eletron."),
widget=CheckboxSelectMultiple(), widget=CheckboxSelectMultiple(),

View File

@@ -0,0 +1,23 @@
# Generated by Django 5.2.4 on 2025-07-19 12:17
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('wei', '0015_remove_weiclub_caution_amount_and_more'),
]
operations = [
migrations.AddField(
model_name='weiregistration',
name='fee',
field=models.PositiveIntegerField(blank=True, default=0, verbose_name='fee'),
),
migrations.AlterField(
model_name='weiclub',
name='fee_soge_credit',
field=models.PositiveIntegerField(default=2000, verbose_name='membership fee (soge credit)'),
),
]

View File

@@ -285,6 +285,12 @@ class WEIRegistration(models.Model):
"encoded in JSON"), "encoded in JSON"),
) )
fee = models.PositiveIntegerField(
default=0,
verbose_name=_('fee'),
blank=True,
)
class Meta: class Meta:
unique_together = ('user', 'wei',) unique_together = ('user', 'wei',)
verbose_name = _("WEI User") verbose_name = _("WEI User")
@@ -309,7 +315,25 @@ class WEIRegistration(models.Model):
self.information_json = json.dumps(information, indent=2) self.information_json = json.dumps(information, indent=2)
@property @property
def fee(self): def is_validated(self):
try:
return self.membership is not None
except AttributeError:
return False
@property
def validation_status(self):
"""
Define an order to have easier access to validatable registrations
"""
if self.fee + (self.wei.deposit_amount if self.deposit_type == 'note' else 0) > self.user.note.balance:
return 2
elif self.first_year:
return 1
else:
return 0
def calculate_fee(self):
bde = Club.objects.get(pk=1) bde = Club.objects.get(pk=1)
kfet = Club.objects.get(pk=2) kfet = Club.objects.get(pk=2)
@@ -336,12 +360,9 @@ class WEIRegistration(models.Model):
return fee return fee
@property def save(self, *args, **kwargs):
def is_validated(self): self.fee = self.calculate_fee()
try: super().save(*args, **kwargs)
return self.membership is not None
except AttributeError:
return False
class WEIMembership(Membership): class WEIMembership(Membership):

View File

@@ -58,8 +58,8 @@ class WEIRegistrationTable(tables.Table):
validate = tables.Column( validate = tables.Column(
verbose_name=_("Validate"), verbose_name=_("Validate"),
orderable=False, orderable=True,
accessor=A('pk'), accessor='validate_status',
attrs={ attrs={
'th': { 'th': {
'id': 'validate-membership-header' 'id': 'validate-membership-header'
@@ -98,12 +98,13 @@ class WEIRegistrationTable(tables.Table):
if not hasperm: if not hasperm:
return format_html("<span class='no-perm'></span>") return format_html("<span class='no-perm'></span>")
url = reverse_lazy('wei:wei_update_registration', args=(record.pk,)) + '?validate=true' url = reverse_lazy('wei:validate_registration', args=(record.pk,))
text = _('Validate') text = _('Validate')
if record.fee > record.user.note.balance and not record.soge_credit: status = record.validation_status
if status == 2:
btn_class = 'btn-secondary' btn_class = 'btn-secondary'
tooltip = _("The user does not have enough money.") tooltip = _("The user does not have enough money.")
elif record.first_year: elif status == 1:
btn_class = 'btn-info' btn_class = 'btn-info'
tooltip = _("The user is in first year. You may validate the credit, the algorithm will run later.") tooltip = _("The user is in first year. You may validate the credit, the algorithm will run later.")
else: else:
@@ -121,6 +122,7 @@ class WEIRegistrationTable(tables.Table):
attrs = { attrs = {
'class': 'table table-condensed table-striped table-hover' 'class': 'table table-condensed table-striped table-hover'
} }
order_by = ('validate', 'user',)
model = WEIRegistration model = WEIRegistration
template_name = 'django_tables2/bootstrap4.html' template_name = 'django_tables2/bootstrap4.html'
fields = ('user', 'user__first_name', 'user__last_name', 'first_year', 'deposit_check', fields = ('user', 'user__first_name', 'user__last_name', 'first_year', 'deposit_check',

View File

@@ -67,20 +67,6 @@ SPDX-License-Identifier: GPL-3.0-or-later
</div> </div>
{% endif %} {% endif %}
{% if history_list.data %}
<div class="card bg-white mb-3">
<div class="card-header position-relative" id="historyListHeading">
<a class="stretched-link font-weight-bold text-decoration-none" {% if "note.view_note"|has_perm:club.note %}
href="{% url 'note:transactions' pk=club.note.pk %}" {% endif %}>
<i class="fa fa-euro"></i> {% trans "Transaction history" %}
</a>
</div>
<div id="history_list">
{% render_table history_list %}
</div>
</div>
{% endif %}
{% if pre_registrations.data %} {% if pre_registrations.data %}
<div class="card bg-white mb-3"> <div class="card bg-white mb-3">
<div class="card-header position-relative" id="historyListHeading"> <div class="card-header position-relative" id="historyListHeading">
@@ -99,6 +85,19 @@ SPDX-License-Identifier: GPL-3.0-or-later
<a href="{% url 'wei:wei_1A_list' pk=object.pk %}" class="btn btn-block btn-info">{% trans "Attribute buses" %}</a> <a href="{% url 'wei:wei_1A_list' pk=object.pk %}" class="btn btn-block btn-info">{% trans "Attribute buses" %}</a>
{% endif %} {% endif %}
{% if history_list.data %}
<div class="card bg-white mt-3">
<div class="card-header position-relative" id="historyListHeading">
<a class="stretched-link font-weight-bold text-decoration-none" {% if "note.view_note"|has_perm:club.note %}
href="{% url 'note:transactions' pk=club.note.pk %}" {% endif %}>
<i class="fa fa-euro"></i> {% trans "Transaction history" %}
</a>
</div>
<div id="history_list">
{% render_table history_list %}
</div>
</div>
{% endif %}
{% endblock %} {% endblock %}

View File

@@ -13,7 +13,7 @@ from django.contrib.auth.mixins import LoginRequiredMixin
from django.contrib.auth.models import User from django.contrib.auth.models import User
from django.core.exceptions import PermissionDenied from django.core.exceptions import PermissionDenied
from django.db import transaction from django.db import transaction
from django.db.models import Q, Count from django.db.models import Q, Count, Case, When, Value, IntegerField, F
from django.db.models.functions.text import Lower from django.db.models.functions.text import Lower
from django import forms from django import forms
from django.http import HttpResponse, Http404 from django.http import HttpResponse, Http404
@@ -35,7 +35,7 @@ from permission.views import ProtectQuerysetMixin, ProtectedCreateView
from .forms.registration import WEIChooseBusForm from .forms.registration import WEIChooseBusForm
from .models import WEIClub, WEIRegistration, WEIMembership, Bus, BusTeam, WEIRole from .models import WEIClub, WEIRegistration, WEIMembership, Bus, BusTeam, WEIRole
from .forms import WEIForm, WEIRegistrationForm, WEIRegistration1AForm, WEIRegistration2AForm, BusForm, BusTeamForm, WEIMembership1AForm, \ from .forms import WEIForm, WEIRegistrationForm, BusForm, BusTeamForm, WEIMembership1AForm, \
WEIMembershipForm, CurrentSurvey WEIMembershipForm, CurrentSurvey
from .tables import BusRepartitionTable, BusTable, BusTeamTable, WEITable, WEIRegistrationTable, \ from .tables import BusRepartitionTable, BusTable, BusTeamTable, WEITable, WEIRegistrationTable, \
WEIRegistration1ATable, WEIMembershipTable WEIRegistration1ATable, WEIMembershipTable
@@ -133,6 +133,23 @@ class WEIDetailView(ProtectQuerysetMixin, LoginRequiredMixin, MultiTableMixin, D
membership=None, membership=None,
wei=club wei=club
) )
# Annotate the query to be able to sort registrations on validate status
pre_registrations = pre_registrations.annotate(
deposit=Case(
When(deposit_type='note', then=F('wei__deposit_amount')),
default=Value(0),
output_field=IntegerField()
)
).annotate(
total_fee=F('fee') + F('deposit')
).annotate(
validate_status=Case(
When(total_fee__gt=F('user__note__balance'), then=Value(2)),
When(first_year=True, then=Value(1)),
default=Value(0),
output_field=IntegerField(),
)
)
buses = Bus.objects.filter(PermissionBackend.filter_queryset(self.request, Bus, "view")) \ buses = Bus.objects.filter(PermissionBackend.filter_queryset(self.request, Bus, "view")) \
.filter(wei=self.object).annotate(count=Count("memberships")).order_by("name") .filter(wei=self.object).annotate(count=Count("memberships")).order_by("name")
return [club_transactions, club_member, pre_registrations, buses, ] return [club_transactions, club_member, pre_registrations, buses, ]
@@ -260,6 +277,23 @@ class WEIRegistrationsView(ProtectQuerysetMixin, LoginRequiredMixin, SingleTable
def get_queryset(self, **kwargs): def get_queryset(self, **kwargs):
qs = super().get_queryset(**kwargs).filter(wei=self.club, membership=None).distinct() qs = super().get_queryset(**kwargs).filter(wei=self.club, membership=None).distinct()
# Annotate the query to be able to sort registrations on validate status
qs = qs.annotate(
deposit=Case(
When(deposit_type='note', then=F('wei__deposit_amount')),
default=Value(0),
output_field=IntegerField()
)
).annotate(
total_fee=F('fee') + F('deposit')
).annotate(
validate_status=Case(
When(total_fee__gt=F('user__note__balance'), then=Value(2)),
When(first_year=True, then=Value(1)),
default=Value(0),
output_field=IntegerField(),
)
)
pattern = self.request.GET.get("search", "") pattern = self.request.GET.get("search", "")
@@ -510,7 +544,7 @@ class WEIRegister1AView(ProtectQuerysetMixin, ProtectedCreateView):
Register a new user to the WEI Register a new user to the WEI
""" """
model = WEIRegistration model = WEIRegistration
form_class = WEIRegistration1AForm form_class = WEIRegistrationForm
extra_context = {"title": _("Register first year student to the WEI")} extra_context = {"title": _("Register first year student to the WEI")}
def get_sample_object(self): def get_sample_object(self):
@@ -606,7 +640,7 @@ class WEIRegister2AView(ProtectQuerysetMixin, ProtectedCreateView):
Register an old user to the WEI Register an old user to the WEI
""" """
model = WEIRegistration model = WEIRegistration
form_class = WEIRegistration2AForm form_class = WEIRegistrationForm
extra_context = {"title": _("Register old student to the WEI")} extra_context = {"title": _("Register old student to the WEI")}
def get_sample_object(self): def get_sample_object(self):
@@ -739,14 +773,11 @@ class WEIUpdateRegistrationView(ProtectQuerysetMixin, LoginRequiredMixin, Update
if today >= wei.date_start or today < wei.membership_start: if today >= wei.date_start or today < wei.membership_start:
return redirect(reverse_lazy('wei:wei_closed', args=(wei.pk,))) return redirect(reverse_lazy('wei:wei_closed', args=(wei.pk,)))
# Store the validate parameter in the view's state # Store the validate parameter in the view's state
self.should_validate = request.GET.get('validate', False)
return super().dispatch(request, *args, **kwargs) return super().dispatch(request, *args, **kwargs)
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs) context = super().get_context_data(**kwargs)
context["club"] = self.object.wei context["club"] = self.object.wei
# Pass the validate parameter to the template
context["should_validate"] = self.should_validate
if self.object.is_validated: if self.object.is_validated:
membership_form = self.get_membership_form(instance=self.object.membership, membership_form = self.get_membership_form(instance=self.object.membership,
@@ -788,7 +819,6 @@ class WEIUpdateRegistrationView(ProtectQuerysetMixin, LoginRequiredMixin, Update
if not self.object.first_year and "deposit_type" in form.fields: if not self.object.first_year and "deposit_type" in form.fields:
form.fields["deposit_type"].required = True form.fields["deposit_type"].required = True
form.fields["deposit_type"].help_text = _("Choose how you want to pay the deposit") form.fields["deposit_type"].help_text = _("Choose how you want to pay the deposit")
form.fields["deposit_type"].widget = forms.RadioSelect(choices=form.fields["deposit_type"].choices)
return form return form
@@ -862,9 +892,6 @@ class WEIUpdateRegistrationView(ProtectQuerysetMixin, LoginRequiredMixin, Update
survey = CurrentSurvey(self.object) survey = CurrentSurvey(self.object)
if not survey.is_complete(): if not survey.is_complete():
return reverse_lazy("wei:wei_survey", kwargs={"pk": self.object.pk}) return reverse_lazy("wei:wei_survey", kwargs={"pk": self.object.pk})
# On redirige vers la validation uniquement si c'est explicitement demandé (et stocké dans la vue)
if self.should_validate and self.request.user.has_perm("wei.add_weimembership"):
return reverse_lazy("wei:validate_registration", kwargs={"pk": self.object.pk})
return reverse_lazy("wei:wei_detail", kwargs={"pk": self.object.wei.pk}) return reverse_lazy("wei:wei_detail", kwargs={"pk": self.object.wei.pk})
@@ -963,9 +990,9 @@ class WEIValidateRegistrationView(ProtectQuerysetMixin, ProtectedCreateView):
form = context["form"] form = context["form"]
if registration.soge_credit: if registration.soge_credit:
form.fields["credit_amount"].initial = registration.fee form.fields["credit_amount"].initial = fee
else: else:
form.fields["credit_amount"].initial = max(0, registration.fee - registration.user.note.balance) form.fields["credit_amount"].initial = max(0, fee - registration.user.note.balance)
return context return context
@@ -1052,7 +1079,7 @@ class WEIValidateRegistrationView(ProtectQuerysetMixin, ProtectedCreateView):
fee = club.membership_fee_paid if user.profile.paid else club.membership_fee_unpaid fee = club.membership_fee_paid if user.profile.paid else club.membership_fee_unpaid
if registration.soge_credit: if registration.soge_credit:
fee = 2000 fee = registration.wei.fee_soge_credit
kfet = club.parent_club kfet = club.parent_club
bde = kfet.parent_club bde = kfet.parent_club

View File

@@ -3152,10 +3152,8 @@ msgid "Note transaction"
msgstr "Transaction Note" msgstr "Transaction Note"
#: apps/wei/models.py:217 #: apps/wei/models.py:217
#, fuzzy
#| msgid "Credit type"
msgid "deposit type" msgid "deposit type"
msgstr "Type de rechargement" msgstr "type de caution"
#: apps/wei/models.py:221 apps/wei/templates/wei/weimembership_form.html:64 #: apps/wei/models.py:221 apps/wei/templates/wei/weimembership_form.html:64
msgid "birth date" msgid "birth date"
@@ -4095,14 +4093,6 @@ msgstr "La note est indisponible pour le moment"
msgid "Thank you for your understanding -- The Respos Info of BDE" msgid "Thank you for your understanding -- The Respos Info of BDE"
msgstr "Merci de votre compréhension -- Les Respos Info du BDE" msgstr "Merci de votre compréhension -- Les Respos Info du BDE"
#: note_kfet/templates/base_search.html:15
msgid "Search by attribute such as name..."
msgstr "Chercher par un attribut tel que le nom..."
#: note_kfet/templates/base_search.html:23
msgid "There is no results."
msgstr "Il n'y a pas de résultat."
#: note_kfet/templates/cas/logged.html:8 #: note_kfet/templates/cas/logged.html:8
msgid "" msgid ""
"<h3>Log In Successful</h3>You have successfully logged into the Central " "<h3>Log In Successful</h3>You have successfully logged into the Central "

34
shell-static.nix Executable file
View File

@@ -0,0 +1,34 @@
# This is a workaround meant for use with the nix package manager. If you don't know what it is or don't use it, please ignore this file.
#
# The nk20 javascript static location are hardcoded for imperative system.
# This make ./manage.py collectstatic hard to use with nixos.
#
# A workaround is to enter a FHSUserEnv with the static placed under /share/javascript/<static>.
# This emulate a debian like system and enable collecting static normally with ./manage.py collectstatics.
# The regular shell.nix should be enough for other configurations.
#
# Warning, you are still supposed to use pip package with a venv !
{ pkgs ? import <nixpkgs> {} }:
(pkgs.buildFHSUserEnv {
name = "pipzone";
targetPkgs = pkgs: (with pkgs;
let
fhs-static = stdenv.mkDerivation {
name = "fhs-static";
buildCommand = ''
mkdir -p $out/share/javascript/bootstrap4
mkdir -p $out/share/javascript/jquery
ln -s ${python39Packages.xstatic-bootstrap}/lib/python3.9/site-packages/xstatic/pkg/bootstrap/data/* $out/share/javascript/bootstrap4
ln -s ${python39Packages.xstatic-jquery}/lib/python3.9/site-packages/xstatic/pkg/jquery/data/* $out/share/javascript/jquery
'';
};
in [
fhs-static
python39
gettext
python39Packages.pip
python39Packages.virtualenv
python39Packages.setuptools
]);
runScript = "bash";
}).env

23
shell.nix Executable file
View File

@@ -0,0 +1,23 @@
# This is meant for use with the nix package manager. If you don't know what it is or don't use it, please ignore this file.
#
# This shell.nix contains all dependencies require to create a venv and pip install -r requirements.txt.
#
# Please check shell-static.nix for running ./manage.py collectstatics.
{ pkgs ? import <nixpkgs> {} }:
pkgs.mkShell {
buildInputs = with pkgs; [
python39
python39Packages.pip
python39Packages.setuptools
gettext
];
shellHook = ''
# Tells pip to put packages into $PIP_PREFIX instead of the usual locations.
# See https://pip.pypa.io/en/stable/user_guide/#environment-variables.
export PIP_PREFIX=$(pwd)/_build/pip_packages
export PYTHONPATH="$PIP_PREFIX/${pkgs.python39.sitePackages}:$PYTHONPATH"
export PATH="$PIP_PREFIX/bin:$PATH"
unset SOURCE_DATE_EPOCH
'';
}