9 Commits

Author SHA1 Message Date
f234f94171 Removed unused xapian action 2023-11-05 23:49:45 +01:00
fa758867cc Migrated from xapian to elastic 2023-11-05 23:42:52 +01:00
f41ff281fb Remove eurocks tickets from eboutic (event is finished) 2023-10-10 15:50:35 +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
c436d39014 [PARTENARIAT] Partenariat Eurockéennes (#663) 2023-09-20 17:57:26 +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
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
288764b551 Mise à jour d'avril (#643) 2023-05-10 11:56:33 +02:00
79 changed files with 4544 additions and 900 deletions

View File

@ -6,13 +6,13 @@ runs:
- name: Install apt packages
uses: awalsh128/cache-apt-pkgs-action@latest
with:
packages: gettext libxapian-dev libgraphviz-dev
packages: gettext libgraphviz-dev
version: 1.0 # increment to reset cache
- name: Install dependencies
run: |
sudo apt update
sudo apt install gettext libxapian-dev libgraphviz-dev
sudo apt install gettext libgraphviz-dev
shell: bash
- name: Set up python

View File

@ -1,10 +0,0 @@
name: "Setup xapian"
description: "Setup the xapian indexes"
runs:
using: composite
steps:
- name: Setup xapian index
run: |
mkdir -p /dev/shm/search_indexes
ln -s /dev/shm/search_indexes sith/search_indexes
shell: bash

View File

@ -2,13 +2,10 @@ name: Sith 3 CI
on:
push:
branches:
- master
- taiste
branches: [master, taiste, features/**]
pull_request:
branches:
- master
- taiste
branches: [master, taiste]
workflow_dispatch:
jobs:
black:
@ -24,11 +21,16 @@ jobs:
tests:
name: Run tests and generate coverage report
runs-on: ubuntu-latest
container:
image: docker.elastic.co/elasticsearch/elasticsearch:7.17.14
env:
discovery.type: single-node
ports:
- 9200
steps:
- name: Check out repository
uses: actions/checkout@v3
- uses: ./.github/actions/setup_project
- uses: ./.github/actions/setup_xapian
- uses: ./.github/actions/compile_messages
- name: Run tests
run: poetry run coverage run ./manage.py test

View File

@ -2,7 +2,8 @@ name: Sith3 taiste
on:
push:
branches: [ taiste ]
branches: [taiste]
workflow_dispatch:
jobs:
deployment:

View File

@ -2,7 +2,7 @@
{% from 'core/macros.jinja' import user_profile_link, paginate %}
{% block content %}
<h3>{% trans %}Sellings{% endtrans %}</h3>
<h3>{% trans %}Sales{% endtrans %}</h3>
<form id="form" action="?page=1" method="post">
{% csrf_token %}
{{ form }}

View File

@ -30,7 +30,7 @@
{% endif %}
</ul>
{% if object.club_account.exists() %}
<h4>{% trans %}Accouting: {% endtrans %}</h4>
<h4>{% trans %}Accounting: {% endtrans %}</h4>
<ul>
{% for ca in object.club_account.all() %}
<li><a href="{{ url('accounting:club_details', c_account_id=ca.id) }}">{{ ca.get_display_name() }}</a></li>

View File

@ -39,7 +39,7 @@
<p> <a href="{{ url('com:news_moderate', news_id=news.id) }}">{% trans %}Moderate{% endtrans %}</a></p>
{% endif %}
{% if user.can_edit(news) %}
<p> <a href="{{ url('com:news_edit', news_id=news.id) }}">{% trans %}Edit (will be remoderated){% endtrans %}</a></p>
<p> <a href="{{ url('com:news_edit', news_id=news.id) }}">{% trans %}Edit (will be moderated again){% endtrans %}</a></p>
{% endif %}
</div>
</div>

View File

@ -159,6 +159,33 @@ class NewsTest(TestCase):
self.assertFalse(self.new.is_owned_by(self.anonymous))
self.assertFalse(self.new.is_owned_by(self.sli))
def test_news_viewer(self):
"""
Test that moderated news can be viewed by anyone
and not moderated news only by com admins
"""
# by default a news isn't moderated
self.assertTrue(self.new.can_be_viewed_by(self.com_admin))
self.assertFalse(self.new.can_be_viewed_by(self.sli))
self.assertFalse(self.new.can_be_viewed_by(self.anonymous))
self.assertFalse(self.new.can_be_viewed_by(self.author))
self.new.is_moderated = True
self.new.save()
self.assertTrue(self.new.can_be_viewed_by(self.com_admin))
self.assertTrue(self.new.can_be_viewed_by(self.sli))
self.assertTrue(self.new.can_be_viewed_by(self.anonymous))
self.assertTrue(self.new.can_be_viewed_by(self.author))
def test_news_editor(self):
"""
Test that only com admins can edit news
"""
self.assertTrue(self.new.can_be_edited_by(self.com_admin))
self.assertFalse(self.new.can_be_edited_by(self.sli))
self.assertFalse(self.new.can_be_edited_by(self.anonymous))
self.assertFalse(self.new.can_be_edited_by(self.author))
class WeekmailArticleTest(TestCase):
@classmethod

View File

@ -180,14 +180,15 @@ def get_group(*, pk: int = None, name: str = None) -> Optional[Group]:
:param pk: The primary key of the group
:param name: The name of the group
:return: The group if it exists, else None
:raises ValueError: If no group matches the criteria
:raise ValueError: If no group matches the criteria
"""
if pk is None and name is None:
raise ValueError("Either pk or name must be set")
if name is not None:
name = name.replace(" ", "_") # avoid errors with memcached backend
pk_or_name: Union[str, int] = pk if pk is not None else name
# replace space characters to hide warnings with memcached backend
pk_or_name: Union[str, int] = pk if pk is not None else name.replace(" ", "_")
group = cache.get(f"sith_group_{pk_or_name}")
if group == "not_found":
# Using None as a cache value is a little bit tricky,
# so we use a special string to represent None
@ -809,6 +810,10 @@ class AnonymousUser(AuthAnonymousUser):
def can_edit(self, obj):
return False
@property
def is_com_admin(self):
return False
def can_view(self, obj):
if (
hasattr(obj, "view_groups")

File diff suppressed because one or more lines are too long

Binary file not shown.

File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 434 KiB

File diff suppressed because one or more lines are too long

View File

@ -1,165 +0,0 @@
Fonticons, Inc. (https://fontawesome.com)
--------------------------------------------------------------------------------
Font Awesome Free License
Font Awesome Free is free, open source, and GPL friendly. You can use it for
commercial projects, open source projects, or really almost whatever you want.
Full Font Awesome Free license: https://fontawesome.com/license/free.
--------------------------------------------------------------------------------
# Icons: CC BY 4.0 License (https://creativecommons.org/licenses/by/4.0/)
The Font Awesome Free download is licensed under a Creative Commons
Attribution 4.0 International License and applies to all icons packaged
as SVG and JS file types.
--------------------------------------------------------------------------------
# Fonts: SIL OFL 1.1 License
In the Font Awesome Free download, the SIL OFL license applies to all icons
packaged as web and desktop font files.
Copyright (c) 2023 Fonticons, Inc. (https://fontawesome.com)
with Reserved Font Name: "Font Awesome".
This Font Software is licensed under the SIL Open Font License, Version 1.1.
This license is copied below, and is also available with a FAQ at:
http://scripts.sil.org/OFL
SIL OPEN FONT LICENSE
Version 1.1 - 26 February 2007
PREAMBLE
The goals of the Open Font License (OFL) are to stimulate worldwide
development of collaborative font projects, to support the font creation
efforts of academic and linguistic communities, and to provide a free and
open framework in which fonts may be shared and improved in partnership
with others.
The OFL allows the licensed fonts to be used, studied, modified and
redistributed freely as long as they are not sold by themselves. The
fonts, including any derivative works, can be bundled, embedded,
redistributed and/or sold with any software provided that any reserved
names are not used by derivative works. The fonts and derivatives,
however, cannot be released under any other type of license. The
requirement for fonts to remain under this license does not apply
to any document created using the fonts or their derivatives.
DEFINITIONS
"Font Software" refers to the set of files released by the Copyright
Holder(s) under this license and clearly marked as such. This may
include source files, build scripts and documentation.
"Reserved Font Name" refers to any names specified as such after the
copyright statement(s).
"Original Version" refers to the collection of Font Software components as
distributed by the Copyright Holder(s).
"Modified Version" refers to any derivative made by adding to, deleting,
or substituting — in part or in whole — any of the components of the
Original Version, by changing formats or by porting the Font Software to a
new environment.
"Author" refers to any designer, engineer, programmer, technical
writer or other person who contributed to the Font Software.
PERMISSION & CONDITIONS
Permission is hereby granted, free of charge, to any person obtaining
a copy of the Font Software, to use, study, copy, merge, embed, modify,
redistribute, and sell modified and unmodified copies of the Font
Software, subject to the following conditions:
1) Neither the Font Software nor any of its individual components,
in Original or Modified Versions, may be sold by itself.
2) Original or Modified Versions of the Font Software may be bundled,
redistributed and/or sold with any software, provided that each copy
contains the above copyright notice and this license. These can be
included either as stand-alone text files, human-readable headers or
in the appropriate machine-readable metadata fields within text or
binary files as long as those fields can be easily viewed by the user.
3) No Modified Version of the Font Software may use the Reserved Font
Name(s) unless explicit written permission is granted by the corresponding
Copyright Holder. This restriction only applies to the primary font name as
presented to the users.
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
Software shall not be used to promote, endorse or advertise any
Modified Version, except to acknowledge the contribution(s) of the
Copyright Holder(s) and the Author(s) or with their explicit written
permission.
5) The Font Software, modified or unmodified, in part or in whole,
must be distributed entirely under this license, and must not be
distributed under any other license. The requirement for fonts to
remain under this license does not apply to any document created
using the Font Software.
TERMINATION
This license becomes null and void if any of the above conditions are
not met.
DISCLAIMER
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
OTHER DEALINGS IN THE FONT SOFTWARE.
--------------------------------------------------------------------------------
# Code: MIT License (https://opensource.org/licenses/MIT)
In the Font Awesome Free download, the MIT license applies to all non-font and
non-icon files.
Copyright 2023 Fonticons, Inc.
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.
--------------------------------------------------------------------------------
# Attribution
Attribution is required by MIT, SIL OFL, and CC BY licenses. Downloaded Font
Awesome Free files already contain embedded comments with sufficient
attribution, so you shouldn't need to do anything additional when using these
files normally.
We've kept attribution comments terse, so we ask that you do not actively work
to remove them from files, especially code. They're a great way for folks to
learn about Font Awesome.
--------------------------------------------------------------------------------
# Brand Icons
All brand icons are trademarks of their respective owners. The use of these
trademarks does not indicate endorsement of the trademark holder by Font
Awesome, nor vice versa. **Please do not use brand logos for any purpose except
to represent the company, product, or service to which they refer.**

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1,6 +0,0 @@
/*!
* Font Awesome Free 6.4.0 by @fontawesome - https://fontawesome.com
* License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License)
* Copyright 2023 Fonticons, Inc.
*/
:host,:root{--fa-style-family-classic:"Font Awesome 6 Free";--fa-font-regular:normal 400 1em/1 "Font Awesome 6 Free"}@font-face{font-family:"Font Awesome 6 Free";font-style:normal;font-weight:400;font-display:block;src:url(../webfonts/fa-regular-400.woff2) format("woff2"),url(../webfonts/fa-regular-400.ttf) format("truetype")}.fa-regular,.far{font-weight:400}

View File

@ -1,6 +0,0 @@
/*!
* Font Awesome Free 6.4.0 by @fontawesome - https://fontawesome.com
* License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License)
* Copyright 2023 Fonticons, Inc.
*/
:host,:root{--fa-style-family-classic:"Font Awesome 6 Free";--fa-font-solid:normal 900 1em/1 "Font Awesome 6 Free"}@font-face{font-family:"Font Awesome 6 Free";font-style:normal;font-weight:900;font-display:block;src:url(../webfonts/fa-solid-900.woff2) format("woff2"),url(../webfonts/fa-solid-900.ttf) format("truetype")}.fa-solid,.fas{font-weight:900}

File diff suppressed because one or more lines are too long

View File

@ -1,6 +0,0 @@
/*!
* Font Awesome Free 6.4.0 by @fontawesome - https://fontawesome.com
* License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License)
* Copyright 2023 Fonticons, Inc.
*/
@font-face{font-family:"FontAwesome";font-display:block;src:url(../webfonts/fa-solid-900.woff2) format("woff2"),url(../webfonts/fa-solid-900.ttf) format("truetype")}@font-face{font-family:"FontAwesome";font-display:block;src:url(../webfonts/fa-brands-400.woff2) format("woff2"),url(../webfonts/fa-brands-400.ttf) format("truetype")}@font-face{font-family:"FontAwesome";font-display:block;src:url(../webfonts/fa-regular-400.woff2) format("woff2"),url(../webfonts/fa-regular-400.ttf) format("truetype");unicode-range:u+f003,u+f006,u+f014,u+f016-f017,u+f01a-f01b,u+f01d,u+f022,u+f03e,u+f044,u+f046,u+f05c-f05d,u+f06e,u+f070,u+f087-f088,u+f08a,u+f094,u+f096-f097,u+f09d,u+f0a0,u+f0a2,u+f0a4-f0a7,u+f0c5,u+f0c7,u+f0e5-f0e6,u+f0eb,u+f0f6-f0f8,u+f10c,u+f114-f115,u+f118-f11a,u+f11c-f11d,u+f133,u+f147,u+f14e,u+f150-f152,u+f185-f186,u+f18e,u+f190-f192,u+f196,u+f1c1-f1c9,u+f1d9,u+f1db,u+f1e3,u+f1ea,u+f1f7,u+f1f9,u+f20a,u+f247-f248,u+f24a,u+f24d,u+f255-f25b,u+f25d,u+f271-f274,u+f278,u+f27b,u+f28c,u+f28e,u+f29c,u+f2b5,u+f2b7,u+f2ba,u+f2bc,u+f2be,u+f2c0-f2c1,u+f2c3,u+f2d0,u+f2d2,u+f2d4,u+f2dc}@font-face{font-family:"FontAwesome";font-display:block;src:url(../webfonts/fa-v4compatibility.woff2) format("woff2"),url(../webfonts/fa-v4compatibility.ttf) format("truetype");unicode-range:u+f041,u+f047,u+f065-f066,u+f07d-f07e,u+f080,u+f08b,u+f08e,u+f090,u+f09a,u+f0ac,u+f0ae,u+f0b2,u+f0d0,u+f0d6,u+f0e4,u+f0ec,u+f10a-f10b,u+f123,u+f13e,u+f148-f149,u+f14c,u+f156,u+f15e,u+f160-f161,u+f163,u+f175-f178,u+f195,u+f1f8,u+f219,u+f27a}

File diff suppressed because one or more lines are too long

View File

@ -1,6 +0,0 @@
/*!
* Font Awesome Free 6.4.0 by @fontawesome - https://fontawesome.com
* License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License)
* Copyright 2023 Fonticons, Inc.
*/
@font-face{font-family:"Font Awesome 5 Brands";font-display:block;font-weight:400;src:url(../webfonts/fa-brands-400.woff2) format("woff2"),url(../webfonts/fa-brands-400.ttf) format("truetype")}@font-face{font-family:"Font Awesome 5 Free";font-display:block;font-weight:900;src:url(../webfonts/fa-solid-900.woff2) format("woff2"),url(../webfonts/fa-solid-900.ttf) format("truetype")}@font-face{font-family:"Font Awesome 5 Free";font-display:block;font-weight:400;src:url(../webfonts/fa-regular-400.woff2) format("woff2"),url(../webfonts/fa-regular-400.ttf) format("truetype")}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -191,42 +191,96 @@ $hovered-red-text-color: #ff4d4d;
>.right {
flex: 1;
justify-content: flex-end;
gap: 10px;
>.links {
>.user {
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
gap: 15px;
@media (max-width: 1200px) {
width: 100%;
flex-direction: row-reverse;
justify-content: flex-end;
}
> a {
display: block;
min-width: 40px;
height: 40px;
border-radius: 50%;
background-position: center center;
background-size: cover;
background-repeat: no-repeat;
background-color: $background-color;
}
>.options {
display: flex;
flex-direction: column;
gap: 5px;
>.username {
display: flex;
justify-content: flex-end;
align-items: center;
height: 40px;
gap: 10px;
gap: 5px;
@media (max-width: 500px) {
justify-content: flex-start;
}
>a {
color: $text-color;
&:hover {
color: $hovered-text-color;
}
}
}
>.links {
width: 100%;
display: flex;
justify-content: flex-end;
gap: 15px;
@media (max-width: 1200px) {
justify-content: flex-start;
}
@media (max-width: 375px) {
height: 30px;
>a {
text-align: right;
color: $text-color;
&:hover {
color: $hovered-text-color;
}
> * {
display: flex;
justify-content: center;
align-items: center;
width: 30px;
height: 30px;
font-size: 20px;
&:last-child {
color: $red-text-color;
> * {
font-size: 20px;
&:hover {
color: $hovered-red-text-color;
}
}
}
}
}
}
>.notification {
height: 100%;
width: 55px;
display: flex;
justify-content: center;
align-items: center;
position: relative;
>a {
color: $text-color;
position: relative;
font-size: 25px;
&:hover {
color: $hovered-text-color;
@ -317,69 +371,6 @@ $hovered-red-text-color: #ff4d4d;
}
}
}
>a {
text-align: right;
color: $text-color;
&:hover {
color: $hovered-text-color;
}
&:last-child {
color: $red-text-color;
&:hover {
color: $hovered-red-text-color;
}
}
}
}
>.user {
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
gap: 10px;
@media (max-width: 1200px) {
width: 100%;
flex-direction: row-reverse;
justify-content: flex-end;
}
> a {
display: block;
min-width: 40px;
height: 40px;
border-radius: 50%;
background-position: center center;
background-size: cover;
background-repeat: no-repeat;
background-color: $background-color;
}
>.username {
display: flex;
justify-content: flex-end;
gap: 5px;
@media (max-width: 500px) {
justify-content: flex-start;
}
>a {
color: $text-color;
&:hover {
color: $hovered-text-color;
}
}
}
}
}
>.left {

View File

@ -1031,7 +1031,7 @@ thead {
}
tbody > tr {
&:nth-child(even) {
&:nth-child(even):not(.highlight) {
background: $primary-neutral-light-color;
}
&.clickable:hover {

View File

@ -16,10 +16,9 @@
{# Thile file is quite heavy (around 250kb), so declaring it in a block allows easy removal #}
<link rel="stylesheet" href="{{ static('core/js/ui/jquery-ui.min.css') }}">
{% endblock %}
<!-- Font Awesome 6 -->
<link rel="preload" as="style" href="{{ static('core/fontawesome/css/all.min.css') }}" onload="this.onload=null;this.rel='stylesheet'">
<noscript><link rel="stylesheet" href="{{ static('core/fontawesome/css/all.min.css') }}"></noscript>
<script defer href="{{ static('core/fontawesome/js/all.min.js') }}"></script>
<link rel="preload" as="style" href="{{ static('core/font-awesome/css/font-awesome.min.css') }}" onload="this.onload=null;this.rel='stylesheet'">
<noscript><link rel="stylesheet" href="{{ static('core/font-awesome/css/font-awesome.min.css') }}"></noscript>
<script defer href="{{ static('core/font-awesome/js/fontawesone.min.js') }}"></script>
<!-- Jquery declared here to be accessible in every django widgets -->
<script src="{{ static('core/js/jquery-3.6.2.min.js') }}"></script>
@ -73,11 +72,11 @@
<a href="{{ url('counter:activity', counter_id=bar.id) }}">
{% endif %}
{% if bar.is_inactive() %}
<i class="fa-solid fa-question" style="color: #f39c12"></i>
<i class="fa fa-question" style="color: #f39c12"></i>
{% elif bar.is_open(): %}
<i class="fa-solid fa-check" style="color: #2ecc71"></i>
<i class="fa fa-check" style="color: #2ecc71"></i>
{% else %}
<i class="fa-solid fa-xmark" style="color: #eb2f06"></i>
<i class="fa fa-times" style="color: #eb2f06"></i>
{% endif %}
<span>{{ bar }}</span>
</a>
@ -88,9 +87,15 @@
</div>
<div class="right">
<div class="user">
<div class="options">
<div class="username">
<a href="{{ url('core:user_profile', user_id=user.id) }}">{{ user.get_display_name() }}</a>
</div>
<div class="links">
<a href="{{ url('core:user_tools') }}">{% trans %}Tools{% endtrans %}</a>
<a href="{{ url('core:logout') }}">{% trans %}Logout{% endtrans %}</a>
</div>
</div>
<a
href="{{ url('core:user_profile', user_id=user.id) }}"
{% if user.profile_pict %}
@ -100,16 +105,19 @@
{% endif %}
></a>
</div>
<div class="links">
<div class="notification">
<a href="#" onclick="display_notif()">
<i class="fa-solid fa-bell"></i>
<i class="fa fa-bell-o"></i>
{% set notification_count = user.notifications.filter(viewed=False).count() %}
{% if notification_count > 0 %}
<span>
{% if notification_count < 100 %} {{ notification_count }} {% else %} &nbsp; {% endif %} </span>
{% if notification_count < 100 %}
{{ notification_count }}
{% else %}
&nbsp;
{% endif %}
</span>
{% endif %}
</a>
<div id="header_notif">
@ -117,7 +125,7 @@
{% if user.notifications.filter(viewed=False).count() > 0 %}
{% for n in user.notifications.filter(viewed=False).order_by('-date') %}
<li>
<a href="{{ url('core:notification', notif_id=n.id) }}">
<a href="{{ url("core:notification", notif_id=n.id) }}">
<div class="datetime">
<span class="header_notif_date">
{{ n.date|localtime|date(DATE_FORMAT) }}
@ -146,9 +154,6 @@
</div>
</div>
</div>
<a href="{{ url('core:user_tools') }}"><i class="fa-solid fa-wrench"></i></a>
<a href="{{ url('core:logout') }}"><i class="fa-solid fa-right-from-bracket"></i></a>
</div>
</div>
</div>
{% endif %}
@ -190,7 +195,7 @@
{% block nav %}
{% if not popup %}
<nav class="navbar">
<button class="expand-button" onclick="showMenu()"><i class="fa-solid fa-bars"></i></button>
<button class="expand-button" onclick="showMenu()"><i class="fa fa-bars"></i></button>
<div id="navbar-content" class="content" style="display: none;">
<a class="link" href="{{ url('core:index') }}">{% trans %}Main{% endtrans %}</a>
<div class="menu">
@ -198,11 +203,7 @@
<ul class="content">
<li><a href="{{ url('core:page', page_name='ae') }}">{% trans %}AE{% endtrans %}</a></li>
<li><a href="{{ url('core:page', page_name='clubs') }}">{% trans %}AE's clubs{% endtrans %}</a></li>
<li><a href="{{ url('core:page', page_name='bdf') }}">{% trans %}BdF{% endtrans %}</a></li>
<li><a href="{{ url('core:page', page_name='bds') }}">{% trans %}BDS{% endtrans %}</a></li>
<li><a href="{{ url('core:page', page_name='cetu') }}">{% trans %}CETU{% endtrans %}</a></li>
<li><a href="{{ url('core:page', page_name='clubs/doceo') }}">{% trans %}Doceo{% endtrans %}</a></li>
<li><a href="{{ url('core:page', page_name='positions') }}">{% trans %}Positions{% endtrans %}</a></li>
<li><a href="{{ url('core:page', page_name='utbm-associations') }}">{% trans %}Others UTBM's Associations{% endtrans %}</a></li>
</ul>
</div>
<div class="menu">

View File

@ -4,9 +4,9 @@
{% block file %}
<h3>
{% if file.is_folder %}
<i class="fa-solid fa-folder-open"></i>
<i class="fa fa-folder fa-3x" aria-hidden="true"></i>
{% else %}
<i class="fa-solid fa-file"></i>
<i class="fa fa-file fa-3x" aria-hidden="true"></i>
{% endif %}
{{ file.get_display_name() }}
</h3>
@ -41,9 +41,9 @@
<li style="list-style-type: none;">
<input type="checkbox" name="file_list" value="{{ f.id }}">
{% if f.is_folder %}
<i class="fa-solid fa-folder"></i>
<i class="fa fa-folder" aria-hidden="true"></i>
{% else %}
<i class="fa-solid fa-file"></i>
<i class="fa fa-file" aria-hidden="true"></i>
{% endif %}
<a href="{{ url('core:file_detail', file_id=f.id, popup=popup) }}">{{ f.get_display_name() }}</a></li>
{% endfor %}

View File

@ -8,9 +8,9 @@
{% for f in file_list %}
<li style="list-style-type: none;">
{% if f.is_folder %}
<i class="fa-solid fa-folder"></i>
<i class="fa fa-folder" aria-hidden="true"></i>
{% else %}
<i class="fa-solid fa-file"></i>
<i class="fa fa-file" aria-hidden="true"></i>
{% endif %}
<a href="{{ url('core:file_detail', file_id=f.id, popup=popup) }}">{{ f.name }}</a></li>
{% endfor %}

View File

@ -40,11 +40,11 @@
{%- endmacro %}
{% macro fb_quick(news) -%}
<a rel="nofollow" target="#" href="https://www.facebook.com/sharer/sharer.php?u={{ news.get_full_url() }}" class="fb fa-brands fa-facebook fa-2x"></a>
<a rel="nofollow" target="#" href="https://www.facebook.com/sharer/sharer.php?u={{ news.get_full_url() }}" class="fb fa fa-facebook-square fa-2x"></a>
{%- endmacro %}
{% macro tweet_quick(news) -%}
<a rel="nofollow" target="#" href="https://twitter.com/intent/tweet?text={{ news.get_full_url() }}" class="twitter fa-brands fa-twitter fa-2x"></a>
<a rel="nofollow" target="#" href="https://twitter.com/intent/tweet?text={{ news.get_full_url() }}" class="twitter fa fa-twitter-square fa-2x"></a>
{%- endmacro %}
{% macro user_mini_profile(user) %}

View File

@ -40,25 +40,25 @@
{
name: "heading-smaller",
action: EasyMDE.toggleHeadingSmaller,
className: "fa-solid fa-heading",
className: "fa fa-header",
title: "{{ translations.heading_smaller }}"
},
{
name: "italic",
action: EasyMDE.toggleItalic,
className: "fa-solid fa-italic",
className: "fa fa-italic",
title: "{{ translations.italic }}"
},
{
name: "bold",
action: EasyMDE.toggleBold,
className: "fa-solid fa-bold",
className: "fa fa-bold",
title: "{{ translations.bold }}"
},
{
name: "strikethrough",
action: EasyMDE.toggleStrikethrough,
className: "fa-solid fa-strikethrough",
className: "fa fa-strikethrough",
title: "{{ translations.strikethrough }}"
},
{
@ -67,7 +67,7 @@
let cm = editor.codemirror;
cm.replaceSelection('__' + cm.getSelection() + '__');
},
className: "fa-solid fa-underline",
className: "fa fa-underline",
title: "{{ translations.underline }}"
},
{
@ -76,7 +76,7 @@
let cm = editor.codemirror;
cm.replaceSelection('<sup>' + cm.getSelection() + '</sup>');
},
className: "fa-solid fa-superscript",
className: "fa fa-superscript",
title: "{{ translations.superscript }}"
},
{
@ -85,84 +85,84 @@
let cm = editor.codemirror;
cm.replaceSelection('<sub>' + cm.getSelection() + '</sub>');
},
className: "fa-solid fa-subscript",
className: "fa fa-subscript",
title: "{{ translations.subscript }}"
},
{
name: "code",
action: EasyMDE.toggleCodeBlock,
className: "fa-solid fa-code",
className: "fa fa-code",
title: "{{ translations.code }}"
},
"|",
{
name: "quote",
action: EasyMDE.toggleBlockquote,
className: "fa-solid fa-quote-left",
className: "fa fa-quote-left",
title: "{{ translations.quote }}"
},
{
name: "unordered-list",
action: EasyMDE.toggleUnorderedList,
className: "fa-solid fa-list-ul",
className: "fa fa-list-ul",
title: "{{ translations.unordered_list }}"
},
{
name: "ordered-list",
action: EasyMDE.toggleOrderedList,
className: "fa-solid fa-list-ol",
className: "fa fa-list-ol",
title: "{{ translations.ordered_list }}"
},
"|",
{
name: "link",
action: EasyMDE.drawLink,
className: "fa-solid fa-link",
className: "fa fa-link",
title: "{{ translations.link }}"
},
{
name: "image",
action: EasyMDE.drawImage,
className: "fa-solid fa-image",
className: "fa fa-picture-o",
title: "{{ translations.image }}"
},
{
name: "table",
action: EasyMDE.drawTable,
className: "fa-solid fa-table",
className: "fa fa-table",
title: "{{ translations.table }}"
},
"|",
{
name: "clean-block",
action: EasyMDE.cleanBlock,
className: "fa-solid fa-eraser",
className: "fa fa-eraser fa-clean-block",
title: "{{ translations.clean_block }}"
},
"|",
{
name: "preview",
action: EasyMDE.togglePreview,
className: "fa-solid fa-eye no-disable",
className: "fa fa-eye no-disable",
title: "{{ translations.preview }}"
},
{
name: "side-by-side",
action: EasyMDE.toggleSideBySide,
className: "fa-solid fa-columns no-disable no-mobile",
className: "fa fa-columns no-disable no-mobile",
title: "{{ translations.side_by_side }}"
},
{
name: "fullscreen",
action: EasyMDE.toggleFullScreen,
className: "fa-solid fa-arrows-alt no-disable no-mobile",
className: "fa fa-arrows-alt no-disable no-mobile",
title: "{{ translations.fullscreen }}"
},
"|",
{
name: "guide",
action: "/page/Aide_sur_la_syntaxe",
className: "fa-solid fa-question-circle",
className: "fa fa-question-circle",
title: "{{ translations.guide }}"
},
]

View File

@ -17,7 +17,7 @@
{% if user_registered %}
{% trans user_name=user_registered.get_display_name() %}Welcome {{ user_name }}!{% endtrans %}<br>
{% trans %}You successfully registred and you will soon receive a confirmation mail.{% endtrans %}<br>
{% trans %}You successfully registered and you will soon receive a confirmation mail.{% endtrans %}<br>
{% trans username=user_registered.username %}Your username is {{ username }}.{% endtrans %}<br>
{% else %}

View File

@ -41,11 +41,11 @@
{% set refilled = customer.refillings.exists() %}
{% if bought or refilled %}
{% if bought %}
<h5>{% trans %}Account buyings{% endtrans %}</h5>
<h5>{% trans %}Account purchases{% endtrans %}</h5>
{{ monthly(buyings_month) }}
{% endif %}
{% if refilled %}
<h5>{% trans %}Refillings{% endtrans %}</h5>
<h5>{% trans %}Reloads{% endtrans %}</h5>
{{ monthly(refilling_month) }}
{% endif %}
{% endif %}

View File

@ -10,7 +10,7 @@
<p>{% trans %}Amount: {% endtrans %}{{ customer.amount }} €</p>
<p><a href="{{ url('core:user_account', user_id=profile.id) }}">{% trans %}Back{% endtrans %}</a></p>
{% if customer.buyings.exists() %}
<h4>{% trans %}Account buyings{% endtrans %}</h4>
<h4>{% trans %}Account purchases{% endtrans %}</h4>
<table>
<thead>
<tr>
@ -43,7 +43,7 @@
</table>
{% endif %}
{% if customer.refillings.exists() %}
<h4>{% trans %}Refillings{% endtrans %}</h4>
<h4>{% trans %}Reloads{% endtrans %}</h4>
<table>
<thead>
<tr>

View File

@ -165,8 +165,8 @@
<a href="{{ url('subscription:subscription') }}?member={{ profile.id }}">{% trans %}New subscription{% endtrans
%}</a>
{% endif %}
</div>
{% endif %}
</div>
{% endif %}
<br>
{% if profile.was_subscribed and (user == profile or user.can_read_subscription_history)%}
@ -176,7 +176,7 @@
{% trans %}Subscription history{% endtrans %}
</span>
<span class="collapse-header-icon" :class="{'reverse': collapsed}">
<i class="fa-sharp fa-solid fa-caret-down"></i>
<i class="fa fa-caret-down"></i>
</span>
</div>
<div class="collapse-body" x-show="collapsed" x-transition.scale.origin.top>
@ -201,6 +201,7 @@
</div>
</div>
{% endif %}
<hr>
<div>
{% if user.is_root or user.is_board_member %}
@ -213,36 +214,25 @@
{% if profile.gifts.exists() %}
{% set gifts = profile.gifts.order_by("-date")|list %}
<br>
<div class="collapse" :class="{'shadow': collapsed}" x-data="{collapsed: false}" x-cloak>
<div class="collapse-header clickable" @click="collapsed = !collapsed">
<span class="collapse-header-text">
{% trans %}Last given gift :{% endtrans %} {{ gifts[0] }}
</span>
<span class="collapse-header-icon" :class="{'reverse': collapsed}">
<i class="fa-solid fa-caret-down"></i>
<i class="fa fa-caret-down"></i>
</span>
</div>
<div class="collapse-body" x-show="collapsed" x-transition.scale.origin.top>
<table>
<thead>
<tr>
<th>{% trans %}Subscription start{% endtrans %}</th>
<th>{% trans %}Subscription end{% endtrans %}</th>
<th>{% trans %}Subscription type{% endtrans %}</th>
<th>{% trans %}Payment method{% endtrans %}</th>
</tr>
</thead>
{% for sub in profile.subscriptions.all() %}
<tr>
<td>{{ sub.subscription_start }}</td>
<td>{{ sub.subscription_end }}</td>
<td>{{ sub.subscription_type }}</td>
<td>{{ sub.get_payment_method_display() }}</td>
</tr>
<ul>
{% for gift in gifts %}
<li>{{ gift }}
<a href="{{ url('core:user_gift_delete', user_id=profile.id, gift_id=gift.id) }}">
<i class="fa fa-trash"></i>
</a>
</li>
{% endfor %}
</table>
</div>
</ul>
</div>
{% else %}
<em>{% trans %}No gift given yet{% endtrans %}</em>

View File

@ -15,17 +15,18 @@
#
import os
from datetime import timedelta
from datetime import date, timedelta
import freezegun
from django.core.cache import cache
from django.test import Client, TestCase
from django.urls import reverse
from django.core.management import call_command
from django.utils.timezone import now
from club.models import Membership
from core.models import User, Group, Page, AnonymousUser
from core.markdown import markdown
from core.models import AnonymousUser, Group, Page, User
from core.utils import get_semester_code, get_start_of_semester
from sith import settings
"""
@ -597,15 +598,95 @@ class UserIsInGroupTest(TestCase):
Test that when a user is removed from a group,
the is_in_group_method return False when calling it again
"""
# testing with pk
self.toto.groups.add(self.com_admin.pk)
self.assertTrue(self.toto.is_in_group(pk=self.com_admin.pk))
self.toto.groups.remove(self.com_admin.pk)
self.assertFalse(self.toto.is_in_group(pk=self.com_admin.pk))
# testing with name
self.toto.groups.add(self.sas_admin.pk)
self.assertTrue(self.toto.is_in_group(name="SAS admin"))
self.toto.groups.remove(self.sas_admin.pk)
self.assertFalse(self.toto.is_in_group(name="SAS admin"))
def test_not_existing_group(self):
"""
Test that searching for a not existing group
returns False
"""
self.assertFalse(self.skia.is_in_group(name="This doesn't exist"))
class DateUtilsTest(TestCase):
@classmethod
def setUpTestData(cls):
cls.autumn_month = settings.SITH_SEMESTER_START_AUTUMN[0]
cls.autumn_day = settings.SITH_SEMESTER_START_AUTUMN[1]
cls.spring_month = settings.SITH_SEMESTER_START_SPRING[0]
cls.spring_day = settings.SITH_SEMESTER_START_SPRING[1]
cls.autumn_semester_january = date(2025, 1, 4)
cls.autumn_semester_september = date(2024, 9, 4)
cls.autumn_first_day = date(2024, cls.autumn_month, cls.autumn_day)
cls.spring_semester_march = date(2023, 3, 4)
cls.spring_first_day = date(2023, cls.spring_month, cls.spring_day)
def test_get_semester(self):
"""
Test that the get_semester function returns the correct semester string
"""
self.assertEqual(get_semester_code(self.autumn_semester_january), "A24")
self.assertEqual(get_semester_code(self.autumn_semester_september), "A24")
self.assertEqual(get_semester_code(self.autumn_first_day), "A24")
self.assertEqual(get_semester_code(self.spring_semester_march), "P23")
self.assertEqual(get_semester_code(self.spring_first_day), "P23")
def test_get_start_of_semester_fixed_date(self):
"""
Test that the get_start_of_semester correctly the starting date of the semester.
"""
automn_2024 = date(2024, self.autumn_month, self.autumn_day)
self.assertEqual(
get_start_of_semester(self.autumn_semester_january), automn_2024
)
self.assertEqual(
get_start_of_semester(self.autumn_semester_september), automn_2024
)
self.assertEqual(get_start_of_semester(self.autumn_first_day), automn_2024)
spring_2023 = date(2023, self.spring_month, self.spring_day)
self.assertEqual(get_start_of_semester(self.spring_semester_march), spring_2023)
self.assertEqual(get_start_of_semester(self.spring_first_day), spring_2023)
def test_get_start_of_semester_today(self):
"""
Test that the get_start_of_semester returns the start of the current semester
when no date is given
"""
with freezegun.freeze_time(self.autumn_semester_september):
self.assertEqual(get_start_of_semester(), self.autumn_first_day)
with freezegun.freeze_time(self.spring_semester_march):
self.assertEqual(get_start_of_semester(), self.spring_first_day)
def test_get_start_of_semester_changing_date(self):
"""
Test that the get_start_of_semester correctly gives the starting date of the semester,
even when the semester changes while the server isn't restarted.
"""
spring_2023 = date(2023, self.spring_month, self.spring_day)
autumn_2023 = date(2023, self.autumn_month, self.autumn_day)
mid_spring = spring_2023 + timedelta(days=45)
mid_autumn = autumn_2023 + timedelta(days=45)
with freezegun.freeze_time(mid_spring) as frozen_time:
self.assertEqual(get_start_of_semester(), spring_2023)
# forward time to the middle of the next semester
frozen_time.move_to(mid_autumn)
self.assertEqual(get_start_of_semester(), autumn_2023)

View File

@ -15,20 +15,19 @@
#
import os
import subprocess
import re
# Image utils
from io import BytesIO
import subprocess
from datetime import date
from PIL import ExifTags
# Image utils
from io import BytesIO
from typing import Optional
import PIL
from django.conf import settings
from django.core.files.base import ContentFile
from PIL import ExifTags
from django.utils import timezone
def get_git_revision_short_hash() -> str:
@ -44,34 +43,54 @@ def get_git_revision_short_hash() -> str:
return ""
def get_start_of_semester(d=date.today()):
def get_start_of_semester(today: Optional[date] = None) -> date:
"""
This function computes the start date of the semester with respect to the given date (default is today),
and the start date given in settings.SITH_START_DATE.
It takes the nearest past start date.
Exemples: with SITH_START_DATE = (8, 15)
Today -> Start date
2015-03-17 -> 2015-02-15
2015-01-11 -> 2014-08-15
Return the date of the start of the semester of the given date.
If no date is given, return the start date of the current semester.
The current semester is computed as follows:
- If the date is between 15/08 and 31/12 => Autumn semester.
- If the date is between 01/01 and 15/02 => Autumn semester of the previous year.
- If the date is between 15/02 and 15/08 => Spring semester
:param today: the date to use to compute the semester. If None, use today's date.
:return: the date of the start of the semester
"""
today = d
year = today.year
start = date(year, settings.SITH_START_DATE[0], settings.SITH_START_DATE[1])
start2 = start.replace(month=(start.month + 6) % 12)
spring, autumn = min(start, start2), max(start, start2)
if today > autumn: # autumn semester
if today is None:
today = timezone.now().date()
autumn = date(today.year, *settings.SITH_SEMESTER_START_AUTUMN)
spring = date(today.year, *settings.SITH_SEMESTER_START_SPRING)
if today >= autumn: # between 15/08 (included) and 31/12 -> autumn semester
return autumn
if today > spring: # spring semester
if today >= spring: # between 15/02 (included) and 15/08 -> spring semester
return spring
return autumn.replace(year=year - 1) # autumn semester of last year
# between 01/01 and 15/02 -> autumn semester of the previous year
return autumn.replace(year=autumn.year - 1)
def get_semester(d=date.today()):
def get_semester_code(d: Optional[date] = None) -> str:
"""
Return the semester code of the given date.
If no date is given, return the semester code of the current semester.
The semester code is an upper letter (A for autumn, P for spring),
followed by the last two digits of the year.
For example, the autumn semester of 2018 is "A18".
:param d: the date to use to compute the semester. If None, use today's date.
:return: the semester code corresponding to the given date
"""
if d is None:
d = timezone.now().date()
start = get_start_of_semester(d)
if start.month <= 6:
return "P" + str(start.year)[-2:]
else:
if (start.month, start.day) == settings.SITH_SEMESTER_START_AUTUMN:
return "A" + str(start.year)[-2:]
return "P" + str(start.year)[-2:]
def file_exist(path):

View File

@ -15,7 +15,7 @@
#
from __future__ import annotations
from typing import Tuple
from typing import Tuple, Optional
from django.db import models
from django.db.models import F, Value, Sum, QuerySet, OuterRef, Exists
@ -536,7 +536,7 @@ class Counter(models.Model):
.order_by("-perm_sum")
)
def get_top_customers(self, since=get_start_of_semester()) -> QuerySet:
def get_top_customers(self, since: Optional[date] = None) -> QuerySet:
"""
Return a QuerySet querying the money spent by customers of this counter
since the specified date, ordered by descending amount of money spent.
@ -546,6 +546,8 @@ class Counter(models.Model):
- the nickname of the customer
- the amount of money spent by the customer
"""
if since is None:
since = get_start_of_semester()
return (
self.sellings.filter(date__gte=since)
.annotate(
@ -557,7 +559,8 @@ class Counter(models.Model):
)
.annotate(nickname=F("customer__user__nick_name"))
.annotate(promo=F("customer__user__promo"))
.values("customer__user", "name", "nickname")
.annotate(user=F("customer__user"))
.values("user", "promo", "name", "nickname")
.annotate(
selling_sum=Sum(
F("unit_price") * F("quantity"), output_field=CurrencyField()
@ -567,15 +570,17 @@ class Counter(models.Model):
.order_by("-selling_sum")
)
def get_total_sales(self, since=get_start_of_semester()) -> CurrencyField:
def get_total_sales(self, since=None) -> CurrencyField:
"""
Compute and return the total turnover of this counter
since the date specified in parameter (by default, since the start of the current
semester)
:param since: timestamp from which to perform the calculation
:type since: datetime | date
:type since: datetime | date | None
:return: Total revenue earned at this counter
"""
if since is None:
since = get_start_of_semester()
if isinstance(since, date):
since = datetime.combine(since, datetime.min.time())
total = self.sellings.filter(date__gte=since).aggregate(

View File

@ -28,15 +28,15 @@
<h5>{% trans %}Legend{% endtrans %}</h5>
<div class="activity-description">
<div>
<i class="fa-solid fa-check" style="color: #2ecc71"></i>
<i class="fa fa-check" style="color: #2ecc71"></i>
<span>{% trans %}counter is open, there's at least one barman connected{% endtrans %}</span>
</div>
<div>
<i class="fa-solid fa-question" style="color: #f39c12"></i>
<i class="fa fa-question" style="color: #f39c12"></i>
<span>{% trans minutes=settings.SITH_COUNTER_MINUTE_INACTIVE %}counter is open but not active, the last sale was done at least {{ minutes }} minutes ago {% endtrans %}</span>
</div>
<div>
<i class="fa-solid fa-xmark" style="color: #eb2f06"></i>
<i class="fa fa-times" style="color: #eb2f06"></i>
<span>{% trans %}counter is not open : no one is connected{% endtrans %}</span>
</div>
</div>

View File

@ -14,7 +14,7 @@
{{ form }}
<p><input type="submit" value="{% trans %}Show{% endtrans %}" /></p>
</form>
<h6>{% trans %}Refillings{% endtrans %}</h6>
<h6>{% trans %}Reloads{% endtrans %}</h6>
<p>
{% for b,s in refilling_sums.items() %}
{{ b }}: {{ s }} €<br/>

View File

@ -19,7 +19,7 @@
{% endif %}
{% if user.is_owner(c) %}
<a href="{{ url('counter:prop_admin', counter_id=c.id) }}">{% trans %}Props{% endtrans %}</a> -
<a href="{{ url('counter:refilling_list', counter_id=c.id) }}">{% trans %}Refillings list{% endtrans %}</a>
<a href="{{ url('counter:refilling_list', counter_id=c.id) }}">{% trans %}Reloads list{% endtrans %}</a>
{% endif %}
</li>
{% endfor %}
@ -41,7 +41,7 @@
{% endif %}
{% if user.is_owner(c) %}
<a href="{{ url('counter:prop_admin', counter_id=c.id) }}">{% trans %}Props{% endtrans %}</a> -
<a href="{{ url('counter:refilling_list', counter_id=c.id) }}">{% trans %}Refillings list{% endtrans %}</a>
<a href="{{ url('counter:refilling_list', counter_id=c.id) }}">{% trans %}Reloads list{% endtrans %}</a>
{% endif %}
</li>
{% endfor %}

View File

@ -21,7 +21,7 @@
{% block content %}
<h3>{% trans counter_name=counter %}{{ counter_name }} counter{% endtrans %}</h3>
<div>
<h3>{% trans %}Sellings{% endtrans %}</h3>
<h3>{% trans %}Sales{% endtrans %}</h3>
{% if last_basket %}
<h4>{% trans %}Last selling: {% endtrans %}</h4>
<p>{% trans %}Client: {% endtrans %}{{ last_customer }} - {% trans %}New amount: {% endtrans %}{{ new_customer_amount }} €.</p>

View File

@ -13,7 +13,7 @@
{% block content %}
<h3>{% trans counter_name=counter %}{{ counter_name }} last operations{% endtrans %}</h3>
<h4>{% trans %}Refillings{% endtrans %}</h4>
<h4>{% trans %}Reloads{% endtrans %}</h4>
<table>
<thead>
<tr>
@ -38,7 +38,7 @@
</tbody>
</table>
<h4>{% trans %}Sellings{% endtrans %}</h4>
<h4>{% trans %}Sales{% endtrans %}</h4>
<table>
<thead>
<tr>

View File

@ -2,7 +2,7 @@
{% from 'core/macros.jinja' import paginate %}
{% block title %}
{%- trans %}Refillings list{% endtrans %} -- {{ counter.name }}
{%- trans %}Reloads list{% endtrans %} -- {{ counter.name }}
{% endblock %}
{% block content %}

View File

@ -11,7 +11,9 @@
{% block content %}
<h3>{% trans counter_name=counter %}{{ counter_name }} stats{% endtrans %}</h3>
<h4>{% trans counter_name=counter.name %}Top 100 {{ counter_name }}{% endtrans %}</h4>
<h4>
{% trans counter_name=counter.name %}Top 100 {{ counter_name }}{% endtrans %} ({{ current_semester }})
</h4>
<table>
<thead>
<tr>
@ -35,7 +37,9 @@
</tbody>
</table>
<h4>{% trans counter_name=counter.name %}Top 100 barman {{ counter_name }}{% endtrans %}</h4>
<h4>
{% trans counter_name=counter.name %}Top 100 barman {{ counter_name }}{% endtrans %} ({{ current_semester }})
</h4>
<table>
<thead>
<tr>

View File

@ -13,6 +13,7 @@
# OR WITHIN THE LOCAL FILE "LICENSE"
#
#
from datetime import date, timedelta
import json
import re
import string
@ -322,42 +323,49 @@ class CounterStatsTest(TestCase):
Test the result of Counter.get_top_customers() is correct
"""
top = iter(self.counter.get_top_customers())
self.assertEqual(
next(top),
expected_results = [
{
"customer__user": self.sli.id,
"user": self.sli.id,
"name": f"{self.sli.first_name} {self.sli.last_name}",
"promo": self.sli.promo,
"nickname": self.sli.nick_name,
"selling_sum": 2000,
},
)
self.assertEqual(
next(top),
{
"customer__user": self.skia.id,
"user": self.skia.id,
"name": f"{self.skia.first_name} {self.skia.last_name}",
"promo": self.skia.promo,
"nickname": self.skia.nick_name,
"selling_sum": 1000,
},
)
self.assertEqual(
next(top),
{
"customer__user": self.krophil.id,
"user": self.krophil.id,
"name": f"{self.krophil.first_name} {self.krophil.last_name}",
"promo": self.krophil.promo,
"nickname": self.krophil.nick_name,
"selling_sum": 100,
},
)
self.assertEqual(
next(top),
{
"customer__user": self.root.id,
"user": self.root.id,
"name": f"{self.root.first_name} {self.root.last_name}",
"promo": self.root.promo,
"nickname": self.root.nick_name,
"selling_sum": 2,
},
]
for result in expected_results:
self.assertEqual(
next(top),
{
"user": result["user"],
"name": result["name"],
"promo": result["promo"],
"nickname": result["nickname"],
"selling_sum": result["selling_sum"],
},
)
self.assertIsNone(next(top, None))

View File

@ -48,7 +48,7 @@ import pytz
from datetime import timedelta, datetime
from http import HTTPStatus
from core.utils import get_start_of_semester
from core.utils import get_start_of_semester, get_semester_code
from core.views import CanViewMixin, TabedViewMixin, CanEditMixin
from core.views.forms import LoginForm
from core.models import User
@ -1354,13 +1354,14 @@ class CounterStatView(DetailView, CounterAdminMixin):
def get_context_data(self, **kwargs):
"""Add stats to the context"""
counter = self.object
counter: Counter = self.object
semester_start = get_start_of_semester()
office_hours = counter.get_top_barmen()
kwargs = super(CounterStatView, self).get_context_data(**kwargs)
kwargs.update(
{
"counter": counter,
"current_semester": get_semester_code(),
"total_sellings": counter.get_total_sales(since=semester_start),
"top_customers": counter.get_top_customers(since=semester_start)[:100],
"top_barman": office_hours[:100],

View File

@ -47,9 +47,9 @@
<template x-for="item in items" :key="item.id">
<li class="item-row" x-show="item.quantity > 0">
<div class="item-quantity">
<i class="fa-solid fa-minus"></i>
<i class="fa fa-minus fa-xs" @click="remove(item.id)"></i>
<span x-text="item.quantity"></span>
<i class="fa-solid fa-plus"></i>
<i class="fa fa-plus" @click="add(item)"></i>
</div>
<span class="item-name" x-text="item.name"></span>
<span class="item-price" x-text="(item.unit_price * item.quantity).toFixed(2) + ' €'"></span>
@ -63,13 +63,13 @@
</ul>
<div class="catalog-buttons">
<button @click="clear_basket()" class="clear">
<i class="fa-solid fa-trash"></i>
<i class="fa fa-trash"></i>
{% trans %}Clear{% endtrans %}
</button>
<form method="get" action="{{ url('eboutic:command') }}">
{% csrf_token %}
<button class="validate">
<i class="fa-solid fa-check"></i>
<i class="fa fa-check"></i>
<input type="submit" value="{% trans %}Validate{% endtrans %}"/>
</button>
</form>
@ -85,7 +85,7 @@
</a>
</span>
<span class="clickable" @click="show_alert = false">
<i class="fa-solid fa-xmark"></i>
<i class="fa fa-close"></i>
</span>
</div>
{% endif %}
@ -108,7 +108,7 @@
{% if p.icon %}
<img class="product-image" src="{{ p.icon.url }}" alt="image de {{ p.name }}">
{% else %}
<i class="fa-solid fa-image product-image"></i>
<i class="fa fa-2x fa-picture-o product-image" ></i>
{% endif %}
<div class="product-description">
<h4>{{ p.name }}</h4>
@ -123,19 +123,6 @@
{% else %}
<p>{% trans %}There are no items available for sale{% endtrans %}</p>
{% endfor %}
<h3>{% trans %}Partnership Eurockéennes 2023{% endtrans %}</h3>
{% if user.is_subscribed %}
<a title="Logiciel billetterie en ligne"
href="https://widget.weezevent.com/ticket/a203b986-73b0-4e63-b18a-b7c8bad986b7?id_evenement=915745&locale=fr-FR&code=71348"
class="weezevent-widget-integration" target="_blank"
data-src="https://widget.weezevent.com/ticket/a203b986-73b0-4e63-b18a-b7c8bad986b7?id_evenement=915745&locale=fr-FR&code=71348"
data-width="650" data-height="600" data-resize="1" data-nopb="0" data-type="neo" data-width_auto="1"
data-noscroll="0" data-id="915745">Billetterie Weezevent</a>
<script type="text/javascript" src="https://widget.weezevent.com/weez.js" async defer></script>
{% else %}
<div>{% trans %}You must be a contributor to access the Eurockéennes ticketing service.{% endtrans %}</div>
{% endif %}
</div>
</div>
{% endblock %}

View File

@ -60,7 +60,7 @@
{% trans %}Edit billing information{% endtrans %}
</span>
<span class="collapse-header-icon" :class="{'reverse': collapsed}">
<i class="fa-sharp fa-solid fa-caret-down"></i>
<i class="fa fa-caret-down"></i>
</span>
</div>
<form class="collapse-body" id="billing_info_form" method="post"
@ -78,7 +78,7 @@
</template>
</div>
<div class="clickable" @click="errors = []">
<i class="fa-solid fa-xmark"></i>
<i class="fa fa-close"></i>
</div>
</div>
<div x-show="successful" class="alert alert-green" x-transition>
@ -86,7 +86,7 @@
Informations de facturation enregistrées
</div>
<div class="clickable" @click="successful = false">
<i class="fa-solid fa-xmark"></i>
<i class="fa fa-close"></i>
</div>
</div>
<input type="submit" class="btn btn-blue clickable"

View File

@ -82,18 +82,18 @@
<a href="{{url('election:update_role', role_id=role.id)}}">✏️</a>
<a href="{{url('election:delete_role', role_id=role.id)}}">❌</a>
{%- if role == role_list.last() %}
<button disabled><i class="fa-solid fa-arrow-down"></i></button>
<button disabled><i class="fa-sharp fa-solid fa-caret-down"></i></button>
<button disabled><i class="fa fa-arrow-down"></i></button>
<button disabled><i class="fa fa-caret-down"></i></button>
{%- else %}
<button type="button" onclick="window.location.replace('?role={{ role.id }}&action=bottom');"><i class="fa-solid fa-arrow-down"></i></button>
<button type="button" onclick="window.location.replace('?role={{ role.id }}&action=down');"><i class="fa-sharp fa-solid fa-caret-down"></i></button>
<button type="button" onclick="window.location.replace('?role={{ role.id }}&action=bottom');"><i class="fa fa-arrow-down"></i></button>
<button type="button" onclick="window.location.replace('?role={{ role.id }}&action=down');"><i class="fa fa-caret-down"></i></button>
{%- endif %}
{% if role == role_list.first() %}
<button disabled><i class="fa-solid fa-caret-up"></i></i></button>
<button disabled><i class="fa-sharp fa-solid fa-arrow-up"></i></i></button>
<button disabled><i class="fa fa-caret-up"></i></button>
<button disabled><i class="fa fa-arrow-up"></i></button>
{% else %}
<button type="button" onclick="window.location.replace('?role={{ role.id }}&action=up');"><i class="fa-solid fa-caret-up"></i></i></button>
<button type="button" onclick="window.location.replace('?role={{ role.id }}&action=top');"><i class="fa-sharp fa-solid fa-arrow-up"></i></i></button>
<button type="button" onclick="window.location.replace('?role={{ role.id }}&action=up');"><i class="fa fa-caret-up"></i></button>
<button type="button" onclick="window.location.replace('?role={{ role.id }}&action=top');"><i class="fa fa-arrow-up"></i></button>
{% endif %}
</div>
{%- endif -%}

View File

@ -63,7 +63,7 @@ msgstr "IBAN"
#: accounting/models.py:112
msgid "account number"
msgstr "numero de compte"
msgstr "numéro de compte"
#: accounting/models.py:116 accounting/models.py:147 club/models.py:275
#: com/models.py:75 com/models.py:266 com/models.py:302 counter/models.py:273
@ -1173,7 +1173,7 @@ msgstr "Au"
#: club/templates/club/club_sellings.jinja:5 club/views.py:154
#: club/views.py:483 counter/templates/counter/counter_main.jinja:24
#: counter/templates/counter/last_ops.jinja:41
msgid "Sellings"
msgid "Sales"
msgstr "Ventes"
#: club/templates/club/club_sellings.jinja:9 club/templates/club/stats.jinja:19
@ -1278,7 +1278,7 @@ msgid "Counters:"
msgstr "Comptoirs : "
#: club/templates/club/club_tools.jinja:33
msgid "Accouting: "
msgid "Accounting: "
msgstr "Comptabilité : "
#: club/templates/club/club_tools.jinja:41
@ -1295,8 +1295,8 @@ msgid ""
"not shown wait until moderation takes action"
msgstr ""
"Rappelez vous : les mailing listes doivent être modérées, si votre liste "
"nouvellement créee n'est pas affichée, attendez jusqu'à qu'un modérateur "
"entre en action"
"nouvellement créée n'est pas affichée, attendez jusqu'à ce qu'un modérateur "
"prenne une décision"
#: club/templates/club/mailing.jinja:13
msgid "Mailing lists waiting for moderation"
@ -1671,8 +1671,8 @@ msgid "Moderator: "
msgstr "Modérateur : "
#: com/templates/com/news_detail.jinja:42
msgid "Edit (will be remoderated)"
msgstr "Éditer (sera resoumise à modération)"
msgid "Edit (will be moderated again)"
msgstr "Éditer (sera soumise de nouveau à la modération)"
#: com/templates/com/news_edit.jinja:6 com/templates/com/news_edit.jinja:29
msgid "Edit news"
@ -1680,7 +1680,7 @@ msgstr "Éditer la nouvelle"
#: com/templates/com/news_edit.jinja:39
msgid "Notice: Information, election result - no date"
msgstr "Information, resultat d'élection - sans date"
msgstr "Information, résultat d'élection - sans date"
#: com/templates/com/news_edit.jinja:40
msgid "Event: punctual event, associated with one date"
@ -1951,7 +1951,7 @@ msgstr "T'es fou? Un événement ne peut pas finir avant même de commencer."
#: com/views.py:466
msgid "Delete and save to regenerate"
msgstr "Supprimer et sauver pour regénérer"
msgstr "Supprimer et sauver pour régénérer"
#: com/views.py:481
msgid "Weekmail of the "
@ -2036,7 +2036,7 @@ msgid ""
"Designates whether this user should be treated as active. Unselect this "
"instead of deleting accounts."
msgstr ""
"Est-ce que l'utilisateur doit être traité comme actif. Déselectionnez au "
"Est-ce que l'utilisateur doit être traité comme actif. Désélectionnez au "
"lieu de supprimer les comptes."
#: core/models.py:185
@ -2085,7 +2085,7 @@ msgstr "pronoms"
#: core/models.py:234
msgid "tshirt size"
msgstr "taille de tshirt"
msgstr "taille de t-shirt"
#: core/models.py:237
msgid "-"
@ -2426,7 +2426,7 @@ msgstr "Voir plus"
#: core/templates/core/base.jinja:122
#: forum/templates/forum/last_unread.jinja:17
msgid "Mark all as read"
msgstr "Marquer tout commme lu"
msgstr "Marquer tout comme lu"
#: core/templates/core/base.jinja:132
msgid "Logout"
@ -2440,33 +2440,17 @@ msgstr "Accueil"
msgid "Associations & Clubs"
msgstr "Associations & Clubs"
#: core/templates/core/base.jinja:173
#: core/templates/core/base.jinja:204
msgid "AE"
msgstr "L'AE"
#: core/templates/core/base.jinja:174
#: core/templates/core/base.jinja:205
msgid "AE's clubs"
msgstr "Les clubs de L'AE"
#: core/templates/core/base.jinja:175
msgid "BdF"
msgstr "Le BdF"
#: core/templates/core/base.jinja:176
msgid "BDS"
msgstr "Le BDS"
#: core/templates/core/base.jinja:177
msgid "CETU"
msgstr "Le CETU"
#: core/templates/core/base.jinja:178
msgid "Doceo"
msgstr "Doceo"
#: core/templates/core/base.jinja:179
msgid "Positions"
msgstr "Postes à pourvoir"
#: core/templates/core/base.jinja:206
msgid "Others UTBM's Associations"
msgstr "Les autres associations de l'UTBM"
#: core/templates/core/base.jinja:187 core/templates/core/user_tools.jinja:118
msgid "Elections"
@ -2780,7 +2764,7 @@ msgstr "Cotisant jusqu'au %(subscription_end)s"
#: core/templates/core/macros.jinja:86 core/templates/core/user_edit.jinja:40
msgid "Account number: "
msgstr "Numero de compte : "
msgstr "Numéro de compte : "
#: core/templates/core/macros.jinja:91 launderette/models.py:217
msgid "Slot"
@ -3004,7 +2988,7 @@ msgstr "Bienvenue, %(user_name)s!"
#: core/templates/core/register.jinja:10
msgid ""
"You successfully registred and you will soon receive a confirmation mail."
"You successfully registered and you will soon receive a confirmation mail."
msgstr ""
"Vous vous êtes correctement enregistré, et vous devriez recevoir rapidement "
"un email de confirmation."
@ -3063,14 +3047,14 @@ msgstr "Compte utilisateur"
#: core/templates/core/user_account.jinja:44
#: core/templates/core/user_account_detail.jinja:13
msgid "Account buyings"
msgstr "Achat sur compte utilisateur"
msgid "Account purchases"
msgstr "Achats du compte"
#: core/templates/core/user_account.jinja:48
#: core/templates/core/user_account_detail.jinja:46
#: counter/templates/counter/cash_summary_list.jinja:17
#: counter/templates/counter/last_ops.jinja:16
msgid "Refillings"
msgid "Reloads"
msgstr "Rechargements"
#: core/templates/core/user_account.jinja:53
@ -4114,7 +4098,7 @@ msgstr "Nouveau comptoir"
#: counter/templates/counter/counter_list.jinja:22
#: counter/templates/counter/counter_list.jinja:44
#: counter/templates/counter/refilling_list.jinja:5
msgid "Refillings list"
msgid "Reloads list"
msgstr "Liste de rechargements"
#: counter/templates/counter/counter_list.jinja:27
@ -5033,7 +5017,7 @@ msgid ""
"The code of an UV must only contains uppercase characters without accent and "
"numbers"
msgstr ""
"Le code d'une UV doit seulement contenire des caractères majuscule sans "
"Le code d'une UV doit seulement contenir des caractères majuscule sans "
"accents et nombres"
#: pedagogy/models.py:67
@ -5058,7 +5042,7 @@ msgstr "département"
#: pedagogy/models.py:103
msgid "objectives"
msgstr "objecifs"
msgstr "objectifs"
#: pedagogy/models.py:104
msgid "program"
@ -5094,7 +5078,7 @@ msgstr "heures TE"
#: pedagogy/models.py:217 pedagogy/models.py:291
msgid "uv"
msgstr "uv"
msgstr "UE"
#: pedagogy/models.py:221
msgid "global grade"
@ -5153,7 +5137,7 @@ msgstr "%(credit_type)s"
#: pedagogy/templates/pedagogy/guide.jinja:59
#: pedagogy/templates/pedagogy/moderation.jinja:12
msgid "UV"
msgstr "UV"
msgstr "UE"
#: pedagogy/templates/pedagogy/guide.jinja:61
msgid "Department"
@ -5253,8 +5237,8 @@ msgid "Key concepts"
msgstr "Concepts clefs"
#: pedagogy/templates/pedagogy/uv_detail.jinja:79
msgid "UV manager: "
msgstr "Gestionnaire d'UV : "
msgid "UE manager: "
msgstr "Gestionnaire d'UE : "
#: pedagogy/templates/pedagogy/uv_detail.jinja:86 pedagogy/tests.py:453
msgid ""
@ -5284,23 +5268,23 @@ msgstr "Signaler ce commentaire"
#: pedagogy/templates/pedagogy/uv_edit.jinja:4
#: pedagogy/templates/pedagogy/uv_edit.jinja:8
msgid "Edit UV"
msgstr "Éditer"
msgid "Edit UE"
msgstr "Éditer l'UE"
#: pedagogy/templates/pedagogy/uv_edit.jinja:27
msgid "Import from UTBM"
msgstr "Importer depuis l'UTBM"
#: pedagogy/templates/pedagogy/uv_edit.jinja:62
msgid "Unknown UV code"
msgstr "Code d'UV inconnu"
msgid "Unknown UE code"
msgstr "Code d'UE inconnu"
#: pedagogy/templates/pedagogy/uv_edit.jinja:77
msgid "Successful autocomplete"
msgstr "Autocomplétion réussite"
#: pedagogy/templates/pedagogy/uv_edit.jinja:80
msgid "An error occured: "
msgid "An error occurred: "
msgstr "Une erreur est survenue : "
#: rootplace/templates/rootplace/delete_user_messages.jinja:8
@ -5566,7 +5550,7 @@ msgstr "Automne et printemps"
#: sith/settings.py:448
msgid "German"
msgstr "Allemant"
msgstr "Allemand"
#: sith/settings.py:449
msgid "Spanish"
@ -6362,11 +6346,3 @@ msgstr "Vous ne pouvez plus écrire de commentaires, la date est passée."
#, python-format
msgid "Maximum characters: %(max_length)s"
msgstr "Nombre de caractères max: %(max_length)s"
#: eboutic/templates/eboutic/eboutic_main.jinja:127
msgid "Partnership Eurockéennes 2023"
msgstr "Partenariat Eurockéennes 2023"
#: eboutic/templates/eboutic/eboutic_main.jinja:137
msgid "You must be a subscriber to access the Eurockéennes ticketing service."
msgstr "Vous devez être cotisant pour pouvoir accéder à la billetterie des Eurockéennes."

View File

@ -45,8 +45,8 @@
<div class="radio-semester">
<div class="radio-guide">
<input type="checkbox" name="semester" id="radioAUTUMN" value="AUTUMN"><label for="radioAUTUMN"><i class="fa-solid fa-leaf"></i></label>
<input type="checkbox" name="semester" id="radioSPRING" value="SPRING"><label for="radioSPRING"><i class="fa-solid fa-sun"></i></label>
<input type="checkbox" name="semester" id="radioAUTUMN" value="AUTUMN"><label for="radioAUTUMN"><i class="fa fa-leaf"></i></label>
<input type="checkbox" name="semester" id="radioSPRING" value="SPRING"><label for="radioSPRING"><i class="fa fa-sun-o"></i></label>
<span><input type="checkbox" name="semester" id="radioAP" value="AUTUMN_AND_SPRING"><label for="radioAP">AP</label></span>
</div>
</div>
@ -60,8 +60,8 @@
<td>{% trans %}Title{% endtrans %}</td>
<td>{% trans %}Department{% endtrans %}</td>
<td>{% trans %}Credit type{% endtrans %}</td>
<td><i class="fa-solid fa-leaf"></i></td>
<td><i class="fa-solid fa-sun"></i></td>
<td><i class="fa fa-leaf"></i></td>
<td><i class="fa fa-sun-o"></i></td>
{% if can_create_uv(user) %}
<td>{% trans %}Edit{% endtrans %}</td>
<td>{% trans %}Delete{% endtrans %}</td>
@ -77,12 +77,12 @@
<td>{{ uv.credit_type }}</td>
<td>
{% if uv.semester in ["AUTUMN", "AUTUMN_AND_SPRING"] %}
<i class="fa-solid fa-leaf"></i>
<i class="fa fa-leaf"></i>
{% endif %}
</td>
<td>
{% if uv.semester in ["SPRING", "AUTUMN_AND_SPRING"] %}
<i class="fa-solid fa-sun"></i>
<i class="fa fa-sun-o"></i>
{% endif %}
</td>
{% if user.is_owner(uv) -%}
@ -107,9 +107,9 @@
var autumn = "";
var spring = "";
if (uv.semester == "AUTUMN" || uv.semester == "AUTUMN_AND_SPRING")
autumn = "<i class='fa-solid fa-leaf'></i>";
autumn = "<i class='fa fa-leaf'></i>";
if (uv.semester == "SPRING" || uv.semester == "AUTUMN_AND_SPRING")
spring = "<i class='fa-solid fa-sun'></i>";
spring = "<i class='fa fa-sun-o'></i>";
var html = `
<tr onclick="window.location.href = '${uv.absolute_url}';">

View File

@ -3,9 +3,9 @@
{% if grade >= 0 %}
{% for i in range(5) %}
{% if i <= grade %}
<span class="fa-solid fa-star pedagogy star-checked grade-with-star"></span>
<span class="fa fa-star pedagogy star-checked grade-with-star"></span>
{% else %}
<span class="fa-solid fa-star pedagogy star-not-checked grade-with-star"></span>
<span class="fa fa-star pedagogy star-not-checked grade-with-star"></span>
{% endif %}
{% endfor %}
<span class="pedagogy grade-without-star">{{ grade }}/5</span>

View File

@ -26,7 +26,7 @@
stars[i].setAttribute("class", attrs + " unchecked");
}
' checked>
<span class="fa-solid fa-xmark-circle"> {{ translations.do_not_vote }}</span>
<span class="fa fa-times-circle"> {{ translations.do_not_vote }}</span>
</label>
{# Star widget #}
@ -46,7 +46,7 @@
}
}
'>
<i class="{{ widget.name }} fa-solid fa-star unchecked"></i>
<i class="{{ widget.name }} fa fa-star unchecked"></i>
</label>
{% endfor %}

View File

@ -76,7 +76,7 @@
<p>{{ object.skills|markdown }}</p>
<p><b>{% trans %}Key concepts{% endtrans %}</b></p>
<p>{{ object.key_concepts|markdown }}</p>
<p><b>{% trans %}UV manager: {% endtrans %}</b>{{ object.manager }}</p>
<p><b>{% trans %}UE manager: {% endtrans %}</b>{{ object.manager }}</p>
</div>
</div>
@ -167,7 +167,7 @@
<div class="comment">
<div class="anchor">
<a href="{{ url('pedagogy:uv_detail', uv_id=uv.id) }}#{{ comment.id }}"><i class="fa-solid fa-paragraph"></i></a>
<a href="{{ url('pedagogy:uv_detail', uv_id=uv.id) }}#{{ comment.id }}"><i class="fa fa-paragraph"></i></a>
</div>
{{ comment.comment|markdown }}
</div>
@ -206,8 +206,8 @@
$("#return_noscript").hide();
$("#return_js").show();
var icons = {
header: "fa-solid fa-toggle-on",
activeHeader: "fa-solid fa-toggle-off"
header: "fa fa-toggle-right",
activeHeader: "fa fa-toggle-down"
};
$(function(){
$("#leave_comment").accordion({

View File

@ -1,11 +1,11 @@
{% extends "core/base.jinja" %}
{% block title %}
{% trans %}Edit UV{% endtrans %}
{% trans %}Edit UE{% endtrans %}
{% endblock %}
{% block content %}
<h2>{% trans %}Edit UV{% endtrans %}</h2>
<h2>{% trans %}Edit UE{% endtrans %}</h2>
<form action="" method="post" enctype="multipart/form-data" id="uv_edit">
{% csrf_token %}
{{ form.non_field_errors() }}
@ -59,7 +59,7 @@
url: url,
success: function(data, _, xhr) {
if (xhr.status != 200) {
createQuickNotif("{% trans %}Unknown UV code{% endtrans %}")
createQuickNotif("{% trans %}Unknown UE code{% endtrans %}")
return
}
for (let key in data) {
@ -77,7 +77,7 @@
createQuickNotif('{% trans %}Successful autocomplete{% endtrans %}')
},
error: function(_, _, statusMessage) {
createQuickNotif('{% trans %}An error occured: {% endtrans %}' + statusMessage)
createQuickNotif('{% trans %}An error occurred: {% endtrans %}' + statusMessage)
},
})
})

140
poetry.lock generated
View File

@ -1,10 +1,9 @@
# This file is automatically @generated by Poetry and should not be changed by hand.
# This file is automatically @generated by Poetry 1.6.1 and should not be changed by hand.
[[package]]
name = "alabaster"
version = "0.7.12"
description = "A configurable sidebar-enabled Sphinx theme"
category = "main"
optional = true
python-versions = "*"
files = [
@ -16,7 +15,6 @@ files = [
name = "appnope"
version = "0.1.3"
description = "Disable App Nap on macOS >= 10.9"
category = "dev"
optional = false
python-versions = "*"
files = [
@ -28,7 +26,6 @@ files = [
name = "asgiref"
version = "3.6.0"
description = "ASGI specs, helper code, and adapters"
category = "main"
optional = false
python-versions = ">=3.7"
files = [
@ -43,7 +40,6 @@ tests = ["mypy (>=0.800)", "pytest", "pytest-asyncio"]
name = "babel"
version = "2.11.0"
description = "Internationalization utilities"
category = "main"
optional = true
python-versions = ">=3.6"
files = [
@ -58,7 +54,6 @@ pytz = ">=2015.7"
name = "backcall"
version = "0.2.0"
description = "Specifications for callback functions passed in to an API"
category = "dev"
optional = false
python-versions = "*"
files = [
@ -70,7 +65,6 @@ files = [
name = "black"
version = "23.3.0"
description = "The uncompromising code formatter."
category = "dev"
optional = false
python-versions = ">=3.7"
files = [
@ -120,7 +114,6 @@ uvloop = ["uvloop (>=0.15.2)"]
name = "certifi"
version = "2022.12.7"
description = "Python package for providing Mozilla's CA Bundle."
category = "main"
optional = false
python-versions = ">=3.6"
files = [
@ -132,7 +125,6 @@ files = [
name = "cffi"
version = "1.15.1"
description = "Foreign Function Interface for Python calling C code."
category = "main"
optional = false
python-versions = "*"
files = [
@ -209,7 +201,6 @@ pycparser = "*"
name = "charset-normalizer"
version = "2.1.1"
description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet."
category = "main"
optional = true
python-versions = ">=3.6.0"
files = [
@ -224,7 +215,6 @@ unicode-backport = ["unicodedata2"]
name = "click"
version = "8.1.3"
description = "Composable command line interface toolkit"
category = "dev"
optional = false
python-versions = ">=3.7"
files = [
@ -239,7 +229,6 @@ colorama = {version = "*", markers = "platform_system == \"Windows\""}
name = "colorama"
version = "0.4.6"
description = "Cross-platform colored terminal text."
category = "main"
optional = false
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7"
files = [
@ -251,7 +240,6 @@ files = [
name = "coverage"
version = "5.5"
description = "Code coverage measurement for Python"
category = "main"
optional = true
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, <4"
files = [
@ -316,7 +304,6 @@ toml = ["toml"]
name = "cryptography"
version = "40.0.1"
description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers."
category = "main"
optional = false
python-versions = ">=3.6"
files = [
@ -358,7 +345,6 @@ tox = ["tox"]
name = "decorator"
version = "5.1.1"
description = "Decorators for Humans"
category = "dev"
optional = false
python-versions = ">=3.5"
files = [
@ -370,7 +356,6 @@ files = [
name = "dict2xml"
version = "1.7.3"
description = "Small utility to convert a python dictionary into an XML string"
category = "main"
optional = false
python-versions = ">=3.5"
files = [
@ -385,7 +370,6 @@ tests = ["noseofyeti[black] (==2.4.1)", "pytest (==7.2.1)"]
name = "django"
version = "3.2.18"
description = "A high-level Python Web framework that encourages rapid development and clean, pragmatic design."
category = "main"
optional = false
python-versions = ">=3.6"
files = [
@ -406,7 +390,6 @@ bcrypt = ["bcrypt"]
name = "django-ajax-selects"
version = "2.2.0"
description = "Edit ForeignKey, ManyToManyField and CharField in Django Admin using jQuery UI AutoComplete."
category = "main"
optional = false
python-versions = "*"
files = [
@ -417,7 +400,6 @@ files = [
name = "django-countries"
version = "7.5.1"
description = "Provides a country field for Django models."
category = "main"
optional = false
python-versions = "*"
files = [
@ -439,7 +421,6 @@ test = ["djangorestframework", "graphene-django", "pytest", "pytest-cov", "pytes
name = "django-debug-toolbar"
version = "4.0.0"
description = "A configurable set of panels that display various debug information about the current request/response."
category = "dev"
optional = false
python-versions = ">=3.8"
files = [
@ -455,7 +436,6 @@ sqlparse = ">=0.2"
name = "django-haystack"
version = "3.2.1"
description = "Pluggable search for Django."
category = "main"
optional = false
python-versions = "*"
files = [
@ -472,7 +452,6 @@ elasticsearch = ["elasticsearch (>=5,<8)"]
name = "django-jinja"
version = "2.10.2"
description = "Jinja2 templating language integrated in Django."
category = "main"
optional = false
python-versions = ">=3.6"
files = [
@ -486,21 +465,19 @@ jinja2 = ">=3"
[[package]]
name = "django-ordered-model"
version = "3.6"
version = "3.7.4"
description = "Allows Django models to be ordered and provides a simple admin interface for reordering them."
category = "main"
optional = false
python-versions = "*"
files = [
{file = "django-ordered-model-3.6.tar.gz", hash = "sha256:62161a6bc51d8b402644854b257605d7b5183d01fd349826682a87e9227c05b5"},
{file = "django_ordered_model-3.6-py3-none-any.whl", hash = "sha256:0006b111f472a2348f75554a4e77bee2b1f379a0f96726af6b1a3ebf3a950789"},
{file = "django-ordered-model-3.7.4.tar.gz", hash = "sha256:f258b9762525c00a53009e82f8b8bf2a3aa315e8b453e281e8fdbbfe2b8cb3ba"},
{file = "django_ordered_model-3.7.4-py3-none-any.whl", hash = "sha256:dfcd3183fe0749dad1c9971cba1d6240ce7328742a30ddc92feca41107bb241d"},
]
[[package]]
name = "django-phonenumber-field"
version = "6.4.0"
description = "An international phone number field for django models."
category = "main"
optional = false
python-versions = ">=3.7"
files = [
@ -519,7 +496,6 @@ phonenumberslite = ["phonenumberslite (>=7.0.2)"]
name = "django-ranged-response"
version = "0.2.0"
description = "Modified Django FileResponse that adds Content-Range headers."
category = "main"
optional = false
python-versions = "*"
files = [
@ -533,7 +509,6 @@ django = "*"
name = "django-simple-captcha"
version = "0.5.17"
description = "A very simple, yet powerful, Django captcha application"
category = "main"
optional = false
python-versions = "*"
files = [
@ -553,7 +528,6 @@ test = ["testfixtures"]
name = "djangorestframework"
version = "3.14.0"
description = "Web APIs for Django, made easy."
category = "main"
optional = false
python-versions = ">=3.6"
files = [
@ -569,7 +543,6 @@ pytz = "*"
name = "docutils"
version = "0.17.1"
description = "Docutils -- Python Documentation Utilities"
category = "main"
optional = true
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
files = [
@ -577,11 +550,24 @@ files = [
{file = "docutils-0.17.1.tar.gz", hash = "sha256:686577d2e4c32380bb50cbb22f575ed742d58168cee37e99117a854bcd88f125"},
]
[[package]]
name = "freezegun"
version = "1.2.2"
description = "Let your Python tests travel through time"
optional = false
python-versions = ">=3.6"
files = [
{file = "freezegun-1.2.2-py3-none-any.whl", hash = "sha256:ea1b963b993cb9ea195adbd893a48d573fda951b0da64f60883d7e988b606c9f"},
{file = "freezegun-1.2.2.tar.gz", hash = "sha256:cd22d1ba06941384410cd967d8a99d5ae2442f57dfafeff2fda5de8dc5c05446"},
]
[package.dependencies]
python-dateutil = ">=2.7"
[[package]]
name = "idna"
version = "3.4"
description = "Internationalized Domain Names in Applications (IDNA)"
category = "main"
optional = true
python-versions = ">=3.5"
files = [
@ -593,7 +579,6 @@ files = [
name = "imagesize"
version = "1.4.1"
description = "Getting image size from png/jpeg/jpeg2000/gif file"
category = "main"
optional = true
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
files = [
@ -605,7 +590,6 @@ files = [
name = "importlib-metadata"
version = "6.0.0"
description = "Read metadata from Python packages"
category = "main"
optional = true
python-versions = ">=3.7"
files = [
@ -625,7 +609,6 @@ testing = ["flake8 (<5)", "flufl.flake8", "importlib-resources (>=1.3)", "packag
name = "ipython"
version = "7.34.0"
description = "IPython: Productive Interactive Computing"
category = "dev"
optional = false
python-versions = ">=3.7"
files = [
@ -662,7 +645,6 @@ test = ["ipykernel", "nbformat", "nose (>=0.10.1)", "numpy (>=1.17)", "pygments"
name = "jedi"
version = "0.18.2"
description = "An autocompletion tool for Python that can be used for text editors."
category = "dev"
optional = false
python-versions = ">=3.6"
files = [
@ -682,7 +664,6 @@ testing = ["Django (<3.1)", "attrs", "colorama", "docopt", "pytest (<7.0.0)"]
name = "jinja2"
version = "3.1.2"
description = "A very fast and expressive template engine."
category = "main"
optional = false
python-versions = ">=3.7"
files = [
@ -700,7 +681,6 @@ i18n = ["Babel (>=2.7)"]
name = "libsass"
version = "0.22.0"
description = "Sass for Python: A straightforward binding of libsass for Python."
category = "main"
optional = false
python-versions = ">=3.6"
files = [
@ -715,7 +695,6 @@ files = [
name = "markupsafe"
version = "2.1.1"
description = "Safely add untrusted strings to HTML/XML markup."
category = "main"
optional = false
python-versions = ">=3.7"
files = [
@ -765,7 +744,6 @@ files = [
name = "matplotlib-inline"
version = "0.1.6"
description = "Inline Matplotlib backend for Jupyter"
category = "dev"
optional = false
python-versions = ">=3.5"
files = [
@ -780,7 +758,6 @@ traitlets = "*"
name = "mistune"
version = "0.8.4"
description = "The fastest markdown parser in pure Python"
category = "main"
optional = false
python-versions = "*"
files = [
@ -792,7 +769,6 @@ files = [
name = "mypy-extensions"
version = "0.4.3"
description = "Experimental type system extensions for programs checked with the mypy typechecker."
category = "dev"
optional = false
python-versions = "*"
files = [
@ -804,7 +780,6 @@ files = [
name = "packaging"
version = "23.0"
description = "Core utilities for Python packages"
category = "main"
optional = false
python-versions = ">=3.7"
files = [
@ -816,7 +791,6 @@ files = [
name = "parso"
version = "0.8.3"
description = "A Python Parser"
category = "dev"
optional = false
python-versions = ">=3.6"
files = [
@ -832,7 +806,6 @@ testing = ["docopt", "pytest (<6.0.0)"]
name = "pathspec"
version = "0.10.3"
description = "Utility library for gitignore style pattern matching of file paths."
category = "dev"
optional = false
python-versions = ">=3.7"
files = [
@ -844,7 +817,6 @@ files = [
name = "pexpect"
version = "4.8.0"
description = "Pexpect allows easy control of interactive console applications."
category = "dev"
optional = false
python-versions = "*"
files = [
@ -859,7 +831,6 @@ ptyprocess = ">=0.5"
name = "phonenumbers"
version = "8.13.4"
description = "Python version of Google's common library for parsing, formatting, storing and validating international phone numbers."
category = "main"
optional = false
python-versions = "*"
files = [
@ -871,7 +842,6 @@ files = [
name = "pickleshare"
version = "0.7.5"
description = "Tiny 'shelve'-like database with concurrency support"
category = "dev"
optional = false
python-versions = "*"
files = [
@ -883,7 +853,6 @@ files = [
name = "pillow"
version = "9.4.0"
description = "Python Imaging Library (Fork)"
category = "main"
optional = false
python-versions = ">=3.7"
files = [
@ -974,7 +943,6 @@ tests = ["check-manifest", "coverage", "defusedxml", "markdown2", "olefile", "pa
name = "platformdirs"
version = "2.6.2"
description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"."
category = "dev"
optional = false
python-versions = ">=3.7"
files = [
@ -990,7 +958,6 @@ test = ["appdirs (==1.4.4)", "covdefaults (>=2.2.2)", "pytest (>=7.2)", "pytest-
name = "prompt-toolkit"
version = "3.0.36"
description = "Library for building powerful interactive command lines in Python"
category = "dev"
optional = false
python-versions = ">=3.6.2"
files = [
@ -1005,7 +972,6 @@ wcwidth = "*"
name = "psycopg2-binary"
version = "2.9.3"
description = "psycopg2 - Python-PostgreSQL Database Adapter"
category = "main"
optional = false
python-versions = ">=3.6"
files = [
@ -1074,7 +1040,6 @@ files = [
name = "ptyprocess"
version = "0.7.0"
description = "Run a subprocess in a pseudo terminal"
category = "dev"
optional = false
python-versions = "*"
files = [
@ -1086,7 +1051,6 @@ files = [
name = "pycparser"
version = "2.21"
description = "C parser in Python"
category = "main"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
files = [
@ -1098,7 +1062,6 @@ files = [
name = "pygments"
version = "2.14.0"
description = "Pygments is a syntax highlighting package written in Python."
category = "main"
optional = false
python-versions = ">=3.6"
files = [
@ -1113,7 +1076,6 @@ plugins = ["importlib-metadata"]
name = "pygraphviz"
version = "1.10"
description = "Python interface to Graphviz"
category = "main"
optional = false
python-versions = ">=3.8"
files = [
@ -1124,7 +1086,6 @@ files = [
name = "pyopenssl"
version = "23.1.1"
description = "Python wrapper module around the OpenSSL library"
category = "main"
optional = false
python-versions = ">=3.6"
files = [
@ -1143,7 +1104,6 @@ test = ["flaky", "pretend", "pytest (>=3.0.1)"]
name = "python-dateutil"
version = "2.8.2"
description = "Extensions to the standard Python datetime module"
category = "main"
optional = false
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7"
files = [
@ -1158,7 +1118,6 @@ six = ">=1.5"
name = "pytz"
version = "2021.3"
description = "World timezone definitions, modern and historical"
category = "main"
optional = false
python-versions = "*"
files = [
@ -1170,7 +1129,6 @@ files = [
name = "reportlab"
version = "3.6.12"
description = "The Reportlab Toolkit"
category = "main"
optional = false
python-versions = ">=3.7,<4"
files = [
@ -1232,7 +1190,6 @@ rlpycairo = ["rlPyCairo (>=0.1.0)"]
name = "requests"
version = "2.28.1"
description = "Python HTTP for Humans."
category = "main"
optional = true
python-versions = ">=3.7, <4"
files = [
@ -1254,7 +1211,6 @@ use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"]
name = "sentry-sdk"
version = "1.21.0"
description = "Python client for Sentry (https://sentry.io)"
category = "main"
optional = false
python-versions = "*"
files = [
@ -1296,7 +1252,6 @@ tornado = ["tornado (>=5)"]
name = "setuptools"
version = "65.6.3"
description = "Easily download, build, install, upgrade, and uninstall Python packages"
category = "dev"
optional = false
python-versions = ">=3.7"
files = [
@ -1313,7 +1268,6 @@ testing-integration = ["build[virtualenv]", "filelock (>=3.4.0)", "jaraco.envs (
name = "six"
version = "1.16.0"
description = "Python 2 and 3 compatibility utilities"
category = "main"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*"
files = [
@ -1325,7 +1279,6 @@ files = [
name = "snowballstemmer"
version = "2.2.0"
description = "This package provides 29 stemmers for 28 languages generated from Snowball algorithms."
category = "main"
optional = true
python-versions = "*"
files = [
@ -1337,7 +1290,6 @@ files = [
name = "sphinx"
version = "4.5.0"
description = "Python documentation generator"
category = "main"
optional = true
python-versions = ">=3.6"
files = [
@ -1373,7 +1325,6 @@ test = ["cython", "html5lib", "pytest", "pytest-cov", "typed-ast"]
name = "sphinx-copybutton"
version = "0.4.0"
description = "Add a copy button to each of your code cells."
category = "main"
optional = true
python-versions = ">=3.6"
files = [
@ -1392,7 +1343,6 @@ rtd = ["ipython", "sphinx", "sphinx-book-theme"]
name = "sphinx-rtd-theme"
version = "1.1.1"
description = "Read the Docs theme for Sphinx"
category = "main"
optional = true
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,>=2.7"
files = [
@ -1411,7 +1361,6 @@ dev = ["bump2version", "sphinxcontrib-httpdomain", "transifex-client", "wheel"]
name = "sphinxcontrib-applehelp"
version = "1.0.3"
description = "sphinxcontrib-applehelp is a Sphinx extension which outputs Apple help books"
category = "main"
optional = true
python-versions = ">=3.8"
files = [
@ -1427,7 +1376,6 @@ test = ["pytest"]
name = "sphinxcontrib-devhelp"
version = "1.0.2"
description = "sphinxcontrib-devhelp is a sphinx extension which outputs Devhelp document."
category = "main"
optional = true
python-versions = ">=3.5"
files = [
@ -1443,7 +1391,6 @@ test = ["pytest"]
name = "sphinxcontrib-htmlhelp"
version = "2.0.0"
description = "sphinxcontrib-htmlhelp is a sphinx extension which renders HTML help files"
category = "main"
optional = true
python-versions = ">=3.6"
files = [
@ -1459,7 +1406,6 @@ test = ["html5lib", "pytest"]
name = "sphinxcontrib-jsmath"
version = "1.0.1"
description = "A sphinx extension which renders display math in HTML via JavaScript"
category = "main"
optional = true
python-versions = ">=3.5"
files = [
@ -1474,7 +1420,6 @@ test = ["flake8", "mypy", "pytest"]
name = "sphinxcontrib-qthelp"
version = "1.0.3"
description = "sphinxcontrib-qthelp is a sphinx extension which outputs QtHelp document."
category = "main"
optional = true
python-versions = ">=3.5"
files = [
@ -1490,7 +1435,6 @@ test = ["pytest"]
name = "sphinxcontrib-serializinghtml"
version = "1.1.5"
description = "sphinxcontrib-serializinghtml is a sphinx extension which outputs \"serialized\" HTML files (json and pickle)."
category = "main"
optional = true
python-versions = ">=3.5"
files = [
@ -1504,21 +1448,24 @@ test = ["pytest"]
[[package]]
name = "sqlparse"
version = "0.4.3"
version = "0.4.4"
description = "A non-validating SQL parser."
category = "main"
optional = false
python-versions = ">=3.5"
files = [
{file = "sqlparse-0.4.3-py3-none-any.whl", hash = "sha256:0323c0ec29cd52bceabc1b4d9d579e311f3e4961b98d174201d5622a23b85e34"},
{file = "sqlparse-0.4.3.tar.gz", hash = "sha256:69ca804846bb114d2ec380e4360a8a340db83f0ccf3afceeb1404df028f57268"},
{file = "sqlparse-0.4.4-py3-none-any.whl", hash = "sha256:5430a4fe2ac7d0f93e66f1efc6e1338a41884b7ddf2a350cedd20ccc4d9d28f3"},
{file = "sqlparse-0.4.4.tar.gz", hash = "sha256:d446183e84b8349fa3061f0fe7f06ca94ba65b426946ffebe6e3e8295332420c"},
]
[package.extras]
dev = ["build", "flake8"]
doc = ["sphinx"]
test = ["pytest", "pytest-cov"]
[[package]]
name = "tomli"
version = "2.0.1"
description = "A lil' TOML parser"
category = "dev"
optional = false
python-versions = ">=3.7"
files = [
@ -1530,7 +1477,6 @@ files = [
name = "traitlets"
version = "5.8.1"
description = "Traitlets Python configuration system"
category = "dev"
optional = false
python-versions = ">=3.7"
files = [
@ -1546,7 +1492,6 @@ test = ["argcomplete (>=2.0)", "pre-commit", "pytest", "pytest-mock"]
name = "typing-extensions"
version = "4.4.0"
description = "Backported and Experimental Type Hints for Python 3.7+"
category = "main"
optional = false
python-versions = ">=3.7"
files = [
@ -1558,7 +1503,6 @@ files = [
name = "urllib3"
version = "1.26.13"
description = "HTTP library with thread-safe connection pooling, file post, and more."
category = "main"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*"
files = [
@ -1575,7 +1519,6 @@ socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"]
name = "wcwidth"
version = "0.2.5"
description = "Measures the displayed width of unicode strings in a terminal"
category = "dev"
optional = false
python-versions = "*"
files = [
@ -1583,37 +1526,10 @@ files = [
{file = "wcwidth-0.2.5.tar.gz", hash = "sha256:c4d647b99872929fdb7bdcaa4fbe7f01413ed3d98077df798530e5b04f116c83"},
]
[[package]]
name = "xapian-bindings"
version = "0.1.0"
description = "Meta-package to build and install xapian-bindings extension."
category = "main"
optional = false
python-versions = "*"
files = [
{file = "xapian-bindings-0.1.0.tar.gz", hash = "sha256:f2b0396082ebf4f6681ab43d6d8fd1f63b6964b18c32c91236ed067c6f62ad14"},
]
[[package]]
name = "xapian-haystack"
version = "3.0.1"
description = "A Xapian backend for Haystack"
category = "main"
optional = false
python-versions = "*"
files = [
{file = "xapian-haystack-3.0.1.tar.gz", hash = "sha256:a5c0e1262b95008df4dfeb58d093c654acee3f2b27ea3f7d366900895cdc70f9"},
]
[package.dependencies]
django = ">=2.2"
django-haystack = ">=2.8.0"
[[package]]
name = "zipp"
version = "3.11.0"
description = "Backport of pathlib-compatible object wrapper for zip files"
category = "main"
optional = true
python-versions = ">=3.7"
files = [
@ -1626,10 +1542,10 @@ docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker
testing = ["flake8 (<5)", "func-timeout", "jaraco.functools", "jaraco.itertools", "more-itertools", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)"]
[extras]
docs = ["Sphinx", "sphinx-rtd-theme", "sphinx-copybutton"]
docs = ["Sphinx", "sphinx-copybutton", "sphinx-rtd-theme"]
testing = ["coverage"]
[metadata]
lock-version = "2.0"
python-versions = "^3.8"
content-hash = "95b9a07660bd8f95a5a90cc273071a675d884424479cd1ed922438dc767d230a"
content-hash = "32bf0229e7ac812ea278a36587c184cd4ae507f87816f573447e85cd5312e52c"

View File

@ -33,10 +33,8 @@ phonenumbers = "^8.12"
django-ajax-selects = "^2.1.0"
reportlab = "^3.6"
django-haystack = "^3.2.1"
xapian-haystack = "^3.0.1"
xapian-bindings = "^0.1.0"
libsass = "^0.22"
django-ordered-model = "^3.6"
django-ordered-model = "^3.7"
django-simple-captcha = "^0.5.17"
python-dateutil = "^2.8.2"
psycopg2-binary = "2.9.3"
@ -59,6 +57,7 @@ testing = ["coverage"]
docs = ["Sphinx", "sphinx-rtd-theme", "sphinx-copybutton"]
[tool.poetry.dev-dependencies]
freezegun = "^1.2.2" # used to test time-dependent code
django-debug-toolbar = "^4.0.0"
ipython = "^7.28.0"
black = "^23.3.0"

View File

@ -191,9 +191,9 @@ TEMPLATES = [
HAYSTACK_CONNECTIONS = {
"default": {
"ENGINE": "xapian_backend.XapianEngine",
"PATH": os.path.join(os.path.dirname(__file__), "search_indexes", "xapian"),
"INCLUDE_SPELLING": True,
"ENGINE": "haystack.backends.elasticsearch7_backend.Elasticsearch7SearchEngine",
'URL': 'http://localhost:9200/',
'INDEX_NAME': 'haystack',
}
}
@ -327,7 +327,8 @@ SITH_CLUB_ROOT_PAGE = "clubs"
# Define the date in the year serving as reference for the subscriptions calendar
# (month, day)
SITH_START_DATE = (8, 15) # 15th August
SITH_SEMESTER_START_AUTUMN = (8, 15) # 15 August
SITH_SEMESTER_START_SPRING = (2, 15) # 15 February
# Used to determine the valid promos
SITH_SCHOOL_START_YEAR = 1999
@ -714,7 +715,7 @@ SITH_FRONT_DEP_VERSIONS = {
"https://github.com/chartjs/Chart.js/": "2.6.0",
"https://github.com/xdan/datetimepicker/": "2.5.21",
"https://github.com/Ionaru/easy-markdown-editor/": "2.18.0",
"https://github.com/FortAwesome/Font-Awesome/": "6.4.0",
"https://github.com/FortAwesome/Font-Awesome/": "4.7.0",
"https://github.com/jquery/jquery/": "3.6.2",
"https://github.com/sethmcl/jquery-ui/": "1.11.1",
"https://github.com/viralpatel/jquery.shorten/": "",

View File

@ -114,12 +114,12 @@ class Subscription(models.Model):
return "No user - " + str(self.pk)
@staticmethod
def compute_start(d=None, duration=1, user=None):
def compute_start(d: date = None, duration: int = 1, user: User = None) -> date:
"""
This function computes the start date of the subscription with respect to the given date (default is today),
and the start date given in settings.SITH_START_DATE.
and the start date given in settings.SITH_SEMESTER_START_AUTUMN.
It takes the nearest past start date.
Exemples: with SITH_START_DATE = (8, 15)
Exemples: with SITH_SEMESTER_START_AUTUMN = (8, 15)
Today -> Start date
2015-03-17 -> 2015-02-15
2015-01-11 -> 2014-08-15
@ -135,9 +135,9 @@ class Subscription(models.Model):
return get_start_of_semester(d)
@staticmethod
def compute_end(duration, start=None, user=None):
def compute_end(duration: int, start: date = None, user: User = None) -> date:
"""
This function compute the end date of the subscription given a start date and a duration in number of semestre
This function compute the end date of the subscription given a start date and a duration in number of semester
Exemple:
Start - Duration -> End date
2015-09-18 - 1 -> 2016-03-18
@ -153,7 +153,7 @@ class Subscription(models.Model):
days=math.ceil((6 * duration - round(6 * duration)) * 30),
)
def can_be_edited_by(self, user):
def can_be_edited_by(self, user: User):
return user.is_board_member or user.is_root
def is_valid_now(self):

View File

@ -31,7 +31,7 @@ from django.core.exceptions import ValidationError
from datetime import timedelta, date
from core.models import User
from core.utils import get_start_of_semester, get_semester
from core.utils import get_start_of_semester, get_semester_code
from club.models import Club
@ -164,14 +164,14 @@ class TrombiUser(models.Model):
if m.description:
role += " (%s)" % m.description
if m.end_date:
end_date = get_semester(m.end_date)
end_date = get_semester_code(m.end_date)
else:
end_date = ""
TrombiClubMembership(
user=self,
club=str(m.club),
role=role[:64],
start=get_semester(m.start_date),
start=get_semester_code(m.start_date),
end=end_date,
).save()