fnschool 20251018.80430.821__tar.gz → 20251019.81526.808__tar.gz

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 (116) hide show
  1. {fnschool-20251018.80430.821 → fnschool-20251019.81526.808}/PKG-INFO +1 -1
  2. {fnschool-20251018.80430.821 → fnschool-20251019.81526.808}/src/fnschoo1/__init__.py +1 -1
  3. {fnschool-20251018.80430.821 → fnschool-20251019.81526.808}/src/fnschoo1/canteen/forms.py +1 -1
  4. fnschool-20251019.81526.808/src/fnschoo1/canteen/migrations/0017_ingredient_updated_at_alter_category_created_at_and_more.py +30 -0
  5. {fnschool-20251018.80430.821 → fnschool-20251019.81526.808}/src/fnschoo1/canteen/models.py +9 -2
  6. {fnschool-20251018.80430.821 → fnschool-20251019.81526.808}/src/fnschoo1/canteen/templates/canteen/consumption/create.html +5 -4
  7. fnschool-20251019.81526.808/src/fnschoo1/canteen/templates/canteen/ingredient/list.html +171 -0
  8. {fnschool-20251018.80430.821 → fnschool-20251019.81526.808}/src/fnschoo1/canteen/views.py +51 -2
  9. {fnschool-20251018.80430.821 → fnschool-20251019.81526.808}/src/fnschoo1/canteen/workbook/generate.py +4 -4
  10. {fnschool-20251018.80430.821 → fnschool-20251019.81526.808}/src/fnschoo1/fnschool/settings.py +2 -1
  11. {fnschool-20251018.80430.821 → fnschool-20251019.81526.808}/src/fnschoo1/static/css/fnschool.css +6 -0
  12. {fnschool-20251018.80430.821 → fnschool-20251019.81526.808}/src/fnschoo1/static/js/fnschool.js +24 -0
  13. {fnschool-20251018.80430.821 → fnschool-20251019.81526.808}/src/fnschool.egg-info/PKG-INFO +1 -1
  14. {fnschool-20251018.80430.821 → fnschool-20251019.81526.808}/src/fnschool.egg-info/SOURCES.txt +1 -0
  15. fnschool-20251018.80430.821/src/fnschoo1/canteen/templates/canteen/ingredient/list.html +0 -139
  16. {fnschool-20251018.80430.821 → fnschool-20251019.81526.808}/LICENSE +0 -0
  17. {fnschool-20251018.80430.821 → fnschool-20251019.81526.808}/README.md +0 -0
  18. {fnschool-20251018.80430.821 → fnschool-20251019.81526.808}/pyproject.toml +0 -0
  19. {fnschool-20251018.80430.821 → fnschool-20251019.81526.808}/setup.cfg +0 -0
  20. {fnschool-20251018.80430.821 → fnschool-20251019.81526.808}/src/fnschoo1/canteen/__init__.py +0 -0
  21. {fnschool-20251018.80430.821 → fnschool-20251019.81526.808}/src/fnschoo1/canteen/admin.py +0 -0
  22. {fnschool-20251018.80430.821 → fnschool-20251019.81526.808}/src/fnschoo1/canteen/apps.py +0 -0
  23. {fnschool-20251018.80430.821 → fnschool-20251019.81526.808}/src/fnschoo1/canteen/migrations/0001_initial.py +0 -0
  24. {fnschool-20251018.80430.821 → fnschool-20251019.81526.808}/src/fnschoo1/canteen/migrations/0002_ingredient_is_disabled.py +0 -0
  25. {fnschool-20251018.80430.821 → fnschool-20251019.81526.808}/src/fnschoo1/canteen/migrations/0003_consumption_is_disabled_alter_ingredient_is_disabled.py +0 -0
  26. {fnschool-20251018.80430.821 → fnschool-20251019.81526.808}/src/fnschoo1/canteen/migrations/0004_alter_ingredient_name_category_and_more.py +0 -0
  27. {fnschool-20251018.80430.821 → fnschool-20251019.81526.808}/src/fnschoo1/canteen/migrations/0005_alter_category_created_at_alter_category_name_and_more.py +0 -0
  28. {fnschool-20251018.80430.821 → fnschool-20251019.81526.808}/src/fnschoo1/canteen/migrations/0006_category_is_disabled_alter_category_user_and_more.py +0 -0
  29. {fnschool-20251018.80430.821 → fnschool-20251019.81526.808}/src/fnschoo1/canteen/migrations/0007_alter_consumption_amount_used_and_more.py +0 -0
  30. {fnschool-20251018.80430.821 → fnschool-20251019.81526.808}/src/fnschoo1/canteen/migrations/0008_category_abbreviation_mealtype.py +0 -0
  31. {fnschool-20251018.80430.821 → fnschool-20251019.81526.808}/src/fnschoo1/canteen/migrations/0009_alter_category_abbreviation_and_more.py +0 -0
  32. {fnschool-20251018.80430.821 → fnschool-20251019.81526.808}/src/fnschoo1/canteen/migrations/0010_alter_consumption_options_alter_ingredient_options_and_more.py +0 -0
  33. {fnschool-20251018.80430.821 → fnschool-20251019.81526.808}/src/fnschoo1/canteen/migrations/0011_category_pin_to_consumptions_top.py +0 -0
  34. {fnschool-20251018.80430.821 → fnschool-20251019.81526.808}/src/fnschoo1/canteen/migrations/0012_alter_ingredient_storage_date.py +0 -0
  35. {fnschool-20251018.80430.821 → fnschool-20251019.81526.808}/src/fnschoo1/canteen/migrations/0013_alter_consumption_options_alter_ingredient_options_and_more.py +0 -0
  36. {fnschool-20251018.80430.821 → fnschool-20251019.81526.808}/src/fnschoo1/canteen/migrations/0014_category_priority.py +0 -0
  37. {fnschool-20251018.80430.821 → fnschool-20251019.81526.808}/src/fnschoo1/canteen/migrations/0015_alter_category_options_alter_category_priority.py +0 -0
  38. {fnschool-20251018.80430.821 → fnschool-20251019.81526.808}/src/fnschoo1/canteen/migrations/0016_consumption_unique_ingredient_date_of_using.py +0 -0
  39. {fnschool-20251018.80430.821 → fnschool-20251019.81526.808}/src/fnschoo1/canteen/migrations/__init__.py +0 -0
  40. {fnschool-20251018.80430.821 → fnschool-20251019.81526.808}/src/fnschoo1/canteen/templates/canteen/category/create.html +0 -0
  41. {fnschool-20251018.80430.821 → fnschool-20251019.81526.808}/src/fnschoo1/canteen/templates/canteen/category/delete.html +0 -0
  42. {fnschool-20251018.80430.821 → fnschool-20251019.81526.808}/src/fnschoo1/canteen/templates/canteen/category/list.html +0 -0
  43. {fnschool-20251018.80430.821 → fnschool-20251019.81526.808}/src/fnschoo1/canteen/templates/canteen/category/update.html +0 -0
  44. {fnschool-20251018.80430.821 → fnschool-20251019.81526.808}/src/fnschoo1/canteen/templates/canteen/close.html +0 -0
  45. {fnschool-20251018.80430.821 → fnschool-20251019.81526.808}/src/fnschoo1/canteen/templates/canteen/consumption/_create.html +0 -0
  46. {fnschool-20251018.80430.821 → fnschool-20251019.81526.808}/src/fnschoo1/canteen/templates/canteen/ingredient/close.html +0 -0
  47. {fnschool-20251018.80430.821 → fnschool-20251019.81526.808}/src/fnschoo1/canteen/templates/canteen/ingredient/create.html +0 -0
  48. {fnschool-20251018.80430.821 → fnschool-20251019.81526.808}/src/fnschoo1/canteen/templates/canteen/ingredient/create_one.html +0 -0
  49. {fnschool-20251018.80430.821 → fnschool-20251019.81526.808}/src/fnschoo1/canteen/templates/canteen/ingredient/delete.html +0 -0
  50. {fnschool-20251018.80430.821 → fnschool-20251019.81526.808}/src/fnschoo1/canteen/templates/canteen/ingredient/update.html +0 -0
  51. {fnschool-20251018.80430.821 → fnschool-20251019.81526.808}/src/fnschoo1/canteen/templates/canteen/meal_type/create.html +0 -0
  52. {fnschool-20251018.80430.821 → fnschool-20251019.81526.808}/src/fnschoo1/canteen/templates/canteen/meal_type/delete.html +0 -0
  53. {fnschool-20251018.80430.821 → fnschool-20251019.81526.808}/src/fnschoo1/canteen/templates/canteen/meal_type/list.html +0 -0
  54. {fnschool-20251018.80430.821 → fnschool-20251019.81526.808}/src/fnschoo1/canteen/templates/canteen/meal_type/update.html +0 -0
  55. {fnschool-20251018.80430.821 → fnschool-20251019.81526.808}/src/fnschoo1/canteen/tests.py +0 -0
  56. {fnschool-20251018.80430.821 → fnschool-20251019.81526.808}/src/fnschoo1/canteen/urls.py +0 -0
  57. {fnschool-20251018.80430.821 → fnschool-20251019.81526.808}/src/fnschoo1/canteen/workbook/__init__.py +0 -0
  58. {fnschool-20251018.80430.821 → fnschool-20251019.81526.808}/src/fnschoo1/fnschool/__init__.py +0 -0
  59. {fnschool-20251018.80430.821 → fnschool-20251019.81526.808}/src/fnschoo1/fnschool/asgi.py +0 -0
  60. {fnschool-20251018.80430.821 → fnschool-20251019.81526.808}/src/fnschoo1/fnschool/templatetags/__init__.py +0 -0
  61. {fnschool-20251018.80430.821 → fnschool-20251019.81526.808}/src/fnschoo1/fnschool/templatetags/fnschool_tags.py +0 -0
  62. {fnschool-20251018.80430.821 → fnschool-20251019.81526.808}/src/fnschoo1/fnschool/urls.py +0 -0
  63. {fnschool-20251018.80430.821 → fnschool-20251019.81526.808}/src/fnschoo1/fnschool/views.py +0 -0
  64. {fnschool-20251018.80430.821 → fnschool-20251019.81526.808}/src/fnschoo1/fnschool/wsgi.py +0 -0
  65. {fnschool-20251018.80430.821 → fnschool-20251019.81526.808}/src/fnschoo1/locale/en/LC_MESSAGES/django.mo +0 -0
  66. {fnschool-20251018.80430.821 → fnschool-20251019.81526.808}/src/fnschoo1/locale/zh_Hans/LC_MESSAGES/django.mo +0 -0
  67. {fnschool-20251018.80430.821 → fnschool-20251019.81526.808}/src/fnschoo1/manage.py +0 -0
  68. {fnschool-20251018.80430.821 → fnschool-20251019.81526.808}/src/fnschoo1/profiles/__init__.py +0 -0
  69. {fnschool-20251018.80430.821 → fnschool-20251019.81526.808}/src/fnschoo1/profiles/admin.py +0 -0
  70. {fnschool-20251018.80430.821 → fnschool-20251019.81526.808}/src/fnschoo1/profiles/apps.py +0 -0
  71. {fnschool-20251018.80430.821 → fnschool-20251019.81526.808}/src/fnschoo1/profiles/forms.py +0 -0
  72. {fnschool-20251018.80430.821 → fnschool-20251019.81526.808}/src/fnschoo1/profiles/migrations/0001_initial.py +0 -0
  73. {fnschool-20251018.80430.821 → fnschool-20251019.81526.808}/src/fnschoo1/profiles/migrations/0002_alter_profile_bio.py +0 -0
  74. {fnschool-20251018.80430.821 → fnschool-20251019.81526.808}/src/fnschoo1/profiles/migrations/0003_alter_profile_options_alter_profile_address_and_more.py +0 -0
  75. {fnschool-20251018.80430.821 → fnschool-20251019.81526.808}/src/fnschoo1/profiles/migrations/0004_profile_gender.py +0 -0
  76. {fnschool-20251018.80430.821 → fnschool-20251019.81526.808}/src/fnschoo1/profiles/migrations/0005_alter_profile_gender.py +0 -0
  77. {fnschool-20251018.80430.821 → fnschool-20251019.81526.808}/src/fnschoo1/profiles/migrations/__init__.py +0 -0
  78. {fnschool-20251018.80430.821 → fnschool-20251019.81526.808}/src/fnschoo1/profiles/models.py +0 -0
  79. {fnschool-20251018.80430.821 → fnschool-20251019.81526.808}/src/fnschoo1/profiles/signals.py +0 -0
  80. {fnschool-20251018.80430.821 → fnschool-20251019.81526.808}/src/fnschoo1/profiles/templates/profiles/create.html +0 -0
  81. {fnschool-20251018.80430.821 → fnschool-20251019.81526.808}/src/fnschoo1/profiles/templates/profiles/detail.html +0 -0
  82. {fnschool-20251018.80430.821 → fnschool-20251019.81526.808}/src/fnschoo1/profiles/templates/profiles/edit.html +0 -0
  83. {fnschool-20251018.80430.821 → fnschool-20251019.81526.808}/src/fnschoo1/profiles/templates/profiles/log_in.html +0 -0
  84. {fnschool-20251018.80430.821 → fnschool-20251019.81526.808}/src/fnschoo1/profiles/templates/profiles/log_out.html +0 -0
  85. {fnschool-20251018.80430.821 → fnschool-20251019.81526.808}/src/fnschoo1/profiles/tests.py +0 -0
  86. {fnschool-20251018.80430.821 → fnschool-20251019.81526.808}/src/fnschoo1/profiles/urls.py +0 -0
  87. {fnschool-20251018.80430.821 → fnschool-20251019.81526.808}/src/fnschoo1/profiles/views.py +0 -0
  88. {fnschool-20251018.80430.821 → fnschool-20251019.81526.808}/src/fnschoo1/static/css/bootstrap.min.css +0 -0
  89. {fnschool-20251018.80430.821 → fnschool-20251019.81526.808}/src/fnschoo1/static/images/favicon.ico +0 -0
  90. {fnschool-20251018.80430.821 → fnschool-20251019.81526.808}/src/fnschoo1/static/js/bootstrap.bundle.min.js +0 -0
  91. {fnschool-20251018.80430.821 → fnschool-20251019.81526.808}/src/fnschoo1/static/js/bootstrap.min.js +0 -0
  92. {fnschool-20251018.80430.821 → fnschool-20251019.81526.808}/src/fnschoo1/static/js/jquery.min.js +0 -0
  93. {fnschool-20251018.80430.821 → fnschool-20251019.81526.808}/src/fnschoo1/static/js/jquery.slim.min.js +0 -0
  94. {fnschool-20251018.80430.821 → fnschool-20251019.81526.808}/src/fnschoo1/static/js/popper.min.js +0 -0
  95. {fnschool-20251018.80430.821 → fnschool-20251019.81526.808}/src/fnschoo1/templates/base/_content.html +0 -0
  96. {fnschool-20251018.80430.821 → fnschool-20251019.81526.808}/src/fnschoo1/templates/base/_css.html +0 -0
  97. {fnschool-20251018.80430.821 → fnschool-20251019.81526.808}/src/fnschoo1/templates/base/_js.html +0 -0
  98. {fnschool-20251018.80430.821 → fnschool-20251019.81526.808}/src/fnschoo1/templates/base/content.html +0 -0
  99. {fnschool-20251018.80430.821 → fnschool-20251019.81526.808}/src/fnschoo1/templates/base/header_content_footer.html +0 -0
  100. {fnschool-20251018.80430.821 → fnschool-20251019.81526.808}/src/fnschoo1/templates/close.html +0 -0
  101. {fnschool-20251018.80430.821 → fnschool-20251019.81526.808}/src/fnschoo1/templates/home.html +0 -0
  102. {fnschool-20251018.80430.821 → fnschool-20251019.81526.808}/src/fnschoo1/templates/includes/_footer.html +0 -0
  103. {fnschool-20251018.80430.821 → fnschool-20251019.81526.808}/src/fnschoo1/templates/includes/_header.html +0 -0
  104. {fnschool-20251018.80430.821 → fnschool-20251019.81526.808}/src/fnschoo1/templates/includes/_navigation.html +0 -0
  105. {fnschool-20251018.80430.821 → fnschool-20251019.81526.808}/src/fnschoo1/templates/includes/_paginator.html +0 -0
  106. {fnschool-20251018.80430.821 → fnschool-20251019.81526.808}/src/fnschoo1/templates/registration/logged_out.html +0 -0
  107. {fnschool-20251018.80430.821 → fnschool-20251019.81526.808}/src/fnschoo1/templates/registration/login.html +0 -0
  108. {fnschool-20251018.80430.821 → fnschool-20251019.81526.808}/src/fnschool.egg-info/SOURCES.txt.py +0 -0
  109. {fnschool-20251018.80430.821 → fnschool-20251019.81526.808}/src/fnschool.egg-info/dependency_links.txt +0 -0
  110. {fnschool-20251018.80430.821 → fnschool-20251019.81526.808}/src/fnschool.egg-info/dependency_links.txt.py +0 -0
  111. {fnschool-20251018.80430.821 → fnschool-20251019.81526.808}/src/fnschool.egg-info/entry_points.txt +0 -0
  112. {fnschool-20251018.80430.821 → fnschool-20251019.81526.808}/src/fnschool.egg-info/entry_points.txt.py +0 -0
  113. {fnschool-20251018.80430.821 → fnschool-20251019.81526.808}/src/fnschool.egg-info/requires.txt +0 -0
  114. {fnschool-20251018.80430.821 → fnschool-20251019.81526.808}/src/fnschool.egg-info/requires.txt.py +0 -0
  115. {fnschool-20251018.80430.821 → fnschool-20251019.81526.808}/src/fnschool.egg-info/top_level.txt +0 -0
  116. {fnschool-20251018.80430.821 → fnschool-20251019.81526.808}/src/fnschool.egg-info/top_level.txt.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fnschool
3
- Version: 20251018.80430.821
3
+ Version: 20251019.81526.808
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>
@@ -6,4 +6,4 @@ import random
6
6
  import sys
7
7
  from pathlib import Path
8
8
 
9
- __version__ = "20251018.80430.821"
9
+ __version__ = "20251019.81526.808"
@@ -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,
@@ -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() {
@@ -0,0 +1,171 @@
1
+ {% extends "base/header_content_footer.html" %}
2
+ {% load tz %}
3
+ {% load crispy_forms_tags %}
4
+ {% block title %}
5
+ {% trans "Ingredient List" %}
6
+ {% endblock %}
7
+ {% block content %}
8
+ <h1>{% trans "Ingredient List" %}</h1>
9
+ <form method="get"
10
+ action="{% url 'canteen:list_ingredients' %}"
11
+ class="d-flex justify-content-between align-items-center">
12
+ <input type="text"
13
+ name="q"
14
+ onkeydown="if(event.keyCode==13){submit_q();return false;}"
15
+ value="{{ search_query }}"
16
+ placeholder="{% trans 'Search ingredient ...' %}"
17
+ class="form-control" />
18
+ <button type="button"
19
+ onclick="submit_q();"
20
+ class="btn btn-primary text-nowrap">{% trans "Search" %}</button>
21
+ {% if search_query %}
22
+ <a onclick="clear_q();" class="text-nowrap">{% trans "Clear" %}</a>
23
+ {% endif %}
24
+ </form>
25
+ <div class="d-flex justify-content-end pt-2">
26
+ <a class="btn btn-primary mx-1"
27
+ href="{% url "canteen:list_meal_types" %}">{% trans "Ingredient Meal Types" %}</a>
28
+ <a class="btn btn-primary mx-1"
29
+ href="{% url "canteen:list_categories" %}">{% trans "Ingredient Categories" %}</a>
30
+ <a class="btn btn-warning mx-1"
31
+ href="{% url "canteen:create_consumptions" %}">{% trans "Consume" %}</a>
32
+ <a class="btn btn-primary mx-1"
33
+ onclick="open_small_window('{% url 'canteen:create_ingredients' %}')">{% trans "Add via template" %}</a>
34
+ <a class="btn btn-primary"
35
+ onclick="open_small_window('{% url 'canteen:create_ingredient' %}')">{% trans "Add one" %}</a>
36
+ </div>
37
+ <hr />
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>
113
+ {% include 'includes/_paginator.html' %}
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
+
129
+ function clear_q() {
130
+ update_href({
131
+ "q": ""
132
+ })
133
+ }
134
+
135
+ function submit_q() {
136
+ const q_value = document.querySelector('input[name="q"]').value;
137
+ update_href({
138
+ q: q_value
139
+ });
140
+ }
141
+
142
+ function sort_ingredients(value) {
143
+ value = value.trim().split(/\s+/);
144
+ name = value[0];
145
+ value = value[1] || "";
146
+ const url = new URL(window.location.href);
147
+ const params = new URLSearchParams(url.search);
148
+ if (value == "-") {
149
+ value = "";
150
+ } else if (value == "") {
151
+ value = "+";
152
+ } else {
153
+ value = "-";
154
+ }
155
+ i_sort = {};
156
+ i_sort[name] = value;
157
+ update_href(i_sort);
158
+ }
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
+
171
+ {% endblock %}
@@ -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
 
@@ -2417,16 +2417,16 @@ class CanteenWorkBook:
2417
2417
 
2418
2418
  sheet.cell(
2419
2419
  month_surplus_header_row_num,
2420
- 4,
2420
+ 10,
2421
2421
  remaining_quantity_last_month,
2422
2422
  )
2423
2423
  sheet.cell(
2424
2424
  month_surplus_header_row_num,
2425
- 6,
2425
+ 12,
2426
2426
  remaining_total_price_last_month,
2427
2427
  )
2428
2428
  sheet.cell(
2429
- month_surplus_header_row_num, 5, unit_price.normalize()
2429
+ month_surplus_header_row_num, 11, unit_price.normalize()
2430
2430
  )
2431
2431
 
2432
2432
  month_ingredients = []
@@ -2680,7 +2680,7 @@ class CanteenWorkBook:
2680
2680
  self.fill_in_consumption_sheet()
2681
2681
  self.fill_in_consumption_list_sheet()
2682
2682
  self.fill_in_surplus_sheet()
2683
- if self.request.GET.get("include_food_sheets") == "true":
2683
+ if self.request.GET.get("include_food_sheets", "") == "true":
2684
2684
  self.fill_in_food_sheets()
2685
2685
 
2686
2686
  return self.wb
@@ -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
@@ -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.80430.821
3
+ Version: 20251019.81526.808
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>
@@ -27,6 +27,7 @@ src/fnschoo1/canteen/migrations/0013_alter_consumption_options_alter_ingredient_
27
27
  src/fnschoo1/canteen/migrations/0014_category_priority.py
28
28
  src/fnschoo1/canteen/migrations/0015_alter_category_options_alter_category_priority.py
29
29
  src/fnschoo1/canteen/migrations/0016_consumption_unique_ingredient_date_of_using.py
30
+ src/fnschoo1/canteen/migrations/0017_ingredient_updated_at_alter_category_created_at_and_more.py
30
31
  src/fnschoo1/canteen/migrations/__init__.py
31
32
  src/fnschoo1/canteen/templates/canteen/close.html
32
33
  src/fnschoo1/canteen/templates/canteen/category/create.html
@@ -1,139 +0,0 @@
1
- {% extends "base/header_content_footer.html" %}
2
- {% load crispy_forms_tags %}
3
- {% block title %}
4
- {% trans "Ingredient List" %}
5
- {% endblock %}
6
- {% block content %}
7
- <h1>{% trans "Ingredient List" %}</h1>
8
- <form method="get"
9
- action="{% url 'canteen:list_ingredients' %}"
10
- class="d-flex justify-content-between align-items-center">
11
- <input type="text"
12
- name="q"
13
- onkeydown="if(event.keyCode==13){submit_q();return false;}"
14
- value="{{ search_query }}"
15
- placeholder="{% trans 'Search ingredient ...' %}"
16
- class="form-control" />
17
- <button type="button"
18
- onclick="submit_q();"
19
- class="btn btn-primary text-nowrap">{% trans "Search" %}</button>
20
- {% if search_query %}
21
- <a onclick="clear_q();" class="text-nowrap">{% trans "Clear" %}</a>
22
- {% endif %}
23
- </form>
24
- <div class="d-flex justify-content-end pt-2">
25
- <a class="btn btn-primary mx-1"
26
- href="{% url "canteen:list_meal_types" %}">{% trans "Ingredient Meal Types" %}</a>
27
- <a class="btn btn-primary mx-1"
28
- href="{% url "canteen:list_categories" %}">{% trans "Ingredient Categories" %}</a>
29
- <a class="btn btn-warning mx-1"
30
- href="{% url "canteen:create_consumptions" %}">{% trans "Consume" %}</a>
31
- <a class="btn btn-primary mx-1"
32
- onclick="open_small_window('{% url 'canteen:create_ingredients' %}')">{% trans "Add via template" %}</a>
33
- <a class="btn btn-primary"
34
- onclick="open_small_window('{% url 'canteen:create_ingredient' %}')">{% trans "Add one" %}</a>
35
- </div>
36
- <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>
106
- {% include 'includes/_paginator.html' %}
107
- <script>
108
- function clear_q() {
109
- update_href({
110
- "q": ""
111
- })
112
- }
113
-
114
- function submit_q() {
115
- const q_value = document.querySelector('input[name="q"]').value;
116
- update_href({
117
- q: q_value
118
- });
119
- }
120
-
121
- function sort_ingredients(value) {
122
- value = value.trim().split(/\s+/);
123
- name = value[0];
124
- value = value[1] || "";
125
- const url = new URL(window.location.href);
126
- const params = new URLSearchParams(url.search);
127
- if (value == "-") {
128
- value = "";
129
- } else if (value == "") {
130
- value = "+";
131
- } else {
132
- value = "-";
133
- }
134
- i_sort = {};
135
- i_sort[name] = value;
136
- update_href(i_sort);
137
- }
138
- </script>
139
- {% endblock %}