fnschool 20251018.81021.825__py3-none-any.whl → 20251020.82239.858__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of fnschool might be problematic. Click here for more details.

Files changed (24) hide show
  1. fnschoo1/__init__.py +1 -1
  2. fnschoo1/canteen/forms.py +1 -1
  3. fnschoo1/canteen/migrations/0017_ingredient_updated_at_alter_category_created_at_and_more.py +30 -0
  4. fnschoo1/canteen/models.py +14 -2
  5. fnschoo1/canteen/templates/canteen/consumption/create.html +5 -4
  6. fnschoo1/canteen/templates/canteen/ingredient/list.html +101 -69
  7. fnschoo1/canteen/views.py +51 -2
  8. fnschoo1/canteen/workbook/generate.py +172 -174
  9. fnschoo1/fnschool/_settings.py +3 -0
  10. fnschoo1/fnschool/settings.py +12 -1
  11. fnschoo1/locale/zh_Hans/LC_MESSAGES/django.mo +0 -0
  12. fnschoo1/static/css/fnschool.css +6 -0
  13. fnschoo1/static/js/fnschool.js +24 -0
  14. {fnschool-20251018.81021.825.dist-info → fnschool-20251020.82239.858.dist-info}/METADATA +1 -1
  15. {fnschool-20251018.81021.825.dist-info → fnschool-20251020.82239.858.dist-info}/RECORD +24 -22
  16. {fnschool-20251018.81021.825.dist-info → fnschool-20251020.82239.858.dist-info}/SOURCES.txt.py +0 -0
  17. {fnschool-20251018.81021.825.dist-info → fnschool-20251020.82239.858.dist-info}/WHEEL +0 -0
  18. {fnschool-20251018.81021.825.dist-info → fnschool-20251020.82239.858.dist-info}/dependency_links.txt.py +0 -0
  19. {fnschool-20251018.81021.825.dist-info → fnschool-20251020.82239.858.dist-info}/entry_points.txt +0 -0
  20. {fnschool-20251018.81021.825.dist-info → fnschool-20251020.82239.858.dist-info}/entry_points.txt.py +0 -0
  21. {fnschool-20251018.81021.825.dist-info → fnschool-20251020.82239.858.dist-info}/licenses/LICENSE +0 -0
  22. {fnschool-20251018.81021.825.dist-info → fnschool-20251020.82239.858.dist-info}/requires.txt.py +0 -0
  23. {fnschool-20251018.81021.825.dist-info → fnschool-20251020.82239.858.dist-info}/top_level.txt +0 -0
  24. {fnschool-20251018.81021.825.dist-info → fnschool-20251020.82239.858.dist-info}/top_level.txt.py +0 -0
fnschoo1/__init__.py CHANGED
@@ -6,4 +6,4 @@ import random
6
6
  import sys
7
7
  from pathlib import Path
8
8
 
9
- __version__ = "20251018.81021.825"
9
+ __version__ = "20251020.82239.858"
fnschoo1/canteen/forms.py CHANGED
@@ -20,7 +20,7 @@ class IngredientForm(forms.ModelForm):
20
20
  fields = [
21
21
  f.name
22
22
  for f in Ingredient._meta.fields
23
- if f.name not in ["id", "user"]
23
+ if f.name not in ["id", "user", "updated_at"]
24
24
  ]
25
25
 
26
26
  current_year = date.today().year
@@ -0,0 +1,30 @@
1
+ # Generated by Django 4.2.25 on 2025-10-19 02:03
2
+
3
+ from django.db import migrations, models
4
+
5
+
6
+ class Migration(migrations.Migration):
7
+
8
+ dependencies = [
9
+ ("canteen", "0016_consumption_unique_ingredient_date_of_using"),
10
+ ]
11
+
12
+ operations = [
13
+ migrations.AddField(
14
+ model_name="ingredient",
15
+ name="updated_at",
16
+ field=models.DateTimeField(
17
+ auto_now=True, null=True, verbose_name="Time of updating"
18
+ ),
19
+ ),
20
+ migrations.AlterField(
21
+ model_name="category",
22
+ name="created_at",
23
+ field=models.DateTimeField(null=True, verbose_name="创建日期"),
24
+ ),
25
+ migrations.AlterField(
26
+ model_name="mealtype",
27
+ name="created_at",
28
+ field=models.DateTimeField(null=True, verbose_name="创建日期"),
29
+ ),
30
+ ]
@@ -21,7 +21,9 @@ class MealType(models.Model):
21
21
  abbreviation = models.CharField(
22
22
  null=True, blank=True, max_length=100, verbose_name=_("Abbreviation")
23
23
  )
24
- created_at = models.DateField(verbose_name=_("Creating Date"))
24
+ created_at = models.DateTimeField(
25
+ null=True, verbose_name=_("Creating Date")
26
+ )
25
27
  is_disabled = models.BooleanField(
26
28
  default=False, verbose_name=_("Is Disabled")
27
29
  )
@@ -41,7 +43,9 @@ class Category(models.Model):
41
43
  abbreviation = models.CharField(
42
44
  null=True, blank=True, max_length=100, verbose_name=_("abbreviation")
43
45
  )
44
- created_at = models.DateField(verbose_name=_("Creating Date"))
46
+ created_at = models.DateTimeField(
47
+ null=True, verbose_name=_("Creating Date")
48
+ )
45
49
  is_disabled = models.BooleanField(
46
50
  default=False, verbose_name=_("Is Disabled")
47
51
  )
@@ -74,6 +78,9 @@ class Ingredient(models.Model):
74
78
  verbose_name=_("User"),
75
79
  )
76
80
  storage_date = models.DateField(verbose_name=_("Storage Date"))
81
+ updated_at = models.DateTimeField(
82
+ null=True, auto_now=True, verbose_name=_("Time of updating")
83
+ )
77
84
  name = models.CharField(max_length=100, verbose_name=_("Ingredient Name"))
78
85
  meal_type = models.ForeignKey(
79
86
  MealType,
@@ -139,6 +146,9 @@ class Ingredient(models.Model):
139
146
  return quantity
140
147
 
141
148
  def get_consuming_quantity(self, date_end):
149
+
150
+ if self.storage_date < date_end:
151
+ return 0
142
152
  consumptions = self.cleaned_consumptions
143
153
  if not consumptions:
144
154
  return 0
@@ -152,6 +162,8 @@ class Ingredient(models.Model):
152
162
  return quantity
153
163
 
154
164
  def get_remaining_quantity(self, date_end):
165
+ if self.storage_date < date_end:
166
+ return 0
155
167
  return self.quantity - self.get_consuming_quantity(date_end)
156
168
 
157
169
  @property
@@ -42,7 +42,7 @@
42
42
  onclick="list_consumptions();">{% trans "Refresh" %}</button>
43
43
  </div>
44
44
  </div>
45
- <div class="table table-responsive table-container">
45
+ <div class="table-responsive table-container">
46
46
  <table class="table table-consumptions cotable-bordered table-striped table-hover table-condensed scroll-vertical ">
47
47
  <thead>
48
48
  <tr>
@@ -97,7 +97,8 @@
97
97
  </table>
98
98
  </div>
99
99
  <div class="">
100
- <button onclick="generate_spreadsheet();"
100
+ <button id="generate_spreadsheet_btn"
101
+ onclick="generate_spreadsheet();"
101
102
  class="btn btn-submit-consumptions btn-success float-end">
102
103
  {% trans "Generate Spreadsheet" %}
103
104
  </button>
@@ -405,11 +406,11 @@
405
406
  }
406
407
 
407
408
  function set_consumptions_table_size() {
408
- const submit_consumptions_btn = $(".btn-submit-consumptions")
409
+ const generate_spreadsheet_btn = $("#generate_spreadsheet_btn")
409
410
  const consumptions_table = $(".table-consumptions")
410
411
  const header = $("header")
411
412
  const footer = $("footer")
412
- const height = Math.round((footer.offset().top - submit_consumptions_btn.height() - consumptions_table.offset().top) * 0.95)
413
+ const height = Math.round((footer.offset().top - generate_spreadsheet_btn.height() - consumptions_table.offset().top) * 0.95)
413
414
  consumptions_table.parent().height(height)
414
415
  }
415
416
  $(window).resize(function() {
@@ -1,4 +1,5 @@
1
1
  {% extends "base/header_content_footer.html" %}
2
+ {% load tz %}
2
3
  {% load crispy_forms_tags %}
3
4
  {% block title %}
4
5
  {% trans "Ingredient List" %}
@@ -34,77 +35,97 @@
34
35
  onclick="open_small_window('{% url 'canteen:create_ingredient' %}')">{% trans "Add one" %}</a>
35
36
  </div>
36
37
  <hr />
37
- <table class="table table-bordered table-striped table-hover table-condensed">
38
- <thead>
39
- <tr>
40
- <th scope="col">#</th>
41
- {% for name,sort, header in headers %}
42
- <th scope="col"
43
- data-sort="sort_{{ name }} {{ sort }}"
44
- onclick="sort_ingredients(this.dataset.sort);">
45
- {{ header }}
46
- {% if sort == "+" %}
47
- &#8593;
48
- {% elif sort == "-" %}
49
- &#8595;
50
- {% endif %}
51
- </th>
52
- {% endfor %}
53
- <th scope="col"></th>
54
- </tr>
55
- </thead>
56
- <tbody>
57
- {% for ingredient in page_obj %}
58
- <tr>
59
- <th scope="row">
60
- <div class="form-check"
61
- title="{% trans 'It helps you check the ingredients.' %}">
62
- <input type="checkbox"
63
- class="form-check-input"
64
- id="ingredient_free_nark_{{ ingredient.id }}" />
65
- <label class="form-check-label"
66
- for="ingredient_free_nark_{{ ingredient.id }}">
67
- {{ forloop.counter }}
68
- </label>
69
- </div>
70
- </th>
71
- <td>{{ ingredient.storage_date }}</td>
72
- <td>{{ ingredient.name }}</td>
73
- <td>{{ ingredient.meal_type }}</td>
74
- <td>{{ ingredient.category }}</td>
75
- <td>{{ ingredient.quantity }}</td>
76
- <td>{{ ingredient.quantity_unit_name }}</td>
77
- <td>{{ ingredient.total_price }}</td>
78
- <td>
79
- {% if ingredient.is_ignorable %}
80
- {% trans "Yes" %}
81
- {% endif %}
82
- </td>
83
- <td>
84
- {% if ingredient.is_disabled %}
85
- {% trans "Yes" %}
86
- {% endif %}
87
- </td>
88
- <td class="">
89
- <a class=""
90
- target="_blank"
91
- onclick="open_small_window('{% url 'canteen:edit_ingredient' ingredient.id %}'); return false;">
92
- {% trans "Edit" %}
93
- </a>
94
- |
95
- <a class="text-danger"
96
- target="_blank"
97
- onclick="open_small_window('{% url 'canteen:delete_ingredient' ingredient.id %}'); return false;">
98
- {% trans "Delete" %}
99
- </a>
100
- </td>
101
- </tr>
102
- {% empty %}
103
- {% endfor %}
104
- </tbody>
105
- </table>
38
+ <div class="container">
39
+ <div class="table-responsive-lg table-container">
40
+ <table class="table table-bordered table-ingredient table-striped table-hover table-condensed">
41
+ <thead>
42
+ <tr>
43
+ <th scope="col">#</th>
44
+ {% for name,sort, header in headers %}
45
+ <th scope="col"
46
+ data-sort="sort_{{ name }} {{ sort }}"
47
+ onclick="sort_ingredients(this.dataset.sort);">
48
+ {{ header }}
49
+ {% if sort == "+" %}
50
+ &#8593;
51
+ {% elif sort == "-" %}
52
+ &#8595;
53
+ {% endif %}
54
+ </th>
55
+ {% endfor %}
56
+ <th scope="col"></th>
57
+ </tr>
58
+ </thead>
59
+ <tbody>
60
+ {% for ingredient in page_obj %}
61
+ <tr class="tr-ingredient"
62
+ data-ingredient_updated_at="{{ ingredient.updated_at|localtime|date:'Y/m/d H:i:s' }}"
63
+ data-ingredient_storage_date="{{ ingredient.storage_date|localtime|date:'Y/m/d' }}">
64
+ <th scope="row">
65
+ <div class="form-check"
66
+ title="{% trans 'It helps you check the ingredients.' %}">
67
+ <input type="checkbox"
68
+ class="form-check-input"
69
+ id="ingredient_free_nark_{{ ingredient.id }}" />
70
+ <label class="form-check-label"
71
+ for="ingredient_free_nark_{{ ingredient.id }}">
72
+ {{ forloop.counter }}
73
+ </label>
74
+ </div>
75
+ </th>
76
+ <td>{{ ingredient.storage_date }}</td>
77
+ <td>{{ ingredient.name }}</td>
78
+ <td>{{ ingredient.meal_type }}</td>
79
+ <td>{{ ingredient.category }}</td>
80
+ <td>{{ ingredient.quantity }}</td>
81
+ <td>{{ ingredient.quantity_unit_name }}</td>
82
+ <td>{{ ingredient.total_price }}</td>
83
+ <td>
84
+ {% if ingredient.is_ignorable %}
85
+ {% trans "Yes" %}
86
+ {% endif %}
87
+ </td>
88
+ <td>
89
+ {% if ingredient.is_disabled %}
90
+ {% trans "Yes" %}
91
+ {% endif %}
92
+ </td>
93
+ <td class="">
94
+ <a class=""
95
+ target="_blank"
96
+ onclick="open_small_window('{% url 'canteen:edit_ingredient' ingredient.id %}'); return false;">
97
+ {% trans "Edit" %}
98
+ </a>
99
+ |
100
+ <a class="text-danger"
101
+ target="_blank"
102
+ onclick="open_small_window('{% url 'canteen:delete_ingredient' ingredient.id %}'); return false;">
103
+ {% trans "Delete" %}
104
+ </a>
105
+ </td>
106
+ </tr>
107
+ {% empty %}
108
+ {% endfor %}
109
+ </tbody>
110
+ </table>
111
+ </div>
112
+ </div>
106
113
  {% include 'includes/_paginator.html' %}
107
114
  <script>
115
+ $(document).ready(function() {
116
+ var ingredient_table = $('.table-ingredient')
117
+ var pagination = $('.pagination')
118
+ var footer = $("footer")
119
+ var height = Math.round((footer.offset().top - pagination.height() - ingredient_table.offset().top) * 0.98)
120
+ ingredient_table.parent().height(
121
+ height
122
+ );
123
+ });
124
+ $(document).ready(
125
+ function() {
126
+ make_highlight(".tr-ingredient", "ingredient_updated_at")
127
+ });
128
+
108
129
  function clear_q() {
109
130
  update_href({
110
131
  "q": ""
@@ -136,4 +157,15 @@
136
157
  update_href(i_sort);
137
158
  }
138
159
  </script>
160
+ <style>
161
+ .table-container {
162
+ overflow: auto;
163
+ }
164
+
165
+ .table-container thead th {
166
+ position: sticky;
167
+ top: 0;
168
+ }
169
+ </style>
170
+
139
171
  {% endblock %}
fnschoo1/canteen/views.py CHANGED
@@ -395,7 +395,31 @@ def edit_ingredient(request, ingredient_id):
395
395
  ingredient = get_object_or_404(Ingredient, pk=ingredient_id)
396
396
 
397
397
  if request.method == "POST":
398
+
398
399
  form = IngredientForm(request.POST, instance=ingredient)
400
+
401
+ total_price = form.instance.total_price
402
+ quantity = form.instance.quantity
403
+
404
+ [total_price0, quantity0], [total_price1, quantity1] = split_price(
405
+ total_price, quantity
406
+ )
407
+
408
+ if total_price1:
409
+ unit_price_error_msg = _(
410
+ "The unit pricei ({unit_price}) has more than 3 decimal places and cannot be saved. Please modify it again."
411
+ ).format(
412
+ unit_price=(
413
+ Decimal(str(total_price)) / Decimal(str(float(quantity)))
414
+ ).normalize()
415
+ )
416
+ form.add_error("total_price", unit_price_error_msg)
417
+ form.add_error("quantity", unit_price_error_msg)
418
+ return render(
419
+ request, "canteen/ingredient/update.html", {"form": form}
420
+ )
421
+
422
+ form.instance.user = request.user
399
423
  if form.is_valid():
400
424
  form.save()
401
425
  return render(
@@ -413,9 +437,10 @@ def list_ingredients(request):
413
437
  search_query = request.GET.get("q", "")
414
438
  search_query_cp = search_query
415
439
  fields = [
416
- f for f in Ingredient._meta.fields if f.name not in ["id", "user"]
440
+ f
441
+ for f in Ingredient._meta.fields
442
+ if f.name in IngredientForm._meta.fields
417
443
  ]
418
-
419
444
  if search_query:
420
445
  queries = Q(user=request.user)
421
446
 
@@ -846,6 +871,30 @@ class IngredientCreateView(LoginRequiredMixin, CreateView):
846
871
 
847
872
  def form_valid(self, form):
848
873
  form.instance.user = self.request.user
874
+ total_price = form.instance.total_price
875
+ quantity = form.instance.quantity
876
+
877
+ [total_price0, quantity0], [total_price1, quantity1] = split_price(
878
+ total_price, quantity
879
+ )
880
+
881
+ if form.is_valid() and total_price1:
882
+ Ingredient.objects.create(
883
+ user=form.instance.user,
884
+ storage_date=form.instance.storage_date,
885
+ name=form.instance.name + _("(2)"),
886
+ meal_type=form.instance.meal_type,
887
+ category=form.instance.category,
888
+ quantity=quantity1,
889
+ total_price=total_price1,
890
+ quantity_unit_name=form.instance.quantity_unit_name,
891
+ is_ignorable=form.instance.is_ignorable,
892
+ )
893
+ form.instance.name = form.instance.name + _("(1)")
894
+
895
+ form.instance.total_price = total_price0
896
+ form.instance.quantity = quantity0
897
+
849
898
  return super().form_valid(form)
850
899
 
851
900
 
@@ -20,6 +20,7 @@ from django.db.models import (
20
20
  DecimalField,
21
21
  ExpressionWrapper,
22
22
  F,
23
+ IntegerField,
23
24
  Q,
24
25
  Sum,
25
26
  Value,
@@ -185,8 +186,16 @@ def set_row_height_in_inches(worksheet, row, inches):
185
186
  worksheet.row_dimensions[row].height = points
186
187
 
187
188
 
188
- class CanteenWorkBook:
189
- def __init__(self, request, month, meal_type):
189
+ class MealTypeWorkbook:
190
+ def __init__(
191
+ self,
192
+ request,
193
+ year=None,
194
+ month=None,
195
+ ingredients=None,
196
+ meal_type=None,
197
+ categories=None,
198
+ ):
190
199
  self.wb = Workbook()
191
200
  self.wb[self.wb.sheetnames[0]].sheet_state = "hidden"
192
201
  self.cover_sheet = self.wb.create_sheet(title=_("Sheet Cover"))
@@ -227,15 +236,27 @@ class CanteenWorkBook:
227
236
  self.font_18_bold = Font(size=18, bold=True)
228
237
  self.font_20_bold = Font(size=20, bold=True)
229
238
 
239
+ self.year = year or datetime().now().year
240
+ self.month = month or datetime().now().month
241
+
242
+ self.first_date_of_month = datetime(self.year, self.month, 1).date()
243
+ self.last_date_of_month = datetime(
244
+ self.year, self.month, calendar.monthrange(self.year, self.month)[1]
245
+ ).date()
246
+ self.first_date_of_year = datetime(self.year, 1, 1)
247
+ self.last_date_of_year = datetime(self.year, 12, 31)
248
+
230
249
  self.request = request
231
250
  self.user = self.request.user
251
+ self.ingredients = ingredients
252
+ self.ignorable_ingredients = self.ingredients.filter(
253
+ Q(is_ignorable=True)
254
+ ).all()
255
+ self.storage_ingredients = self.ingredients.filter(
256
+ Q(is_ignorable=False)
257
+ ).all()
232
258
  self.meal_type = meal_type
233
- self.year = int(month.split("-")[0])
234
- self.month = int(month.split("-")[1])
235
- self.date_start = datetime(self.year, self.month, 1).date()
236
- self.date_end = datetime(
237
- self.year, self.month, calendar.monthrange(self.year, self.month)[1]
238
- ).date()
259
+ self.categories = categories
239
260
  self.is_zh_CN = is_zh_CN()
240
261
 
241
262
  self._is_school = None
@@ -294,7 +315,7 @@ class CanteenWorkBook:
294
315
  affiliation=user.affiliation,
295
316
  year=self.year,
296
317
  month=self.month,
297
- day=self.date_end.day,
318
+ day=self.last_date_of_month.day,
298
319
  )
299
320
 
300
321
  header_row_num = 3
@@ -318,10 +339,7 @@ class CanteenWorkBook:
318
339
  cell.alignment = self.center_alignment
319
340
  cell.border = self.thin_border
320
341
 
321
- categories = Category.objects.filter(
322
- Q(user=user) & Q(is_disabled=False)
323
- ).all()
324
-
342
+ categories = self.categories
325
343
  set_row_height_in_inches(sheet, 1, 0.38)
326
344
  set_row_height_in_inches(sheet, 2, 0.22)
327
345
  set_row_height_in_inches(sheet, 3, 0.32)
@@ -334,14 +352,10 @@ class CanteenWorkBook:
334
352
  category_cell = sheet.cell(row_num, 1)
335
353
  category_cell.value = category.name
336
354
 
337
- ingredients = Ingredient.objects.filter(
338
- Q(user=user)
339
- & Q(category=category)
340
- & Q(storage_date__gte=self.date_start)
341
- & Q(storage_date__lte=self.date_end)
342
- & Q(meal_type=self.meal_type)
343
- & Q(is_disabled=False)
344
- & Q(is_ignorable=True)
355
+ ingredients = self.ignorable_ingredients.filter(
356
+ Q(category=category)
357
+ & Q(storage_date__gte=self.first_date_of_month)
358
+ & Q(storage_date__lte=self.last_date_of_month)
345
359
  ).all()
346
360
  total_price_cell = sheet.cell(row_num, 2)
347
361
  total_price_cell.value = sum([i.total_price for i in ingredients])
@@ -353,13 +367,9 @@ class CanteenWorkBook:
353
367
  cell.alignment = self.center_alignment
354
368
  cell.border = self.thin_border
355
369
 
356
- ingredients = Ingredient.objects.filter(
357
- Q(user=user)
358
- & Q(storage_date__gte=self.date_start)
359
- & Q(storage_date__lte=self.date_end)
360
- & Q(meal_type=self.meal_type)
361
- & Q(is_disabled=False)
362
- & Q(is_ignorable=True)
370
+ ingredients = self.ignorable_ingredients.filter(
371
+ Q(storage_date__gte=self.first_date_of_month)
372
+ & Q(storage_date__lte=self.last_date_of_month)
363
373
  ).all()
364
374
 
365
375
  summary_row_num = len(categories) + header_row_num + 1
@@ -464,7 +474,7 @@ class CanteenWorkBook:
464
474
  affiliation=user.affiliation,
465
475
  year=self.year,
466
476
  month=self.month,
467
- day=self.date_end.day,
477
+ day=self.last_date_of_month.day,
468
478
  )
469
479
 
470
480
  header_row_num = 3
@@ -490,10 +500,7 @@ class CanteenWorkBook:
490
500
  cell.alignment = self.center_alignment
491
501
  cell.border = self.thin_border
492
502
 
493
- categories = Category.objects.filter(
494
- Q(user=user) & Q(is_disabled=False)
495
- ).all()
496
-
503
+ categories = self.categories
497
504
  set_row_height_in_inches(sheet, 1, 0.38)
498
505
  set_row_height_in_inches(sheet, 2, 0.22)
499
506
  set_row_height_in_inches(sheet, 3, 0.32)
@@ -506,19 +513,14 @@ class CanteenWorkBook:
506
513
  category_cell = sheet.cell(row_num, 1)
507
514
  category_cell.value = category.name
508
515
 
509
- ingredients = Ingredient.objects.filter(
510
- Q(user=user)
511
- & Q(category=category)
516
+ ingredients = self.storage_ingredients.filter(
517
+ Q(category=category)
512
518
  & Q(
513
519
  consumptions__date_of_using__range=(
514
- self.date_start,
515
- self.date_end,
520
+ self.first_date_of_month,
521
+ self.last_date_of_month,
516
522
  )
517
523
  )
518
- & Q(meal_type=self.meal_type)
519
- & Q(category__is_disabled=False)
520
- & Q(is_disabled=False)
521
- & Q(is_ignorable=False)
522
524
  ).distinct()
523
525
 
524
526
  total_price_cell = sheet.cell(row_num, 2)
@@ -526,8 +528,8 @@ class CanteenWorkBook:
526
528
  for i in ingredients:
527
529
  consumptions = i.consumptions.filter(
528
530
  Q(is_disabled=False)
529
- & Q(date_of_using__lte=self.date_end)
530
- & Q(date_of_using__gte=self.date_start)
531
+ & Q(date_of_using__lte=self.last_date_of_month)
532
+ & Q(date_of_using__gte=self.first_date_of_month)
531
533
  ).all()
532
534
  total_price_consumed += sum(
533
535
  [c.amount_used * i.unit_price for c in consumptions]
@@ -541,18 +543,13 @@ class CanteenWorkBook:
541
543
  cell.alignment = self.center_alignment
542
544
  cell.border = self.thin_border
543
545
 
544
- ingredients = Ingredient.objects.filter(
545
- Q(user=user)
546
- & Q(
546
+ ingredients = self.storage_ingredients.filter(
547
+ Q(
547
548
  consumptions__date_of_using__range=(
548
- self.date_start,
549
- self.date_end,
549
+ self.first_date_of_month,
550
+ self.last_date_of_month,
550
551
  )
551
552
  )
552
- & Q(meal_type=self.meal_type)
553
- & Q(category__is_disabled=False)
554
- & Q(is_disabled=False)
555
- & Q(is_ignorable=False)
556
553
  ).distinct()
557
554
 
558
555
  summary_row_num = len(categories) + header_row_num + 1
@@ -560,8 +557,8 @@ class CanteenWorkBook:
560
557
  for i in ingredients:
561
558
  consumptions = i.consumptions.filter(
562
559
  Q(is_disabled=False)
563
- & Q(date_of_using__lte=self.date_end)
564
- & Q(date_of_using__gte=self.date_start)
560
+ & Q(date_of_using__lte=self.last_date_of_month)
561
+ & Q(date_of_using__gte=self.first_date_of_month)
565
562
  ).all()
566
563
  summary_total_price += sum(
567
564
  [c.amount_used * i.unit_price for c in consumptions]
@@ -666,7 +663,7 @@ class CanteenWorkBook:
666
663
  affiliation=user.affiliation,
667
664
  year=self.year,
668
665
  month=self.month,
669
- day=self.date_end.day,
666
+ day=self.last_date_of_month.day,
670
667
  )
671
668
 
672
669
  header_row_num = 3
@@ -690,10 +687,7 @@ class CanteenWorkBook:
690
687
  cell.alignment = self.center_alignment
691
688
  cell.border = self.thin_border
692
689
 
693
- categories = Category.objects.filter(
694
- Q(user=user) & Q(is_disabled=False)
695
- ).all()
696
-
690
+ categories = self.categories
697
691
  set_row_height_in_inches(sheet, 1, 0.38)
698
692
  set_row_height_in_inches(sheet, 2, 0.22)
699
693
  set_row_height_in_inches(sheet, 3, 0.32)
@@ -706,15 +700,12 @@ class CanteenWorkBook:
706
700
  category_cell = sheet.cell(row_num, 1)
707
701
  category_cell.value = category.name
708
702
 
709
- ingredients = Ingredient.objects.filter(
710
- Q(user=user)
703
+ ingredients = self.storage_ingredients.filter(
704
+ Q(storage_date__gte=self.first_date_of_month)
705
+ & Q(storage_date__lte=self.last_date_of_month)
711
706
  & Q(category=category)
712
- & Q(storage_date__gte=self.date_start)
713
- & Q(storage_date__lte=self.date_end)
714
- & Q(meal_type=self.meal_type)
715
- & Q(is_disabled=False)
716
- & Q(is_ignorable=False)
717
707
  ).all()
708
+
718
709
  total_price_cell = sheet.cell(row_num, 2)
719
710
  total_price_cell.value = sum([i.total_price for i in ingredients])
720
711
 
@@ -725,13 +716,9 @@ class CanteenWorkBook:
725
716
  cell.alignment = self.center_alignment
726
717
  cell.border = self.thin_border
727
718
 
728
- ingredients = Ingredient.objects.filter(
729
- Q(user=user)
730
- & Q(storage_date__gte=self.date_start)
731
- & Q(storage_date__lte=self.date_end)
732
- & Q(meal_type=self.meal_type)
733
- & Q(is_disabled=False)
734
- & Q(is_ignorable=False)
719
+ ingredients = self.storage_ingredients.filter(
720
+ Q(storage_date__gte=self.first_date_of_month)
721
+ & Q(storage_date__lte=self.last_date_of_month)
735
722
  ).all()
736
723
 
737
724
  summary_row_num = len(categories) + header_row_num + 1
@@ -811,16 +798,10 @@ class CanteenWorkBook:
811
798
  sheet.sheet_properties.tabColor = "ff9e16"
812
799
  user = self.user
813
800
  consumption_rows_count = 21
814
- categories = Category.objects.filter(
815
- Q(user=user) & Q(is_disabled=False)
816
- ).all()
817
- consumptions = Consumption.objects.filter(
818
- Q(is_disabled=False)
819
- & Q(ingredient__meal_type=self.meal_type)
820
- & Q(ingredient__user=user)
821
- & Q(date_of_using__gte=self.date_start)
822
- & Q(date_of_using__lte=self.date_end)
823
- ).all()
801
+ categories = self.categories
802
+ consumptions = []
803
+ for i in self.storage_ingredients:
804
+ consumptions += i.consumptions.filter(Q(is_disabled=False)).all()
824
805
  consumption_row_height = 0.18
825
806
  consumption_rows_height = consumption_rows_count * 0.18
826
807
 
@@ -880,7 +861,7 @@ class CanteenWorkBook:
880
861
  Ingredient(
881
862
  user=user,
882
863
  name="",
883
- storage_date=self.date_start,
864
+ storage_date=self.first_date_of_month,
884
865
  meal_type=split_dated_consumption.ingredient.meal_type,
885
866
  category=c,
886
867
  quantity=0.0,
@@ -1204,22 +1185,16 @@ class CanteenWorkBook:
1204
1185
  sheet.sheet_properties.tabColor = "16b1ff"
1205
1186
  user = self.user
1206
1187
  ingredient_rows_count = 21
1207
- ingredients = Ingredient.objects.filter(
1208
- Q(user=user)
1209
- & Q(is_disabled=False)
1210
- & Q(is_ignorable=False)
1211
- & Q(storage_date__gte=self.date_start)
1212
- & Q(storage_date__lte=self.date_end)
1213
- & Q(meal_type=self.meal_type)
1214
- ).all()
1215
- categories = Category.objects.filter(
1216
- Q(user=user) & Q(is_disabled=False)
1188
+ ingredients = self.storage_ingredients.filter(
1189
+ Q(storage_date__gte=self.first_date_of_month)
1190
+ & Q(storage_date__lte=self.last_date_of_month)
1217
1191
  ).all()
1192
+ categories = self.categories
1218
1193
  ingredient_row_height = 0.18
1219
1194
  ingredient_rows_height = ingredient_rows_count * 0.18
1220
1195
  storage_dates = sorted(list(set([i.storage_date for i in ingredients])))
1221
1196
 
1222
- storaged_ingredients = []
1197
+ formed_ingredients = []
1223
1198
  for storage_date in storage_dates:
1224
1199
  dated_ingredients = [
1225
1200
  i for i in ingredients if i.storage_date == storage_date
@@ -1282,7 +1257,7 @@ class CanteenWorkBook:
1282
1257
  )
1283
1258
 
1284
1259
  storage_date_index = sub_storage_num
1285
- storaged_ingredients.append(
1260
+ formed_ingredients.append(
1286
1261
  [storage_date, storage_date_index, split_dated_ingredients]
1287
1262
  )
1288
1263
 
@@ -1293,7 +1268,7 @@ class CanteenWorkBook:
1293
1268
  storage_date,
1294
1269
  storage_date_index,
1295
1270
  dated_ingredients,
1296
- ) in enumerate(storaged_ingredients):
1271
+ ) in enumerate(formed_ingredients):
1297
1272
  row_num = (ingredient_rows_count + 6) * index + 1
1298
1273
 
1299
1274
  title_cell_row_num = row_num
@@ -1327,13 +1302,13 @@ class CanteenWorkBook:
1327
1302
  storage_num += 1
1328
1303
 
1329
1304
  prev_storage_date = (
1330
- storaged_ingredients[index - 1][0]
1331
- if 0 <= index - 1 < len(storaged_ingredients)
1305
+ formed_ingredients[index - 1][0]
1306
+ if 0 <= index - 1 < len(formed_ingredients)
1332
1307
  else None
1333
1308
  )
1334
1309
  next_storage_date = (
1335
- storaged_ingredients[index + 1][0]
1336
- if 0 < index + 1 < (len(storaged_ingredients) - 1)
1310
+ formed_ingredients[index + 1][0]
1311
+ if 0 < index + 1 < (len(formed_ingredients) - 1)
1337
1312
  else None
1338
1313
  )
1339
1314
  sub_title_num_cell_row_num = title_cell_row_num + 1
@@ -1563,10 +1538,7 @@ class CanteenWorkBook:
1563
1538
  cell.border = self.thin_border
1564
1539
  cell.font = self.font_12_bold
1565
1540
 
1566
- categories = Category.objects.filter(
1567
- Q(user=user) & Q(is_disabled=False)
1568
- ).all()
1569
-
1541
+ categories = self.categories
1570
1542
  set_row_height_in_inches(sheet, 1, 0.60)
1571
1543
  set_row_height_in_inches(sheet, 2, 0.44)
1572
1544
 
@@ -1578,13 +1550,10 @@ class CanteenWorkBook:
1578
1550
  category_cell = sheet.cell(row_num, 1)
1579
1551
  category_cell.value = category.name
1580
1552
 
1581
- ingredients = Ingredient.objects.filter(
1582
- Q(user=user)
1583
- & Q(category=category)
1584
- & Q(storage_date__gte=self.date_start)
1585
- & Q(storage_date__lte=self.date_end)
1586
- & Q(meal_type=self.meal_type)
1587
- & Q(is_disabled=False)
1553
+ ingredients = self.ingredients.filter(
1554
+ Q(category=category)
1555
+ & Q(storage_date__gte=self.first_date_of_month)
1556
+ & Q(storage_date__lte=self.last_date_of_month)
1588
1557
  ).all()
1589
1558
  total_price_cell = sheet.cell(row_num, 2)
1590
1559
  total_price_cell.value = sum([i.total_price for i in ingredients])
@@ -1602,12 +1571,9 @@ class CanteenWorkBook:
1602
1571
  cell.alignment = self.center_alignment
1603
1572
  cell.border = self.thin_border
1604
1573
 
1605
- ingredients = Ingredient.objects.filter(
1606
- Q(user=user)
1607
- & Q(storage_date__gte=self.date_start)
1608
- & Q(storage_date__lte=self.date_end)
1609
- & Q(meal_type=self.meal_type)
1610
- & Q(is_disabled=False)
1574
+ ingredients = self.ingredients.filter(
1575
+ Q(storage_date__gte=self.first_date_of_month)
1576
+ & Q(storage_date__lte=self.last_date_of_month)
1611
1577
  ).all()
1612
1578
 
1613
1579
  summary_row_num = len(categories) + header_row_num + 1
@@ -1644,12 +1610,8 @@ class CanteenWorkBook:
1644
1610
  user = self.user
1645
1611
  ingredient_rows_count = 17
1646
1612
 
1647
- ingredients = Ingredient.objects.filter(
1648
- Q(user=user)
1649
- & Q(storage_date__lte=self.date_end)
1650
- & Q(meal_type=self.meal_type)
1651
- & Q(is_disabled=False)
1652
- & Q(is_ignorable=False)
1613
+ ingredients = self.storage_ingredients.filter(
1614
+ Q(storage_date__lte=self.last_date_of_month)
1653
1615
  ).all()
1654
1616
 
1655
1617
  consumptions = []
@@ -1657,7 +1619,8 @@ class CanteenWorkBook:
1657
1619
  consumptions += [
1658
1620
  c
1659
1621
  for c in ingredient.consumptions.filter(
1660
- Q(date_of_using__lte=self.date_end) & Q(is_disabled=False)
1622
+ Q(date_of_using__lte=self.last_date_of_month)
1623
+ & Q(is_disabled=False)
1661
1624
  ).all()
1662
1625
  ]
1663
1626
 
@@ -1673,11 +1636,9 @@ class CanteenWorkBook:
1673
1636
  inventory_days.append(date_of_using)
1674
1637
 
1675
1638
  if len(inventory_days) < 1:
1676
- inventory_days.insert(-1, self.date_end)
1639
+ inventory_days.insert(-1, self.last_date_of_month)
1677
1640
 
1678
- inventory_days.insert(
1679
- 0, (self.date_start.replace(day=1) - timedelta(days=1))
1680
- )
1641
+ inventory_days.insert(0, (self.first_date_of_month - timedelta(days=1)))
1681
1642
  formed_ingredients = []
1682
1643
  for inventory_day in inventory_days:
1683
1644
  inventory_day_ingredients = []
@@ -1716,7 +1677,7 @@ class CanteenWorkBook:
1716
1677
  inventory_day_ingredients += [
1717
1678
  Ingredient(
1718
1679
  user=user,
1719
- storage_date=self.date_start,
1680
+ storage_date=self.first_date_of_month,
1720
1681
  name="",
1721
1682
  meal_type=self.meal_type,
1722
1683
  category=s_ingredient0.category,
@@ -1973,13 +1934,9 @@ class CanteenWorkBook:
1973
1934
  user = self.user
1974
1935
  ingredient_rows_count = 11
1975
1936
 
1976
- ingredients = Ingredient.objects.filter(
1977
- Q(user=user)
1978
- & Q(storage_date__gte=self.date_start)
1979
- & Q(storage_date__lte=self.date_end)
1980
- & Q(meal_type=self.meal_type)
1981
- & Q(is_disabled=False)
1982
- & Q(is_ignorable=True)
1937
+ ingredients = self.ignorable_ingredients.filter(
1938
+ Q(storage_date__gte=self.first_date_of_month)
1939
+ & Q(storage_date__lte=self.last_date_of_month)
1983
1940
  ).all()
1984
1941
 
1985
1942
  categories = list(set([i.category for i in ingredients]))
@@ -1996,7 +1953,7 @@ class CanteenWorkBook:
1996
1953
  _split_ingredients += [
1997
1954
  Ingredient(
1998
1955
  user=user,
1999
- storage_date=self.date_end,
1956
+ storage_date=self.last_date_of_month,
2000
1957
  name="",
2001
1958
  meal_type=_split_ingredient0.meal_type,
2002
1959
  category=_split_ingredient0.category,
@@ -2036,7 +1993,11 @@ class CanteenWorkBook:
2036
1993
  sub_title_date_cell = sheet.cell(sub_title_row_num, 4)
2037
1994
  sub_title_date_cell.value = _(
2038
1995
  "{year}.{month:0>2}.{day:0>2} (Non-storage list sheet)"
2039
- ).format(year=self.year, month=self.month, day=self.date_end.day)
1996
+ ).format(
1997
+ year=self.year,
1998
+ month=self.month,
1999
+ day=self.last_date_of_month.day,
2000
+ )
2040
2001
 
2041
2002
  for cell in [sub_title_affiliation_cell, sub_title_date_cell]:
2042
2003
  cell.font = self.font_14
@@ -2170,29 +2131,7 @@ class CanteenWorkBook:
2170
2131
  date_end = date(year, 12, 31)
2171
2132
  meal_type = self.meal_type
2172
2133
 
2173
- ingredients = (
2174
- Ingredient.objects.filter(
2175
- Q(is_disabled=False)
2176
- & Q(is_ignorable=False)
2177
- & Q(user=user)
2178
- & Q(meal_type=meal_type)
2179
- )
2180
- .prefetch_related("consumptions")
2181
- .all()
2182
- )
2183
- ingredients = [
2184
- i
2185
- for i in ingredients
2186
- if date_start <= i.storage_date <= date_end
2187
- or any(
2188
- [
2189
- date_start <= c.date_of_using <= date_end
2190
- for c in i.consumptions.all()
2191
- if c.is_disabled == False
2192
- ]
2193
- )
2194
- ]
2195
-
2134
+ ingredients = self.storage_ingredients
2196
2135
  ingredient_names = list(set([i.name for i in ingredients]))
2197
2136
  for ingredient_name_index, ingredient_name in enumerate(
2198
2137
  ingredient_names
@@ -2417,16 +2356,16 @@ class CanteenWorkBook:
2417
2356
 
2418
2357
  sheet.cell(
2419
2358
  month_surplus_header_row_num,
2420
- 4,
2359
+ 10,
2421
2360
  remaining_quantity_last_month,
2422
2361
  )
2423
2362
  sheet.cell(
2424
2363
  month_surplus_header_row_num,
2425
- 6,
2364
+ 12,
2426
2365
  remaining_total_price_last_month,
2427
2366
  )
2428
2367
  sheet.cell(
2429
- month_surplus_header_row_num, 5, unit_price.normalize()
2368
+ month_surplus_header_row_num, 11, unit_price.normalize()
2430
2369
  )
2431
2370
 
2432
2371
  month_ingredients = []
@@ -2680,7 +2619,7 @@ class CanteenWorkBook:
2680
2619
  self.fill_in_consumption_sheet()
2681
2620
  self.fill_in_consumption_list_sheet()
2682
2621
  self.fill_in_surplus_sheet()
2683
- if self.request.GET.get("include_food_sheets","") == "true":
2622
+ if self.request.GET.get("include_food_sheets", "") == "true":
2684
2623
  self.fill_in_food_sheets()
2685
2624
 
2686
2625
  return self.wb
@@ -2689,10 +2628,59 @@ class CanteenWorkBook:
2689
2628
  def get_workbook_zip(request, month):
2690
2629
  from ..views import meal_type_name_0
2691
2630
 
2692
- meal_types = MealType.objects.annotate(
2693
- ingredients_count=Count("ingredients")
2694
- ).filter(
2695
- Q(user=request.user) & Q(is_disabled=False) & Q(ingredients_count__gt=0)
2631
+ year, month = [int(v) for v in month.split("-")]
2632
+ first_date_of_year = datetime(year, 1, 1).date()
2633
+ last_date_of_year = datetime(year, 12, 31).date()
2634
+
2635
+ meal_types = (
2636
+ MealType.objects.annotate(ingredients_count=Count("ingredients"))
2637
+ .filter(
2638
+ Q(user=request.user)
2639
+ & Q(is_disabled=False)
2640
+ & Q(ingredients_count__gt=0)
2641
+ )
2642
+ .all()
2643
+ )
2644
+
2645
+ categories = (
2646
+ Category.objects.annotate(ingredients_count=Count("ingredients"))
2647
+ .filter(
2648
+ Q(user=request.user)
2649
+ & Q(is_disabled=False)
2650
+ & Q(ingredients_count__gt=0)
2651
+ )
2652
+ .order_by("priority")
2653
+ .all()
2654
+ )
2655
+
2656
+ user_ingredients = (
2657
+ Ingredient.objects.annotate(
2658
+ total_consumed=Coalesce(
2659
+ Sum("consumptions__amount_used"), 0, output_field=IntegerField()
2660
+ )
2661
+ )
2662
+ .filter(
2663
+ Q(is_disabled=False)
2664
+ & (
2665
+ Q(
2666
+ consumptions__date_of_using__range=(
2667
+ first_date_of_year,
2668
+ last_date_of_year,
2669
+ )
2670
+ )
2671
+ | Q(quantity__gt=F("total_consumed"))
2672
+ | (
2673
+ Q(storage_date__gte=first_date_of_year)
2674
+ & Q(storage_date__lte=last_date_of_year)
2675
+ )
2676
+ )
2677
+ & Q(user=request.user)
2678
+ & Q(category__is_disabled=False)
2679
+ & Q(meal_type__is_disabled=False)
2680
+ )
2681
+ .select_related("meal_type", "category")
2682
+ .prefetch_related("consumptions")
2683
+ .distinct()
2696
2684
  )
2697
2685
 
2698
2686
  zip_buffer = io.BytesIO()
@@ -2707,13 +2695,20 @@ def get_workbook_zip(request, month):
2707
2695
  if not meal_type.name == meal_type_name_0
2708
2696
  else ""
2709
2697
  ),
2710
- month=month.replace("-", ""),
2698
+ month=f"{year}{month:0>2}",
2711
2699
  affiliation=request.user.affiliation,
2712
2700
  )
2713
2701
  + ".xlsx"
2714
2702
  )
2715
-
2716
- wb = CanteenWorkBook(request, month, meal_type).fill_in()
2703
+ __ingredients = user_ingredients.filter(meal_type=meal_type).all()
2704
+ wb = MealTypeWorkbook(
2705
+ request,
2706
+ year=year,
2707
+ month=month,
2708
+ ingredients=__ingredients,
2709
+ meal_type=meal_type,
2710
+ categories=categories,
2711
+ ).fill_in()
2717
2712
  excel_buffer = io.BytesIO()
2718
2713
  wb.save(excel_buffer)
2719
2714
  excel_buffer.seek(0)
@@ -2722,3 +2717,6 @@ def get_workbook_zip(request, month):
2722
2717
 
2723
2718
  zip_buffer.seek(0)
2724
2719
  return zip_buffer
2720
+
2721
+
2722
+ # The end.
@@ -0,0 +1,3 @@
1
+
2
+
3
+
@@ -126,7 +126,8 @@ AUTH_PASSWORD_VALIDATORS = [
126
126
  # Internationalization
127
127
  # https://docs.djangoproject.com/en/4.2/topics/i18n/
128
128
 
129
- TIME_ZONE = "UTC"
129
+ USE_TZ = True
130
+ TIME_ZONE = "Asia/Shanghai"
130
131
 
131
132
  USE_I18N = True
132
133
  USE_L10N = True
@@ -164,4 +165,14 @@ MEDIA_URL = "/media/"
164
165
  MEDIA_ROOT = os.path.join(BASE_DIR, "media")
165
166
 
166
167
  LOGIN_URL = reverse_lazy("profiles:log_in")
168
+
169
+ _settings_path = Path(__file__).parent / "_settings.py"
170
+ if _settings_path.exists():
171
+ print(
172
+ ('Custom configuration "{_settings_path}" has been used.').format(
173
+ _settings_path=_settings_path.as_posix()
174
+ )
175
+ )
176
+ from ._settings import *
177
+
167
178
  # The end.
@@ -1,3 +1,7 @@
1
+ .fn-highlight > td {
2
+ color: #00adff;
3
+ }
4
+
1
5
  main {
2
6
  padding-top: 60px;
3
7
  padding-bottom: 120px;
@@ -24,3 +28,5 @@ main {
24
28
  padding: 1rem;
25
29
  display: none;
26
30
  }
31
+
32
+ /* The end. */
@@ -1,3 +1,25 @@
1
+ function make_highlight(query, time_data) {
2
+ var highlight_elements = $(query)
3
+ var highlight_elements_toggled = []
4
+ highlight_elements.each(function (index, element) {
5
+ var element = $(element)
6
+ var time_value = element.data(time_data)
7
+ var seconds_diff = Math.floor((new Date() - new Date(time_value)) / 1000)
8
+ if (seconds_diff < 46) {
9
+ highlight_elements_toggled.push(element)
10
+ element.toggleClass('fn-highlight')
11
+ $('html,body').animate({ scrollTop: element.offset().top }, 1)
12
+ }
13
+ })
14
+
15
+ setTimeout(function () {
16
+ $(highlight_elements_toggled).each(function (index, element) {
17
+ element = $(element)
18
+ element.toggleClass('fn-highlight')
19
+ })
20
+ }, 10 * 1000)
21
+ }
22
+
1
23
  function get_cookie(name) {
2
24
  const cookies = document.cookie.split(';')
3
25
  for (const cookie of cookies) {
@@ -89,3 +111,5 @@ $(document).ready(function () {
89
111
  function set_page(num) {
90
112
  update_href({ page: num })
91
113
  }
114
+
115
+ // The end.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fnschool
3
- Version: 20251018.81021.825
3
+ Version: 20251020.82239.858
4
4
  Summary: Just some school related scripts, without any ambition.
5
5
  Author-email: larryw3i <larryw3i@163.com>, Larry Wei <larryw3i@126.com>, Larry W3i <larryw3i@yeah.net>
6
6
  Maintainer-email: larryw3i <larryw3i@163.com>, Larry Wei <larryw3i@126.com>
@@ -1,13 +1,13 @@
1
- fnschoo1/__init__.py,sha256=cU-wRDCQ1k5lKflYAtz3btmy6XuybJdT6pNLtXeZwBE,197
1
+ fnschoo1/__init__.py,sha256=rT-4fDJu76Ag8U4PoXAAd-4nAnLc2YFqPkwnObEVhqc,197
2
2
  fnschoo1/manage.py,sha256=VZIol9q_Dhg81_KJ9Jfq-L5O8ubQelShkA-cZVJ1S6E,1539
3
3
  fnschoo1/canteen/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
4
  fnschoo1/canteen/admin.py,sha256=suMo4x8I3JBxAFBVIdE-5qnqZ6JAZV0FESABHOSc-vg,63
5
5
  fnschoo1/canteen/apps.py,sha256=zUjM0ZJwHW4i72vOm87QewRlvFQORQo5yb053u4YIGs,146
6
- fnschoo1/canteen/forms.py,sha256=PDPAzZZ_y533KtMxCixLXXsXok5gjUShCSXeonYgGqQ,2582
7
- fnschoo1/canteen/models.py,sha256=wvlmLzY2X7ez2hbCDkjxYCO3O12oJKuuIZyS1rXERgQ,6034
6
+ fnschoo1/canteen/forms.py,sha256=JaN6SX9KIrJeUKRfTMf6bA0Op3mEGCc6xqUE6yWC8TU,2596
7
+ fnschoo1/canteen/models.py,sha256=44GtfJbp6kiQYdcBmSXqHiW6JLLcmiRydf7DC6EToWY,6331
8
8
  fnschoo1/canteen/tests.py,sha256=mrbGGRNg5jwbTJtWWa7zSKdDyeB4vmgZCRc2nk6VY-g,60
9
9
  fnschoo1/canteen/urls.py,sha256=4GtrqC9L59c8zopfjRgqhbcvA5iPnGcAUVuM6CrKWpk,2797
10
- fnschoo1/canteen/views.py,sha256=S7ByVMcbmpisH50mF38YaeEUYSIuK15z-vwqvFiKRM8,27368
10
+ fnschoo1/canteen/views.py,sha256=9qsNiCM4sUEUpV_8g874msSamrp-dqTb5mdUtyWvZmQ,29171
11
11
  fnschoo1/canteen/migrations/0001_initial.py,sha256=IHlyfT9sNc-kRQZy7NyjgWzp4EGus405QCAUw4oNdAQ,3943
12
12
  fnschoo1/canteen/migrations/0002_ingredient_is_disabled.py,sha256=j8oGWb2b99YwsEk-uwESLA_JRITEcz6b35ekoYOUGGc,444
13
13
  fnschoo1/canteen/migrations/0003_consumption_is_disabled_alter_ingredient_is_disabled.py,sha256=9RB5SHjINgrrqtDpcVIUXEBa3C_MTcR_keXLGG_PcOs,619
@@ -24,6 +24,7 @@ fnschoo1/canteen/migrations/0013_alter_consumption_options_alter_ingredient_opti
24
24
  fnschoo1/canteen/migrations/0014_category_priority.py,sha256=AywjPQhwtwm8xttEEOGQ3yyQ8j890hL5J-wCUNEwbkE,677
25
25
  fnschoo1/canteen/migrations/0015_alter_category_options_alter_category_priority.py,sha256=l8WVVTaDDbB3-xBMdU402gsQo3mhfeUUEcIp_hYVEPk,720
26
26
  fnschoo1/canteen/migrations/0016_consumption_unique_ingredient_date_of_using.py,sha256=kiNr4ISVoy99U7LuJMItJYtNyE4BPYR_jSdnKoXxWt4,520
27
+ fnschoo1/canteen/migrations/0017_ingredient_updated_at_alter_category_created_at_and_more.py,sha256=fvbJV9uf8lUrqASwPO2EsyW3ApXkFk4zBE1vlT2UV0o,872
27
28
  fnschoo1/canteen/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
28
29
  fnschoo1/canteen/templates/canteen/close.html,sha256=pLYeJmGaOEJKMUJdZmYzz_n--l28IRDQ4fXvetP_Vsc,200
29
30
  fnschoo1/canteen/templates/canteen/category/create.html,sha256=T33zbHRYUrWlLYSLAYZ14Y03Gw2tGMoYzBWUVv-I8pk,609
@@ -31,29 +32,30 @@ fnschoo1/canteen/templates/canteen/category/delete.html,sha256=iPmGAB2r8K9DqBTp9
31
32
  fnschoo1/canteen/templates/canteen/category/list.html,sha256=QLO2GA13Co48Ijd0DCCN4lcMm1fgJqT2CprH5VdLZPw,2148
32
33
  fnschoo1/canteen/templates/canteen/category/update.html,sha256=UnCI95eqsQXfdUMdJXWUf1qNVWM9ZgsP03ayZ_Nrak8,831
33
34
  fnschoo1/canteen/templates/canteen/consumption/_create.html,sha256=tXlJcd566D9sYbsnj5KN-m0lyobT2AwwYt9oxUHbn4M,1026
34
- fnschoo1/canteen/templates/canteen/consumption/create.html,sha256=Wy30E3bnOcP7D1LdqtZhp6WUCuJfuCiUmmSmlj6HhGQ,16707
35
+ fnschoo1/canteen/templates/canteen/consumption/create.html,sha256=LSxyYQZfQLAekD84AQaJp_JRYge8I4IBF7DkTGLIGEI,16744
35
36
  fnschoo1/canteen/templates/canteen/ingredient/close.html,sha256=pLYeJmGaOEJKMUJdZmYzz_n--l28IRDQ4fXvetP_Vsc,200
36
37
  fnschoo1/canteen/templates/canteen/ingredient/create.html,sha256=i4ajPDpXBTUp08lIiwn0wZoNEohyXlX3G64zG1lmIcs,839
37
38
  fnschoo1/canteen/templates/canteen/ingredient/create_one.html,sha256=y9EAKXmyIrlp5-DxHd86l1rMtB16KuOjPMbPiLAN_ag,615
38
39
  fnschoo1/canteen/templates/canteen/ingredient/delete.html,sha256=npUtDGV7KK2jvc2AkiNlK6F2wUsKwLB5DQuBULm1pW8,1227
39
- fnschoo1/canteen/templates/canteen/ingredient/list.html,sha256=GVHpYU1aghnntVRrukbIq49ArwgbRiuVQ2pMYHU7Q7c,4655
40
+ fnschoo1/canteen/templates/canteen/ingredient/list.html,sha256=QqPvNbfQCv74jOWpgMX9I-m-Omg3HR_k51XlDFMv3YE,5900
40
41
  fnschoo1/canteen/templates/canteen/ingredient/update.html,sha256=JwW8dVQ1fzvxjA32RvRSSqrYzPPaajObvy7D45gz5pc,835
41
42
  fnschoo1/canteen/templates/canteen/meal_type/create.html,sha256=OIxOcAa3Pp3OPT-Z0PRLINfZ08CO61ulNDU-YJA6EKI,612
42
43
  fnschoo1/canteen/templates/canteen/meal_type/delete.html,sha256=i9PBX3ShXU4Az62MEawAuDWM5jwOmKByywizhdvuSTI,1567
43
44
  fnschoo1/canteen/templates/canteen/meal_type/list.html,sha256=c5kJUE1OgpRtSrMV4wFwL_gbRSaX_XMM4Zt1JTx34_g,1907
44
45
  fnschoo1/canteen/templates/canteen/meal_type/update.html,sha256=vqqyuC1m2CnPd3KrcjA8t4hbJslT001s4XvcYpkQmO0,834
45
46
  fnschoo1/canteen/workbook/__init__.py,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
46
- fnschoo1/canteen/workbook/generate.py,sha256=P2I_L494IFkWrNVXmpr9UFADdtcGdyQAEfRSwxcpftQ,109430
47
+ fnschoo1/canteen/workbook/generate.py,sha256=wmqt5eFxmZRZdasMTsuc1AbntwOpUN4uVOtjfpM2j1k,109350
47
48
  fnschoo1/fnschool/__init__.py,sha256=TmHhzykpKNMoMf6eD-EKvbvmnlzs1XGHtvD55ae1sXs,287
49
+ fnschoo1/fnschool/_settings.py,sha256=ajz1GSNU9xYVrFEDSz6Xwg7amWQ_yvW75tQa1ZvRIWc,3
48
50
  fnschoo1/fnschool/asgi.py,sha256=kzkqosS10uBlyBX53EXcsATcvEZmac6nsPzyOHCuucE,393
49
- fnschoo1/fnschool/settings.py,sha256=l9Y1iQCivSfmsNfnHltfXLzxP6gG4VMIL4GEZgAp8YM,4357
51
+ fnschoo1/fnschool/settings.py,sha256=4etVUGXK6YNv7xmNi95N4serk9JEr93kG8LsL0O3r5Y,4651
50
52
  fnschoo1/fnschool/urls.py,sha256=8WPemtCUuStd0R9gDP70c-NRQ5k7G4ksq6dYGH6xCDM,1036
51
53
  fnschoo1/fnschool/views.py,sha256=MfujMhFkRLxT-saID1xTU16v0khzIl6ceDl7_JgrgFs,152
52
54
  fnschoo1/fnschool/wsgi.py,sha256=dQq4S0vZWCz8w5R9KooJeLYTVFXvEgJRYe7NFZwVxU8,393
53
55
  fnschoo1/fnschool/templatetags/__init__.py,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
54
56
  fnschoo1/fnschool/templatetags/fnschool_tags.py,sha256=l5Zov4VlQKpz-69SFftP4kXyMymz-a0D5F_ss_eiFc4,568
55
57
  fnschoo1/locale/en/LC_MESSAGES/django.mo,sha256=M8AB6fmjwlEd761iFlasNWdiEYfE-2nIwBoioGtEVUo,404
56
- fnschoo1/locale/zh_Hans/LC_MESSAGES/django.mo,sha256=FgrQvpbmFej9tjCMnRf6evjeAbA0ofCuUpFlCIxkccs,22621
58
+ fnschoo1/locale/zh_Hans/LC_MESSAGES/django.mo,sha256=a70R8-JrOVyLEYOxZGLsqn7Q0DzKQkwi09-5p05Dlbc,22621
57
59
  fnschoo1/profiles/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
58
60
  fnschoo1/profiles/admin.py,sha256=93UCvdODI63KzCDfFFnKmfvCMGCp6FCG9WErE91i79Y,522
59
61
  fnschoo1/profiles/apps.py,sha256=WDu6eceFnDkBFMqquAolMZMo7XPb0ah6l2q2AruTrl4,215
@@ -75,11 +77,11 @@ fnschoo1/profiles/templates/profiles/edit.html,sha256=49afyNH7OsqEKfD9TFrDKbRZ7r
75
77
  fnschoo1/profiles/templates/profiles/log_in.html,sha256=9hcuxdxAUHdFBxrJTJ_X6IVwKC_Jw6mfA5Sqx9a4bZA,736
76
78
  fnschoo1/profiles/templates/profiles/log_out.html,sha256=RRb6K-0G1Jm4RhfY9VwftJgNwgI8jsKb_N5Gvp5XtiE,320
77
79
  fnschoo1/static/css/bootstrap.min.css,sha256=eGY1FwN6FhXUvbmXraT1t_q2vcNlQa8g5xQDwg8mG6w,280591
78
- fnschoo1/static/css/fnschool.css,sha256=nPjOh0vWuuFnZWrXcT2E3j3adL77v_PRyeml65MvkEE,418
80
+ fnschoo1/static/css/fnschool.css,sha256=k71DBJi4EwUQSOPyXX6G-15m8HN_z4_Ac72Du0Zf9Co,476
79
81
  fnschoo1/static/images/favicon.ico,sha256=S8Tf0NsUdHnoYO0SEn-sig6YjB0atIpEtSlm7p1HxjY,5014
80
82
  fnschoo1/static/js/bootstrap.bundle.min.js,sha256=6kw84LCFc4QJzifgkle0FsvQrpt2NVRNPNjSSc9caiM,125881
81
83
  fnschoo1/static/js/bootstrap.min.js,sha256=0SHpZTHghUOz_BNedMzuH00z5lgwOSRKP_KI9G5Ogbk,88597
82
- fnschoo1/static/js/fnschool.js,sha256=tq9QzYsq8S3CvCqLk2U9tQuwQTcE-hYpf5YanqIHi4g,2959
84
+ fnschoo1/static/js/fnschool.js,sha256=M2hdpT_g6f77bDLzN3zWxjPybcfrWHRSpo6deNSxkH4,3684
83
85
  fnschoo1/static/js/jquery.min.js,sha256=np_WnfpAmUmEO_iheFAJKf6mbm0_laW3Ns4x7kjSlt4,162505
84
86
  fnschoo1/static/js/jquery.slim.min.js,sha256=p5YkbOjgHxX3hTadKlGuDW58NvJ1ldjjokDuDQ_5yXs,129962
85
87
  fnschoo1/static/js/popper.min.js,sha256=O2xdmtEow7gq3I7-0lKjshvxHkBe0hTWrMkbX2fy0XQ,36887
@@ -96,14 +98,14 @@ fnschoo1/templates/includes/_navigation.html,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRk
96
98
  fnschoo1/templates/includes/_paginator.html,sha256=Q-FRCODFNlETjn2yX18IfhctRWfqEgEnIc5LcdHzKSo,1262
97
99
  fnschoo1/templates/registration/logged_out.html,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
98
100
  fnschoo1/templates/registration/login.html,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
99
- fnschool-20251018.81021.825.dist-info/licenses/LICENSE,sha256=2n6rt7r999OuXp8iOqW9we7ORaxWncIbOwN1ILRGR2g,7651
100
- fnschool-20251018.81021.825.dist-info/METADATA,sha256=ybABvY8yWMb1doynfn_prJV9EHRUCi0VeG1wBqhniYg,4752
101
- fnschool-20251018.81021.825.dist-info/SOURCES.txt.py,sha256=2LY2mshgNtxI3ICB-oBjyMYgJk2bQqeGFM5J5ay5TQs,4954
102
- fnschool-20251018.81021.825.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
103
- fnschool-20251018.81021.825.dist-info/dependency_links.txt.py,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
104
- fnschool-20251018.81021.825.dist-info/entry_points.txt,sha256=Ow5nChVFJY3O4TJAIE1ZydMev1MUtgRsT1b8eFP6728,54
105
- fnschool-20251018.81021.825.dist-info/entry_points.txt.py,sha256=7iOwIx_m9Y6xJt___BZHWJh27LV5hqWnUjmj77MoRys,47
106
- fnschool-20251018.81021.825.dist-info/requires.txt.py,sha256=PqRcHIQSMPUb271hacYrlSDHwB1WDZmlWUkh6RnBz_g,113
107
- fnschool-20251018.81021.825.dist-info/top_level.txt,sha256=s6ZKnNm94Q0-247a50eI7jDK98uPF6P2kC9Ovd3LUlM,9
108
- fnschool-20251018.81021.825.dist-info/top_level.txt.py,sha256=_7CbrSihm0dzBn_tTy2ass_Y2VlkVNT2eylE8mcfwHY,9
109
- fnschool-20251018.81021.825.dist-info/RECORD,,
101
+ fnschool-20251020.82239.858.dist-info/licenses/LICENSE,sha256=2n6rt7r999OuXp8iOqW9we7ORaxWncIbOwN1ILRGR2g,7651
102
+ fnschool-20251020.82239.858.dist-info/METADATA,sha256=_e8pXoy9CHrKNnLPpki1a7kt3BWuBlOO5ZSwhX7HVZo,4752
103
+ fnschool-20251020.82239.858.dist-info/SOURCES.txt.py,sha256=2LY2mshgNtxI3ICB-oBjyMYgJk2bQqeGFM5J5ay5TQs,4954
104
+ fnschool-20251020.82239.858.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
105
+ fnschool-20251020.82239.858.dist-info/dependency_links.txt.py,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
106
+ fnschool-20251020.82239.858.dist-info/entry_points.txt,sha256=Ow5nChVFJY3O4TJAIE1ZydMev1MUtgRsT1b8eFP6728,54
107
+ fnschool-20251020.82239.858.dist-info/entry_points.txt.py,sha256=7iOwIx_m9Y6xJt___BZHWJh27LV5hqWnUjmj77MoRys,47
108
+ fnschool-20251020.82239.858.dist-info/requires.txt.py,sha256=PqRcHIQSMPUb271hacYrlSDHwB1WDZmlWUkh6RnBz_g,113
109
+ fnschool-20251020.82239.858.dist-info/top_level.txt,sha256=s6ZKnNm94Q0-247a50eI7jDK98uPF6P2kC9Ovd3LUlM,9
110
+ fnschool-20251020.82239.858.dist-info/top_level.txt.py,sha256=_7CbrSihm0dzBn_tTy2ass_Y2VlkVNT2eylE8mcfwHY,9
111
+ fnschool-20251020.82239.858.dist-info/RECORD,,