fnschool 20251019.81526.808__py3-none-any.whl → 20251021.80056.821__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.

fnschoo1/__init__.py CHANGED
@@ -6,4 +6,4 @@ import random
6
6
  import sys
7
7
  from pathlib import Path
8
8
 
9
- __version__ = "20251019.81526.808"
9
+ __version__ = "20251021.80056.821"
@@ -146,6 +146,9 @@ class Ingredient(models.Model):
146
146
  return quantity
147
147
 
148
148
  def get_consuming_quantity(self, date_end):
149
+
150
+ if self.storage_date < date_end:
151
+ return 0
149
152
  consumptions = self.cleaned_consumptions
150
153
  if not consumptions:
151
154
  return 0
@@ -159,6 +162,8 @@ class Ingredient(models.Model):
159
162
  return quantity
160
163
 
161
164
  def get_remaining_quantity(self, date_end):
165
+ if self.storage_date < date_end:
166
+ return 0
162
167
  return self.quantity - self.get_consuming_quantity(date_end)
163
168
 
164
169
  @property
fnschoo1/canteen/views.py CHANGED
@@ -97,6 +97,9 @@ quantity_header = (
97
97
  "To prevent the number of decimal places in the unit "
98
98
  + 'price from becoming too large, "quantity" is '
99
99
  + "only allowed to be an integer."
100
+ + " If your unit price is a decimal, please use a "
101
+ + "smaller quantity unit and then expand the "
102
+ + "quantity to an integer."
100
103
  ),
101
104
  )
102
105
 
@@ -396,25 +399,26 @@ def edit_ingredient(request, ingredient_id):
396
399
 
397
400
  if request.method == "POST":
398
401
 
399
- form = IngredientForm(request.POST, instance=ingredient)
400
-
401
- total_price = form.instance.total_price
402
- quantity = form.instance.quantity
402
+ total_price = request.POST.get("total_price")
403
+ quantity = request.POST.get("quantity")
403
404
 
404
405
  [total_price0, quantity0], [total_price1, quantity1] = split_price(
405
406
  total_price, quantity
406
407
  )
407
408
 
409
+ form = IngredientForm(request.POST, instance=ingredient)
408
410
  if total_price1:
409
411
  unit_price_error_msg = _(
410
- "The unit pricei ({unit_price}) has more than 3 decimal places and cannot be saved. Please modify it again."
412
+ "The unit pricei ({unit_price}) has more than {decimal_prec} decimal places and cannot be saved. Please modify it again."
411
413
  ).format(
412
414
  unit_price=(
413
415
  Decimal(str(total_price)) / Decimal(str(float(quantity)))
414
- ).normalize()
416
+ ).normalize(),
417
+ decimal_prec=decimal_prec,
415
418
  )
416
419
  form.add_error("total_price", unit_price_error_msg)
417
420
  form.add_error("quantity", unit_price_error_msg)
421
+ form.fields["total_price"].widget.attrs.update({"autofocus": ""})
418
422
  return render(
419
423
  request, "canteen/ingredient/update.html", {"form": form}
420
424
  )
@@ -578,7 +582,7 @@ def create_ingredients(request):
578
582
  created_at=datetime.now().date(),
579
583
  )
580
584
 
581
- storage_date = row[storage_date_header[0]]
585
+ storage_date = date_parser.parse(row[storage_date_header[0]])
582
586
  name = row[ingredient_name_header[0]]
583
587
  quantity_unit_name = row[quantity_unit_name_header[0]]
584
588
  is_ignorable = not row[is_ignorable_header[0]] is np.nan
@@ -591,19 +595,6 @@ def create_ingredients(request):
591
595
  )
592
596
 
593
597
  if total_price1:
594
-
595
- Ingredient.objects.create(
596
- user=request.user,
597
- storage_date=storage_date,
598
- name=name + _("(1)"),
599
- meal_type=meal_type,
600
- category=category,
601
- quantity=quantity0,
602
- total_price=total_price0,
603
- quantity_unit_name=quantity_unit_name,
604
- is_ignorable=is_ignorable,
605
- )
606
-
607
598
  Ingredient.objects.create(
608
599
  user=request.user,
609
600
  storage_date=storage_date,
@@ -615,19 +606,19 @@ def create_ingredients(request):
615
606
  quantity_unit_name=quantity_unit_name,
616
607
  is_ignorable=is_ignorable,
617
608
  )
618
-
619
- else:
620
- Ingredient.objects.create(
621
- user=request.user,
622
- storage_date=storage_date,
623
- name=name,
624
- meal_type=meal_type,
625
- category=category,
626
- quantity=quantity0,
627
- total_price=total_price0,
628
- quantity_unit_name=quantity_unit_name,
629
- is_ignorable=is_ignorable,
630
- )
609
+ name = name + _("(1)")
610
+
611
+ Ingredient.objects.create(
612
+ user=request.user,
613
+ storage_date=storage_date,
614
+ name=name,
615
+ meal_type=meal_type,
616
+ category=category,
617
+ quantity=quantity0,
618
+ total_price=total_price0,
619
+ quantity_unit_name=quantity_unit_name,
620
+ is_ignorable=is_ignorable,
621
+ )
631
622
 
632
623
  return redirect("canteen:close_window")
633
624
 
@@ -674,7 +665,7 @@ def get_template_workbook_of_purchased_ingredients(request):
674
665
  ws.column_dimensions[column_letter].width = len(h) + hans_len + 2
675
666
  if c:
676
667
  h_cell.comment = Comment(c, _("the FNSCHOOL Authors"))
677
-
668
+ ws.freeze_panes = "B2"
678
669
  buffer = io.BytesIO()
679
670
  wb.save(buffer)
680
671
  buffer.seek(0)
@@ -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
@@ -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
+
@@ -165,4 +165,14 @@ MEDIA_URL = "/media/"
165
165
  MEDIA_ROOT = os.path.join(BASE_DIR, "media")
166
166
 
167
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
+
168
178
  # The end.
@@ -8,7 +8,17 @@ function make_highlight(query, time_data) {
8
8
  if (seconds_diff < 46) {
9
9
  highlight_elements_toggled.push(element)
10
10
  element.toggleClass('fn-highlight')
11
- $('html,body').animate({ scrollTop: element.offset().top }, 1)
11
+ var table_container = $(element.closest('.table-container'))
12
+ if (table_container.length) {
13
+ var container = table_container
14
+ var thead = $(element.closest('table').find('thead'))
15
+ var pos =
16
+ element.offset().top -
17
+ container.offset().top +
18
+ container.scrollTop() -
19
+ thead.height()
20
+ container.animate({ scrollTop: pos }, 1)
21
+ }
12
22
  }
13
23
  })
14
24
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fnschool
3
- Version: 20251019.81526.808
3
+ Version: 20251021.80056.821
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=3uZNnXdN943uWbtfJhdf4iLpV-akwekm2ysAMBUoD-0,197
1
+ fnschoo1/__init__.py,sha256=RZS5WHI7d3qOtdVETVDp36aT2u7RHa4Ol549RY1QmdI,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
6
  fnschoo1/canteen/forms.py,sha256=JaN6SX9KIrJeUKRfTMf6bA0Op3mEGCc6xqUE6yWC8TU,2596
7
- fnschoo1/canteen/models.py,sha256=gk3Lk8Li6P7YlYU2yAQeI7WVG8Jm8jxRgI2uyYJwbBA,6206
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=9qsNiCM4sUEUpV_8g874msSamrp-dqTb5mdUtyWvZmQ,29171
10
+ fnschoo1/canteen/views.py,sha256=4YYacxxa_regc56TqRQ_Bb-_wX3zuFIKyhGGp4QPh7M,28983
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
@@ -44,17 +44,18 @@ fnschoo1/canteen/templates/canteen/meal_type/delete.html,sha256=i9PBX3ShXU4Az62M
44
44
  fnschoo1/canteen/templates/canteen/meal_type/list.html,sha256=c5kJUE1OgpRtSrMV4wFwL_gbRSaX_XMM4Zt1JTx34_g,1907
45
45
  fnschoo1/canteen/templates/canteen/meal_type/update.html,sha256=vqqyuC1m2CnPd3KrcjA8t4hbJslT001s4XvcYpkQmO0,834
46
46
  fnschoo1/canteen/workbook/__init__.py,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
47
- fnschoo1/canteen/workbook/generate.py,sha256=sRJ_r6DLh0DiUr1FTBST17YUqB7G6P_XUCpw2YQn0BA,109434
47
+ fnschoo1/canteen/workbook/generate.py,sha256=wmqt5eFxmZRZdasMTsuc1AbntwOpUN4uVOtjfpM2j1k,109350
48
48
  fnschoo1/fnschool/__init__.py,sha256=TmHhzykpKNMoMf6eD-EKvbvmnlzs1XGHtvD55ae1sXs,287
49
+ fnschoo1/fnschool/_settings.py,sha256=ajz1GSNU9xYVrFEDSz6Xwg7amWQ_yvW75tQa1ZvRIWc,3
49
50
  fnschoo1/fnschool/asgi.py,sha256=kzkqosS10uBlyBX53EXcsATcvEZmac6nsPzyOHCuucE,393
50
- fnschoo1/fnschool/settings.py,sha256=VJJFsbLReFDbOqhqC1zWX_xZSw9hcboXQD2oJaXrPqE,4381
51
+ fnschoo1/fnschool/settings.py,sha256=4etVUGXK6YNv7xmNi95N4serk9JEr93kG8LsL0O3r5Y,4651
51
52
  fnschoo1/fnschool/urls.py,sha256=8WPemtCUuStd0R9gDP70c-NRQ5k7G4ksq6dYGH6xCDM,1036
52
53
  fnschoo1/fnschool/views.py,sha256=MfujMhFkRLxT-saID1xTU16v0khzIl6ceDl7_JgrgFs,152
53
54
  fnschoo1/fnschool/wsgi.py,sha256=dQq4S0vZWCz8w5R9KooJeLYTVFXvEgJRYe7NFZwVxU8,393
54
55
  fnschoo1/fnschool/templatetags/__init__.py,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
55
56
  fnschoo1/fnschool/templatetags/fnschool_tags.py,sha256=l5Zov4VlQKpz-69SFftP4kXyMymz-a0D5F_ss_eiFc4,568
56
57
  fnschoo1/locale/en/LC_MESSAGES/django.mo,sha256=M8AB6fmjwlEd761iFlasNWdiEYfE-2nIwBoioGtEVUo,404
57
- fnschoo1/locale/zh_Hans/LC_MESSAGES/django.mo,sha256=FgrQvpbmFej9tjCMnRf6evjeAbA0ofCuUpFlCIxkccs,22621
58
+ fnschoo1/locale/zh_Hans/LC_MESSAGES/django.mo,sha256=FgGcCau9FuxB2G_Q2mjrNGjxhlndLTyyp-6aWarsHQw,23151
58
59
  fnschoo1/profiles/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
59
60
  fnschoo1/profiles/admin.py,sha256=93UCvdODI63KzCDfFFnKmfvCMGCp6FCG9WErE91i79Y,522
60
61
  fnschoo1/profiles/apps.py,sha256=WDu6eceFnDkBFMqquAolMZMo7XPb0ah6l2q2AruTrl4,215
@@ -80,7 +81,7 @@ fnschoo1/static/css/fnschool.css,sha256=k71DBJi4EwUQSOPyXX6G-15m8HN_z4_Ac72Du0Zf
80
81
  fnschoo1/static/images/favicon.ico,sha256=S8Tf0NsUdHnoYO0SEn-sig6YjB0atIpEtSlm7p1HxjY,5014
81
82
  fnschoo1/static/js/bootstrap.bundle.min.js,sha256=6kw84LCFc4QJzifgkle0FsvQrpt2NVRNPNjSSc9caiM,125881
82
83
  fnschoo1/static/js/bootstrap.min.js,sha256=0SHpZTHghUOz_BNedMzuH00z5lgwOSRKP_KI9G5Ogbk,88597
83
- fnschoo1/static/js/fnschool.js,sha256=M2hdpT_g6f77bDLzN3zWxjPybcfrWHRSpo6deNSxkH4,3684
84
+ fnschoo1/static/js/fnschool.js,sha256=O4WdKFHViJVY1Z2SIHaQWuUokajBsciK-IRR8bpUyNg,4022
84
85
  fnschoo1/static/js/jquery.min.js,sha256=np_WnfpAmUmEO_iheFAJKf6mbm0_laW3Ns4x7kjSlt4,162505
85
86
  fnschoo1/static/js/jquery.slim.min.js,sha256=p5YkbOjgHxX3hTadKlGuDW58NvJ1ldjjokDuDQ_5yXs,129962
86
87
  fnschoo1/static/js/popper.min.js,sha256=O2xdmtEow7gq3I7-0lKjshvxHkBe0hTWrMkbX2fy0XQ,36887
@@ -97,14 +98,14 @@ fnschoo1/templates/includes/_navigation.html,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRk
97
98
  fnschoo1/templates/includes/_paginator.html,sha256=Q-FRCODFNlETjn2yX18IfhctRWfqEgEnIc5LcdHzKSo,1262
98
99
  fnschoo1/templates/registration/logged_out.html,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
99
100
  fnschoo1/templates/registration/login.html,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
100
- fnschool-20251019.81526.808.dist-info/licenses/LICENSE,sha256=2n6rt7r999OuXp8iOqW9we7ORaxWncIbOwN1ILRGR2g,7651
101
- fnschool-20251019.81526.808.dist-info/METADATA,sha256=YmFIFr7SUfIaw_itWuS1Ob4vM70tpTj4N2wsFt5-4fk,4752
102
- fnschool-20251019.81526.808.dist-info/SOURCES.txt.py,sha256=2LY2mshgNtxI3ICB-oBjyMYgJk2bQqeGFM5J5ay5TQs,4954
103
- fnschool-20251019.81526.808.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
104
- fnschool-20251019.81526.808.dist-info/dependency_links.txt.py,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
105
- fnschool-20251019.81526.808.dist-info/entry_points.txt,sha256=Ow5nChVFJY3O4TJAIE1ZydMev1MUtgRsT1b8eFP6728,54
106
- fnschool-20251019.81526.808.dist-info/entry_points.txt.py,sha256=7iOwIx_m9Y6xJt___BZHWJh27LV5hqWnUjmj77MoRys,47
107
- fnschool-20251019.81526.808.dist-info/requires.txt.py,sha256=PqRcHIQSMPUb271hacYrlSDHwB1WDZmlWUkh6RnBz_g,113
108
- fnschool-20251019.81526.808.dist-info/top_level.txt,sha256=s6ZKnNm94Q0-247a50eI7jDK98uPF6P2kC9Ovd3LUlM,9
109
- fnschool-20251019.81526.808.dist-info/top_level.txt.py,sha256=_7CbrSihm0dzBn_tTy2ass_Y2VlkVNT2eylE8mcfwHY,9
110
- fnschool-20251019.81526.808.dist-info/RECORD,,
101
+ fnschool-20251021.80056.821.dist-info/licenses/LICENSE,sha256=2n6rt7r999OuXp8iOqW9we7ORaxWncIbOwN1ILRGR2g,7651
102
+ fnschool-20251021.80056.821.dist-info/METADATA,sha256=i1XpkLUas8C312aV46zXzVizpQcOpGUg-M9-hFLnLlk,4752
103
+ fnschool-20251021.80056.821.dist-info/SOURCES.txt.py,sha256=2LY2mshgNtxI3ICB-oBjyMYgJk2bQqeGFM5J5ay5TQs,4954
104
+ fnschool-20251021.80056.821.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
105
+ fnschool-20251021.80056.821.dist-info/dependency_links.txt.py,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
106
+ fnschool-20251021.80056.821.dist-info/entry_points.txt,sha256=Ow5nChVFJY3O4TJAIE1ZydMev1MUtgRsT1b8eFP6728,54
107
+ fnschool-20251021.80056.821.dist-info/entry_points.txt.py,sha256=7iOwIx_m9Y6xJt___BZHWJh27LV5hqWnUjmj77MoRys,47
108
+ fnschool-20251021.80056.821.dist-info/requires.txt.py,sha256=PqRcHIQSMPUb271hacYrlSDHwB1WDZmlWUkh6RnBz_g,113
109
+ fnschool-20251021.80056.821.dist-info/top_level.txt,sha256=s6ZKnNm94Q0-247a50eI7jDK98uPF6P2kC9Ovd3LUlM,9
110
+ fnschool-20251021.80056.821.dist-info/top_level.txt.py,sha256=_7CbrSihm0dzBn_tTy2ass_Y2VlkVNT2eylE8mcfwHY,9
111
+ fnschool-20251021.80056.821.dist-info/RECORD,,