1825 Commits

Author SHA1 Message Date
5888b96271 apply review comments 2025-06-27 13:28:44 +02:00
455e1d85d4 remove CanCreateMixin usage from election 2025-06-27 12:01:15 +02:00
6072142a28 refactor election result computing 2025-06-27 12:01:15 +02:00
f9c2794a25 refactor election detail view 2025-06-27 12:01:14 +02:00
b245d5bc1b feat: add x-limited-choices directive 2025-06-27 12:00:52 +02:00
fc773f375b refactor CandidatureForm 2025-06-27 12:00:51 +02:00
e3d0c3f369 move forms to their own file 2025-06-27 12:00:51 +02:00
eb4fbcbda4 Merge pull request #1140 from Juknum/feature/update-footer-on-mobile
Màj du footer sur mobile
2025-06-26 16:01:20 +02:00
570510f18d Merge pull request #1135 from ae-utbm/group
Small group tweak
2025-06-25 22:04:56 +02:00
7f371984d8 Merge pull request #1143 from ae-utbm/fix/mail-enumeration
fix: enumeration attack vector on login form
2025-06-25 17:53:53 +02:00
abf7bf6bfa rename location_admin to campus_admin 2025-06-25 17:13:24 +02:00
02ef8fdb88 fix: enumeration attack vector on login form 2025-06-25 17:03:53 +02:00
a7f4630d13 Merge pull request #1138 from ae-utbm/counter-admin
improve counter admin pages
2025-06-25 17:03:03 +02:00
c7087c6e7e Merge pull request #1137 from ae-utbm/fix-user-pictures
fix: user pictures ordering
2025-06-25 16:40:23 +02:00
f38926c4a3 fix: user pictures ordering 2025-06-25 16:25:51 +02:00
9a19f34ea2 Merge pull request #1141 from ae-utbm/fix-permanences
Fix permanences
2025-06-25 14:55:36 +02:00
67884017f8 fix old permanences having end replaced by activity 2025-06-25 01:22:13 +02:00
Sli
f474edc84f Style adjustment on the new footer 2025-06-24 17:04:52 +02:00
f5a8228358 Rework footer's UX on small devices 2025-06-22 20:01:22 +02:00
59a714af9f Merge pull request #1134 from ae-utbm/family
Add zoom controls to family graph
2025-06-21 15:20:47 +02:00
9049d8779c improve counter admin pages 2025-06-21 15:06:08 +02:00
Sli
d111023363 Apply review comments 2025-06-21 12:37:01 +02:00
cdfa76ad57 add missing "Respo site" group 2025-06-18 18:01:37 +02:00
88b70bf51f rename main groups to their real production version 2025-06-18 18:01:37 +02:00
Sli
ca593c7d81 Avoid click on graph when zooming 2025-06-18 16:24:53 +02:00
Sli
94bdc5e615 Remove useless closures 2025-06-18 14:13:06 +02:00
Sli
7d454749e0 Add style to zoom controls on family graph 2025-06-18 14:10:26 +02:00
06090e0cd9 Merge pull request #1133 from ae-utbm/api-fixes
fix: api title typo (again)
2025-06-18 12:25:31 +02:00
a1ae67da7d Merge pull request #1132 from ae-utbm/missing-perm
Missing SAS permission
2025-06-18 12:25:15 +02:00
Sli
10d5b9d63f Add zoom control of family graph 2025-06-18 12:22:30 +02:00
Sli
cc96c93d23 Convert family tree to typescript 2025-06-18 11:59:46 +02:00
8cc0b01e9c fix: api title typo (again) 2025-06-17 21:01:51 +02:00
88755358a6 fix: add missing sas permission 2025-06-17 21:00:38 +02:00
0e850e5486 Merge pull request #1131 from ae-utbm/api-fixes
Api fixes
2025-06-17 15:57:33 +02:00
af67c5fc27 Merge pull request #1130 from ae-utbm/navbar-keyboard-navigation
Fix click on navbar
2025-06-17 15:41:42 +02:00
Sli
30809a69c9 Move navbar script to dedicated file 2025-06-17 15:39:35 +02:00
0c442a8f03 fix: select only active club members on GET /club/{club_id} 2025-06-17 15:35:49 +02:00
f1b69dd47d fix: typo in API name 2025-06-17 15:35:49 +02:00
Sli
b5ebf09fcb Fix click on navbar 2025-06-17 15:31:51 +02:00
9d9ce5b30a Merge pull request #1129 from ae-utbm/fix-docs
fix: documentation CI/CD
2025-06-17 15:09:06 +02:00
a87460fa3e fix: documentation CI/CD 2025-06-17 14:45:51 +02:00
48fae33651 Merge pull request #1119 from ae-utbm/notifs
Improve notification on picture identification
2025-06-17 11:22:06 +02:00
6fec250658 display album name on picture identification notif 2025-06-16 18:36:08 +02:00
75b37cd6e3 fix album grouping on user pictures page 2025-06-16 18:36:08 +02:00
9c3820f986 Merge pull request #1127 from ae-utbm/deps
Update dependencies
2025-06-16 18:35:50 +02:00
28b60c7bae Merge pull request #1097 from ae-utbm/api-key
Basic api key management
2025-06-16 18:21:19 +02:00
efbbfcda76 update js deps 2025-06-16 15:51:11 +02:00
9e1fe7a296 update python deps 2025-06-16 15:51:04 +02:00
50d7b7e731 Move api urls to api app 2025-06-16 15:00:30 +02:00
ae7784a973 rename apikey to api 2025-06-16 14:54:42 +02:00
a23604383b doc: incompatibility between api keys and csrf 2025-06-16 13:44:43 +02:00
80866086a8 Forbid authentication with revoked keys 2025-06-16 13:44:43 +02:00
2c7eb99f31 use 54 bytes keys and sha512 hashing 2025-06-16 13:44:43 +02:00
189081f5a8 api key doc for developers 2025-06-16 13:44:43 +02:00
52e53da9ef adapt CanAccessLookup to api key auth 2025-06-16 13:44:43 +02:00
b5d65133f3 add doc for external API consumers 2025-06-16 13:44:43 +02:00
44e1902693 Add GET /api/club/{club_id} to fetch details about a club 2025-06-16 13:44:43 +02:00
1d55a5c2da Make HasPerm work with ApiKeyAuth 2025-06-16 13:44:43 +02:00
853aa34c18 adapt pedagogy api to api key auth 2025-06-16 13:44:43 +02:00
dc72789c14 feat: basic api key management 2025-06-16 13:44:41 +02:00
2f0454355f Merge pull request #1126 from ae-utbm/hey-api
Upgrade hey-api
2025-06-16 12:25:50 +02:00
1c14bb22a0 Merge pull request #1125 from ae-utbm/navbar-keyboard-navigation
Disable mouse click on navbar for desktop
2025-06-16 12:25:33 +02:00
d1f11216c7 Merge pull request #1124 from ae-utbm/tabs
Add tab widget and remove jquery-ui
2025-06-16 12:25:14 +02:00
Sli
2299e3f966 Upgrade hey-api 2025-06-16 11:20:41 +02:00
Sli
0f55bcc513 Disable mouse click on navbar for desktop 2025-06-16 09:17:40 +02:00
Sli
b19973ec9c Move ts files at the wrong place in com module 2025-06-16 09:05:19 +02:00
Sli
17129af1bb Remove unused popup system and jquery-ui 2025-06-16 09:05:19 +02:00
Sli
42434d10ca Remove jquery-ui tabs from counter 2025-06-16 09:05:19 +02:00
Sli
c904e41ea3 Replace tab macro with new tab web component 2025-06-16 09:05:19 +02:00
Sli
2dd4fd5c71 Initial tab concept 2025-06-16 09:05:18 +02:00
dad09deab7 Merge pull request #1123 from ae-utbm/fix-com-dates
fix: datetime format in main page news list
2025-06-15 20:20:16 +02:00
6782638a5d Merge pull request #1122 from ae-utbm/fix-election-css
Fix election css
2025-06-15 20:20:01 +02:00
c7e4de7df2 fix: datetime format in main page news list 2025-06-14 11:54:58 +02:00
dcc84894e5 fix: bad role title alignment in election.scss 2025-06-14 10:43:02 +02:00
9d841cd606 Merge pull request #1121 from ae-utbm/fix-sales
Fix counter selection performance on SellingForm
2025-06-13 13:58:38 +02:00
9f54e8362d Merge pull request #1117 from ae-utbm/lit-html
Add lit-html and use it for ics-calendar popups
2025-06-13 13:25:15 +02:00
c62c09f603 fix: counter selection queryset performance on SellingForm 2025-06-12 14:35:39 +02:00
9c8e3b7cac Merge pull request #1118 from ae-utbm/notifs
Notification improvements
2025-06-11 17:58:42 +02:00
c07f0c33cb fix permanent notification callback 2025-06-11 17:38:21 +02:00
7b778d3e6b Merge pull request #1114 from ae-utbm/accordions
Improve accordion animation
2025-06-11 14:00:46 +02:00
Sli
4c67bb1e2a Support animation with calc-size and detect browser features 2025-06-11 13:43:00 +02:00
Sli
96f91138dd Fix accordion transition on chrome 2025-06-11 00:20:46 +02:00
Sli
7b8102c242 Add lit-html and use it for ics-calendar popups 2025-06-10 23:08:04 +02:00
Sli
36d4a02a45 Remove js size animation and only use the opacity one 2025-06-10 15:06:59 +02:00
Sli
4774a7b741 Improve accordion animation 2025-06-05 20:38:32 +02:00
d58c713fc5 Merge pull request #1113 from ae-utbm/accordions
Fix bad css scoping on accordions
2025-06-05 20:37:23 +02:00
Sli
6f48a9a151 Fix bad css scoping on accordions 2025-06-05 19:57:25 +02:00
99be8a56f3 Merge pull request #1109 from ae-utbm/remove-laundry
Remove remaining laundry code
2025-06-05 18:28:16 +02:00
e04a99cabd Merge pull request #1111 from ae-utbm/calendar-cache
Disable calendar cache on API
2025-06-05 18:27:51 +02:00
Sli
bfea0989fb Disable cache on ics calendar on API response headers 2025-06-05 18:20:25 +02:00
be32486115 Merge pull request #1106 from ae-utbm/navbar-keyboard-navigation
Refactor navbar css and use details instead of div for better semantics
2025-06-05 18:17:58 +02:00
861447ae36 Merge pull request #1105 from ae-utbm/accordions
Remove jquery-ui accordions
2025-06-05 18:09:45 +02:00
Sli
5f701d1a17 Fix centering of detail elements 2025-06-05 18:04:45 +02:00
Sli
64fd123a85 Remove shadow and disable hovering on mobile view 2025-06-05 17:49:47 +02:00
Sli
7090254658 Refactor navbar css and use details instead of div for better semantics 2025-06-05 17:49:47 +02:00
d80f2e73e8 Merge pull request #1110 from ae-utbm/fix-old-promo
fix promo logo older than promo 10
2025-06-05 16:38:10 +02:00
ee3646594b fix promo logo older than promo 10 2025-06-05 16:31:36 +02:00
b0d9063153 remove remaining laundry code 2025-06-04 12:53:22 +02:00
Sli
0980fccf93 Add accordion animation with js 2025-06-03 20:48:45 +02:00
Sli
fb3fd9536e Improve accordion icon 2025-06-03 20:48:45 +02:00
Sli
3892e1cee2 Add fade animation 2025-06-03 20:48:45 +02:00
Sli
c10b488080 Remove jquery-ui accordions 2025-06-03 20:48:45 +02:00
ad91c8ed4f Merge pull request #1108 from ae-utbm/revert-python
Revert "bump python to 3.13"
2025-06-03 17:15:07 +02:00
3b90bd54fc Revert "bump python to 3.13"
This reverts commit f0fa27a8b5.
2025-06-03 10:37:04 +02:00
350a92bc44 Merge pull request #1102 from ae-utbm/update-deps
Update dependencies
2025-06-02 18:26:51 +02:00
95b4b0ba03 Merge pull request #1087 from ae-utbm/remove-laundry
remove laundry
2025-05-26 13:01:01 +02:00
bab44b31b1 Merge pull request #1100 from ae-utbm/filter-counter
Filter counters in ClubSellingView
2025-05-26 12:59:47 +02:00
f0fa27a8b5 bump python to 3.13 2025-05-26 12:56:35 +02:00
6d16e35624 update dependencies 2025-05-26 12:35:24 +02:00
13b892cd01 Merge pull request #1101 from ae-utbm/redirect-me
redirect `/user/me/*`
2025-05-26 10:30:07 +02:00
54be8addeb remove laundry 2025-05-26 09:10:35 +02:00
9256aff944 redirect /user/me/* 2025-05-26 08:48:18 +02:00
7c989cd749 filter selectable counters in ClubSellingView 2025-05-24 10:25:55 +02:00
2fc51e9901 Merge pull request #1099 from ae-utbm/hide-cash-register
hide cash register views to barmen
2025-05-22 11:20:52 +02:00
943fb5979d Merge pull request #1098 from ae-utbm/cached-groups
simplify `User.cached_groups`
2025-05-22 10:58:00 +02:00
ff1f1040b6 simplify User.cached_groups 2025-05-22 10:43:49 +02:00
fa90477de5 hide cash register views to barmen 2025-05-22 10:06:22 +02:00
af613c4cca Merge pull request #1094 from ae-utbm/tooltips
Create a js tooltip library
2025-05-17 07:01:15 +02:00
Sli
13f8b5db61 Fix bug where help button moves as if possessed 2025-05-14 15:04:20 +02:00
Sli
e63a09ee7e Synchronize tooltip-id and don't append tooltip to body twice 2025-05-14 15:02:48 +02:00
Sli
6bb6be011c Fix tooltip fading transitions and synchronize additional attributes 2025-05-14 15:02:46 +02:00
Sli
3a5bff8810 Fix tooltip not appearing/disapearing when attribute is removed 2025-05-14 15:01:19 +02:00
Sli
d1e5c93a08 Improve tooltips by using mutation observers 2025-05-14 15:01:19 +02:00
Sli
19aac8f302 Fix tooltip size 2025-05-14 15:01:19 +02:00
Sli
9bd3c618a4 Add doc, tooltip offset and css cleanup 2025-05-14 15:01:19 +02:00
Sli
68b1a96270 Allow popup customization 2025-05-14 15:01:18 +02:00
Sli
48bf72f623 Improve tooltips performance 2025-05-14 15:00:51 +02:00
Sli
1872e4abe5 Adapt calendar to new tooltip library 2025-05-14 15:00:51 +02:00
Sli
35e96fb875 Create basic tooltip library 2025-05-14 14:58:59 +02:00
5e953d04fe Merge pull request #1086 from ae-utbm/calendar_link_help
Calendar link help button
2025-05-14 13:14:22 +02:00
cab2adb45d fixed translations 2025-05-12 16:02:39 +02:00
2ed0fad51a remove unwanted focus behavior 2025-05-11 16:06:22 +02:00
39422a0cb8 added tooltip translations 2025-05-11 16:05:47 +02:00
33ba1e8bbb added button to layout 2025-05-11 16:05:21 +02:00
5ca1f03d9d ics help button 2025-05-11 15:23:11 +02:00
ac0c2c9880 Merge pull request #979 from ae-utbm/celery
Use celery for task queue
2025-05-07 11:53:39 +02:00
1a81911cd6 adapt celery to honcho setup 2025-05-07 11:49:38 +02:00
934d17d9d2 celery 2025-04-28 12:15:38 +02:00
f647feb8c8 use .env for project configuration 2025-04-28 12:15:38 +02:00
73e9c3132b Merge pull request #1093 from ae-utbm/calendar
Remove cache workaround on news calendar moderation
2025-04-25 12:23:57 +02:00
Sli
5da417c1a0 Remove cache workaround on news calendar moderation 2025-04-25 10:20:32 +02:00
df26ab4d50 Merge pull request #1085 from ae-utbm/eboutic
Don't use cookies for processing eboutic baskets
2025-04-23 15:56:44 +02:00
Sli
5dc9e24cd0 Apply review comments 2025-04-23 15:00:51 +02:00
Sli
7c9a966e54 Fix eboutic display bug 2025-04-23 14:04:08 +02:00
Sli
a31dc8254a Test pay with card 2025-04-23 00:53:39 +02:00
Sli
2a7c1a6438 Test pay with sith 2025-04-23 00:11:50 +02:00
Sli
8af6af1303 Test basket creation 2025-04-22 23:03:06 +02:00
4f5a69c353 Merge pull request #1091 from ae-utbm/docs-install
Update installation istructions
2025-04-21 13:09:24 +02:00
7296640a8d Merge pull request #1090 from ae-utbm/fix-album-upload
fix sas album creation rights
2025-04-21 01:01:36 +02:00
Sli
e556305062 Update dev install docs 2025-04-20 18:04:56 +02:00
e9c956e08c fix sas album creation rights 2025-04-19 17:05:15 +02:00
da56a7f651 Merge pull request #1089 from ae-utbm/login-register
Fix overflow issue on login/register page
2025-04-17 15:20:43 +02:00
Sli
0654dfb05d Fix overflow issue on login/register page 2025-04-17 13:02:40 +02:00
2a381101ac Merge pull request #1088 from ae-utbm/fix-anonymous-sas
fix 500 on SAS main page for anonymous users
2025-04-17 11:14:36 +02:00
ccd4275b02 fix 500 on SAS main page for anonymous users 2025-04-17 09:00:22 +02:00
Sli
f64409c612 Add more error messages on main view 2025-04-15 23:41:58 +02:00
Sli
f352b89fc0 Add auto basket invalidation 2025-04-15 23:33:57 +02:00
Sli
2ae9baa82f Fix etransaction_data endpoint 2025-04-15 18:50:25 +02:00
Sli
bc99390b25 Rename makecommand to checkout 2025-04-15 18:42:17 +02:00
Sli
262ed7eb4c Don't use cookies for processing eboutic baskets 2025-04-15 00:07:07 +02:00
4fa83d0667 Merge pull request #1083 from ae-utbm/promo-24-logo
new promo 24 logo
2025-04-14 12:17:40 +02:00
6b55b981ca new promo 24 logo 2025-04-14 11:02:41 +02:00
e6668728f2 Merge pull request #1082 from ae-utbm/eurok-consent
Add consent form for eurok partnership
2025-04-14 10:53:05 +02:00
Sli
3431fbf2d1 Add consent form for eurok partnership 2025-04-14 01:25:50 +02:00
77853b808a Merge pull request #1081 from ae-utbm/remove-accounting
remove remaining accounting code
2025-04-13 22:10:02 +02:00
05756520a3 remove remaining accounting code 2025-04-13 14:14:26 +02:00
b3eb6a945f Merge pull request #1076 from ae-utbm/eboutic-billing
Use htmx to fill up billing info
2025-04-13 09:52:49 +02:00
169faec479 Merge pull request #1080 from ae-utbm/fix-sas-order
Fix sas order
2025-04-13 01:06:31 +02:00
Sli
42317bfecc Use django messages for billing info ux 2025-04-13 00:20:57 +02:00
Sli
d6e858e0e3 Apply review comments 2025-04-12 22:59:25 +02:00
Sli
d8be9a62b5 Use assertRedirects for test_not_authorized 2025-04-12 22:46:23 +02:00
2658244671 fix album ordering on SAS 2025-04-12 18:09:39 +02:00
Sli
31575d0b64 Add tests 2025-04-11 17:12:23 +02:00
Sli
4ce885ac6b Default France value and cleaner handling of BillingInfo creation 2025-04-11 17:12:23 +02:00
Sli
5c2f324e13 Use htmx to fill up billing info 2025-04-11 17:12:22 +02:00
ed52a4f828 Merge pull request #1079 from ae-utbm/update-django
Update django
2025-04-11 17:11:58 +02:00
ff220e67c1 use new django.url.reverse query kwarg 2025-04-11 14:54:06 +02:00
805ffc498f response.content.decode() => response.text 2025-04-11 14:54:06 +02:00
ad4afce67f fix club members tests 2025-04-11 14:54:06 +02:00
f4276d6be5 fix account creation view tests 2025-04-11 14:54:06 +02:00
64085ac2a4 bump django to 5.2 2025-04-11 14:54:06 +02:00
f301365ebb update dependencies 2025-04-11 14:54:06 +02:00
53038a365f Merge pull request #1074 from ae-utbm/import-error
Fix cyclic import error on core/views/user.py
2025-04-10 11:57:42 +02:00
Sli
d2fe0f1fab Fix cyclic import error on core/views/user.py 2025-04-10 11:49:59 +02:00
e96d224a8d Merge pull request #1035 from ae-utbm/picture-upload
Picture upload from markdown editor
2025-04-10 11:41:54 +02:00
Sli
6128b6564c Ensure quickupload image field uniqueness 2025-04-10 11:38:33 +02:00
Sli
0f961c71e0 Auto delete image files when object has been deleted 2025-04-10 11:22:47 +02:00
59b275ef43 Merge pull request #1073 from ae-utbm/header-fix
Fix unaligned tool link
2025-04-10 11:17:24 +02:00
Sli
6362fcdf2d Fix unaligned tool link 2025-04-10 11:04:03 +02:00
Sli
3e61560875 Use group permissions 2025-04-10 01:01:40 +02:00
Sli
744223b76f Auto rescale quick upload image sizes 2025-04-09 22:50:51 +02:00
Sli
6e39b59dd5 Use UploadedImage to check image correctness and better error responses 2025-04-09 22:15:12 +02:00
Sli
67bc49fb21 Serve upload files directly from nginx 2025-04-09 20:55:24 +02:00
Sli
91b30e7550 Add quick upload tests 2025-04-09 20:55:24 +02:00
Sli
c236092c4f Create dedicated image upload model 2025-04-09 20:53:23 +02:00
Sli
7b23196071 Add image upload to easymde widget 2025-04-09 20:52:38 +02:00
Sli
10367d21ab Add API endpoint to upload images 2025-04-09 20:52:38 +02:00
60fd72917d Merge pull request #1053 from ae-utbm/sas-upload
Improve SAS upload
2025-04-09 19:32:55 +02:00
2c7b94547c improve upload error display 2025-04-09 12:33:40 +02:00
376af35bfb Check that uploaded images are actually images 2025-04-08 17:21:30 +02:00
13f417ba30 Use Alpine and the API for SAS picture upload 2025-04-08 15:59:30 +02:00
b83fbf91e1 extract album creation form into its own fragment 2025-04-08 15:59:30 +02:00
156305a16a add api endpoint to upload a sas picture 2025-04-08 15:59:30 +02:00
11efa4fca2 Merge pull request #1070 from ae-utbm/calendar-link
Remote calendar link for external sync
2025-04-08 15:50:38 +02:00
26456e3a7f Merge pull request #1071 from ae-utbm/news-overflow
Fix news x overflow on mobile
2025-04-08 15:35:17 +02:00
Sli
fab0d19eeb Fix news x overflow on mobile 2025-04-08 15:12:19 +02:00
Sli
8a381aed38 Smooth animation 2025-04-08 11:54:19 +02:00
Sli
5de05c0360 Introduce position attributes for tooltips 2025-04-08 10:27:08 +02:00
2e1a849aff modification du style du tooltip 2025-04-07 19:15:16 +02:00
Sli
b09d5e5ffd Remote calendar link for external sync 2025-04-07 13:58:08 +02:00
811c83552f Merge pull request #1051 from ae-utbm/fragment-mixin
Fragment mixins
2025-04-07 11:31:50 +02:00
b6511d5b84 Merge pull request #1069 from ae-utbm/upgrade-ical
Bump ical to 9.1.0 version
2025-04-06 23:34:55 +02:00
Sli
e52b2eadbe Bump ical to 9.1.0 version 2025-04-06 23:31:15 +02:00
86b8745665 Merge pull request #1068 from ae-utbm/dependabot/npm_and_yarn/vite-6.2.5
Bump vite from 6.2.3 to 6.2.5
2025-04-06 23:27:22 +02:00
597339749a Bump vite from 6.2.3 to 6.2.5
Bumps [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite) from 6.2.3 to 6.2.5.
- [Release notes](https://github.com/vitejs/vite/releases)
- [Changelog](https://github.com/vitejs/vite/blob/v6.2.5/packages/vite/CHANGELOG.md)
- [Commits](https://github.com/vitejs/vite/commits/v6.2.5/packages/vite)

---
updated-dependencies:
- dependency-name: vite
  dependency-version: 6.2.5
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-04-06 21:17:09 +00:00
d94d90357e Merge pull request #1067 from ae-utbm/taiste
bug fixing, external calendar removal and eurockéennes partnership
2025-04-06 23:16:04 +02:00
59e8272c7f Merge pull request #1064 from ae-utbm/makecommand
refactor eboutic command page
2025-04-06 22:36:56 +02:00
d98718f7ba fix makecommand jinja indentation 2025-04-06 22:29:43 +02:00
d03c425a17 refactor eboutic command page 2025-04-06 22:29:39 +02:00
e35c1d1928 move eboutic/makecommand.js to bundled directory 2025-04-06 22:29:26 +02:00
3b9c8d7b03 Merge pull request #1049 from ae-utbm/dependabot/npm_and_yarn/vite-6.2.3
Bump vite from 6.0.7 to 6.2.3
2025-04-06 21:36:52 +02:00
322cb74635 Merge pull request #1058 from ae-utbm/dependabot/npm_and_yarn/babel/runtime-7.27.0
Bump @babel/runtime from 7.26.0 to 7.27.0
2025-04-06 21:35:40 +02:00
62c394eec4 Merge pull request #1065 from ae-utbm/eurocks
Eurockéennes 2025
2025-04-06 21:29:01 +02:00
f254490790 Merge pull request #1063 from ae-utbm/fixes
Fixes
2025-04-06 21:28:45 +02:00
a78ccbd2cc Merge pull request #1066 from ae-utbm/remove-gcalendar
Remove external calendar
2025-04-06 17:21:33 +02:00
77537a84c2 remove external calendar 2025-04-06 17:14:39 +02:00
65c06dda8b partnership eurockéennes 2025 2025-04-06 17:01:00 +02:00
7623474124 add fragments documentation 2025-04-06 14:36:00 +02:00
6a5da0302d add FragmentMixin and UseFragmentsMixin classes 2025-04-06 14:34:22 +02:00
9e0cb7647b fix counter stats page access 2025-04-06 14:18:20 +02:00
fe5c685204 fix displayed user tabs 2025-04-06 14:17:12 +02:00
b0e24350e2 fix com admin pages 2025-04-06 14:17:12 +02:00
98e470fa2a Merge pull request #1060 from ae-utbm/master
Merge Back
2025-04-04 15:11:31 +02:00
49cca67eba Merge pull request #1062 from ae-utbm/fix-doc-ci
fix doc deployment (again)
2025-04-04 15:08:36 +02:00
a6e23b0b4c fix doc deployment (v2) 2025-04-04 15:05:08 +02:00
5c48924387 Merge pull request #1061 from ae-utbm/fix-doc-ci
fix doc deployment
2025-04-04 15:01:44 +02:00
e4264d400a fix doc deployment 2025-04-04 14:46:38 +02:00
b541e7c1fc Merge pull request #1059 from ae-utbm/fix-club-detail
Fix club detail
2025-04-04 14:37:56 +02:00
89efda6e26 fix club detail on ClubView 2025-04-04 14:27:07 +02:00
056b3a1702 split club/tests.py 2025-04-04 14:27:07 +02:00
df5838034e Bump @babel/runtime from 7.26.0 to 7.27.0
Bumps [@babel/runtime](https://github.com/babel/babel/tree/HEAD/packages/babel-runtime) from 7.26.0 to 7.27.0.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.27.0/packages/babel-runtime)

---
updated-dependencies:
- dependency-name: "@babel/runtime"
  dependency-version: 7.27.0
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-04-04 10:09:43 +00:00
29c1142537 Merge pull request #1057 from ae-utbm/taiste
Honcho, more product filters, sas improvements, club refactor, accounting removal, dcons and more
2025-04-04 12:08:36 +02:00
3d40e92958 Merge pull request #1056 from ae-utbm/fixes
Fixes
2025-04-04 11:25:18 +02:00
b8a40027b8 remove data migration in migration 0030 of counter 2025-04-04 11:00:31 +02:00
c527e87fd1 fix club edition page 2025-04-04 11:00:31 +02:00
8699750c72 Merge pull request #1045 from ae-utbm/accounting
Remove accounting appp
2025-04-04 10:38:28 +02:00
24f6a2b1cc remove unused translations 2025-04-04 10:35:47 +02:00
cdd32c9a82 remove accounting models 2025-04-04 10:35:17 +02:00
a6ac10e60c clean populate.py 2025-04-04 10:35:17 +02:00
26d4c4b811 move accound refound view to counter 2025-04-04 10:35:17 +02:00
002554b802 move CurrencyField to counter 2025-04-04 10:35:17 +02:00
6dfd4e16e2 remove accounting views 2025-04-04 10:27:51 +02:00
635bc79dd6 Merge pull request #1041 from ae-utbm/dcons
Returnable products management
2025-04-04 09:36:54 +02:00
eee78008b1 add pages to manage returnable products 2025-04-04 09:33:02 +02:00
e7bb08448c feat: generic returnable products 2025-04-04 09:29:54 +02:00
7515e739b6 Merge pull request #1037 from ae-utbm/unix-name
Refactor Club
2025-04-04 09:27:33 +02:00
c7d02d4c77 Merge pull request #1055 from ae-utbm/april
April fool
2025-04-01 15:45:59 +02:00
9a691b5b0a Revert "April fool day"
This reverts commit 6e0e633660.
2025-04-01 12:01:20 +02:00
6e0e633660 April fool day
Définitivement une des blagues de tous les temps
2025-04-01 12:01:17 +02:00
b9a8b46049 Merge pull request #1052 from ae-utbm/remove-galaxy
Remove galaxy from production
2025-03-28 19:41:44 +01:00
812e0f5f4c remove galaxy from production 2025-03-28 18:34:16 +01:00
3d3c6adfa5 test club edit view 2025-03-28 17:42:56 +01:00
b14b498eb1 fix generate_galaxy_test_data 2025-03-28 17:42:56 +01:00
fb4909fc36 merge ClubEditView and ClubEditPropView 2025-03-28 17:42:56 +01:00
805b146f17 change Club.unix_name to Club.slug_name and remove it from forms 2025-03-28 17:42:53 +01:00
f764ce1585 remove ClubStatView 2025-03-28 17:40:52 +01:00
15d541b596 Merge pull request #1050 from ae-utbm/merge_same_users
Check that a user cannot be merged into itself
2025-03-28 15:01:26 +01:00
df2d0d4d4c methode clean dans MergeForm
fixed formatting

Update rootplace/forms.py

Co-authored-by: thomas girod <56346771+imperosol@users.noreply.github.com>

Check that a user cannot be merged into itself

ajout des traductions

changed test language to french

Check that a user cannot be merged into itself
2025-03-28 14:44:55 +01:00
ac1e40038e Merge pull request #1048 from ae-utbm/sas-visibility
Make SAS pictures visible for their owner
2025-03-27 12:11:13 +01:00
146c14fc86 Bump vite from 6.0.7 to 6.2.3
Bumps [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite) from 6.0.7 to 6.2.3.
- [Release notes](https://github.com/vitejs/vite/releases)
- [Changelog](https://github.com/vitejs/vite/blob/v6.2.3/packages/vite/CHANGELOG.md)
- [Commits](https://github.com/vitejs/vite/commits/v6.2.3/packages/vite)

---
updated-dependencies:
- dependency-name: vite
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-03-25 15:20:25 +00:00
e1eb634c62 Make SAS pictures visible for their owner 2025-03-24 15:38:00 +01:00
bb3dfb7e8a Merge pull request #1044 from ae-utbm/fix-deprecations
Fix some deprecations
2025-03-14 12:37:11 +01:00
93d11bb439 refactor RefoundAccountView permission checking 2025-03-13 18:50:19 +01:00
99e1318071 increase max pagination size from 199 to 200 2025-03-13 18:50:16 +01:00
d16237d015 make PageCreateView a PermissionRequiredMixin 2025-03-13 18:49:42 +01:00
8a38ebb09d skip useless checks when creating a subscription for a new member 2025-03-13 18:49:42 +01:00
7f2ee24cb9 deps: add BeautifulSoup as test dependency 2025-03-13 18:49:42 +01:00
9ac8728d30 fix deprecated ninja_extra context import 2025-03-13 18:49:42 +01:00
4b0cd04355 add missing ordering 2025-03-13 18:49:42 +01:00
5c5755d4a6 make logout a POST operation 2025-03-13 18:49:42 +01:00
aaa8c4ba67 Merge pull request #1031 from ae-utbm/ts-album
ajaxify album loading
2025-03-12 18:09:11 +01:00
04c7df8ac8 fix typescript types 2025-03-12 17:37:37 +01:00
0f6cda377c Fix paginated TS interfaces 2025-03-12 17:35:22 +01:00
60db7e2516 ajaxify album loading in the SAS 2025-03-12 17:35:22 +01:00
218aab1af3 api to fetch albums 2025-03-12 17:35:22 +01:00
650227b6e2 typescriptify album-index.js 2025-03-12 17:35:22 +01:00
598ff3ffdf Merge pull request #1043 from ae-utbm/upgrade
Update dependencies
2025-03-11 15:17:34 +01:00
76fc55b125 Merge pull request #1039 from ae-utbm/openapi
Compile openapi client in background when django runserver is reloading
2025-03-10 20:14:32 +01:00
7c3186da79 apply ruff rule A005 2025-03-10 10:33:05 +01:00
Sli
106dc32a3d Fix schema.json being auto deleted and remove formating and linting of generated openapi client 2025-03-09 16:30:21 +01:00
Sli
05edf33062 Compile openapi client in background when django runserver is reloading 2025-03-09 15:55:37 +01:00
bba5339407 apply ruff rule DJ012 2025-03-09 15:05:10 +01:00
d10393ea37 update dependencies 2025-03-09 15:04:53 +01:00
98175e397c Merge pull request #1040 from ae-utbm/country_flags
made country flags apply to windows in chrome browsers
2025-03-05 20:24:41 +01:00
62246f342d removed unnecessary event listener 2025-03-05 20:19:11 +01:00
bff6513192 renamed polyfill-index.ts to country-flags-index.ts 2025-03-05 20:13:20 +01:00
bf0779a096 fix formatting 2025-03-05 20:04:20 +01:00
9991507297 made country flags apply to windows in chrome browsers 2025-03-05 19:59:28 +01:00
222ff762da Merge pull request #1022 from ae-utbm/poors-man-docker
Poor mans docker compose
2025-03-04 23:38:57 +01:00
Sli
6b27a97e7b Launch multiple honcho files depending on the context 2025-03-04 14:48:44 +01:00
87f790a044 Don't minify statics in debug mode 2025-03-04 12:00:00 +01:00
Sli
75c4c55a32 Only run full procfile on runserver 2025-03-04 11:59:35 +01:00
Sli
728ad157e9 Apply review comments 2025-03-04 10:30:36 +01:00
Sli
e542fe11b9 Add a pid file to avoid running honcho multiple times 2025-03-04 10:30:36 +01:00
Sli
aa66fc61ab Apply review comments 2025-03-04 10:30:36 +01:00
Sli
7f8304e407 Add redis to test pipeline 2025-03-04 10:30:36 +01:00
Sli
3b80b36ed6 Update doc 2025-03-04 10:30:36 +01:00
Sli
ba6e2a6402 Integrate automatic redis startup with the project 2025-03-04 10:30:36 +01:00
Sli
6841d96455 Enable honcho on non debug 2025-03-04 10:30:36 +01:00
Sli
8528820d89 Run bundler through honcho 2025-03-04 10:30:36 +01:00
2c9b72fe1d Merge pull request #1038 from ae-utbm/ninja-csrf
Enable csrf tokens on API routes
2025-03-03 13:39:47 +01:00
Sli
fe417b0c29 Enable csrf tokens on API routes
* Upgrade openapi-ts
* Migrate openapi-ts settings to new version
* Add csrf token to headers of all API calls
* Force csrf token authentication on API routes
2025-03-03 13:33:58 +01:00
b3f67657d7 Merge pull request #1036 from ae-utbm/fix-com-poster
fix com poster
2025-02-28 19:33:48 +01:00
602c57c001 fix com poster 2025-02-28 19:20:19 +01:00
6a17e4480e Merge pull request #1029 from ae-utbm/product-filter
add club and counter filters on product list page
2025-02-26 16:20:03 +01:00
2b99da5a37 Merge pull request #1034 from ae-utbm/taiste
Great news improvements, .env for configuration, full uv guide update command and more
2025-02-25 19:00:35 +01:00
1f1cd2ce0f Merge pull request #1027 from ae-utbm/calendar-moderation
Moderation of news through calendar and rename moderation to publish
2025-02-25 18:32:15 +01:00
Sli
a653f98fc1 Apply review comments 2025-02-25 18:28:16 +01:00
Sli
a01ea13f5b Fix crash when no news is available 2025-02-25 18:09:11 +01:00
Sli
10701ccdfa Synchronize calendar moderation and news list moderation 2025-02-25 18:09:11 +01:00
Sli
07028c8dd8 Harmonize news date display 2025-02-25 18:09:11 +01:00
Sli
4890fcf0e1 Rename news moderate to publish 2025-02-25 18:09:08 +01:00
Sli
2e71275f5b Connect calendar moderation with outside moderation 2025-02-25 15:35:01 +01:00
be87af5e06 Merge pull request #1033 from ae-utbm/fixed
Fix sales display
2025-02-25 14:41:14 +01:00
Sli
f9c36c8f99 Apply review comments 2025-02-25 14:38:58 +01:00
Sli
92d282f4ba Add possibility to de-moderate news through api and calendar widget 2025-02-25 14:38:58 +01:00
Sli
a1bf86dabf Add moderation through calendar widget 2025-02-25 14:37:18 +01:00
21284546c4 Merge pull request #1032 from ae-utbm/check_cashreg
is_check attribute refactor
2025-02-25 14:36:12 +01:00
e936f0d285 Merge pull request #1024 from ae-utbm/news-list
Allow displaying more news
2025-02-25 14:07:51 +01:00
6af03240a1 fix Selling.__str__ 2025-02-25 12:59:49 +01:00
01c92feb40 fix warning message display on subsequently loaded news 2025-02-25 11:53:02 +01:00
94d2c5660a move hybrid translation to full front translation 2025-02-25 11:10:05 +01:00
71b3588577 Add a "see more" button on news dates list 2025-02-25 08:56:45 +01:00
2def57d82c Close alerts related to a moderated event 2025-02-25 08:55:35 +01:00
0e88260c31 fix news dates timestamp in populate.py 2025-02-25 08:55:35 +01:00
86c2ea7fd9 API route to fetch news dates 2025-02-25 08:55:35 +01:00
fc3b82c35c Make upcoming nws scrollable on y-overflow 2025-02-25 08:55:35 +01:00
1d177412c3 change upcoming news selection on main page 2025-02-25 08:55:35 +01:00
c272cad2ea Merge pull request #1030 from ae-utbm/subscription-student-status
Give the student role when creating a new user subscription
2025-02-25 08:36:23 +01:00
e757fb43a1 replaced check with valid attribute is_check 2025-02-24 19:38:00 +01:00
8705fbe4b2 Merge pull request #1025 from ae-utbm/dl_pictures
download button for user pictures and albums
2025-02-24 07:39:00 +01:00
aa60462653 Merge pull request #1028 from ae-utbm/counters
Allow transactions on counter when an user has recorded too many products
2025-02-24 07:38:34 +01:00
9c0d89de83 Give the student role when creating a new user subscription 2025-02-24 07:13:19 +01:00
809febc353 add club and counter filters on product list page 2025-02-24 06:34:38 +01:00
Sli
f4ff247862 Remove call from removed loadCounter function 2025-02-23 18:05:37 +01:00
Sli
1978658b9c Allow transactions on counter when an user has recorded too many products as long as he doesn't record more 2025-02-21 14:50:07 +01:00
Sli
219700f0bc Add redirect for user picture url 2025-02-20 18:54:50 +01:00
Sli
2918048b16 Improve download user album button 2025-02-20 18:51:08 +01:00
Sli
a87016a23f Apply some review comments 2025-02-20 18:13:40 +01:00
Sli
f7ff77b88f Use real images with lazy loading in sas albums and user pictures 2025-02-19 00:12:30 +01:00
Sli
e8db68b960 Add missing translations 2025-02-18 20:10:54 +01:00
Sli
93a5c3a02a Separate album downloading logic from user display. Allow downloading individual user albums. 2025-02-18 20:10:54 +01:00
Sli
e46cba7a06 Move all user picture logic to sas 2025-02-18 20:10:51 +01:00
ba21738bd9 biome reformat 2025-02-18 14:56:08 +01:00
b1db52d2b6 clean typescript 2025-02-18 14:56:08 +01:00
2bed89aaba typescriptification de picture-index et bonne instantiation alpine-data 2025-02-18 14:56:08 +01:00
86c68eeb32 fix indenting 2025-02-18 14:56:08 +01:00
8cb53ceba2 download button for user pictures and albums 2025-02-18 14:56:08 +01:00
a96b374ad7 Merge pull request #971 from ae-utbm/environ
Use .env for project configuration
2025-02-17 13:37:01 +01:00
9945993f0b simplify .env.example
La plupart des variables du `.env.example` n'ont pas besoin d'être modifiées régulièrement et ont déjà des valeurs par défaut dans le `settings.py` qui sont adaptées à un environnement local.
En gardant uniquement les variables qui seront régulièrement modifiées, on rend le fichier plus compréhensible et plus simple à maintenir.
2025-02-17 11:33:15 +01:00
59e90ec754 add CSRF_TRUSTED_ORIGINS to settings 2025-02-16 12:47:46 +01:00
41bff53853 use .env for project configuration 2025-02-16 12:47:38 +01:00
88b3f7c322 Merge pull request #1009 from ae-utbm/news-list
News list improvements
2025-02-15 18:29:16 +01:00
b31445fefb Merge pull request #1010 from ae-utbm/populate-all-uvs
Management command to populate all uvs
2025-02-15 18:28:42 +01:00
2dc32f8b20 Merge pull request #1021 from ae-utbm/master
merge back
2025-02-15 18:13:19 +01:00
b43b531c3b Add a disclaimer when moderating weekly news 2025-02-15 14:06:01 +01:00
bf388e68f0 remove Alpine import in moderation-alert-index.ts 2025-02-15 14:04:57 +01:00
5252d450a9 remove alpine instructions for moderated news 2025-02-15 14:04:57 +01:00
8f17c3d830 Set the moderator when moderating news 2025-02-15 14:04:57 +01:00
6627ea417c News moderation buttons directly on the home page 2025-02-15 14:04:43 +01:00
92b2befd55 Improve news list display 2025-02-15 14:04:32 +01:00
43207455b8 API to moderate and delete news 2025-02-15 14:04:32 +01:00
5fa431e29b Visually differentiate closed UVs from the others 2025-02-15 13:51:51 +01:00
78f3caa455 management command to update the whole uv guide 2025-02-15 13:51:39 +01:00
6d519e3a07 Custom client for UTBM UV API calls 2025-02-15 13:51:39 +01:00
85c8b7d11c Use requests for external requests
L'API de requests est beaucoup plus claire que celle d'urllib et urllib3.
2025-02-15 13:51:39 +01:00
fa02f4b5f0 Merge pull request #1020 from ae-utbm/taiste
RSS feed, subscription creation permisssion, pedagogy permissions and bugfixes
2025-02-15 13:00:21 +01:00
3df33261ce Merge pull request #1017 from ae-utbm/subscription-perms
Subscription perms
2025-02-15 12:18:40 +01:00
ee1bcf2011 add forgotten input field label 2025-02-15 12:05:54 +01:00
571b3a4e02 fix perms in user_tools.jinja 2025-02-15 12:05:54 +01:00
6bf02cecd9 Allow some customisation in core/edit.jinja 2025-02-15 12:05:54 +01:00
05d4a09f8c Add a page to manage the groups that can create permissions 2025-02-15 12:05:54 +01:00
169e9ea55a Merge pull request #1019 from ae-utbm/calendar-fix
Fix wrong overflow on chrome for calendar
2025-02-14 13:20:04 +01:00
Sli
9b916f6204 Fix wrong overflow on chrome for calendar 2025-02-14 13:09:05 +01:00
2123e83010 fix user_tools.jinja indentation 2025-02-13 13:36:46 +01:00
294b59b4d6 use django auth for subscription creation page 2025-02-13 13:36:46 +01:00
820ceb48dd Merge pull request #1018 from ae-utbm/fix-upload-artifact
fix upload artifact step of CI
2025-02-13 13:35:29 +01:00
73ce681307 fix upload artifact step of CI 2025-02-13 13:30:34 +01:00
faa757b54f Merge pull request #1016 from ae-utbm/fix-groups
Fix user groups update view
2025-02-07 15:15:09 +01:00
36076aefcc fix user groups update view
Le formulaire remplaçait la totalité des groupes de l'utilisateur, c'est-à-dire également les groupes pas affichés dans le formulaire. Ça fait que la soumission du formulaire retirait l'utilisateur de tous ses groupes de groupes et des autres groupes non-gérables manuellement (comme Publique et Anciens Cotisants).

Jusqu'ici, les groupes non-manuels étaient gérés bizarrement, en regardant dynamiquement à chaque fois si l'utilisateur est dans le groupe, donc le bug ne se voyait pas. Maintenant que tous les groupes sont gérés presque de la même manière, ça se voit.
2025-02-07 13:28:47 +01:00
b9482a6f08 Merge pull request #1014 from ae-utbm/github
Update upload-artifacts to v4
2025-01-25 16:14:34 +01:00
Sli
d573182f4b Update upload-artifacts to v4 2025-01-23 15:34:12 +01:00
75be6454eb Merge pull request #1013 from ae-utbm/fix-balance-updat
fix `CustomerQuerySet.update_balance`
2025-01-23 15:19:45 +01:00
428fe68cdb fix CustomerQuerySet.update_amount 2025-01-23 14:55:10 +01:00
18967cf3d6 Merge pull request #1012 from ae-utbm/fix-counter-access
Fix office counter access
2025-01-23 14:37:32 +01:00
14ed43aaa5 fix office counter click access 2025-01-23 13:32:13 +01:00
c555d5c78c Merge pull request #1008 from ae-utbm/feed
Add atom/rss news feed
2025-01-21 00:38:35 +01:00
Sli
5db9819560 Address review comments 2025-01-21 00:28:35 +01:00
Sli
dd2cd0a18d Add atom/rss news feed 2025-01-19 18:22:02 +01:00
c7ae70972f Merge pull request #1007 from ae-utbm/calendar
Force ics cache invalidation on ics calendar
2025-01-17 18:09:41 +01:00
17cf0c67b3 Merge pull request #1006 from ae-utbm/pedagogy-perms
Improve pedagogy permissions
2025-01-17 18:08:31 +01:00
Sli
7d40387f43 Force ics cache invalidation on ics calendar 2025-01-17 17:45:21 +01:00
20a535429c pedagogy api permissions 2025-01-17 17:31:22 +01:00
5ff7bb3259 Add has_perm api permission 2025-01-17 17:31:08 +01:00
0d95c3b9c9 Improve pedagogy permissions 2025-01-17 09:42:16 +01:00
170f9dde61 Merge pull request #1005 from ae-utbm/taiste
More group rework, ajax input style, news creation form rework and counter fixes
2025-01-14 22:06:52 +01:00
61170c0918 Merge pull request #1000 from ae-utbm/imghdr
Remove call to deprecated `imghdr` module
2025-01-14 18:00:19 +01:00
80940765fe Merge pull request #1002 from ae-utbm/perms
Permissions refactor
2025-01-14 17:58:22 +01:00
9d98a20e40 Merge pull request #1004 from ae-utbm/js-upgrade
Upgrade js dependencies
2025-01-14 17:57:48 +01:00
Sli
6f9f1ac1e7 Upgrade js dependencies
* biomejs
 * hey-api
 * vite
 * threejs
 * other minor upgrades
2025-01-14 17:17:41 +01:00
71b096f9ef Apply review comment 2025-01-14 17:17:31 +01:00
9272f53bea fix doc display 2025-01-14 17:14:59 +01:00
9b5f08e13c Improve permission documentation 2025-01-13 18:20:29 +01:00
d0b1a49300 deprecate CanCreateMixin
Les motifs de cette déprécation sont indiqués dans la documentation.
Le mixin a été remplacé par `PermissionRequiredMixin` dans les endroits où ce remplacement était aisé.
2025-01-13 18:20:29 +01:00
e500cf92ee Remove SubscriberMixin 2025-01-13 15:57:01 +01:00
551091f650 add PermissionOrAuthorRequiredMixin 2025-01-13 15:45:58 +01:00
0c01ad1770 Move core auth mixins to their own file 2025-01-13 15:45:55 +01:00
cba915c34d Move core views mixins to their own file 2025-01-13 15:45:27 +01:00
7ac41ac5cb remove UserIsRootMixin 2025-01-13 15:45:23 +01:00
c6bb509fc3 remove call to deprecated imghdr module 2025-01-12 15:00:17 +01:00
4d0d7adce1 Merge pull request #998 from ae-utbm/simpler-com
Rework news creation form
2025-01-11 20:47:21 +01:00
6bcc420af1 Merge pull request #969 from ae-utbm/default-groups
Give default groups to users
2025-01-11 20:44:39 +01:00
9f35f5356b fix NewsQuerySet.viewable_by 2025-01-10 22:08:28 +01:00
8d73ec797b remove unwanted translation
Django ne traduit pas ses permissions. Si on traduit les nôtres, ça devient inconsistant
2025-01-10 22:08:28 +01:00
c3fc8538cc rework news form 2025-01-10 22:08:24 +01:00
600657b1a8 get_end_of_semester util function 2025-01-10 22:08:10 +01:00
d3f21c8f16 remove news event type 2025-01-10 22:08:10 +01:00
895d51586e put com forms in their own file 2025-01-10 22:08:10 +01:00
e200f28267 Merge pull request #1001 from ae-utbm/counter
Fix selling ordering bug that created "not enough money" errors
2025-01-10 16:39:44 +01:00
Sli
a4c6439981 Fix selling ordering bug that created "not enough money" errors
* Add tests
* Add tests for cons/dcons
2025-01-10 16:35:42 +01:00
6ee2e8c5da Merge pull request #996 from ae-utbm/elections
Remove shorten dependency and use clip instead
2025-01-10 15:41:31 +01:00
Sli
f4af29acb4 Fix missing translation 2025-01-10 15:34:46 +01:00
a8810816f0 Give the public group to newly created users 2025-01-10 02:23:07 +01:00
b7bf3fd375 Give the old_subscribers group when subscribing 2025-01-10 02:12:17 +01:00
b26e85ebb2 Merge pull request #999 from ae-utbm/fix-perms
fix ban page access
2025-01-10 01:41:19 +01:00
8b8a295e16 fix ban page access 2025-01-10 01:29:24 +01:00
894690a97f Merge pull request #997 from ae-utbm/counter
Fix inconsistent search behavior on counter click codes
2025-01-09 22:32:59 +01:00
843ce2e3a7 Merge pull request #990 from ae-utbm/jquery
Remove some jquery
2025-01-09 22:32:39 +01:00
Sli
9f33ddd883 Fix inconsistent search behavior on counter click codes 2025-01-09 01:04:11 +01:00
Sli
a2dc4f1964 Create a new better script for showing more/less 2025-01-08 14:51:14 +01:00
cca486f2b9 Merge pull request #995 from ae-utbm/elections
Fix election display on mobile and add missing signal for news deletion
2025-01-08 09:42:18 +01:00
b9e27ef191 Merge pull request #976 from ae-utbm/tom-select-style
make ajax select appearance consistant with other inputs
2025-01-08 09:40:31 +01:00
Sli
29e875bcde Fix election display on mobile and add missing signal for news deletion 2025-01-08 09:32:24 +01:00
686d67410a Merge pull request #994 from ae-utbm/taiste
UV as package manager and election style fix
2025-01-08 09:12:06 +01:00
4226ba88ae Merge pull request #993 from ae-utbm/elections
Quick fix for election display
2025-01-08 08:55:22 +01:00
Sli
672bc91e36 Quick fix for election display 2025-01-08 03:17:18 +01:00
bc9cb9b36c Merge pull request #992 from ae-utbm/uv
Fix install documentation
2025-01-06 22:30:32 +01:00
Sli
edafc06c3f Fix install documentation 2025-01-06 22:26:46 +01:00
134f8a7989 Merge pull request #991 from ae-utbm/uv
Switch from poetry to uv
2025-01-06 22:19:19 +01:00
Sli
771cbdbd77 More explicit uv install steps 2025-01-06 21:59:36 +01:00
Sli
a491baddb9 Apply review comments 2025-01-06 20:13:41 +01:00
Sli
8d10a5e0ab Update deploy scripts to uv 2025-01-06 16:17:56 +01:00
Sli
cbe42d3a60 Add caching for virtualenv 2025-01-06 16:17:56 +01:00
Sli
0c4d72e17a Switch from poetry to uv 2025-01-06 16:17:54 +01:00
Sli
2db3290bed Remove some jquery 2025-01-05 20:17:30 +01:00
429df81ec9 Merge pull request #989 from ae-utbm/counter
Make code matching rank first in counter click
2025-01-05 19:05:10 +01:00
Sli
bb24516474 Make code matching rank first in counter click 2025-01-05 18:54:54 +01:00
16de128fdb Merge pull request #987 from ae-utbm/taiste
Better group management, unified calendar and fixes
2025-01-05 17:52:36 +01:00
8e339c3d4b Merge pull request #988 from ae-utbm/news
fix: wrong link for ae dev discord
2025-01-05 17:29:57 +01:00
Sli
25298518bc fix: wrong link for ae dev discord 2025-01-05 17:25:23 +01:00
2e26ff2cde Merge pull request #986 from ae-utbm/news
Improve welcome page
2025-01-05 17:18:55 +01:00
Sli
a8702d4f5e Improve welcome page
* Improve code readability of calendar details
* Add link to AE Dev discord in useful links
* Add link to github at the bottom
2025-01-05 16:42:26 +01:00
7f4cc5fb0f Merge pull request #980 from ae-utbm/ban-groups
Ban groups
2025-01-05 15:54:19 +01:00
e7215be00e translations 2025-01-05 15:49:30 +01:00
4f35cc00bc Add UserBan management views 2025-01-05 15:49:08 +01:00
af47587116 Split groups and ban groups 2025-01-05 15:49:08 +01:00
3c4daeadb0 Merge pull request #985 from ae-utbm/form-fixes
small form fixes
2025-01-05 15:47:44 +01:00
348ab19ac6 small form fixes
le `display:block` avait disparu des helptext, ce qui rendait leur affichage bizarre. Et il manquait quelques détails sur le `ProductForm`
2025-01-05 15:40:41 +01:00
ada74a3e42 Merge pull request #984 from ae-utbm/lock-poetry
Pin poetry version
2025-01-05 15:02:45 +01:00
785ac9bdab pin poetry version 2025-01-05 14:48:40 +01:00
d1e604e7a5 Merge pull request #975 from ae-utbm/unified-calendar
Unified calendar widget on main com page with external and internal events
2025-01-05 01:46:38 +01:00
Sli
2749a88704 Basic test for internal calendar 2025-01-05 01:36:41 +01:00
Sli
eb3db134f8 Test external calendar caching 2025-01-05 01:32:54 +01:00
Sli
fa7f5d24b0 Test external calendar api 2025-01-05 01:04:11 +01:00
Sli
ba76015c71 Use a newer ical library 2025-01-04 23:12:34 +01:00
Sli
1887a2790f Move IcsCalendar to it's own file 2025-01-04 23:08:09 +01:00
Sli
5d0fc38107 Make social icons links pretty 2025-01-04 23:08:09 +01:00
Sli
65df55a635 Use signals to update internal ics 2025-01-04 23:08:09 +01:00
Sli
a60e1f1fdc Create dedicated class to manage ics calendar files 2025-01-04 23:08:09 +01:00
Sli
0a0f44607e Return calendars as real files 2025-01-04 23:08:09 +01:00
Sli
007080ee48 Extract send_file response creation logic to a dedicated function 2025-01-04 23:08:09 +01:00
Sli
a13e3e95b7 Harmonize titles on front page 2025-01-04 23:08:09 +01:00
Sli
169938e1da Replace old agenda of event with links to services and change permission to see birthdays 2025-01-04 23:08:09 +01:00
Sli
e5fb875968 Add support for event location and more detail link 2025-01-04 22:52:17 +01:00
Sli
9bd14f1b4e Refactor popup creation 2025-01-04 22:51:45 +01:00
Sli
fd2295119d nice looking popup with well aligned icon 2025-01-04 22:51:45 +01:00
Sli
eac2709e86 Create basic (ugly) event detail popup 2025-01-04 22:51:45 +01:00
Sli
48f6d134bf Fix news page layout 2025-01-04 22:51:45 +01:00
Sli
6d7467e746 Make new calendar look like the iframe one 2025-01-04 22:51:44 +01:00
Sli
0d1629495b Refactor com scss and add basic unified event calendar 2025-01-04 22:51:44 +01:00
Sli
63839dc22b Fix poster edition and display bug 2025-01-04 22:51:44 +01:00
c627944bd1 Merge pull request #983 from ae-utbm/gettext
Remove line numbers from locale files
2025-01-04 22:50:10 +01:00
f0be4b270b remove line numbers from locale files 2025-01-04 22:03:37 +01:00
728065e771 Merge pull request #982 from ae-utbm/groups
fix get_or_create in club group migration
2025-01-04 19:01:16 +01:00
849fac490d fix get_or_create in club group migration 2025-01-04 18:49:00 +01:00
5752229312 Merge pull request #981 from ae-utbm/groups
split migrations
2025-01-04 18:14:09 +01:00
6eb860579a split migrations 2025-01-04 18:05:02 +01:00
d08d54b4c9 Merge pull request #935 from ae-utbm/groups
Remove `RealGroup` and `MetaGroup`
2025-01-04 17:13:48 +01:00
bb210f8d47 change club group names when the club name changes 2025-01-04 16:43:38 +01:00
efca10e252 remove Club.view_groups, Club.edit_groups and Club.owner_group 2025-01-03 17:30:24 +01:00
b8f851b009 translations 2025-01-03 01:18:28 +01:00
1e29ae4171 fixes on club group attribution 2025-01-03 01:18:28 +01:00
0ae1e850f4 improve admin 2025-01-03 01:18:28 +01:00
d380668c0f Move users to the club groups in the migration 2025-01-03 01:18:28 +01:00
9a72c5eb72 fix galaxy tests 2025-01-03 01:18:28 +01:00
407cfbe02b update docs 2025-01-03 01:18:28 +01:00
6400b2c2c2 replace MetaGroups by proper group management 2025-01-03 01:18:28 +01:00
0d3fd954a3 make ajax select appearance consistant with other inputs 2024-12-29 18:16:52 +01:00
cce7ecbe73 Merge pull request #974 from ae-utbm/fix-page
fix 500 error when accessing history of non-existing page
2024-12-29 15:47:38 +01:00
d200c1e381 fix 500 error when accessing history of non-existing page 2024-12-28 13:25:42 +01:00
673c427485 Merge pull request #973 from ae-utbm/taiste
Better counter, product management improvement, better form style and custom auth backend
2024-12-27 22:42:52 +01:00
2f9e5bfee1 Merge pull request #965 from ae-utbm/form-style
rework form style
2024-12-27 22:24:09 +01:00
11702d3d7c Merge pull request #959 from ae-utbm/counter-click-step-4
Make counter click client side first
2024-12-27 22:06:35 +01:00
Sli
43f47e2087 Improve product card display on counter click 2024-12-27 01:59:54 +01:00
4b881903f0 Merge pull request #972 from ae-utbm/fix-product-fetch
Fix product fetch
2024-12-26 23:43:41 +01:00
761e37ade6 fix product fetch 2024-12-26 17:26:06 +01:00
10ed2f7404 Merge pull request #963 from ae-utbm/fix-group-edit
Fix error when submitting group form without any group checked
2024-12-26 17:02:02 +01:00
Sli
43768f1691 Refactor counter-click css 2024-12-26 11:52:30 +01:00
Sli
280d27343d Put error popup inside the basket 2024-12-25 20:44:52 +01:00
Sli
138e1662c7 Add popup css class and display basket error messages with it on counter click 2024-12-24 00:29:23 +01:00
Sli
c80fe094a2 Remove useless form elements in counters and improve alignment 2024-12-23 20:44:49 +01:00
Sli
139221dd22 Apply review comments 2024-12-23 15:15:24 +01:00
72c2981d66 rework form style 2024-12-23 15:11:15 +01:00
Sli
6f003ffa53 Add translations 2024-12-23 02:41:41 +01:00
Sli
7f6fd7dc47 Fix wrong tests/permissions 2024-12-23 02:37:41 +01:00
Sli
ccf5118c9d Add invalid form tests 2024-12-23 02:26:39 +01:00
Sli
022c19c020 Fix counter permissions issues 2024-12-23 02:17:28 +01:00
Sli
2e5e217842 Disable eboutic in counter click/main 2024-12-23 01:35:44 +01:00
Sli
9c93c004ec Add more counter click tests 2024-12-23 01:18:01 +01:00
Sli
472800eff6 Add nice snackbar message on counter interface and fix not enough money protection on frontend 2024-12-23 00:56:57 +01:00
Sli
b8d43a629b Increase selling label size and add more counter click tests 2024-12-23 00:00:40 +01:00
Sli
f6693e12cf Basic counter click tests 2024-12-22 19:24:07 +01:00
Sli
38f491cf57 Properly test annotations in counter click 2024-12-22 16:43:07 +01:00
Sli
3464d5d860 Add proper tests for refilling view 2024-12-22 16:16:28 +01:00
81773dc800 Merge pull request #964 from ae-utbm/fix-backend
Fix custom auth backend
2024-12-22 15:07:46 +01:00
da400155eb fix SithModelBackend._get_group_permissions 2024-12-22 15:01:58 +01:00
Sli
5079938a5b Fix get_operator on non bar counters and better display of counter with no products 2024-12-22 13:36:50 +01:00
Sli
b8430adc50 Split counter-click-index.ts 2024-12-22 13:01:37 +01:00
Sli
eed434aeb2 Improve age management for getting products and make get_product a part of counter model 2024-12-22 12:27:58 +01:00
Sli
372470b44b Improve empty basket and tray price management 2024-12-22 12:06:15 +01:00
Sli
7071553c3b Optimize product id validation on counter click 2024-12-22 12:06:15 +01:00
Sli
eea237b813 Pre-filter allowed products in backend for counter click 2024-12-22 12:06:15 +01:00
Sli
c37288c285 Display nice product cards on counter click interface 2024-12-22 12:06:15 +01:00
Sli
ccf5767a01 Fix customerBalance not init and submit/cancel buttons visuals 2024-12-22 12:06:15 +01:00
Sli
ffe6fc8c2a Redirect when cancelling instead of submitting a form 2024-12-22 12:06:15 +01:00
Sli
5f0b4d2050 Properly display form errors in counter 2024-12-22 12:06:15 +01:00
Sli
f9d7dc7d3a Restore form when form submit fails due to error 2024-12-22 12:06:15 +01:00
Sli
8ebea00896 Fix crash during validation 2024-12-22 12:06:15 +01:00
Sli
a548f4744e Fix counter main
* Fix crash when submitting nothing
* Fix code field not being autofocus
2024-12-22 12:06:15 +01:00
Sli
a383f3e717 Don't use codes as a primary key in counter click 2024-12-22 12:06:15 +01:00
Sli
60f18669c8 Make counter click client side first 2024-12-22 12:06:14 +01:00
Sli
a36946529b Fix error when submitting group form without any group checked 2024-12-22 12:04:51 +01:00
eaac0c728f Merge pull request #961 from ae-utbm/auth-backend
Custom auth backend
2024-12-22 06:38:34 +01:00
9ca95774a3 Merge pull request #962 from ae-utbm/query-news
Fix N+1 queries on birthdays
2024-12-22 06:32:58 +01:00
fa66851889 fix n+1 queries on birthdays 2024-12-21 21:09:08 +01:00
ab81f11199 Manage subscribers group permissions 2024-12-21 18:52:16 +01:00
bea7741d35 populate group permissions 2024-12-21 18:48:30 +01:00
81e163812e custom auth backend 2024-12-21 17:34:20 +01:00
4f233538e0 Merge pull request #955 from ae-utbm/counter-click-step-3
Use TomSelect for product selection on counter
2024-12-21 16:00:06 +01:00
Sli
4ac09ac08b Use tomselect instead of jquery autoselect for counter clicks 2024-12-21 15:56:18 +01:00
6d02970676 Merge pull request #946 from ae-utbm/product-csv
Rework the product admin page
2024-12-21 15:50:34 +01:00
b773a05bb5 Merge pull request #960 from ae-utbm/taiste
User model migration, better product types ordering and subscription page fix
2024-12-21 02:40:20 +01:00
accf1befce Make products filterable by product type 2024-12-21 02:15:51 +01:00
6953eaa9d0 fix sanitization of the csv content 2024-12-21 02:14:38 +01:00
180bae59c8 Add translations 2024-12-21 02:14:38 +01:00
9cafc163e8 fix frontend archived products filter 2024-12-21 02:14:38 +01:00
8f8eef4107 display products as cards 2024-12-21 02:14:38 +01:00
7af745087e create a card css component 2024-12-21 02:14:38 +01:00
aab093200b slightly improve style 2024-12-21 02:14:38 +01:00
1a9556f811 add a button to download products as csv 2024-12-21 02:14:38 +01:00
39b36aa509 ajaxify the product admin page 2024-12-21 02:14:38 +01:00
3fc260a12c add csv converter 2024-12-21 02:14:38 +01:00
1696a2f579 Add NestedKeyOf Type 2024-12-21 02:14:38 +01:00
baebc0b690 Merge pull request #958 from ae-utbm/fix-group-form
fix user groups form
2024-12-20 11:07:13 +01:00
9f3a10ca71 fix user groups form 2024-12-20 11:00:57 +01:00
38ceaf3106 Merge pull request #957 from ae-utbm/user-model
Fix groups displayed on user profile group edition
2024-12-19 20:32:39 +01:00
Sli
87b619794d Fix groups displayed on user profile group edition 2024-12-19 18:57:50 +01:00
29c4a36479 Merge pull request #956 from ae-utbm/query-page-hist
Fix N+1 queries on page history
2024-12-19 15:09:11 +01:00
ddeb12f08c Merge pull request #929 from ae-utbm/user-model
Migrate User parent class from AbstractBaseUser to AbstractUser
2024-12-19 14:27:16 +01:00
a7b1406e06 post-rebase fix 2024-12-19 10:53:11 +01:00
871ef60cf6 remove obsolete RunPython operations 2024-12-19 10:39:07 +01:00
7e9071a533 optimize User.is_subscribed and User.was_subscribed 2024-12-19 10:39:07 +01:00
8c660e9856 Make core.User inherit from AbstractUser instead of AbstractBaseUser 2024-12-19 10:39:04 +01:00
6ca641ab7f fix: N+1 queries on page version list page 2024-12-19 10:32:02 +01:00
8d6609566f Merge pull request #951 from ae-utbm/refactor-news
refactor news model and creation form
2024-12-18 16:09:41 +01:00
17e4c63737 refactor news model and creation form 2024-12-18 15:54:10 +01:00
fad470b670 Merge pull request #952 from ae-utbm/sort-producttypes
Sort product types
2024-12-18 15:45:50 +01:00
c5646b1e59 Merge pull request #954 from ae-utbm/fix-subscription
fix access to the subscription page
2024-12-18 15:45:17 +01:00
5da27bb266 rename producttype to product_type 2024-12-18 14:48:59 +01:00
be6a077c8e fix access to the subscription page 2024-12-18 14:13:39 +01:00
8d643fc6b4 Apply review comments 2024-12-17 17:23:13 +01:00
47876e3971 Make product types dynamically orderable. 2024-12-17 13:35:29 +01:00
c79c251ba7 Add ProductTypeController 2024-12-17 13:35:29 +01:00
483670e798 Make ProductType an OrderedModel 2024-12-17 13:35:29 +01:00
6c8a6008d5 api route to search products with detailed infos. 2024-12-17 12:38:59 +01:00
e680124d7b fix makemessages command in docs 2024-12-17 12:38:59 +01:00
b06a06f50c feat: add restore on backspace plugin for tom select 2024-12-17 12:38:59 +01:00
c1be55a719 Merge pull request #953 from ae-utbm/taiste
Counter views split, unique student cards, student cards and reloads HTMXification, refactors and fixes
2024-12-17 11:43:32 +01:00
6416de237f Merge pull request #923 from ae-utbm/counter-click-step-2
Casser counter click step 2 : separate refilling from counter clicks with fragments
2024-12-17 10:58:34 +01:00
Sli
ad44fd52a4 Apply review comments 2024-12-17 10:54:41 +01:00
Sli
03c27b10e5 Fix refill permissions
* Remove ability to refill from counters
* Fix bug where you could refill without any board member on a BAR
* Add a warning message explaining why refilling are disabled
2024-12-17 02:42:07 +01:00
Sli
fc0ef29738 Remove GetCustomer API endpoint 2024-12-17 01:42:10 +01:00
Sli
a0eb53a607 Apply review comments 2024-12-17 01:41:45 +01:00
Sli
66e5ef64fd Don't use API to update amount after a refilling query 2024-12-17 00:47:43 +01:00
f5d5cc18a8 Merge pull request #949 from ae-utbm/trombi
Fix crash when admin gets to preferences of an user subscribed to a trombinoscope
2024-12-16 10:06:17 +01:00
Sli
4c65939bbe Fix crash when admin gets to preferences of an user subscribed to a trombinoscope 2024-12-16 09:31:43 +01:00
Sli
379527cd58 Add a nice animation on successful refilling 2024-12-16 00:58:23 +01:00
Sli
f63fb59cbf Allow filtering of refilling options
* Move settings.SITH_COUNTER_PAYMENT_METHOD to counter.apps.PAYMENT_METHOD
* Move student cards to an accordion on counter click
* Make cash default refilling option
* Disable bank selection option in refilling if CHECK are not allowed
* Disable refilling with CHECK from the frontend
2024-12-16 00:15:21 +01:00
Sli
cde864fdc7 Apply review comments 2024-12-15 22:47:59 +01:00
Sli
e9361697f7 Convert customer refill to a fragment view 2024-12-15 21:33:19 +01:00
830c752971 Merge pull request #948 from ae-utbm/sentry
Enable sentry workflow again
2024-12-15 18:36:46 +01:00
Sli
6bdc1b73ae Enable sentry workflow again 2024-12-15 17:31:41 +01:00
0f003870bb Merge pull request #924 from ae-utbm/unique-student-card
Make student card unique per user
2024-12-15 17:06:35 +01:00
Sli
0631c77a1c Apply review comments 2024-12-15 17:02:44 +01:00
Sli
2cc4308a58 Fix tooltip shadow and position and improve unittests 2024-12-15 16:49:24 +01:00
Sli
4975475e85 Add tooltip on current registered card, allow barmen to delete cards and make card deletion a fragment 2024-12-15 16:49:24 +01:00
466fe58763 feat: make student card unique per user 2024-12-15 16:49:24 +01:00
3b7e338808 fix 500 when accessing preferences
Quand on tente d'accéder aux préférences d'un utilisateur relié à un trombi, sans être soi-même dans un trombi, on a une erreur.
2024-12-15 16:49:24 +01:00
53b13e7aef Merge pull request #947 from ae-utbm/dependencies
Upgrade dependencies
2024-12-15 13:53:28 +01:00
Sli
fa60ecb25a Upgrade dependencies 2024-12-15 00:59:55 +01:00
a975824481 Merge pull request #945 from ae-utbm/refactor-product
Remove `Product.parent_product`
2024-12-09 20:20:11 +01:00
c51e5eb6cb remove parent_product column in the Product table 2024-12-09 12:59:33 +01:00
f0bc502ec9 fix translation in subscription creation success fragment 2024-12-09 12:31:58 +01:00
902cafc5e4 Merge pull request #921 from ae-utbm/counter-click
Casser counter click étape 1 : introduire des fragments
2024-12-08 13:49:08 +01:00
b2f54aa23e Merge pull request #943 from ae-utbm/update-deps
Update deps
2024-12-08 13:46:53 +01:00
Sli
29a5425259 Add spinner to student card form 2024-12-08 13:17:56 +01:00
e2a34c75ea deps: update dependencies 2024-12-08 11:54:58 +01:00
Sli
de7aa6f6a6 Create a generic form fragment renderer 2024-12-08 11:45:16 +01:00
9acb421b2e deps: update ruff 2024-12-08 11:17:27 +01:00
Sli
66d2dc74e7 Pre-fetch forms for student card 2024-12-08 00:32:28 +01:00
Sli
2f613607af Update number of queries in test_num_queries 2024-12-07 23:35:35 +01:00
Sli
d4b9c3afb1 Make StudentCardFormView fragment only 2024-12-07 22:36:15 +01:00
Sli
b81cf49d0a Remove student card creation from CounterClick view and use fragment instead
Intercept htmx on submit requests, this allows auto submit from nfc fields

Fix super call with parameters

Add loading wheel on student card form for counter_click.jinja
2024-12-07 12:57:10 +01:00
1da45fdffc Merge pull request #934 from ae-utbm/split-counter
Split counter views into multiple files
2024-12-07 11:53:14 +01:00
10dde3f002 fix imports 2024-12-07 00:18:17 +01:00
c2d6af12ab Merge branches 'split-home' and 'split-studentcard' into split-counter 2024-12-07 00:13:50 +01:00
6e48f88c06 extract counter auth views 2024-12-07 00:12:10 +01:00
7a91a71565 extract counter auth views 2024-12-07 00:11:18 +01:00
c4764110d8 extract counter home views 2024-12-07 00:10:46 +01:00
ff68e65250 extract counter home views 2024-12-07 00:07:37 +01:00
c9d83e5916 extract student card views 2024-12-07 00:06:33 +01:00
5dc99dbfcb extract student card views 2024-12-07 00:05:45 +01:00
8dbec85c8e Merge pull request #941 from ae-utbm/optimize-search
Optimize search
2024-12-06 21:00:06 +01:00
84d7e40e66 feat: client-side cache for ajax-select inputs 2024-12-06 18:38:30 +01:00
0b509f2200 fix N+1 queries on user search 2024-12-06 18:38:30 +01:00
9591162cc9 Merge pull request #940 from ae-utbm/fix-dump
Fix the account dump command.
2024-12-05 19:52:07 +01:00
007e17fd8b Fix the account dump command.
- a missing `fail_silently` flag made the whole command fail if an invalid recipient is used (like closed utbm mail address)
- Not specifying the seller make the account detail pages crash.
2024-12-05 12:50:40 +01:00
35c5f96672 Merge pull request #939 from ae-utbm/taiste
`dump_account`, HTMX, Subscriptions and more
2024-12-04 00:10:19 +01:00
95f8e7517c Merge pull request #932 from ae-utbm/fix-subscriptions
Rework the subscription page
2024-12-03 19:45:26 +01:00
9667c79162 remove htmx-ext-response-targets 2024-12-03 19:41:10 +01:00
1c79c25262 better tab style 2024-12-03 19:41:09 +01:00
04b4b34bfe add back user profiles on subscription form 2024-12-03 19:41:09 +01:00
fc0e689d4e add initial values to forms 2024-12-03 19:41:09 +01:00
83bb4b3b12 add translation 2024-12-03 19:41:09 +01:00
8dcfc604a0 write tests 2024-12-03 19:41:09 +01:00
d2d639e5f6 Split SubscriptionForm into SubscriptionNewUserForm and SubscriptionExistingUserForm 2024-12-03 19:41:09 +01:00
b3eb7693e3 Merge pull request #933 from ae-utbm/remove-stock
delete stock application
2024-11-28 23:20:35 +01:00
10f42b1522 fix imports 2024-11-27 19:03:34 +01:00
76e9f3b1dc Merge branches 'split-cash', 'split-click', 'split-main', 'split-admin', 'split-mixins', 'split-eticket' and 'split-invoices' into split-clean 2024-11-27 18:49:40 +01:00
d0ff9bc16c extract mixins views 2024-11-27 18:48:06 +01:00
5e4ebd16f9 extract mixins views 2024-11-27 18:47:55 +01:00
d2b19424ff extract eticket views 2024-11-27 18:47:18 +01:00
08286254cd extract eticket views 2024-11-27 18:47:03 +01:00
4805c39b45 extract cash views 2024-11-27 18:46:24 +01:00
f845bbf20a extract cash views 2024-11-27 18:45:27 +01:00
71c7158124 extract invoice views 2024-11-27 18:43:26 +01:00
c4643ee52c extract invoice views 2024-11-27 18:42:50 +01:00
b46b0882f3 extract admin views 2024-11-27 18:42:26 +01:00
1c4efc9431 extract admin views 2024-11-27 18:41:47 +01:00
4133e0ccdd extract click views 2024-11-27 18:41:12 +01:00
de415e7e75 split click views 2024-11-27 18:40:38 +01:00
9d17524f45 extract main views 2024-11-27 18:00:48 +01:00
68ad9650af extract main views 2024-11-27 17:56:44 +01:00
8d4d8a3abc create views package 2024-11-27 17:07:08 +01:00
9617e29ed5 delete stock application 2024-11-26 17:35:10 +01:00
75406f7b58 Tabs jinja component 2024-11-26 16:17:44 +01:00
70f5ae4f9c Move subscription forms to subscription/forms.py 2024-11-26 16:17:44 +01:00
ff307f1d65 Merge pull request #928 from ae-utbm/vite
Integrate vite manifests in django
2024-11-22 18:34:49 +01:00
d7ae601c52 Merge pull request #911 from ae-utbm/skia/fix_user_profile_picture
core: fix user profile picture size
2024-11-21 19:13:36 +01:00
33b9ff78bb Merge pull request #913 from ae-utbm/dump-accounts
Dump accounts
2024-11-21 18:39:49 +01:00
Sli
0739ce2fb4 Improve readability and usability 2024-11-21 00:33:40 +01:00
Sli
8fc1a754de Integrates vite manifests to django 2024-11-20 18:24:28 +01:00
Sli
ca8c1c9d92 Mirror -index.css generation with their import location in -index.js/ts files 2024-11-19 21:22:14 +01:00
Sli
0485ab1120 Remove defer from script where type=module is used 2024-11-19 21:22:14 +01:00
Sli
8a8851847c Passage de webpack à vite.dev 2024-11-19 21:22:14 +01:00
Sli
7b41051d0d Go for a more generic js bundling architecture
* Don't tie the output name to webpack itself
* Don't call js bundling webpack in python code
* Make the doc more generic about js bundling
2024-11-19 21:22:14 +01:00
3db1f592e2 Merge pull request #927 from ae-utbm/password-and-username
Improve password and username generation
2024-11-19 17:39:54 +01:00
6853ec0b69 make random password generation safe 2024-11-19 13:21:08 +01:00
3b39049c20 Make User.generate_username less stupid 2024-11-19 13:07:59 +01:00
37d1669a72 typo in docstrings
Co-authored-by: NaNoMelo <56289688+NaNoMelo@users.noreply.github.com>
2024-11-19 00:48:35 +01:00
ee9f36d883 implement the dump_accounts command 2024-11-19 00:48:35 +01:00
e712f9fdb8 improve counter dump admin 2024-11-19 00:43:17 +01:00
Sli
9991f5dc64 Create nice animation when scanning nfc cards 2024-11-15 14:51:45 +01:00
Sli
fce6c3d29c Convert nfc input to a web component 2024-11-15 14:51:45 +01:00
346439076e Merge pull request #922 from ae-utbm/ci
Fix CI
2024-11-15 14:46:05 +01:00
Sli
5e8d8b8d5d Revert back curl install of poetry in pipelines 2024-11-15 14:41:25 +01:00
db9f86c41e Merge pull request #919 from ae-utbm/ts-eboutic
Migrate eboutic to Typescript
2024-11-14 11:07:37 +01:00
c7adde62eb reset poetry cache in github CI 2024-11-13 23:50:43 +01:00
34559dda08 migrate eboutic to typescript 2024-11-13 23:26:05 +01:00
37c4621e9e Merge pull request #912 from ae-utbm/refactor_populate
Refactor populate
2024-11-13 15:43:18 +01:00
dd7ed290f5 Merge pull request #883 from ae-utbm/htmx
Introduce htmx in sith files
2024-11-13 15:35:24 +01:00
Sli
dc1e1fc897 Fix typos 2024-11-12 21:38:38 +01:00
0a5ddcea68 Merge pull request #918 from ae-utbm/taiste
Ajax search input enhancement, promo 25 logo and small improvements
2024-11-12 13:20:53 +01:00
Sli
37abde04d7 Improve fragment doc 2024-11-11 13:56:34 +01:00
Sli
40f2f7033e Add test for AllowFragment mixin 2024-11-11 13:49:38 +01:00
Sli
aebf909dc6 Apply review comments 2024-11-11 13:49:38 +01:00
Sli
ec7d45fd91 Add documentation for htmx 2024-11-11 13:49:38 +01:00
Sli
3af5d96bf5 Introduce htmx in sith files
* Convert FileModerationView into ListView and add pagination with htmx
* Don't allow sas moderation in file moderation view
* Split up base.jinja and introduce base_fragment.jinja
* Improve FileModerationView performances and make it root only
* Add permissions tests for file modération
2024-11-11 13:49:38 +01:00
c7a8a1a91c refactor CI 2024-11-11 13:28:44 +01:00
2dd434d987 Merge pull request #917 from ae-utbm/doc
Fix some doc typos
2024-11-11 12:47:51 +01:00
Sli
5e954bae6a Fix some doc typos 2024-11-11 00:32:04 +01:00
a97dba18c2 Reduce width of non-multiple ajax selects 2024-11-11 00:26:16 +01:00
26770de40e Make selected option more visible 2024-11-11 00:26:16 +01:00
583d4ddfb8 Use less requests in GetUserForm.clean 2024-11-11 00:26:16 +01:00
486047b929 remove the honeypot from the login page
Des utilisateurs humains se font régulièrement "éclairer" par le honeypot. Les mesures anti-bot ne devraient pas bloquer des humains.
2024-11-11 00:22:07 +01:00
b65ec6463b fix picture display in profile page 2024-11-10 16:18:56 +01:00
7cc13ea669 Merge pull request #899 from ae-utbm/ajax-select
Improve ajax select
2024-11-10 13:37:57 +01:00
c2efc969d0 refactor populate.py 2024-11-10 02:59:43 +01:00
b091fee035 custom queryset method to bulk update customer balance 2024-11-10 02:59:43 +01:00
2a0f2454f4 core: fix user profile picture size
Since 28f397574f and the removal of the
`flex-basis: 50px` property from `user_profile_pictures_thumbnails`,
the main picture was always displayed small-ish, at least on Firefox.
Setting back a flex-basis helps getting more consistent behavior once
again.
2024-11-07 15:51:43 +01:00
97ea1763f1 Merge pull request #910 from ae-utbm/logo-25
Add promo 25 logo
2024-11-07 15:25:06 +01:00
b9f51596e9 Add promo 25 logo 2024-11-07 13:39:24 +01:00
Sli
0610794dbe Fix ajax-select visual 2024-10-28 18:18:56 +01:00
Sli
a6b32fcad1 Fix readability and avoid instantiating too many TypeAdapter 2024-10-28 18:08:13 +01:00
Sli
e583e78a4e Convert the whole request to json at once on select widget 2024-10-21 17:11:07 +02:00
Sli
3eb3feea49 Fix deprecated usage of schema json method and avoid multiple inheritance on select widgets 2024-10-21 16:14:00 +02:00
Sli
935914428b Remove ajax_select completely 2024-10-21 13:30:12 +02:00
Sli
ab63ba1c54 Remove ajax_select from accounting 2024-10-21 13:26:11 +02:00
Sli
afdc6b69df Remove ajax_select from sas 2024-10-21 10:30:35 +02:00
Sli
8b419dcee6 Remove ajax_select from core 2024-10-20 23:25:56 +02:00
Sli
e7181257e3 Remove ajax_select from core/views/forms.py 2024-10-20 23:04:54 +02:00
Sli
8e7c09332f Remove ajax_select from core/views/group.py 2024-10-20 22:58:39 +02:00
Sli
d9ea5e5538 Remove ajax_select from trombi 2024-10-20 22:41:35 +02:00
Sli
a21460a1b8 Remove ajax_select from subscriptions 2024-10-20 22:36:55 +02:00
Sli
b6a480ff61 Remove ajax_select from forum 2024-10-20 22:29:07 +02:00
Sli
84ee6dd2f5 Remove ajax_select from clubs 2024-10-20 21:28:25 +02:00
Sli
a950585a02 Remove ajax_select from rootplace 2024-10-20 20:55:07 +02:00
Sli
7f8a2c1eaf Remove ajax_select from counters 2024-10-20 20:55:05 +02:00
Sli
125157fdf4 Move gettext to the top 2024-10-20 18:35:55 +02:00
Sli
517263dd58 Automatically move inner html in created node when inheriting from HTMLElement 2024-10-20 18:29:48 +02:00
Sli
301fc73687 Fix markdown input initial value and crash when alpine is not loaded 2024-10-20 18:13:48 +02:00
Sli
45441c351d Improve ajax-select style 2024-10-20 17:37:51 +02:00
Sli
be5ce414ba Add proper delete button and fix item ordering 2024-10-20 16:57:38 +02:00
Sli
bb3f277ba5 Extract js and css from select widgets to editable class attributes 2024-10-20 13:40:59 +02:00
23049a8ae2 Merge pull request #901 from ae-utbm/improve-warning-dump
Improve warning dump
2024-10-20 13:35:08 +02:00
Sli
8bbebfdb13 Add AutoCompleteSelectGroup 2024-10-20 13:33:44 +02:00
662b4b5c53 precise that dumped users can still subscribe 2024-10-20 12:45:37 +02:00
9675b6372c add flags to the dump warning mail command 2024-10-20 12:32:28 +02:00
03afd49115 make the mail text only 2024-10-20 12:32:28 +02:00
Sli
0af3505c2a Make a generic AjaxSelect abstract class 2024-10-20 02:26:32 +02:00
Sli
f78b968075 Move markdown input and select widgets to a widget folder 2024-10-20 01:05:34 +02:00
7d40316044 Merge pull request #900 from ae-utbm/optimize-again
optimize product pages again
2024-10-20 00:55:27 +02:00
Sli
e3dcad62cc Migrates lookups
* products
* files
* Groups
* Clubs
* Accounting
2024-10-20 00:47:31 +02:00
db6a871854 optimize product pages again 2024-10-20 00:27:25 +02:00
Sli
ce4f57bd8f Add ajax user widget and remove ajax_select from elections 2024-10-19 22:06:34 +02:00
Sli
8be8328830 Create select widget based on tomselect on django backend
Replace make_ajax in elections by the new widget
2024-10-19 21:32:58 +02:00
Sli
0a0092e189 Add link-once and script-once web components 2024-10-19 18:55:32 +02:00
Sli
c50f0a2ac5 Simplify ajax-select inheritance and make simple auto complete 2024-10-19 16:02:54 +02:00
Sli
6b3012d21c Fix broken sas ui in webkit based browsers 2024-10-18 23:50:04 +02:00
Sli
729f848c14 Add min-characters-for-search attribute for user-ajax-select 2024-10-18 23:34:37 +02:00
Sli
56cc4776a6 Create base class for ajax-select 2024-10-18 23:26:04 +02:00
e6f25fb707 Merge pull request #898 from ae-utbm/taiste
Complete webpack migration, introduction of tom select, better SAS moderation workflow, more ruff and bugfixes
2024-10-18 11:11:39 +02:00
b9cbba2309 Merge pull request #896 from ae-utbm/relpace-select2
Replace selec2 with tom-select
2024-10-18 00:24:09 +02:00
Sli
4165f8d4af Add register decorator for web components and a better inheriting system for html elements 2024-10-17 23:14:54 +02:00
Sli
cac185634d Avoid keeping text after selecting item 2024-10-17 18:21:51 +02:00
Sli
66dceefcf0 Fix bad constructor when adding attrs that are not part of the parent and fix tom-select on safari 2024-10-17 18:15:55 +02:00
Sli
677ff51ea5 Create web component util 2024-10-17 18:15:55 +02:00
Sli
645b8a543e Make easymde compatible with safari 2024-10-17 18:15:55 +02:00
Sli
74a506c48b Add missing features
* Fix display
* Add internationalization
* Avoid querying under a certain amount of characters
* Update docs for translations with typescript
* Add interpolate to typescript globals
2024-10-17 18:15:55 +02:00
Sli
deda2b4055 Replace selec2 with tom-select 2024-10-17 18:15:55 +02:00
67ebb90ffa Merge pull request #897 from ae-utbm/fix-xss
Fix xss on select2 results
2024-10-17 12:10:08 +02:00
5d16ba135a fix: xss on select2 results 2024-10-17 08:15:34 +02:00
150d08dc45 Merge pull request #894 from ae-utbm/sentry
Test sentry-debug endpoint
2024-10-15 20:48:01 +02:00
Sli
c1a85486cc Add test for sentry-debug endpoint 2024-10-15 14:09:51 +02:00
d16a207a83 Add more Ruff rules (#891)
* ruff: apply rule F

* ruff: apply rule E

* ruff: apply rule SIM

* ruff: apply rule TCH

* ruff: apply rule ERA

* ruff: apply rule PLW

* ruff: apply rule FLY

* ruff: apply rule PERF

* ruff: apply rules FURB & RUF
2024-10-15 11:36:26 +02:00
Sli
d114b01bcc Make sure Alpine is always loaded when using markdown-input component 2024-10-15 00:28:43 +02:00
Sli
dee54c3b41 Use manifest storage and correct webpack conversion on easymde form 2024-10-15 00:28:43 +02:00
Sli
670d2fa12e Use a web component for easymde
* Bump tsconfig output to es6
* Fix wrong import behavior on typescript according to webpack's doc
* Create an easymde component
2024-10-15 00:28:43 +02:00
a68e47ce8c Merge pull request #890 from ae-utbm/update-python-deps
Upgrade python dependencies
2024-10-14 15:45:18 +02:00
Sli
0314aa6733 Upgrade python dependencies
* Upgrade pre-commit
* Upgrade model-bakery
* Uprgade mkdocstrings
* Upgrade mkdocstrings-python
* Upgrade mkdocs-material
2024-10-14 15:16:07 +02:00
496ad7ce9b Merge pull request #868 from ae-utbm/delete-picture-confirm-button
Delete picture confirm button
2024-10-14 14:12:50 +02:00
efdd4a6b16 fix ruff breakpoint 2024-10-14 01:59:24 +02:00
Sli
0b31b215f6 Remove check_front command 2024-10-14 00:55:48 +02:00
Sli
7e1734aed5 Migrate chartjs to npm 2024-10-14 00:55:48 +02:00
19cd51043a feat: display moderation requests to moderators 2024-10-14 00:47:07 +02:00
5348a451e9 feat: picture moderation requests 2024-10-14 00:45:52 +02:00
83ae21140d move SAS forms to their own file 2024-10-14 00:45:52 +02:00
Sli
cdf9519a9f Port galaxy to webpack 2024-10-13 20:09:55 +02:00
d77358eaac Merge pull request #879 from ae-utbm/optimize-products-page
optimize: product list views
2024-10-13 19:20:01 +02:00
Sli
9609a7615b Don't apply js minification to webpack generated files 2024-10-13 17:14:21 +02:00
Sli
361a06e5b3 Migrate sentry to webpack 2024-10-13 17:14:21 +02:00
Sli
1720307c21 Add biome for typescript in vscode config 2024-10-13 17:00:09 +02:00
15ae24f0bd optimize: product list views 2024-10-13 12:32:50 +02:00
143713fac1 Merge pull request #878 from ae-utbm/fix-invoices
fix: InvoiceQuerySet.annotate_total() (but this time good)
2024-10-13 11:57:59 +02:00
e4845b580b fix: invoices month grouping 2024-10-13 11:47:22 +02:00
Sli
40c623b202 Optimize select2 import 2024-10-13 10:55:15 +02:00
Sli
092ace8432 Add commands to easily analyze webpack outputs 2024-10-13 10:55:15 +02:00
Sli
00cf619c68 Remove hand crafted urls on viewer-index.ts 2024-10-13 10:55:15 +02:00
Sli
b6e1c3bc88 Add helper function to export ts functions to html 2024-10-13 10:55:15 +02:00
Sli
3b1d06a71d Update select2 documentation 2024-10-13 10:55:15 +02:00
Sli
a5d8c96bab Remove select2 from vendored
* Make core/utils/select2.ts
* Convert viewer-index.js to typescript
2024-10-13 10:55:15 +02:00
564d95f701 fix: InvoiceQuerySet.annotate_total() (but for real this time) 2024-10-13 10:37:48 +02:00
19e21c80df Merge pull request #875 from ae-utbm/taiste
Send mail to inactive users, fix user accounts and webpack sas
2024-10-12 20:04:47 +02:00
Sli
768e2867b5 Fix wrong formatter doc on vscode 2024-10-12 19:43:07 +02:00
Sli
f07a855e7e Remove history management from script.js and migrate sas albums to webpack 2024-10-12 19:19:23 +02:00
2fa9daf627 Merge pull request #872 from ae-utbm/invoices-bug
fix: InvoiceQuerySet.annotate_total()
2024-10-12 19:18:37 +02:00
a1bae7ced3 fix empty options in paginated with typescript 2024-10-12 18:59:06 +02:00
7312580a8d fix: InvoiceQuerySet.annotate_total() 2024-10-12 15:52:40 +02:00
1c774aa4a0 Merge pull request #861 from ae-utbm/mail-inactives
Send mail to inactive users
2024-10-12 15:33:23 +02:00
cbcdc6171f Merge pull request #871 from ae-utbm/fix-doc-generation
delete stocks remaining docs
2024-10-12 13:02:52 +02:00
444a2936e2 delete stocks remaining docs 2024-10-12 12:45:40 +02:00
6a31f38ceb Merge pull request #870 from ae-utbm/taiste
Counter state improvement, Stock app removal, lot of work on Webpack and more
2024-10-11 15:18:12 +02:00
29b32f6cbf Tell the customer balance in the warning mail 2024-10-11 09:59:03 +02:00
465e0f31d9 write command test 2024-10-11 09:57:46 +02:00
5a8052ae47 send mail to inactive users 2024-10-11 09:57:41 +02:00
6a64e05247 select inactive users 2024-10-11 09:45:54 +02:00
81a64eed08 Merge pull request #867 from ae-utbm/barmen-link
Better UX and performance for counter state display
2024-10-11 09:31:12 +02:00
29b27dc626 Merge pull request #866 from ae-utbm/openapi
Typescript support and auto generated typescript client API
2024-10-11 09:30:35 +02:00
ca25a12be0 Increase the barmen timeout limit
La limite actuelle est trop faible. En soirée, on s'en fout. Mais en journée, c'est terriblement chiant. Certains barmens passent leur temps à rafraichir la la page, certains mettent un rechargement auto à intervalles réguliers (ce qui tue le concept du timeout), et d'autres encore ont juste arrêté d'y prêter attention (mais le comptoir apparait alors comme fermé, et des étudiants qui auraient pu venir au Foyer ne viennent finalement pas)
2024-10-10 19:38:49 +02:00
c0a6f5eb30 Optimize barmen timeout and counter state fetch
Le timeout se fait en une seule requête et la récupération de l'état des comptoirs en une seule requête aussi. Grâce à ça, on peut en grande partie retirer le cache pour l'affichage de l'état des comptoirs, ce qui a des implications excellentes en termes d'UX (comme le fait que la redirection vers la page de comptoir ou d'activité aura plus une apparence de truc aléatoire)
2024-10-10 19:38:49 +02:00
4bc4d266c2 Remove the question mark from the counter state
En raison de la manière dont le timeout marche et de l'activité des comptoirs, la notion de "comptoir inactif" n'est pas intuitive. Un comptoir est ouvert ou fermé. Point.
2024-10-10 19:37:00 +02:00
8f0ee4df6d Merge pull request #828 from ae-utbm/remove-stocks
remove stock application
2024-10-10 19:00:38 +02:00
Sli
579d077b35 Fix docstring 2024-10-10 15:45:43 +02:00
Sli
32444fac90 Apply review comments 2024-10-10 15:42:11 +02:00
Sli
849177562d Add a way to get the base url of an endpoint 2024-10-10 02:57:54 +02:00
Sli
86bbc4cf6e Migrate uv guide to webpack 2024-10-10 02:04:49 +02:00
Sli
46e58bb49e Remove fetchPaginated and migrate viewer.js to viewer-index.js in webpack 2024-10-09 21:46:56 +02:00
Sli
9199f91151 Use typescript api for user pictures and allow imports across js files
* Add imports paths for js files in node
* Add a ts version of fetchPaginated
* Update documentation
2024-10-09 20:59:12 +02:00
Sli
9247696c1c Don't collect .ts files in statics 2024-10-09 17:30:44 +02:00
Sli
37f62e15cf Use new typescript api for user graphs 2024-10-09 17:21:05 +02:00
Sli
a98c924b24 Use auto generated api for markdown input 2024-10-09 16:56:53 +02:00
Sli
a71ca60270 Add typescript support and automatic openapi client generation from ninja 2024-10-09 16:28:54 +02:00
76cc730d8f Merge pull request #865 from ae-utbm/deps
Update Deps
2024-10-09 15:54:09 +02:00
12bb7e9294 remove stock application 2024-10-09 14:50:41 +02:00
1dca0ea003 update ruff 2024-10-09 14:28:13 +02:00
b340a6568f update dependencies 2024-10-09 14:26:39 +02:00
Sli
6f4e93bb76 Use configuration object for load builders 2024-10-09 12:14:10 +02:00
Sli
93eb09887e Fix translations 2024-10-09 12:14:10 +02:00
Sli
09081b03b6 Move family_graph.js to webpack
* Remove cytoscape dependencies
2024-10-09 12:14:10 +02:00
Sli
ceee393bd8 Move user_picture.js to webpack
* Fix relative path generation in webpack
* remove vendored/native-file-system-adapter
* remove vendored/zip.js
2024-10-09 12:14:10 +02:00
b969513d94 Merge pull request #858 from ae-utbm/jsstandard
Add biome to format js files
2024-10-08 23:45:20 +02:00
2111a2c67e Merge pull request #859 from ae-utbm/account-pages
Optimize user account pages
2024-10-08 19:55:45 +02:00
Sli
7405241b82 Apply all biomejs fixes 2024-10-08 17:14:22 +02:00
b0884c6b04 return 404 when accessing not existing account 2024-10-08 15:30:35 +02:00
20bea62542 use spaces for indentation 2024-10-08 13:54:44 +02:00
Sli
24925f7726 Add Biome to documentation 2024-10-08 01:49:29 +02:00
Sli
d0c18d4538 Format with biome instead of standard 2024-10-08 01:49:15 +02:00
Sli
37eaa4b912 Add Standard to documentation 2024-10-07 01:36:13 +02:00
Sli
a3cca056ae Apply standard to easymde 2024-10-07 00:10:24 +02:00
Sli
ee965008d1 Properly fix no-unused-vars warning 2024-10-07 00:04:48 +02:00
Sli
c57d2ece9c Apply standard formater and linter on js files 2024-10-07 00:04:48 +02:00
Sli
e5aa7aa866 Move easymde widget to easymde-index.js 2024-10-07 00:04:05 +02:00
cacdf600f4 Merge pull request #860 from ae-utbm/fix-sas-owner
Fix sas owner
2024-10-05 21:44:21 +02:00
5ee0ee8efb tests for picture ownership 2024-10-05 21:02:19 +02:00
08f20796a7 access rights fix 2024-10-05 20:53:52 +02:00
58d3a7ee2c Optimize user account pages 2024-10-04 13:41:39 +02:00
f6be360eab Merge pull request #857 from ae-utbm/fix-slideshow
Fix slideshow
2024-10-03 22:51:25 +02:00
543a48b4ab reminder to use wsl when cloning the project 2024-10-03 18:32:27 +02:00
Sli
0f657b934d Fix makemessage doc 2024-10-03 18:32:27 +02:00
c4e42212aa Better install doc 2024-10-03 18:32:27 +02:00
a4fe4996aa Merge pull request #834 from ae-utbm/real-name
Restore real name
2024-10-03 10:12:23 +02:00
Sli
fbcacb24f8 Fix broken screen slideshow 2024-10-03 01:05:56 +02:00
0eaa20e09d fix localdate issues 2024-10-03 00:25:22 +02:00
Sli
271d57051e Upgrade to fontawesome 6
* Adapt fontawesome usage when needed
* Fix uv guide not importing css
* Remove utf8 usage for fontawesome
2024-10-03 00:06:03 +02:00
3d6c260e53 Merge pull request #854 from ae-utbm/img-resizing
faster image resizing and smaller results
2024-10-02 23:50:33 +02:00
d0f17bd41a faster image resizing and smaller results 2024-10-02 23:16:47 +02:00
819cd257a8 Merge pull request #853 from ae-utbm/taiste
Webpack, Forum style and faster counter operations page
2024-10-02 18:03:00 +02:00
Sli
655d72a2b1 Completely integrate wepack in django
* Migrate alpine
* Migrate jquery and jquery-ui
* Migrate shorten
* Add babel for javascript
* Introduce staticfiles django app
* Only bundle -index.js files in static/webpack
* Unify scss and webpack generated files
* Convert scss calls to static
* Add --clear-generated option to collectstatic
* Fix docs warnings
2024-10-02 16:11:02 +02:00
71c96fdf62 Merge pull request #852 from ae-utbm/master
Merge back
2024-10-01 10:39:42 +02:00
3f2327dee4 Merge pull request #851 from ae-utbm/841-sales-selection-performance
841 sales selection performance
2024-09-30 16:07:19 +02:00
06eecfce40 Optimized last operations on counters 2024-09-30 16:02:17 +02:00
67af1485b3 Merge pull request #850 from ae-utbm/sas-history
Fix history navigation bug in picture viewer in sas
2024-09-30 15:06:10 +02:00
Sli
a00a85a56a Fix recursive link history updates for picture viewer 2024-09-30 15:02:11 +02:00
bb953a6139 Merge pull request #831 from ae-utbm/forum-css-rework
Forum css rework
2024-09-30 12:13:52 +02:00
140dc26dc6 Merge pull request #846 from ae-utbm/fix-select2-img
fix profile pictures layout in Select2 results
2024-09-30 12:13:21 +02:00
3548deebf6 Merge pull request #849 from ae-utbm/taiste
New 3DSv2 fields and Bugfixes
2024-09-30 11:33:32 +02:00
c67155f02c Merge pull request #845 from ae-utbm/fix-search
Fix 500 whean searching users
2024-09-30 10:51:14 +02:00
c10e1e8cbf fix profile pictures layout in Select2 results 2024-09-29 23:31:33 +02:00
c5f5ad3f75 fix 500 when searching users 2024-09-29 23:01:55 +02:00
8ec3074488 Merge pull request #842 from ae-utbm/3dsv2-again
Add the new 3DSv2 fields
2024-09-28 17:59:37 +02:00
1b1284d3d0 Better validation for phone number in billing info 2024-09-28 17:25:34 +02:00
f71518ed6f Move deprecated paginate macro to a lower scope 2024-09-27 11:21:33 +02:00
1800785b80 generalize usage of the paginate_jinja macro 2024-09-27 11:21:33 +02:00
6449724ed5 fix pagination macro and add ellision 2024-09-27 11:21:33 +02:00
6179c3e7d4 Better style for forum messages 2024-09-27 11:21:33 +02:00
3e5d4c5fbb add fixtures for the forum 2024-09-27 11:21:33 +02:00
3f2b63aaa5 move forum style into its own file 2024-09-27 11:21:33 +02:00
d29a5cdb44 Add the new 3DSv2 fields 2024-09-27 11:10:38 +02:00
bbcc7ffeaa Merge pull request #839 from ae-utbm/user-ordering
User ordering
2024-09-25 17:51:25 +02:00
93f4dede3e Put users that never logged in at the end 2024-09-25 14:36:22 +02:00
683f8235b1 Merge pull request #840 from ae-utbm/faster-album-rights
Optimize SithFile recursive rights
2024-09-25 14:35:45 +02:00
43917317b4 optimize file recursive rights 2024-09-25 12:31:51 +02:00
f182de5929 restore user ordering 2024-09-24 12:52:40 +02:00
c6657bffd2 fix: profile picture deletion by board members 2024-09-23 23:35:14 +02:00
3d138d404f move webcam JS to its own file 2024-09-23 23:35:14 +02:00
Sli
9c93162741 Add missing files 2024-09-23 10:25:27 +02:00
Sli
6068c6048a Use real name of the website once again 2024-09-23 01:37:25 +02:00
d47461ba40 Merge pull request #830 from ae-utbm/repair-pagination
fix: `fetch_paginated`
2024-09-20 00:03:34 +02:00
66e88ac6fb Merge pull request #832 from ae-utbm/image-deletion-fix
Fixes after last deployment
2024-09-19 23:57:12 +02:00
d3cada4c95 fix family graph image exension 2024-09-19 20:52:10 +02:00
27443bcd21 fix image deletion. again. 2024-09-19 20:35:08 +02:00
b246e171b7 fix: fetch_paginated 2024-09-18 22:03:39 +02:00
ec434bec56 Merge pull request #829 from ae-utbm/taiste
Family tree and blazingly fast SAS
2024-09-18 16:06:01 +02:00
7458f622f5 Merge pull request #809 from ae-utbm/ajax-image-sas
Ajax image sas
2024-09-18 15:03:54 +02:00
ab72e01707 lower the number of characters to trigger a fulltext search 2024-09-17 17:52:39 +02:00
acad74528d fix: sale creation in populate_more 2024-09-17 16:05:42 +02:00
813bbbb94a preload images and identifications 2024-09-17 12:23:13 +02:00
a2a858262a apply review comments 2024-09-17 12:23:13 +02:00
Sli
727e5cb199 Dummy data on default current_picture to avoid javascript errors 2024-09-17 12:23:13 +02:00
Sli
71602b43bd implement back feature on sas ajax view 2024-09-17 12:23:13 +02:00
bc40b92744 completely ajaxify the picture page 2024-09-17 12:23:13 +02:00
d545becf24 add spinner during loading 2024-09-17 12:17:21 +02:00
48f605dbe0 Use select2 for user picture identification 2024-09-17 12:17:17 +02:00
b0d7bbbb79 select 2 builder 2024-09-17 12:14:20 +02:00
f624b7c66d Graph de famille en frontend (#820)
* Remove graphviz and use cytoscape.js instead

* Frontend generated graphs
* Make installation easier and faster
* Better user experience
* Family api and improved interface
* Fix url history when using 0, improve button selection and reset reverse with reset button
* Use klay layout
* Add js translations and apply review comments
2024-09-17 12:10:06 +02:00
bf96d8a10c Merge pull request #824 from ae-utbm/compress-product-images
auto compress product icons
2024-09-15 18:26:56 +02:00
e8b496cfdc test: Product and ProductType icon resizing 2024-09-15 16:38:58 +02:00
79ef151ad3 auto compress product icons 2024-09-15 14:12:41 +02:00
8e48103fd2 Merge pull request #823 from ae-utbm/fix-image-extension
fix image extension
2024-09-14 19:48:30 +02:00
ed4c65600c fix image extension 2024-09-14 18:45:12 +02:00
ae16a1bd89 Merge pull request #821 from ae-utbm/taiste
Python upgrade and bugfixes
2024-09-12 11:37:27 +02:00
e2b42145e1 Merge pull request #819 from ae-utbm/fix-delete-picture
fix undeletable SAS pictures
2024-09-10 23:12:59 +02:00
55ad1f99fd fix undeletable SAS pictures 2024-09-10 21:38:13 +02:00
5b427bee35 Merge pull request #817 from ae-utbm/skia/faster_install_xapian
Faster install xapian
2024-09-09 15:37:12 +02:00
d1c88a5cef core: commands: make 'install_xapian' way faster 2024-09-09 15:17:09 +02:00
99a25d5e9b Merge pull request #811 from ae-utbm/sas-form-length
unify album name length
2024-09-08 14:32:26 +02:00
d148d6b3a5 unify album name length 2024-09-08 13:30:23 +02:00
66189d3ab2 Merge pull request #810 from ae-utbm/fix-membership-end
fix memberships ending today
2024-09-04 16:27:00 +02:00
f1afa3b436 fix memberships ending today 2024-09-04 16:21:42 +02:00
6380fb193c Merge pull request #808 from ae-utbm/update
Update Python and dependencies
2024-09-02 13:59:23 +02:00
341ffc9a55 update CI 2024-09-02 12:49:11 +02:00
6962b39fc9 use typing.Self for custom queryset methods 2024-09-02 01:03:46 +02:00
d04b4c77c6 update dependencies 2024-09-02 01:03:46 +02:00
453b7df0be bump Python to 3.12 2024-09-02 01:03:46 +02:00
878ee99fe4 Merge pull request #806 from ae-utbm/taiste
Bugfixes
2024-09-02 00:03:40 +02:00
6918e3044f Merge pull request #801 from ae-utbm/remove-version
remove sith version from the footer
2024-09-01 23:50:53 +02:00
cf46c3800f remove sith version from the footer 2024-09-01 23:47:25 +02:00
7c0c132f40 Merge pull request #804 from ae-utbm/repair-subscription-translation
fix subscription form translation
2024-09-01 23:42:18 +02:00
e0bf797876 Merge pull request #805 from ae-utbm/images-format
Better images format
2024-09-01 23:33:57 +02:00
dd07c374d7 convert uploaded images to webp 2024-09-01 19:05:54 +02:00
b3e59b3829 remove unused view GET user/<user_id>/profile_upload 2024-09-01 18:49:50 +02:00
352b09d9cd fix subscription form translation 2024-09-01 15:20:07 +02:00
93cc6d99f8 Merge pull request #803 from ae-utbm/fix-promo-image
fix promo img on clicks
2024-09-01 12:49:38 +02:00
85a99fc8fa fix promo img on clicks 2024-09-01 12:33:49 +02:00
a4d801bed4 Merge pull request #798 from ae-utbm/fix-content-disposition
repair name of protected files
2024-08-30 10:44:49 +02:00
fbff38c5c3 repair name of protected files
Depuis l'implémentation de l'envoi des fichiers par le reverse-proxy, le nom des fichiers n'était plus envoyé.
2024-08-30 10:27:03 +02:00
14402f7537 Merge pull request #800 from ae-utbm/forgotten-migrations
add forgotten migration
2024-08-29 12:53:21 +02:00
88d24f8067 Merge pull request #799 from ae-utbm/remove-sentry-ci
remove sentry deployment CI (until Sentry is repaired)
2024-08-29 12:53:06 +02:00
cc1d700f7d add forgotten migration 2024-08-29 11:57:09 +02:00
e82acdabb0 remove sentry deployment CI (until Sentry is repaired) 2024-08-29 11:48:26 +02:00
ea42c98571 Merge pull request #797 from ae-utbm/fix-image-injection
Better form for user submiting images
2024-08-27 22:46:01 +02:00
Sli
cc5df9b171 Better form for user submiting images, fix potential attack vector on bad file being resized and treated as an image 2024-08-27 17:05:37 +02:00
b4749f297b Merge pull request #795 from ae-utbm/taiste
Last update before Inté
2024-08-27 14:16:40 +02:00
e564c6604c Merge pull request #788 from ae-utbm/manifest-static-files
Manifest static files
2024-08-27 11:08:49 +02:00
712615a312 Merge pull request #794 from ae-utbm/user-pictures-ajax
Better browser compatibility for user picture page
2024-08-27 11:08:27 +02:00
Sli
d95d4901d2 Use reduce instead of groupBy for user picture sorting to support more browsers 2024-08-27 10:35:38 +02:00
9373654306 use rjsmin for js minification
Ca minifie moins bien le JS que Uglify, mais c'est intégrable directement dans les dépendances du projet
2024-08-26 23:16:13 +02:00
4a9d9f03a8 fix test workflow 2024-08-26 22:59:40 +02:00
b7261ec629 custom manifest static files storage that also minify scss and js files 2024-08-26 22:34:32 +02:00
2e1f16fa04 slim jquery-ui 2024-08-26 22:34:32 +02:00
d295cc5223 move vendored files into their own folder 2024-08-26 22:34:32 +02:00
ff088009d9 move static files in their respective application 2024-08-26 22:34:31 +02:00
52c19e9962 simplify scss management 2024-08-26 22:34:31 +02:00
68d0a16d1c Merge pull request #782 from ae-utbm/ajax-navigation-history
Ajax navigation history in uv guide
2024-08-26 22:29:19 +02:00
Sli
a422e8d39a Improve rendering of file input 2024-08-26 22:21:16 +02:00
Sli
ef80c1be61 Make camera error gray to be less aggressive on the eyes 2024-08-26 22:21:16 +02:00
Sli
85d9816aaa Improve delete button behavior 2024-08-26 22:21:16 +02:00
Sli
93b66d980d Directly display selected img of form on screen and convert to webp 2024-08-26 22:21:16 +02:00
Sli
07d617da91 Get video resolution from the camera settings for a better image quality 2024-08-26 22:21:16 +02:00
Sli
34aac40e65 Add translations 2024-08-26 22:21:16 +02:00
Sli
f54bf2b8af Adjust css 2024-08-26 22:21:16 +02:00
Sli
e7d04d9817 Unify user profile display with a nice macro and handle camera errors 2024-08-26 22:21:16 +02:00
Sli
ef1537ac2c Basic webcam setup with modern web api 2024-08-26 22:21:16 +02:00
d1f86fe3d9 Merge pull request #791 from ae-utbm/remove-bbcode
Remove to_markdown.jinja forgotten during bbcode convertion removal
2024-08-24 20:40:04 +02:00
Sli
d13b79552b Remove to_markdown.jinja forgotten during bbcode convertion removal 2024-08-21 15:35:43 +02:00
4036bfd703 Merge pull request #775 from ae-utbm/user-pictures-ajax
Render user picture page with ajax to improve performances
2024-08-18 12:40:07 +02:00
Sli
759e360a1d Don't use unnecessary promises 2024-08-17 10:15:13 +02:00
Sli
8865529b39 Use native alpine debounce 2024-08-17 02:58:53 +02:00
Sli
cdb73ee49c Don't rely on waiting for pedagogy history 2024-08-17 02:57:00 +02:00
Sli
9188c28ee7 Remove intersect 2024-08-16 22:52:20 +02:00
Sli
2a6c1f050d Create a paginate_alpine macro 2024-08-11 15:11:51 +02:00
Sli
2ec1f8cdc0 Fix back action in uv guide 2024-08-11 14:58:05 +02:00
121b388d85 Merge pull request #781 from ae-utbm/ajax-navigation-history
Fix back function in album pagination
2024-08-11 00:34:21 +02:00
Sli
589119c9ee Improve update_query_string with enum action 2024-08-10 23:32:50 +02:00
Sli
b35e1a476e Fix back function in album pagination 2024-08-10 18:38:04 +02:00
8174bce720 Merge pull request #780 from ae-utbm/remove-bbcode
remove doku/bbcode to markdown
2024-08-10 16:04:10 +02:00
d8a7d62b23 Merge pull request #779 from ae-utbm/fix-queryset
fix crash on album fetch & test
2024-08-10 16:03:56 +02:00
Sli
a75730d91f Fix unbalanced html 2024-08-10 15:16:37 +02:00
Sli
a2b5f929dd Apply review comments
* Add alpine intersect
* Move alpine and it's plugins to a folder
* Fix spinning wheel position
* Improve album title position
2024-08-10 14:49:02 +02:00
7a0fa9f1a0 remove doku/bbcode to markdown 2024-08-10 14:23:01 +02:00
28ff7f24c5 Merge pull request #774 from ae-utbm/fix-operation-logs
Fix operation logs
2024-08-10 10:33:39 +02:00
e6db25357b Merge pull request #778 from ae-utbm/user-search-privacy
User search privacy
2024-08-10 09:38:29 +02:00
72ea6b6fdd fix crash on album fetch & test 2024-08-10 00:46:40 +02:00
Sli
bf5f72fd9d Fix user search displaying results that shouldn't be viewed 2024-08-10 00:43:03 +02:00
af724a1e0e Merge pull request #777 from ae-utbm/taiste
SAS Hotfixes
2024-08-09 18:20:22 +02:00
Sli
0eeaf1ce21 Render user picture page with ajax to improve performances 2024-08-09 18:09:58 +02:00
57a8215c6b Merge pull request #776 from ae-utbm/fix-album-navigation
SAS fixes
2024-08-09 18:09:06 +02:00
9163e4dee6 fix SAS album display 2024-08-09 18:08:36 +02:00
c56d6e3f6b fix wrong page size when fetching pictures. 2024-08-09 17:35:33 +02:00
Sli
20e8854467 Fix operation logs 2024-08-09 17:35:26 +02:00
3ef38fabdb fix picture navigation 2024-08-09 17:34:35 +02:00
f5cee10761 Merge pull request #773 from ae-utbm/taiste
SAS, Eboutic, Antispam, psycopg
2024-08-09 13:35:26 +02:00
d1cbb765c0 Merge pull request #769 from ae-utbm/query-sas
Sas picture selection
2024-08-09 12:11:16 +02:00
7ea9a5ca2d improved feedback when loading ajax content 2024-08-09 11:58:26 +02:00
20c015c312 improved UX 2024-08-09 11:58:26 +02:00
ecb48ce663 fix error when uploading image with an alpha channel 2024-08-09 11:58:26 +02:00
00dc03a235 fix rights on albums and next/previous pictures 2024-08-08 13:35:48 +02:00
d3b203a4a1 change cache on picture download 2024-08-08 11:50:45 +02:00
4506440a62 add PictureQuerySet.viewable_by(user) method 2024-08-08 11:50:45 +02:00
da6bd84cdf restify album view 2024-08-08 11:50:45 +02:00
0b9ccf6a57 paginate GET /api/sas/picture 2024-08-08 11:50:45 +02:00
a056bd177f Merge pull request #772 from ae-utbm/master
Merge-back
2024-08-08 11:47:26 +02:00
d2ea8f2898 Merge pull request #742 from ae-utbm/refactor-eboutic
Eboutic big refactor
2024-08-07 20:36:50 +02:00
5cce4269bb remove fuzzy from translations 2024-08-07 20:33:26 +02:00
0a2ed6dd94 fix crash when basket contains not existing product 2024-08-07 20:15:46 +02:00
417f328206 fix billing infos not sending 2024-08-07 14:29:51 +02:00
cca9732925 eboutic big refactor 2024-08-06 16:49:20 +02:00
f02864b752 Merge pull request #768 from ae-utbm/ruff-print
T2 ruff rule
2024-08-06 16:45:20 +02:00
62bb15317c T2 ruff rule 2024-08-06 11:42:10 +02:00
b35751126f Merge pull request #762 from ae-utbm/dependabot/pip/taiste/sentry-sdk-2.12.0
[UPDATE] Bump sentry-sdk from 2.11.0 to 2.12.0
2024-08-06 11:14:47 +02:00
28d6d8ba96 Merge pull request #766 from ae-utbm/alpine
Alpine
2024-08-06 10:43:08 +02:00
6bdb16e293 [UPDATE] Bump sentry-sdk from 2.11.0 to 2.12.0
Bumps [sentry-sdk](https://github.com/getsentry/sentry-python) from 2.11.0 to 2.12.0.
- [Release notes](https://github.com/getsentry/sentry-python/releases)
- [Changelog](https://github.com/getsentry/sentry-python/blob/master/CHANGELOG.md)
- [Commits](https://github.com/getsentry/sentry-python/compare/2.11.0...2.12.0)

---
updated-dependencies:
- dependency-name: sentry-sdk
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-05 21:41:48 +00:00
eb45cf6175 Merge pull request #761 from ae-utbm/dependabot/pip/taiste/django-honeypot-1.2.1
[UPDATE] Bump django-honeypot from 1.2.0 to 1.2.1
2024-08-05 23:39:42 +02:00
d6d8f56570 [UPDATE] Bump django-honeypot from 1.2.0 to 1.2.1
Bumps [django-honeypot](https://github.com/jamesturk/django-honeypot) from 1.2.0 to 1.2.1.
- [Changelog](https://github.com/jamesturk/django-honeypot/blob/main/CHANGELOG)
- [Commits](https://github.com/jamesturk/django-honeypot/commits)

---
updated-dependencies:
- dependency-name: django-honeypot
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-05 21:31:09 +00:00
5322dc1de8 Merge pull request #764 from ae-utbm/dependabot/pip/taiste/faker-26.1.0
[UPDATE] Bump faker from 26.0.0 to 26.1.0
2024-08-05 23:28:58 +02:00
51bb1a5c9d Merge pull request #765 from ae-utbm/fix-referer
fix-referer
2024-08-05 23:28:25 +02:00
996dadf6f5 update alpineJS to 3.14 2024-08-05 17:16:24 +02:00
29bb0f6712 promote AlpineJS to global dependency 2024-08-05 17:08:30 +02:00
f6fbad8403 fix missing HTTP_REFERER 2024-08-05 15:53:41 +02:00
e37ce4172e Merge pull request #759 from ae-utbm/accel-redirect
Accel redirect
2024-08-05 15:15:39 +02:00
1dfd871169 add doc for nginx configuration 2024-08-05 13:32:47 +02:00
a637742bb0 apply review comment 2024-08-05 10:52:15 +02:00
a5e4db99fb Use X-Accel-Redirect to send files in prod 2024-08-05 10:52:15 +02:00
a9f66e2cd9 extract sent_from_logged_counter(request) 2024-08-05 10:52:15 +02:00
7bc7af8245 [UPDATE] Bump faker from 26.0.0 to 26.1.0
Bumps [faker](https://github.com/joke2k/faker) from 26.0.0 to 26.1.0.
- [Release notes](https://github.com/joke2k/faker/releases)
- [Changelog](https://github.com/joke2k/faker/blob/master/CHANGELOG.md)
- [Commits](https://github.com/joke2k/faker/compare/v26.0.0...v26.1.0)

---
updated-dependencies:
- dependency-name: faker
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-05 08:51:26 +00:00
e5dfe1e638 Merge pull request #763 from ae-utbm/dependabot/pip/taiste/phonenumbers-8.13.42
[UPDATE] Bump phonenumbers from 8.13.40 to 8.13.42
2024-08-05 10:49:13 +02:00
284f064cbf [UPDATE] Bump phonenumbers from 8.13.40 to 8.13.42
Bumps [phonenumbers](https://github.com/daviddrysdale/python-phonenumbers) from 8.13.40 to 8.13.42.
- [Commits](https://github.com/daviddrysdale/python-phonenumbers/compare/v8.13.40...v8.13.42)

---
updated-dependencies:
- dependency-name: phonenumbers
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-05 08:34:32 +00:00
e5c6f00283 Merge pull request #758 from ae-utbm/psycopg
update psycopg v2 to psycopg v3
2024-08-04 23:19:56 +02:00
12d316ebe4 doc: advanced install 2024-08-04 23:12:24 +02:00
cbd8932075 update psycopg v2 to psycopg v3 2024-08-04 23:12:24 +02:00
feb6dcbc94 Merge pull request #756 from ae-utbm/antispam
Filter blocked emails
2024-08-04 22:36:03 +02:00
Sli
181e74b1d1 Add antispam app
* update_spam_database command to update suspicious domains from an
   external provider
* Add a AntiSpamEmailField that deny emails from suspicious domains
* Update documentation
2024-08-04 22:34:40 +02:00
eb04e26b22 Merge pull request #757 from ae-utbm/taiste
Taiste
2024-08-04 16:51:36 +02:00
7b97f0bf47 [UPDATE] Bump pre-commit from 3.7.1 to 3.8.0
Bumps [pre-commit](https://github.com/pre-commit/pre-commit) from 3.7.1 to 3.8.0.
- [Release notes](https://github.com/pre-commit/pre-commit/releases)
- [Changelog](https://github.com/pre-commit/pre-commit/blob/main/CHANGELOG.md)
- [Commits](https://github.com/pre-commit/pre-commit/compare/v3.7.1...v3.8.0)

---
updated-dependencies:
- dependency-name: pre-commit
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-01 17:51:19 +02:00
19fdaf4c89 fix club counter click 2024-08-01 17:50:43 +02:00
7ca9c8dc42 Merge pull request #749 from ae-utbm/redis
Redis
2024-08-01 13:01:42 +02:00
946f35c601 Merge pull request #752 from ae-utbm/autocomplete-admin
use autocomplete_fields in admin
2024-08-01 13:01:27 +02:00
c7b47bdd02 use redis for the cache 2024-08-01 12:26:23 +02:00
eef15e05f4 use autocomplete_fields in admin 2024-08-01 11:27:54 +02:00
Sli
f265346a10 Sort pictures by album in zip file 2024-07-31 12:01:08 +02:00
a321bd79ed Merge pull request #745 from ae-utbm/picture-zip
Add image download progress bar and fix output name of pictures
2024-07-31 10:19:50 +02:00
819e2b5f9f better download button style 2024-07-30 19:58:58 +02:00
91344741a5 add some alpine to picture download 2024-07-30 19:23:48 +02:00
4d2b82235c downgrade ajax-select 2024-07-30 14:15:46 +02:00
Sli
ffa3936878 Improve zip download
* Remove jszip for zip.js which is better maintained
* Pictures keep their creation date
2024-07-30 11:11:31 +02:00
26c70aa071 Merge pull request #746 from ae-utbm/pedagogy
Use full text search in pedagogy uv search api
2024-07-29 18:12:43 +02:00
Sli
1bd887567e Use full text search in pedagogy uv search api 2024-07-29 13:01:20 +02:00
Sli
3304f32ef0 Add image download progress bar and fix output name of pictures 2024-07-28 23:53:18 +02:00
0790ae2298 Merge pull request #743 from ae-utbm/taiste
Taiste
2024-07-28 21:37:38 +02:00
39151b61e7 Merge pull request #741 from ae-utbm/better-pagination
improve pagination
2024-07-28 16:46:00 +02:00
3f49d70745 remove pedagogy style from style.scss 2024-07-28 16:39:15 +02:00
e5434961de Merge pull request #736 from ae-utbm/better-scss
Better scss
2024-07-28 16:35:12 +02:00
aab2d3a03f Merge pull request #740 from ae-utbm/deps
Update dependencies
2024-07-28 16:34:53 +02:00
b022ebb80e improve pagination 2024-07-27 10:46:57 +02:00
2737cae4ab update django-phonenumber-field 2024-07-26 21:45:18 +02:00
c4e6272535 various deps updates 2024-07-26 21:45:18 +02:00
aa0c98bf34 increase delay between dependabot alerts 2024-07-26 18:24:04 +02:00
63b6b262c6 repair BASE_DIR 2024-07-26 18:21:57 +02:00
424639ea80 better scss 2024-07-26 15:55:15 +02:00
594776f3a6 better scss compilation 2024-07-26 15:55:15 +02:00
918e93d211 Merge pull request #735 from ae-utbm/more-fixtures
Add a command to create more fixtures
2024-07-26 15:28:47 +02:00
b82f98c87f reorganize pyproject.toml 2024-07-26 15:16:54 +02:00
6c4251a91f populate more 2024-07-26 15:15:50 +02:00
2261782920 Merge pull request #738 from ae-utbm/fix-remove-from-picture
Fix button to remove a user from picture
2024-07-26 14:48:28 +02:00
043dcfb283 add tests 2024-07-26 14:25:26 +02:00
3c76c5e0f1 fix grouping 2024-07-26 00:39:29 +02:00
d348e6314a fix the pictures order (not just the album) 2024-07-26 00:39:29 +02:00
b3fa6f352b fix album order for user pictures 2024-07-26 00:39:29 +02:00
191b05c305 Fix button to remove a user from picture 2024-07-25 23:31:54 +02:00
215fdce411 Fix button to remove a user from picture 2024-07-25 23:29:12 +02:00
b25805e0a1 introduce djhtml as jinja+scss formater 2024-07-25 16:46:45 +02:00
13d0d2a300 Merge pull request #733 from ae-utbm/nfc
Add nfc widget
2024-07-25 15:56:51 +02:00
Sli
15f51fb03f Create an NFC button for browser supporting NFC API 2024-07-25 07:18:39 +02:00
a24b1f5c2a Merge pull request #730 from ae-utbm/picture-zip
Téléchargement des images dans un zip
2024-07-25 01:21:02 +02:00
04e7f65e8e Merge pull request #725 from ae-utbm/drop-jquery-calendar
Remove jquery datetimepicker
2024-07-25 01:20:41 +02:00
Sli
41b9318028 Download user pictures as a zip 2024-07-24 23:51:15 +02:00
378e8b53f2 Merge pull request #731 from ae-utbm/taiste
MkDocs, Ninja API, logo promo 24 et refactors
2024-07-24 17:56:57 +02:00
c832e8b1a7 Merge pull request #729 from ae-utbm/test-name-refactoring
Harmonize test names
2024-07-24 01:17:39 +02:00
Sli
fee7ade1a5 Harmonize test names 2024-07-24 00:50:48 +02:00
d51dbf8a53 Merge pull request #724 from ae-utbm/ninja
Use django-ninja for the API
2024-07-24 00:48:08 +02:00
c03a1b57c5 update doc 2024-07-24 00:44:09 +02:00
Sli
0c566cfbde Add picture size in sas api 2024-07-24 00:44:09 +02:00
9295325d21 remove jquery datetime picker 2024-07-23 23:26:48 +02:00
cb1aa8bef0 add tests 2024-07-23 20:36:57 +02:00
Sli
b9d19be183 Fix markdown api and add test for user picture page 2024-07-23 20:36:46 +02:00
Sli
293369f165 Pagination on UV guide 2024-07-23 19:58:11 +02:00
3046438cb1 replace drf by django-ninja 2024-07-23 19:57:33 +02:00
811e5a5ad1 Merge pull request #726 from ae-utbm/honeypot
better honeypot logging
2024-07-22 12:45:45 +02:00
Sli
2c8f18d7fc Add honeypot on forum 2024-07-22 11:40:11 +02:00
Sli
c7f8cdd098 Support field_name argument in honeypot jinja extension 2024-07-22 11:34:22 +02:00
Sli
58ff5b934a add get_client_ip util function 2024-07-22 09:49:08 +02:00
03d15ddded better honeypot logging 2024-07-21 22:31:05 +02:00
002d8f80a6 Merge pull request #720 from ae-utbm/counter-refactor
Refactor on counters
2024-07-21 15:39:07 +02:00
82d3791859 refactor counter 2024-07-21 10:51:08 +02:00
d9531838f2 Merge pull request #716 from ae-utbm/docs
Use MkDocs for documentation
2024-07-21 01:05:32 +02:00
Sli
c7b5c77395 Improve perms doc 2024-07-21 01:01:49 +02:00
Sli
223aa37161 move old pdf to the repo github wiki 2024-07-21 01:00:37 +02:00
c1acadbf3d add content to howto/querysets.md 2024-07-21 00:57:15 +02:00
Sli
54af894b82 Improve documentation 2024-07-21 00:57:12 +02:00
Sli
e1ac75f394 Rework readme and remove readthedocs config 2024-07-21 00:57:06 +02:00
8c69a94488 use google convention for docstrings 2024-07-21 00:57:04 +02:00
07b625d4aa Rewrite documentation with MkDocs 2024-07-21 00:56:58 +02:00
a1296dc7af Merge pull request #721 from ae-utbm/remove-pytz
Remove pytz from deps
2024-07-20 11:36:30 +02:00
e5a2236d72 remove pytz 2024-07-18 17:33:14 +02:00
588a82426e Merge pull request #719 from ae-utbm/page-fix
Fix markdown style for code
2024-07-18 15:08:21 +02:00
8245ddf2a6 fix font for code blocks in markdown 2024-07-18 14:51:50 +02:00
775a0c6478 Merge pull request #717 from ae-utbm/logo-24
Add promo 24 logo
2024-07-16 00:35:43 +02:00
Sli
bad67a8b65 Add promo 24 logo 2024-07-16 00:25:50 +02:00
Sli
7e98e184a0 Improve tests 2024-07-11 13:23:24 +02:00
Sli
6240eff160 Apply review suggestions 2024-07-11 13:23:24 +02:00
Sli
a8918ebe86 Fix forum topic creation 2024-07-11 13:23:24 +02:00
b852176958 Merge pull request #714 from ae-utbm/taiste
More ruff rules, mistune update and more bot-blocking features
2024-07-11 11:47:45 +02:00
c9e398b7ec Merge pull request #715 from ae-utbm/master 2024-07-11 11:33:28 +02:00
e84d5626df Merge pull request #711 from ae-utbm/bot-filtering
Implement mechanisms to block bots on authentication views
2024-07-11 11:00:10 +02:00
Sli
0fb61938ce Reorganize honeypot settings 2024-07-11 10:49:08 +02:00
Sli
d6b27f2f21 Make honeypot errors less suspicious 2024-07-10 19:30:01 +02:00
e15bcfae07 Send an email when creating an account via POST /register 2024-07-10 17:21:07 +02:00
Sli
72cf5a3d5e Introduce honeypot for login/registering/password changing 2024-07-10 14:51:39 +02:00
7de2e00c94 Merge pull request #701 from ae-utbm/dependabot/pip/taiste/ruff-0.5.1
[UPDATE] Bump ruff from 0.4.10 to 0.5.1
2024-07-10 14:15:38 +02:00
efe5d75798 update ruff config
Co-authored-by: Bartuccio Antoine <klmp200@users.noreply.github.com>
2024-07-10 10:52:30 +02:00
9f1eedbe1b [UPDATE] Bump ruff from 0.4.10 to 0.5.1
Bumps [ruff](https://github.com/astral-sh/ruff) from 0.4.10 to 0.5.1.
- [Release notes](https://github.com/astral-sh/ruff/releases)
- [Changelog](https://github.com/astral-sh/ruff/blob/main/CHANGELOG.md)
- [Commits](https://github.com/astral-sh/ruff/compare/v0.4.10...0.5.1)

---
updated-dependencies:
- dependency-name: ruff
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-07-08 15:30:54 +00:00
7fe495179f Merge pull request #707 from ae-utbm/update-mistune
Update mistune (0.8 => 3.0)
2024-07-08 17:29:13 +02:00
7f6c4f6236 change sup and sub in mde editor 2024-07-08 17:04:18 +02:00
30948f1701 better style for rendered markdown 2024-07-08 15:56:38 +02:00
02ec3607b2 fix install_xapian.sh 2024-07-08 15:56:37 +02:00
3c2dcfbfa2 update mistune 2024-07-08 15:56:37 +02:00
8bcf59aaf0 Merge pull request #706 from ae-utbm/ruff-rules
Add more ruff rules
2024-07-08 15:42:22 +02:00
c6d2ac9100 ruff rule B 2024-07-08 15:37:10 +02:00
2ac578c3ad ruff rule DJ
Co-authored-by: Bartuccio Antoine <klmp200@users.noreply.github.com>
2024-07-08 15:37:10 +02:00
f941435232 ruff rule C4 2024-07-08 15:37:10 +02:00
171a1cb876 ruff rule FBT 2024-07-08 15:37:09 +02:00
cfc19434d0 ruff rules UP008 and UP009 2024-07-08 15:37:09 +02:00
688871a680 ruff rule A 2024-07-08 15:37:08 +02:00
44c8558aa3 Merge pull request #704 from ae-utbm/taiste
Mises à jour (django 4.2, Pillow 10, cryptography 42), changement de la CI et enlèvement de l'offre Eurockéennes
2024-07-08 11:16:39 +02:00
6b923d2310 Merge pull request #700 from ae-utbm/remove-eurocks
Remove eurocks
2024-07-08 10:11:09 +02:00
09e0b31bc9 remove Eurockéennes link 2024-07-08 10:03:27 +02:00
eb2454eded Merge branch 'master' into taiste
# Conflicts:
#	eboutic/templates/eboutic/eboutic_main.jinja
#	locale/fr/LC_MESSAGES/django.po
2024-07-08 10:01:37 +02:00
3014d8cead Merge pull request #698 from ae-utbm/update-cryptography
update cryptography
2024-07-05 15:21:42 +02:00
70fdc2edf2 update cryptography 2024-07-05 14:02:01 +02:00
e47f29aa38 Merge pull request #697 from ae-utbm/update-pillow
update pillow (9.5 => 10.4)
2024-07-05 13:53:47 +02:00
d811896e21 update pillow 2024-07-05 13:14:58 +02:00
5c999b6ef1 Merge pull request #696 from ae-utbm/fix-xapian-deploy
Fix missing xapian install step in deploy workflows
2024-07-05 10:28:41 +02:00
79a6d9e771 Merge pull request #693 from ae-utbm/faster-tests
faster tests
2024-07-05 10:27:30 +02:00
Sli
e1cf1c786d Fix missing xapian install step in deploy workflows 2024-07-04 19:44:22 +02:00
71fe9559b1 parallelize the CI 2024-07-04 14:44:28 +02:00
f1fa8d34bf fix family relations in generate_galaxy_test_data.py 2024-07-04 14:39:12 +02:00
aa07fa9207 faster tests 2024-07-04 14:03:19 +02:00
47fec973bc Merge pull request #691 from ae-utbm/update-django
Update django (3.2 => 4.2)
2024-07-04 12:40:23 +02:00
ea8247aa16 fix broken translations 2024-07-04 11:31:36 +02:00
bf18284450 apply forgotten migrations 2024-07-04 11:31:36 +02:00
cd58d5a357 resolve warnings 2024-07-04 11:31:35 +02:00
75bb3f992c fix: wrong logic in Club.delete() 2024-07-04 11:31:35 +02:00
ae1fcdb8c0 fix: CashRegisterSummaryItem.check overriding a django method 2024-07-04 11:20:24 +02:00
507080f75e update django to 4.2 2024-07-03 15:11:06 +02:00
5bcf043d97 Merge pull request #683 from ae-utbm/pre-commits
Use pre-commits hooks instead of ruff directly
2024-07-03 10:04:53 +02:00
Sli
99605b98d4 Two steps pre-commit and better workflow output 2024-07-02 20:16:02 +02:00
6dfd43a8da [UPDATE] Bump reportlab from 4.2.0 to 4.2.2
Bumps [reportlab](https://www.reportlab.com/) from 4.2.0 to 4.2.2.

---
updated-dependencies:
- dependency-name: reportlab
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-07-02 11:26:16 +02:00
Sli
c7135875b8 Use pre-commits hooks instead of ruff directly 2024-07-01 17:36:19 +02:00
e29e1101cd [UPDATE] Bump ipython from 7.34.0 to 8.26.0
Bumps [ipython](https://github.com/ipython/ipython) from 7.34.0 to 8.26.0.
- [Release notes](https://github.com/ipython/ipython/releases)
- [Commits](https://github.com/ipython/ipython/compare/7.34.0...8.26.0)

---
updated-dependencies:
- dependency-name: ipython
  dependency-type: direct:development
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-07-01 14:00:43 +02:00
d97602e60b Use pytest for tests (#681)
* use pytest for tests

Eh ouais, il y a que la config qui change. Pytest est implémentable par étapes. Et ça c'est beau.

* rework tests with pytest

* remove unittest custom TestRunner

* Edit doc and CI
2024-06-26 19:10:24 +02:00
a5cbac1f97 Merge pull request #680 from ae-utbm/ruff
Introduct Ruff as formater and linter
2024-06-26 14:11:26 +02:00
3143d3d91a reorganize imports with ruff 2024-06-26 12:35:38 +02:00
9bdf3fc4ac use ruff for formating
Co-authored-by: Bartuccio Antoine <klmp200@users.noreply.github.com>
2024-06-26 12:35:14 +02:00
e06bc7dba3 reorganize pyproject.toml 2024-06-26 12:33:35 +02:00
a8b9f38000 Merge pull request #679 from ae-utbm/xapian-from-sources
Xapian from sources and fix CVE
2024-06-26 11:48:31 +02:00
Sli
ca27b89a8b Apply shellcheck on install_xapian.sh 2024-06-26 11:31:39 +02:00
Sli
e1bf7caa9a Fix CVE-2023-31047 2024-06-24 13:27:22 +02:00
Sli
e681c17a0f Adapt CI to new xapian install process 2024-06-24 13:26:58 +02:00
Sli
5416d88c97 Upgrade dependencies and install xapian from sources 2024-06-24 13:26:58 +02:00
0dca152436 Hotfix (typo) 2024-05-28 21:25:30 +02:00
9ce7abd31d Partnership Eurockéennes 2024 2024-05-28 19:10:17 +02:00
f41ff281fb Remove eurocks tickets from eboutic (event is finished) 2023-10-10 15:50:35 +02:00
ee437649f0 Revert "Merge branch 'master' into taiste"
This reverts commit 4303d51c0a, reversing
changes made to d16bf12611.
2023-10-10 15:47:02 +02:00
321cb72ca8 October 2023 update (#672)
* integration of 3D secure v2 for eboutic bank payment

* edit yml to avoid git conflict when deploying on test

* escape html characters on xml (#505)

* Change country id to ISO 3166 1 numeric for 3DSV2 (#510)

* remove useless tests

* Fix le panier de l'Eboutic pour Safari (#518)

Co-authored-by: Théo DURR <git@theodurr.fr>
Co-authored-by: thomas girod <56346771+imperosol@users.noreply.github.com>

* update some dependencies (#523)

* [Eboutic] Fix double quote issue & improved user experience on small screen (#522)

* Fix #511 Regex issue with escaped double quotes

* Fix basket being when reloading the page (when cookie != "")

+ Added JSDoc
+ Cleaned some code

* Fix #509 Improved user experience on small screens

* Fix css class not being added back when reloading page

* CSS Fixes (see description)

+ Fixed overlaping item title with the cart emoji on small screen
+ Fixed minimal size of the basket on small screen (full width)

* Added darkened background circle to items with no image

* Fix issue were the basket could be None


* Edited CSS to have bette img ratio & the 🛒 icon

Adapt, Improve, Overcome

* Moved basket down on small screen size

* enhance admin pages

* update documentation

* Update doc/about/tech.rst

Co-authored-by: Julien Constant <49886317+Juknum@users.noreply.github.com>

* remove csrf_token

* Fix 3DSv2 implementation (#542)

* Fixed wrong HMAC signature generation

* Fix xml du panier

Co-authored-by: Julien Constant <julienconstant190@gmail.com>

* [FIX] 3DSv2 - Echappement du XML et modif tables (#543)

* Fixed wrong HMAC signature generation
* Updated migration files

Co-authored-by: Julien Constant <julienconstant190@gmail.com>

* Update doc/about/tech.rst

* Update doc/start/install.rst

* Updated lock file according to pyproject

* unify account_id creation

* upgrade re_path to path (#533)

* redirect directly on counter if user is barman

* Passage de vue à Alpine pour les comptoirs (#561)

Vue, c'est cool, mais avec Django c'est un peu chiant à utiliser. Alpine a l'avantage d'être plus léger et d'avoir une syntaxe qui ne ressemble pas à celle de Jinja (ce qui évite d'avoir à mettre des {% raw %} partout).

* resolved importError (#565)

* Add galaxy (#562)

* style.scss: lint

* style.scss: add 'th' padding

* core: populate: add much more data for development

* Add galaxy

* repair user merging tool (#498)

* Disabled galaxy feature (only visually)

* Disabled Galaxy button & Removed 404 exception display

* Update 404.jinja

* Fixed broken test

* Added eurocks links to eboutic

* fix typo

* fix wording

Co-authored-by: Théo DURR <git@theodurr.fr>

* Edited unit tests

This test caused a breach in security due to the alert block displaying sensitive data.

* Repair NaN bug for autocomplete on counter click

* remove-useless-queries-counter-stats (#519)

* Amélioration des pages utilisateurs pour les petits écrans (#578, #520)

- Refonte de l'organisation des pages utilisateurs (principalement du front)
  - Page des parrains/fillots
  - Page d'édition du profil
  - Page du profil
  - Page des outils
  - Page des préférences
  - Page des stats utilisateurs

- Refonte du CSS / organisation de la navbar principale (en haut de l'écran)
- Refonte du CSS de la navbar bleu clair (le menu)
- Refonte du CSS du SAS :
  - Page de photo
  - Page d'albums

* Added GA/Clubs Google Calendar to main page (#585)

* Added GA/Clubs google calendar to main page

* Made tables full width

* Create dependabot.yml (#587)

* Bump django from 3.2.16 to 3.2.18 (#574)

* [CSS] Follow up of #578 (#589)

* [FIX] Broken link in readme and license fix (& update) (#591)

* Fixes pour la mise à jour de mars (#598)

* Fix problème de cache dans le SAS & améliore le CSS du SAS

Co-authored-by: Bartuccio Antoine <klmp200@users.noreply.github.com>

* Fixes & améliorations du nouveau CSS (#616)

* [UPDATE] Bump sentry-sdk from 1.12.1 to 1.19.1 (#620)

* [FIX] Fixes supplémentaires pour la màj de mars (#622)

- Les photos de l'onglet de la page utilisateur utilise désormais leur version thumbnail au lieu de leur version HD
- Une des classes du CSS du SAS a été renommée car elle empiétait sur une class de la navbar
- Le profil utilisateur a été revu pour ajouter plus d'espacement entre le tableau des cotisations et le numéro de cotisants
- Les images de forum & blouse sont de nouveau cliquable pour les afficher en grands
- Sur mobile, lorsqu'on cliquait sur le premier élément de la navbar, ce dernier avait un overlay avec des angles arrondis
- Sur mobile, les utilisateurs avec des images de profils non carrées dépassait dans l'onglet Famille

* [UPDATE] Bump dict2xml from 1.7.2 to 1.7.3 (#592)

Bumps [dict2xml](https://github.com/delfick/python-dict2xml) from 1.7.2 to 1.7.3.
- [Release notes](https://github.com/delfick/python-dict2xml/releases)
- [Commits](https://github.com/delfick/python-dict2xml/compare/release-1.7.2...release-1.7.3)

---
updated-dependencies:
- dependency-name: dict2xml
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

* [UPDATE] Bump django-debug-toolbar from 3.8.1 to 4.0.0 (#593)

Bumps [django-debug-toolbar](https://github.com/jazzband/django-debug-toolbar) from 3.8.1 to 4.0.0.
- [Release notes](https://github.com/jazzband/django-debug-toolbar/releases)
- [Changelog](https://github.com/jazzband/django-debug-toolbar/blob/main/docs/changes.rst)
- [Commits](https://github.com/jazzband/django-debug-toolbar/compare/3.8.1...4.0.0)

---
updated-dependencies:
- dependency-name: django-debug-toolbar
  dependency-type: direct:development
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* [UPDATE] Bump cryptography from 37.0.4 to 40.0.1 (#594)

* [UPDATE] Bump cryptography from 37.0.4 to 40.0.1

Bumps [cryptography](https://github.com/pyca/cryptography) from 37.0.4 to 40.0.1.
- [Release notes](https://github.com/pyca/cryptography/releases)
- [Changelog](https://github.com/pyca/cryptography/blob/main/CHANGELOG.rst)
- [Commits](https://github.com/pyca/cryptography/compare/37.0.4...40.0.1)

---
updated-dependencies:
- dependency-name: cryptography
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>

* Updated pyOpenSSL to match cryptography requirements

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: Julien Constant <julienconstant190@gmail.com>

* Mise à jour de Black vers la version 23.3 (#629)

* update link for poetry install

* [UPDATE] Bump django-countries from 7.5 to 7.5.1 (#624)

Bumps [django-countries](https://github.com/SmileyChris/django-countries) from 7.5 to 7.5.1.
- [Release notes](https://github.com/SmileyChris/django-countries/releases)
- [Changelog](https://github.com/SmileyChris/django-countries/blob/main/CHANGES.rst)
- [Commits](https://github.com/SmileyChris/django-countries/compare/v7.5...v7.5.1)

---
updated-dependencies:
- dependency-name: django-countries
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* [UPDATE] Bump sentry-sdk from 1.19.1 to 1.21.0

Bumps [sentry-sdk](https://github.com/getsentry/sentry-python) from 1.19.1 to 1.21.0.
- [Release notes](https://github.com/getsentry/sentry-python/releases)
- [Changelog](https://github.com/getsentry/sentry-python/blob/master/CHANGELOG.md)
- [Commits](https://github.com/getsentry/sentry-python/compare/1.19.1...1.21.0)

---
updated-dependencies:
- dependency-name: sentry-sdk
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

* Speed up tests (#638)

* Better usage of cache for groups and clubs related operations (#634)

* Better usage of cache for group retrieval

* Cache clearing on object deletion or update

* replace signals by save and delete override

* add is_anonymous check in is_owned_by

Add in many is_owned_by(self, user) methods that user is not anonymous. Since many of those functions do db queries, this should reduce a little bit the load of the db.

* Stricter usage of User.is_in_group

Constrain the parameters that can be passed to the function to make sure only a str or an int can be used. Also force to explicitly specify if the group id or the group name is used.

* write test and correct bugs

* remove forgotten populate commands

* Correct test

* [FIX] Correction de bugs (#617)

* Fix #600

* Fix #602

* Fixes & améliorations du nouveau CSS (#616)

* Fix #604

* should fix #605

* Fix #608

* Update core/views/site.py

Co-Authored-By: thomas girod <56346771+imperosol@users.noreply.github.com>

* Added back the permission denied

* Should fix #609

* Fix failing test when 2 user are merged

* Should fix #610

* Should fix #627

* Should fix #109

Block les URLs suivantes lorsque le fichier se trouve dans le dir `profiles` ou `SAS` :
- `/file/<id>/`
- `/file/<id>/[delete|prop|edit]`

> Les urls du SAS restent accessiblent pour les roots & les admins SAS
> Les urls de profiles sont uniquement accessiblent aux roots

* Fix root dir of SAS being unnaccessible for sas admins

⚠️ need to edit the SAS directory & save it (no changes required in sas directory properties)

* Remove overwritten code

* Should fix duplicated albums in user profile (wtf)

* Fix typo

* Extended profiles picture access to board members

* Should fix #607

* Fix keyboard navigation not working properly

* Fix user tagged pictures section inside python rather than in the template

* Update utils.py

* Apply suggested changes

* Fix #604

* Fix #608

* Added back the permission denied

* Should fix duplicated albums in user profile (wtf)

* Fix user tagged pictures section inside python rather than in the template

* Apply suggested changes

---------

Co-authored-by: thomas girod <56346771+imperosol@users.noreply.github.com>

* Remove duplicated css

* Galaxy improvements (#628)

* galaxy: improve logging and performance reporting

* galaxy: add a full galaxy state test

* galaxy: optimize user self score computation

* galaxy: add 'generate_galaxy_test_data' command for development at scale

* galaxy: big refactor

Main changes:
  - Multiple Galaxy objects can now exist at the same time in DB. This allows for ruling a new galaxy while still
    displaying the old one.
  - The criteria to quickly know whether a user is a possible citizen is now a simple query on picture count. This
    avoids a very complicated query to database, that could often result in huge working memory load. With this change,
    it should be possible to run the galaxy even on a vanilla Postgres that didn't receive fine tuning for the Sith's
    galaxy.

* galaxy: template: make the galaxy graph work and be usable with a lot of stars

- Display focused star and its connections clearly
- Display star label faintly by default for other stars to avoid overloading the graph
- Hide non-focused lanes
- Avoid clicks on non-highlighted, too far stars
- Make the canva adapt its width to initial screen size, doesn't work dynamically

* galaxy: better docstrings

* galaxy: use bulk_create whenever possible

This is a big performance gain, especially for the tests.

Examples:

----

`./manage.py test galaxy.tests.GalaxyTest.test_full_galaxy_state`

Measurements averaged over 3 run on *my machine*™:
Before: 2min15s
After: 1m41s

----

`./manage.py generate_galaxy_test_data --user-pack-count 1`

Before: 48s
After: 25s

----

`./manage.py rule_galaxy` (for 600 citizen, corresponding to 1 user-pack)

Before: 14m4s
After: 12m34s

* core: populate: use a less ambiguous 'timezone.now()'

When running the tests around midnight, the day is changing, leading to some values being offset to the next day
depending on the timezone, and making some tests to fail. This ensure to use a less ambiguous `now` when populating
the database.

* write more extensive documentation

- add documentation to previously documented classes and functions and refactor some of the documented one, in accordance to the PEP257 and ReStructuredText standards ;
- add some type hints ;
- use a NamedTuple for the `Galaxy.compute_users_score` method instead of a raw tuple. Also change a little bit the logic in the function which call the latter ;
- add some additional parameter checks on a few functions ;
- change a little bit the logic of the log level setting for the galaxy related commands.

* galaxy: tests: split Model and View for more efficient data usage

---------

Co-authored-by: maréchal <thgirod@hotmail.com>

* [UPDATE] Bump libsass from 0.21.0 to 0.22.0 (#640)

Bumps [libsass](https://github.com/sass/libsass-python) from 0.21.0 to 0.22.0.
- [Release notes](https://github.com/sass/libsass-python/releases)
- [Changelog](https://github.com/sass/libsass-python/blob/main/docs/changes.rst)
- [Commits](https://github.com/sass/libsass-python/compare/0.21.0...0.22.0)

---
updated-dependencies:
- dependency-name: libsass
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* [FIX] Fix cached groups (#647)

* Bump sqlparse from 0.4.3 to 0.4.4 (#645)

Bumps [sqlparse](https://github.com/andialbrecht/sqlparse) from 0.4.3 to 0.4.4.
- [Release notes](https://github.com/andialbrecht/sqlparse/releases)
- [Changelog](https://github.com/andialbrecht/sqlparse/blob/master/CHANGELOG)
- [Commits](https://github.com/andialbrecht/sqlparse/compare/0.4.3...0.4.4)

---
updated-dependencies:
- dependency-name: sqlparse
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* [UPDATE] Bump django-ordered-model from 3.6 to 3.7.4 (#625)

Bumps [django-ordered-model](https://github.com/django-ordered-model/django-ordered-model) from 3.6 to 3.7.4.
- [Release notes](https://github.com/django-ordered-model/django-ordered-model/releases)
- [Changelog](https://github.com/django-ordered-model/django-ordered-model/blob/master/CHANGES.md)
- [Commits](https://github.com/django-ordered-model/django-ordered-model/compare/3.6...3.7.4)

---
updated-dependencies:
- dependency-name: django-ordered-model
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* Fix immutable default variable in `get_start_of_semester` (#656)

Le serveur ne percevait pas le changement de semestre, parce
que la valeur par défaut passée à la fonction `get_start_of_semester()` était une fonction appelée une seule fois, lors du lancement du serveur. Bref, c'était ça : https://beta.ruff.rs/docs/rules/function-call-in-default-argument/

---------

Co-authored-by: imperosol <thgirod@hotmail.com>

* Add missing method on AnonymousUser (#649)

* Add eurocks partnership in the eboutic (#661)

* Add eurocks partnership in the eboutic (#661)

Revert "Add eurocks partnership in the eboutic (#661)"

This reverts commit 193c820757.

Add eurocks partnership in the eboutic (#661)

* Update workflow

Following this update : https://github.blog/changelog/2023-09-13-github-actions-updates-to-github_ref-and-github-ref/

* Update workflow

* Remove eurocks tickets from eboutic (event is finished)

* Links update & translations typos fixes (#671)

* Remove BDF link (as BDF is now part of AE)

* Remove unused pages

* Fix typos

* Fix typo again

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: Thomas Girod <thgirod@hotmail.com>
Co-authored-by: thomas girod <56346771+imperosol@users.noreply.github.com>
Co-authored-by: Théo DURR <git@theodurr.fr>
Co-authored-by: Skia <skia@hya.sk>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Bartuccio Antoine <klmp200@users.noreply.github.com>
2023-10-10 15:41:19 +02:00
4303d51c0a Merge branch 'master' into taiste 2023-10-10 15:32:46 +02:00
d16bf12611 Links update & translations typos fixes (#671)
* Remove BDF link (as BDF is now part of AE)

* Remove unused pages

* Fix typos

* Fix typo again
2023-10-10 15:29:02 +02:00
4231a7972d Remove eurocks tickets from eboutic (event is finished) 2023-10-04 14:27:21 +02:00
c436d39014 [PARTENARIAT] Partenariat Eurockéennes (#663) 2023-09-20 17:57:26 +02:00
51a12814f9 Update workflow 2023-09-19 22:17:26 +02:00
00ae6e4623 Update workflow
Following this update : https://github.blog/changelog/2023-09-13-github-actions-updates-to-github_ref-and-github-ref/
2023-09-19 22:04:46 +02:00
4b587e8711 Merge branch 'taiste' of https://github.com/ae-utbm/sith3 into taiste 2023-09-19 21:31:02 +02:00
d2f377b54f Add eurocks partnership in the eboutic (#661)
Revert "Add eurocks partnership in the eboutic (#661)"

This reverts commit 193c820757.

Add eurocks partnership in the eboutic (#661)
2023-09-19 21:29:17 +02:00
193c820757 Add eurocks partnership in the eboutic (#661) 2023-09-19 20:59:22 +02:00
b9298792ae Mise à jour de septembre 2023 (#659)
* integration of 3D secure v2 for eboutic bank payment

* edit yml to avoid git conflict when deploying on test

* escape html characters on xml (#505)

* Change country id to ISO 3166 1 numeric for 3DSV2 (#510)

* remove useless tests

* Fix le panier de l'Eboutic pour Safari (#518)

Co-authored-by: Théo DURR <git@theodurr.fr>
Co-authored-by: thomas girod <56346771+imperosol@users.noreply.github.com>

* update some dependencies (#523)

* [Eboutic] Fix double quote issue & improved user experience on small screen (#522)

* Fix #511 Regex issue with escaped double quotes

* Fix basket being when reloading the page (when cookie != "")

+ Added JSDoc
+ Cleaned some code

* Fix #509 Improved user experience on small screens

* Fix css class not being added back when reloading page

* CSS Fixes (see description)

+ Fixed overlaping item title with the cart emoji on small screen
+ Fixed minimal size of the basket on small screen (full width)

* Added darkened background circle to items with no image

* Fix issue were the basket could be None


* Edited CSS to have bette img ratio & the 🛒 icon

Adapt, Improve, Overcome

* Moved basket down on small screen size

* enhance admin pages

* update documentation

* Update doc/about/tech.rst

Co-authored-by: Julien Constant <49886317+Juknum@users.noreply.github.com>

* remove csrf_token

* Fix 3DSv2 implementation (#542)

* Fixed wrong HMAC signature generation

* Fix xml du panier

Co-authored-by: Julien Constant <julienconstant190@gmail.com>

* [FIX] 3DSv2 - Echappement du XML et modif tables (#543)

* Fixed wrong HMAC signature generation
* Updated migration files

Co-authored-by: Julien Constant <julienconstant190@gmail.com>

* Update doc/about/tech.rst

* Update doc/start/install.rst

* Updated lock file according to pyproject

* unify account_id creation

* upgrade re_path to path (#533)

* redirect directly on counter if user is barman

* Passage de vue à Alpine pour les comptoirs (#561)

Vue, c'est cool, mais avec Django c'est un peu chiant à utiliser. Alpine a l'avantage d'être plus léger et d'avoir une syntaxe qui ne ressemble pas à celle de Jinja (ce qui évite d'avoir à mettre des {% raw %} partout).

* resolved importError (#565)

* Add galaxy (#562)

* style.scss: lint

* style.scss: add 'th' padding

* core: populate: add much more data for development

* Add galaxy

* repair user merging tool (#498)

* Disabled galaxy feature (only visually)

* Disabled Galaxy button & Removed 404 exception display

* Update 404.jinja

* Fixed broken test

* Added eurocks links to eboutic

* fix typo

* fix wording

Co-authored-by: Théo DURR <git@theodurr.fr>

* Edited unit tests

This test caused a breach in security due to the alert block displaying sensitive data.

* Repair NaN bug for autocomplete on counter click

* remove-useless-queries-counter-stats (#519)

* Amélioration des pages utilisateurs pour les petits écrans (#578, #520)

- Refonte de l'organisation des pages utilisateurs (principalement du front)
  - Page des parrains/fillots
  - Page d'édition du profil
  - Page du profil
  - Page des outils
  - Page des préférences
  - Page des stats utilisateurs

- Refonte du CSS / organisation de la navbar principale (en haut de l'écran)
- Refonte du CSS de la navbar bleu clair (le menu)
- Refonte du CSS du SAS :
  - Page de photo
  - Page d'albums

* Added GA/Clubs Google Calendar to main page (#585)

* Added GA/Clubs google calendar to main page

* Made tables full width

* Create dependabot.yml (#587)

* Bump django from 3.2.16 to 3.2.18 (#574)

* [CSS] Follow up of #578 (#589)

* [FIX] Broken link in readme and license fix (& update) (#591)

* Fixes pour la mise à jour de mars (#598)

* Fix problème de cache dans le SAS & améliore le CSS du SAS

Co-authored-by: Bartuccio Antoine <klmp200@users.noreply.github.com>

* Fixes & améliorations du nouveau CSS (#616)

* [UPDATE] Bump sentry-sdk from 1.12.1 to 1.19.1 (#620)

* [FIX] Fixes supplémentaires pour la màj de mars (#622)

- Les photos de l'onglet de la page utilisateur utilise désormais leur version thumbnail au lieu de leur version HD
- Une des classes du CSS du SAS a été renommée car elle empiétait sur une class de la navbar
- Le profil utilisateur a été revu pour ajouter plus d'espacement entre le tableau des cotisations et le numéro de cotisants
- Les images de forum & blouse sont de nouveau cliquable pour les afficher en grands
- Sur mobile, lorsqu'on cliquait sur le premier élément de la navbar, ce dernier avait un overlay avec des angles arrondis
- Sur mobile, les utilisateurs avec des images de profils non carrées dépassait dans l'onglet Famille

* [UPDATE] Bump dict2xml from 1.7.2 to 1.7.3 (#592)

Bumps [dict2xml](https://github.com/delfick/python-dict2xml) from 1.7.2 to 1.7.3.
- [Release notes](https://github.com/delfick/python-dict2xml/releases)
- [Commits](https://github.com/delfick/python-dict2xml/compare/release-1.7.2...release-1.7.3)

---
updated-dependencies:
- dependency-name: dict2xml
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

* [UPDATE] Bump django-debug-toolbar from 3.8.1 to 4.0.0 (#593)

Bumps [django-debug-toolbar](https://github.com/jazzband/django-debug-toolbar) from 3.8.1 to 4.0.0.
- [Release notes](https://github.com/jazzband/django-debug-toolbar/releases)
- [Changelog](https://github.com/jazzband/django-debug-toolbar/blob/main/docs/changes.rst)
- [Commits](https://github.com/jazzband/django-debug-toolbar/compare/3.8.1...4.0.0)

---
updated-dependencies:
- dependency-name: django-debug-toolbar
  dependency-type: direct:development
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* [UPDATE] Bump cryptography from 37.0.4 to 40.0.1 (#594)

* [UPDATE] Bump cryptography from 37.0.4 to 40.0.1

Bumps [cryptography](https://github.com/pyca/cryptography) from 37.0.4 to 40.0.1.
- [Release notes](https://github.com/pyca/cryptography/releases)
- [Changelog](https://github.com/pyca/cryptography/blob/main/CHANGELOG.rst)
- [Commits](https://github.com/pyca/cryptography/compare/37.0.4...40.0.1)

---
updated-dependencies:
- dependency-name: cryptography
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>

* Updated pyOpenSSL to match cryptography requirements

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: Julien Constant <julienconstant190@gmail.com>

* Mise à jour de Black vers la version 23.3 (#629)

* update link for poetry install

* [UPDATE] Bump django-countries from 7.5 to 7.5.1 (#624)

Bumps [django-countries](https://github.com/SmileyChris/django-countries) from 7.5 to 7.5.1.
- [Release notes](https://github.com/SmileyChris/django-countries/releases)
- [Changelog](https://github.com/SmileyChris/django-countries/blob/main/CHANGES.rst)
- [Commits](https://github.com/SmileyChris/django-countries/compare/v7.5...v7.5.1)

---
updated-dependencies:
- dependency-name: django-countries
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* [UPDATE] Bump sentry-sdk from 1.19.1 to 1.21.0

Bumps [sentry-sdk](https://github.com/getsentry/sentry-python) from 1.19.1 to 1.21.0.
- [Release notes](https://github.com/getsentry/sentry-python/releases)
- [Changelog](https://github.com/getsentry/sentry-python/blob/master/CHANGELOG.md)
- [Commits](https://github.com/getsentry/sentry-python/compare/1.19.1...1.21.0)

---
updated-dependencies:
- dependency-name: sentry-sdk
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

* Speed up tests (#638)

* Better usage of cache for groups and clubs related operations (#634)

* Better usage of cache for group retrieval

* Cache clearing on object deletion or update

* replace signals by save and delete override

* add is_anonymous check in is_owned_by

Add in many is_owned_by(self, user) methods that user is not anonymous. Since many of those functions do db queries, this should reduce a little bit the load of the db.

* Stricter usage of User.is_in_group

Constrain the parameters that can be passed to the function to make sure only a str or an int can be used. Also force to explicitly specify if the group id or the group name is used.

* write test and correct bugs

* remove forgotten populate commands

* Correct test

* [FIX] Correction de bugs (#617)

* Fix #600

* Fix #602

* Fixes & améliorations du nouveau CSS (#616)

* Fix #604

* should fix #605

* Fix #608

* Update core/views/site.py

Co-Authored-By: thomas girod <56346771+imperosol@users.noreply.github.com>

* Added back the permission denied

* Should fix #609

* Fix failing test when 2 user are merged

* Should fix #610

* Should fix #627

* Should fix #109

Block les URLs suivantes lorsque le fichier se trouve dans le dir `profiles` ou `SAS` :
- `/file/<id>/`
- `/file/<id>/[delete|prop|edit]`

> Les urls du SAS restent accessiblent pour les roots & les admins SAS
> Les urls de profiles sont uniquement accessiblent aux roots

* Fix root dir of SAS being unnaccessible for sas admins

⚠️ need to edit the SAS directory & save it (no changes required in sas directory properties)

* Remove overwritten code

* Should fix duplicated albums in user profile (wtf)

* Fix typo

* Extended profiles picture access to board members

* Should fix #607

* Fix keyboard navigation not working properly

* Fix user tagged pictures section inside python rather than in the template

* Update utils.py

* Apply suggested changes

* Fix #604

* Fix #608

* Added back the permission denied

* Should fix duplicated albums in user profile (wtf)

* Fix user tagged pictures section inside python rather than in the template

* Apply suggested changes

---------

Co-authored-by: thomas girod <56346771+imperosol@users.noreply.github.com>

* Remove duplicated css

* Galaxy improvements (#628)

* galaxy: improve logging and performance reporting

* galaxy: add a full galaxy state test

* galaxy: optimize user self score computation

* galaxy: add 'generate_galaxy_test_data' command for development at scale

* galaxy: big refactor

Main changes:
  - Multiple Galaxy objects can now exist at the same time in DB. This allows for ruling a new galaxy while still
    displaying the old one.
  - The criteria to quickly know whether a user is a possible citizen is now a simple query on picture count. This
    avoids a very complicated query to database, that could often result in huge working memory load. With this change,
    it should be possible to run the galaxy even on a vanilla Postgres that didn't receive fine tuning for the Sith's
    galaxy.

* galaxy: template: make the galaxy graph work and be usable with a lot of stars

- Display focused star and its connections clearly
- Display star label faintly by default for other stars to avoid overloading the graph
- Hide non-focused lanes
- Avoid clicks on non-highlighted, too far stars
- Make the canva adapt its width to initial screen size, doesn't work dynamically

* galaxy: better docstrings

* galaxy: use bulk_create whenever possible

This is a big performance gain, especially for the tests.

Examples:

----

`./manage.py test galaxy.tests.GalaxyTest.test_full_galaxy_state`

Measurements averaged over 3 run on *my machine*™:
Before: 2min15s
After: 1m41s

----

`./manage.py generate_galaxy_test_data --user-pack-count 1`

Before: 48s
After: 25s

----

`./manage.py rule_galaxy` (for 600 citizen, corresponding to 1 user-pack)

Before: 14m4s
After: 12m34s

* core: populate: use a less ambiguous 'timezone.now()'

When running the tests around midnight, the day is changing, leading to some values being offset to the next day
depending on the timezone, and making some tests to fail. This ensure to use a less ambiguous `now` when populating
the database.

* write more extensive documentation

- add documentation to previously documented classes and functions and refactor some of the documented one, in accordance to the PEP257 and ReStructuredText standards ;
- add some type hints ;
- use a NamedTuple for the `Galaxy.compute_users_score` method instead of a raw tuple. Also change a little bit the logic in the function which call the latter ;
- add some additional parameter checks on a few functions ;
- change a little bit the logic of the log level setting for the galaxy related commands.

* galaxy: tests: split Model and View for more efficient data usage

---------

Co-authored-by: maréchal <thgirod@hotmail.com>

* [UPDATE] Bump libsass from 0.21.0 to 0.22.0 (#640)

Bumps [libsass](https://github.com/sass/libsass-python) from 0.21.0 to 0.22.0.
- [Release notes](https://github.com/sass/libsass-python/releases)
- [Changelog](https://github.com/sass/libsass-python/blob/main/docs/changes.rst)
- [Commits](https://github.com/sass/libsass-python/compare/0.21.0...0.22.0)

---
updated-dependencies:
- dependency-name: libsass
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* [FIX] Fix cached groups (#647)

* Bump sqlparse from 0.4.3 to 0.4.4 (#645)

Bumps [sqlparse](https://github.com/andialbrecht/sqlparse) from 0.4.3 to 0.4.4.
- [Release notes](https://github.com/andialbrecht/sqlparse/releases)
- [Changelog](https://github.com/andialbrecht/sqlparse/blob/master/CHANGELOG)
- [Commits](https://github.com/andialbrecht/sqlparse/compare/0.4.3...0.4.4)

---
updated-dependencies:
- dependency-name: sqlparse
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* [UPDATE] Bump django-ordered-model from 3.6 to 3.7.4 (#625)

Bumps [django-ordered-model](https://github.com/django-ordered-model/django-ordered-model) from 3.6 to 3.7.4.
- [Release notes](https://github.com/django-ordered-model/django-ordered-model/releases)
- [Changelog](https://github.com/django-ordered-model/django-ordered-model/blob/master/CHANGES.md)
- [Commits](https://github.com/django-ordered-model/django-ordered-model/compare/3.6...3.7.4)

---
updated-dependencies:
- dependency-name: django-ordered-model
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* Fix immutable default variable in `get_start_of_semester` (#656)

Le serveur ne percevait pas le changement de semestre, parce
que la valeur par défaut passée à la fonction `get_start_of_semester()` était une fonction appelée une seule fois, lors du lancement du serveur. Bref, c'était ça : https://beta.ruff.rs/docs/rules/function-call-in-default-argument/

---------

Co-authored-by: imperosol <thgirod@hotmail.com>

* Add missing method on AnonymousUser (#649)

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: Thomas Girod <thgirod@hotmail.com>
Co-authored-by: thomas girod <56346771+imperosol@users.noreply.github.com>
Co-authored-by: Théo DURR <git@theodurr.fr>
Co-authored-by: Skia <skia@hya.sk>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Bartuccio Antoine <klmp200@users.noreply.github.com>
2023-09-09 13:09:13 +02:00
aaf30ab965 Add missing method on AnonymousUser (#649) 2023-09-07 23:53:42 +02:00
2db66e6154 Merge branch 'master' into taiste 2023-09-07 23:44:09 +02:00
38295e591d Fix immutable default variable in get_start_of_semester (#656)
Le serveur ne percevait pas le changement de semestre, parce
que la valeur par défaut passée à la fonction `get_start_of_semester()` était une fonction appelée une seule fois, lors du lancement du serveur. Bref, c'était ça : https://beta.ruff.rs/docs/rules/function-call-in-default-argument/

---------

Co-authored-by: imperosol <thgirod@hotmail.com>
2023-09-07 23:11:58 +02:00
544b0248b2 [UPDATE] Bump django-ordered-model from 3.6 to 3.7.4 (#625)
Bumps [django-ordered-model](https://github.com/django-ordered-model/django-ordered-model) from 3.6 to 3.7.4.
- [Release notes](https://github.com/django-ordered-model/django-ordered-model/releases)
- [Changelog](https://github.com/django-ordered-model/django-ordered-model/blob/master/CHANGES.md)
- [Commits](https://github.com/django-ordered-model/django-ordered-model/compare/3.6...3.7.4)

---
updated-dependencies:
- dependency-name: django-ordered-model
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-09-06 12:01:44 +02:00
2bccf633d5 Bump sqlparse from 0.4.3 to 0.4.4 (#645)
Bumps [sqlparse](https://github.com/andialbrecht/sqlparse) from 0.4.3 to 0.4.4.
- [Release notes](https://github.com/andialbrecht/sqlparse/releases)
- [Changelog](https://github.com/andialbrecht/sqlparse/blob/master/CHANGELOG)
- [Commits](https://github.com/andialbrecht/sqlparse/compare/0.4.3...0.4.4)

---
updated-dependencies:
- dependency-name: sqlparse
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-09-06 11:37:28 +02:00
4f9d5ae7b1 Revert "[PARTENARIAT] Ajout vitrine d'achat billets eurockéennes 2023 (#582)"
This reverts commit b12e8dc147.
2023-07-02 18:22:14 +02:00
259337dff1 [FIX] Fix cached groups (#647) 2023-05-12 13:29:16 +02:00
84768eb74e [FIX] Fix cached groups (#647) 2023-05-12 13:27:51 +02:00
8852ef990e [UPDATE] Bump libsass from 0.21.0 to 0.22.0 (#640)
Bumps [libsass](https://github.com/sass/libsass-python) from 0.21.0 to 0.22.0.
- [Release notes](https://github.com/sass/libsass-python/releases)
- [Changelog](https://github.com/sass/libsass-python/blob/main/docs/changes.rst)
- [Commits](https://github.com/sass/libsass-python/compare/0.21.0...0.22.0)

---
updated-dependencies:
- dependency-name: libsass
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-05-11 14:19:19 +02:00
87295ad9b7 Galaxy improvements (#628)
* galaxy: improve logging and performance reporting

* galaxy: add a full galaxy state test

* galaxy: optimize user self score computation

* galaxy: add 'generate_galaxy_test_data' command for development at scale

* galaxy: big refactor

Main changes:
  - Multiple Galaxy objects can now exist at the same time in DB. This allows for ruling a new galaxy while still
    displaying the old one.
  - The criteria to quickly know whether a user is a possible citizen is now a simple query on picture count. This
    avoids a very complicated query to database, that could often result in huge working memory load. With this change,
    it should be possible to run the galaxy even on a vanilla Postgres that didn't receive fine tuning for the Sith's
    galaxy.

* galaxy: template: make the galaxy graph work and be usable with a lot of stars

- Display focused star and its connections clearly
- Display star label faintly by default for other stars to avoid overloading the graph
- Hide non-focused lanes
- Avoid clicks on non-highlighted, too far stars
- Make the canva adapt its width to initial screen size, doesn't work dynamically

* galaxy: better docstrings

* galaxy: use bulk_create whenever possible

This is a big performance gain, especially for the tests.

Examples:

----

`./manage.py test galaxy.tests.GalaxyTest.test_full_galaxy_state`

Measurements averaged over 3 run on *my machine*™:
Before: 2min15s
After: 1m41s

----

`./manage.py generate_galaxy_test_data --user-pack-count 1`

Before: 48s
After: 25s

----

`./manage.py rule_galaxy` (for 600 citizen, corresponding to 1 user-pack)

Before: 14m4s
After: 12m34s

* core: populate: use a less ambiguous 'timezone.now()'

When running the tests around midnight, the day is changing, leading to some values being offset to the next day
depending on the timezone, and making some tests to fail. This ensure to use a less ambiguous `now` when populating
the database.

* write more extensive documentation

- add documentation to previously documented classes and functions and refactor some of the documented one, in accordance to the PEP257 and ReStructuredText standards ;
- add some type hints ;
- use a NamedTuple for the `Galaxy.compute_users_score` method instead of a raw tuple. Also change a little bit the logic in the function which call the latter ;
- add some additional parameter checks on a few functions ;
- change a little bit the logic of the log level setting for the galaxy related commands.

* galaxy: tests: split Model and View for more efficient data usage

---------

Co-authored-by: maréchal <thgirod@hotmail.com>
2023-05-10 12:47:02 +02:00
288764b551 Mise à jour d'avril (#643) 2023-05-10 11:56:33 +02:00
5ab5ef681c Remove duplicated css 2023-05-09 23:08:32 +02:00
c9e70889dd Merge branch 'master' into taiste 2023-05-09 22:37:49 +02:00
b30ee0a27a [FIX] Correction de bugs (#617)
* Fix #600

* Fix #602

* Fixes & améliorations du nouveau CSS (#616)

* Fix #604

* should fix #605

* Fix #608

* Update core/views/site.py

Co-Authored-By: thomas girod <56346771+imperosol@users.noreply.github.com>

* Added back the permission denied

* Should fix #609

* Fix failing test when 2 user are merged

* Should fix #610

* Should fix #627

* Should fix #109

Block les URLs suivantes lorsque le fichier se trouve dans le dir `profiles` ou `SAS` :
- `/file/<id>/`
- `/file/<id>/[delete|prop|edit]`

> Les urls du SAS restent accessiblent pour les roots & les admins SAS
> Les urls de profiles sont uniquement accessiblent aux roots

* Fix root dir of SAS being unnaccessible for sas admins

⚠️ need to edit the SAS directory & save it (no changes required in sas directory properties)

* Remove overwritten code

* Should fix duplicated albums in user profile (wtf)

* Fix typo

* Extended profiles picture access to board members

* Should fix #607

* Fix keyboard navigation not working properly

* Fix user tagged pictures section inside python rather than in the template

* Update utils.py

* Apply suggested changes

* Fix #604

* Fix #608

* Added back the permission denied

* Should fix duplicated albums in user profile (wtf)

* Fix user tagged pictures section inside python rather than in the template

* Apply suggested changes

---------

Co-authored-by: thomas girod <56346771+imperosol@users.noreply.github.com>
2023-05-02 13:07:36 +02:00
ef968f3673 Better usage of cache for groups and clubs related operations (#634)
* Better usage of cache for group retrieval

* Cache clearing on object deletion or update

* replace signals by save and delete override

* add is_anonymous check in is_owned_by

Add in many is_owned_by(self, user) methods that user is not anonymous. Since many of those functions do db queries, this should reduce a little bit the load of the db.

* Stricter usage of User.is_in_group

Constrain the parameters that can be passed to the function to make sure only a str or an int can be used. Also force to explicitly specify if the group id or the group name is used.

* write test and correct bugs

* remove forgotten populate commands

* Correct test
2023-05-02 12:36:59 +02:00
96dede5077 Speed up tests (#638) 2023-05-02 11:00:23 +02:00
66fcb76cb5 Merge pull request #635 from ae-utbm/dependabot/pip/taiste/sentry-sdk-1.21.0
[UPDATE] Bump sentry-sdk from 1.19.1 to 1.21.0
2023-04-26 22:17:13 +02:00
63c8e51137 [UPDATE] Bump sentry-sdk from 1.19.1 to 1.21.0
Bumps [sentry-sdk](https://github.com/getsentry/sentry-python) from 1.19.1 to 1.21.0.
- [Release notes](https://github.com/getsentry/sentry-python/releases)
- [Changelog](https://github.com/getsentry/sentry-python/blob/master/CHANGELOG.md)
- [Commits](https://github.com/getsentry/sentry-python/compare/1.19.1...1.21.0)

---
updated-dependencies:
- dependency-name: sentry-sdk
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-04-26 09:08:06 +00:00
12bec5c553 [UPDATE] Bump django-countries from 7.5 to 7.5.1 (#624)
Bumps [django-countries](https://github.com/SmileyChris/django-countries) from 7.5 to 7.5.1.
- [Release notes](https://github.com/SmileyChris/django-countries/releases)
- [Changelog](https://github.com/SmileyChris/django-countries/blob/main/CHANGES.rst)
- [Commits](https://github.com/SmileyChris/django-countries/compare/v7.5...v7.5.1)

---
updated-dependencies:
- dependency-name: django-countries
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-04-26 02:48:04 +02:00
08460a6964 update link for poetry install 2023-04-22 22:29:43 +02:00
b5a40cfda9 Mise à jour de Black vers la version 23.3 (#629) 2023-04-22 15:32:31 +02:00
c78953b036 [UPDATE] Bump cryptography from 37.0.4 to 40.0.1 (#594)
* [UPDATE] Bump cryptography from 37.0.4 to 40.0.1

Bumps [cryptography](https://github.com/pyca/cryptography) from 37.0.4 to 40.0.1.
- [Release notes](https://github.com/pyca/cryptography/releases)
- [Changelog](https://github.com/pyca/cryptography/blob/main/CHANGELOG.rst)
- [Commits](https://github.com/pyca/cryptography/compare/37.0.4...40.0.1)

---
updated-dependencies:
- dependency-name: cryptography
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>

* Updated pyOpenSSL to match cryptography requirements

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: Julien Constant <julienconstant190@gmail.com>
2023-04-20 14:43:46 +02:00
427f7ceaff [UPDATE] Bump django-debug-toolbar from 3.8.1 to 4.0.0 (#593)
Bumps [django-debug-toolbar](https://github.com/jazzband/django-debug-toolbar) from 3.8.1 to 4.0.0.
- [Release notes](https://github.com/jazzband/django-debug-toolbar/releases)
- [Changelog](https://github.com/jazzband/django-debug-toolbar/blob/main/docs/changes.rst)
- [Commits](https://github.com/jazzband/django-debug-toolbar/compare/3.8.1...4.0.0)

---
updated-dependencies:
- dependency-name: django-debug-toolbar
  dependency-type: direct:development
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-04-09 21:37:48 +02:00
1055385bcc [UPDATE] Bump dict2xml from 1.7.2 to 1.7.3 (#592)
Bumps [dict2xml](https://github.com/delfick/python-dict2xml) from 1.7.2 to 1.7.3.
- [Release notes](https://github.com/delfick/python-dict2xml/releases)
- [Commits](https://github.com/delfick/python-dict2xml/compare/release-1.7.2...release-1.7.3)

---
updated-dependencies:
- dependency-name: dict2xml
  dependency-type: direct:production
  update-type: version-update:semver-patch
...
2023-04-09 11:38:58 +02:00
c1022642a2 [FIX] Fixes supplémentaires pour la màj de mars (#622)
- Les photos de l'onglet de la page utilisateur utilise désormais leur version thumbnail au lieu de leur version HD
- Une des classes du CSS du SAS a été renommée car elle empiétait sur une class de la navbar
- Le profil utilisateur a été revu pour ajouter plus d'espacement entre le tableau des cotisations et le numéro de cotisants
- Les images de forum & blouse sont de nouveau cliquable pour les afficher en grands
- Sur mobile, lorsqu'on cliquait sur le premier élément de la navbar, ce dernier avait un overlay avec des angles arrondis
- Sur mobile, les utilisateurs avec des images de profils non carrées dépassait dans l'onglet Famille
2023-04-08 20:59:43 +02:00
910a6f8b34 [FIX] Fixes supplémentaires pour la màj de mars (#622)
- Les photos de l'onglet de la page utilisateur utilise désormais leur version thumbnail au lieu de leur version HD
- Une des classes du CSS du SAS a été renommée car elle empiétait sur une class de la navbar
- Le profil utilisateur a été revu pour ajouter plus d'espacement entre le tableau des cotisations et le numéro de cotisants
- Les images de forum & blouse sont de nouveau cliquable pour les afficher en grands
- Sur mobile, lorsqu'on cliquait sur le premier élément de la navbar, ce dernier avait un overlay avec des angles arrondis
- Sur mobile, les utilisateurs avec des images de profils non carrées dépassait dans l'onglet Famille
2023-04-08 20:58:55 +02:00
06253f029c [UPDATE] Bump sentry-sdk from 1.12.1 to 1.19.1 (#620) 2023-04-08 18:50:31 +00:00
fa6527b24f [FIX] Deuxième vague de fixes pour la mise à jour de mars (#619) 2023-04-06 16:09:29 +02:00
0501e6417a Merge branch 'master' into taiste 2023-04-05 20:02:12 +02:00
a198f5252d Fixes & améliorations du nouveau CSS (#616) 2023-04-05 18:03:43 +02:00
d83842af27 Fix problème de cache dans le SAS & améliore le CSS du SAS
Co-authored-by: Bartuccio Antoine <klmp200@users.noreply.github.com>
2023-04-05 14:32:32 +02:00
f605f7dcc6 Fixes pour la mise à jour de mars (#598) 2023-04-04 22:55:26 +02:00
e638bc04ed Fixes pour la mise à jour de mars (#598) 2023-04-04 22:50:19 +02:00
4830c3ea2d Mise à jour de mars (#586)
---------

Co-authored-by: Thomas Girod <thgirod@hotmail.com>
Co-authored-by: Théo DURR <git@theodurr.fr>
2023-04-04 19:17:44 +02:00
8e7c025e47 [FIX] Broken link in readme and license fix (& update) (#591) 2023-04-04 18:39:45 +02:00
1bfe929ab3 [CSS] Follow up of #578 (#589) 2023-04-04 15:21:09 +02:00
93cc2c883e Bump django from 3.2.16 to 3.2.18 (#574) 2023-04-04 10:16:55 +02:00
44290a20a6 Create dependabot.yml (#587) 2023-04-03 17:18:16 +02:00
1f10a284f2 Added GA/Clubs Google Calendar to main page (#585)
* Added GA/Clubs google calendar to main page

* Made tables full width
2023-04-03 15:54:12 +02:00
28f397574f Amélioration des pages utilisateurs pour les petits écrans (#578, #520)
- Refonte de l'organisation des pages utilisateurs (principalement du front)
  - Page des parrains/fillots
  - Page d'édition du profil
  - Page du profil
  - Page des outils
  - Page des préférences
  - Page des stats utilisateurs

- Refonte du CSS / organisation de la navbar principale (en haut de l'écran)
- Refonte du CSS de la navbar bleu clair (le menu)
- Refonte du CSS du SAS :
  - Page de photo
  - Page d'albums
2023-03-30 14:38:40 +02:00
6c1fa6de0b remove-useless-queries-counter-stats (#519) 2023-03-24 15:32:05 +01:00
f0a08afd31 Merge branch 'repair_NaN_bug_on_click' into taiste 2023-03-10 10:50:26 +01:00
982fc09908 Repair NaN bug for autocomplete on counter click (#583)
* Repair NaN bug for autocomplete on counter click
2023-03-10 10:49:14 +01:00
9e0b5b0b82 Merge branch 'partenariat-eurocks' into taiste 2023-03-09 17:14:34 +01:00
b12e8dc147 [PARTENARIAT] Ajout vitrine d'achat billets eurockéennes 2023 (#582)
* Added eurocks links to eboutic
2023-03-09 17:13:45 +01:00
25c5a3297c Repair NaN bug for autocomplete on counter click 2023-03-09 14:21:12 +01:00
dd3ad42eb5 Mise à jour de février (#581)
Co-authored-by: Thomas Girod <thgirod@hotmail.com>
Co-authored-by: Julien Constant <julienconstant190@gmail.com>
Co-authored-by: Skia <skia@hya.sk>
2023-03-09 13:39:33 +01:00
5ea181829e Edited unit tests
This test caused a breach in security due to the alert block displaying sensitive data.
2023-03-08 20:47:59 +01:00
0cf203669f fix wording
Co-authored-by: Théo DURR <git@theodurr.fr>
2023-03-08 20:35:24 +01:00
559bfcac60 fix typo 2023-03-08 20:13:12 +01:00
db8a1ed0ab Added eurocks links to eboutic 2023-03-08 20:10:54 +01:00
16150905a0 Fixed broken test 2023-03-08 14:11:10 +01:00
9a376887ac Update 404.jinja 2023-03-08 13:08:23 +01:00
773808fa59 Disabled Galaxy button & Removed 404 exception display 2023-03-08 12:50:52 +01:00
c1e59a0676 Disabled galaxy feature (only visually) 2023-03-07 21:32:37 +01:00
05febc60bd Merge branch 'master' into taiste 2023-03-04 16:35:41 +01:00
a73fe598ef repair user merging tool (#498) 2023-03-04 15:01:08 +01:00
b7f20fed6c Galaxy (#575)
Co-authored-by: Skia <florent.jacquet@eshard.com>
2023-03-02 15:11:23 +01:00
585923c827 Add galaxy (#562)
* style.scss: lint

* style.scss: add 'th' padding

* core: populate: add much more data for development

* Add galaxy
2023-02-07 12:08:25 +01:00
394e17d599 resolved importError (#565) 2023-01-13 02:22:53 +01:00
59136850b8 Merge pull request #530 from ae-utbm/redirection_for_barmen
redirect the user directly on counter when barman
2023-01-11 23:24:45 +01:00
d726f4b1e8 Merge pull request #499 from ae-utbm/unify-account-creation
Unify account id creation
2023-01-11 13:26:00 +01:00
705b9b1e6a Passage de vue à Alpine pour les comptoirs (#561)
Vue, c'est cool, mais avec Django c'est un peu chiant à utiliser. Alpine a l'avantage d'être plus léger et d'avoir une syntaxe qui ne ressemble pas à celle de Jinja (ce qui évite d'avoir à mettre des {% raw %} partout).
2023-01-10 22:26:46 +01:00
31e8ad8a3e redirect directly on counter if user is barman 2023-01-10 17:37:26 +01:00
99827e005b upgrade re_path to path (#533) 2023-01-09 22:07:03 +01:00
751c8a8bc6 unify account_id creation 2023-01-09 21:40:38 +01:00
73305c0b28 Implémentation 3DSv2 + résolution bugs eboutic + amélioration pages admin (#558)
Eboutic :
- Implémentation de la norme 3DSecure v2 pour les paiement par carte bancaire
- Amélioration générale de l'interface utilisateur
- Résolution du problème avec les caractères spéciaux dans le panier sur Safari
- Réparation du cookie du panier de l'eboutic qui n'était pas fonctionnel

Autre :
- Mise à jour de la documentation
- Mise à jour des dépendances Javascript
- Suppression du code inutilisé dans `subscription/models.py`
- Amélioration des pages administrateur (back-office Django)

Co-authored-by: thomas girod <56346771+imperosol@users.noreply.github.com>
Co-authored-by: Théo DURR <git@theodurr.fr>
Co-authored-by: Julien Constant <julienconstant190@gmail.com>
2023-01-09 20:53:12 +01:00
37216cd16b Updated lock file according to pyproject 2023-01-09 19:29:04 +01:00
dae68638cf Merge branch 'master' into taiste 2023-01-09 19:15:00 +01:00
7cadc0bc28 Update doc/start/install.rst 2023-01-09 19:04:43 +01:00
cce686f3a8 Update doc/about/tech.rst 2023-01-09 19:04:32 +01:00
4fe46fbcef [FIX] 3DSv2 - Echappement du XML et modif tables (#543)
* Fixed wrong HMAC signature generation
* Updated migration files

Co-authored-by: Julien Constant <julienconstant190@gmail.com>
2023-01-09 17:46:34 +01:00
fe8b8f46aa Fix 3DSv2 implementation (#542)
* Fixed wrong HMAC signature generation

* Fix xml du panier

Co-authored-by: Julien Constant <julienconstant190@gmail.com>
2023-01-06 20:02:45 +01:00
310f1a2283 [FEATURE] Ajout du logo de la promo 23 & Amélioration des anciens logos (#541) 2023-01-05 18:37:13 +01:00
7079761ffe Merge pull request #540 from ae-utbm/3dsv2-encore-un-patch
remove csrf_token
2022-12-26 18:55:41 +01:00
f681c981c6 remove csrf_token 2022-12-26 18:51:04 +01:00
5d97146d14 Merge pull request #531 from ae-utbm/remove_useless_tests
remove useless tests
2022-12-22 13:01:56 +01:00
7b56bd697d Merge pull request #536 from ae-utbm/refactor_admin
enhance admin pages
2022-12-21 17:10:51 +01:00
14cd268d69 Merge pull request #532 from ae-utbm/update-doc
update documentation
2022-12-21 16:02:17 +01:00
754be1c9c9 Update doc/about/tech.rst
Co-authored-by: Julien Constant <49886317+Juknum@users.noreply.github.com>
2022-12-20 21:17:52 +01:00
da2c155254 update documentation 2022-12-20 17:26:28 +01:00
ceb2888f82 enhance admin pages 2022-12-19 20:55:33 +01:00
ce3e2bb32b Revert "Bump cryptography from 37.0.4 to 38.0.3 (#515)" (#529)
This reverts commit 7b6eed9a47.
2022-12-17 14:05:53 +01:00
26c94c9ec6 [Eboutic] Fix double quote issue & improved user experience on small screen (#522)
* Fix #511 Regex issue with escaped double quotes

* Fix basket being when reloading the page (when cookie != "")

+ Added JSDoc
+ Cleaned some code

* Fix #509 Improved user experience on small screens

* Fix css class not being added back when reloading page

* CSS Fixes (see description)

+ Fixed overlaping item title with the cart emoji on small screen
+ Fixed minimal size of the basket on small screen (full width)

* Added darkened background circle to items with no image

* Fix issue were the basket could be None


* Edited CSS to have bette img ratio & the 🛒 icon

Adapt, Improve, Overcome

* Moved basket down on small screen size
2022-12-16 00:37:07 +01:00
7b6eed9a47 Bump cryptography from 37.0.4 to 38.0.3 (#515)
Bumps [cryptography](https://github.com/pyca/cryptography) from 37.0.4 to 38.0.3.
- [Release notes](https://github.com/pyca/cryptography/releases)
- [Changelog](https://github.com/pyca/cryptography/blob/main/CHANGELOG.rst)
- [Commits](https://github.com/pyca/cryptography/compare/37.0.4...38.0.3)

---
updated-dependencies:
- dependency-name: cryptography
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-12-16 00:35:46 +01:00
639197f4c8 update some dependencies (#523) 2022-12-15 23:57:31 +01:00
13bae8d2fa Update deploy.yml (#527) 2022-12-15 23:55:29 +01:00
6b2027550c Revert "Dépendance poetry manquante (setuptools)" (#526)
This reverts commit 022b365bb2.
2022-12-15 23:18:41 +01:00
022b365bb2 Dépendance poetry manquante (setuptools) 2022-12-15 22:36:56 +01:00
d8867fc9ea Edited workflows (#521)
Résoud le soucis lié à dependabot.

Le problème venait du fait que l'on faisait un poetry update et non un poetry Install. Un update écrit dans poetry.lock, alors qu'un Install lit ce fichier. C'est là toute la différence.

Cette PR change donc les workflows.

Laisser ce bot apporte beaucoup de sécurité, vu qu'il nous prévient des changement, et aussi des vulnérabilités au niveau des dépendances.
2022-12-15 19:02:29 +01:00
118c58b5fa Bump django from 3.2.15 to 3.2.16 (#514)
Bumps [django](https://github.com/django/django) from 3.2.15 to 3.2.16.
- [Release notes](https://github.com/django/django/releases)
- [Commits](https://github.com/django/django/compare/3.2.15...3.2.16)

---
updated-dependencies:
- dependency-name: django
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-12-14 14:11:58 +01:00
faccc1367f Fix le panier de l'Eboutic pour Safari (#518)
Co-authored-by: Théo DURR <git@theodurr.fr>
Co-authored-by: thomas girod <56346771+imperosol@users.noreply.github.com>
2022-12-14 08:38:41 +01:00
22b83b0814 remove useless tests 2022-12-12 22:56:06 +01:00
1d82e2a7d9 Change country id to ISO 3166 1 numeric for 3DSV2 (#510) 2022-12-12 22:54:31 +01:00
823bd578f2 Fix election page css (#508)
* Fix election candidate overflowing texte
* Fix "Show Less" invisible still occurring in production (wtf)
* Fix program text still overflowing still occurring in production (wtf)

Bonus:
* Fix miss-aligned links in navbar
2022-12-12 21:22:18 +01:00
3e5c36b39e Fix election candidate overflowing texte (#506)
Le texte affiché lorsqu'on appuyait sur "Show more" dépassait horizontalement (programme des candidats)
2022-12-12 20:27:47 +01:00
8fb0897160 add a sentry capture for when eboutic basket regex validation fails. (#504)
Co-authored-by: Théo DURR <git@theodurr.fr>
2022-12-11 10:56:02 +01:00
b8a72c57e1 escape html characters on xml (#505) 2022-12-10 20:41:35 +01:00
6a0a8e8ab4 Edited bar manager club to pdf (pdfesti) (#503)
* Edited bar manager club to pdf (pdfesti)
* Fixed unit tests
2022-12-08 18:26:41 +01:00
9188565a86 Merge pull request #501 from ae-utbm/ci-cl-edit
Ci cl edit
2022-12-01 11:14:56 +01:00
4d7d22c337 edit yml to avoid git conflict when deploying on test 2022-12-01 10:07:03 +01:00
b58116b023 Merge pull request #500 from ae-utbm/eboutic-3DSv2-patch
integration of 3D secure v2 for eboutic bank payment
2022-11-30 23:10:53 +01:00
fe9e5ce861 integration of 3D secure v2 for eboutic bank payment 2022-11-30 22:52:56 +01:00
e43d53e564 Merge pull request #497 from ae-utbm/eboutic-patch
Correct wrong et_autoanswer url
2022-11-16 23:47:24 +01:00
d4a5039efc correct wrong et_autoanswer url 2022-11-16 23:33:22 +01:00
35506e0175 Revert "Merge pull request #496 from ae-utbm/eboutic-patch"
This reverts commit 1c27831f92, reversing
changes made to b92580943a.
2022-11-16 23:31:34 +01:00
1c27831f92 Merge pull request #496 from ae-utbm/eboutic-patch
hide bank payment button during investigation for bug
2022-11-16 22:17:04 +01:00
cdbf07a835 hide bank payment button during investigation for bug 2022-11-16 22:14:16 +01:00
b92580943a Merge pull request #495 from ae-utbm/eboutic-patch
Eboutic patch
2022-11-16 20:57:32 +01:00
60eff1000f second patch on eboutic 2022-11-16 20:41:24 +01:00
96510b270d bux fixing on new eboutic (#493)
- les alt des images des produits n'étaient pas bonnes
- les noms de produits avec une apostrophe dedans n'étaient pas cliquables
2022-11-16 19:08:58 +01:00
1281104d96 Merge pull request #490 from ae-utbm/taiste
Eboutic
2022-11-16 17:04:13 +01:00
3c1724fa81 Add warning message when user has no birthdate 2022-11-15 21:07:50 +01:00
1630af4fbd Merge pull request #489 from TheRolfFR/remove-pinktober
Removing pinktober for AE
2022-11-14 21:04:58 +01:00
e76e2b1537 Removing pinktober for AE 2022-11-14 20:40:19 +01:00
6c276dc596 resolved crash when user has no birthdate 2022-11-12 13:59:58 +01:00
d3c115e3f9 Merge pull request #477 from imperosol/eboutic
Refonte de la boutique en ligne
2022-10-31 16:28:56 +01:00
c245ef7149 refonte de la boutique en ligne 2022-10-31 16:15:16 +01:00
8b09ba2924 refonte de la boutique en ligne 2022-10-30 12:33:21 +01:00
52eb310f95 Merge pull request #486 from ae-utbm/taiste-ci-cl
add yaml config files for test deployment
2022-10-30 11:04:38 +01:00
5bff38fc7b add yaml config files for test deployment 2022-10-28 01:41:53 +02:00
2813a59323 Revert "add yaml config files for taiste deployment"
This reverts commit 89d6db4208.
2022-10-28 01:35:46 +02:00
eef33fa263 Revert "revert push to wrong branch"
This reverts commit 241d3cea53.
2022-10-28 01:35:34 +02:00
241d3cea53 revert push to wrong branch 2022-10-28 01:10:00 +02:00
89d6db4208 add yaml config files for taiste deployment 2022-10-28 01:08:12 +02:00
e0ad288cf4 Suppression des appels à la db de l'ancien site (#483) 2022-10-19 16:26:30 +02:00
f4d7fae8ca Merge pull request #482 from ae-utbm/pinktober-patch-patch
Patch to fix the pinktober patch
2022-10-13 20:56:00 +02:00
95a7493fc1 Patch to fix the pinktober patch 2022-10-13 20:50:18 +02:00
8243dbcbef mend pinktober logo (#481)
* mend pinktober logo

* Increased contrast for links

Co-authored-by: Théo DURR <git@theodurr.fr>
2022-10-12 08:47:06 +02:00
c3a4071627 Edited navbar for pinktober (#480)
Design by @TheRolfFR
2022-10-11 21:55:19 +02:00
cef3f22e0d Merge pull request #476 from ae-utbm/lsacienne/change_banner_to_invitation
Return to classical weekmail banner
2022-09-26 08:18:15 +02:00
c206b965ad Return to classical weekmail banner 2022-09-25 21:44:56 +02:00
e868946fd7 Merge pull request #475 from imperosol/patch-1
update link for poetry install
2022-09-25 17:42:06 +02:00
254044c36b Merge pull request #474 from ae-utbm/lsacienne/change_banner_to_invitation
💄 Modification of banner
2022-09-25 17:26:48 +02:00
c695d6f7a0 update link for poetry install 2022-09-25 12:06:29 +02:00
feef855f01 💄 Modification of banner 2022-09-21 22:12:35 +02:00
b3a48ca5af Merge pull request #471 from ae-utbm/remove-gitlab-files
Added back the **git** .mailmap file
2022-09-11 23:54:03 +02:00
f3a52d094e Merge pull request #472 from ae-utbm/lsacienne/change_banner
💄 Modification of banner
2022-09-11 23:37:43 +02:00
2901bd919f 💄 Modification of banner 2022-09-11 23:17:29 +02:00
0396a5bf2b Added back the **git** .mailmap file 2022-09-09 13:59:01 +02:00
b48ad16f04 Merge pull request #470 from ae-utbm/remove-gitlab-files
Remove old GitLab files
2022-09-02 20:13:20 +02:00
7cc6250860 Delete thank_you.md 2022-09-02 19:53:41 +02:00
ae2e4b518d Removed old GitLab files & may fix auto_assign for reviewers 2022-09-02 19:49:28 +02:00
e9b9f3a62b Merge pull request #469 from ae-utbm/remove-calendar-page
Switched Calendar link to Elections list link (as it was unused)
2022-09-02 19:43:47 +02:00
3321669726 Switched Calendar link to Elections list link (as it was unused) 2022-09-02 19:34:16 +02:00
21fc85670e hot fix: Updated variable names & comments (#461)
- Fixed a wrong condition on the users subscribing history `read` permission.
- The comments are clearer and mentions how to specify clubs by their id.
2022-08-31 20:53:08 +02:00
18a5ad6541 Merge pull request #460 from ae-utbm/integration-subscriptions 2022-08-31 18:51:40 +02:00
71c5456225 Selected club members can now see subscriptions 2022-08-31 18:39:49 +02:00
50e04164a2 Merge pull request #457 from ae-utbm/455-sentry-modal
Fixed some mess done on settings.py
2022-08-27 22:06:13 +02:00
3b1d71f317 Merge pull request #458 from ae-utbm/actions
Editing workflow process
2022-08-27 22:00:54 +02:00
65c2689578 Editing workflow process
Sentry new release only triggers when deployment is successful
2022-08-27 21:56:46 +02:00
b45673f04a Update settings.py 2022-08-27 21:54:20 +02:00
cb6e037f5e Fixed some mess done on settings.py 2022-08-27 21:52:16 +02:00
5e6d60bb3a Merge pull request #456 from ae-utbm/455-sentry-modal
Updated sentry modal SDK
2022-08-27 21:47:59 +02:00
64f8d9bad3 Function name refactor
So the name is clearer
2022-08-27 21:36:45 +02:00
05b86e1f7a Black again 2022-08-27 21:23:49 +02:00
700fed860d Code refactor and comments 2022-08-27 21:22:31 +02:00
820bf6279b Modal window is now autocompleted if user is logged in 2022-08-27 20:14:31 +02:00
b97ce81dd2 Fixed black lint 2022-08-27 19:48:23 +02:00
f4dfd8f99c settings.DEBUG variable sets the sentry env to development
DSN still needs to be specified manually
2022-08-27 19:46:26 +02:00
29139bf360 SENTRY_ENV can now be overriden in settings.py 2022-08-27 18:58:12 +02:00
4f9c2724f5 Updated sentry modal SDK
Specified default environment for issues
2022-08-27 18:46:22 +02:00
7a914f5e94 Merge pull request #451 from ae-utbm/django-3.2-migration
Edited deprecated code
2022-08-27 00:17:36 +02:00
121d04e1d5 Merge branch 'master' into django-3.2-migration 2022-08-27 00:03:58 +02:00
fc6cdba8e2 Merge pull request #452 from ae-utbm/actions
Going back to actions again
2022-08-26 23:33:24 +02:00
7f39ead159 This should work now 2022-08-26 23:32:43 +02:00
1da82ac2dd Another regex 2022-08-26 23:31:33 +02:00
f2dcc39c14 No inspiration 2022-08-26 23:28:08 +02:00
705dc56153 Testing another regex 2022-08-26 23:26:37 +02:00
02047b62d7 Edited random file 2022-08-26 23:21:34 +02:00
895d4b33a6 Going back to actions again 2022-08-26 23:19:29 +02:00
142cb3316e Edited deprecated code
Fixes #449

See : https://docs.djangoproject.com/en/3.2/ref/forms/api/\#notes-on-field-ordering
2022-08-26 22:33:21 +02:00
997fcc9fff Merge pull request #448 from ae-utbm/actions 2022-08-26 22:26:33 +02:00
ec65ca11d6 Added sentry release action (See: #444) 2022-08-26 21:33:18 +02:00
0198027544 I forgot sth 2022-08-26 17:11:20 +02:00
69e0550d4f Merge pull request #447 from ae-utbm/actions
Implemented diff file for CI
2022-08-26 17:09:13 +02:00
9a1a5635e2 Implemented file diff (see: #445) 2022-08-26 17:04:09 +02:00
863f9ff77e Added some safety to deploy script 2022-08-26 16:39:49 +02:00
4146c4c5cb Merge pull request #443 from ae-utbm/actions
Unit tests do not run on master push
2022-08-26 16:24:23 +02:00
b3ad5c5df9 Unit tests do not run on master push
They are only trigerred on PRs
2022-08-26 16:12:17 +02:00
9388e2dc88 Merge pull request #442 from ae-utbm/actions
Actions should work now
2022-08-26 14:54:42 +02:00
56dec9eaa1 Added auto assign for PR 2022-08-26 14:43:51 +02:00
596126f4f4 Actions seem to be operationnal 2022-08-26 14:39:10 +02:00
8646b2c8f7 Rollback to previous version (see: https://github.com/appleboy/ssh-action/issues/174) 2022-08-26 14:10:37 +02:00
c81bb1fb90 Merge pull request #440 from ae-utbm/links-update
Updated links before moving to GitHub
2022-08-26 14:05:12 +02:00
d17a52a8d6 Updated links before moving to GitHub 2022-08-26 14:04:05 +02:00
55e0eecc0b SSH Connection now works 2022-08-26 13:58:45 +02:00
496adc17ea Updated links & moved to a markdown file 2022-08-26 13:58:21 +02:00
ab43d7d2df Testing things 2022-08-26 13:53:17 +02:00
13f0bfe546 Enabled debug 2022-08-26 13:48:40 +02:00
83a384145b Fixed spelling 2022-08-26 13:43:13 +02:00
8a923761a5 Specified environment 2022-08-26 13:26:41 +02:00
6e4a99eba3 Added sample deploy action 2022-08-26 13:20:57 +02:00
0470aa185e Merge pull request #441 from ae-utbm/actions
First try for CI/CD using actions
2022-08-25 22:47:34 +02:00
273371db8b Updated for merging into master 2022-08-25 22:46:30 +02:00
ed3aa0c328 Removed real tests during actions deployment 2022-08-25 22:45:33 +02:00
acfff6b103 Edited master to actions for testing purposes 2022-08-25 22:30:32 +02:00
ada4579193 Created deploy workflow & made a dry run 2022-08-25 22:23:13 +02:00
3a17c3079e 0/10 en dictée 2022-08-25 21:23:48 +02:00
26e46de8e1 Je sais pas écrire 2022-08-25 21:21:12 +02:00
111bcc8e60 Fixed permission issue on apt-get 2022-08-25 21:19:23 +02:00
cdaa204ba2 Added bullshit 2022-08-25 21:17:19 +02:00
e85511fcb9 Initial unit testing action 2022-08-25 21:14:04 +02:00
Sli
35c120a29f Merge branch 'download-all-my-pictures' into 'master'
Fix 'download all my picture button' being displayed in all albums sections

This MR fix the following issue where the download all button is displayed in each album (hence it's for all photos & not only by album)
![image](/uploads/c888e2bf8715d18cd2ea26e63f9fac28/image.png)

See merge request ae-utbm/Sith!320
2022-08-09 17:09:48 +00:00
Sli
7c4c1bc387 Fix permissions on download pictures feature 2022-08-09 18:11:20 +02:00
6e77edcf67 Fix 'download all my picture button' being displayed in all albums sections 2022-08-09 17:57:02 +02:00
effed9c760 Merge branch 'download-all-my-pictures' into 'master'
Add feature to download all of your pictures as a user


See merge request ae-utbm/Sith!319
2022-08-09 13:51:04 +00:00
Sli
0e5c8b53b0 Add missing translations and update doc 2022-08-07 16:45:18 +02:00
Sli
47a332445c Add feature to download all of your pictures as a user 2022-08-07 16:08:56 +02:00
Sli
c904b2d827 Merge branch 'fix/broken-js' into 'master'
Fix broken forms


See merge request ae-utbm/Sith!318
2022-08-06 12:53:30 +00:00
Sli
f56263d6bd Fix broken forms 2022-08-06 14:28:35 +02:00
0c2494cb34 Merge branch 'django-3.2' into 'master'
Upgrade to django 3.2

* Upgrade dependencies
* Fix ugettext
* Fix bad urls

See merge request ae-utbm/Sith!316
2022-08-05 18:46:24 +00:00
9e5743a64c Merge branch 'defer-script-and-font-awesom' into 'master'
Update de base.jinja

Defer des balises script. Ajout de preload sur l'import de fontawesome. Changement de certains commentaires html en commentaires jinja.

Le deux premiers points devraient permettre de gagner un temps non-négligeable au chargement de la page.

See merge request ae-utbm/Sith!317
2022-08-05 18:12:29 +00:00
b5241ec75e Defer des balises script. Ajout de preload sur fa. 2022-08-05 13:22:09 +00:00
Sli
4f00224f0d Update dependencies, apply black and fix wrong default SITH_COUNTER_OFFICES values 2022-08-04 18:42:29 +02:00
Sli
320a896610 Fix tests and broken forms 2022-08-04 17:20:21 +02:00
Sli
08924c5e05 Fix wrong url and set default auto field 2022-08-04 00:38:50 +02:00
Sli
98bfc308a7 Minimal working version
* Upgrade dependencies
* Fix ugettext
* Fix bad urls
2022-08-04 00:28:09 +02:00
Sli
dee24fbc9c Fix deprecation warnings 2022-08-03 21:48:37 +02:00
2556427c7d Merge branch 'lsacienne/invitation_banner2' into 'master'
💄 Change banner to invitation banner

We must set an invitation banner again. For the next one, we should create a new feature with a new button to avoid doing this switch every time.

See merge request ae-utbm/Sith!314
2022-07-04 19:23:28 +00:00
a2b35e5bba 💄 Change banner to invitation banner 2022-07-04 14:03:50 +02:00
3e8f1acb96 Merge branch 'election-css' into 'master'
Improved Elections CSS for the table

- Everything can be seen without scrolling sideways (unless you're on a small screen)
- Each column makes the same size
- Candidate description/program is now below its profile picture
- If the candidate does not have any profile picture, the default one is shown
- The Edit/Delete message has been replaced with their corresponding emojis (they takes fewer spaces and doesn't need to be translated)
- Modified links at the bottom to look like buttons

<details><summary>Before</summary>
![image](/uploads/fd42e2fa027786612582d41c97090277/image.png)
</details>

<details><summary>This MR (root)</summary>
![image](/uploads/8350518422392f971d98f3c7ee48a558/image.png)
</details>

<details><summary>This MR (lambda user)</summary>
![image](/uploads/e6b66730e47556ea21230e89d2d06f83/image.png)
</details>

<details><summary>When a candidate is selected</summary>
![image](/uploads/adde527405fb321ba2023c36e06f4dc3/image.png)
</details>

See merge request ae-utbm/Sith!313
2022-06-15 19:13:47 +00:00
85788977fe Moved file to correct place & improved CSS a bit 2022-06-15 15:32:16 +02:00
066ca5bada This shouldn't be unminified 2022-06-15 01:57:57 +02:00
41369f738e Improved Elections CSS for the table 2022-06-15 01:42:17 +02:00
67377b3cbf Merge branch 'lsacienne/change_weekmail_banner_P22_08_06_2022' into 'master'
Change the invitation banner in weekmail to regular weekmail banner

We now have the weekmail banner and not the invitation banner

See merge request ae-utbm/Sith!312
2022-06-14 09:18:26 +00:00
ac3d668655 💄 CHange the invitation banner in weekmail
We now have the weekmail banner and not the invitation banner
2022-06-08 22:05:24 +02:00
c57b15e159 Merge branch 'lsacienne/change_weekmail_banner_P22' into 'master'
Modification of the banner and footer for the Special General Meeting

There will be a special general meeting next week so we modify the banner to fit with this event.

See merge request ae-utbm/Sith!311
2022-06-01 21:14:38 +00:00
66efb8012e ♻️ Fix black pipeline 2022-06-01 22:46:12 +02:00
cad0c0dadb 💄 Modification of the banner and footer
for the special invitation
2022-06-01 22:40:52 +02:00
b32c90ed5d Add of weekmail footer 2022-06-01 22:39:44 +02:00
4d361dc67b Add of weekmail banner in 2 versions 2022-06-01 22:39:17 +02:00
2b170d91f7 Add of Invitation banner 2022-06-01 22:38:44 +02:00
9e074d6ca6 Merge branch 'service_desk_reply' into 'master'
Update .gitlab/service_desk_templates/thank_you.md


See merge request ae-utbm/Sith!310
2022-05-26 13:46:54 +00:00
b655b2695b Update .gitlab/service_desk_templates/thank_you.md 2022-05-26 08:41:37 +00:00
366aeed2ba Merge branch 'lsacienne/refilling_authorized_for_Bdf_ae' into 'master'
Add authorization to refill to the counters AE & BdF

Since the FIMU is coming, there is a necessity to allow access to physical refilling to the people who will manage the stands.

Therefore, We should authorize the refilling on the BdF and AE counter.

See merge request ae-utbm/Sith!309
2022-05-22 09:56:56 +00:00
454ae5f9e3 Add authorization to refill to the counters AE & BdF 2022-05-22 09:56:53 +00:00
b811114425 fix black pipeline 2022-05-21 21:53:25 +02:00
712e7c8939 Add of verification on the counter 2022-05-21 12:23:34 +02:00
713cd92141 Modification of the settings to fit better with the code 2022-05-21 12:23:23 +02:00
4154b499b1 Add of a new settings for the counters AE & BdF 2022-05-21 09:45:28 +02:00
253f204225 Merge branch '125-fix-family-tree' into 'master'
Ajout de pygraphviz en dépendance

Closes #125

On change également la version minimale de python (`3.7` -> `3.8`)

Closes #125

See merge request ae-utbm/Sith!306
2022-05-08 12:09:38 +00:00
7241f3eb1d Ajout de pygraphviz en dépendance 2022-05-08 12:09:37 +00:00
2422f60898 Merge branch 'lsacienne/refilling_only_for_ae_member' into 'master'
Adds a Restriction for refilling

As it was asked by many members of the AE. I added a restriction applied to the barmens.
In fact, we oftenly loose money due to the physic refilling.
The goal with this change is to only allow **the members of the AE** to refill with physic money.

See merge request ae-utbm/Sith!303
2022-05-05 21:53:57 +00:00
ba6599fa56 Add of tests 2022-05-05 23:24:08 +02:00
f2666f6fb0 Replace the query by a function which already
existed
2022-05-02 00:04:00 +02:00
b33839191d Fix black pipeline 2022-04-28 13:16:03 +02:00
ee3e375dde Post request management 2022-04-28 11:13:07 +02:00
5b0f7ca21b Merge branch 'skia/deploy_in_ci' into 'master'
gitlab-ci: deploy with Gitlab CI/CD

This MR is a proof-of-concept for deploying the Sith using Gitlab CI/CD. It leverage the CI variable to use a private key that is deployed for the `sith` user of `ae-web`. The `prod.sh` script shall do the rest.

TODO before merge:
* [x] Ensure the private key variable is protected (currently done, but may change during development to be used on this branch)
* [x] Remove this branch from the `only:refs` list
* [x] Change `test_prod.sh` for the real script

See merge request ae/Sith!293
2022-04-27 18:21:49 +00:00
f581d91730 gitlab-ci: deploy with Gitlab CI/CD 2022-04-27 18:21:48 +00:00
bbf362691b Change to use settings instead of hardcoding 2022-04-27 15:38:55 +02:00
15e2c8c7b3 Fix the balck pipeline 2022-04-27 15:38:14 +02:00
f838127730 Merge branch 'aile-master-patch-00174' into 'master'
Update badges and links on the readme


See merge request ae/Sith!305
2022-04-27 13:13:08 +00:00
d4c0bb3b0e Fix pipeline
Signed-off-by: Théo DURR <03ht@theodurr.fr>
2022-04-27 14:52:33 +02:00
b81aee3f1c Update badges and links 2022-04-27 09:50:38 +00:00
c6caf5dbce Add of restriction for refilling 2022-04-20 14:01:33 +02:00
7acc59f2cd Merge branch 'lsacienne/refilling_date' into 'master'
Add of date in the counter/refilling_list view

I only add a new field in the counter/refilling_list view which will *normally* display the date of each refilling.

See merge request ae/Sith!302
2022-04-19 10:28:09 +00:00
757ff7ead7 Add of date in the counter/refilling_list view 2022-04-19 12:02:22 +02:00
bc2fe16b74 Merge branch '117-django-2-2-not-compatible-with-psycopg-2-9' into 'master'
Resolve "Django 2.2 not compatible with psycopg 2.9"
Closes #117

See merge request ae/Sith!299
2022-04-18 20:21:19 +00:00
35363d9ee7 Resolve "Django 2.2 not compatible with psycopg 2.9" 2022-04-18 20:21:18 +00:00
52106db6fd Merge branch '118-black-pipeline-is-broken' into 'master'
Resolve "Black pipeline is broken"

Closes #118

Closes #118

See merge request ae/Sith!300
2022-04-18 18:33:39 +00:00
c4b1829e78 Resolve "Black pipeline is broken" 2022-04-18 18:33:36 +00:00
489a9378c5 Merge branch 'poetry' into 'master'
Add missing dependencies and improve pipeline

* Use black version specified in requirements for checking with black
* Check if pyproject.toml file is valid at CI level
* Build documentation in CI
* Add missing postgres dependencie

See merge request ae/Sith!284
2022-03-26 21:27:22 +00:00
Sli
28ae109b32 Add missing dependencies and improve pipeline 2022-03-26 21:27:20 +00:00
e7a6a94ff2 Merge branch 'doc-windows-install' into 'master'
Added WSL Windows doc for the project install

Added steps to install the project on Windows using WSL :)

See merge request ae/Sith!291
2022-03-03 18:18:55 +00:00
234556a172 Merge branch 'skia/fix_eboutic' into 'master'
Multiple fixes

* Bump `black` and fix issues
* `club`: fix tests broken by inclusive translation
* `gitlab-ci`: use `poetry`, as `pip` was broken anyway
* `eboutic`: et_autoanswer: don't require 'Auto' to proceed checking the request: As described in the [doc](https://www.paybox.com/espace-integrateur-documentation/la-solution-paybox-system/gestion-de-la-reponse/), `Auto` may be missing if the payment failed. Thus, it's not required to proceed checking the bank's answer.

See merge request ae/Sith!296
2022-03-02 16:21:10 +00:00
e4ddceabea club: fix tests with inclusive translation 2022-02-28 14:50:24 +01:00
05dd3ad642 gitlab-ci: use poetry 2022-02-28 10:34:15 +01:00
6c5db61a97 eboutic: et_autoanswer: don't require 'Auto' to proceed checking the request 2022-02-28 10:01:32 +01:00
a0e4e9e8e3 Update 'black' version 2022-02-28 10:01:32 +01:00
c66df77d4a Merge branch 'master' of https://ae-dev.utbm.fr/ae/Sith 2022-02-18 16:35:10 +01:00
cfb6b34630 Updated roles to be more inclusive 2022-02-18 16:30:45 +01:00
d8fd0adf47 Merge branch 'skia/et_autoanswer' into 'master'
eboutic: change HTTP return code to avoid blaming the bank's service

See merge request ae/Sith!295
2022-02-10 12:32:43 +00:00
928ae13a8a Merge branch 'bugfix-113-error500' into 'master'
#113: bug fixed

See merge request ae/Sith!294
2022-02-10 12:30:55 +00:00
c2e0ea70e4 eboutic: change HTTP return code to avoid blaming the bank's service 2022-01-04 15:50:36 +01:00
782ce24895 Changed python3 to python 2021-12-02 12:22:34 +01:00
b630742fd4 #113: bug fixed 2021-11-30 17:54:51 +01:00
b20df930a2 Merge branch 'feature-111-fixture_documentation' into 'master'
add fixture documentation

See merge request ae/Sith!292
2021-11-25 22:06:41 +00:00
d60a96fc5c correct populate.rst 2021-11-23 23:44:34 +01:00
05b0a0ab2f Adapted WSL doc to follow recommendation :) 2021-11-23 19:19:24 +01:00
9eb137e503 add fixture documentation 2021-11-22 21:37:10 +01:00
7d797009bb Added WSL windows doc for project install 2021-11-19 13:10:18 +01:00
3c1818f229 Merge branch 'family_rework' into 'master'
Updated text and translations to be more inclusive

See merge request ae/Sith!290
2021-11-18 15:38:13 +00:00
d8b69e9b45 Updated text and translations to be more inclusive 2021-11-18 16:24:14 +01:00
9177c9d4c2 Merge branch 'bugfix-110-ClubSellings' into 'master'
Fix error 500 in club sellings

Closes #110

See merge request ae/Sith!289
2021-11-18 14:32:11 +00:00
5195352975 fixed black pipeline 2021-11-18 15:14:39 +01:00
deb8f865df fix #110 2021-11-18 15:04:25 +01:00
5b2c70e4fb Merge branch 'gender_options' into 'master'
Fix pronouns field being mandatory

See merge request ae/Sith!288
2021-11-18 09:07:21 +00:00
Cel
f66db0859e Fix pronouns field being mandatory 2021-11-18 09:07:19 +00:00
b6488d1d00 Merge branch 'poor_logo_quality' into 'master'
Updated somo logo size where they looked blurry (we love responsive)

See merge request ae/Sith!287
2021-11-10 11:33:05 +00:00
6a4ac336ad Updated somo logo size where they looked blurry (we love responsive) 2021-11-10 12:11:07 +01:00
7ac6dcf8a0 Merge branch 'family_rework' into 'master'
Edited the word "GodFather" to "Family"

See merge request ae/Sith!286
2021-11-10 10:35:40 +00:00
c6a3677cc5 Fixed duplicated translation 2021-11-05 21:11:52 +01:00
707459acd6 Changed word 'Godfather' to 'Family' 2021-11-05 21:01:19 +01:00
6390c3320e Applied black on migration 2021-11-05 20:40:20 +01:00
b8aabc466c Fixed locales
+Pronoun description on the user's profile

Signed-off-by: Ailé <03ht@theodurr.fr>
2021-11-05 20:28:37 +01:00
c66e4232b9 Merge branch 'master' into gender_options
Signed-off-by: Théo DURR <03ht@theodurr.fr>
2021-11-05 17:18:17 +01:00
336450d43f Merge branch 'add-promo-logos' into 'master'
Add missing promo logos

Closes #107

See merge request ae/Sith!285
2021-10-27 10:22:07 +00:00
7e66aadd6f Add missing promo logos 2021-10-27 08:37:58 +02:00
Sli
bf2b796936 Merge branch 'poetry' into 'master'
Using poetry as a dependency system for development

See merge request ae/Sith!281
2021-10-15 16:12:59 +00:00
Sli
85623f48a9 Using poetry as a dependency system for development 2021-10-15 16:12:56 +00:00
4fbee9c3de Make pronouns visible on profile and miniprofile 2021-10-13 08:59:40 +02:00
bfa3b45547 counter_click.jinja: fix error display with Vue 2021-10-11 22:09:45 +02:00
677a9da469 Merge branch 'master' into gender_options 2021-10-11 17:13:06 +02:00
1f7752d457 Add pronouns to profile ; Update gender settings
Add pronouns to option list in profile
Modify "Sex" translation to "Genre"
Added "Other" to sex option list (alongside Man and Woman)

update DB,add default value to Pronouns field

Update views.py
2021-10-06 14:12:34 +02:00
89979dbf61 com: news list: fix UI for admins 2021-10-03 19:08:14 +02:00
8d1abb8f33 Add .mailmap file for cleaner stats 2021-10-03 18:44:47 +02:00
2df3494c3b Merge branch 'skia/weekmail_fix' into 'master'
com: fix weekmail for the case of non-existing email addresses

See merge request ae/Sith!282
2021-10-03 16:35:41 +00:00
39bb490257 com: fix weekmail for the case of non-existing email addresses
If an email address is set as destination for the Weekmail, the SMTP may
refuse it, and `smtplib` will throw a `SMTPRecipientsRefused` error,
containing the list of refused addresses. This commit provides an
interface for the weekmail sender to quickly unsubscribe the faulty
users, so that the next try sending the weekmail can be performed
successfully.
2021-10-03 18:16:51 +02:00
7a7aad0503 style: fix header bar on medium size screens 2021-10-03 16:08:53 +02:00
b157a3fa90 Merge branch 'skia/mobile_ui' into 'master'
Add a first version of a mobile friendly UI

Although not perfect and with many flaws, this should still allow far
easier navigation on mobile devices.

See merge request ae/Sith!280
2021-10-01 17:05:11 +00:00
1b688a8aa5 Add a first version of a mobile friendly UI
Although not perfect and with many flaws, this should still allow far
easier navigation on mobile devices.
2021-10-01 18:44:14 +02:00
e8978cc065 sith/toolbar_debug: don't fail when there is no template 2021-10-01 14:08:57 +02:00
7fd68e4825 Merge branch 'skia/ci_speedup' into 'master'
CI speedup

* Put the Xapian search index in `/dev/shm`, which is an in-memory storage makes the tests go from about 1500s to about 600s.
* Keep the `pip` cache between jobs, to avoid re-downloading all the wheels all the time. This gains about 1min.

See merge request ae/Sith!279
2021-09-30 10:58:49 +00:00
4119eefe37 gitlab-ci: keep pip cache between jobs 2021-09-30 12:07:00 +02:00
aafc2e6e96 gitlab-ci: put search_indexes in shared memory 2021-09-30 12:07:00 +02:00
2cbe6fa11c Merge branch 'genderMatmatroncheV2' into 'master'
Remove gender option of matmatronche & update gender settings

Afin de se mettre à jour il est dorénavant possible de ne pas définir son genre sexué sur l'édit de son profil. D'ailleurs j'ai découvert que de base pour un profil random le sexe était défini sur "Homme" maintenant il est en "-------" !

![image](/uploads/43e9f32dc545b35cbe422a53602b2457/image.png)

De plus afin que personnes n'utilisent l'outil matmatronche à des fins de site de rencontres en cherchant uniquement les "Homme" ou les "Femme" d'une promo etc... Le choix du sexe dans la recherche a été supprimé.

![image](/uploads/e6e75d5661862178acfbe71f3f7efc35/image.png)

C'est la première fois que je fais une modification en solo alors n'hésitez pas à me casser en deux et m'expliquer si j'ai fauté :D

See merge request ae/Sith!264
2021-09-29 15:57:52 +00:00
eec7bcf296 Remove gender option of matmatronche & update gender settings 2021-09-29 17:29:01 +02:00
6c45de34a4 Merge branch 'poster' into 'master'
[com]: add helper_text for resolution and format of poster

See merge request ae/Sith!209
2021-09-29 14:56:30 +00:00
Cyl
61a40c47d2 [com]: add helper_text for resolution and format of poster 2021-09-29 16:09:05 +02:00
007157e2e8 Merge branch 'datetime-hell2' into 'master'
core: create TzAwareDateTimeField to replace forms.DateTimeField

Follow up of !267. I read about Gitlab's slash and merge just after I did my own kind by resetting back to the original commit and creating one commit manually. Sublime merge helps but I still need more practice. :)

What was the right way to group every commit under one?

See merge request ae/Sith!270
2021-09-29 13:53:12 +00:00
49a0ade315 core: create TzAwareDateTimeField to replace forms.DateTimeField 2021-09-29 15:24:06 +02:00
782cd9a45a Merge branch 'sexy-search' into 'master'
Sexy search

The goal of this MR is to solve the search issue #96. Let's assume we have a user with firstname `Jean-François`, lastname `Du Pont` and nickname `Ai'gnan`. Here is a list of search that did not include him previously but now includes him (was and still is case-insensitive):

* `jean françois` (missing -) ;
* `jean-francois` (missing ç) ;
* `jean francois` (both) ;
* `dupont` (space) ;
* `françois` (not the start of his name) ;
* `aignan` (missing ').

You get it, there are a lot of mistakes that humans can do. It also sorts results by `User.last_update` to avoid putting old accounts at the top of common requests (such as firstname-only or lastname-only requests).

### How it works

For those who don't know, the search is handled by Xapian (the search backend) through the haystack library which provides a Django-friendly interface to multiple search backends. Xapian maintains kind of a duplicate of the database (only for models against which we want to search something) which is optimised for search operations. Its "models" are called "indexes" (see `core.search_indexes.UserIndex` for the user model).

Every time a user is created or modified, it is indexed (through a signal handler) so that Xapian knows about it. For the user search, what is indexed is the string outputted by the `core/templates/search/indexes/core/user_auto.txt` template. For our example from above, it looks like this:

```
jean francois
du pont
aignan
jeanfrancois
dupont

jeanfrancoisdupont
```

As you can see, unicode is removed. There also are kind-of duplicates with different spacing as we are using an autocomplete algorithm: it searches from the beginning of words.

The one I am not sure about is the last one. Its goal is to allow searching without putting a space between the firstname and lastname. Is this useful?

The prod will have to do a `./manage.py update_index`, not sure it does it in the upgrade script.

See merge request ae/Sith!269
2021-09-28 00:14:38 +00:00
6382e631b6 search: reduce user index size 2021-09-28 01:44:15 +02:00
12493cffca search: make sure we don't have indexes that are too long 2021-09-28 01:44:15 +02:00
a38ab57ddf search: sort by User.last_update 2021-09-28 01:44:15 +02:00
30091ef69c search: ascii everywhere and unformalized whitespace 2021-09-28 01:44:15 +02:00
1a483bfa2c Merge branch 'och' into 'master'
Settings: Added new subscription for the new CA offer

This year we made a new deal with the CA: if a student open an account, they give us 50€ and the student 80€ with on year of subscription.

See merge request ae/Sith!276
2021-09-27 23:31:46 +00:00
1a091951e8 Added new subscription for the new CA offer 2021-09-28 01:11:23 +02:00
bfb66b352a Merge branch 'dep-hell2' into 'master'
core: add ./manage.py check_front command and call it on runserver

See #92 and !268.

This simplifies checking that front-end dependencies are up to date. It does not allow one to update an outdated dependency. That must be done manually (would otherwise require depending on a CDN or add npm as a dependency). A manual update will make sure changelogs are read and changes will be made appropriately.

We add a `check_front` command to `manage.py` and run it on calls to `runserver`.

This MR does not update any dependency as it is not its goal. MR incoming!

Should doc be added? It seems pretty simple and I don't see what should be documented: if it's red, update it.

~"Review TODO" @sli

See merge request ae/Sith!271
2021-09-27 20:23:35 +00:00
be26e3df7f core: add ./manage.py check_front command and call it on runserver 2021-09-27 22:00:36 +02:00
cb3307509d Merge branch 'skia/counter_rework' into 'master'
counter: make click page dynamic to avoid repetitive loading

See merge request ae/Sith!278
2021-09-27 19:21:28 +00:00
a3158253a7 Black update 2021-09-26 13:58:39 +02:00
406380e4f1 counter: make click page dynamic to avoid repetitive loading
This makes the whole click page load only once for a normal click
workflow. The current basket is now rendered client side with Vue.JS,
and the backend view is able to answer with JSON if asked to.

This should lighten the workflow a lot on the client side, especially
with poor connectivity, and the server should also feel lighter during
big events, due to far less complex Jinja pages to render.
2021-09-26 13:58:39 +02:00
efb70652af counter: redirect to counter main when barman login is timed out 2021-09-26 13:58:39 +02:00
05256bb99a counter: templates: click: JS clean up 2021-09-26 13:58:39 +02:00
64d0cc2fa8 counter: don't display info boxes and navigation menu
This will lighten the pages and make the functionality directly
accessible without ever scrolling the header garbage that is never
needed on those pages.
2021-09-26 13:58:39 +02:00
f5d7267ba7 Merge branch 'skia/fix_ci' into 'master'
Fix CI

See merge request ae/Sith!277
2021-07-21 13:16:02 +00:00
24c0a21cc1 locale: update with latest code version 2021-04-23 12:02:03 +02:00
6a352d642b accounting: fix tests with a computed date instead of hard-coded one 2021-04-23 12:02:03 +02:00
Sli
48ae1f7c1c Merge branch 'och' into 'master'
Edited subscriptions

See merge request ae/Sith!275
2020-09-01 00:21:48 +02:00
aaf1adaaa1 sith: Added a new subscription 2020-08-30 23:53:19 +02:00
f34f5fe693 Upgrade black and format accordingly 2020-08-27 15:59:42 +02:00
Sli
f485178422 Merge branch 'och' into 'master'
settings: Added a new subscription

See merge request ae/Sith!274
2020-06-18 00:23:51 +02:00
Och
797ca0f926 settings: Added a new subscription 2020-06-18 00:23:51 +02:00
Sli
390a4b0064 Merge branch 'bugfix' into 'master'
cache: fix error 500 with new django version

See merge request ae/Sith!273
2020-06-16 19:11:49 +02:00
94b029dc9c cache: fix error 500 with new django version 2020-06-12 20:44:37 +02:00
Sli
45d5728c3e Merge branch 'skia/lazy_load_user_pictures' into 'master'
core: add lazy loading in user pictures page

See merge request ae/Sith!272
2020-06-12 20:19:34 +02:00
6eabbaf209 core: add lazy loading in user pictures page 2020-05-15 12:14:14 +02:00
Sli
03fdd0b947 Merge branch 'trombi' into 'master'
trombi: raw tool for trombi admins to add a club membership to a trombi member

See merge request ae/Sith!266
2020-03-23 21:12:58 +01:00
fb8faacddc trombi: raw tool for trombi admins to add a club membership to a trombi member 2020-03-22 16:14:37 +01:00
Sli
7ee4557ab5 Merge branch 'fix-webcam-error' into 'master'
Front: turn Webcam.js error from an alert to a console log

See merge request ae/Sith!265
2020-03-05 19:20:22 +01:00
5accdbccbb Front: use Webcam.on() for error handling 2020-03-04 07:13:16 +01:00
7fb26f9e45 Front: turn Webcam.js error from an alert to a console log 2020-03-03 09:01:20 +01:00
26a07f722d Remove gender option of matmatronche & update gender settings 2020-02-16 17:51:51 +01:00
Sli
9176a03a8a Merge branch 'bugfix' into 'master'
Fix some SAS and forum errors

Closes #89

See merge request ae/Sith!263
2019-12-17 12:03:28 +01:00
4a1bfc366d sas: fix 500 error when tagging the same user twice or adding a non existing user 2019-12-17 11:25:17 +01:00
ebee8c34e1 forum: fix ForumTopicSubscribeView error 500 with anonymous user 2019-12-16 15:00:33 +01:00
4ecad1c73b Revert "PÈRE 200 !!!!!!!!!!! PÈRE 200 !!!!!!!! TRALALALALÈREEEEUUUU !!!!"
This reverts commit d1b3a4d3f6.
2019-12-10 15:31:37 +01:00
d1b3a4d3f6 PÈRE 200 !!!!!!!!!!! PÈRE 200 !!!!!!!! TRALALALALÈREEEEUUUU !!!! 2019-12-09 03:16:57 +01:00
Sli
40832bb3bf Merge branch 'clubs' into 'master'
Improve Sellings view for clubs

See merge request ae/Sith!262
2019-11-29 16:32:18 +01:00
4a78157f9a club: fix typo on ClubSellingView 2019-11-28 15:14:51 +01:00
bf5fc8750d club: steam CSV download for SellingView 2019-11-28 14:52:33 +01:00
274a7b7137 core/club: allow adding custom js action to pagination link, useful for FormDetailView with pagination 2019-11-28 01:46:41 +01:00
8dd2c02d3e club: add pagination for ClubSellingView 2019-11-28 00:30:51 +01:00
a73f5cb270 club: use sums in bdd for ClubSellingView 2019-11-27 21:37:59 +01:00
7d40e11144 club: ClubSellingView way faster and with multiple selections everywhere 2019-11-27 20:59:32 +01:00
af48553e35 club: separation between archived products and non archived ones 2019-11-27 16:23:14 +01:00
Sli
ad8bcc7282 Merge branch 'bugfix' into 'master'
com: fix 500 error when utbm mail server refuse weekmail

See merge request ae/Sith!260
2019-11-25 14:18:22 +01:00
Sli
22a44415e4 Merge branch 'sli' into 'master'
core: add UserIsRootMixin and an admin delete view for memberships

See merge request ae/Sith!261
2019-11-25 13:32:46 +01:00
6a153719f9 com: fix 500 error when utbm mail server refuse weekmail 2019-11-25 13:30:47 +01:00
5c8fa1b9e7 core: add UserIsRootMixin and an admin delete view for memberships 2019-11-24 19:23:43 +01:00
Sli
d82679e3d7 Merge branch 'documentation' into 'master'
add autoreload/build to documentation server and enhace documentation

See merge request ae/Sith!246
2019-11-21 15:06:13 +01:00
Sli
9cb432a082 doc: correct documentation for groups 2019-11-21 11:11:25 +01:00
Sli
869d29d4a4 doc: corrections for populate documentation 2019-11-21 11:10:31 +01:00
c3d2e64a43 doc: add infos on populate command with group and users available 2019-11-20 18:51:13 +01:00
e1770ec52c doc: add documentation for groups 2019-11-20 17:55:00 +01:00
1256744f1b documentation: add autoreload and build for documentation server 2019-11-20 17:03:18 +01:00
77dddbc581 documentation: add help ressources and update installation instructions 2019-11-20 17:03:18 +01:00
Sli
bfa4000365 Merge branch 'eboutic' into 'master'
eboutic: don't display future account balance if contains refilling item

See merge request ae/Sith!258
2019-11-14 19:31:19 +01:00
Sli
50c2f8164d Merge branch 'deletion_logs' into 'master'
Add generic operation logs and implements it for Sellings and Refilling deletions

See merge request ae/Sith!259
2019-11-14 19:29:55 +01:00
5c30de5f22 core: redesign request middleware with django latest design and better use of threading 2019-11-14 16:32:29 +01:00
1c03ce621f core: remove default value for OperationLog 2019-11-14 16:11:20 +01:00
e634cda318 core/counter: add generic operation logs and implements it for Sellings and Refilling deletions 2019-11-14 01:14:44 +01:00
3501703c15 eboutic: don't display future account balance if contains refilling item 2019-11-05 19:50:08 +01:00
Sli
129f2e53ee Merge branch 'galaRequests' into 'master'
eticketListView: product id instead of eticket id

See merge request ae/Sith!257
2019-11-05 11:23:52 +01:00
209867b3a8 black: makes new version happy 2019-11-04 13:46:09 +01:00
59511d255f eticketListView: product id instead of eticket id 2019-11-03 20:58:20 +01:00
Sli
f42daa01c5 Merge branch 'add-account-amount' into 'master'
Add the account amount to the eboutic

See merge request ae/Sith!254
2019-10-28 23:37:33 +01:00
29ee1b05af Merge branch 'master' into 'add-account-amount'
# Conflicts:
#   locale/fr/LC_MESSAGES/django.po
2019-10-28 15:48:54 +01:00
Sli
42055b9001 Merge branch 'auto-uv-pedagogy' into 'master'
Auto fill UVs in pedagogy

See merge request ae/Sith!253
2019-10-25 12:09:39 +02:00
00c96f5b71 eboutic: fix account amount 2019-10-24 14:40:26 +02:00
5cc7eff94f pedagogy: uv autofill finishing touches 2019-10-24 14:18:29 +02:00
Sli
28077ef0b0 Merge branch 'fix-create-club' into 'master'
club: fix 500 on club_new

See merge request ae/Sith!256
2019-10-22 08:34:08 +02:00
143b128891 club: fix 500 on club_new 2019-10-21 22:56:24 +02:00
6b06b647bc eboutic: add p tag in makecommand 2019-10-21 22:10:38 +02:00
413c613c9f Fix translation for basket account amount 2019-10-21 22:06:56 +02:00
1c0d15ba2a settings: fix black report 2019-10-21 21:17:11 +02:00
28bd6b8708 uv: make autofill available on edit page 2019-10-21 17:10:16 +02:00
419a48ac3a /pedagogy/uv/create put urls in settings 2019-10-21 17:01:21 +02:00
6fce27113a /pedagogy/uv/create use quick notif 2019-10-21 16:52:51 +02:00
53a7633700 uv: Add error handler to uv autofill 2019-10-21 10:34:46 +02:00
4094394cef api: typo in doc comment 2019-10-21 08:13:36 +02:00
f533c39e67 api: fix uv manager acquisition if uv is only available in spring 2019-10-21 02:10:09 +02:00
86bc491df4 Fix UV_endpoint auth 2019-10-20 18:26:11 +02:00
4759551c16 Autofull UV small changes 2019-10-20 17:09:36 +02:00
b057dbfd60 Initial add account amount in eboutic 2019-10-18 21:41:39 +02:00
bddb88d97f Comment UV API and fix little bugs 2019-10-18 18:13:53 +02:00
dbe44a9c1c Fix hour count and submit button 2019-10-18 01:45:49 +02:00
eeb791c460 Initial autofill on UV 2019-10-18 01:28:59 +02:00
Sli
6d0eba6bcf Merge branch 'rework-front' into 'master'
Markdown widget follows the required attribute

See merge request ae/Sith!249
2019-10-17 14:47:56 +02:00
Sli
4d04b21f04 Merge branch 'cleanup-forms' into 'master'
Remove unused multiple-select library

See merge request ae/Sith!250
2019-10-17 14:46:02 +02:00
Sli
2f1b26053b Merge branch 'fix-news-form' into 'master'
Fix 500 when a news needs a start_date and/or end_date but we don't provide

See merge request ae/Sith!252
2019-10-17 14:42:41 +02:00
Sli
1848945d64 Merge branch 'bugfix' into 'master'
Fix huge permission problem inducing server memory leaks for etickets

See merge request ae/Sith!251
2019-10-17 12:36:57 +02:00
9278419345 core: rename GenericContentPermission into GenericContentPermissionMixinBuilder 2019-10-17 11:56:02 +02:00
566dcc7aee counter: fix Selling view permission 2019-10-17 11:24:52 +02:00
a6088c0e4a core: refactor permissions mixins 2019-10-17 11:24:51 +02:00
60c9498a56 Fix 500 on news creation/edition 2019-10-17 10:25:29 +02:00
241650c171 counter: fix eticket server crash induced by old permission system and fix Selling permission 2019-10-16 21:21:51 +02:00
811809895e club: fix mailing list form that unexpectedly relied on try catch in permissions 2019-10-16 21:21:06 +02:00
fe9164bfef core: don't use try/except to catch type of view in permissions mixins 2019-10-16 19:28:32 +02:00
ad3f003fbb Remove unused multiple-select library 2019-10-16 14:28:53 +02:00
7ecb057b68 Isolate easymde instances so that they can be referenced 2019-10-16 12:18:23 +02:00
e932abfa74 Prevent pressing submit if the Markdown widget is empty 2019-10-15 10:41:10 +02:00
0011f4c7b0 Only register onchange once the submit button has been pressed 2019-10-15 10:23:15 +02:00
13312e9879 Highlight a markdown input in red if required and submit is pressed
Kind of copy the behaviour of a Firefox input
Once the submit button has been pressed, highlight in red the text
input if it's required but empty
2019-10-15 09:54:10 +02:00
ced90c23db More JS-like, callback as last argument 2019-10-15 09:53:44 +02:00
Sli
42f5773f51 Merge branch 'fix-guy-feature' into 'master'
Fix the guyguy "feature" on the profile page

See merge request ae/Sith!248
2019-10-15 00:26:04 +02:00
Sli
b270c76249 Merge branch 'galaRequests' into 'master'
Some gala requests: new minor features

See merge request ae/Sith!247
2019-10-14 22:55:55 +02:00
34df825718 Fix the guyguy "feature" on the profile page 2019-10-14 23:49:32 +03:00
Cyl
aac4e3b99c Minor fix for requestGala 2019-10-14 00:32:11 +02:00
5a55a6c642 E-ticket link is sent in the email 2019-10-13 19:02:25 +02:00
65c3483c1f core:login allow the user to create an account when not logged 2019-10-13 18:43:30 +02:00
Sli
660a3161f5 Merge branch 'new_django' into 'master'
upgrade to django 2.2

See merge request ae/Sith!243
2019-10-12 23:19:26 +02:00
Sli
9e6c4b32e3 Merge branch 'bugfix' into 'master'
Fix error when editing poster while being admin

See merge request ae/Sith!245
2019-10-09 18:11:05 +02:00
25225fc451 com: fix error when editing poster while being admin 2019-10-09 17:43:23 +02:00
c3f2d0a134 django2.2: unlock djangorestframework version limit 2019-10-08 22:46:38 +02:00
cd2d3ee6b4 django2.2: fix tests for accounting 2019-10-08 22:46:38 +02:00
81fcf411c1 django2.2: forms fixs for new API 2019-10-08 22:46:38 +02:00
d7075eb762 django2.2: fix breaking change for getting uploaded files size 2019-10-08 22:46:38 +02:00
cf3f5ea60c django2.2: fix django server crash because of breaking change in widget rendering method 2019-10-08 22:46:37 +02:00
59185ab2a8 django2.2: rewrite login and password stack because of removed API 2019-10-08 22:46:37 +02:00
a177fa8232 django2.2: replace deprecated base_name with basename in urls 2019-10-08 22:46:37 +02:00
308cf30a5a django2.2: replace deprecated login view 2019-10-08 22:46:37 +02:00
99c8d95443 django2.2: remove direct assignments to many-to-many fields 2019-10-08 22:46:37 +02:00
97c316b62e django2.2: replace removed is_anonymous() and is_authenticated to their now used counterparts 2019-10-08 22:46:03 +02:00
90921fd4cd django2.2: some migrations to make django happy 2019-10-08 22:46:03 +02:00
296cc4144c django2.2: remove SessionAuthenticationMiddleware
See https://docs.djangoproject.com/en/2.2/releases/2.0/ for more details
2019-10-08 22:46:03 +02:00
f7548ab8d1 django2.2: add on_delete on migrations for OneToOneField 2019-10-08 22:46:03 +02:00
3cb306bc91 django2.2: add on_delete on migrations for ForeignKey 2019-10-08 22:46:03 +02:00
c20d5855e4 django2.2: remove view_page permission as it clash with built-in permission
Need a bit of discussion as I'm not an expert in django built-in permissions
An issue on github says it's related to https://docs.djangoproject.com/en/2.1/releases/2.1/#considerations-for-the-new-model-view-permission
2019-10-08 22:46:02 +02:00
00bd60ef4f django2.2: add exception parameter in forbidden and not_found views 2019-10-08 22:46:02 +02:00
b8c7fb6f74 django2.2: adapt ChoiceField to the new API 2019-10-08 22:46:02 +02:00
df531198c9 django2.2: fix urls and adapt them to new format 2019-10-08 22:46:02 +02:00
12b6f0d488 django2.2: adapt DateTimeField in forms with the new API 2019-10-08 22:46:02 +02:00
6cc234e8d3 django2.2: add default mandatory on_delete on OneToOneField 2019-10-08 22:46:02 +02:00
4dadb1dbc0 django2.2: add default mandatory on_delete on all ForeignKey 2019-10-08 22:46:01 +02:00
2616e8b24c django2.2: relpace django.core.urlresolvers by django.urls 2019-10-08 22:46:01 +02:00
be855c6c90 django2.2: migrate url to re_path 2019-10-08 22:46:01 +02:00
Sli
7be9077fce Merge branch 'documentation' into 'master'
write a new shiny and comprehensive documentation

See merge request ae/Sith!224
2019-10-08 22:42:19 +02:00
d48c09a914 documentation: revise title levels on git documentation 2019-10-08 22:13:29 +02:00
d5c3dbf864 Add git introduction 2019-10-08 20:22:36 +02:00
2a9b89fd2a core: remove README wiki page that wasn't helpful and anyway broken with rst files 2019-10-08 11:12:22 +02:00
c73f4ca847 documentation: add CONTRIBUTING 2019-10-08 11:08:47 +02:00
d63b5335d4 documentation: apply corrections suggested in comments 2019-10-08 11:08:33 +02:00
a766f7137c documentation: add instructions for direnv 2019-10-08 00:59:19 +02:00
5c3c14ab37 core: wrap compilemessages to avoid compiling whole env and avoid crash at sphinx messages compilation 2019-10-08 00:59:00 +02:00
775413ac7e documentation: weekmail documentation 2019-10-07 23:58:04 +02:00
1f271c75f0 documentation: add instructions to add and edit subscriptions 2019-10-07 23:51:38 +02:00
4df152185e documentation: rewrite README and remove TODO 2019-10-07 23:51:38 +02:00
c83b30f27b documentation: add doc compile test in pipeline 2019-10-07 23:51:38 +02:00
db10f7b963 documentation: tutorial about rights management 2019-10-07 23:51:37 +02:00
ed68c2cb38 documentation: move documentation about usage in production 2019-10-07 23:51:37 +02:00
a6c8dea190 documentation: move markdown syntax documentation and move helper for counting lines 2019-10-07 23:51:37 +02:00
124eaf42cd documentation: add models manipulation in hello world tutorial 2019-10-07 23:51:37 +02:00
5489096bf5 documentation: add explanation on MVT and explain project structure 2019-10-07 23:51:37 +02:00
3a425c6792 documentation: add infos about fontawesome + tests instructions + more external doc 2019-10-07 23:51:37 +02:00
8809753108 documentation fix tests because of missing README.md 2019-10-07 23:51:37 +02:00
4428a2e89c documentation: add hello world app tutorial 2019-10-07 23:51:36 +02:00
0616597bf2 documentation: remove CONTRIBUTING and migrate its content in the doc 2019-10-07 23:51:36 +02:00
782a763046 documentation: update instructions, add down loadable files and add all pdf reports for download purpose 2019-10-07 23:51:36 +02:00
8dcade6890 documentation: remove Doxygen, include README into doc update tech and install 2019-10-07 23:51:36 +02:00
dd49d71cb7 documentation: installation instructions 2019-10-07 23:51:36 +02:00
b0b52fd714 documentation: technologies presentation 2019-10-07 23:51:36 +02:00
ef40baaa84 documentation: bump sphinx version 2019-10-07 23:51:35 +02:00
7c259bf26b documentation: write introduction 2019-10-07 23:51:35 +02:00
05e5008305 documentation: base structure for new documentation 2019-10-07 23:51:35 +02:00
Sli
448f5ff40f Merge branch 'com' into 'master'
com: fix error 500 when editing poster with com admin

See merge request ae/Sith!242
2019-10-06 17:55:30 +02:00
Sli
5482f1174d Merge branch 'trombi' into 'master'
trombi: permissions fixs

See merge request ae/Sith!241
2019-10-02 17:30:46 +02:00
2da0560ec8 com: fix error 500 when editing poster with com admin 2019-10-02 15:32:33 +02:00
5151fc3792 trombi: permissions fixs 2019-10-02 14:56:04 +02:00
Sli
fd5cd56f81 Merge branch 'trombi' into 'master'
Trombi fixs

See merge request ae/Sith!240
2019-09-30 00:00:38 +02:00
35d9c05abf trombi: fix trombi tools if user has a trombi profile but no trombi linked 2019-09-29 12:19:22 +02:00
fcb3035b67 trombi: fix some 500 errors when accessing page without being in a trombi 2019-09-29 12:09:03 +02:00
Sli
b7db969f08 Merge branch 'counter' into 'master'
[Counter] - Buying group is now required

See merge request ae/Sith!239
2019-09-20 14:03:07 +02:00
Cyl
14303fd46c [Counter] - Buying group is now required 2019-09-19 23:04:03 +02:00
Sli
5e6b17cd19 Merge branch 'eticket' into 'master'
[Counter] - add the product ID for every Eticket

See merge request ae/Sith!237
2019-09-18 14:42:16 +02:00
Cyl
8232ff59a0 [Counter] - add the product ID for every Eticket 2019-09-16 23:41:06 +02:00
Sli
411c117f0f Merge branch 'performances' into 'master'
Improve performances on forum and SAS

See merge request ae/Sith!235
2019-09-16 14:23:52 +02:00
298499c749 sas: add cache on Picture permissions to avoid hitting the bdd too much 2019-09-16 11:33:32 +02:00
b8ad2d4835 sas: add pagination on AlbumView 2019-09-16 01:26:20 +02:00
Sli
d37eb134e2 Merge branch 'bugfix' into 'master'
Fix error 500 when editing properties of user without linked customer

See merge request ae/Sith!236
2019-09-15 17:48:21 +02:00
63ec5d68f4 core: fix error 500 when editing properties of user without linked customer 2019-09-15 17:05:07 +02:00
8330e1eaf2 sas: simplify and optimize permissions for SAS images 2019-09-15 16:43:17 +02:00
1f86827e46 core: improve performances on not found images 2019-09-15 16:22:13 +02:00
321e5e3ff5 forum: enhance performances on toggle_favorite 2019-09-15 16:12:24 +02:00
Sli
3eb8292d15 Merge branch 'bugfix' into 'master'
Fix error 500 with expired reset password links

See merge request ae/Sith!234
2019-09-11 10:33:39 +02:00
5a3f90fd28 core: fix error 500 with expired reset password links 2019-09-10 16:56:58 +02:00
Sli
eb975f4de1 Merge branch 'performances' into 'master'
Speed up counter click interface and auto scroll

See merge request ae/Sith!233
2019-09-10 15:50:38 +02:00
405b938e08 counter: speed counter click interface and auto scroll 2019-09-10 14:22:13 +02:00
Sli
f899e32fb0 Merge branch 'performances' into 'master'
Improve overall performances on notifications, news pages and navbar

See merge request ae/Sith!232
2019-09-09 11:07:04 +02:00
9181e77d55 core: add some cache on birthdays and counters_activity 2019-09-09 01:20:15 +02:00
f1b3a174b6 core: improve performances on notification reads and display 2019-09-09 00:45:08 +02:00
Sli
eb9821ed36 Merge branch 'sas' into 'master'
core: add index on folder and sas properties of SithFile to speed up SAS

See merge request ae/Sith!231
2019-09-06 16:47:18 +02:00
Sli
defb7fb3a3 Merge branch 'pedagogy_v2' into 'master'
Disable having two comments from the same user for the same uv in uv guide

See merge request ae/Sith!229
2019-09-06 16:28:28 +02:00
83e225a744 core: add index on folder and sas properties of SithFile to speed up SAS 2019-09-06 16:16:03 +02:00
f30bea3dc9 pedagogy: add script to remove all previous doubled comments 2019-09-04 20:49:18 +02:00
a69f7b12b1 pedagogy: add script to remove all previous doubled comments 2019-09-04 20:49:17 +02:00
Sli
ca042fe75e Merge branch 'a19_subscriptions' into 'master'
SUBSCRIPTIONS: 5 new discounted subscriptions for integration

See merge request ae/Sith!226
2019-08-29 19:33:07 +02:00
Zar
dc9111dbcd SUBSCRIPTIONS: 5 new discounted subscriptions for integration 2019-08-29 19:06:58 +02:00
Sli
8a16a66299 Merge branch 'bugfix' into 'master'
com/core: remove links to index edition that caused error 500

See merge request ae/Sith!230
2019-08-29 18:03:34 +02:00
d7a7613807 com: add basics tests for ComAlert and ComInfo to detect regressions on tabs 2019-08-29 17:29:38 +02:00
3fc8688941 com/core: remove links to index edition that caused error 500 2019-08-29 17:23:27 +02:00
Sli
d7b351a1aa Merge branch 'cyl' into 'master'
[COM] Make the news visible for non-authenticated user and birthday visible for subriber only

See merge request ae/Sith!225
2019-08-29 15:07:08 +02:00
Cyl
9e0c4e70d4 [COM] Make the news visible for non-authenticated user and birthday visible for subriber only 2019-08-28 20:40:31 +02:00
Sli
2232c495be Merge branch 'bugfix' into 'master'
pedagogy: fix a bug when updating an UV from a different author

See merge request ae/Sith!228
2019-08-28 16:08:32 +02:00
Sli
18b1bea664 Merge branch 'weekmail' into 'master'
com: add weekmail banner and footer for A19

See merge request ae/Sith!227
2019-08-28 02:35:04 +02:00
a5d5c41dd6 pedagogy: fix a bug when updating an UV from a different author 2019-08-27 22:46:41 +02:00
66d5c71a92 com: add weekmail banner and footer for A19 2019-08-27 18:56:43 +02:00
Sli
824ea37f44 Merge branch 'pedagogy_v2' into 'master'
Better display on mobile for guide page

See merge request ae/Sith!221
2019-08-26 12:11:26 +02:00
Sli
eb29f98c37 Merge branch 'bugfix' into 'master'
pedagogy: correctly fill star widget when editing comment

Closes #88

See merge request ae/Sith!223
2019-08-20 22:57:02 +02:00
d903dc58cf pedagogy: correctly fill star widget when editing comment 2019-08-08 18:59:44 +02:00
f09de0ab7d pedagogy: remove stars on small devices for grade 2019-08-08 12:46:51 +02:00
d29603c584 pedagogy: fix display of guide on smaller devices 2019-08-07 20:03:21 +02:00
3380980c5c pedagogy: add generic font for .radio-guide 2019-08-07 17:50:50 +02:00
Sli
38ef13d9b6 Merge branch 'markdown-editor' into 'master'
core: upgrade easymde

See merge request ae/Sith!222
2019-07-29 15:28:35 +02:00
6c43b1c43d pedagogy: better display on mobile for guide page
Widen search bar and use a grid template
Reduce zooming when clicking on the search bar (firexfox)
Remove zooming when clicking on search bar (chrome)
2019-07-25 19:01:53 +02:00
2b34c46412 core: upgrade easymde 2019-07-19 23:36:56 +02:00
Sli
f9227fa29d Merge branch 'bugfix' into 'master'
Fix error 500 when accessing user tools with anonymous user and fix dependancies

See merge request ae/Sith!220
2019-07-15 15:03:27 +02:00
96a3eaff1c ci: fix django rest framework version 2019-07-15 14:40:03 +02:00
65cb85a887 ci: fix building of pygraphviz 2019-07-15 14:29:47 +02:00
640a72c52d core: add tests for UserToolsView 2019-07-15 12:36:05 +02:00
9b7b96a310 core: add UserIsLoggedMixin to check if an user is not anonymous 2019-07-15 12:27:19 +02:00
b18746e769 core: fix error 500 when accessing user tools with anonymous user 2019-07-13 04:58:23 +02:00
Sli
a2b431b1ab Merge branch 'pedagogy_v2' into 'master'
New version of the pedagogy

See merge request ae/Sith!212
2019-07-11 00:05:27 +02:00
d844bccb04 pedagogy: improve performances on json mode for UVListView 2019-07-10 12:26:37 +02:00
Sli
49f928e754 Apply suggestion to pedagogy/views.py 2019-07-10 12:12:56 +02:00
07fc1014be pedagogy: put methods after properties in models 2019-07-09 16:59:59 +02:00
Sli
facb6faf75 Merge branch 'pedagogy_v2_front' into 'pedagogy_v2'
Frontend for pedagogy

See merge request ae/Sith!218
2019-07-09 16:57:00 +02:00
e72338a7d9 pedagogy: enlarge shape around author 2019-07-09 16:33:06 +02:00
f37c022538 pedagogy: put report button at left and author at right 2019-07-09 16:03:47 +02:00
5229628d48 pedagogy: fix weird blank spacing in comment block 2019-07-09 15:08:48 +02:00
b4b7bf05b4 pedagogy: remove scrolling on desktop for comments 2019-07-09 14:43:46 +02:00
231415a772 pedagogy: hide scroll bar at bottom of comments 2019-07-08 23:43:27 +02:00
f052d307d7 pedagogy: make report button less visible and author more important 2019-07-08 23:16:53 +02:00
f15971cecf pedagogy: simplify moderation form for user 2019-07-08 17:34:23 +02:00
99cf59c7a4 pedagogy: remove fira font to reduce downloaded content for user 2019-07-08 15:51:31 +02:00
0d13014e8a pedagogy: simpler generation for department radio buttons 2019-07-08 15:36:30 +02:00
fd1f89de1d pedagogy: wrap all css inside a class named pedagogy to avoid name clashes 2019-07-08 15:25:28 +02:00
Sli
78b616427f Merge branch 'new_django' into 'master'
core: rename MIDDLEWARE_CLASSES into MIDDLEWARE

See merge request ae/Sith!219
2019-07-08 15:18:21 +02:00
c15ea345dd pedagogy: generate search form radios trough loop and remove semester_translated 2019-07-08 15:17:12 +02:00
1d319e90f0 pedagogy: don't make the anchor scroll with comment text 2019-07-08 09:47:54 +02:00
e6e500e2f9 pedagogy: fix margins on stars for mobile devices 2019-07-08 09:37:30 +02:00
cf1ec1dc86 pedagogy: add missing markdown treatment for key_concept 2019-07-08 09:26:22 +02:00
46a042cde2 pedagogy: fix mobile display on chrome 2019-07-08 09:19:23 +02:00
52129d7511 pedagogy: new mobile view and use of css grids for comment display 2019-07-08 02:54:49 +02:00
d03835d737 pedagogy: allow search on uv title 2019-07-08 00:54:53 +02:00
b4b7817baa pedagogy: auto send form when typing 2019-07-08 00:53:02 +02:00
d85152e58c pedagogy: quick access to comment from moderation 2019-07-08 00:01:54 +02:00
f118040432 pedagogy: add pedagogy in user tools 2019-07-08 00:01:38 +02:00
9f1aff8c07 pedagogy: add retries on search form and make uv table clickable 2019-07-07 23:52:54 +02:00
94bbdf372b pedagogy: fix css grids on chrome android 2019-07-07 22:14:46 +02:00
240d94bd57 pedagogy: enhance display on mobile and fix some bugs with webkit 2019-07-07 21:56:59 +02:00
3ee7ff2752 pedagogy: display hours of UVs 2019-07-07 21:38:00 +02:00
2c5385cf5c pedagogy: enhance mobile view 2019-07-07 21:03:27 +02:00
c8a691044f pedagogy: add translations 2019-07-07 19:36:47 +02:00
f93eaff876 pedagogy: small fix for leave comment on desktop 2019-07-07 19:08:30 +02:00
10faa14bef pedagogy: better display on mobile 2019-07-07 19:07:19 +02:00
30ccbdc32d pedagogy: fix search api when searching one letter (case sensitivity) 2019-07-07 18:55:23 +02:00
79243aece3 pedagogy: better display for uv_details 2019-07-07 18:51:36 +02:00
a61322b83f pedagogy: fix search form display rights 2019-07-07 18:43:43 +02:00
3df73f4d1f pedagogy: css class name consistency 2019-07-07 18:33:56 +02:00
7165a63e97 pedagogy: polish uv_details 2019-07-07 18:29:31 +02:00
2404edd289 pedagogy: clean up guide page 2019-07-07 16:52:28 +02:00
3bff09b04c pedagogy: correctly display uv infos 2019-07-07 16:33:03 +02:00
28748af5d3 pedagogy: smart back button in uv_detail 2019-07-07 14:44:25 +02:00
a56a4e2cb8 pedagogy: better comment display on mobile 2019-07-06 03:54:46 +02:00
339497b2c2 pedagogy: display semester in search view 2019-07-06 02:57:20 +02:00
c05168a2b5 pedagogy: display most recent comment first 2019-07-06 02:33:05 +02:00
782ee35779 pedagogy: incorpore all elements in comment block 2019-07-06 02:30:47 +02:00
43acee8f1b pedagogy: enhance comment look 2019-07-06 02:16:04 +02:00
4a19441a17 pedagogy: translations for semesters in details and handle markdown 2019-07-06 01:16:09 +02:00
11acf5897f pedagogy: correctly hide AP input with its label 2019-07-06 00:34:41 +02:00
4be99fe828 guide design not finished 2019-07-06 00:14:15 -05:00
601193ff3c small change comment 2019-07-05 22:42:13 -05:00
f500dec1f1 core: rename MIDDLEWARE_CLASSES into MIDDLEWARE
MIDDLEWARE_CLASSES is deprecated since django 1.11 and with last version of django-debug-toolbar it broke the dev server
See here for more details https://docs.djangoproject.com/fr/2.2/topics/http/middleware/#upgrading-pre-django-1-10-style-middleware
2019-07-05 22:01:30 +02:00
Cyl
bfb7380715 [Pedagogy] Front comment nearly ended 2019-07-05 20:54:14 +02:00
3e3c576ad7 pedagogy: live uv update on guide 2019-07-05 20:11:33 +02:00
2aa1314fac pedagogy: basic display of the guide 2019-07-05 18:16:28 +02:00
3063e4a24f pedagogy: auto fill search form from get arguments in URL 2019-07-05 16:40:14 +02:00
6f8ec4740c pedagogy: simpler user interface for autumn and spring search 2019-07-05 16:40:14 +02:00
cbcd84c931 pedagogy: add a search form 2019-07-05 16:40:14 +02:00
e475273cd3 pedagogy: enhance StarList widget 2019-07-05 16:40:14 +02:00
Cyl
851231869b [pedagogy] Star for comment grades 2019-07-05 16:40:13 +02:00
3376f4dfb4 pedagogy: fix typo for AUTUMN_AND_SPRING 2019-07-05 16:40:02 +02:00
205f93569a pedagogy: grade averages for UVs 2019-07-04 18:07:51 +02:00
Sli
4f7a8661ba Merge branch 'pedagogy_v2_moderation' into 'pedagogy_v2'
Pedagogy comments moderation

See merge request ae/Sith!215
2019-07-04 15:54:24 +02:00
6e7d351e8e pedagogy: send notification to pedagogy admins at comment report 2019-07-04 15:32:00 +02:00
Sli
73f1927ce4 Merge branch 'pedagogy_v2_old_base_import' into 'pedagogy_v2'
pedagogy: fix bdd id out of sync after old data base import

See merge request ae/Sith!216
2019-06-25 15:24:46 +02:00
56e3f39de1 pedagogy: fix bdd id out of sync after old data base import 2019-06-20 19:20:06 +02:00
75a2aefd69 pedagogy: display if comment is reported 2019-06-20 15:03:51 +02:00
55e822412a pedagogy: full test suite for pedagogy moderation 2019-06-20 14:57:58 +02:00
171d9a4381 pedagogy: tests and fixs for uv moderation form 2019-06-20 14:22:06 +02:00
806084e707 pedagogy: allow to deny removal request for comment in moderation 2019-06-20 13:19:35 +02:00
04009a6a5b pedagogy: moderation interface 2019-06-20 12:15:12 +02:00
3d0f5c0a15 pedagogy: base for uv comment moderation 2019-06-20 01:29:12 +02:00
Sli
437af4dd04 Merge branch 'pedagogy_v2_search_api' into 'pedagogy_v2'
Pedagogy v2 search api

See merge request ae/Sith!214
2019-06-19 10:36:27 +02:00
Sli
ba61455017 Merge branch 'pedagogy_v2_old_base_import' into 'pedagogy_v2'
Pedagogy v2 old base import

See merge request ae/Sith!213
2019-06-19 10:35:50 +02:00
624f1d653d pedagogy: tests for search API 2019-06-19 10:04:29 +02:00
e21821ace5 pedagogy: handle one letter search 2019-06-19 02:00:00 +02:00
22c028af11 pedagogy: rename query to search in search API 2019-06-19 01:53:02 +02:00
f0560f0d2a pedagogy: fix import for HUMA on old database 2019-06-19 01:49:34 +02:00
502ae09523 pedagogy: add filters to search api 2019-06-19 01:26:11 +02:00
2cbef2babc pedagogy: support json response from search API 2019-06-19 00:58:20 +02:00
e11d45b51e pedagogy: more details on uv_detail for tests purpose 2019-06-19 00:58:14 +02:00
061320a5df pedagogy: search index for uvs and search api 2019-06-19 00:57:55 +02:00
2aa465b138 pedagogy: don't update uv comment publish date at each save 2019-06-19 00:35:44 +02:00
d18f0aa829 pedagogy: import results from old uv guide 2019-06-18 17:46:46 +02:00
e7b8ddb631 pedagogy: importation from old uv guide 2019-06-18 17:20:10 +02:00
358a625cc4 pedagogy: simplify and implement department system according to old database model 2019-06-18 10:56:05 +02:00
d44fa73b2a pedagogy: Fix grade range on UVCOmment 2019-06-17 18:42:33 +02:00
1018 changed files with 68300 additions and 77837 deletions

18
.env.example Normal file
View File

@ -0,0 +1,18 @@
HTTPS=off
SITH_DEBUG=true
# This is not the real key used in prod
SECRET_KEY=(4sjxvhz@m5$0a$j0_pqicnc$s!vbve)z+&++m%g%bjhlz4+g2
# comment the sqlite line and uncomment the postgres one to switch the dbms
DATABASE_URL=sqlite:///db.sqlite3
#DATABASE_URL=postgres://user:password@127.0.0.1:5432/sith
REDIS_PORT=7963
CACHE_URL=redis://127.0.0.1:${REDIS_PORT}/0
TASK_BROKER_URL=redis://127.0.0.1:${REDIS_PORT}/1
# Used to select which other services to run alongside
# manage.py, pytest and runserver
PROCFILE_STATIC=Procfile.static
PROCFILE_SERVICE=Procfile.service

6
.envrc Normal file
View File

@ -0,0 +1,6 @@
if [[ ! -d .venv ]]; then
log_error 'No .venv folder found. Use `uv sync` to create one first.'
exit 2
fi
. .venv/bin/activate

View File

@ -0,0 +1,65 @@
name: "Setup project"
description: "Setup Python and Poetry"
inputs:
full:
description: >
If true, do a full setup, else install
only python, uv and non-xapian python deps
required: false
default: "false"
runs:
using: composite
steps:
- name: Install apt packages
if: ${{ inputs.full == 'true' }}
uses: awalsh128/cache-apt-pkgs-action@v1.4.3
with:
packages: gettext
version: 1.0 # increment to reset cache
- name: Install Redis
if: ${{ inputs.full == 'true' }}
uses: shogo82148/actions-setup-redis@v1
with:
redis-version: "7.x"
- name: Install uv
uses: astral-sh/setup-uv@v5
with:
version: "0.5.14"
enable-cache: true
cache-dependency-glob: "uv.lock"
- name: "Set up Python"
uses: actions/setup-python@v5
with:
python-version-file: ".python-version"
- name: Restore cached virtualenv
uses: actions/cache/restore@v4
with:
key: venv-${{ runner.os }}-${{ hashFiles('.python-version') }}-${{ hashFiles('pyproject.toml') }}-${{ env.CACHE_SUFFIX }}
path: .venv
- name: Install dependencies
run: uv sync
shell: bash
- name: Install Xapian
if: ${{ inputs.full == 'true' }}
run: uv run ./manage.py install_xapian
shell: bash
# compiling xapian accounts for almost the entirety of the virtualenv setup,
# so we save the virtual environment only on workflows where it has been installed
- name: Save cached virtualenv
if: ${{ inputs.full == 'true' }}
uses: actions/cache/save@v4
with:
key: venv-${{ runner.os }}-${{ hashFiles('.python-version') }}-${{ hashFiles('pyproject.toml') }}-${{ env.CACHE_SUFFIX }}
path: .venv
- name: Compile gettext messages
if: ${{ inputs.full == 'true' }}
run: uv run ./manage.py compilemessages
shell: bash

13
.github/auto_assign.yml vendored Normal file
View File

@ -0,0 +1,13 @@
# Set to true to add reviewers to pull requests
addReviewers: true
# Set to true to add assignees to pull requests
addAssignees: author
# A list of team reviewers to be added to pull requests (GitHub team slug)
reviewers:
- ae-utbm/sith-3-developers
# Number of reviewers has no impact on GitHub teams
# Set 0 to add all the reviewers (default: 0)
numberOfReviewers: 0

14
.github/dependabot.yml vendored Normal file
View File

@ -0,0 +1,14 @@
# To get started with Dependabot version updates, you'll need to specify which
# package ecosystems to update and where the package manifests are located.
# Please see the documentation for all configuration options:
# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
version: 2
updates:
- package-ecosystem: "pip" # See documentation for possible values
directory: "/" # Location of package manifests
schedule:
interval: "weekly"
target-branch: "taiste"
commit-message:
prefix: "[UPDATE] "

55
.github/workflows/ci.yml vendored Normal file
View File

@ -0,0 +1,55 @@
name: Sith CI
on:
push:
branches: [master, taiste]
pull_request:
branches: [master, taiste]
workflow_dispatch:
env:
SECRET_KEY: notTheRealOne
DATABASE_URL: sqlite:///db.sqlite3
CACHE_URL: redis://127.0.0.1:6379/0
TASK_BROKER_URL: redis://127.0.0.1:6379/1
jobs:
pre-commit:
name: Launch pre-commits checks (ruff)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version-file: ".python-version"
- uses: pre-commit/action@v3.0.1
with:
extra_args: --all-files
tests:
name: Run tests and generate coverage report
runs-on: ubuntu-latest
strategy:
fail-fast: false # don't interrupt the other test processes
matrix:
pytest-mark: [not slow]
steps:
- name: Check out repository
uses: actions/checkout@v4
- uses: ./.github/actions/setup_project
with:
full: true
env:
# To avoid race conditions on environment cache
CACHE_SUFFIX: ${{ matrix.pytest-mark }}
- name: Run tests
run: uv run coverage run -m pytest -m "${{ matrix.pytest-mark }}"
- name: Generate coverage report
run: |
uv run coverage report
uv run coverage html
- name: Archive code coverage results
uses: actions/upload-artifact@v4
with:
name: coverage-report-${{ matrix.pytest-mark }}
path: coverage_report

65
.github/workflows/deploy.yml vendored Normal file
View File

@ -0,0 +1,65 @@
name: Deploy to production
concurrency: production
on:
push:
branches: [master]
workflow_dispatch:
jobs:
deployment:
runs-on: ubuntu-latest
environment: production
timeout-minutes: 30
steps:
- name: SSH Remote Commands
uses: appleboy/ssh-action@v1.1.0
with:
# Proxy
proxy_host : ${{secrets.PROXY_HOST}}
proxy_port : ${{secrets.PROXY_PORT}}
proxy_username : ${{secrets.PROXY_USER}}
proxy_passphrase: ${{secrets.PROXY_PASSPHRASE}}
proxy_key: ${{secrets.PROXY_KEY}}
# Serveur web
host: ${{secrets.HOST}}
port : ${{secrets.PORT}}
username : ${{secrets.USER}}
key: ${{secrets.KEY}}
script_stop: true
# See https://github.com/ae-utbm/sith/wiki/GitHub-Actions#deployment-action
script: |
cd ${{secrets.SITH_PATH}}
git fetch
git reset --hard origin/master
uv sync --group prod
npm install
uv run ./manage.py install_xapian
uv run ./manage.py migrate
uv run ./manage.py collectstatic --clear --noinput
uv run ./manage.py compilemessages
sudo systemctl restart uwsgi
sentry:
runs-on: ubuntu-latest
environment: production
timeout-minutes: 30
needs: deployment
steps:
- uses: actions/checkout@v4
- name: Sentry Release
uses: getsentry/action-release@v1.7.0
env:
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
SENTRY_ORG: ${{ secrets.SENTRY_ORG }}
SENTRY_PROJECT: ${{ secrets.SENTRY_PROJECT }}
SENTRY_URL: ${{ secrets.SENTRY_URL }}
with:
environment: production

21
.github/workflows/deploy_docs.yml vendored Normal file
View File

@ -0,0 +1,21 @@
name: deploy_docs
on:
push:
branches:
- taiste
permissions:
contents: write
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: ./.github/actions/setup_project
- run: echo "cache_id=$(date --utc '+%V')" >> $GITHUB_ENV
- uses: actions/cache@v3
with:
key: mkdocs-material-${{ env.cache_id }}
path: .cache
restore-keys: |
mkdocs-material-
- run: uv run mkdocs gh-deploy --force

46
.github/workflows/taiste.yml vendored Normal file
View File

@ -0,0 +1,46 @@
name: Sith taiste
on:
push:
branches: [taiste]
workflow_dispatch:
jobs:
deployment:
runs-on: ubuntu-latest
environment: taiste
timeout-minutes: 30
steps:
- name: SSH Remote Commands
uses: appleboy/ssh-action@v1.1.0
with:
# Proxy
proxy_host : ${{secrets.PROXY_HOST}}
proxy_port : ${{secrets.PROXY_PORT}}
proxy_username : ${{secrets.PROXY_USER}}
proxy_passphrase: ${{secrets.PROXY_PASSPHRASE}}
proxy_key: ${{secrets.PROXY_KEY}}
# Serveur web
host: ${{secrets.HOST}}
port : ${{secrets.PORT}}
username : ${{secrets.USER}}
key: ${{secrets.KEY}}
script_stop: true
# See https://github.com/ae-utbm/sith/wiki/GitHub-Actions#deployment-action
script: |
cd ${{secrets.SITH_PATH}}
git fetch
git reset --hard origin/taiste
uv sync --group prod
npm install
uv run ./manage.py install_xapian
uv run ./manage.py migrate
uv run ./manage.py collectstatic --clear --noinput
uv run ./manage.py compilemessages
sudo systemctl restart uwsgi

20
.gitignore vendored
View File

@ -1,15 +1,31 @@
db.sqlite3
*.sqlite3
*.log
*.pyc
*.mo
*__pycache__*
.DS_Store
pyrightconfig.json
dist/
.vscode/
env/
.idea/
.venv/
doc/html
data/
galaxy/test_galaxy_state.json
/static/
sith/settings_custom.py
sith/search_indexes/
.coverage
coverage_report/
node_modules/
.env
*.pid
# compiled documentation
site/
### Redis ###
# Ignore redis binary dump (dump.rdb) files
*.rdb

View File

@ -1,26 +0,0 @@
stages:
- test
test:
stage: test
script:
- apt-get update
- apt-get install -y gettext python3-xapian
- pushd /usr/lib/python3/dist-packages/xapian && ln -s _xapian* _xapian.so && popd
- export PYTHONPATH="/usr/lib/python3/dist-packages:$PYTHONPATH"
- python -c 'import xapian' # Fail immediately if there is a problem with xapian
- pip install -r requirements.txt
- pip install coverage
- ./manage.py compilemessages
- coverage run ./manage.py test
- coverage html
- coverage report
artifacts:
paths:
- coverage_report/
black:
stage: test
script:
- pip install black
- black --check .

19
.mailmap Normal file
View File

@ -0,0 +1,19 @@
Code <gregoire.duvauchelle@utbm.fr>
Cyl <labetowiez@aol.fr>
Juste <maaxleblanc@gmail.com>
Krophil <pierre.brunet@krophil.fr>
Lo-J <renaudg779@gmail.com>
Nabos <gnikwo@hotmail.com>
Och <francescowitz68@gmail.com>
Partoo <joqaste@gmail.com>
Skia <skia@hya.sk> <lordbanana25@mailoo.org>
Skia <skia@hya.sk> <skia@libskia.so>
Sli <klmp200@klmp200.net> <antoine@bartuccio.fr>
Soldat <ryan-68@live.fr>
Terre <jbaptiste.lenglet+git@gmail.com>
Vial <robin.trioux@utbm.fr>
Zar <antoine.charmeau@utbm.fr> <antoine.charmeau@laposte.net>
root <root@localhost.localdomain>
tleb <tleb@openmailbox.org> <theo.lebrun@live.fr>
tleb <tleb@openmailbox.org> <theo.lebrun@utbm.fr>
Maréchal <thgirod@hotmail.com>

1
.npmrc Normal file
View File

@ -0,0 +1 @@
@jsr:registry=https://npm.jsr.io

26
.pre-commit-config.yaml Normal file
View File

@ -0,0 +1,26 @@
repos:
- repo: https://github.com/astral-sh/ruff-pre-commit
# Ruff version.
rev: v0.11.13
hooks:
- id: ruff-check # just check the code, and print the errors
- id: ruff-check # actually fix the fixable errors, but print nothing
args: ["--fix", "--silent"]
# Run the formatter.
- id: ruff-format
- repo: https://github.com/biomejs/pre-commit
rev: v0.6.1
hooks:
- id: biome-check
additional_dependencies: ["@biomejs/biome@1.9.4"]
- repo: https://github.com/rtts/djhtml
rev: 3.0.7
hooks:
- id: djhtml
name: format templates
entry: djhtml --tabwidth 2
types: ["jinja"]
- id: djcss
name: format scss files
entry: djcss --tabwidth 2
types: ["scss"]

1
.python-version Normal file
View File

@ -0,0 +1 @@
3.12

View File

@ -1,106 +0,0 @@
*Contribuer c'est la vie*
=========================
Hey ! Tu veux devenir un mec bien et en plus devenir bon en python si tu l'es pas déjà ?
Il se trouve que le sith AE prévu pour l'été 2016 a besoin de toi !
Pour faire le sith, on utilise le framework Web [Django](https://docs.djangoproject.com/fr/1.11/intro/)
N'hésite pas à lire les tutos et à nous demander (ae.info@utbm.fr).
Bon, passons aux choses sérieuses, pour bidouiller le sith sans le casser :
Ben en fait, tu peux pas le casser, tu vas juste t'amuser comme un petit fou sur un clone du sith.
C'est pas compliqué, il suffit d'avoir [Git](http://www.git-scm.com/book/fr/v2), python et pip (pour faciliter la gestion des paquets python).
Tout d'abord, tu vas avoir besoin d'un compte Gitlab pour pouvoir te connecter.
Ensuite, tu fais :
`git clone https://ae-dev.utbm.fr/ae/Sith.git`
Avec cette commande, tu clones le sith AE dans le dossier courant.
```bash
cd Sith
virtualenv --system-site-packages --python=python3 env
source env/bin/activate
pip install -r requirements.txt
./manage runserver
```
Attention aux dépendances système, à voir dans le README.md
Maintenant, faut passer le sith en mode debug dans le fichier de settings personnalisé.
```bash
echo "DEBUG=True" > sith/settings_custom.py
echo 'SITH_URL = "localhost:8000"' >> sith/settings_custom.py
```
Enfin, il s'agit de créer la base de donnée de test lors de la première utilisation
```bash
./manage.py setup
```
Et pour lancer le sith, tu fais `python3 manage.py runserver`
Voilà, c'est le sith AE. Il y a des issues dans le gitlab qui sont à régler. Si tu as un domaine qui t'intéresse, une appli que tu voudrais développer, n'hésites pas et contacte-nous.
Va, et que l'AE soit avec toi.
# Black
Pour uniformiser le formattage du code nous utilisons [Black](https://github.com/ambv/black). Cela permet d'avoir le même codestyle et donc le codereview prend moins de temps. Tout étant dans le même format, il est plus facile pour chacun de comprendre le code de chacun ! Cela permet aussi d'éviter des erreurs (y parait 🤷‍♀️).
Installation de black:
```bash
pip install black
```
## Sous VsCode:
Attention, pour VsCode, Black doit être installé dans votre virtualenv !
Ajouter ces deux lignes dans les settings de VsCode
```json
{
"python.formatting.provider": "black",
"editor.formatOnSave": true
}
```
## Sous Sublime Text
Il faut installer le plugin [sublack](https://packagecontrol.io/packages/sublack) depuis Package Control.
Il suffit ensuite d'ajouter dans les settings du projet (ou en global)
```json
{
"sublack.black_on_save": true
}
```
Si vous utilisez le plugin [anaconda](http://damnwidget.github.io/anaconda/), pensez à modifier les paramètres du linter pep8 pour éviter de recevoir des warnings dans le formatage de black
```json
{
"pep8_ignore": [
"E203",
"E266",
"E501",
"W503"
]
}
```
Sites et doc cools
------------------
[Classy Class-Based Views](http://ccbv.co.uk/projects/Django/1.11/)
Helpers:
`./manage.py makemessages --ignore "env/*" -e py,jinja`
`for f in $(find . -name "*.py" ! -path "*migration*" ! -path "./env/*" ! -path "./doc/*"); do cat ./doc/header "$f" > /tmp/temp && mv /tmp/temp "$f"; done`

3
CONTRIBUTING.rst Normal file
View File

@ -0,0 +1,3 @@
Pour contribuer au projet, vous pouvez vous référer à la documentation disponible à https://sith-ae.readthedocs.io/.
Et n'oubliez pas, contribuer c'est la vie !

2320
Doxyfile

File diff suppressed because it is too large Load Diff

View File

@ -1,21 +0,0 @@
The MIT License (MIT)
Copyright (c) 2016 Skia
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

2
Procfile.service Normal file
View File

@ -0,0 +1,2 @@
redis: redis-server --port $REDIS_PORT
celery: uv run celery -A sith worker --beat -l INFO

1
Procfile.static Normal file
View File

@ -0,0 +1 @@
bundler: npm run serve

113
README.md
View File

@ -1,104 +1,21 @@
[![pipeline status](https://ae-dev.utbm.fr/ae/Sith/badges/master/pipeline.svg)](https://ae-dev.utbm.fr/ae/Sith/commits/master)
[![coverage report](https://ae-dev.utbm.fr/ae/Sith/badges/master/coverage.svg)](https://ae-dev.utbm.fr/ae/Sith/commits/master)
[![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/ambv/black)
[![project chat](https://img.shields.io/badge/zulip-join_chat-brightgreen.svg)](https://ae-dev.zulipchat.com)
# Sith
## Sith AE
[![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](#)
[![License: GPL v3](https://img.shields.io/badge/License-GPLv3-blue.svg)](https://www.gnu.org/licenses/gpl-3.0)
[![CI status](https://github.com/ae-utbm/sith/actions/workflows/ci.yml/badge.svg)](#)
[![Docs status](https://github.com/ae-utbm/sith/actions/workflows/deploy_docs.yml/badge.svg)](https://ae-utbm.github.io/sith)
[![Built with Material for MkDocs](https://img.shields.io/badge/Material_for_MkDocs-526CFE?style=default&logo=MaterialForMkDocs&logoColor=white)](https://squidfunk.github.io/mkdocs-material/)
[![Checked with Biome](https://img.shields.io/badge/Checked_with-Biome-60a5fa?style=flat&logo=biome)](https://biomejs.dev)
[![discord](https://img.shields.io/discord/971448179075731476?label=discord&logo=discord&style=default)](https://discord.gg/xk9wfpsufm)
### Dependencies:
See requirements.txt
### This is the source code of the UTBM's student association available at [https://ae.utbm.fr/](https://ae.utbm.fr/).
You may need to install some dev libraries like `libmysqlclient-dev`, `libssl-dev`, `libjpeg-dev`, `python3-xapian`, or `zlib1g-dev` to install all the
requiered dependancies with pip. You may also need `mysql-client`. Don't also forget `python3-dev` if you don't have it
already.
All documentation is in the `docs` directory and online at [https://ae-utbm.github.io/sith](https://ae-utbm.github.io/sith). This documentation is written in French because it targets a French audience and it's too much work to maintain two versions. The code and code comments are strictly written in English.
You can check all of them with:
```bash
sudo apt install libmysqlclient-dev libssl-dev libjpeg-dev zlib1g-dev python3-dev libffi-dev python3-dev libgraphviz-dev pkg-config python3-xapian gettext
```
On macos, you will need homebrew
```bash
brew install xapian
```
If it doesn't work it's because it need [this pull request](https://github.com/Homebrew/homebrew-core/pull/34835) to be validated.
The development is done with sqlite, but it is advised to set a more robust DBMS for production (Postgresql for example)
### Get started
To start working on the project, just run the following commands:
```bash
git clone https://ae-dev.utbm.fr/ae/Sith.git
cd Sith
virtualenv --system-site-packages --python=python3 env
source env/bin/activate
pip install -r requirements.txt
./manage.py setup
```
To start the simple development server, just run `python3 manage.py runserver`
For more informations, check out the CONTRIBUTING.md file.
### Logging errors with sentry
To connect the app to sentry.io, you must set the variable SENTRY_DSN in your settings custom. It's composed of the full link given on your sentry project
### Generating documentation
There is a Doxyfile at the root of the project, meaning that if you have Doxygen, you can run `doxygen Doxyfile` to
generate a complete HTML documentation that will be available in the *./doc/html/* folder.
### Collecting statics for production:
We use scss in the project. In development environment (DEBUG=True), scss is compiled every time the file is needed. For production, it assumes you have already compiled every files and to do so, you need to use the following commands :
```bash
./manage.py collectstatic # To collect statics
./manage.py compilestatic # To compile scss in those statics
```
### Misc about development
#### Controlling the rights
When you need to protect an object, there are three levels:
* Editing the object properties
* Editing the object various values
* Viewing the object
Now you have many solutions in your model:
* You can define a `is_owned_by(self, user)`, a `can_be_edited_by(self, user)`, and/or a `can_be_viewed_by(self, user)`
method, each returning True is the user passed can edit/view the object, False otherwise.
This allows you to make complex request when the group solution is not powerful enough.
It's useful too when you want to define class-wide permissions, e.g. the club members, that are viewable only for
Subscribers.
* You can add an `owner_group` field, as a ForeignKey to Group. Second is an `edit_groups` field, as a ManyToMany to
Group, and third is a `view_groups`, same as for edit.
Finally, when building a class based view, which is highly advised, you just have to inherit it from CanEditPropMixin,
CanEditMixin, or CanViewMixin, which are located in core.views. Your view will then be protected using either the
appropriate group fields, or the right method to check user permissions.
#### Counting the number of line of code
```bash
sudo apt install cloc
cloc --exclude-dir=doc,env .
```
#### Updating doc/SYNTAX.md
If you make an update in the Markdown syntax parser, it's good to document
update the syntax reference page in `doc/SYNTAX.md`. But updating this file will
break the tests if you don't update the corresponding `doc/SYNTAX.html` file at
the same time.
To do that, simply run `./manage.py markdown > doc/SYNTAX.html`,
and the tests should pass again.
#### If you want to contribute, here's how we recommend to read the docs:
* First, it's advised to read the about part of the project to understand the goals and the mindset of the current and previous maintainers and know what to expect to learn.
* If in the first part you realize that you need more background about what we use, we provide some links to tutorials and documentation at the end of our documentation. Feel free to use it and complete it with what you found helpful.
* Keep in mind that this documentation is thought to be read in order.
> This project is licensed under GNU GPL, see the LICENSE file at the top of the repository for more details.

View File

@ -1,9 +0,0 @@
# TODO
## Easter eggs
* 'A' 'L' 'L' 'O': Entendre le Allooo de Madame Coucoune
* idem avec cacafe
* Un meat spin quelque part
* Konami code

View File

@ -1,23 +0,0 @@
# -*- coding:utf-8 -*
#
# Copyright 2016,2017
# - Skia <skia@libskia.so>
#
# Ce fichier fait partie du site de l'Association des Étudiants de l'UTBM,
# http://ae.utbm.fr.
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License a published by the Free Software
# Foundation; either version 3 of the License, or (at your option) any later
# version.
#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
# details.
#
# You should have received a copy of the GNU General Public License along with
# this program; if not, write to the Free Sofware Foundation, Inc., 59 Temple
# Place - Suite 330, Boston, MA 02111-1307, USA.
#
#

View File

@ -1,278 +0,0 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations, models
import django.core.validators
import accounting.models
class Migration(migrations.Migration):
dependencies = []
operations = [
migrations.CreateModel(
name="AccountingType",
fields=[
(
"id",
models.AutoField(
primary_key=True,
serialize=False,
verbose_name="ID",
auto_created=True,
),
),
(
"code",
models.CharField(
max_length=16,
verbose_name="code",
validators=[
django.core.validators.RegexValidator(
"^[0-9]*$",
"An accounting type code contains only numbers",
)
],
),
),
("label", models.CharField(max_length=128, verbose_name="label")),
(
"movement_type",
models.CharField(
choices=[
("CREDIT", "Credit"),
("DEBIT", "Debit"),
("NEUTRAL", "Neutral"),
],
max_length=12,
verbose_name="movement type",
),
),
],
options={
"verbose_name": "accounting type",
"ordering": ["movement_type", "code"],
},
),
migrations.CreateModel(
name="BankAccount",
fields=[
(
"id",
models.AutoField(
primary_key=True,
serialize=False,
verbose_name="ID",
auto_created=True,
),
),
("name", models.CharField(max_length=30, verbose_name="name")),
(
"iban",
models.CharField(max_length=255, blank=True, verbose_name="iban"),
),
(
"number",
models.CharField(
max_length=255, blank=True, verbose_name="account number"
),
),
],
options={"verbose_name": "Bank account", "ordering": ["club", "name"]},
),
migrations.CreateModel(
name="ClubAccount",
fields=[
(
"id",
models.AutoField(
primary_key=True,
serialize=False,
verbose_name="ID",
auto_created=True,
),
),
("name", models.CharField(max_length=30, verbose_name="name")),
],
options={
"verbose_name": "Club account",
"ordering": ["bank_account", "name"],
},
),
migrations.CreateModel(
name="Company",
fields=[
(
"id",
models.AutoField(
primary_key=True,
serialize=False,
verbose_name="ID",
auto_created=True,
),
),
("name", models.CharField(max_length=60, verbose_name="name")),
],
options={"verbose_name": "company"},
),
migrations.CreateModel(
name="GeneralJournal",
fields=[
(
"id",
models.AutoField(
primary_key=True,
serialize=False,
verbose_name="ID",
auto_created=True,
),
),
("start_date", models.DateField(verbose_name="start date")),
(
"end_date",
models.DateField(
null=True, verbose_name="end date", default=None, blank=True
),
),
("name", models.CharField(max_length=40, verbose_name="name")),
(
"closed",
models.BooleanField(verbose_name="is closed", default=False),
),
(
"amount",
accounting.models.CurrencyField(
decimal_places=2,
default=0,
verbose_name="amount",
max_digits=12,
),
),
(
"effective_amount",
accounting.models.CurrencyField(
decimal_places=2,
default=0,
verbose_name="effective_amount",
max_digits=12,
),
),
],
options={"verbose_name": "General journal", "ordering": ["-start_date"]},
),
migrations.CreateModel(
name="Operation",
fields=[
(
"id",
models.AutoField(
primary_key=True,
serialize=False,
verbose_name="ID",
auto_created=True,
),
),
("number", models.IntegerField(verbose_name="number")),
(
"amount",
accounting.models.CurrencyField(
decimal_places=2, max_digits=12, verbose_name="amount"
),
),
("date", models.DateField(verbose_name="date")),
("remark", models.CharField(max_length=128, verbose_name="comment")),
(
"mode",
models.CharField(
choices=[
("CHECK", "Check"),
("CASH", "Cash"),
("TRANSFERT", "Transfert"),
("CARD", "Credit card"),
],
max_length=255,
verbose_name="payment method",
),
),
(
"cheque_number",
models.CharField(
max_length=32,
null=True,
verbose_name="cheque number",
default="",
blank=True,
),
),
("done", models.BooleanField(verbose_name="is done", default=False)),
(
"target_type",
models.CharField(
choices=[
("USER", "User"),
("CLUB", "Club"),
("ACCOUNT", "Account"),
("COMPANY", "Company"),
("OTHER", "Other"),
],
max_length=10,
verbose_name="target type",
),
),
(
"target_id",
models.IntegerField(
null=True, verbose_name="target id", blank=True
),
),
(
"target_label",
models.CharField(
max_length=32,
blank=True,
verbose_name="target label",
default="",
),
),
(
"accounting_type",
models.ForeignKey(
null=True,
related_name="operations",
verbose_name="accounting type",
to="accounting.AccountingType",
blank=True,
),
),
],
options={"ordering": ["-number"]},
),
migrations.CreateModel(
name="SimplifiedAccountingType",
fields=[
(
"id",
models.AutoField(
primary_key=True,
serialize=False,
verbose_name="ID",
auto_created=True,
),
),
("label", models.CharField(max_length=128, verbose_name="label")),
(
"accounting_type",
models.ForeignKey(
verbose_name="simplified accounting types",
to="accounting.AccountingType",
related_name="simplified_types",
),
),
],
options={
"verbose_name": "simplified type",
"ordering": ["accounting_type__movement_type", "accounting_type__code"],
},
),
]

View File

@ -1,94 +0,0 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("club", "0001_initial"),
("accounting", "0001_initial"),
("core", "0001_initial"),
]
operations = [
migrations.AddField(
model_name="operation",
name="invoice",
field=models.ForeignKey(
null=True,
related_name="operations",
verbose_name="invoice",
to="core.SithFile",
blank=True,
),
),
migrations.AddField(
model_name="operation",
name="journal",
field=models.ForeignKey(
verbose_name="journal",
to="accounting.GeneralJournal",
related_name="operations",
),
),
migrations.AddField(
model_name="operation",
name="linked_operation",
field=models.OneToOneField(
blank=True,
to="accounting.Operation",
null=True,
related_name="operation_linked_to",
verbose_name="linked operation",
default=None,
),
),
migrations.AddField(
model_name="operation",
name="simpleaccounting_type",
field=models.ForeignKey(
null=True,
related_name="operations",
verbose_name="simple type",
to="accounting.SimplifiedAccountingType",
blank=True,
),
),
migrations.AddField(
model_name="generaljournal",
name="club_account",
field=models.ForeignKey(
verbose_name="club account",
to="accounting.ClubAccount",
related_name="journals",
),
),
migrations.AddField(
model_name="clubaccount",
name="bank_account",
field=models.ForeignKey(
verbose_name="bank account",
to="accounting.BankAccount",
related_name="club_accounts",
),
),
migrations.AddField(
model_name="clubaccount",
name="club",
field=models.ForeignKey(
verbose_name="club", to="club.Club", related_name="club_account"
),
),
migrations.AddField(
model_name="bankaccount",
name="club",
field=models.ForeignKey(
verbose_name="club", to="club.Club", related_name="bank_accounts"
),
),
migrations.AlterUniqueTogether(
name="operation", unique_together=set([("number", "journal")])
),
]

View File

@ -1,50 +0,0 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations, models
import phonenumber_field.modelfields
class Migration(migrations.Migration):
dependencies = [("accounting", "0002_auto_20160824_2152")]
operations = [
migrations.AddField(
model_name="company",
name="city",
field=models.CharField(blank=True, verbose_name="city", max_length=60),
),
migrations.AddField(
model_name="company",
name="country",
field=models.CharField(blank=True, verbose_name="country", max_length=32),
),
migrations.AddField(
model_name="company",
name="email",
field=models.EmailField(blank=True, verbose_name="email", max_length=254),
),
migrations.AddField(
model_name="company",
name="phone",
field=phonenumber_field.modelfields.PhoneNumberField(
blank=True, verbose_name="phone", max_length=128
),
),
migrations.AddField(
model_name="company",
name="postcode",
field=models.CharField(blank=True, verbose_name="postcode", max_length=10),
),
migrations.AddField(
model_name="company",
name="street",
field=models.CharField(blank=True, verbose_name="street", max_length=60),
),
migrations.AddField(
model_name="company",
name="website",
field=models.CharField(blank=True, verbose_name="website", max_length=64),
),
]

View File

@ -1,51 +0,0 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [("accounting", "0003_auto_20160824_2203")]
operations = [
migrations.CreateModel(
name="Label",
fields=[
(
"id",
models.AutoField(
verbose_name="ID",
primary_key=True,
auto_created=True,
serialize=False,
),
),
("name", models.CharField(max_length=64, verbose_name="label")),
(
"club_account",
models.ForeignKey(
related_name="labels",
verbose_name="club account",
to="accounting.ClubAccount",
),
),
],
),
migrations.AddField(
model_name="operation",
name="label",
field=models.ForeignKey(
on_delete=django.db.models.deletion.SET_NULL,
related_name="operations",
null=True,
blank=True,
verbose_name="label",
to="accounting.Label",
),
),
migrations.AlterUniqueTogether(
name="label", unique_together=set([("name", "club_account")])
),
]

View File

@ -1,19 +0,0 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [("accounting", "0004_auto_20161005_1505")]
operations = [
migrations.AlterField(
model_name="operation",
name="remark",
field=models.CharField(
null=True, max_length=128, blank=True, verbose_name="comment"
),
)
]

View File

@ -1,542 +0,0 @@
# -*- coding:utf-8 -*
#
# Copyright 2016,2017
# - Skia <skia@libskia.so>
#
# Ce fichier fait partie du site de l'Association des Étudiants de l'UTBM,
# http://ae.utbm.fr.
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License a published by the Free Software
# Foundation; either version 3 of the License, or (at your option) any later
# version.
#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
# details.
#
# You should have received a copy of the GNU General Public License along with
# this program; if not, write to the Free Sofware Foundation, Inc., 59 Temple
# Place - Suite 330, Boston, MA 02111-1307, USA.
#
#
from django.core.urlresolvers import reverse
from django.core.exceptions import ValidationError
from django.core import validators
from django.db import models
from django.conf import settings
from django.utils.translation import ugettext_lazy as _
from django.template import defaultfilters
from phonenumber_field.modelfields import PhoneNumberField
from decimal import Decimal
from core.models import User, SithFile
from club.models import Club
class CurrencyField(models.DecimalField):
"""
This is a custom database field used for currency
"""
def __init__(self, *args, **kwargs):
kwargs["max_digits"] = 12
kwargs["decimal_places"] = 2
super(CurrencyField, self).__init__(*args, **kwargs)
def to_python(self, value):
try:
return super(CurrencyField, self).to_python(value).quantize(Decimal("0.01"))
except AttributeError:
return None
# Accounting classes
class Company(models.Model):
name = models.CharField(_("name"), max_length=60)
street = models.CharField(_("street"), max_length=60, blank=True)
city = models.CharField(_("city"), max_length=60, blank=True)
postcode = models.CharField(_("postcode"), max_length=10, blank=True)
country = models.CharField(_("country"), max_length=32, blank=True)
phone = PhoneNumberField(_("phone"), blank=True)
email = models.EmailField(_("email"), blank=True)
website = models.CharField(_("website"), max_length=64, blank=True)
class Meta:
verbose_name = _("company")
def is_owned_by(self, user):
"""
Method to see if that object can be edited by the given user
"""
if user.is_in_group(settings.SITH_GROUP_ACCOUNTING_ADMIN_ID):
return True
return False
def can_be_edited_by(self, user):
"""
Method to see if that object can be edited by the given user
"""
for club in user.memberships.filter(end_date=None).all():
if club and club.role == settings.SITH_CLUB_ROLES_ID["Treasurer"]:
return True
return False
def can_be_viewed_by(self, user):
"""
Method to see if that object can be viewed by the given user
"""
for club in user.memberships.filter(end_date=None).all():
if club and club.role >= settings.SITH_CLUB_ROLES_ID["Treasurer"]:
return True
return False
def get_absolute_url(self):
return reverse("accounting:co_edit", kwargs={"co_id": self.id})
def get_display_name(self):
return self.name
def __str__(self):
return self.name
class BankAccount(models.Model):
name = models.CharField(_("name"), max_length=30)
iban = models.CharField(_("iban"), max_length=255, blank=True)
number = models.CharField(_("account number"), max_length=255, blank=True)
club = models.ForeignKey(Club, related_name="bank_accounts", verbose_name=_("club"))
class Meta:
verbose_name = _("Bank account")
ordering = ["club", "name"]
def is_owned_by(self, user):
"""
Method to see if that object can be edited by the given user
"""
if user.is_in_group(settings.SITH_GROUP_ACCOUNTING_ADMIN_ID):
return True
m = self.club.get_membership_for(user)
if m is not None and m.role >= settings.SITH_CLUB_ROLES_ID["Treasurer"]:
return True
return False
def get_absolute_url(self):
return reverse("accounting:bank_details", kwargs={"b_account_id": self.id})
def __str__(self):
return self.name
class ClubAccount(models.Model):
name = models.CharField(_("name"), max_length=30)
club = models.ForeignKey(Club, related_name="club_account", verbose_name=_("club"))
bank_account = models.ForeignKey(
BankAccount, related_name="club_accounts", verbose_name=_("bank account")
)
class Meta:
verbose_name = _("Club account")
ordering = ["bank_account", "name"]
def is_owned_by(self, user):
"""
Method to see if that object can be edited by the given user
"""
if user.is_in_group(settings.SITH_GROUP_ACCOUNTING_ADMIN_ID):
return True
return False
def can_be_edited_by(self, user):
"""
Method to see if that object can be edited by the given user
"""
m = self.club.get_membership_for(user)
if m and m.role == settings.SITH_CLUB_ROLES_ID["Treasurer"]:
return True
return False
def can_be_viewed_by(self, user):
"""
Method to see if that object can be viewed by the given user
"""
m = self.club.get_membership_for(user)
if m and m.role >= settings.SITH_CLUB_ROLES_ID["Treasurer"]:
return True
return False
def has_open_journal(self):
for j in self.journals.all():
if not j.closed:
return True
return False
def get_open_journal(self):
return self.journals.filter(closed=False).first()
def get_absolute_url(self):
return reverse("accounting:club_details", kwargs={"c_account_id": self.id})
def __str__(self):
return self.name
def get_display_name(self):
return _("%(club_account)s on %(bank_account)s") % {
"club_account": self.name,
"bank_account": self.bank_account,
}
class GeneralJournal(models.Model):
"""
Class storing all the operations for a period of time
"""
start_date = models.DateField(_("start date"))
end_date = models.DateField(_("end date"), null=True, blank=True, default=None)
name = models.CharField(_("name"), max_length=40)
closed = models.BooleanField(_("is closed"), default=False)
club_account = models.ForeignKey(
ClubAccount, related_name="journals", null=False, verbose_name=_("club account")
)
amount = CurrencyField(_("amount"), default=0)
effective_amount = CurrencyField(_("effective_amount"), default=0)
class Meta:
verbose_name = _("General journal")
ordering = ["-start_date"]
def is_owned_by(self, user):
"""
Method to see if that object can be edited by the given user
"""
if user.is_in_group(settings.SITH_GROUP_ACCOUNTING_ADMIN_ID):
return True
if self.club_account.can_be_edited_by(user):
return True
return False
def can_be_edited_by(self, user):
"""
Method to see if that object can be edited by the given user
"""
if user.is_in_group(settings.SITH_GROUP_ACCOUNTING_ADMIN_ID):
return True
if self.club_account.can_be_edited_by(user):
return True
return False
def can_be_viewed_by(self, user):
return self.club_account.can_be_viewed_by(user)
def get_absolute_url(self):
return reverse("accounting:journal_details", kwargs={"j_id": self.id})
def __str__(self):
return self.name
def update_amounts(self):
self.amount = 0
self.effective_amount = 0
for o in self.operations.all():
if o.accounting_type.movement_type == "CREDIT":
if o.done:
self.effective_amount += o.amount
self.amount += o.amount
else:
if o.done:
self.effective_amount -= o.amount
self.amount -= o.amount
self.save()
class Operation(models.Model):
"""
An operation is a line in the journal, a debit or a credit
"""
number = models.IntegerField(_("number"))
journal = models.ForeignKey(
GeneralJournal, related_name="operations", null=False, verbose_name=_("journal")
)
amount = CurrencyField(_("amount"))
date = models.DateField(_("date"))
remark = models.CharField(_("comment"), max_length=128, null=True, blank=True)
mode = models.CharField(
_("payment method"),
max_length=255,
choices=settings.SITH_ACCOUNTING_PAYMENT_METHOD,
)
cheque_number = models.CharField(
_("cheque number"), max_length=32, default="", null=True, blank=True
)
invoice = models.ForeignKey(
SithFile,
related_name="operations",
verbose_name=_("invoice"),
null=True,
blank=True,
)
done = models.BooleanField(_("is done"), default=False)
simpleaccounting_type = models.ForeignKey(
"SimplifiedAccountingType",
related_name="operations",
verbose_name=_("simple type"),
null=True,
blank=True,
)
accounting_type = models.ForeignKey(
"AccountingType",
related_name="operations",
verbose_name=_("accounting type"),
null=True,
blank=True,
)
label = models.ForeignKey(
"Label",
related_name="operations",
verbose_name=_("label"),
null=True,
blank=True,
on_delete=models.SET_NULL,
)
target_type = models.CharField(
_("target type"),
max_length=10,
choices=[
("USER", _("User")),
("CLUB", _("Club")),
("ACCOUNT", _("Account")),
("COMPANY", _("Company")),
("OTHER", _("Other")),
],
)
target_id = models.IntegerField(_("target id"), null=True, blank=True)
target_label = models.CharField(
_("target label"), max_length=32, default="", blank=True
)
linked_operation = models.OneToOneField(
"self",
related_name="operation_linked_to",
verbose_name=_("linked operation"),
null=True,
blank=True,
default=None,
)
class Meta:
unique_together = ("number", "journal")
ordering = ["-number"]
def __getattribute__(self, attr):
if attr == "target":
return self.get_target()
else:
return object.__getattribute__(self, attr)
def clean(self):
super(Operation, self).clean()
if self.date is None:
raise ValidationError(_("The date must be set."))
elif self.date < self.journal.start_date:
raise ValidationError(
_(
"""The date can not be before the start date of the journal, which is
%(start_date)s."""
)
% {
"start_date": defaultfilters.date(
self.journal.start_date, settings.DATE_FORMAT
)
}
)
if self.target_type != "OTHER" and self.get_target() is None:
raise ValidationError(_("Target does not exists"))
if self.target_type == "OTHER" and self.target_label == "":
raise ValidationError(
_("Please add a target label if you set no existing target")
)
if not self.accounting_type and not self.simpleaccounting_type:
raise ValidationError(
_(
"You need to provide ether a simplified accounting type or a standard accounting type"
)
)
if self.simpleaccounting_type:
self.accounting_type = self.simpleaccounting_type.accounting_type
@property
def target(self):
return self.get_target()
def get_target(self):
tar = None
if self.target_type == "USER":
tar = User.objects.filter(id=self.target_id).first()
elif self.target_type == "CLUB":
tar = Club.objects.filter(id=self.target_id).first()
elif self.target_type == "ACCOUNT":
tar = ClubAccount.objects.filter(id=self.target_id).first()
elif self.target_type == "COMPANY":
tar = Company.objects.filter(id=self.target_id).first()
return tar
def save(self):
if self.number is None:
self.number = self.journal.operations.count() + 1
super(Operation, self).save()
self.journal.update_amounts()
def is_owned_by(self, user):
"""
Method to see if that object can be edited by the given user
"""
if user.is_in_group(settings.SITH_GROUP_ACCOUNTING_ADMIN_ID):
return True
if self.journal.closed:
return False
m = self.journal.club_account.club.get_membership_for(user)
if m is not None and m.role >= settings.SITH_CLUB_ROLES_ID["Treasurer"]:
return True
return False
def can_be_edited_by(self, user):
"""
Method to see if that object can be edited by the given user
"""
if user.is_in_group(settings.SITH_GROUP_ACCOUNTING_ADMIN_ID):
return True
if self.journal.closed:
return False
m = self.journal.club_account.club.get_membership_for(user)
if m is not None and m.role == settings.SITH_CLUB_ROLES_ID["Treasurer"]:
return True
return False
def get_absolute_url(self):
return reverse("accounting:journal_details", kwargs={"j_id": self.journal.id})
def __str__(self):
return "%d € | %s | %s | %s" % (
self.amount,
self.date,
self.accounting_type,
self.done,
)
class AccountingType(models.Model):
"""
Class describing the accounting types.
Thoses are numbers used in accounting to classify operations
"""
code = models.CharField(
_("code"),
max_length=16,
validators=[
validators.RegexValidator(
r"^[0-9]*$", _("An accounting type code contains only numbers")
)
],
)
label = models.CharField(_("label"), max_length=128)
movement_type = models.CharField(
_("movement type"),
choices=[
("CREDIT", _("Credit")),
("DEBIT", _("Debit")),
("NEUTRAL", _("Neutral")),
],
max_length=12,
)
class Meta:
verbose_name = _("accounting type")
ordering = ["movement_type", "code"]
def is_owned_by(self, user):
"""
Method to see if that object can be edited by the given user
"""
if user.is_in_group(settings.SITH_GROUP_ACCOUNTING_ADMIN_ID):
return True
return False
def get_absolute_url(self):
return reverse("accounting:type_list")
def __str__(self):
return self.code + " - " + self.get_movement_type_display() + " - " + self.label
class SimplifiedAccountingType(models.Model):
"""
Class describing the simplified accounting types.
"""
label = models.CharField(_("label"), max_length=128)
accounting_type = models.ForeignKey(
AccountingType,
related_name="simplified_types",
verbose_name=_("simplified accounting types"),
)
class Meta:
verbose_name = _("simplified type")
ordering = ["accounting_type__movement_type", "accounting_type__code"]
@property
def movement_type(self):
return self.accounting_type.movement_type
def get_movement_type_display(self):
return self.accounting_type.get_movement_type_display()
def get_absolute_url(self):
return reverse("accounting:simple_type_list")
def __str__(self):
return (
self.get_movement_type_display()
+ " - "
+ self.accounting_type.code
+ " - "
+ self.label
)
class Label(models.Model):
"""Label allow a club to sort its operations"""
name = models.CharField(_("label"), max_length=64)
club_account = models.ForeignKey(
ClubAccount, related_name="labels", verbose_name=_("club account")
)
class Meta:
unique_together = ("name", "club_account")
def __str__(self):
return "%s (%s)" % (self.name, self.club_account.name)
def get_absolute_url(self):
return reverse(
"accounting:label_list", kwargs={"clubaccount_id": self.club_account.id}
)
def is_owned_by(self, user):
return self.club_account.is_owned_by(user)
def can_be_edited_by(self, user):
return self.club_account.can_be_edited_by(user)
def can_be_viewed_by(self, user):
return self.club_account.can_be_viewed_by(user)

View File

@ -1,27 +0,0 @@
{% extends "core/base.jinja" %}
{% block title %}
{% trans %}Accounting type list{% endtrans %}
{% endblock %}
{% block content %}
<div id="accounting">
<p>
<a href="{{ url('accounting:bank_list') }}">{% trans %}Accounting{% endtrans %}</a> >
{% trans %}Accounting types{% endtrans %}
</p>
<hr>
<p><a href="{{ url('accounting:type_new') }}">{% trans %}New accounting type{% endtrans %}</a></p>
{% if accountingtype_list %}
<h3>{% trans %}Accounting type list{% endtrans %}</h3>
<ul>
{% for a in accountingtype_list %}
<li><a href="{{ url('accounting:type_edit', type_id=a.id) }}">{{ a }}</a></li>
{% endfor %}
</ul>
{% else %}
{% trans %}There is no types in this website.{% endtrans %}
{% endif %}
</div>
{% endblock %}

View File

@ -1,38 +0,0 @@
{% extends "core/base.jinja" %}
{% block title %}
{% trans %}Bank account: {% endtrans %}{{ object.name }}
{% endblock %}
{% block content %}
<div id="accounting">
<p>
<a href="{{ url('accounting:bank_list') }}">{% trans %}Accounting{% endtrans %}</a> >
{{ object.name }}
</p>
<hr>
<h2>{% trans %}Bank account: {% endtrans %}{{ object.name }}</h2>
{% if user.is_in_group(settings.SITH_GROUP_ACCOUNTING_ADMIN_ID) and not object.club_accounts.exists() %}
<a href="{{ url('accounting:bank_delete', b_account_id=object.id) }}">{% trans %}Delete{% endtrans %}</a>
{% endif %}
<h4>{% trans %}Infos{% endtrans %}</h4>
<ul>
<li><strong>{% trans %}IBAN: {% endtrans %}</strong>{{ object.iban }}</li>
<li><strong>{% trans %}Number: {% endtrans %}</strong>{{ object.number }}</li>
</ul>
<p><a href="{{ url('accounting:club_new') }}?parent={{ object.id }}">{% trans %}New club account{% endtrans %}</a></p>
<ul>
{% for c in object.club_accounts.all() %}
<li><a href="{{ url('accounting:club_details', c_account_id=c.id) }}">{{ c }}</a>
- <a href="{{ url('accounting:club_edit', c_account_id=c.id) }}">{% trans %}Edit{% endtrans %}</a>
{% if c.journals.count() == 0 %}
- <a href="{{ url('accounting:club_delete', c_account_id=c.id) }}">{% trans %}Delete{% endtrans %}</a>
{% endif %}
</li>
{% endfor %}
</ul>
</div>
{% endblock %}

View File

@ -1,33 +0,0 @@
{% extends "core/base.jinja" %}
{% block title %}
{% trans %}Bank account list{% endtrans %}
{% endblock %}
{% block content %}
<div id="accounting">
<h4>
{% trans %}Accounting{% endtrans %}
</h4>
{% if user.is_in_group(settings.SITH_GROUP_ACCOUNTING_ADMIN_ID) %}
<p><a href="{{ url('accounting:simple_type_list') }}">{% trans %}Manage simplified types{% endtrans %}</a></p>
<p><a href="{{ url('accounting:type_list') }}">{% trans %}Manage accounting types{% endtrans %}</a></p>
<p><a href="{{ url('accounting:bank_new') }}">{% trans %}New bank account{% endtrans %}</a></p>
{% endif %}
{% if bankaccount_list %}
<h3>{% trans %}Bank account list{% endtrans %}</h3>
<ul>
{% for a in object_list %}
<li><a href="{{ url('accounting:bank_details', b_account_id=a.id) }}">{{ a }}</a>
- <a href="{{ url('accounting:bank_edit', b_account_id=a.id) }}">{% trans %}Edit{% endtrans %}</a>
</li>
{% endfor %}
</ul>
{% else %}
{% trans %}There is no accounts in this website.{% endtrans %}
{% endif %}
</div>
{% endblock %}

View File

@ -1,68 +0,0 @@
{% extends "core/base.jinja" %}
{% block title %}
{% trans %}Club account:{% endtrans %} {{ object.name }}
{% endblock %}
{% block content %}
<div id="accounting">
<p>
<a href="{{ url('accounting:bank_list') }}">{% trans %}Accounting{% endtrans %}</a> >
<a href="{{ url('accounting:bank_details', b_account_id=object.bank_account.id) }}">{{object.bank_account }}</a> >
{{ object }}
</p>
<hr>
<h2>{% trans %}Club account:{% endtrans %} {{ object.name }}</h2>
{% if user.is_root and not object.journals.exists() %}
<a href="{{ url('accounting:club_delete', c_account_id=object.id) }}">{% trans %}Delete{% endtrans %}</a>
{% endif %}
{% if user.is_in_group(settings.SITH_GROUP_ACCOUNTING_ADMIN_ID) %}
<p><a href="{{ url('accounting:label_new') }}?parent={{ object.id }}">{% trans %}New label{% endtrans %}</a></p>
{% endif %}
<p><a href="{{ url('accounting:label_list', clubaccount_id=object.id) }}">{% trans %}Label list{% endtrans %}</a></p>
{% if not object.has_open_journal() %}
<p><a href="{{ url('accounting:journal_new') }}?parent={{ object.id }}">{% trans %}New journal{% endtrans %}</a></p>
{% else %}
<p>{% trans %}You can not create new journal while you still have one opened{% endtrans %}</p>
{% endif %}
<table>
<thead>
<tr>
<td>{% trans %}Name{% endtrans %}</td>
<td>{% trans %}Start{% endtrans %}</td>
<td>{% trans %}End{% endtrans %}</td>
<td>{% trans %}Amount{% endtrans %}</td>
<td>{% trans %}Effective amount{% endtrans %}</td>
<td>{% trans %}Closed{% endtrans %}</td>
<td>{% trans %}Actions{% endtrans %}</td>
</tr>
</thead>
<tbody>
{% for j in object.journals.all() %}
<tr>
<td>{{ j.name }}</td>
<td>{{ j.start_date }}</td>
{% if j.end_date %}
<td>{{ j.end_date }}</td>
{% else %}
<td> - </td>
{% endif %}
<td>{{ j.amount }} €</td>
<td>{{ j.effective_amount }} €</td>
{% if j.closed %}
<td>{% trans %}Yes{% endtrans %}</td>
{% else %}
<td>{% trans %}No{% endtrans %}</td>
{% endif %}
<td> <a href="{{ url('accounting:journal_details', j_id=j.id) }}">{% trans %}View{% endtrans %}</a>
<a href="{{ url('accounting:journal_edit', j_id=j.id) }}">{% trans %}Edit{% endtrans %}</a>
{% if user.is_in_group(settings.SITH_GROUP_ACCOUNTING_ADMIN_ID) and j.operations.count() == 0 %}
<a href="{{ url('accounting:journal_delete', j_id=j.id) }}">{% trans %}Delete{% endtrans %}</a>
{% endif %}
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% endblock %}

View File

@ -1,29 +0,0 @@
{% extends "core/base.jinja" %}
{% block title %}
{% trans %}Company list{% endtrans %}
{% endblock %}
{% block content %}
<div id="accounting">
{% if user.is_in_group(settings.SITH_GROUP_ACCOUNTING_ADMIN_ID) or user.is_root %}
<p><a href="{{ url('accounting:co_new') }}">{% trans %}Create new company{% endtrans %}</a></p>
{% endif %}
</br>
<table>
<thead>
<tr>
<td>{% trans %}Companies{% endtrans %}</td>
</tr>
</thead>
<tbody>
{% for o in object_list %}
<tr>
<td><a href="{{ url('accounting:co_edit', co_id=o.id) }}">{{ o.get_display_name() }}</a></td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% endblock %}

View File

@ -1,100 +0,0 @@
{% extends "core/base.jinja" %}
{% block title %}
{% trans %}General journal:{% endtrans %} {{ object.name }}
{% endblock %}
{% block content %}
<div id="accounting">
<p>
<a href="{{ url('accounting:bank_list') }}">{% trans %}Accounting{% endtrans %}</a> >
<a href="{{ url('accounting:bank_details', b_account_id=object.club_account.bank_account.id) }}">{{object.club_account.bank_account }}</a> >
<a href="{{ url('accounting:club_details', c_account_id=object.club_account.id) }}">{{ object.club_account }}</a> >
{{ object.name }}
</p>
<hr>
<h2>{% trans %}General journal:{% endtrans %} {{ object.name }}</h2>
<p><a href="{{ url('accounting:label_new') }}?parent={{ object.club_account.id }}">{% trans %}New label{% endtrans %}</a></p>
<p><a href="{{ url('accounting:label_list', clubaccount_id=object.club_account.id) }}">{% trans %}Label list{% endtrans %}</a></p>
<p><a href="{{ url('accounting:co_list') }}">{% trans %}Company list{% endtrans %}</a></p>
<p><strong>{% trans %}Amount: {% endtrans %}</strong>{{ object.amount }} € -
<strong>{% trans %}Effective amount: {% endtrans %}</strong>{{ object.effective_amount }} €</p>
{% if object.closed %}
<p>{% trans %}Journal is closed, you can not create operation{% endtrans %}</p>
{% else %}
<p><a href="{{ url('accounting:op_new', j_id=object.id) }}">{% trans %}New operation{% endtrans %}</a></p>
</br>
{% endif %}
<div class="journal-table">
<table>
<thead>
<tr>
<td>{% trans %}Nb{% endtrans %}</td>
<td>{% trans %}Date{% endtrans %}</td>
<td>{% trans %}Label{% endtrans %}</td>
<td>{% trans %}Amount{% endtrans %}</td>
<td>{% trans %}Payment mode{% endtrans %}</td>
<td>{% trans %}Target{% endtrans %}</td>
<td>{% trans %}Code{% endtrans %}</td>
<td>{% trans %}Nature{% endtrans %}</td>
<td>{% trans %}Done{% endtrans %}</td>
<td>{% trans %}Comment{% endtrans %}</td>
<td>{% trans %}File{% endtrans %}</td>
<td>{% trans %}Actions{% endtrans %}</td>
<td>{% trans %}PDF{% endtrans %}</td>
</tr>
</thead>
<tbody>
{% for o in object.operations.all() %}
<tr>
<td>{{ o.number }}</td>
<td>{{ o.date }}</td>
<td>{{ o.label or "" }}</td>
{% if o.accounting_type.movement_type == "DEBIT" %}
<td class="neg-amount">&nbsp;{{ o.amount }}&nbsp;€</td>
{% else %}
<td class="pos-amount">&nbsp;{{ o.amount }}&nbsp;€</td>
{% endif %}
<td>{{ o.get_mode_display() }}</td>
{% if o.target_type == "OTHER" %}
<td>{{ o.target_label }}</td>
{% else %}
<td><a href="{{ o.target.get_absolute_url() }}">{{ o.target.get_display_name() }}</a></td>
{% endif %}
<td>{{ o.accounting_type.code }}</td>
<td>{{ o.accounting_type.label }}</td>
{% if o.done %}
<td>{% trans %}Yes{% endtrans %}</td>
{% else %}
<td>{% trans %}No{% endtrans %}</td>
{% endif %}
<td>{{ o.remark }}
{% if not o.linked_operation and o.target_type == "ACCOUNT" and not o.target.has_open_journal() %}
<p><strong>
{% trans %}Warning: this operation has no linked operation because the targeted club account has no opened journal.{% endtrans %}
</strong></p>
<p><strong>
{% trans url=o.target.get_absolute_url() %}Open a journal in <a href="{{ url }}">this club account</a>, then save this operation again to make the linked operation.{% endtrans %}
</strong></p>
{% endif %}
</td>
{% if o.invoice %}
<td><a href="{{ url('core:download', file_id=o.invoice.id) }}">{{ o.invoice.name }}</a></td>
{% else %}
<td>-</td>
{% endif %}
<td>
{% if o.journal.club_account.bank_account.name != "AE TI" and o.journal.club_account.bank_account.name != "TI" or user.is_in_group(settings.SITH_GROUP_ACCOUNTING_ADMIN_ID) %}
{% if not o.journal.closed %}
<a href="{{ url('accounting:op_edit', op_id=o.id) }}">{% trans %}Edit{% endtrans %}</a>
{% endif %}
{% endif %}
</td>
<td><a href="{{ url('accounting:op_pdf', op_id=o.id) }}">{% trans %}Generate{% endtrans %}</a></td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
{% endblock %}

View File

@ -1,33 +0,0 @@
{% extends "core/base.jinja" %}
{% block title %}
{% trans %}General journal:{% endtrans %} {{ object.name }}
{% endblock %}
{% block content %}
<div id="accounting">
<h3>{% trans %}Accounting statement: {% endtrans %} {{ object.name }}</h3>
<table>
<thead>
<tr>
<td>{% trans %}Operation type{% endtrans %}</td>
<td>{% trans %}Sum{% endtrans %}</td>
</tr>
</thead>
<tbody>
{% for k,v in statement.items() %}
<tr>
<td>{{ k }}</td>
<td>{{ v }}</td>
</tr>
{% endfor %}
</tbody>
</table>
<p><strong>{% trans %}Amount: {% endtrans %}</strong>{{ object.amount }} €</p>
<p><strong>{% trans %}Effective amount: {% endtrans %}</strong>{{ object.effective_amount }} €</p>
</div>
{% endblock %}

View File

@ -1,57 +0,0 @@
{% extends "core/base.jinja" %}
{% block title %}
{% trans %}General journal:{% endtrans %} {{ object.name }}
{% endblock %}
{% macro display_tables(dict) %}
<div id="accounting">
<h6>{% trans %}Credit{% endtrans %}</h6>
<table>
<thead>
<tr>
<td>{% trans %}Nature of operation{% endtrans %}</td>
<td>{% trans %}Sum{% endtrans %}</td>
</tr>
</thead>
<tbody>
{% for k,v in dict['CREDIT'].items() %}
<tr>
<td>{{ k }}</td>
<td>{{ v }}</td>
</tr>
{% endfor %}
</tbody>
</table>
{% trans %}Total: {% endtrans %}{{ dict['CREDIT_sum'] }}
<h6>{% trans %}Debit{% endtrans %}</h6>
<table>
<thead>
<tr>
<td>{% trans %}Nature of operation{% endtrans %}</td>
<td>{% trans %}Sum{% endtrans %}</td>
</tr>
</thead>
<tbody>
{% for k,v in dict['DEBIT'].items() %}
<tr>
<td>{{ k }}</td>
<td>{{ v }}</td>
</tr>
{% endfor %}
</tbody>
</table>
{% trans %}Total: {% endtrans %}{{ dict['DEBIT_sum'] }}
{% endmacro %}
{% block content %}
<h3>{% trans %}Statement by nature: {% endtrans %} {{ object.name }}</h3>
{% for k,v in statement.items() %}
<h4 style="background: lightblue; padding: 4px;">{{ k }} : {{ v['CREDIT_sum'] - v['DEBIT_sum'] }}</h4>
{{ display_tables(v) }}
<hr>
{% endfor %}
</div>
{% endblock %}

View File

@ -1,68 +0,0 @@
{% extends "core/base.jinja" %}
{% block title %}
{% trans %}General journal:{% endtrans %} {{ object.name }}
{% endblock %}
{% block content %}
<div id="accounting">
<h3>{% trans %}Statement by person: {% endtrans %} {{ object.name }}</h3>
<h4>{% trans %}Credit{% endtrans %}</h4>
<table>
<thead>
<tr>
<td>{% trans %}Target of the operation{% endtrans %}</td>
<td>{% trans %}Sum{% endtrans %}</td>
</tr>
</thead>
<tbody>
{% for key in credit_statement.keys() %}
<tr>
{% if key.target_type == "OTHER" %}
<td>{{ o.target_label }}</td>
{% elif key %}
<td><a href="{{ key.get_absolute_url() }}">{{ key.get_display_name() }}</a></td>
{% else %}
<td></td>
{% endif %}
<td>{{ credit_statement[key] }}</td>
</tr>
{% endfor %}
</tbody>
</table>
<p>Total : {{ total_credit }}</p>
<h4>{% trans %}Debit{% endtrans %}</h4>
<table>
<thead>
<tr>
<td>{% trans %}Target of the operation{% endtrans %}</td>
<td>{% trans %}Sum{% endtrans %}</td>
</tr>
</thead>
<tbody>
{% for key in debit_statement.keys() %}
<tr>
{% if key.target_type == "OTHER" %}
<td>{{ o.target_label }}</td>
{% elif key %}
<td><a href="{{ key.get_absolute_url() }}">{{ key.get_display_name() }}</a></td>
{% else %}
<td></td>
{% endif %}
<td>{{ debit_statement[key] }}</td>
</tr>
{% endfor %}
</tbody>
</table>
<p>Total : {{ total_debit }}</p>
</div>
{% endblock %}

View File

@ -1,36 +0,0 @@
{% extends "core/base.jinja" %}
{% block title %}
{% trans %}Label list{% endtrans %}
{% endblock %}
{% block content %}
<div id="accounting">
<p>
<a href="{{ url('accounting:bank_list') }}">{% trans %}Accounting{% endtrans %}</a> >
<a href="{{ url('accounting:bank_details', b_account_id=object.bank_account.id) }}">{{object.bank_account }}</a> >
<a href="{{ url('accounting:club_details', c_account_id=object.id) }}">{{ object }}</a>
</p>
<hr>
<p><a href="{{ url('accounting:club_details', c_account_id=object.id) }}">{% trans %}Back to club account{% endtrans %}</a></p>
{% if user.is_in_group(settings.SITH_GROUP_ACCOUNTING_ADMIN_ID) %}
<p><a href="{{ url('accounting:label_new') }}?parent={{ object.id }}">{% trans %}New label{% endtrans %}</a></p>
{% endif %}
{% if object.labels.all() %}
<h3>{% trans %}Label list{% endtrans %}</h3>
<ul>
{% for l in object.labels.all() %}
<li><a href="{{ url('accounting:label_edit', label_id=l.id) }}">{{ l }}</a>
{% if user.is_in_group(settings.SITH_GROUP_ACCOUNTING_ADMIN_ID) %}
-
<a href="{{ url('accounting:label_delete', label_id=l.id) }}">{% trans %}Delete{% endtrans %}</a>
{% endif %}
</li>
{% endfor %}
</ul>
{% else %}
{% trans %}There is no label in this club account.{% endtrans %}
{% endif %}
</div>
{% endblock %}

View File

@ -1,123 +0,0 @@
{% extends "core/base.jinja" %}
{% block title %}
{% trans %}Edit operation{% endtrans %}
{% endblock %}
{% block content %}
<div id="accounting">
<p>
<a href="{{ url('accounting:bank_list') }}">{% trans %}Accounting{% endtrans %}</a> >
<a href="{{ url('accounting:bank_details', b_account_id=object.club_account.bank_account.id) }}">{{object.club_account.bank_account }}</a> >
<a href="{{ url('accounting:club_details', c_account_id=object.club_account.id) }}">{{ object.club_account }}</a> >
<a href="{{ url('accounting:journal_details', j_id=object.id) }}">{{ object.name }}</a> >
{% trans %}Edit operation{% endtrans %}
</p>
<hr>
<h2>{% trans %}Edit operation{% endtrans %}</h2>
<form action="" method="post">
{% csrf_token %}
{{ form.non_field_errors() }}
{{ form.journal }}
{{ form.target_id }}
<p>{{ form.amount.errors }}<label for="{{ form.amount.name }}">{{ form.amount.label }}</label> {{ form.amount }}</p>
<p>{{ form.remark.errors }}<label for="{{ form.remark.name }}">{{ form.remark.label }}</label> {{ form.remark }}</p>
<br />
<strong>{% trans %}Warning: if you select <em>Account</em>, the opposite operation will be created in the target account. If you don't want that, select <em>Club</em> instead of <em>Account</em>.{% endtrans %}</strong>
<p>{{ form.target_type.errors }}<label for="{{ form.target_type.name }}">{{ form.target_type.label }}</label> {{ form.target_type }}</p>
{{ form.user }}
{{ form.club }}
{{ form.club_account }}
{{ form.company }}
{{ form.target_label }}
<span id="id_need_link_full"><label>{{ form.need_link.label }}</label> {{ form.need_link }}</span>
<p>{{ form.date.errors }}<label for="{{ form.date.name }}">{{ form.date.label }}</label> {{ form.date }}</p>
<p>{{ form.mode.errors }}<label for="{{ form.mode.name }}">{{ form.mode.label }}</label> {{ form.mode }}</p>
<p>{{ form.cheque_number.errors }}<label for="{{ form.cheque_number.name }}">{{ form.cheque_number.label }}</label> {{
form.cheque_number }}</p>
<p>{{ form.invoice.errors }}<label for="{{ form.invoice.name }}">{{ form.invoice.label }}</label> {{ form.invoice }}</p>
<p>{{ form.simpleaccounting_type.errors }}<label for="{{ form.simpleaccounting_type.name }}">{{
form.simpleaccounting_type.label }}</label> {{ form.simpleaccounting_type }}</p>
<p>{{ form.accounting_type.errors }}<label for="{{ form.accounting_type.name }}">{{ form.accounting_type.label }}</label> {{
form.accounting_type }}</p>
<p>{{ form.label.errors }}<label for="{{ form.label.name }}">{{ form.label.label }}</label> {{ form.label }}</p>
<p>{{ form.done.errors }}<label for="{{ form.done.name }}">{{ form.done.label }}</label> {{ form.done }}</p>
{% if form.instance.linked_operation %}
{% set obj = form.instance.linked_operation %}
<p><strong>{% trans %}Linked operation:{% endtrans %}</strong><br>
<a href="{{ url('accounting:bank_details', b_account_id=obj.journal.club_account.bank_account.id) }}">
{{obj.journal.club_account.bank_account }}</a> >
<a href="{{ url('accounting:club_details', c_account_id=obj.journal.club_account.id) }}">{{ obj.journal.club_account }}</a> >
<a href="{{ url('accounting:journal_details', j_id=obj.journal.id) }}">{{ obj.journal }}</a> >
{{ obj.number }}
</p>
{% endif %}
<p><input type="submit" value="{% trans %}Save{% endtrans %}" /></p>
</form>
{% endblock %}
{% block script %}
{{ super() }}
<script>
$( function() {
var target_type = $('#id_target_type');
var user = $('#id_user_wrapper');
var club = $('#id_club_wrapper');
var club_account = $('#id_club_account_wrapper');
var company = $('#id_company_wrapper');
var other = $('#id_target_label');
var need_link = $('#id_need_link_full');
function update_targets () {
if (target_type.val() == "USER") {
console.log(user);
user.show();
club.hide();
club_account.hide();
company.hide();
other.hide();
need_link.hide();
} else if (target_type.val() == "ACCOUNT") {
club_account.show();
need_link.show();
user.hide();
club.hide();
company.hide();
other.hide();
} else if (target_type.val() == "CLUB") {
club.show();
user.hide();
club_account.hide();
company.hide();
other.hide();
need_link.hide();
} else if (target_type.val() == "COMPANY") {
company.show();
user.hide();
club_account.hide();
club.hide();
other.hide();
need_link.hide();
} else if (target_type.val() == "OTHER") {
other.show();
user.hide();
club.hide();
club_account.hide();
company.hide();
need_link.hide();
} else {
company.hide();
user.hide();
club_account.hide();
club.hide();
other.hide();
need_link.hide();
}
}
update_targets();
target_type.change(update_targets);
} );
</script>
</div>
{% endblock %}

View File

@ -1,16 +0,0 @@
{% extends "core/base.jinja" %}
{% block title %}
{% trans %}Refound account{% endtrans %}
{% endblock %}
{% block content %}
<div id="accounting">
<h3>{% trans %}Refound account{% endtrans %}</h3>
<form action="" method="post">
{% csrf_token %}
{{ form.as_p() }}
<p><input type="submit" value="{% trans %}Refound{% endtrans %}" /></p>
</form>
</div>
{% endblock %}

View File

@ -1,27 +0,0 @@
{% extends "core/base.jinja" %}
{% block title %}
{% trans %}Simplified type list{% endtrans %}
{% endblock %}
{% block content %}
<div id="accounting">
<p>
<a href="{{ url('accounting:bank_list') }}">{% trans %}Accounting{% endtrans %}</a> >
{% trans %}Simplified types{% endtrans %}
</p>
<hr>
<p><a href="{{ url('accounting:simple_type_new') }}">{% trans %}New simplified type{% endtrans %}</a></p>
{% if simplifiedaccountingtype_list %}
<h3>{% trans %}Simplified type list{% endtrans %}</h3>
<ul>
{% for a in simplifiedaccountingtype_list %}
<li><a href="{{ url('accounting:simple_type_edit', type_id=a.id) }}">{{ a }}</a></li>
{% endfor %}
</ul>
{% else %}
{% trans %}There is no types in this website.{% endtrans %}
{% endif %}
</div>
{% endblock %}

View File

@ -1,301 +0,0 @@
# -*- coding:utf-8 -*
#
# Copyright 2016,2017
# - Skia <skia@libskia.so>
#
# Ce fichier fait partie du site de l'Association des Étudiants de l'UTBM,
# http://ae.utbm.fr.
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License a published by the Free Software
# Foundation; either version 3 of the License, or (at your option) any later
# version.
#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
# details.
#
# You should have received a copy of the GNU General Public License along with
# this program; if not, write to the Free Sofware Foundation, Inc., 59 Temple
# Place - Suite 330, Boston, MA 02111-1307, USA.
#
#
from django.test import TestCase
from django.core.urlresolvers import reverse
from django.core.management import call_command
from datetime import date
from core.models import User
from accounting.models import (
GeneralJournal,
Operation,
Label,
AccountingType,
SimplifiedAccountingType,
)
class RefoundAccountTest(TestCase):
def setUp(self):
call_command("populate")
self.skia = User.objects.filter(username="skia").first()
# reffil skia's account
self.skia.customer.amount = 800
self.skia.customer.save()
def test_permission_denied(self):
self.client.login(username="guy", password="plop")
response_post = self.client.post(
reverse("accounting:refound_account"), {"user": self.skia.id}
)
response_get = self.client.get(reverse("accounting:refound_account"))
self.assertTrue(response_get.status_code == 403)
self.assertTrue(response_post.status_code == 403)
def test_root_granteed(self):
self.client.login(username="root", password="plop")
response_post = self.client.post(
reverse("accounting:refound_account"), {"user": self.skia.id}
)
self.skia = User.objects.filter(username="skia").first()
response_get = self.client.get(reverse("accounting:refound_account"))
self.assertFalse(response_get.status_code == 403)
self.assertTrue('<form action="" method="post">' in str(response_get.content))
self.assertFalse(response_post.status_code == 403)
self.assertTrue(self.skia.customer.amount == 0)
def test_comptable_granteed(self):
self.client.login(username="comptable", password="plop")
response_post = self.client.post(
reverse("accounting:refound_account"), {"user": self.skia.id}
)
self.skia = User.objects.filter(username="skia").first()
response_get = self.client.get(reverse("accounting:refound_account"))
self.assertFalse(response_get.status_code == 403)
self.assertTrue('<form action="" method="post">' in str(response_get.content))
self.assertFalse(response_post.status_code == 403)
self.assertTrue(self.skia.customer.amount == 0)
class JournalTest(TestCase):
def setUp(self):
call_command("populate")
self.journal = GeneralJournal.objects.filter(id=1).first()
def test_permission_granted(self):
self.client.login(username="comptable", password="plop")
response_get = self.client.get(
reverse("accounting:journal_details", args=[self.journal.id])
)
self.assertTrue(response_get.status_code == 200)
self.assertTrue(
"<td>M\\xc3\\xa9thode de paiement</td>" in str(response_get.content)
)
def test_permission_not_granted(self):
self.client.login(username="skia", password="plop")
response_get = self.client.get(
reverse("accounting:journal_details", args=[self.journal.id])
)
self.assertTrue(response_get.status_code == 403)
self.assertFalse(
"<td>M\xc3\xa9thode de paiement</td>" in str(response_get.content)
)
class OperationTest(TestCase):
def setUp(self):
call_command("populate")
self.journal = GeneralJournal.objects.filter(id=1).first()
self.skia = User.objects.filter(username="skia").first()
at = AccountingType(
code="443", label="Ce code n'existe pas", movement_type="CREDIT"
)
at.save()
l = Label(club_account=self.journal.club_account, name="bob")
l.save()
self.client.login(username="comptable", password="plop")
self.op1 = Operation(
journal=self.journal,
date=date.today(),
amount=1,
remark="Test bilan",
mode="CASH",
done=True,
label=l,
accounting_type=at,
target_type="USER",
target_id=self.skia.id,
)
self.op1.save()
self.op2 = Operation(
journal=self.journal,
date=date.today(),
amount=2,
remark="Test bilan",
mode="CASH",
done=True,
label=l,
accounting_type=at,
target_type="USER",
target_id=self.skia.id,
)
self.op2.save()
def test_new_operation(self):
self.client.login(username="comptable", password="plop")
at = AccountingType.objects.filter(code="604").first()
response = self.client.post(
reverse("accounting:op_new", args=[self.journal.id]),
{
"amount": 30,
"remark": "Un gros test",
"journal": self.journal.id,
"target_type": "OTHER",
"target_id": "",
"target_label": "Le fantome de la nuit",
"date": "04/12/2020",
"mode": "CASH",
"cheque_number": "",
"invoice": "",
"simpleaccounting_type": "",
"accounting_type": at.id,
"label": "",
"done": False,
},
)
self.assertFalse(response.status_code == 403)
self.assertTrue(
self.journal.operations.filter(
target_label="Le fantome de la nuit"
).exists()
)
response_get = self.client.get(
reverse("accounting:journal_details", args=[self.journal.id])
)
self.assertTrue("<td>Le fantome de la nuit</td>" in str(response_get.content))
def test_bad_new_operation(self):
self.client.login(username="comptable", password="plop")
AccountingType.objects.filter(code="604").first()
response = self.client.post(
reverse("accounting:op_new", args=[self.journal.id]),
{
"amount": 30,
"remark": "Un gros test",
"journal": self.journal.id,
"target_type": "OTHER",
"target_id": "",
"target_label": "Le fantome de la nuit",
"date": "04/12/2020",
"mode": "CASH",
"cheque_number": "",
"invoice": "",
"simpleaccounting_type": "",
"accounting_type": "",
"label": "",
"done": False,
},
)
self.assertTrue(
"Vous devez fournir soit un type comptable simplifi\\xc3\\xa9 ou un type comptable standard"
in str(response.content)
)
def test_new_operation_not_authorized(self):
self.client.login(username="skia", password="plop")
at = AccountingType.objects.filter(code="604").first()
response = self.client.post(
reverse("accounting:op_new", args=[self.journal.id]),
{
"amount": 30,
"remark": "Un gros test",
"journal": self.journal.id,
"target_type": "OTHER",
"target_id": "",
"target_label": "Le fantome du jour",
"date": "04/12/2020",
"mode": "CASH",
"cheque_number": "",
"invoice": "",
"simpleaccounting_type": "",
"accounting_type": at.id,
"label": "",
"done": False,
},
)
self.assertTrue(response.status_code == 403)
self.assertFalse(
self.journal.operations.filter(target_label="Le fantome du jour").exists()
)
def test__operation_simple_accounting(self):
self.client.login(username="comptable", password="plop")
sat = SimplifiedAccountingType.objects.all().first()
response = self.client.post(
reverse("accounting:op_new", args=[self.journal.id]),
{
"amount": 23,
"remark": "Un gros test",
"journal": self.journal.id,
"target_type": "OTHER",
"target_id": "",
"target_label": "Le fantome de l'aurore",
"date": "04/12/2020",
"mode": "CASH",
"cheque_number": "",
"invoice": "",
"simpleaccounting_type": sat.id,
"accounting_type": "",
"label": "",
"done": False,
},
)
self.assertFalse(response.status_code == 403)
self.assertTrue(self.journal.operations.filter(amount=23).exists())
response_get = self.client.get(
reverse("accounting:journal_details", args=[self.journal.id])
)
self.assertTrue(
"<td>Le fantome de l&#39;aurore</td>" in str(response_get.content)
)
self.assertTrue(
self.journal.operations.filter(amount=23)
.values("accounting_type")
.first()["accounting_type"]
== AccountingType.objects.filter(code=6).values("id").first()["id"]
)
def test_nature_statement(self):
self.client.login(username="comptable", password="plop")
response_get = self.client.get(
reverse("accounting:journal_nature_statement", args=[self.journal.id])
)
self.assertTrue(
"bob (Troll Pench\\xc3\\xa9) : 3.00" in str(response_get.content)
)
def test_person_statement(self):
self.client.login(username="comptable", password="plop")
response_get = self.client.get(
reverse("accounting:journal_person_statement", args=[self.journal.id])
)
self.assertTrue(
"<td>3.00</td>" in str(response_get.content)
and '<td><a href="/user/1/">S&#39; Kia</a></td>'
in str(response_get.content)
)
def test_accounting_statement(self):
self.client.login(username="comptable", password="plop")
response_get = self.client.get(
reverse("accounting:journal_accounting_statement", args=[self.journal.id])
)
self.assertTrue(
"<td>443 - Cr\\xc3\\xa9dit - Ce code n&#39;existe pas</td>"
in str(response_get.content)
)

View File

@ -1,152 +0,0 @@
# -*- coding:utf-8 -*
#
# Copyright 2016,2017
# - Skia <skia@libskia.so>
#
# Ce fichier fait partie du site de l'Association des Étudiants de l'UTBM,
# http://ae.utbm.fr.
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License a published by the Free Software
# Foundation; either version 3 of the License, or (at your option) any later
# version.
#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
# details.
#
# You should have received a copy of the GNU General Public License along with
# this program; if not, write to the Free Sofware Foundation, Inc., 59 Temple
# Place - Suite 330, Boston, MA 02111-1307, USA.
#
#
from django.conf.urls import url
from accounting.views import *
urlpatterns = [
# Accounting types
url(
r"^simple_type$",
SimplifiedAccountingTypeListView.as_view(),
name="simple_type_list",
),
url(
r"^simple_type/create$",
SimplifiedAccountingTypeCreateView.as_view(),
name="simple_type_new",
),
url(
r"^simple_type/(?P<type_id>[0-9]+)/edit$",
SimplifiedAccountingTypeEditView.as_view(),
name="simple_type_edit",
),
# Accounting types
url(r"^type$", AccountingTypeListView.as_view(), name="type_list"),
url(r"^type/create$", AccountingTypeCreateView.as_view(), name="type_new"),
url(
r"^type/(?P<type_id>[0-9]+)/edit$",
AccountingTypeEditView.as_view(),
name="type_edit",
),
# Bank accounts
url(r"^$", BankAccountListView.as_view(), name="bank_list"),
url(r"^bank/create$", BankAccountCreateView.as_view(), name="bank_new"),
url(
r"^bank/(?P<b_account_id>[0-9]+)$",
BankAccountDetailView.as_view(),
name="bank_details",
),
url(
r"^bank/(?P<b_account_id>[0-9]+)/edit$",
BankAccountEditView.as_view(),
name="bank_edit",
),
url(
r"^bank/(?P<b_account_id>[0-9]+)/delete$",
BankAccountDeleteView.as_view(),
name="bank_delete",
),
# Club accounts
url(r"^club/create$", ClubAccountCreateView.as_view(), name="club_new"),
url(
r"^club/(?P<c_account_id>[0-9]+)$",
ClubAccountDetailView.as_view(),
name="club_details",
),
url(
r"^club/(?P<c_account_id>[0-9]+)/edit$",
ClubAccountEditView.as_view(),
name="club_edit",
),
url(
r"^club/(?P<c_account_id>[0-9]+)/delete$",
ClubAccountDeleteView.as_view(),
name="club_delete",
),
# Journals
url(r"^journal/create$", JournalCreateView.as_view(), name="journal_new"),
url(
r"^journal/(?P<j_id>[0-9]+)$",
JournalDetailView.as_view(),
name="journal_details",
),
url(
r"^journal/(?P<j_id>[0-9]+)/edit$",
JournalEditView.as_view(),
name="journal_edit",
),
url(
r"^journal/(?P<j_id>[0-9]+)/delete$",
JournalDeleteView.as_view(),
name="journal_delete",
),
url(
r"^journal/(?P<j_id>[0-9]+)/statement/nature$",
JournalNatureStatementView.as_view(),
name="journal_nature_statement",
),
url(
r"^journal/(?P<j_id>[0-9]+)/statement/person$",
JournalPersonStatementView.as_view(),
name="journal_person_statement",
),
url(
r"^journal/(?P<j_id>[0-9]+)/statement/accounting$",
JournalAccountingStatementView.as_view(),
name="journal_accounting_statement",
),
# Operations
url(
r"^operation/create/(?P<j_id>[0-9]+)$",
OperationCreateView.as_view(),
name="op_new",
),
url(r"^operation/(?P<op_id>[0-9]+)$", OperationEditView.as_view(), name="op_edit"),
url(
r"^operation/(?P<op_id>[0-9]+)/pdf$", OperationPDFView.as_view(), name="op_pdf"
),
# Companies
url(r"^company/list$", CompanyListView.as_view(), name="co_list"),
url(r"^company/create$", CompanyCreateView.as_view(), name="co_new"),
url(r"^company/(?P<co_id>[0-9]+)$", CompanyEditView.as_view(), name="co_edit"),
# Labels
url(r"^label/new$", LabelCreateView.as_view(), name="label_new"),
url(
r"^label/(?P<clubaccount_id>[0-9]+)$",
LabelListView.as_view(),
name="label_list",
),
url(
r"^label/(?P<label_id>[0-9]+)/edit$", LabelEditView.as_view(), name="label_edit"
),
url(
r"^label/(?P<label_id>[0-9]+)/delete$",
LabelDeleteView.as_view(),
name="label_delete",
),
# User account
url(r"^refound/account$", RefoundAccountView.as_view(), name="refound_account"),
]

View File

@ -1,942 +0,0 @@
# -*- coding:utf-8 -*
#
# Copyright 2016,2017
# - Skia <skia@libskia.so>
#
# Ce fichier fait partie du site de l'Association des Étudiants de l'UTBM,
# http://ae.utbm.fr.
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License a published by the Free Software
# Foundation; either version 3 of the License, or (at your option) any later
# version.
#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
# details.
#
# You should have received a copy of the GNU General Public License along with
# this program; if not, write to the Free Sofware Foundation, Inc., 59 Temple
# Place - Suite 330, Boston, MA 02111-1307, USA.
#
#
from django.views.generic import ListView, DetailView
from django.views.generic.edit import UpdateView, CreateView, DeleteView, FormView
from django.core.urlresolvers import reverse_lazy, reverse
from django.utils.translation import ugettext_lazy as _
from django.forms.models import modelform_factory
from django.core.exceptions import PermissionDenied, ValidationError
from django.forms import HiddenInput
from django.db import transaction
from django.db.models import Sum
from django.conf import settings
from django import forms
from django.http import HttpResponse
import collections
from ajax_select.fields import AutoCompleteSelectField
from core.views import (
CanViewMixin,
CanEditMixin,
CanEditPropMixin,
CanCreateMixin,
TabedViewMixin,
)
from core.views.forms import SelectFile, SelectDate
from accounting.models import (
BankAccount,
ClubAccount,
GeneralJournal,
Operation,
AccountingType,
Company,
SimplifiedAccountingType,
Label,
)
from counter.models import Counter, Selling, Product
# Main accounting view
class BankAccountListView(CanViewMixin, ListView):
"""
A list view for the admins
"""
model = BankAccount
template_name = "accounting/bank_account_list.jinja"
ordering = ["name"]
# Simplified accounting types
class SimplifiedAccountingTypeListView(CanViewMixin, ListView):
"""
A list view for the admins
"""
model = SimplifiedAccountingType
template_name = "accounting/simplifiedaccountingtype_list.jinja"
class SimplifiedAccountingTypeEditView(CanViewMixin, UpdateView):
"""
An edit view for the admins
"""
model = SimplifiedAccountingType
pk_url_kwarg = "type_id"
fields = ["label", "accounting_type"]
template_name = "core/edit.jinja"
class SimplifiedAccountingTypeCreateView(CanCreateMixin, CreateView):
"""
Create an accounting type (for the admins)
"""
model = SimplifiedAccountingType
fields = ["label", "accounting_type"]
template_name = "core/create.jinja"
# Accounting types
class AccountingTypeListView(CanViewMixin, ListView):
"""
A list view for the admins
"""
model = AccountingType
template_name = "accounting/accountingtype_list.jinja"
class AccountingTypeEditView(CanViewMixin, UpdateView):
"""
An edit view for the admins
"""
model = AccountingType
pk_url_kwarg = "type_id"
fields = ["code", "label", "movement_type"]
template_name = "core/edit.jinja"
class AccountingTypeCreateView(CanCreateMixin, CreateView):
"""
Create an accounting type (for the admins)
"""
model = AccountingType
fields = ["code", "label", "movement_type"]
template_name = "core/create.jinja"
# BankAccount views
class BankAccountEditView(CanViewMixin, UpdateView):
"""
An edit view for the admins
"""
model = BankAccount
pk_url_kwarg = "b_account_id"
fields = ["name", "iban", "number", "club"]
template_name = "core/edit.jinja"
class BankAccountDetailView(CanViewMixin, DetailView):
"""
A detail view, listing every club account
"""
model = BankAccount
pk_url_kwarg = "b_account_id"
template_name = "accounting/bank_account_details.jinja"
class BankAccountCreateView(CanCreateMixin, CreateView):
"""
Create a bank account (for the admins)
"""
model = BankAccount
fields = ["name", "club", "iban", "number"]
template_name = "core/create.jinja"
class BankAccountDeleteView(
CanEditPropMixin, DeleteView
): # TODO change Delete to Close
"""
Delete a bank account (for the admins)
"""
model = BankAccount
pk_url_kwarg = "b_account_id"
template_name = "core/delete_confirm.jinja"
success_url = reverse_lazy("accounting:bank_list")
# ClubAccount views
class ClubAccountEditView(CanViewMixin, UpdateView):
"""
An edit view for the admins
"""
model = ClubAccount
pk_url_kwarg = "c_account_id"
fields = ["name", "club", "bank_account"]
template_name = "core/edit.jinja"
class ClubAccountDetailView(CanViewMixin, DetailView):
"""
A detail view, listing every journal
"""
model = ClubAccount
pk_url_kwarg = "c_account_id"
template_name = "accounting/club_account_details.jinja"
class ClubAccountCreateView(CanCreateMixin, CreateView):
"""
Create a club account (for the admins)
"""
model = ClubAccount
fields = ["name", "club", "bank_account"]
template_name = "core/create.jinja"
def get_initial(self):
ret = super(ClubAccountCreateView, self).get_initial()
if "parent" in self.request.GET.keys():
obj = BankAccount.objects.filter(id=int(self.request.GET["parent"])).first()
if obj is not None:
ret["bank_account"] = obj.id
return ret
class ClubAccountDeleteView(
CanEditPropMixin, DeleteView
): # TODO change Delete to Close
"""
Delete a club account (for the admins)
"""
model = ClubAccount
pk_url_kwarg = "c_account_id"
template_name = "core/delete_confirm.jinja"
success_url = reverse_lazy("accounting:bank_list")
# Journal views
class JournalTabsMixin(TabedViewMixin):
def get_tabs_title(self):
return _("Journal")
def get_list_of_tabs(self):
tab_list = []
tab_list.append(
{
"url": reverse(
"accounting:journal_details", kwargs={"j_id": self.object.id}
),
"slug": "journal",
"name": _("Journal"),
}
)
tab_list.append(
{
"url": reverse(
"accounting:journal_nature_statement",
kwargs={"j_id": self.object.id},
),
"slug": "nature_statement",
"name": _("Statement by nature"),
}
)
tab_list.append(
{
"url": reverse(
"accounting:journal_person_statement",
kwargs={"j_id": self.object.id},
),
"slug": "person_statement",
"name": _("Statement by person"),
}
)
tab_list.append(
{
"url": reverse(
"accounting:journal_accounting_statement",
kwargs={"j_id": self.object.id},
),
"slug": "accounting_statement",
"name": _("Accounting statement"),
}
)
return tab_list
class JournalCreateView(CanCreateMixin, CreateView):
"""
Create a general journal
"""
model = GeneralJournal
form_class = modelform_factory(
GeneralJournal,
fields=["name", "start_date", "club_account"],
widgets={"start_date": SelectDate},
)
template_name = "core/create.jinja"
def get_initial(self):
ret = super(JournalCreateView, self).get_initial()
if "parent" in self.request.GET.keys():
obj = ClubAccount.objects.filter(id=int(self.request.GET["parent"])).first()
if obj is not None:
ret["club_account"] = obj.id
return ret
class JournalDetailView(JournalTabsMixin, CanViewMixin, DetailView):
"""
A detail view, listing every operation
"""
model = GeneralJournal
pk_url_kwarg = "j_id"
template_name = "accounting/journal_details.jinja"
current_tab = "journal"
class JournalEditView(CanEditMixin, UpdateView):
"""
Update a general journal
"""
model = GeneralJournal
pk_url_kwarg = "j_id"
fields = ["name", "start_date", "end_date", "club_account", "closed"]
template_name = "core/edit.jinja"
class JournalDeleteView(CanEditPropMixin, DeleteView):
"""
Delete a club account (for the admins)
"""
model = GeneralJournal
pk_url_kwarg = "j_id"
template_name = "core/delete_confirm.jinja"
success_url = reverse_lazy("accounting:club_details")
def dispatch(self, request, *args, **kwargs):
self.object = self.get_object()
if self.object.operations.count() == 0:
return super(JournalDeleteView, self).dispatch(request, *args, **kwargs)
else:
raise PermissionDenied
# Operation views
class OperationForm(forms.ModelForm):
class Meta:
model = Operation
fields = [
"amount",
"remark",
"journal",
"target_type",
"target_id",
"target_label",
"date",
"mode",
"cheque_number",
"invoice",
"simpleaccounting_type",
"accounting_type",
"label",
"done",
]
widgets = {
"journal": HiddenInput,
"target_id": HiddenInput,
"date": SelectDate,
"invoice": SelectFile,
}
user = AutoCompleteSelectField("users", help_text=None, required=False)
club_account = AutoCompleteSelectField(
"club_accounts", help_text=None, required=False
)
club = AutoCompleteSelectField("clubs", help_text=None, required=False)
company = AutoCompleteSelectField("companies", help_text=None, required=False)
need_link = forms.BooleanField(
label=_("Link this operation to the target account"),
required=False,
initial=False,
)
def __init__(self, *args, **kwargs):
club_account = kwargs.pop("club_account", None)
super(OperationForm, self).__init__(*args, **kwargs)
if club_account:
self.fields["label"].queryset = club_account.labels.order_by("name").all()
if self.instance.target_type == "USER":
self.fields["user"].initial = self.instance.target_id
elif self.instance.target_type == "ACCOUNT":
self.fields["club_account"].initial = self.instance.target_id
elif self.instance.target_type == "CLUB":
self.fields["club"].initial = self.instance.target_id
elif self.instance.target_type == "COMPANY":
self.fields["company"].initial = self.instance.target_id
def clean(self):
self.cleaned_data = super(OperationForm, self).clean()
if "target_type" in self.cleaned_data.keys():
if (
self.cleaned_data.get("user") is None
and self.cleaned_data.get("club") is None
and self.cleaned_data.get("club_account") is None
and self.cleaned_data.get("company") is None
and self.cleaned_data.get("target_label") == ""
):
self.add_error(
"target_type", ValidationError(_("The target must be set."))
)
else:
if self.cleaned_data["target_type"] == "USER":
self.cleaned_data["target_id"] = self.cleaned_data["user"].id
elif self.cleaned_data["target_type"] == "ACCOUNT":
self.cleaned_data["target_id"] = self.cleaned_data[
"club_account"
].id
elif self.cleaned_data["target_type"] == "CLUB":
self.cleaned_data["target_id"] = self.cleaned_data["club"].id
elif self.cleaned_data["target_type"] == "COMPANY":
self.cleaned_data["target_id"] = self.cleaned_data["company"].id
if self.cleaned_data.get("amount") is None:
self.add_error("amount", ValidationError(_("The amount must be set.")))
return self.cleaned_data
def save(self):
ret = super(OperationForm, self).save()
if (
self.instance.target_type == "ACCOUNT"
and not self.instance.linked_operation
and self.instance.target.has_open_journal()
and self.cleaned_data["need_link"]
):
inst = self.instance
club_account = inst.target
acc_type = (
AccountingType.objects.exclude(movement_type="NEUTRAL")
.exclude(movement_type=inst.accounting_type.movement_type)
.order_by("code")
.first()
) # Select a random opposite accounting type
op = Operation(
journal=club_account.get_open_journal(),
amount=inst.amount,
date=inst.date,
remark=inst.remark,
mode=inst.mode,
cheque_number=inst.cheque_number,
invoice=inst.invoice,
done=False, # Has to be checked by hand
simpleaccounting_type=None,
accounting_type=acc_type,
target_type="ACCOUNT",
target_id=inst.journal.club_account.id,
target_label="",
linked_operation=inst,
)
op.save()
self.instance.linked_operation = op
self.save()
return ret
class OperationCreateView(CanCreateMixin, CreateView):
"""
Create an operation
"""
model = Operation
form_class = OperationForm
template_name = "accounting/operation_edit.jinja"
def get_form(self, form_class=None):
self.journal = GeneralJournal.objects.filter(id=self.kwargs["j_id"]).first()
ca = self.journal.club_account if self.journal else None
return self.form_class(club_account=ca, **self.get_form_kwargs())
def get_initial(self):
ret = super(OperationCreateView, self).get_initial()
if self.journal is not None:
ret["journal"] = self.journal.id
return ret
def get_context_data(self, **kwargs):
""" Add journal to the context """
kwargs = super(OperationCreateView, self).get_context_data(**kwargs)
if self.journal:
kwargs["object"] = self.journal
return kwargs
class OperationEditView(CanEditMixin, UpdateView):
"""
An edit view, working as detail for the moment
"""
model = Operation
pk_url_kwarg = "op_id"
form_class = OperationForm
template_name = "accounting/operation_edit.jinja"
def get_context_data(self, **kwargs):
""" Add journal to the context """
kwargs = super(OperationEditView, self).get_context_data(**kwargs)
kwargs["object"] = self.object.journal
return kwargs
class OperationPDFView(CanViewMixin, DetailView):
"""
Display the PDF of a given operation
"""
model = Operation
pk_url_kwarg = "op_id"
def get(self, request, *args, **kwargs):
from reportlab.pdfgen import canvas
from reportlab.lib.units import cm
from reportlab.platypus import Table, TableStyle
from reportlab.lib import colors
from reportlab.lib.pagesizes import letter
from reportlab.lib.utils import ImageReader
from reportlab.pdfbase.ttfonts import TTFont
from reportlab.pdfbase import pdfmetrics
pdfmetrics.registerFont(TTFont("DejaVu", "DejaVuSerif.ttf"))
self.object = self.get_object()
amount = self.object.amount
remark = self.object.remark
nature = self.object.accounting_type.movement_type
num = self.object.number
date = self.object.date
mode = self.object.mode
club_name = self.object.journal.club_account.name
ti = self.object.journal.name
op_label = self.object.label
club_address = self.object.journal.club_account.club.address
id_op = self.object.id
if self.object.target_type == "OTHER":
target = self.object.target_label
else:
target = self.object.target.get_display_name()
response = HttpResponse(content_type="application/pdf")
response["Content-Disposition"] = 'filename="op-%d(%s_on_%s).pdf"' % (
num,
ti,
club_name,
)
p = canvas.Canvas(response)
p.setFont("DejaVu", 12)
p.setTitle("%s %d" % (_("Operation"), num))
width, height = letter
im = ImageReader("core/static/core/img/logo.jpg")
iw, ih = im.getSize()
p.drawImage(im, 40, height - 50, width=iw / 2, height=ih / 2)
labelStr = [["%s %s - %s %s" % (_("Journal"), ti, _("Operation"), num)]]
label = Table(labelStr, colWidths=[150], rowHeights=[20])
label.setStyle(TableStyle([("ALIGN", (0, 0), (-1, -1), "RIGHT")]))
w, h = label.wrapOn(label, 0, 0)
label.drawOn(p, width - 180, height)
p.drawString(
90, height - 100, _("Financial proof: ") + "OP%010d" % (id_op)
) # Justificatif du libellé
p.drawString(
90, height - 130, _("Club: %(club_name)s") % ({"club_name": club_name})
)
p.drawString(
90,
height - 160,
_("Label: %(op_label)s")
% {"op_label": op_label if op_label is not None else ""},
)
p.drawString(90, height - 190, _("Date: %(date)s") % {"date": date})
data = []
data += [
["%s" % (_("Credit").upper() if nature == "CREDIT" else _("Debit").upper())]
]
data += [[_("Amount: %(amount).2f") % {"amount": amount}]]
payment_mode = ""
for m in settings.SITH_ACCOUNTING_PAYMENT_METHOD:
if m[0] == mode:
payment_mode += "[\u00D7]"
else:
payment_mode += "[ ]"
payment_mode += " %s\n" % (m[1])
data += [[payment_mode]]
data += [
[
"%s : %s"
% (_("Debtor") if nature == "CREDIT" else _("Creditor"), target),
"",
]
]
data += [["%s \n%s" % (_("Comment:"), remark)]]
t = Table(
data, colWidths=[(width - 90 * 2) / 2] * 2, rowHeights=[20, 20, 70, 20, 80]
)
t.setStyle(
TableStyle(
[
("ALIGN", (0, 0), (-1, -1), "CENTER"),
("VALIGN", (-2, -1), (-1, -1), "TOP"),
("VALIGN", (0, 0), (-1, -2), "MIDDLE"),
("INNERGRID", (0, 0), (-1, -1), 0.25, colors.black),
("SPAN", (0, 0), (1, 0)), # line DEBIT/CREDIT
("SPAN", (0, 1), (1, 1)), # line amount
("SPAN", (-2, -1), (-1, -1)), # line comment
("SPAN", (0, -2), (-1, -2)), # line creditor/debtor
("SPAN", (0, 2), (1, 2)), # line payment_mode
("ALIGN", (0, 2), (1, 2), "LEFT"), # line payment_mode
("ALIGN", (-2, -1), (-1, -1), "LEFT"),
("BOX", (0, 0), (-1, -1), 0.25, colors.black),
]
)
)
signature = []
signature += [[_("Signature:")]]
tSig = Table(signature, colWidths=[(width - 90 * 2)], rowHeights=[80])
tSig.setStyle(
TableStyle(
[
("VALIGN", (0, 0), (-1, -1), "TOP"),
("BOX", (0, 0), (-1, -1), 0.25, colors.black),
]
)
)
w, h = tSig.wrapOn(p, 0, 0)
tSig.drawOn(p, 90, 200)
w, h = t.wrapOn(p, 0, 0)
t.drawOn(p, 90, 350)
p.drawCentredString(10.5 * cm, 2 * cm, club_name)
p.drawCentredString(10.5 * cm, 1 * cm, club_address)
p.showPage()
p.save()
return response
class JournalNatureStatementView(JournalTabsMixin, CanViewMixin, DetailView):
"""
Display a statement sorted by labels
"""
model = GeneralJournal
pk_url_kwarg = "j_id"
template_name = "accounting/journal_statement_nature.jinja"
current_tab = "nature_statement"
def statement(self, queryset, movement_type):
ret = collections.OrderedDict()
statement = collections.OrderedDict()
total_sum = 0
for sat in [None] + list(
SimplifiedAccountingType.objects.order_by("label").all()
):
sum = queryset.filter(
accounting_type__movement_type=movement_type, simpleaccounting_type=sat
).aggregate(amount_sum=Sum("amount"))["amount_sum"]
if sat:
sat = sat.label
else:
sat = ""
if sum:
total_sum += sum
statement[sat] = sum
ret[movement_type] = statement
ret[movement_type + "_sum"] = total_sum
return ret
def big_statement(self):
label_list = (
self.object.operations.order_by("label").values_list("label").distinct()
)
labels = Label.objects.filter(id__in=label_list).all()
statement = collections.OrderedDict()
gen_statement = collections.OrderedDict()
no_label_statement = collections.OrderedDict()
gen_statement.update(self.statement(self.object.operations.all(), "CREDIT"))
gen_statement.update(self.statement(self.object.operations.all(), "DEBIT"))
statement[_("General statement")] = gen_statement
no_label_statement.update(
self.statement(self.object.operations.filter(label=None).all(), "CREDIT")
)
no_label_statement.update(
self.statement(self.object.operations.filter(label=None).all(), "DEBIT")
)
statement[_("No label operations")] = no_label_statement
for l in labels:
l_stmt = collections.OrderedDict()
l_stmt.update(
self.statement(self.object.operations.filter(label=l).all(), "CREDIT")
)
l_stmt.update(
self.statement(self.object.operations.filter(label=l).all(), "DEBIT")
)
statement[l] = l_stmt
return statement
def get_context_data(self, **kwargs):
""" Add infos to the context """
kwargs = super(JournalNatureStatementView, self).get_context_data(**kwargs)
kwargs["statement"] = self.big_statement()
return kwargs
class JournalPersonStatementView(JournalTabsMixin, CanViewMixin, DetailView):
"""
Calculate a dictionary with operation target and sum of operations
"""
model = GeneralJournal
pk_url_kwarg = "j_id"
template_name = "accounting/journal_statement_person.jinja"
current_tab = "person_statement"
def sum_by_target(self, target_id, target_type, movement_type):
return self.object.operations.filter(
accounting_type__movement_type=movement_type,
target_id=target_id,
target_type=target_type,
).aggregate(amount_sum=Sum("amount"))["amount_sum"]
def statement(self, movement_type):
statement = collections.OrderedDict()
for op in (
self.object.operations.filter(accounting_type__movement_type=movement_type)
.order_by("target_type", "target_id")
.distinct()
):
statement[op.target] = self.sum_by_target(
op.target_id, op.target_type, movement_type
)
return statement
def total(self, movement_type):
return sum(self.statement(movement_type).values())
def get_context_data(self, **kwargs):
""" Add journal to the context """
kwargs = super(JournalPersonStatementView, self).get_context_data(**kwargs)
kwargs["credit_statement"] = self.statement("CREDIT")
kwargs["debit_statement"] = self.statement("DEBIT")
kwargs["total_credit"] = self.total("CREDIT")
kwargs["total_debit"] = self.total("DEBIT")
return kwargs
class JournalAccountingStatementView(JournalTabsMixin, CanViewMixin, DetailView):
"""
Calculate a dictionary with operation type and sum of operations
"""
model = GeneralJournal
pk_url_kwarg = "j_id"
template_name = "accounting/journal_statement_accounting.jinja"
current_tab = "accounting_statement"
def statement(self):
statement = collections.OrderedDict()
for at in AccountingType.objects.order_by("code").all():
sum_by_type = self.object.operations.filter(
accounting_type__code__startswith=at.code
).aggregate(amount_sum=Sum("amount"))["amount_sum"]
if sum_by_type:
statement[at] = sum_by_type
return statement
def get_context_data(self, **kwargs):
""" Add journal to the context """
kwargs = super(JournalAccountingStatementView, self).get_context_data(**kwargs)
kwargs["statement"] = self.statement()
return kwargs
# Company views
class CompanyListView(CanViewMixin, ListView):
model = Company
template_name = "accounting/co_list.jinja"
class CompanyCreateView(CanCreateMixin, CreateView):
"""
Create a company
"""
model = Company
fields = ["name"]
template_name = "core/create.jinja"
success_url = reverse_lazy("accounting:co_list")
class CompanyEditView(CanCreateMixin, UpdateView):
"""
Edit a company
"""
model = Company
pk_url_kwarg = "co_id"
fields = ["name"]
template_name = "core/edit.jinja"
success_url = reverse_lazy("accounting:co_list")
# Label views
class LabelListView(CanViewMixin, DetailView):
model = ClubAccount
pk_url_kwarg = "clubaccount_id"
template_name = "accounting/label_list.jinja"
class LabelCreateView(
CanCreateMixin, CreateView
): # FIXME we need to check the rights before creating the object
model = Label
form_class = modelform_factory(
Label, fields=["name", "club_account"], widgets={"club_account": HiddenInput}
)
template_name = "core/create.jinja"
def get_initial(self):
ret = super(LabelCreateView, self).get_initial()
if "parent" in self.request.GET.keys():
obj = ClubAccount.objects.filter(id=int(self.request.GET["parent"])).first()
if obj is not None:
ret["club_account"] = obj.id
return ret
class LabelEditView(CanEditMixin, UpdateView):
model = Label
pk_url_kwarg = "label_id"
fields = ["name"]
template_name = "core/edit.jinja"
class LabelDeleteView(CanEditMixin, DeleteView):
model = Label
pk_url_kwarg = "label_id"
template_name = "core/delete_confirm.jinja"
def get_success_url(self):
return self.object.get_absolute_url()
class CloseCustomerAccountForm(forms.Form):
user = AutoCompleteSelectField(
"users", label=_("Refound this account"), help_text=None, required=True
)
class RefoundAccountView(FormView):
"""
Create a selling with the same amount than the current user money
"""
template_name = "accounting/refound_account.jinja"
form_class = CloseCustomerAccountForm
def permission(self, user):
if user.is_root or user.is_in_group(settings.SITH_GROUP_ACCOUNTING_ADMIN_ID):
return True
else:
raise PermissionDenied
def dispatch(self, request, *arg, **kwargs):
res = super(RefoundAccountView, self).dispatch(request, *arg, **kwargs)
if self.permission(request.user):
return res
def post(self, request, *arg, **kwargs):
self.operator = request.user
if self.permission(request.user):
return super(RefoundAccountView, self).post(self, request, *arg, **kwargs)
def form_valid(self, form):
self.customer = form.cleaned_data["user"]
self.create_selling()
return super(RefoundAccountView, self).form_valid(form)
def get_success_url(self):
return reverse("accounting:refound_account")
def create_selling(self):
with transaction.atomic():
uprice = self.customer.customer.amount
refound_club_counter = Counter.objects.get(
id=settings.SITH_COUNTER_REFOUND_ID
)
refound_club = refound_club_counter.club
s = Selling(
label=_("Refound account"),
unit_price=uprice,
quantity=1,
seller=self.operator,
customer=self.customer.customer,
club=refound_club,
counter=refound_club_counter,
product=Product.objects.get(id=settings.SITH_PRODUCT_REFOUND_ID),
)
s.save()

10
antispam/admin.py Normal file
View File

@ -0,0 +1,10 @@
from django.contrib import admin
from antispam.models import ToxicDomain
@admin.register(ToxicDomain)
class ToxicDomainAdmin(admin.ModelAdmin):
list_display = ("domain", "is_externally_managed", "created")
search_fields = ("domain", "is_externally_managed", "created")
list_filter = ("is_externally_managed",)

7
antispam/apps.py Normal file
View File

@ -0,0 +1,7 @@
from django.apps import AppConfig
class AntispamConfig(AppConfig):
default_auto_field = "django.db.models.BigAutoField"
verbose_name = "antispam"
name = "antispam"

22
antispam/forms.py Normal file
View File

@ -0,0 +1,22 @@
from django import forms
from django.core.validators import EmailValidator
from django.utils.translation import gettext_lazy as _
from antispam.models import ToxicDomain
class AntiSpamEmailValidator(EmailValidator):
def __call__(self, value: str):
super().__call__(value)
domain_part = value.rsplit("@", 1)[1]
if ToxicDomain.objects.filter(domain=domain_part).exists():
raise forms.ValidationError(_("Email domain is not allowed."))
validate_antispam_email = AntiSpamEmailValidator()
class AntiSpamEmailField(forms.EmailField):
"""An email field that email addresses with a known toxic domain."""
default_validators = [validate_antispam_email]

View File

@ -0,0 +1,69 @@
import requests
from django.conf import settings
from django.core.management import BaseCommand
from django.db.models import Max
from django.utils import timezone
from antispam.models import ToxicDomain
class Command(BaseCommand):
"""Update blocked ips/mails database"""
help = "Update blocked ips/mails database"
def add_arguments(self, parser):
parser.add_argument(
"--force", action="store_true", help="Force re-creation even if up to date"
)
def _should_update(self, *, force: bool = False) -> bool:
if force:
return True
oldest = ToxicDomain.objects.filter(is_externally_managed=True).aggregate(
res=Max("created")
)["res"]
return not (oldest and timezone.now() < (oldest + timezone.timedelta(days=1)))
def _download_domains(self, providers: list[str]) -> set[str]:
domains = set()
for provider in providers:
res = requests.get(provider)
if not res.ok:
self.stderr.write(
f"Source {provider} responded with code {res.status_code}"
)
continue
domains |= set(res.text.splitlines())
return domains
def _update_domains(self, domains: set[str]):
# Cleanup database
ToxicDomain.objects.filter(is_externally_managed=True).delete()
# Create database
ToxicDomain.objects.bulk_create(
[
ToxicDomain(domain=domain, is_externally_managed=True)
for domain in domains
],
ignore_conflicts=True,
)
self.stdout.write("Domain database updated")
def handle(self, *args, **options):
if not self._should_update(force=options["force"]):
self.stdout.write("Domain database is up to date")
return
self.stdout.write("Updating domain database")
domains = self._download_domains(settings.TOXIC_DOMAINS_PROVIDERS)
if not domains:
self.stderr.write(
"No domains could be fetched from settings.TOXIC_DOMAINS_PROVIDERS. "
"Please, have a look at your settings."
)
return
self._update_domains(domains)

View File

@ -0,0 +1,35 @@
# Generated by Django 4.2.14 on 2024-08-03 23:05
from django.db import migrations, models
class Migration(migrations.Migration):
initial = True
dependencies = []
operations = [
migrations.CreateModel(
name="ToxicDomain",
fields=[
(
"domain",
models.URLField(
max_length=253,
primary_key=True,
serialize=False,
verbose_name="domain",
),
),
("created", models.DateTimeField(auto_now_add=True)),
(
"is_externally_managed",
models.BooleanField(
default=False,
help_text="True if kept up-to-date using external toxic domain providers, else False",
verbose_name="is externally managed",
),
),
],
),
]

19
antispam/models.py Normal file
View File

@ -0,0 +1,19 @@
from django.db import models
from django.utils.translation import gettext_lazy as _
class ToxicDomain(models.Model):
"""Domain marked as spam in public databases"""
domain = models.URLField(_("domain"), max_length=253, primary_key=True)
created = models.DateTimeField(auto_now_add=True)
is_externally_managed = models.BooleanField(
_("is externally managed"),
default=False,
help_text=_(
"True if kept up-to-date using external toxic domain providers, else False"
),
)
def __str__(self) -> str:
return self.domain

View File

@ -1,23 +0,0 @@
# -*- coding:utf-8 -*
#
# Copyright 2016,2017
# - Skia <skia@libskia.so>
#
# Ce fichier fait partie du site de l'Association des Étudiants de l'UTBM,
# http://ae.utbm.fr.
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License a published by the Free Software
# Foundation; either version 3 of the License, or (at your option) any later
# version.
#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
# details.
#
# You should have received a copy of the GNU General Public License along with
# this program; if not, write to the Free Sofware Foundation, Inc., 59 Temple
# Place - Suite 330, Boston, MA 02111-1307, USA.
#
#

View File

@ -1,27 +1,55 @@
# -*- coding:utf-8 -*
#
# Copyright 2016,2017
# - Skia <skia@libskia.so>
#
# Ce fichier fait partie du site de l'Association des Étudiants de l'UTBM,
# http://ae.utbm.fr.
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License a published by the Free Software
# Foundation; either version 3 of the License, or (at your option) any later
# version.
#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
# details.
#
# You should have received a copy of the GNU General Public License along with
# this program; if not, write to the Free Sofware Foundation, Inc., 59 Temple
# Place - Suite 330, Boston, MA 02111-1307, USA.
#
#
from django.contrib import admin, messages
from django.db.models import QuerySet
from django.http import HttpRequest
from django.utils.translation import gettext_lazy as _
from django.contrib import admin
from api.hashers import generate_key
from api.models import ApiClient, ApiKey
# Register your models here.
@admin.register(ApiClient)
class ApiClientAdmin(admin.ModelAdmin):
list_display = ("name", "owner", "created_at", "updated_at")
search_fields = (
"name",
"owner__first_name",
"owner__last_name",
"owner__nick_name",
)
autocomplete_fields = ("owner", "groups", "client_permissions")
@admin.register(ApiKey)
class ApiKeyAdmin(admin.ModelAdmin):
list_display = ("name", "client", "created_at", "revoked")
list_filter = ("revoked",)
date_hierarchy = "created_at"
readonly_fields = ("prefix", "hashed_key")
actions = ("revoke_keys",)
def save_model(self, request: HttpRequest, obj: ApiKey, form, change):
if not change:
key, hashed = generate_key()
obj.prefix = key[: ApiKey.PREFIX_LENGTH]
obj.hashed_key = hashed
self.message_user(
request,
_(
"The API key for %(name)s is: %(key)s. "
"Please store it somewhere safe: "
"you will not be able to see it again."
)
% {"name": obj.name, "key": key},
level=messages.WARNING,
)
return super().save_model(request, obj, form, change)
def get_readonly_fields(self, request, obj: ApiKey | None = None):
if obj is None or obj.revoked:
return ["revoked", *self.readonly_fields]
return self.readonly_fields
@admin.action(description=_("Revoke selected API keys"))
def revoke_keys(self, _request: HttpRequest, queryset: QuerySet[ApiKey]):
queryset.update(revoked=True)

6
api/apps.py Normal file
View File

@ -0,0 +1,6 @@
from django.apps import AppConfig
class ApiConfig(AppConfig):
default_auto_field = "django.db.models.BigAutoField"
name = "api"

20
api/auth.py Normal file
View File

@ -0,0 +1,20 @@
from django.http import HttpRequest
from ninja.security import APIKeyHeader
from api.hashers import get_hasher
from api.models import ApiClient, ApiKey
class ApiKeyAuth(APIKeyHeader):
param_name = "X-APIKey"
def authenticate(self, request: HttpRequest, key: str | None) -> ApiClient | None:
if not key or len(key) != ApiKey.KEY_LENGTH:
return None
hasher = get_hasher()
hashed_key = hasher.encode(key)
try:
key_obj = ApiKey.objects.get(revoked=False, hashed_key=hashed_key)
except ApiKey.DoesNotExist:
return None
return key_obj.client

43
api/hashers.py Normal file
View File

@ -0,0 +1,43 @@
import functools
import hashlib
import secrets
from django.contrib.auth.hashers import BasePasswordHasher
from django.utils.crypto import constant_time_compare
class Sha512ApiKeyHasher(BasePasswordHasher):
"""
An API key hasher using the sha256 algorithm.
This hasher shouldn't be used in Django's `PASSWORD_HASHERS` setting.
It is insecure for use in hashing passwords, but is safe for hashing
high entropy, randomly generated API keys.
"""
algorithm = "sha512"
def salt(self) -> str:
# No need for a salt on a high entropy key.
return ""
def encode(self, password: str, salt: str = "") -> str:
hashed = hashlib.sha512(password.encode()).hexdigest()
return f"{self.algorithm}$${hashed}"
def verify(self, password: str, encoded: str) -> bool:
encoded_2 = self.encode(password, "")
return constant_time_compare(encoded, encoded_2)
@functools.cache
def get_hasher():
return Sha512ApiKeyHasher()
def generate_key() -> tuple[str, str]:
"""Generate a [key, hash] couple."""
# this will result in key with a length of 72
key = str(secrets.token_urlsafe(54))
hasher = get_hasher()
return key, hasher.encode(key)

View File

@ -0,0 +1,113 @@
# Generated by Django 5.2 on 2025-06-01 08:53
import django.db.models.deletion
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
initial = True
dependencies = [
("auth", "0012_alter_user_first_name_max_length"),
("core", "0046_permissionrights"),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.CreateModel(
name="ApiClient",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
("name", models.CharField(max_length=64, verbose_name="name")),
("created_at", models.DateTimeField(auto_now_add=True)),
("updated_at", models.DateTimeField(auto_now=True)),
(
"client_permissions",
models.ManyToManyField(
blank=True,
help_text="Specific permissions for this api client.",
related_name="clients",
to="auth.permission",
verbose_name="client permissions",
),
),
(
"groups",
models.ManyToManyField(
blank=True,
related_name="api_clients",
to="core.group",
verbose_name="groups",
),
),
(
"owner",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name="api_clients",
to=settings.AUTH_USER_MODEL,
verbose_name="owner",
),
),
],
options={
"verbose_name": "api client",
"verbose_name_plural": "api clients",
},
),
migrations.CreateModel(
name="ApiKey",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
("name", models.CharField(blank=True, default="", verbose_name="name")),
(
"prefix",
models.CharField(
editable=False, max_length=5, verbose_name="prefix"
),
),
(
"hashed_key",
models.CharField(
db_index=True,
editable=False,
max_length=136,
verbose_name="hashed key",
),
),
("revoked", models.BooleanField(default=False, verbose_name="revoked")),
("created_at", models.DateTimeField(auto_now_add=True)),
(
"client",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name="api_keys",
to="api.apiclient",
verbose_name="api client",
),
),
],
options={
"verbose_name": "api key",
"verbose_name_plural": "api keys",
"permissions": [("revoke_apikey", "Revoke API keys")],
},
),
]

View File

View File

@ -1,27 +1,94 @@
# -*- coding:utf-8 -*
#
# Copyright 2016,2017
# - Skia <skia@libskia.so>
#
# Ce fichier fait partie du site de l'Association des Étudiants de l'UTBM,
# http://ae.utbm.fr.
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License a published by the Free Software
# Foundation; either version 3 of the License, or (at your option) any later
# version.
#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
# details.
#
# You should have received a copy of the GNU General Public License along with
# this program; if not, write to the Free Sofware Foundation, Inc., 59 Temple
# Place - Suite 330, Boston, MA 02111-1307, USA.
#
#
from typing import Iterable
from django.contrib.auth.models import Permission
from django.db import models
from django.utils.translation import gettext_lazy as _
from django.utils.translation import pgettext_lazy
# Create your models here.
from core.models import Group, User
class ApiClient(models.Model):
name = models.CharField(_("name"), max_length=64)
owner = models.ForeignKey(
User,
verbose_name=_("owner"),
related_name="api_clients",
on_delete=models.CASCADE,
)
groups = models.ManyToManyField(
Group, verbose_name=_("groups"), related_name="api_clients", blank=True
)
client_permissions = models.ManyToManyField(
Permission,
verbose_name=_("client permissions"),
blank=True,
help_text=_("Specific permissions for this api client."),
related_name="clients",
)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
_perm_cache: set[str] | None = None
class Meta:
verbose_name = _("api client")
verbose_name_plural = _("api clients")
def __str__(self):
return self.name
def has_perm(self, perm: str):
"""Return True if the client has the specified permission."""
if self._perm_cache is None:
group_permissions = (
Permission.objects.filter(group__group__in=self.groups.all())
.values_list("content_type__app_label", "codename")
.order_by()
)
client_permissions = self.client_permissions.values_list(
"content_type__app_label", "codename"
).order_by()
self._perm_cache = {
f"{content_type}.{name}"
for content_type, name in (*group_permissions, *client_permissions)
}
return perm in self._perm_cache
def has_perms(self, perm_list):
"""
Return True if the client has each of the specified permissions. If
object is passed, check if the client has all required perms for it.
"""
if not isinstance(perm_list, Iterable) or isinstance(perm_list, str):
raise ValueError("perm_list must be an iterable of permissions.")
return all(self.has_perm(perm) for perm in perm_list)
class ApiKey(models.Model):
PREFIX_LENGTH = 5
KEY_LENGTH = 72
HASHED_KEY_LENGTH = 136
name = models.CharField(_("name"), blank=True, default="")
prefix = models.CharField(_("prefix"), max_length=PREFIX_LENGTH, editable=False)
hashed_key = models.CharField(
_("hashed key"), max_length=HASHED_KEY_LENGTH, db_index=True, editable=False
)
client = models.ForeignKey(
ApiClient,
verbose_name=_("api client"),
related_name="api_keys",
on_delete=models.CASCADE,
)
revoked = models.BooleanField(pgettext_lazy("api key", "revoked"), default=False)
created_at = models.DateTimeField(auto_now_add=True)
class Meta:
verbose_name = _("api key")
verbose_name_plural = _("api keys")
permissions = [("revoke_apikey", "Revoke API keys")]
def __str__(self):
return f"{self.name} ({self.prefix}***)"

197
api/permissions.py Normal file
View File

@ -0,0 +1,197 @@
"""Permission classes to be used within ninja-extra controllers.
Some permissions are global (like `IsInGroup` or `IsRoot`),
and some others are per-object (like `CanView` or `CanEdit`).
Example:
```python
# restrict all the routes of this controller
# to subscribed users
@api_controller("/foo", permissions=[IsSubscriber])
class FooController(ControllerBase):
@route.get("/bar")
def bar_get(self):
# This route inherits the permissions of the controller
# ...
@route.bar("/bar/{bar_id}", permissions=[CanView])
def bar_get_one(self, bar_id: int):
# per-object permission resolution happens
# when calling either the `get_object_or_exception`
# or `get_object_or_none` method.
bar = self.get_object_or_exception(Counter, pk=bar_id)
# you can also call the `check_object_permission` manually
other_bar = Counter.objects.first()
self.check_object_permissions(other_bar)
# ...
# This route is restricted to counter admins and root users
@route.delete(
"/bar/{bar_id}",
permissions=[IsRoot | IsInGroup(settings.SITH_GROUP_COUNTER_ADMIN_ID)
]
def bar_delete(self, bar_id: int):
# ...
```
"""
import operator
from functools import reduce
from typing import Any, Callable
from django.contrib.auth.models import Permission
from django.http import HttpRequest
from ninja_extra import ControllerBase
from ninja_extra.permissions import BasePermission
from counter.models import Counter
class IsInGroup(BasePermission):
"""Check that the user is in the group whose primary key is given."""
def __init__(self, group_pk: int):
self._group_pk = group_pk
def has_permission(self, request: HttpRequest, controller: ControllerBase) -> bool:
return request.user.is_in_group(pk=self._group_pk)
class HasPerm(BasePermission):
"""Check that the user has the required perm.
If multiple perms are given, a comparer function can also be passed,
in order to change the way perms are checked.
Example:
```python
@api_controller("/foo")
class FooController(ControllerBase):
# this route will require both permissions
@route.put("/foo", permissions=[HasPerm(["foo.change_foo", "foo.add_foo"])]
def foo(self): ...
# This route will require at least one of the perm,
# but it's not mandatory to have all of them
@route.put(
"/bar",
permissions=[HasPerm(["foo.change_bar", "foo.add_bar"], op=operator.or_)],
)
def bar(self): ...
```
"""
def __init__(
self,
perms: str | Permission | list[str | Permission],
op: Callable[[bool, bool], bool] = operator.and_,
):
"""
Args:
perms: a permission or a list of permissions the user must have
op: An operator to combine multiple permissions (in most cases,
it will be either `operator.and_` or `operator.or_`)
"""
super().__init__()
if not isinstance(perms, (list, tuple, set)):
perms = [perms]
self._operator = op
self._perms = perms
def has_permission(self, request: HttpRequest, controller: ControllerBase) -> bool:
# if the request has the `auth` property,
# it means that the user has been explicitly authenticated
# using a django-ninja authentication backend
# (whether it is SessionAuth or ApiKeyAuth).
# If not, this authentication has not been done, but the user may
# still be implicitly authenticated through AuthenticationMiddleware
user = request.auth if hasattr(request, "auth") else request.user
# `user` may either be a `core.User` or an `api.ApiClient` ;
# they are not the same model, but they both implement the `has_perm` method
return reduce(self._operator, (user.has_perm(p) for p in self._perms))
class IsRoot(BasePermission):
"""Check that the user is root."""
def has_permission(self, request: HttpRequest, controller: ControllerBase) -> bool:
return request.user.is_root
class IsSubscriber(BasePermission):
"""Check that the user is currently subscribed."""
def has_permission(self, request: HttpRequest, controller: ControllerBase) -> bool:
return request.user.is_subscribed
class IsOldSubscriber(BasePermission):
"""Check that the user has at least one subscription in its history."""
def has_permission(self, request: HttpRequest, controller: ControllerBase) -> bool:
return request.user.was_subscribed
class CanView(BasePermission):
"""Check that this user has the permission to view the object of this route.
Wrap the `user.can_view(obj)` method.
To see an example, look at the example in the module docstring.
"""
def has_permission(self, request: HttpRequest, controller: ControllerBase) -> bool:
return True
def has_object_permission(
self, request: HttpRequest, controller: ControllerBase, obj: Any
) -> bool:
return request.user.can_view(obj)
class CanEdit(BasePermission):
"""Check that this user has the permission to edit the object of this route.
Wrap the `user.can_edit(obj)` method.
To see an example, look at the example in the module docstring.
"""
def has_permission(self, request: HttpRequest, controller: ControllerBase) -> bool:
return True
def has_object_permission(
self, request: HttpRequest, controller: ControllerBase, obj: Any
) -> bool:
return request.user.can_edit(obj)
class IsOwner(BasePermission):
"""Check that this user owns the object of this route.
Wrap the `user.is_owner(obj)` method.
To see an example, look at the example in the module docstring.
"""
def has_permission(self, request: HttpRequest, controller: ControllerBase) -> bool:
return True
def has_object_permission(
self, request: HttpRequest, controller: ControllerBase, obj: Any
) -> bool:
return request.user.is_owner(obj)
class IsLoggedInCounter(BasePermission):
"""Check that a user is logged in a counter."""
def has_permission(self, request: HttpRequest, controller: ControllerBase) -> bool:
if "/counter/" not in request.META.get("HTTP_REFERER", ""):
return False
token = request.session.get("counter_token")
if not token:
return False
return Counter.objects.filter(token=token).exists()
CanAccessLookup = IsLoggedInCounter | HasPerm("core.access_lookup")

View File

@ -1,27 +0,0 @@
# -*- coding:utf-8 -*
#
# Copyright 2016,2017
# - Skia <skia@libskia.so>
#
# Ce fichier fait partie du site de l'Association des Étudiants de l'UTBM,
# http://ae.utbm.fr.
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License a published by the Free Software
# Foundation; either version 3 of the License, or (at your option) any later
# version.
#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
# details.
#
# You should have received a copy of the GNU General Public License along with
# this program; if not, write to the Free Sofware Foundation, Inc., 59 Temple
# Place - Suite 330, Boston, MA 02111-1307, USA.
#
#
from django.test import TestCase
# Create your tests here.

0
api/tests/__init__.py Normal file
View File

29
api/tests/test_api_key.py Normal file
View File

@ -0,0 +1,29 @@
import pytest
from django.test import RequestFactory
from model_bakery import baker
from api.auth import ApiKeyAuth
from api.hashers import generate_key
from api.models import ApiClient, ApiKey
@pytest.mark.django_db
def test_api_key_auth():
key, hashed = generate_key()
client = baker.make(ApiClient)
baker.make(ApiKey, client=client, hashed_key=hashed)
auth = ApiKeyAuth()
assert auth.authenticate(RequestFactory().get(""), key) == client
@pytest.mark.django_db
@pytest.mark.parametrize(
("key", "hashed"), [(generate_key()[0], generate_key()[1]), (generate_key()[0], "")]
)
def test_api_key_auth_invalid(key, hashed):
client = baker.make(ApiClient)
baker.make(ApiKey, client=client, hashed_key=hashed)
auth = ApiKeyAuth()
assert auth.authenticate(RequestFactory().get(""), key) is None

View File

@ -1,56 +1,10 @@
# -*- coding:utf-8 -*
#
# Copyright 2016,2017
# - Skia <skia@libskia.so>
#
# Ce fichier fait partie du site de l'Association des Étudiants de l'UTBM,
# http://ae.utbm.fr.
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License a published by the Free Software
# Foundation; either version 3 of the License, or (at your option) any later
# version.
#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
# details.
#
# You should have received a copy of the GNU General Public License along with
# this program; if not, write to the Free Sofware Foundation, Inc., 59 Temple
# Place - Suite 330, Boston, MA 02111-1307, USA.
#
#
from ninja_extra import NinjaExtraAPI
from django.conf.urls import url, include
from api.views import *
from rest_framework import routers
# Router config
router = routers.DefaultRouter()
router.register(r"counter", CounterViewSet, base_name="api_counter")
router.register(r"user", UserViewSet, base_name="api_user")
router.register(r"club", ClubViewSet, base_name="api_club")
router.register(r"group", GroupViewSet, base_name="api_group")
# Launderette
router.register(
r"launderette/place", LaunderettePlaceViewSet, base_name="api_launderette_place"
api = NinjaExtraAPI(
title="PICON",
description="Portail Interactif de Communication avec les Outils Numériques",
version="0.2.0",
urls_namespace="api",
csrf=True,
)
router.register(
r"launderette/machine",
LaunderetteMachineViewSet,
base_name="api_launderette_machine",
)
router.register(
r"launderette/token", LaunderetteTokenViewSet, base_name="api_launderette_token"
)
urlpatterns = [
# API
url(r"^", include(router.urls)),
url(r"^login/", include("rest_framework.urls", namespace="rest_framework")),
url(r"^markdown$", RenderMarkdown, name="api_markdown"),
url(r"^mailings$", FetchMailingLists, name="mailings_fetch"),
]
api.auto_discover_controllers()

View File

@ -1,79 +0,0 @@
# -*- coding:utf-8 -*
#
# Copyright 2016,2017
# - Skia <skia@libskia.so>
#
# Ce fichier fait partie du site de l'Association des Étudiants de l'UTBM,
# http://ae.utbm.fr.
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License a published by the Free Software
# Foundation; either version 3 of the License, or (at your option) any later
# version.
#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
# details.
#
# You should have received a copy of the GNU General Public License along with
# this program; if not, write to the Free Sofware Foundation, Inc., 59 Temple
# Place - Suite 330, Boston, MA 02111-1307, USA.
#
#
from rest_framework.response import Response
from rest_framework import viewsets
from django.core.exceptions import PermissionDenied
from rest_framework.decorators import detail_route
from django.db.models.query import QuerySet
from core.views import can_view, can_edit
def check_if(obj, user, test):
"""
Detect if it's a single object or a queryset
aply a given test on individual object and return global permission
"""
if isinstance(obj, QuerySet):
for o in obj:
if test(o, user) is False:
return False
return True
else:
return test(obj, user)
class ManageModelMixin:
@detail_route()
def id(self, request, pk=None):
"""
Get by id (api/v1/router/{pk}/id/)
"""
self.queryset = get_object_or_404(self.queryset.filter(id=pk))
serializer = self.get_serializer(self.queryset)
return Response(serializer.data)
class RightModelViewSet(ManageModelMixin, viewsets.ModelViewSet):
def dispatch(self, request, *arg, **kwargs):
res = super(RightModelViewSet, self).dispatch(request, *arg, **kwargs)
obj = self.queryset
user = self.request.user
try:
if request.method == "GET" and check_if(obj, user, can_view):
return res
if request.method != "GET" and check_if(obj, user, can_edit):
return res
except:
pass # To prevent bug with Anonymous user
raise PermissionDenied
from .api import *
from .counter import *
from .user import *
from .club import *
from .group import *
from .launderette import *

View File

@ -1,43 +0,0 @@
# -*- coding:utf-8 -*
#
# Copyright 2016,2017
# - Skia <skia@libskia.so>
#
# Ce fichier fait partie du site de l'Association des Étudiants de l'UTBM,
# http://ae.utbm.fr.
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License a published by the Free Software
# Foundation; either version 3 of the License, or (at your option) any later
# version.
#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
# details.
#
# You should have received a copy of the GNU General Public License along with
# this program; if not, write to the Free Sofware Foundation, Inc., 59 Temple
# Place - Suite 330, Boston, MA 02111-1307, USA.
#
#
from rest_framework.response import Response
from rest_framework.decorators import api_view, renderer_classes
from rest_framework.renderers import StaticHTMLRenderer
from rest_framework.views import APIView
from core.templatetags.renderer import markdown
@api_view(["POST"])
@renderer_classes((StaticHTMLRenderer,))
def RenderMarkdown(request):
"""
Render Markdown
"""
try:
data = markdown(request.POST["text"])
except:
data = "Error"
return Response(data)

View File

@ -1,64 +0,0 @@
# -*- coding:utf-8 -*
#
# Copyright 2016,2017
# - Skia <skia@libskia.so>
#
# Ce fichier fait partie du site de l'Association des Étudiants de l'UTBM,
# http://ae.utbm.fr.
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License a published by the Free Software
# Foundation; either version 3 of the License, or (at your option) any later
# version.
#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
# details.
#
# You should have received a copy of the GNU General Public License along with
# this program; if not, write to the Free Sofware Foundation, Inc., 59 Temple
# Place - Suite 330, Boston, MA 02111-1307, USA.
#
#
from rest_framework.response import Response
from rest_framework import serializers
from rest_framework.decorators import api_view, renderer_classes
from rest_framework.renderers import StaticHTMLRenderer
from django.conf import settings
from django.core.exceptions import PermissionDenied
from club.models import Club, Mailing
from api.views import RightModelViewSet
class ClubSerializer(serializers.ModelSerializer):
class Meta:
model = Club
fields = ("id", "name", "unix_name", "address", "members")
class ClubViewSet(RightModelViewSet):
"""
Manage Clubs (api/v1/club/)
"""
serializer_class = ClubSerializer
queryset = Club.objects.all()
@api_view(["GET"])
@renderer_classes((StaticHTMLRenderer,))
def FetchMailingLists(request):
key = request.GET.get("key", "")
if key != settings.SITH_MAILING_FETCH_KEY:
raise PermissionDenied
data = ""
for mailing in Mailing.objects.filter(
is_moderated=True, club__is_active=True
).all():
data += mailing.fetch_format() + "\n"
return Response(data)

View File

@ -1,61 +0,0 @@
# -*- coding:utf-8 -*
#
# Copyright 2016,2017
# - Skia <skia@libskia.so>
#
# Ce fichier fait partie du site de l'Association des Étudiants de l'UTBM,
# http://ae.utbm.fr.
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License a published by the Free Software
# Foundation; either version 3 of the License, or (at your option) any later
# version.
#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
# details.
#
# You should have received a copy of the GNU General Public License along with
# this program; if not, write to the Free Sofware Foundation, Inc., 59 Temple
# Place - Suite 330, Boston, MA 02111-1307, USA.
#
#
from rest_framework import serializers
from rest_framework.response import Response
from rest_framework.decorators import list_route
from counter.models import Counter
from api.views import RightModelViewSet
class CounterSerializer(serializers.ModelSerializer):
is_open = serializers.BooleanField(read_only=True)
barman_list = serializers.ListField(
child=serializers.IntegerField(), read_only=True
)
class Meta:
model = Counter
fields = ("id", "name", "type", "club", "products", "is_open", "barman_list")
class CounterViewSet(RightModelViewSet):
"""
Manage Counters (api/v1/counter/)
"""
serializer_class = CounterSerializer
queryset = Counter.objects.all()
@list_route()
def bar(self, request):
"""
Return all bars (api/v1/counter/bar/)
"""
self.queryset = self.queryset.filter(type="BAR")
serializer = self.get_serializer(self.queryset, many=True)
return Response(serializer.data)

View File

@ -1,137 +0,0 @@
# -*- coding:utf-8 -*
#
# Copyright 2016,2017
# - Skia <skia@libskia.so>
#
# Ce fichier fait partie du site de l'Association des Étudiants de l'UTBM,
# http://ae.utbm.fr.
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License a published by the Free Software
# Foundation; either version 3 of the License, or (at your option) any later
# version.
#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
# details.
#
# You should have received a copy of the GNU General Public License along with
# this program; if not, write to the Free Sofware Foundation, Inc., 59 Temple
# Place - Suite 330, Boston, MA 02111-1307, USA.
#
#
from rest_framework import serializers
from rest_framework.response import Response
from rest_framework.decorators import list_route
from launderette.models import Launderette, Machine, Token
from api.views import RightModelViewSet
class LaunderettePlaceSerializer(serializers.ModelSerializer):
machine_list = serializers.ListField(
child=serializers.IntegerField(), read_only=True
)
token_list = serializers.ListField(child=serializers.IntegerField(), read_only=True)
class Meta:
model = Launderette
fields = (
"id",
"name",
"counter",
"machine_list",
"token_list",
"get_absolute_url",
)
class LaunderetteMachineSerializer(serializers.ModelSerializer):
class Meta:
model = Machine
fields = ("id", "name", "type", "is_working", "launderette")
class LaunderetteTokenSerializer(serializers.ModelSerializer):
class Meta:
model = Token
fields = (
"id",
"name",
"type",
"launderette",
"borrow_date",
"user",
"is_avaliable",
)
class LaunderettePlaceViewSet(RightModelViewSet):
"""
Manage Launderette (api/v1/launderette/place/)
"""
serializer_class = LaunderettePlaceSerializer
queryset = Launderette.objects.all()
class LaunderetteMachineViewSet(RightModelViewSet):
"""
Manage Washing Machines (api/v1/launderette/machine/)
"""
serializer_class = LaunderetteMachineSerializer
queryset = Machine.objects.all()
class LaunderetteTokenViewSet(RightModelViewSet):
"""
Manage Launderette's tokens (api/v1/launderette/token/)
"""
serializer_class = LaunderetteTokenSerializer
queryset = Token.objects.all()
@list_route()
def washing(self, request):
"""
Return all washing tokens (api/v1/launderette/token/washing)
"""
self.queryset = self.queryset.filter(type="WASHING")
serializer = self.get_serializer(self.queryset, many=True)
return Response(serializer.data)
@list_route()
def drying(self, request):
"""
Return all drying tokens (api/v1/launderette/token/drying)
"""
self.queryset = self.queryset.filter(type="DRYING")
serializer = self.get_serializer(self.queryset, many=True)
return Response(serializer.data)
@list_route()
def avaliable(self, request):
"""
Return all avaliable tokens (api/v1/launderette/token/avaliable)
"""
self.queryset = self.queryset.filter(
borrow_date__isnull=True, user__isnull=True
)
serializer = self.get_serializer(self.queryset, many=True)
return Response(serializer.data)
@list_route()
def unavaliable(self, request):
"""
Return all unavaliable tokens (api/v1/launderette/token/unavaliable)
"""
self.queryset = self.queryset.filter(
borrow_date__isnull=False, user__isnull=False
)
serializer = self.get_serializer(self.queryset, many=True)
return Response(serializer.data)

View File

@ -1,68 +0,0 @@
# -*- coding:utf-8 -*
#
# Copyright 2016,2017
# - Skia <skia@libskia.so>
#
# Ce fichier fait partie du site de l'Association des Étudiants de l'UTBM,
# http://ae.utbm.fr.
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License a published by the Free Software
# Foundation; either version 3 of the License, or (at your option) any later
# version.
#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
# details.
#
# You should have received a copy of the GNU General Public License along with
# this program; if not, write to the Free Sofware Foundation, Inc., 59 Temple
# Place - Suite 330, Boston, MA 02111-1307, USA.
#
#
import datetime
from rest_framework import serializers
from rest_framework.response import Response
from rest_framework.decorators import list_route
from core.models import User
from api.views import RightModelViewSet
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = (
"id",
"first_name",
"last_name",
"email",
"date_of_birth",
"nick_name",
"is_active",
"date_joined",
)
class UserViewSet(RightModelViewSet):
"""
Manage Users (api/v1/user/)
Only show active users
"""
serializer_class = UserSerializer
queryset = User.objects.filter(is_active=True)
@list_route()
def birthday(self, request):
"""
Return all users born today (api/v1/user/birstdays)
"""
date = datetime.datetime.today()
self.queryset = self.queryset.filter(date_of_birth=date)
serializer = self.get_serializer(self.queryset, many=True)
return Response(serializer.data)

29
biome.json Normal file
View File

@ -0,0 +1,29 @@
{
"$schema": "./node_modules/@biomejs/biome/configuration_schema.json",
"vcs": {
"enabled": true,
"clientKind": "git",
"useIgnoreFile": true
},
"files": {
"ignoreUnknown": false,
"ignore": ["*.min.*", "staticfiles/generated"]
},
"formatter": {
"enabled": true,
"indentStyle": "space",
"lineWidth": 88
},
"organizeImports": {
"enabled": true
},
"linter": {
"enabled": true,
"rules": {
"all": true
}
},
"javascript": {
"globals": ["Alpine", "$", "jQuery", "gettext", "interpolate"]
}
}

View File

@ -1,23 +1,14 @@
# -*- coding:utf-8 -*
#
# Copyright 2016,2017
# - Skia <skia@libskia.so>
# Copyright 2023 © AE UTBM
# ae@utbm.fr / ae.info@utbm.fr
#
# Ce fichier fait partie du site de l'Association des Étudiants de l'UTBM,
# http://ae.utbm.fr.
# This file is part of the website of the UTBM Student Association (AE UTBM),
# https://ae.utbm.fr.
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License a published by the Free Software
# Foundation; either version 3 of the License, or (at your option) any later
# version.
# You can find the source code of the website at https://github.com/ae-utbm/sith
#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
# details.
#
# You should have received a copy of the GNU General Public License along with
# this program; if not, write to the Free Sofware Foundation, Inc., 59 Temple
# Place - Suite 330, Boston, MA 02111-1307, USA.
# LICENSED UNDER THE GNU GENERAL PUBLIC LICENSE VERSION 3 (GPLv3)
# SEE : https://raw.githubusercontent.com/ae-utbm/sith/master/LICENSE
# OR WITHIN THE LOCAL FILE "LICENSE"
#
#

View File

@ -1,31 +1,42 @@
# -*- coding:utf-8 -*
#
# Copyright 2016,2017
# - Skia <skia@libskia.so>
# Copyright 2023 © AE UTBM
# ae@utbm.fr / ae.info@utbm.fr
#
# Ce fichier fait partie du site de l'Association des Étudiants de l'UTBM,
# http://ae.utbm.fr.
# This file is part of the website of the UTBM Student Association (AE UTBM),
# https://ae.utbm.fr.
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License a published by the Free Software
# Foundation; either version 3 of the License, or (at your option) any later
# version.
# You can find the source code of the website at https://github.com/ae-utbm/sith
#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
# details.
#
# You should have received a copy of the GNU General Public License along with
# this program; if not, write to the Free Sofware Foundation, Inc., 59 Temple
# Place - Suite 330, Boston, MA 02111-1307, USA.
# LICENSED UNDER THE GNU GENERAL PUBLIC LICENSE VERSION 3 (GPLv3)
# SEE : https://raw.githubusercontent.com/ae-utbm/sith/master/LICENSE
# OR WITHIN THE LOCAL FILE "LICENSE"
#
#
from django.contrib import admin
from club.models import Club, Membership
admin.site.register(Club)
admin.site.register(Membership)
@admin.register(Club)
class ClubAdmin(admin.ModelAdmin):
list_display = ("name", "slug_name", "parent", "is_active")
search_fields = ("name", "slug_name")
autocomplete_fields = (
"parent",
"board_group",
"members_group",
"home",
"page",
)
@admin.register(Membership)
class MembershipAdmin(admin.ModelAdmin):
list_display = ("user", "club", "role", "start_date", "end_date")
search_fields = (
"user__username",
"user__first_name",
"user__last_name",
"club__name",
)
autocomplete_fields = ("user",)

42
club/api.py Normal file
View File

@ -0,0 +1,42 @@
from typing import Annotated
from annotated_types import MinLen
from django.db.models import Prefetch
from ninja.security import SessionAuth
from ninja_extra import ControllerBase, api_controller, paginate, route
from ninja_extra.pagination import PageNumberPaginationExtra
from ninja_extra.schemas import PaginatedResponseSchema
from api.auth import ApiKeyAuth
from api.permissions import CanAccessLookup, HasPerm
from club.models import Club, Membership
from club.schemas import ClubSchema, SimpleClubSchema
@api_controller("/club")
class ClubController(ControllerBase):
@route.get(
"/search",
response=PaginatedResponseSchema[SimpleClubSchema],
auth=[SessionAuth(), ApiKeyAuth()],
permissions=[CanAccessLookup],
url_name="search_club",
)
@paginate(PageNumberPaginationExtra, page_size=50)
def search_club(self, search: Annotated[str, MinLen(1)]):
return Club.objects.filter(name__icontains=search).values()
@route.get(
"/{int:club_id}",
response=ClubSchema,
auth=[SessionAuth(), ApiKeyAuth()],
permissions=[HasPerm("club.view_club")],
url_name="fetch_club",
)
def fetch_club(self, club_id: int):
prefetch = Prefetch(
"members", queryset=Membership.objects.ongoing().select_related("user")
)
return self.get_object_or_exception(
Club.objects.prefetch_related(prefetch), id=club_id
)

View File

@ -1,4 +1,3 @@
# -*- coding:utf-8 -*
#
# Copyright 2016,2017
# - Skia <skia@libskia.so>
@ -23,50 +22,56 @@
#
#
from django.conf import settings
from django import forms
from django.utils.translation import ugettext_lazy as _
from ajax_select.fields import AutoCompleteSelectField, AutoCompleteSelectMultipleField
from club.models import Mailing, MailingSubscription, Club, Membership
from django.conf import settings
from django.db.models import Exists, OuterRef, Q
from django.db.models.functions import Lower
from django.utils.translation import gettext_lazy as _
from club.models import Club, Mailing, MailingSubscription, Membership
from core.models import User
from core.views.forms import SelectDate, SelectDateTime
from counter.models import Counter
from core.views.widgets.ajax_select import AutoCompleteSelectMultipleUser
from counter.models import Counter, Selling
class ClubEditForm(forms.ModelForm):
error_css_class = "error"
required_css_class = "required"
class Meta:
model = Club
fields = ["address", "logo", "short_description"]
widgets = {"short_description": forms.Textarea()}
def __init__(self, *args, **kwargs):
super(ClubEditForm, self).__init__(*args, **kwargs)
self.fields["short_description"].widget = forms.Textarea()
class ClubAdminEditForm(ClubEditForm):
admin_fields = ["name", "parent", "is_active"]
class Meta(ClubEditForm.Meta):
fields = ["name", "parent", "is_active", *ClubEditForm.Meta.fields]
class MailingForm(forms.Form):
"""
Form handling mailing lists right
"""
"""Form handling mailing lists right."""
ACTION_NEW_MAILING = 1
ACTION_NEW_SUBSCRIPTION = 2
ACTION_REMOVE_SUBSCRIPTION = 3
subscription_users = AutoCompleteSelectMultipleField(
"users",
subscription_users = forms.ModelMultipleChoiceField(
label=_("Users to add"),
help_text=_("Search users to add (one or more)."),
required=False,
widget=AutoCompleteSelectMultipleUser,
queryset=User.objects.all(),
)
def __init__(self, club_id, user_id, mailings, *args, **kwargs):
super(MailingForm, self).__init__(*args, **kwargs)
super().__init__(*args, **kwargs)
self.fields["action"] = forms.TypedChoiceField(
(
choices=(
(self.ACTION_NEW_MAILING, _("New Mailing")),
(self.ACTION_NEW_SUBSCRIPTION, _("Subscribe")),
(self.ACTION_REMOVE_SUBSCRIPTION, _("Remove")),
@ -108,24 +113,15 @@ class MailingForm(forms.Form):
)
def check_required(self, cleaned_data, field):
"""
If the given field doesn't exist or has no value, add a required error on it
"""
"""If the given field doesn't exist or has no value, add a required error on it."""
if not cleaned_data.get(field, None):
self.add_error(field, _("This field is required"))
def clean_subscription_users(self):
"""
Convert given users into real users and check their validity
"""
cleaned_data = super(MailingForm, self).clean()
"""Convert given users into real users and check their validity."""
cleaned_data = super().clean()
users = []
for user in cleaned_data["subscription_users"]:
user = User.objects.filter(id=user).first()
if not user:
raise forms.ValidationError(
_("One of the selected users doesn't exist"), code="invalid"
)
if not user.email:
raise forms.ValidationError(
_("One of the selected users doesn't have an email address"),
@ -135,9 +131,9 @@ class MailingForm(forms.Form):
return users
def clean(self):
cleaned_data = super(MailingForm, self).clean()
cleaned_data = super().clean()
if not "action" in cleaned_data:
if "action" not in cleaned_data:
# If there is no action provided, we can stop here
raise forms.ValidationError(_("An action is required"), code="invalid")
@ -157,37 +153,53 @@ class MailingForm(forms.Form):
return cleaned_data
class SellingsFormBase(forms.Form):
class SellingsForm(forms.Form):
begin_date = forms.DateTimeField(
["%Y-%m-%d %H:%M:%S"],
label=_("Begin date"),
required=False,
widget=SelectDateTime,
label=_("Begin date"), widget=SelectDateTime, required=False
)
end_date = forms.DateTimeField(
["%Y-%m-%d %H:%M:%S"],
label=_("End date"),
required=False,
widget=SelectDateTime,
)
counter = forms.ModelChoiceField(
Counter.objects.order_by("name").all(), label=_("Counter"), required=False
label=_("End date"), widget=SelectDateTime, required=False
)
def __init__(self, club, *args, **kwargs):
super().__init__(*args, **kwargs)
# postgres struggles really hard with a single query having three WHERE conditions,
# but deals perfectly fine with UNION of multiple queryset with their own WHERE clause,
# so we do this to get the ids, which we use to build another queryset that can be used by django.
club_sales_subquery = Selling.objects.filter(counter=OuterRef("pk"), club=club)
ids = (
Counter.objects.filter(Q(club=club) | Q(products__club=club))
.union(Counter.objects.filter(Exists(club_sales_subquery)))
.values_list("id", flat=True)
)
counters_qs = Counter.objects.filter(id__in=ids).order_by(Lower("name"))
self.fields["counters"] = forms.ModelMultipleChoiceField(
counters_qs, label=_("Counter"), required=False
)
self.fields["products"] = forms.ModelMultipleChoiceField(
club.products.order_by("name").filter(archived=False).all(),
label=_("Products"),
required=False,
)
self.fields["archived_products"] = forms.ModelMultipleChoiceField(
club.products.order_by("name").filter(archived=True).all(),
label=_("Archived products"),
required=False,
)
class ClubMemberForm(forms.Form):
"""
Form handling the members of a club
"""
"""Form handling the members of a club."""
error_css_class = "error"
required_css_class = "required"
users = AutoCompleteSelectMultipleField(
"users",
users = forms.ModelMultipleChoiceField(
label=_("Users to add"),
help_text=_("Search users to add (one or more)."),
required=False,
widget=AutoCompleteSelectMultipleUser,
queryset=User.objects.all(),
)
def __init__(self, *args, **kwargs):
@ -195,11 +207,9 @@ class ClubMemberForm(forms.Form):
self.request_user = kwargs.pop("request_user")
self.club_members = kwargs.pop("club_members", None)
if not self.club_members:
self.club_members = (
self.club.members.filter(end_date=None).order_by("-role").all()
)
self.club_members = self.club.members.ongoing().order_by("-role").all()
self.request_user_membership = self.club.get_membership_for(self.request_user)
super(ClubMemberForm, self).__init__(*args, **kwargs)
super().__init__(*args, **kwargs)
# Using a ModelForm binds too much the form with the model and we don't want that
# We want the view to process the model creation since they are multiple users
@ -224,9 +234,7 @@ class ClubMemberForm(forms.Form):
id__in=[
ms.user.id
for ms in self.club_members
if ms.can_be_edited_by(
self.request_user, self.request_user_membership
)
if ms.can_be_edited_by(self.request_user)
]
).all(),
label=_("Mark as old"),
@ -237,18 +245,13 @@ class ClubMemberForm(forms.Form):
self.fields.pop("start_date")
def clean_users(self):
"""Check that the user is not trying to add an user already in the club.
Also check that the user is valid and has a valid subscription.
"""
Check that the user is not trying to add an user already in the club
Also check that the user is valid and has a valid subscription
"""
cleaned_data = super(ClubMemberForm, self).clean()
cleaned_data = super().clean()
users = []
for user_id in cleaned_data["users"]:
user = User.objects.filter(id=user_id).first()
if not user:
raise forms.ValidationError(
_("One of the selected users doesn't exist"), code="invalid"
)
for user in cleaned_data["users"]:
if not user.is_subscribed:
raise forms.ValidationError(
_("User must be subscriber to take part to a club"), code="invalid"
@ -261,10 +264,8 @@ class ClubMemberForm(forms.Form):
return users
def clean(self):
"""
Check user rights for adding an user
"""
cleaned_data = super(ClubMemberForm, self).clean()
"""Check user rights for adding an user."""
cleaned_data = super().clean()
if "start_date" in cleaned_data and not cleaned_data["start_date"]:
# Drop start_date if allowed to edition but not specified

View File

@ -1,12 +1,11 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations, models
import django.core.validators
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = []
operations = [
@ -90,7 +89,10 @@ class Migration(migrations.Migration):
(
"club",
models.ForeignKey(
verbose_name="club", to="club.Club", related_name="members"
verbose_name="club",
to="club.Club",
related_name="members",
on_delete=django.db.models.deletion.CASCADE,
),
),
],

View File

@ -1,12 +1,11 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations, models
import django.db.models.deletion
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
("club", "0001_initial"),
@ -18,6 +17,7 @@ class Migration(migrations.Migration):
model_name="membership",
name="user",
field=models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
verbose_name="user",
to=settings.AUTH_USER_MODEL,
related_name="membership",
@ -34,6 +34,7 @@ class Migration(migrations.Migration):
model_name="club",
name="home",
field=models.OneToOneField(
on_delete=django.db.models.deletion.CASCADE,
blank=True,
null=True,
related_name="home_of_club",
@ -45,14 +46,21 @@ class Migration(migrations.Migration):
model_name="club",
name="owner_group",
field=models.ForeignKey(
default=1, to="core.Group", related_name="owned_club"
on_delete=django.db.models.deletion.CASCADE,
default=1,
to="core.Group",
related_name="owned_club",
),
),
migrations.AddField(
model_name="club",
name="parent",
field=models.ForeignKey(
null=True, to="club.Club", related_name="children", blank=True
on_delete=django.db.models.deletion.CASCADE,
null=True,
to="club.Club",
related_name="children",
blank=True,
),
),
migrations.AddField(

View File

@ -1,11 +1,9 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [("club", "0002_auto_20160824_2152")]
operations = [

View File

@ -1,12 +1,11 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations, models
import django.db.models.deletion
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [("club", "0003_auto_20160902_2042")]
operations = [
@ -14,6 +13,7 @@ class Migration(migrations.Migration):
model_name="membership",
name="user",
field=models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
verbose_name="user",
related_name="memberships",
to=settings.AUTH_USER_MODEL,

View File

@ -1,12 +1,10 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations, models
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [("club", "0004_auto_20160915_1057")]
operations = [

View File

@ -1,12 +1,10 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations, models
import django.utils.timezone
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [("club", "0005_auto_20161120_1149")]
operations = [

View File

@ -1,11 +1,9 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations, models
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [("club", "0006_auto_20161229_0040")]
operations = [

View File

@ -1,11 +1,9 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [("club", "0007_auto_20170324_0917")]
operations = [

View File

@ -1,14 +1,14 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations, models
from django.conf import settings
import re
import django.core.validators
import django.db.models.deletion
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
("club", "0008_auto_20170515_2214"),
@ -51,12 +51,16 @@ class Migration(migrations.Migration):
(
"club",
models.ForeignKey(
verbose_name="Club", related_name="mailings", to="club.Club"
on_delete=django.db.models.deletion.CASCADE,
verbose_name="Club",
related_name="mailings",
to="club.Club",
),
),
(
"moderator",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
null=True,
verbose_name="moderator",
related_name="moderated_mailings",
@ -84,6 +88,7 @@ class Migration(migrations.Migration):
(
"mailing",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
verbose_name="Mailing",
related_name="subscriptions",
to="club.Mailing",
@ -92,6 +97,7 @@ class Migration(migrations.Migration):
(
"user",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
null=True,
verbose_name="User",
related_name="mailing_subscriptions",
@ -103,6 +109,6 @@ class Migration(migrations.Migration):
),
migrations.AlterUniqueTogether(
name="mailingsubscription",
unique_together=set([("user", "email", "mailing")]),
unique_together={("user", "email", "mailing")},
),
]

View File

@ -1,24 +1,10 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
import django.db.models.deletion
from django.db import migrations, models
from club.models import Club
from core.operations import PsqlRunOnly
def generate_club_pages(apps, schema_editor):
def recursive_generate_club_page(club):
club.make_page()
for child in Club.objects.filter(parent=club).all():
recursive_generate_club_page(child)
for club in Club.objects.filter(parent=None).all():
recursive_generate_club_page(club)
class Migration(migrations.Migration):
dependencies = [("core", "0024_auto_20170906_1317"), ("club", "0010_club_logo")]
operations = [
@ -31,7 +17,11 @@ class Migration(migrations.Migration):
model_name="club",
name="page",
field=models.OneToOneField(
related_name="club", blank=True, null=True, to="core.Page"
on_delete=django.db.models.deletion.CASCADE,
related_name="club",
blank=True,
null=True,
to="core.Page",
),
),
migrations.AddField(
@ -45,11 +35,4 @@ class Migration(migrations.Migration):
null=True,
),
),
PsqlRunOnly(
"SET CONSTRAINTS ALL IMMEDIATE", reverse_sql=migrations.RunSQL.noop
),
migrations.RunPython(generate_club_pages),
PsqlRunOnly(
migrations.RunSQL.noop, reverse_sql="SET CONSTRAINTS ALL IMMEDIATE"
),
]

View File

@ -1,11 +1,9 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [("club", "0009_auto_20170822_2232")]
operations = [

View File

@ -1,12 +1,11 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
import django.db.models.deletion
from django.conf import settings
from django.db import migrations, models
import club.models
class Migration(migrations.Migration):
dependencies = [("club", "0010_auto_20170912_2028")]
operations = [
@ -14,7 +13,8 @@ class Migration(migrations.Migration):
model_name="club",
name="owner_group",
field=models.ForeignKey(
default=club.models.Club.get_default_owner_group,
on_delete=django.db.models.deletion.CASCADE,
default=lambda: settings.SITH_ROOT_USER_ID,
related_name="owned_club",
to="core.Group",
),

View File

@ -0,0 +1,106 @@
# Generated by Django 4.2.16 on 2024-11-20 17:08
import django.db.models.deletion
import django.db.models.functions.datetime
from django.conf import settings
from django.db import migrations, models
from django.db.migrations.state import StateApps
from django.db.models import Q
from django.utils.timezone import localdate
def migrate_meta_groups(apps: StateApps, schema_editor):
"""Attach the existing meta groups to the clubs.
Until now, the meta groups were not attached to the clubs,
nor to the users.
This creates actual foreign relationships between the clubs
and theirs groups and the users and theirs groups.
Warnings:
When the meta groups associated with the clubs aren't found,
they are created.
Thus the migration shouldn't fail, and all the clubs will
have their groups.
However, there will probably be some groups that have
not been found but exist nonetheless,
so there will be duplicates and dangling groups.
There must be a manual cleanup after this migration.
"""
Group = apps.get_model("core", "Group")
Club = apps.get_model("club", "Club")
meta_groups = Group.objects.filter(is_meta=True)
clubs = list(Club.objects.all())
for club in clubs:
club.board_group = meta_groups.get_or_create(
name=club.unix_name + settings.SITH_BOARD_SUFFIX,
defaults={"is_meta": True},
)[0]
club.members_group = meta_groups.get_or_create(
name=club.unix_name + settings.SITH_MEMBER_SUFFIX,
defaults={"is_meta": True},
)[0]
club.save()
club.refresh_from_db()
memberships = club.members.filter(
Q(end_date=None) | Q(end_date__gt=localdate())
).select_related("user")
club.members_group.users.set([m.user for m in memberships])
club.board_group.users.set(
[
m.user
for m in memberships.filter(role__gt=settings.SITH_MAXIMUM_FREE_ROLE)
]
)
# steps of the migration :
# - Create a nullable field for the board group and the member group
# - Edit those new fields to make them point to currently existing meta groups
# - When this data migration is done, make the fields non-nullable
class Migration(migrations.Migration):
dependencies = [
("core", "0040_alter_user_options_user_user_permissions_and_more"),
("club", "0011_auto_20180426_2013"),
]
operations = [
migrations.RemoveField(
model_name="club",
name="edit_groups",
),
migrations.RemoveField(
model_name="club",
name="owner_group",
),
migrations.RemoveField(
model_name="club",
name="view_groups",
),
migrations.AddField(
model_name="club",
name="board_group",
field=models.OneToOneField(
blank=True,
null=True,
on_delete=django.db.models.deletion.PROTECT,
related_name="club_board",
to="core.group",
),
),
migrations.AddField(
model_name="club",
name="members_group",
field=models.OneToOneField(
blank=True,
null=True,
on_delete=django.db.models.deletion.PROTECT,
related_name="club",
to="core.group",
),
),
migrations.RunPython(
migrate_meta_groups, reverse_code=migrations.RunPython.noop, elidable=True
),
]

View File

@ -0,0 +1,36 @@
# Generated by Django 4.2.17 on 2025-01-04 16:46
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [("club", "0012_club_board_group_club_members_group")]
operations = [
migrations.AlterField(
model_name="club",
name="board_group",
field=models.OneToOneField(
on_delete=django.db.models.deletion.PROTECT,
related_name="club_board",
to="core.group",
),
),
migrations.AlterField(
model_name="club",
name="members_group",
field=models.OneToOneField(
on_delete=django.db.models.deletion.PROTECT,
related_name="club",
to="core.group",
),
),
migrations.AddConstraint(
model_name="membership",
constraint=models.CheckConstraint(
check=models.Q(("end_date__gte", models.F("start_date"))),
name="end_after_start",
),
),
]

View File

@ -0,0 +1,75 @@
# Generated by Django 4.2.17 on 2025-02-28 20:34
import django.db.models.deletion
from django.db import migrations, models
import core.fields
class Migration(migrations.Migration):
dependencies = [
("core", "0044_alter_userban_options"),
("club", "0013_alter_club_board_group_alter_club_members_group_and_more"),
]
operations = [
migrations.AlterModelOptions(name="club", options={"ordering": ["name"]}),
migrations.RenameField(
model_name="club",
old_name="unix_name",
new_name="slug_name",
),
migrations.AlterField(
model_name="club",
name="name",
field=models.CharField(unique=True, max_length=64, verbose_name="name"),
),
migrations.AlterField(
model_name="club",
name="slug_name",
field=models.SlugField(
editable=False, max_length=30, unique=True, verbose_name="slug name"
),
),
migrations.AlterField(
model_name="club",
name="id",
field=models.AutoField(
auto_created=True, primary_key=True, serialize=False, verbose_name="ID"
),
),
migrations.AlterField(
model_name="club",
name="logo",
field=core.fields.ResizedImageField(
blank=True,
force_format="WEBP",
height=200,
null=True,
upload_to="club_logos",
verbose_name="logo",
width=200,
),
),
migrations.AlterField(
model_name="club",
name="page",
field=models.OneToOneField(
blank=True,
on_delete=django.db.models.deletion.CASCADE,
related_name="club",
to="core.page",
),
),
migrations.AlterField(
model_name="club",
name="short_description",
field=models.CharField(
blank=True,
default="",
help_text="A summary of what your club does. This will be displayed on the club list page.",
max_length=1000,
verbose_name="short description",
),
),
]

View File

@ -1,4 +1,3 @@
# -*- coding:utf-8 -*
#
# Copyright 2016,2017
# - Skia <skia@libskia.so>
@ -22,67 +21,58 @@
# Place - Suite 330, Boston, MA 02111-1307, USA.
#
#
from __future__ import annotations
from typing import Iterable, Self
from django.db import models
from django.core import validators
from django.conf import settings
from django.utils.translation import ugettext_lazy as _
from django.core.exceptions import ValidationError, ObjectDoesNotExist
from django.db import transaction
from django.core.urlresolvers import reverse
from django.utils import timezone
from django.core.cache import cache
from django.core.exceptions import ObjectDoesNotExist, ValidationError
from django.core.validators import RegexValidator, validate_email
from django.db import models, transaction
from django.db.models import Exists, F, OuterRef, Q
from django.urls import reverse
from django.utils import timezone
from django.utils.functional import cached_property
from django.utils.text import slugify
from django.utils.timezone import localdate
from django.utils.translation import gettext_lazy as _
from core.models import User, MetaGroup, Group, SithFile, RealGroup, Notification, Page
# Create your models here.
from core.fields import ResizedImageField
from core.models import Group, Notification, Page, SithFile, User
class Club(models.Model):
"""
The Club class, made as a tree to allow nice tidy organization
"""
"""The Club class, made as a tree to allow nice tidy organization."""
id = models.AutoField(primary_key=True, db_index=True)
name = models.CharField(_("name"), max_length=64)
parent = models.ForeignKey("Club", related_name="children", null=True, blank=True)
unix_name = models.CharField(
_("unix name"),
max_length=30,
unique=True,
validators=[
validators.RegexValidator(
r"^[a-z0-9][a-z0-9._-]*[a-z0-9]$",
_(
"Enter a valid unix name. This value may contain only "
"letters, numbers ./-/_ characters."
),
)
],
error_messages={"unique": _("A club with that unix name already exists.")},
name = models.CharField(_("name"), unique=True, max_length=64)
parent = models.ForeignKey(
"Club", related_name="children", null=True, blank=True, on_delete=models.CASCADE
)
logo = models.ImageField(
upload_to="club_logos", verbose_name=_("logo"), null=True, blank=True
slug_name = models.SlugField(
_("slug name"), max_length=30, unique=True, editable=False
)
logo = ResizedImageField(
upload_to="club_logos",
verbose_name=_("logo"),
null=True,
blank=True,
force_format="WEBP",
height=200,
width=200,
)
is_active = models.BooleanField(_("is active"), default=True)
short_description = models.CharField(
_("short description"), max_length=1000, default="", blank=True, null=True
_("short description"),
max_length=1000,
default="",
blank=True,
help_text=_(
"A summary of what your club does. "
"This will be displayed on the club list page."
),
)
address = models.CharField(_("address"), max_length=254)
# This function prevents generating migration upon settings change
def get_default_owner_group():
return settings.SITH_GROUP_ROOT_ID
owner_group = models.ForeignKey(
Group, related_name="owned_club", default=get_default_owner_group
)
edit_groups = models.ManyToManyField(
Group, related_name="editable_club", blank=True
)
view_groups = models.ManyToManyField(
Group, related_name="viewable_club", blank=True
)
home = models.OneToOneField(
SithFile,
related_name="home_of_club",
@ -91,19 +81,60 @@ class Club(models.Model):
blank=True,
on_delete=models.SET_NULL,
)
page = models.OneToOneField(Page, related_name="club", blank=True, null=True)
page = models.OneToOneField(
Page, related_name="club", blank=True, on_delete=models.CASCADE
)
members_group = models.OneToOneField(
Group, related_name="club", on_delete=models.PROTECT
)
board_group = models.OneToOneField(
Group, related_name="club_board", on_delete=models.PROTECT
)
class Meta:
ordering = ["name", "unix_name"]
ordering = ["name"]
def __str__(self):
return self.name
@transaction.atomic()
def save(self, *args, **kwargs):
creation = self._state.adding
if (slug := slugify(self.name)[:30]) != self.slug_name:
self.slug_name = slug
if not creation:
db_club = Club.objects.get(id=self.id)
if self.name != db_club.name:
self.home.name = self.slug_name
self.home.save()
if self.name != db_club.name:
self.board_group.name = f"{self.name} - Bureau"
self.board_group.save()
self.members_group.name = f"{self.name} - Membres"
self.members_group.save()
if creation:
self.board_group = Group.objects.create(
name=f"{self.name} - Bureau", is_manually_manageable=False
)
self.members_group = Group.objects.create(
name=f"{self.name} - Membres", is_manually_manageable=False
)
self.make_home()
self.make_page()
super().save(*args, **kwargs)
def get_absolute_url(self):
return reverse("club:club_view", kwargs={"club_id": self.id})
@cached_property
def president(self):
def president(self) -> Membership | None:
"""Fetch the membership of the current president of this club."""
return self.members.filter(
role=settings.SITH_CLUB_ROLES_ID["President"], end_date=None
).first()
def check_loop(self):
"""Raise a validation error when a loop is found within the parent list"""
"""Raise a validation error when a loop is found within the parent list."""
objs = []
cur = self
while cur.parent is not None:
@ -115,137 +146,166 @@ class Club(models.Model):
def clean(self):
self.check_loop()
def _change_unixname(self, new_name):
c = Club.objects.filter(unix_name=new_name).first()
if c is None:
if self.home:
self.home.name = new_name
self.home.save()
else:
raise ValidationError(_("A club with that unix_name already exists"))
def make_home(self) -> None:
if self.home:
return
home_root = SithFile.objects.get(parent=None, name="clubs")
root = User.objects.get(id=settings.SITH_ROOT_USER_ID)
self.home = SithFile.objects.create(
parent=home_root, name=self.slug_name, owner=root
)
def make_home(self):
if not self.home:
home_root = SithFile.objects.filter(parent=None, name="clubs").first()
root = User.objects.filter(username="root").first()
if home_root and root:
home = SithFile(parent=home_root, name=self.unix_name, owner=root)
home.save()
self.home = home
self.save()
def make_page(self):
root = User.objects.filter(username="root").first()
if not self.page:
club_root = Page.objects.filter(name=settings.SITH_CLUB_ROOT_PAGE).first()
if root and club_root:
public = Group.objects.filter(id=settings.SITH_GROUP_PUBLIC_ID).first()
p = Page(name=self.unix_name)
p.parent = club_root
p.save(force_lock=True)
if public:
p.view_groups.add(public)
p.save(force_lock=True)
if self.parent and self.parent.page:
p.parent = self.parent.page
self.page = p
self.save()
elif self.page and self.page.name != self.unix_name:
self.page.unset_lock()
self.page.name = self.unix_name
self.page.save(force_lock=True)
elif (
self.page
and self.parent
and self.parent.page
and self.page.parent != self.parent.page
):
self.page.unset_lock()
def make_page(self) -> None:
page_name = self.slug_name
if not self.page_id:
# Club.page is a OneToOneField, so if we are inside this condition
# then self._meta.state.adding is True.
club_root = Page.objects.get(name=settings.SITH_CLUB_ROOT_PAGE)
public = Group.objects.get(id=settings.SITH_GROUP_PUBLIC_ID)
p = Page(name=page_name, parent=club_root)
p.save(force_lock=True)
p.view_groups.add(public)
if self.parent and self.parent.page_id:
p.parent_id = self.parent.page_id
self.page = p
return
self.page.unset_lock()
if self.page.name != page_name:
self.page.name = page_name
elif self.parent and self.parent.page and self.page.parent != self.parent.page:
self.page.parent = self.parent.page
self.page.save(force_lock=True)
self.page.save(force_lock=True)
def save(self, *args, **kwargs):
with transaction.atomic():
creation = False
old = Club.objects.filter(id=self.id).first()
if not old:
creation = True
else:
if old.unix_name != self.unix_name:
self._change_unixname(self.unix_name)
super(Club, self).save(*args, **kwargs)
if creation:
board = MetaGroup(name=self.unix_name + settings.SITH_BOARD_SUFFIX)
board.save()
member = MetaGroup(name=self.unix_name + settings.SITH_MEMBER_SUFFIX)
member.save()
subscribers = Group.objects.filter(
name=settings.SITH_MAIN_MEMBERS_GROUP
).first()
self.make_home()
self.home.edit_groups = [board]
self.home.view_groups = [member, subscribers]
self.home.save()
self.make_page()
def delete(self, *args, **kwargs) -> tuple[int, dict[str, int]]:
# Invalidate the cache of this club and of its memberships
for membership in self.members.ongoing().select_related("user"):
cache.delete(f"membership_{self.id}_{membership.user.id}")
self.board_group.delete()
self.members_group.delete()
return super().delete(*args, **kwargs)
def __str__(self):
def get_display_name(self) -> str:
return self.name
def get_absolute_url(self):
return reverse("club:club_view", kwargs={"club_id": self.id})
def is_owned_by(self, user: User) -> bool:
"""Method to see if that object can be super edited by the given user."""
if user.is_anonymous:
return False
return user.is_root or user.is_board_member
def get_display_name(self):
return self.name
def get_full_logo_url(self) -> str:
return f"https://{settings.SITH_URL}{self.logo.url}"
def is_owned_by(self, user):
"""
Method to see if that object can be super edited by the given user
"""
return user.is_in_group(settings.SITH_MAIN_BOARD_GROUP)
def get_full_logo_url(self):
return "https://%s%s" % (settings.SITH_URL, self.logo.url)
def can_be_edited_by(self, user):
"""
Method to see if that object can be edited by the given user
"""
def can_be_edited_by(self, user: User) -> bool:
"""Method to see if that object can be edited by the given user."""
return self.has_rights_in_club(user)
def can_be_viewed_by(self, user):
"""
Method to see if that object can be seen by the given user
"""
sub = User.objects.filter(pk=user.pk).first()
if sub is None:
return False
return sub.was_subscribed
def can_be_viewed_by(self, user: User) -> bool:
"""Method to see if that object can be seen by the given user."""
return user.was_subscribed
_memberships = {}
def get_membership_for(self, user: User) -> Membership | None:
"""Return the current membership the given user.
def get_membership_for(self, user):
Note:
The result is cached.
"""
Returns the current membership the given user
"""
try:
return Club._memberships[self.id][user.id]
except:
m = self.members.filter(user=user.id).filter(end_date=None).first()
try:
Club._memberships[self.id][user.id] = m
except:
Club._memberships[self.id] = {}
Club._memberships[self.id][user.id] = m
return m
if user.is_anonymous:
return None
membership = cache.get(f"membership_{self.id}_{user.id}")
if membership == "not_member":
return None
if membership is None:
membership = self.members.filter(user=user, end_date=None).first()
if membership is None:
cache.set(f"membership_{self.id}_{user.id}", "not_member")
else:
cache.set(f"membership_{self.id}_{user.id}", membership)
return membership
def has_rights_in_club(self, user):
m = self.get_membership_for(user)
return m is not None and m.role > settings.SITH_MAXIMUM_FREE_ROLE
def has_rights_in_club(self, user: User) -> bool:
return user.is_in_group(pk=self.board_group_id)
class MembershipQuerySet(models.QuerySet):
def ongoing(self) -> Self:
"""Filter all memberships which are not finished yet."""
return self.filter(Q(end_date=None) | Q(end_date__gt=localdate()))
def board(self) -> Self:
"""Filter all memberships where the user is/was in the board.
Be aware that users who were in the board in the past
are included, even if there are no more members.
If you want to get the users who are currently in the board,
mind combining this with the :meth:`ongoing` queryset method
"""
return self.filter(role__gt=settings.SITH_MAXIMUM_FREE_ROLE)
def update(self, **kwargs) -> int:
"""Refresh the cache and edit group ownership.
Update the cache, when necessary, remove
users from club groups they are no more in
and add them in the club groups they should be in.
Be aware that this adds three db queries :
one to retrieve the updated memberships,
one to perform group removal and one to perform
group attribution.
"""
nb_rows = super().update(**kwargs)
if nb_rows == 0:
# if no row was affected, no need to refresh the cache
return 0
cache_memberships = {}
memberships = set(self.select_related("club"))
# delete all User-Group relations and recreate the necessary ones
# It's more concise to write and more reliable
Membership._remove_club_groups(memberships)
Membership._add_club_groups(memberships)
for member in memberships:
cache_key = f"membership_{member.club_id}_{member.user_id}"
if member.end_date is None:
cache_memberships[cache_key] = member
else:
cache_memberships[cache_key] = "not_member"
cache.set_many(cache_memberships)
return nb_rows
def delete(self) -> tuple[int, dict[str, int]]:
"""Work just like the default Django's delete() method,
but add a cache invalidation for the elements of the queryset
before the deletion,
and a removal of the user from the club groups.
Be aware that this adds some db queries :
- 1 to retrieve the deleted elements in order to perform
post-delete operations.
As we can't know if a delete will affect rows or not,
this query will always happen
- 1 query to remove the users from the club groups.
If the delete operation affected no row,
this query won't happen.
"""
memberships = set(self.all())
nb_rows, rows_counts = super().delete()
if nb_rows > 0:
Membership._remove_club_groups(memberships)
cache.set_many(
{
f"membership_{m.club_id}_{m.user_id}": "not_member"
for m in memberships
}
)
return nb_rows, rows_counts
class Membership(models.Model):
"""
The Membership class makes the connection between User and Clubs
"""The Membership class makes the connection between User and Clubs.
Both Users and Clubs can have many Membership objects:
- a user can be a member of many clubs at a time
@ -261,9 +321,15 @@ class Membership(models.Model):
related_name="memberships",
null=False,
blank=False,
on_delete=models.CASCADE,
)
club = models.ForeignKey(
Club, verbose_name=_("club"), related_name="members", null=False, blank=False
Club,
verbose_name=_("club"),
related_name="members",
null=False,
blank=False,
on_delete=models.CASCADE,
)
start_date = models.DateField(_("start date"), default=timezone.now)
end_date = models.DateField(_("end date"), null=True, blank=True)
@ -276,48 +342,153 @@ class Membership(models.Model):
_("description"), max_length=128, null=False, blank=True
)
objects = MembershipQuerySet.as_manager()
class Meta:
constraints = [
models.CheckConstraint(
check=Q(end_date__gte=F("start_date")), name="end_after_start"
),
]
def __str__(self):
return (
self.club.name
+ " - "
+ self.user.username
+ " - "
+ str(settings.SITH_CLUB_ROLES[self.role])
+ str(" - " + str(_("past member")) if self.end_date is not None else "")
f"{self.club.name} - {self.user.username} "
f"- {settings.SITH_CLUB_ROLES[self.role]} "
f"- {str(_('past member')) if self.end_date is not None else ''}"
)
def is_owned_by(self, user):
"""
Method to see if that object can be super edited by the given user
"""
return user.is_in_group(settings.SITH_MAIN_BOARD_GROUP)
def can_be_edited_by(self, user, membership=None):
"""
Method to see if that object can be edited by the given user
"""
if user.memberships:
if membership: # This is for optimisation purpose
ms = membership
else:
ms = user.memberships.filter(club=self.club, end_date=None).first()
return (ms and ms.role >= self.role) or user.is_in_group(
settings.SITH_MAIN_BOARD_GROUP
)
return user.is_in_group(settings.SITH_MAIN_BOARD_GROUP)
def save(self, *args, **kwargs):
super().save(*args, **kwargs)
# a save may either be an update or a creation
# and may result in either an ongoing or an ended membership.
# It could also be a retrogradation from the board to being a simple member.
# To avoid problems, the user is removed from the club groups beforehand ;
# he will be added back if necessary
self._remove_club_groups([self])
if self.end_date is None:
self._add_club_groups([self])
cache.set(f"membership_{self.club_id}_{self.user_id}", self)
else:
cache.set(f"membership_{self.club_id}_{self.user_id}", "not_member")
def get_absolute_url(self):
return reverse("club:club_members", kwargs={"club_id": self.club.id})
return reverse("club:club_members", kwargs={"club_id": self.club_id})
def is_owned_by(self, user: User) -> bool:
"""Method to see if that object can be super edited by the given user."""
if user.is_anonymous:
return False
return user.is_root or user.is_board_member
def can_be_edited_by(self, user: User) -> bool:
"""Check if that object can be edited by the given user."""
if user.is_root or user.is_board_member:
return True
membership = self.club.get_membership_for(user)
return membership is not None and membership.role >= self.role
def delete(self, *args, **kwargs):
self._remove_club_groups([self])
super().delete(*args, **kwargs)
cache.delete(f"membership_{self.club_id}_{self.user_id}")
@staticmethod
def _remove_club_groups(
memberships: Iterable[Membership],
) -> tuple[int, dict[str, int]]:
"""Remove users of those memberships from the club groups.
For example, if a user is in the Troll club board,
he is in the board group and the members group of the Troll.
After calling this function, he will be in neither.
Returns:
The result of the deletion queryset.
Warnings:
If this function isn't used in combination
with an actual deletion of the memberships,
it will result in an inconsistent state,
where users will be in the clubs, without
having the associated rights.
"""
clubs = {m.club_id for m in memberships}
users = {m.user_id for m in memberships}
groups = Group.objects.filter(Q(club__in=clubs) | Q(club_board__in=clubs))
return User.groups.through.objects.filter(
Q(group__in=groups) & Q(user__in=users)
).delete()
@staticmethod
def _add_club_groups(
memberships: Iterable[Membership],
) -> list[User.groups.through]:
"""Add users of those memberships to the club groups.
For example, if a user just joined the Troll club board,
he will be added in both the members group and the board group
of the club.
Returns:
The created User-Group relations.
Warnings:
If this function isn't used in combination
with an actual update/creation of the memberships,
it will result in an inconsistent state,
where users will have the rights associated to the
club, without actually being part of it.
"""
# only active membership (i.e. `end_date=None`)
# grant the attribution of club groups.
memberships = [m for m in memberships if m.end_date is None]
if not memberships:
return []
if sum(1 for m in memberships if not hasattr(m, "club")) > 1:
# if more than one membership hasn't its `club` attribute set
# it's less expensive to reload the whole query with
# a select_related than perform a distinct query
# to fetch each club.
ids = {m.id for m in memberships}
memberships = list(
Membership.objects.filter(id__in=ids).select_related("club")
)
club_groups = []
for membership in memberships:
club_groups.append(
User.groups.through(
user_id=membership.user_id,
group_id=membership.club.members_group_id,
)
)
if membership.role > settings.SITH_MAXIMUM_FREE_ROLE:
club_groups.append(
User.groups.through(
user_id=membership.user_id,
group_id=membership.club.board_group_id,
)
)
return User.groups.through.objects.bulk_create(
club_groups, ignore_conflicts=True
)
class Mailing(models.Model):
"""
This class correspond to a mailing list
Remember that mailing lists should be validated by UTBM
"""A Mailing list for a club.
Warning:
Remember that mailing lists should be validated by UTBM.
"""
club = models.ForeignKey(
Club, verbose_name=_("Club"), related_name="mailings", null=False, blank=False
Club,
verbose_name=_("Club"),
related_name="mailings",
null=False,
blank=False,
on_delete=models.CASCADE,
)
email = models.CharField(
_("Email address"),
@ -334,9 +505,32 @@ class Mailing(models.Model):
)
is_moderated = models.BooleanField(_("is moderated"), default=False)
moderator = models.ForeignKey(
User, related_name="moderated_mailings", verbose_name=_("moderator"), null=True
User,
related_name="moderated_mailings",
verbose_name=_("moderator"),
null=True,
on_delete=models.CASCADE,
)
def __str__(self):
return "%s - %s" % (self.club, self.email_full)
def save(self, *args, **kwargs):
if not self.is_moderated:
unread_notif_subquery = Notification.objects.filter(
user=OuterRef("pk"), type="MAILING_MODERATION", viewed=False
)
for user in User.objects.filter(
~Exists(unread_notif_subquery),
groups__id__in=[settings.SITH_GROUP_COM_ADMIN_ID],
):
Notification(
user=user,
url=reverse("com:mailing_admin"),
type="MAILING_MODERATION",
).save(*args, **kwargs)
super().save(*args, **kwargs)
def clean(self):
if Mailing.objects.filter(email=self.email).exists():
raise ValidationError(_("This mailing list already exists."))
@ -344,21 +538,19 @@ class Mailing(models.Model):
self.is_moderated = True
else:
self.moderator = None
super(Mailing, self).clean()
super().clean()
@property
def email_full(self):
return self.email + "@" + settings.SITH_MAILING_DOMAIN
def can_moderate(self, user):
return user.is_root or user.is_in_group(settings.SITH_GROUP_COM_ADMIN_ID)
return user.is_root or user.is_com_admin
def is_owned_by(self, user):
return (
user.is_in_group(self)
or user.is_root
or user.is_in_group(settings.SITH_GROUP_COM_ADMIN_ID)
)
if user.is_anonymous:
return False
return user.is_root or user.is_com_admin
def can_view(self, user):
return self.club.has_rights_in_club(user)
@ -366,42 +558,17 @@ class Mailing(models.Model):
def can_be_edited_by(self, user):
return self.club.has_rights_in_club(user)
def delete(self):
for sub in self.subscriptions.all():
sub.delete()
super(Mailing, self).delete()
def delete(self, *args, **kwargs):
self.subscriptions.all().delete()
super().delete()
def fetch_format(self):
resp = self.email + ": "
for sub in self.subscriptions.all():
resp += sub.fetch_format()
return resp
def save(self):
if not self.is_moderated:
for user in (
RealGroup.objects.filter(id=settings.SITH_GROUP_COM_ADMIN_ID)
.first()
.users.all()
):
if not user.notifications.filter(
type="MAILING_MODERATION", viewed=False
).exists():
Notification(
user=user,
url=reverse("com:mailing_admin"),
type="MAILING_MODERATION",
).save()
super(Mailing, self).save()
def __str__(self):
return "%s - %s" % (self.club, self.email_full)
destination = "".join(s.fetch_format() for s in self.subscriptions.all())
return f"{self.email}: {destination}"
class MailingSubscription(models.Model):
"""
This class makes the link between user and mailing list
"""
"""Link between user and mailing list."""
mailing = models.ForeignKey(
Mailing,
@ -409,6 +576,7 @@ class MailingSubscription(models.Model):
related_name="subscriptions",
null=False,
blank=False,
on_delete=models.CASCADE,
)
user = models.ForeignKey(
User,
@ -416,12 +584,16 @@ class MailingSubscription(models.Model):
related_name="mailing_subscriptions",
null=True,
blank=True,
on_delete=models.CASCADE,
)
email = models.EmailField(_("Email address"), blank=False, null=False)
class Meta:
unique_together = (("user", "email", "mailing"),)
def __str__(self):
return "(%s) - %s : %s" % (self.mailing, self.get_username, self.email)
def clean(self):
if not self.user and not self.email:
raise ValidationError(_("At least user or email is required"))
@ -436,13 +608,15 @@ class MailingSubscription(models.Model):
)
except ObjectDoesNotExist:
pass
super(MailingSubscription, self).clean()
super().clean()
def is_owned_by(self, user):
if user.is_anonymous:
return False
return (
self.mailing.club.has_rights_in_club(user)
or user.is_root
or self.user.is_in_group(settings.SITH_GROUP_COM_ADMIN_ID)
or self.user.is_com_admin
)
def can_be_edited_by(self, user):
@ -462,6 +636,3 @@ class MailingSubscription(models.Model):
def fetch_format(self):
return self.get_email + " "
def __str__(self):
return "(%s) - %s : %s" % (self.mailing, self.get_username, self.email)

40
club/schemas.py Normal file
View File

@ -0,0 +1,40 @@
from ninja import ModelSchema
from club.models import Club, Membership
from core.schemas import SimpleUserSchema
class SimpleClubSchema(ModelSchema):
class Meta:
model = Club
fields = ["id", "name"]
class ClubProfileSchema(ModelSchema):
"""The infos needed to display a simple club profile."""
class Meta:
model = Club
fields = ["id", "name", "logo"]
url: str
@staticmethod
def resolve_url(obj: Club) -> str:
return obj.get_absolute_url()
class ClubMemberSchema(ModelSchema):
class Meta:
model = Membership
fields = ["start_date", "end_date", "role", "description"]
user: SimpleUserSchema
class ClubSchema(ModelSchema):
class Meta:
model = Club
fields = ["id", "name", "logo", "is_active", "short_description", "address"]
members: list[ClubMemberSchema]

View File

@ -0,0 +1,30 @@
import { AjaxSelect } from "#core:core/components/ajax-select-base";
import { registerComponent } from "#core:utils/web-components";
import type { TomOption } from "tom-select/dist/types/types";
import type { escape_html } from "tom-select/dist/types/utils";
import { type ClubSchema, clubSearchClub } from "#openapi";
@registerComponent("club-ajax-select")
export class ClubAjaxSelect extends AjaxSelect {
protected valueField = "id";
protected labelField = "name";
protected searchField = ["code", "name"];
protected async search(query: string): Promise<TomOption[]> {
const resp = await clubSearchClub({ query: { search: query } });
if (resp.data) {
return resp.data.results;
}
return [];
}
protected renderOption(item: ClubSchema, sanitize: typeof escape_html) {
return `<div class="select-item">
<span class="select-item-text">${sanitize(item.name)}</span>
</div>`;
}
protected renderItem(item: ClubSchema, sanitize: typeof escape_html) {
return `<span>${sanitize(item.name)}</span>`;
}
}

View File

@ -2,16 +2,16 @@
{% from 'core/macros.jinja' import user_profile_link %}
{% block content %}
<div id="club_detail">
{% if club.logo %}
<div class="club_logo"><img src="{{ club.logo.url }}" alt="{{ club.unix_name }}"></div>
{% endif %}
{% if page_revision %}
{{ page_revision|markdown }}
{% else %}
<h3>{% trans %}Club{% endtrans %}</h3>
{% endif %}
</div>
<div id="club_detail">
{% if club.logo %}
<div class="club_logo"><img src="{{ club.logo.url }}" alt="{{ club.name }}"></div>
{% endif %}
{% if page_revision %}
{{ page_revision|markdown }}
{% else %}
<h3>{% trans %}Club{% endtrans %}</h3>
{% endif %}
</div>
{% endblock %}

View File

@ -1,48 +1,48 @@
{% extends "core/base.jinja" %}
{% block title %}
{% trans %}Club list{% endtrans %}
{% trans %}Club list{% endtrans %}
{% endblock %}
{% macro display_club(club) -%}
{% if club.is_active or user.is_root %}
{% if club.is_active or user.is_root %}
<li><a href="{{ url('club:club_view', club_id=club.id) }}">{{ club.name }}</a>
<li><a href="{{ url('club:club_view', club_id=club.id) }}">{{ club.name }}</a>
{% if not club.is_active %}
({% trans %}inactive{% endtrans %})
{% endif %}
{% if not club.is_active %}
({% trans %}inactive{% endtrans %})
{% endif %}
{% if club.president %} - <a href="{{ url('core:user_profile', user_id=club.president.user.id) }}">{{ club.president.user }}</a>{% endif %}
{% if club.short_description %}<p>{{ club.short_description|markdown }}</p>{% endif %}
{% if club.president %} - <a href="{{ url('core:user_profile', user_id=club.president.user.id) }}">{{ club.president.user }}</a>{% endif %}
{% if club.short_description %}<p>{{ club.short_description|markdown }}</p>{% endif %}
{% endif %}
{% endif %}
{%- if club.children.all()|length != 0 %}
<ul>
{%- for c in club.children.order_by('name') %}
{{ display_club(c) }}
{%- endfor %}
</ul>
{%- endif -%}
</li>
{%- if club.children.all()|length != 0 %}
<ul>
{%- for c in club.children.order_by('name') %}
{{ display_club(c) }}
{%- endfor %}
</ul>
{%- endif -%}
</li>
{%- endmacro %}
{% block content %}
{% if user.is_root %}
{% if user.is_root %}
<p><a href="{{ url('club:club_new') }}">{% trans %}New club{% endtrans %}</a></p>
{% endif %}
{% if club_list %}
{% endif %}
{% if club_list %}
<h3>{% trans %}Club list{% endtrans %}</h3>
<ul>
{%- for c in club_list.all().order_by('name') if c.parent is none %}
{%- for c in club_list.all().order_by('name') if c.parent is none %}
{{ display_club(c) }}
{%- endfor %}
{%- endfor %}
</ul>
{% else %}
{% else %}
{% trans %}There is no club in this website.{% endtrans %}
{% endif %}
{% endif %}
{% endblock %}

View File

@ -2,79 +2,81 @@
{% from 'core/macros.jinja' import user_profile_link, select_all_checkbox %}
{% block content %}
<h2>{% trans %}Club members{% endtrans %}</h2>
{% if members %}
<h2>{% trans %}Club members{% endtrans %}</h2>
{% if members %}
<form action="{{ url('club:club_members', club_id=club.id) }}" id="users_old" method="post">
{% csrf_token %}
{% set users_old = dict(form.users_old | groupby("choice_label")) %}
{% if users_old %}
{{ select_all_checkbox("users_old") }}
<p></p>
{% endif %}
<table>
<thead>
<td>{% trans %}User{% endtrans %}</td>
<td>{% trans %}Role{% endtrans %}</td>
<td>{% trans %}Description{% endtrans %}</td>
<td>{% trans %}Since{% endtrans %}</td>
{% if users_old %}
<td>{% trans %}Mark as old{% endtrans %}</td>
{% endif %}
</thead>
<tbody>
{% for m in members %}
<tr>
<td>{{ user_profile_link(m.user) }}</td>
<td>{{ settings.SITH_CLUB_ROLES[m.role] }}</td>
<td>{{ m.description }}</td>
<td>{{ m.start_date }}</td>
{% if users_old %}
<td>
{% set user_old = users_old[m.user.get_display_name()] %}
{% if user_old %}
{{ user_old[0].tag() }}
{% endif %}
</td>
{% endif %}
</tr>
{% endfor %}
</tbody>
</table>
{{ form.users_old.errors }}
{% if users_old %}
<p></p>
<input type="submit" name="submit" value="{% trans %}Mark as old{% endtrans %}">
{% endif %}
{% csrf_token %}
{% set users_old = dict(form.users_old | groupby("choice_label")) %}
{% if users_old %}
{{ select_all_checkbox("users_old") }}
<p></p>
{% endif %}
<table id="club_members_table">
<thead>
<tr>
<td>{% trans %}User{% endtrans %}</td>
<td>{% trans %}Role{% endtrans %}</td>
<td>{% trans %}Description{% endtrans %}</td>
<td>{% trans %}Since{% endtrans %}</td>
{% if users_old %}
<td>{% trans %}Mark as old{% endtrans %}</td>
{% endif %}
</tr>
</thead>
<tbody>
{% for m in members %}
<tr>
<td>{{ user_profile_link(m.user) }}</td>
<td>{{ settings.SITH_CLUB_ROLES[m.role] }}</td>
<td>{{ m.description }}</td>
<td>{{ m.start_date }}</td>
{% if users_old %}
<td>
{% set user_old = users_old[m.user.get_display_name()] %}
{% if user_old %}
{{ user_old[0].tag() }}
{% endif %}
</td>
{% endif %}
</tr>
{% endfor %}
</tbody>
</table>
{{ form.users_old.errors }}
{% if users_old %}
<p></p>
<input type="submit" name="submit" value="{% trans %}Mark as old{% endtrans %}">
{% endif %}
</form>
{% else %}
{% else %}
<p>{% trans %}There are no members in this club.{% endtrans %}</p>
{% endif %}
<form action="{{ url('club:club_members', club_id=club.id) }}" id="add_users" method="post">
{% csrf_token %}
{{ form.non_field_errors() }}
<p>
{{ form.users.errors }}
<label for="{{ form.users.id_for_label }}">{{ form.users.label }} :</label>
{{ form.users }}
<span class="helptext">{{ form.users.help_text }}</span>
</p>
<p>
{{ form.role.errors }}
<label for="{{ form.role.id_for_label }}">{{ form.role.label }} :</label>
{{ form.role }}
</p>
{% if form.start_date %}
<p>
{{ form.start_date.errors }}
<label for="{{ form.start_date.id_for_label }}">{{ form.start_date.label }} :</label>
{{ form.start_date }}
</p>
{% endif %}
<form action="{{ url('club:club_members', club_id=club.id) }}" id="add_users" method="post">
{% csrf_token %}
{{ form.non_field_errors() }}
<p>
{{ form.users.errors }}
<label for="{{ form.users.id_for_label }}">{{ form.users.label }} :</label>
{{ form.users }}
<span class="helptext">{{ form.users.help_text }}</span>
</p>
<p>
{{ form.role.errors }}
<label for="{{ form.role.id_for_label }}">{{ form.role.label }} :</label>
{{ form.role }}
</p>
{% if form.start_date %}
<p>
{{ form.start_date.errors }}
<label for="{{ form.start_date.id_for_label }}">{{ form.start_date.label }} :</label>
{{ form.start_date }}
</p>
{% endif %}
<p>
{{ form.description.errors }}
<label for="{{ form.description.id_for_label }}">{{ form.description.label }} :</label>
{{ form.description }}
</p>
<p><input type="submit" value="{% trans %}Add{% endtrans %}" /></p>
</form>
<p>
{{ form.description.errors }}
<label for="{{ form.description.id_for_label }}">{{ form.description.label }} :</label>
{{ form.description }}
</p>
<p><input type="submit" value="{% trans %}Add{% endtrans %}" /></p>
</form>
{% endblock %}

Some files were not shown because too many files have changed in this diff Show More