mirror of
				https://github.com/ae-utbm/sith.git
				synced 2025-11-04 02:53:06 +00:00 
			
		
		
		
	Compare commits
	
		
			78 Commits
		
	
	
		
			features/m
			...
			feature/im
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					46fa14ed12 | ||
| 
						 | 
					18dffb0053 | ||
| 
						 | 
					6e47d1471e | ||
| 
						 | 
					b5146569e1 | ||
| 
						 | 
					acde993352 | ||
| 
						 | 
					8852ef990e | ||
| 
						 | 
					87295ad9b7 | ||
| 
						 | 
					5ab5ef681c | ||
| 
						 | 
					c9e70889dd | ||
| 
						 | 
					b30ee0a27a | ||
| 
						 | 
					ef968f3673 | ||
| 
						 | 
					96dede5077 | ||
| 
						 | 
					66fcb76cb5 | ||
| 
						 | 
					63c8e51137 | ||
| 
						 | 
					12bec5c553 | ||
| 
						 | 
					08460a6964 | ||
| 
						 | 
					b5a40cfda9 | ||
| 
						 | 
					c78953b036 | ||
| 
						 | 
					427f7ceaff | ||
| 
						 | 
					1055385bcc | ||
| 
						 | 
					c1022642a2 | ||
| 
						 | 
					06253f029c | ||
| 
						 | 
					0501e6417a | ||
| 
						 | 
					a198f5252d | ||
| 
						 | 
					d83842af27 | ||
| 
						 | 
					f605f7dcc6 | ||
| 
						 | 
					8e7c025e47 | ||
| 
						 | 
					1bfe929ab3 | ||
| 
						 | 
					93cc2c883e | ||
| 
						 | 
					44290a20a6 | ||
| 
						 | 
					1f10a284f2 | ||
| 
						 | 
					28f397574f | ||
| 
						 | 
					6c1fa6de0b | ||
| 
						 | 
					f0a08afd31 | ||
| 
						 | 
					9e0b5b0b82 | ||
| 
						 | 
					25c5a3297c | ||
| 
						 | 
					5ea181829e | ||
| 
						 | 
					0cf203669f | ||
| 
						 | 
					559bfcac60 | ||
| 
						 | 
					db8a1ed0ab | ||
| 
						 | 
					16150905a0 | ||
| 
						 | 
					9a376887ac | ||
| 
						 | 
					773808fa59 | ||
| 
						 | 
					c1e59a0676 | ||
| 
						 | 
					05febc60bd | ||
| 
						 | 
					a73fe598ef | ||
| 
						 | 
					585923c827 | ||
| 
						 | 
					394e17d599 | ||
| 
						 | 
					59136850b8 | ||
| 
						 | 
					d726f4b1e8 | ||
| 
						 | 
					705b9b1e6a | ||
| 
						 | 
					31e8ad8a3e | ||
| 
						 | 
					99827e005b | ||
| 
						 | 
					751c8a8bc6 | ||
| 
						 | 
					37216cd16b | ||
| 
						 | 
					dae68638cf | ||
| 
						 | 
					7cadc0bc28 | ||
| 
						 | 
					cce686f3a8 | ||
| 
						 | 
					4fe46fbcef | ||
| 
						 | 
					fe8b8f46aa | ||
| 
						 | 
					7079761ffe | ||
| 
						 | 
					f681c981c6 | ||
| 
						 | 
					5d97146d14 | ||
| 
						 | 
					7b56bd697d | ||
| 
						 | 
					14cd268d69 | ||
| 
						 | 
					754be1c9c9 | ||
| 
						 | 
					da2c155254 | ||
| 
						 | 
					ceb2888f82 | ||
| 
						 | 
					26c94c9ec6 | ||
| 
						 | 
					639197f4c8 | ||
| 
						 | 
					faccc1367f | ||
| 
						 | 
					22b83b0814 | ||
| 
						 | 
					1d82e2a7d9 | ||
| 
						 | 
					b8a72c57e1 | ||
| 
						 | 
					9188565a86 | ||
| 
						 | 
					4d7d22c337 | ||
| 
						 | 
					b58116b023 | ||
| 
						 | 
					fe9e5ce861 | 
							
								
								
									
										8
									
								
								.github/actions/setup_project/action.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										8
									
								
								.github/actions/setup_project/action.yml
									
									
									
									
										vendored
									
									
								
							@@ -6,13 +6,13 @@ runs:
 | 
			
		||||
    - name: Install apt packages
 | 
			
		||||
      uses: awalsh128/cache-apt-pkgs-action@latest
 | 
			
		||||
      with:
 | 
			
		||||
        packages: gettext libgraphviz-dev
 | 
			
		||||
        version: 1.0 # increment to reset cache
 | 
			
		||||
        packages: gettext libxapian-dev libgraphviz-dev
 | 
			
		||||
        version: 1.0  # increment to reset cache
 | 
			
		||||
 | 
			
		||||
    - name: Install dependencies
 | 
			
		||||
      run: |
 | 
			
		||||
        sudo apt update
 | 
			
		||||
        sudo apt install gettext libgraphviz-dev
 | 
			
		||||
        sudo apt install gettext libxapian-dev libgraphviz-dev
 | 
			
		||||
      shell: bash
 | 
			
		||||
 | 
			
		||||
    - name: Set up python
 | 
			
		||||
@@ -25,7 +25,7 @@ runs:
 | 
			
		||||
      uses: actions/cache@v3
 | 
			
		||||
      with:
 | 
			
		||||
        path: ~/.local
 | 
			
		||||
        key: poetry-0 # increment to reset cache
 | 
			
		||||
        key: poetry-0  # increment to reset cache
 | 
			
		||||
 | 
			
		||||
    - name: Install Poetry
 | 
			
		||||
      if: steps.cached-poetry.outputs.cache-hit != 'true'
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										10
									
								
								.github/actions/setup_xapian/action.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								.github/actions/setup_xapian/action.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,10 @@
 | 
			
		||||
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
 | 
			
		||||
							
								
								
									
										16
									
								
								.github/workflows/ci.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										16
									
								
								.github/workflows/ci.yml
									
									
									
									
										vendored
									
									
								
							@@ -2,10 +2,13 @@ name: Sith 3 CI
 | 
			
		||||
 | 
			
		||||
on:
 | 
			
		||||
  push:
 | 
			
		||||
    branches: [master, taiste, features/**]
 | 
			
		||||
    branches:
 | 
			
		||||
      - master
 | 
			
		||||
      - taiste
 | 
			
		||||
  pull_request:
 | 
			
		||||
    branches: [master, taiste]
 | 
			
		||||
  workflow_dispatch:
 | 
			
		||||
    branches:
 | 
			
		||||
      - master
 | 
			
		||||
      - taiste
 | 
			
		||||
 | 
			
		||||
jobs:
 | 
			
		||||
  black:
 | 
			
		||||
@@ -21,16 +24,11 @@ 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
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										3
									
								
								.github/workflows/taiste.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								.github/workflows/taiste.yml
									
									
									
									
										vendored
									
									
								
							@@ -2,8 +2,7 @@ name: Sith3 taiste
 | 
			
		||||
 | 
			
		||||
on:
 | 
			
		||||
  push:
 | 
			
		||||
    branches: [taiste]
 | 
			
		||||
  workflow_dispatch:
 | 
			
		||||
    branches: [ taiste ]
 | 
			
		||||
 | 
			
		||||
jobs:
 | 
			
		||||
  deployment:
 | 
			
		||||
 
 | 
			
		||||
@@ -2,7 +2,7 @@
 | 
			
		||||
{% from 'core/macros.jinja' import user_profile_link, paginate %}
 | 
			
		||||
 | 
			
		||||
{% block content %}
 | 
			
		||||
<h3>{% trans %}Sales{% endtrans %}</h3>
 | 
			
		||||
<h3>{% trans %}Sellings{% endtrans %}</h3>
 | 
			
		||||
<form id="form" action="?page=1" method="post">
 | 
			
		||||
    {% csrf_token %}
 | 
			
		||||
    {{ form }}
 | 
			
		||||
 
 | 
			
		||||
@@ -30,7 +30,7 @@
 | 
			
		||||
    {% endif %}
 | 
			
		||||
    </ul>
 | 
			
		||||
    {% if object.club_account.exists() %}
 | 
			
		||||
        <h4>{% trans %}Accounting: {% endtrans %}</h4>
 | 
			
		||||
        <h4>{% trans %}Accouting: {% 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>
 | 
			
		||||
 
 | 
			
		||||
@@ -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 moderated again){% endtrans %}</a></p>
 | 
			
		||||
        <p> <a href="{{ url('com:news_edit', news_id=news.id) }}">{% trans %}Edit (will be remoderated){% endtrans %}</a></p>
 | 
			
		||||
        {% endif %}
 | 
			
		||||
        </div>
 | 
			
		||||
        </div>
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										27
									
								
								com/tests.py
									
									
									
									
									
								
							
							
						
						
									
										27
									
								
								com/tests.py
									
									
									
									
									
								
							@@ -159,33 +159,6 @@ 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
 | 
			
		||||
 
 | 
			
		||||
@@ -180,15 +180,14 @@ 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
 | 
			
		||||
    :raise ValueError: If no group matches the criteria
 | 
			
		||||
    :raises ValueError: If no group matches the criteria
 | 
			
		||||
    """
 | 
			
		||||
    if pk is None and name is None:
 | 
			
		||||
        raise ValueError("Either pk or name must be set")
 | 
			
		||||
 | 
			
		||||
    # replace space characters to hide warnings with memcached backend
 | 
			
		||||
    pk_or_name: Union[str, int] = pk if pk is not None else name.replace(" ", "_")
 | 
			
		||||
    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
 | 
			
		||||
    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
 | 
			
		||||
@@ -810,10 +809,6 @@ 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.
										
									
								
							
										
											Binary file not shown.
										
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| 
		 Before Width: | Height: | Size: 434 KiB  | 
										
											Binary file not shown.
										
									
								
							
										
											Binary file not shown.
										
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										1232
									
								
								core/static/core/font-awesome/js/fontawesome.min.js
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1232
									
								
								core/static/core/font-awesome/js/fontawesome.min.js
									
									
									
									
										vendored
									
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
							
								
								
									
										165
									
								
								core/static/core/fontawesome/LICENSE.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										165
									
								
								core/static/core/fontawesome/LICENSE.txt
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,165 @@
 | 
			
		||||
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.**
 | 
			
		||||
							
								
								
									
										9
									
								
								core/static/core/fontawesome/css/all.min.css
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								core/static/core/fontawesome/css/all.min.css
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
							
								
								
									
										6
									
								
								core/static/core/fontawesome/css/brands.min.css
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								core/static/core/fontawesome/css/brands.min.css
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
							
								
								
									
										9
									
								
								core/static/core/fontawesome/css/fontawesome.min.css
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								core/static/core/fontawesome/css/fontawesome.min.css
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
							
								
								
									
										6
									
								
								core/static/core/fontawesome/css/regular.min.css
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								core/static/core/fontawesome/css/regular.min.css
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,6 @@
 | 
			
		||||
/*!
 | 
			
		||||
 * 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}
 | 
			
		||||
							
								
								
									
										6
									
								
								core/static/core/fontawesome/css/solid.min.css
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								core/static/core/fontawesome/css/solid.min.css
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,6 @@
 | 
			
		||||
/*!
 | 
			
		||||
 * 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}
 | 
			
		||||
							
								
								
									
										6
									
								
								core/static/core/fontawesome/css/svg-with-js.min.css
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								core/static/core/fontawesome/css/svg-with-js.min.css
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
							
								
								
									
										6
									
								
								core/static/core/fontawesome/css/v4-font-face.min.css
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								core/static/core/fontawesome/css/v4-font-face.min.css
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,6 @@
 | 
			
		||||
/*!
 | 
			
		||||
 * 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}
 | 
			
		||||
							
								
								
									
										6
									
								
								core/static/core/fontawesome/css/v4-shims.min.css
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								core/static/core/fontawesome/css/v4-shims.min.css
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
							
								
								
									
										6
									
								
								core/static/core/fontawesome/css/v5-font-face.min.css
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								core/static/core/fontawesome/css/v5-font-face.min.css
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,6 @@
 | 
			
		||||
/*!
 | 
			
		||||
 * 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")}
 | 
			
		||||
							
								
								
									
										6
									
								
								core/static/core/fontawesome/js/all.min.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								core/static/core/fontawesome/js/all.min.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
							
								
								
									
										6
									
								
								core/static/core/fontawesome/js/brands.min.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								core/static/core/fontawesome/js/brands.min.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
							
								
								
									
										6
									
								
								core/static/core/fontawesome/js/conflict-detection.min.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								core/static/core/fontawesome/js/conflict-detection.min.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
							
								
								
									
										6
									
								
								core/static/core/fontawesome/js/fontawesome.min.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								core/static/core/fontawesome/js/fontawesome.min.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
							
								
								
									
										6
									
								
								core/static/core/fontawesome/js/regular.min.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								core/static/core/fontawesome/js/regular.min.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
							
								
								
									
										6
									
								
								core/static/core/fontawesome/js/solid.min.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								core/static/core/fontawesome/js/solid.min.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
							
								
								
									
										6
									
								
								core/static/core/fontawesome/js/v4-shims.min.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								core/static/core/fontawesome/js/v4-shims.min.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								core/static/core/fontawesome/webfonts/fa-brands-400.ttf
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								core/static/core/fontawesome/webfonts/fa-brands-400.ttf
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								core/static/core/fontawesome/webfonts/fa-brands-400.woff2
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								core/static/core/fontawesome/webfonts/fa-brands-400.woff2
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								core/static/core/fontawesome/webfonts/fa-regular-400.ttf
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								core/static/core/fontawesome/webfonts/fa-regular-400.ttf
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								core/static/core/fontawesome/webfonts/fa-regular-400.woff2
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								core/static/core/fontawesome/webfonts/fa-regular-400.woff2
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								core/static/core/fontawesome/webfonts/fa-solid-900.ttf
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								core/static/core/fontawesome/webfonts/fa-solid-900.ttf
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								core/static/core/fontawesome/webfonts/fa-solid-900.woff2
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								core/static/core/fontawesome/webfonts/fa-solid-900.woff2
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								core/static/core/fontawesome/webfonts/fa-v4compatibility.ttf
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								core/static/core/fontawesome/webfonts/fa-v4compatibility.ttf
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								core/static/core/fontawesome/webfonts/fa-v4compatibility.woff2
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								core/static/core/fontawesome/webfonts/fa-v4compatibility.woff2
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							@@ -191,13 +191,157 @@ $hovered-red-text-color: #ff4d4d;
 | 
			
		||||
    >.right {
 | 
			
		||||
      flex: 1;
 | 
			
		||||
      justify-content: flex-end;
 | 
			
		||||
      gap: 10px;
 | 
			
		||||
 | 
			
		||||
      >.links {
 | 
			
		||||
        display: flex;
 | 
			
		||||
        justify-content: flex-end;
 | 
			
		||||
        align-items: center;
 | 
			
		||||
        height: 40px;
 | 
			
		||||
        gap: 10px;
 | 
			
		||||
    
 | 
			
		||||
        @media (max-width: 1200px) {
 | 
			
		||||
          justify-content: flex-start;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        @media (max-width: 375px) {
 | 
			
		||||
          height: 30px; 
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        > * {
 | 
			
		||||
          display: flex;
 | 
			
		||||
          justify-content: center;
 | 
			
		||||
          align-items: center;
 | 
			
		||||
          width: 30px;
 | 
			
		||||
          height: 30px;
 | 
			
		||||
          font-size: 20px;
 | 
			
		||||
 | 
			
		||||
          > * {
 | 
			
		||||
            font-size: 20px;
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        >.notification {
 | 
			
		||||
          position: relative;
 | 
			
		||||
      
 | 
			
		||||
          >a {
 | 
			
		||||
            color: $text-color;
 | 
			
		||||
            position: relative;
 | 
			
		||||
      
 | 
			
		||||
            &:hover {
 | 
			
		||||
              color: $hovered-text-color;
 | 
			
		||||
            }
 | 
			
		||||
      
 | 
			
		||||
            >span {
 | 
			
		||||
              color: $text-color;
 | 
			
		||||
              font-size: 14px;
 | 
			
		||||
              display: flex;
 | 
			
		||||
              justify-content: center;
 | 
			
		||||
              align-items: center;
 | 
			
		||||
              width: 10px;
 | 
			
		||||
              height: 10px;
 | 
			
		||||
              padding: 5px;
 | 
			
		||||
              background-color: $red-text-color;
 | 
			
		||||
              border-radius: 50%;
 | 
			
		||||
              position: absolute;
 | 
			
		||||
              top: -50%;
 | 
			
		||||
              right: -50%;
 | 
			
		||||
            }
 | 
			
		||||
          }
 | 
			
		||||
      
 | 
			
		||||
          >#header_notif {
 | 
			
		||||
            box-sizing: border-box;
 | 
			
		||||
            display: none;
 | 
			
		||||
            position: absolute;
 | 
			
		||||
            margin: 0;
 | 
			
		||||
            background-color: whitesmoke;
 | 
			
		||||
            top: calc(100% + 10px);
 | 
			
		||||
            right: calc(50% - 30px);
 | 
			
		||||
            width: 400px;
 | 
			
		||||
            max-width: calc(100vw - 30px);
 | 
			
		||||
            padding: 10px;
 | 
			
		||||
            z-index: 100;
 | 
			
		||||
            border-radius: 10px;
 | 
			
		||||
            box-shadow: 3px 3px 3px 0 #767676;
 | 
			
		||||
      
 | 
			
		||||
            >ul {
 | 
			
		||||
              list-style-type: none;
 | 
			
		||||
              margin: 0;
 | 
			
		||||
              display: flex;
 | 
			
		||||
              flex-direction: column;
 | 
			
		||||
              gap: 10px;
 | 
			
		||||
              min-height: 20px;
 | 
			
		||||
              max-height: 120px;
 | 
			
		||||
              overflow-y: auto;
 | 
			
		||||
      
 | 
			
		||||
              >li {
 | 
			
		||||
                >a {
 | 
			
		||||
                  .datetime {
 | 
			
		||||
                    display: flex;
 | 
			
		||||
                    justify-content: flex-start;
 | 
			
		||||
                    gap: 10px;
 | 
			
		||||
                    font-style: italic;
 | 
			
		||||
                    font-size: .8em;
 | 
			
		||||
                  }
 | 
			
		||||
                }
 | 
			
		||||
      
 | 
			
		||||
                &.empty-notification {
 | 
			
		||||
                  text-align: center;
 | 
			
		||||
                  font-style: italic;
 | 
			
		||||
                }
 | 
			
		||||
              }
 | 
			
		||||
            }
 | 
			
		||||
      
 | 
			
		||||
            .options {
 | 
			
		||||
              width: 100%;
 | 
			
		||||
              display: flex;
 | 
			
		||||
              justify-content: space-between;
 | 
			
		||||
              margin-top: 10px;
 | 
			
		||||
              gap: 5px;
 | 
			
		||||
      
 | 
			
		||||
              >a {
 | 
			
		||||
                color: black;
 | 
			
		||||
                padding: 5px;
 | 
			
		||||
                width: 50%;
 | 
			
		||||
                display: flex;
 | 
			
		||||
                justify-content: center;
 | 
			
		||||
                text-align: center;
 | 
			
		||||
                align-items: center;
 | 
			
		||||
                border-radius: 5px;
 | 
			
		||||
                background-color: #ddd;
 | 
			
		||||
      
 | 
			
		||||
                &:hover {
 | 
			
		||||
                  background-color: rgba(0, 0, 0, .2);
 | 
			
		||||
                }
 | 
			
		||||
              }
 | 
			
		||||
            }
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
    
 | 
			
		||||
        >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: 15px;
 | 
			
		||||
        gap: 10px;
 | 
			
		||||
 | 
			
		||||
        @media (max-width: 1200px) {
 | 
			
		||||
          width: 100%;
 | 
			
		||||
@@ -215,162 +359,27 @@ $hovered-red-text-color: #ff4d4d;
 | 
			
		||||
          background-repeat: no-repeat;
 | 
			
		||||
          background-color: $background-color;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        >.options {
 | 
			
		||||
        
 | 
			
		||||
        >.username {
 | 
			
		||||
          display: flex;
 | 
			
		||||
          flex-direction: column;
 | 
			
		||||
          justify-content: flex-end;
 | 
			
		||||
          gap: 5px;
 | 
			
		||||
 | 
			
		||||
          >.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;
 | 
			
		||||
              }
 | 
			
		||||
            }
 | 
			
		||||
          @media (max-width: 500px) {
 | 
			
		||||
            justify-content: flex-start;
 | 
			
		||||
          }
 | 
			
		||||
 | 
			
		||||
          >.links {
 | 
			
		||||
            width: 100%;
 | 
			
		||||
            display: flex;
 | 
			
		||||
            justify-content: flex-end;
 | 
			
		||||
            gap: 15px;
 | 
			
		||||
 | 
			
		||||
            @media (max-width: 1200px) {
 | 
			
		||||
              justify-content: flex-start;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            >a {
 | 
			
		||||
              text-align: right;
 | 
			
		||||
              color: $text-color;
 | 
			
		||||
 | 
			
		||||
              &:hover {
 | 
			
		||||
                color: $hovered-text-color;
 | 
			
		||||
              }
 | 
			
		||||
 | 
			
		||||
              &:last-child {
 | 
			
		||||
                color: $red-text-color;
 | 
			
		||||
 | 
			
		||||
                &: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;
 | 
			
		||||
          }
 | 
			
		||||
 | 
			
		||||
          >span {
 | 
			
		||||
          >a {
 | 
			
		||||
            color: $text-color;
 | 
			
		||||
            font-size: 14px;
 | 
			
		||||
            display: flex;
 | 
			
		||||
            justify-content: center;
 | 
			
		||||
            align-items: center;
 | 
			
		||||
            width: 10px;
 | 
			
		||||
            height: 10px;
 | 
			
		||||
            padding: 5px;
 | 
			
		||||
            background-color: $red-text-color;
 | 
			
		||||
            border-radius: 50%;
 | 
			
		||||
            position: absolute;
 | 
			
		||||
            top: -50%;
 | 
			
		||||
            right: -50%;
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        >#header_notif {
 | 
			
		||||
          box-sizing: border-box;
 | 
			
		||||
          display: none;
 | 
			
		||||
          position: absolute;
 | 
			
		||||
          margin: 0;
 | 
			
		||||
          background-color: whitesmoke;
 | 
			
		||||
          top: calc(100% + 10px);
 | 
			
		||||
          right: calc(50% - 30px);
 | 
			
		||||
          width: 400px;
 | 
			
		||||
          max-width: calc(100vw - 30px);
 | 
			
		||||
          padding: 10px;
 | 
			
		||||
          z-index: 100;
 | 
			
		||||
          border-radius: 10px;
 | 
			
		||||
          box-shadow: 3px 3px 3px 0 #767676;
 | 
			
		||||
 | 
			
		||||
          >ul {
 | 
			
		||||
            list-style-type: none;
 | 
			
		||||
            margin: 0;
 | 
			
		||||
            display: flex;
 | 
			
		||||
            flex-direction: column;
 | 
			
		||||
            gap: 10px;
 | 
			
		||||
            min-height: 20px;
 | 
			
		||||
            max-height: 120px;
 | 
			
		||||
            overflow-y: auto;
 | 
			
		||||
 | 
			
		||||
            >li {
 | 
			
		||||
              >a {
 | 
			
		||||
                .datetime {
 | 
			
		||||
                  display: flex;
 | 
			
		||||
                  justify-content: flex-start;
 | 
			
		||||
                  gap: 10px;
 | 
			
		||||
                  font-style: italic;
 | 
			
		||||
                  font-size: .8em;
 | 
			
		||||
                }
 | 
			
		||||
              }
 | 
			
		||||
 | 
			
		||||
              &.empty-notification {
 | 
			
		||||
                text-align: center;
 | 
			
		||||
                font-style: italic;
 | 
			
		||||
              }
 | 
			
		||||
            }
 | 
			
		||||
          }
 | 
			
		||||
 | 
			
		||||
          .options {
 | 
			
		||||
            width: 100%;
 | 
			
		||||
            display: flex;
 | 
			
		||||
            justify-content: space-between;
 | 
			
		||||
            margin-top: 10px;
 | 
			
		||||
            gap: 5px;
 | 
			
		||||
 | 
			
		||||
            >a {
 | 
			
		||||
              color: black;
 | 
			
		||||
              padding: 5px;
 | 
			
		||||
              width: 50%;
 | 
			
		||||
              display: flex;
 | 
			
		||||
              justify-content: center;
 | 
			
		||||
              text-align: center;
 | 
			
		||||
              align-items: center;
 | 
			
		||||
              border-radius: 5px;
 | 
			
		||||
              background-color: #ddd;
 | 
			
		||||
 | 
			
		||||
              &:hover {
 | 
			
		||||
                background-color: rgba(0, 0, 0, .2);
 | 
			
		||||
              }
 | 
			
		||||
            &:hover {
 | 
			
		||||
              color: $hovered-text-color;
 | 
			
		||||
            }
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    >.left {
 | 
			
		||||
 
 | 
			
		||||
@@ -1031,7 +1031,7 @@ thead {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
tbody > tr {
 | 
			
		||||
  &:nth-child(even):not(.highlight) {
 | 
			
		||||
  &:nth-child(even) {
 | 
			
		||||
    background: $primary-neutral-light-color;
 | 
			
		||||
  }
 | 
			
		||||
  &.clickable:hover {
 | 
			
		||||
 
 | 
			
		||||
@@ -16,9 +16,10 @@
 | 
			
		||||
                {# 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 %}
 | 
			
		||||
            <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>
 | 
			
		||||
            <!-- 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>
 | 
			
		||||
 | 
			
		||||
            <!-- Jquery declared here to be accessible in every django widgets -->
 | 
			
		||||
            <script src="{{ static('core/js/jquery-3.6.2.min.js') }}"></script>
 | 
			
		||||
@@ -72,11 +73,11 @@
 | 
			
		||||
                                                    <a href="{{ url('counter:activity', counter_id=bar.id) }}">
 | 
			
		||||
                                                {% endif %}
 | 
			
		||||
                                                {% if bar.is_inactive() %}
 | 
			
		||||
                                                  <i class="fa fa-question" style="color: #f39c12"></i>
 | 
			
		||||
                                                  <i class="fa-solid fa-question" style="color: #f39c12"></i>
 | 
			
		||||
                                                {% elif bar.is_open(): %}
 | 
			
		||||
                                                  <i class="fa fa-check" style="color: #2ecc71"></i>
 | 
			
		||||
                                                  <i class="fa-solid fa-check" style="color: #2ecc71"></i>
 | 
			
		||||
                                                {% else %}
 | 
			
		||||
                                                  <i class="fa fa-times" style="color: #eb2f06"></i>
 | 
			
		||||
                                                  <i class="fa-solid fa-xmark" style="color: #eb2f06"></i>
 | 
			
		||||
                                                {% endif %}
 | 
			
		||||
                                                <span>{{ bar }}</span>
 | 
			
		||||
                                                </a>
 | 
			
		||||
@@ -87,14 +88,8 @@
 | 
			
		||||
                            </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 class="username">
 | 
			
		||||
                                        <a href="{{ url('core:user_profile', user_id=user.id) }}">{{ user.get_display_name() }}</a>
 | 
			
		||||
                                    </div>
 | 
			
		||||
                                    <a 
 | 
			
		||||
                                        href="{{ url('core:user_profile', user_id=user.id) }}"
 | 
			
		||||
@@ -105,54 +100,54 @@
 | 
			
		||||
                                        {% endif %}                                  
 | 
			
		||||
                                    ></a>
 | 
			
		||||
                                </div>
 | 
			
		||||
                                <div class="notification">
 | 
			
		||||
                                    <a href="#" onclick="display_notif()">
 | 
			
		||||
                                        <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 %}
 | 
			
		||||
                                                 
 | 
			
		||||
                                            {% endif %}
 | 
			
		||||
                                        </span>
 | 
			
		||||
                                        {% endif %}
 | 
			
		||||
                                    </a>
 | 
			
		||||
                                    <div id="header_notif">
 | 
			
		||||
                                        <ul>
 | 
			
		||||
                                            {% if user.notifications.filter(viewed=False).count() > 0 %}
 | 
			
		||||
                                <div class="links">
 | 
			
		||||
                                    <div class="notification">
 | 
			
		||||
                                        <a href="#" onclick="display_notif()">
 | 
			
		||||
                                            <i class="fa-solid fa-bell"></i>
 | 
			
		||||
                                            {% set notification_count = user.notifications.filter(viewed=False).count() %}
 | 
			
		||||
                                    
 | 
			
		||||
                                            {% if notification_count > 0 %}
 | 
			
		||||
                                            <span>
 | 
			
		||||
                                                {% if notification_count < 100 %} {{ notification_count }} {% else %}   {% endif %} </span>
 | 
			
		||||
                                                    {% endif %}
 | 
			
		||||
                                        </a>
 | 
			
		||||
                                        <div id="header_notif">
 | 
			
		||||
                                            <ul>
 | 
			
		||||
                                                {% 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) }}">
 | 
			
		||||
                                                            <div class="datetime">
 | 
			
		||||
                                                                <span class="header_notif_date">
 | 
			
		||||
                                                                    {{ n.date|localtime|date(DATE_FORMAT) }}
 | 
			
		||||
                                                                </span>
 | 
			
		||||
                                                                <span class="header_notif_time">
 | 
			
		||||
                                                                    {{ n.date|localtime|time(DATETIME_FORMAT) }}
 | 
			
		||||
                                                                </span>
 | 
			
		||||
                                                            </div>
 | 
			
		||||
                                                            <div class="reason">
 | 
			
		||||
                                                                {{ n }}
 | 
			
		||||
                                                            </div>
 | 
			
		||||
                                                        </a>
 | 
			
		||||
                                                    </li>
 | 
			
		||||
                                                <li>
 | 
			
		||||
                                                    <a href="{{ url('core:notification', notif_id=n.id) }}">
 | 
			
		||||
                                                        <div class="datetime">
 | 
			
		||||
                                                            <span class="header_notif_date">
 | 
			
		||||
                                                                {{ n.date|localtime|date(DATE_FORMAT) }}
 | 
			
		||||
                                                            </span>
 | 
			
		||||
                                                            <span class="header_notif_time">
 | 
			
		||||
                                                                {{ n.date|localtime|time(DATETIME_FORMAT) }}
 | 
			
		||||
                                                            </span>
 | 
			
		||||
                                                        </div>
 | 
			
		||||
                                                        <div class="reason">
 | 
			
		||||
                                                            {{ n }}
 | 
			
		||||
                                                        </div>
 | 
			
		||||
                                                    </a>
 | 
			
		||||
                                                </li>
 | 
			
		||||
                                                {% endfor %}
 | 
			
		||||
                                            {% else %}
 | 
			
		||||
                                                {% else %}
 | 
			
		||||
                                                <li class="empty-notification">{% trans %}You do not have any unread notification{% endtrans %}</li>
 | 
			
		||||
                                            {% endif %}
 | 
			
		||||
                                        </ul>
 | 
			
		||||
                                        <div class="options">
 | 
			
		||||
                                            <a href="{{ url('core:notification_list') }}">
 | 
			
		||||
                                                {% trans %}View more{% endtrans %}
 | 
			
		||||
                                            </a>
 | 
			
		||||
                                            <a href="{{ url('core:notification_list') }}?see_all">
 | 
			
		||||
                                                {% trans %}Mark all as read{% endtrans %}
 | 
			
		||||
                                            </a>
 | 
			
		||||
                                                {% endif %}
 | 
			
		||||
                                            </ul>
 | 
			
		||||
                                            <div class="options">
 | 
			
		||||
                                                <a href="{{ url('core:notification_list') }}">
 | 
			
		||||
                                                    {% trans %}View more{% endtrans %}
 | 
			
		||||
                                                </a>
 | 
			
		||||
                                                <a href="{{ url('core:notification_list') }}?see_all">
 | 
			
		||||
                                                    {% trans %}Mark all as read{% endtrans %}
 | 
			
		||||
                                                </a>
 | 
			
		||||
                                            </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>
 | 
			
		||||
@@ -195,7 +190,7 @@
 | 
			
		||||
        {% block nav %}
 | 
			
		||||
            {% if not popup %}
 | 
			
		||||
                <nav class="navbar">
 | 
			
		||||
                    <button class="expand-button" onclick="showMenu()"><i class="fa fa-bars"></i></button>
 | 
			
		||||
                    <button class="expand-button" onclick="showMenu()"><i class="fa-solid 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">
 | 
			
		||||
@@ -203,7 +198,11 @@
 | 
			
		||||
                            <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='utbm-associations') }}">{% trans %}Others UTBM's Associations{% 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>
 | 
			
		||||
                            </ul>
 | 
			
		||||
                        </div>
 | 
			
		||||
                        <div class="menu">
 | 
			
		||||
 
 | 
			
		||||
@@ -4,9 +4,9 @@
 | 
			
		||||
{% block file %}
 | 
			
		||||
<h3>
 | 
			
		||||
{% if file.is_folder %}
 | 
			
		||||
<i class="fa fa-folder fa-3x" aria-hidden="true"></i>
 | 
			
		||||
<i class="fa-solid fa-folder-open"></i>
 | 
			
		||||
{% else %}
 | 
			
		||||
<i class="fa fa-file fa-3x" aria-hidden="true"></i>
 | 
			
		||||
<i class="fa-solid fa-file"></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 fa-folder" aria-hidden="true"></i>
 | 
			
		||||
                <i class="fa-solid fa-folder"></i>
 | 
			
		||||
            {% else %}
 | 
			
		||||
                <i class="fa fa-file" aria-hidden="true"></i>
 | 
			
		||||
                <i class="fa-solid fa-file"></i>
 | 
			
		||||
            {% endif %}
 | 
			
		||||
            <a href="{{ url('core:file_detail', file_id=f.id, popup=popup) }}">{{ f.get_display_name() }}</a></li>
 | 
			
		||||
        {% endfor %}
 | 
			
		||||
 
 | 
			
		||||
@@ -8,9 +8,9 @@
 | 
			
		||||
    {% for f in file_list %}
 | 
			
		||||
    <li style="list-style-type: none;">
 | 
			
		||||
        {% if f.is_folder %}
 | 
			
		||||
			<i class="fa fa-folder" aria-hidden="true"></i>
 | 
			
		||||
            <i class="fa-solid fa-folder"></i>
 | 
			
		||||
        {% else %}
 | 
			
		||||
			<i class="fa fa-file" aria-hidden="true"></i>
 | 
			
		||||
            <i class="fa-solid fa-file"></i>
 | 
			
		||||
        {% endif %}
 | 
			
		||||
        <a href="{{ url('core:file_detail', file_id=f.id, popup=popup) }}">{{ f.name }}</a></li>
 | 
			
		||||
    {% endfor %}
 | 
			
		||||
 
 | 
			
		||||
@@ -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 fa-facebook-square fa-2x"></a>
 | 
			
		||||
<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>
 | 
			
		||||
{%- endmacro %}
 | 
			
		||||
 | 
			
		||||
{% macro tweet_quick(news) -%}
 | 
			
		||||
<a rel="nofollow" target="#" href="https://twitter.com/intent/tweet?text={{ news.get_full_url() }}" class="twitter fa fa-twitter-square fa-2x"></a>
 | 
			
		||||
<a rel="nofollow" target="#" href="https://twitter.com/intent/tweet?text={{ news.get_full_url() }}" class="twitter fa-brands fa-twitter fa-2x"></a>
 | 
			
		||||
{%- endmacro %}
 | 
			
		||||
 | 
			
		||||
{% macro user_mini_profile(user) %}
 | 
			
		||||
 
 | 
			
		||||
@@ -40,25 +40,25 @@
 | 
			
		||||
					{
 | 
			
		||||
						name: "heading-smaller",
 | 
			
		||||
						action: EasyMDE.toggleHeadingSmaller,
 | 
			
		||||
						className: "fa fa-header",
 | 
			
		||||
						className: "fa-solid fa-heading",
 | 
			
		||||
						title: "{{ translations.heading_smaller }}"
 | 
			
		||||
					},
 | 
			
		||||
					{
 | 
			
		||||
						name: "italic",
 | 
			
		||||
						action: EasyMDE.toggleItalic,
 | 
			
		||||
						className: "fa fa-italic",
 | 
			
		||||
						className: "fa-solid fa-italic",
 | 
			
		||||
						title: "{{ translations.italic }}"
 | 
			
		||||
					},
 | 
			
		||||
					{
 | 
			
		||||
						name: "bold",
 | 
			
		||||
						action: EasyMDE.toggleBold,
 | 
			
		||||
						className: "fa fa-bold",
 | 
			
		||||
						className: "fa-solid fa-bold",
 | 
			
		||||
						title: "{{ translations.bold }}"
 | 
			
		||||
					},
 | 
			
		||||
					{
 | 
			
		||||
						name: "strikethrough",
 | 
			
		||||
						action: EasyMDE.toggleStrikethrough,
 | 
			
		||||
						className: "fa fa-strikethrough",
 | 
			
		||||
						className: "fa-solid fa-strikethrough",
 | 
			
		||||
						title: "{{ translations.strikethrough }}"
 | 
			
		||||
					},
 | 
			
		||||
					{
 | 
			
		||||
@@ -67,7 +67,7 @@
 | 
			
		||||
							let cm = editor.codemirror;
 | 
			
		||||
							cm.replaceSelection('__' + cm.getSelection() + '__');
 | 
			
		||||
						},
 | 
			
		||||
						className: "fa fa-underline",
 | 
			
		||||
						className: "fa-solid fa-underline",
 | 
			
		||||
						title: "{{ translations.underline }}"
 | 
			
		||||
					},
 | 
			
		||||
					{
 | 
			
		||||
@@ -76,7 +76,7 @@
 | 
			
		||||
							let cm = editor.codemirror;
 | 
			
		||||
							cm.replaceSelection('<sup>' + cm.getSelection() + '</sup>');
 | 
			
		||||
						},
 | 
			
		||||
						className: "fa fa-superscript",
 | 
			
		||||
						className: "fa-solid fa-superscript",
 | 
			
		||||
						title: "{{ translations.superscript }}"
 | 
			
		||||
					},
 | 
			
		||||
					{
 | 
			
		||||
@@ -85,84 +85,84 @@
 | 
			
		||||
							let cm = editor.codemirror;
 | 
			
		||||
							cm.replaceSelection('<sub>' + cm.getSelection() + '</sub>');
 | 
			
		||||
						},
 | 
			
		||||
						className: "fa fa-subscript",
 | 
			
		||||
						className: "fa-solid fa-subscript",
 | 
			
		||||
						title: "{{ translations.subscript }}"
 | 
			
		||||
					},
 | 
			
		||||
					{
 | 
			
		||||
						name: "code",
 | 
			
		||||
						action: EasyMDE.toggleCodeBlock,
 | 
			
		||||
						className: "fa fa-code",
 | 
			
		||||
						className: "fa-solid fa-code",
 | 
			
		||||
						title: "{{ translations.code }}"
 | 
			
		||||
					},
 | 
			
		||||
					"|",
 | 
			
		||||
					{
 | 
			
		||||
						name: "quote",
 | 
			
		||||
						action: EasyMDE.toggleBlockquote,
 | 
			
		||||
						className: "fa fa-quote-left",
 | 
			
		||||
						className: "fa-solid fa-quote-left",
 | 
			
		||||
						title: "{{ translations.quote }}"
 | 
			
		||||
					},
 | 
			
		||||
					{
 | 
			
		||||
						name: "unordered-list",
 | 
			
		||||
						action: EasyMDE.toggleUnorderedList,
 | 
			
		||||
						className: "fa fa-list-ul",
 | 
			
		||||
						className: "fa-solid fa-list-ul",
 | 
			
		||||
						title: "{{ translations.unordered_list }}"
 | 
			
		||||
					},
 | 
			
		||||
					{
 | 
			
		||||
						name: "ordered-list",
 | 
			
		||||
						action: EasyMDE.toggleOrderedList,
 | 
			
		||||
						className: "fa fa-list-ol",
 | 
			
		||||
						className: "fa-solid fa-list-ol",
 | 
			
		||||
						title: "{{ translations.ordered_list }}"
 | 
			
		||||
					},
 | 
			
		||||
					"|",
 | 
			
		||||
					{
 | 
			
		||||
						name: "link",
 | 
			
		||||
						action: EasyMDE.drawLink,
 | 
			
		||||
						className: "fa fa-link",
 | 
			
		||||
						className: "fa-solid fa-link",
 | 
			
		||||
						title: "{{ translations.link }}"
 | 
			
		||||
					},
 | 
			
		||||
					{
 | 
			
		||||
						name: "image",
 | 
			
		||||
						action: EasyMDE.drawImage,
 | 
			
		||||
						className: "fa fa-picture-o",
 | 
			
		||||
						className: "fa-solid fa-image",
 | 
			
		||||
						title: "{{ translations.image }}"
 | 
			
		||||
					},
 | 
			
		||||
					{
 | 
			
		||||
						name: "table",
 | 
			
		||||
						action: EasyMDE.drawTable,
 | 
			
		||||
						className: "fa fa-table",
 | 
			
		||||
						className: "fa-solid fa-table",
 | 
			
		||||
						title: "{{ translations.table }}"
 | 
			
		||||
					},
 | 
			
		||||
					"|",
 | 
			
		||||
					{
 | 
			
		||||
						name: "clean-block",
 | 
			
		||||
						action: EasyMDE.cleanBlock,
 | 
			
		||||
						className: "fa fa-eraser fa-clean-block",
 | 
			
		||||
						className: "fa-solid fa-eraser",
 | 
			
		||||
						title: "{{ translations.clean_block }}"
 | 
			
		||||
					},
 | 
			
		||||
					"|",
 | 
			
		||||
					{
 | 
			
		||||
						name: "preview",
 | 
			
		||||
						action: EasyMDE.togglePreview,
 | 
			
		||||
						className: "fa fa-eye no-disable",
 | 
			
		||||
						className: "fa-solid fa-eye no-disable",
 | 
			
		||||
						title: "{{ translations.preview }}"
 | 
			
		||||
					},
 | 
			
		||||
					{
 | 
			
		||||
						name: "side-by-side",
 | 
			
		||||
						action: EasyMDE.toggleSideBySide,
 | 
			
		||||
						className: "fa fa-columns no-disable no-mobile",
 | 
			
		||||
						className: "fa-solid fa-columns no-disable no-mobile",
 | 
			
		||||
						title: "{{ translations.side_by_side }}"
 | 
			
		||||
					},
 | 
			
		||||
					{
 | 
			
		||||
						name: "fullscreen",
 | 
			
		||||
						action: EasyMDE.toggleFullScreen,
 | 
			
		||||
						className: "fa fa-arrows-alt no-disable no-mobile",
 | 
			
		||||
						className: "fa-solid fa-arrows-alt no-disable no-mobile",
 | 
			
		||||
						title: "{{ translations.fullscreen }}"
 | 
			
		||||
					},
 | 
			
		||||
					"|",
 | 
			
		||||
					{
 | 
			
		||||
						name: "guide",
 | 
			
		||||
						action: "/page/Aide_sur_la_syntaxe",
 | 
			
		||||
						className: "fa fa-question-circle",
 | 
			
		||||
						className: "fa-solid fa-question-circle",
 | 
			
		||||
						title: "{{ translations.guide }}"
 | 
			
		||||
					},
 | 
			
		||||
				]
 | 
			
		||||
 
 | 
			
		||||
@@ -17,7 +17,7 @@
 | 
			
		||||
 | 
			
		||||
    {% if user_registered %}
 | 
			
		||||
        {% trans user_name=user_registered.get_display_name() %}Welcome {{ user_name }}!{% endtrans %}<br>
 | 
			
		||||
        {% trans %}You successfully registered and you will soon receive a confirmation mail.{% endtrans %}<br>
 | 
			
		||||
        {% trans %}You successfully registred and you will soon receive a confirmation mail.{% endtrans %}<br>
 | 
			
		||||
        {% trans username=user_registered.username %}Your username is {{ username }}.{% endtrans %}<br>
 | 
			
		||||
    
 | 
			
		||||
    {% else %}
 | 
			
		||||
 
 | 
			
		||||
@@ -41,11 +41,11 @@
 | 
			
		||||
{% set refilled = customer.refillings.exists() %}
 | 
			
		||||
{% if bought or refilled %}
 | 
			
		||||
    {% if bought %}
 | 
			
		||||
    <h5>{% trans %}Account purchases{% endtrans %}</h5>
 | 
			
		||||
    <h5>{% trans %}Account buyings{% endtrans %}</h5>
 | 
			
		||||
        {{ monthly(buyings_month) }}
 | 
			
		||||
    {% endif %}
 | 
			
		||||
    {% if refilled %}
 | 
			
		||||
    <h5>{% trans %}Reloads{% endtrans %}</h5>
 | 
			
		||||
    <h5>{% trans %}Refillings{% endtrans %}</h5>
 | 
			
		||||
        {{ monthly(refilling_month) }}
 | 
			
		||||
    {% endif %}
 | 
			
		||||
{% endif %}
 | 
			
		||||
 
 | 
			
		||||
@@ -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 purchases{% endtrans %}</h4>
 | 
			
		||||
<h4>{% trans %}Account buyings{% endtrans %}</h4>
 | 
			
		||||
<table>
 | 
			
		||||
    <thead>
 | 
			
		||||
        <tr>
 | 
			
		||||
@@ -43,7 +43,7 @@
 | 
			
		||||
</table>
 | 
			
		||||
{% endif %}
 | 
			
		||||
{% if customer.refillings.exists() %}
 | 
			
		||||
<h4>{% trans %}Reloads{% endtrans %}</h4>
 | 
			
		||||
<h4>{% trans %}Refillings{% endtrans %}</h4>
 | 
			
		||||
<table>
 | 
			
		||||
    <thead>
 | 
			
		||||
        <tr>
 | 
			
		||||
 
 | 
			
		||||
@@ -165,18 +165,62 @@
 | 
			
		||||
                <a href="{{ url('subscription:subscription') }}?member={{ profile.id }}">{% trans %}New subscription{% endtrans
 | 
			
		||||
                    %}</a>
 | 
			
		||||
                {% endif %}
 | 
			
		||||
                {% endif %}
 | 
			
		||||
            </div>
 | 
			
		||||
        {% endif %}
 | 
			
		||||
        <br>
 | 
			
		||||
        {% if profile.was_subscribed and (user == profile or user.can_read_subscription_history)%}
 | 
			
		||||
    {% endif %}
 | 
			
		||||
    <br>
 | 
			
		||||
    {% if profile.was_subscribed and (user == profile or user.can_read_subscription_history)%}
 | 
			
		||||
        <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 %}Subscription history{% endtrans %}
 | 
			
		||||
                </span>
 | 
			
		||||
                <span class="collapse-header-icon" :class="{'reverse': collapsed}">
 | 
			
		||||
                    <i class="fa-sharp fa-solid 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>
 | 
			
		||||
                    {% endfor %}
 | 
			
		||||
                </table>
 | 
			
		||||
            </div>
 | 
			
		||||
        </div>
 | 
			
		||||
    {% endif %}
 | 
			
		||||
    <hr>
 | 
			
		||||
    <div>
 | 
			
		||||
    {% if user.is_root or user.is_board_member %}
 | 
			
		||||
        <form class="form-gifts" action="{{ url('core:user_gift_create', user_id=profile.id) }}" method="post">
 | 
			
		||||
            {% csrf_token %}
 | 
			
		||||
            {{ gift_form.label }}
 | 
			
		||||
            {{ gift_form.user }}
 | 
			
		||||
            <input type="submit" value="{% trans %}Give gift{% endtrans %}">
 | 
			
		||||
        </form>
 | 
			
		||||
        {% 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 %}Subscription history{% endtrans %}
 | 
			
		||||
                        {% trans %}Last given gift :{% endtrans %} {{ gifts[0] }}
 | 
			
		||||
                    </span>
 | 
			
		||||
                    <span class="collapse-header-icon" :class="{'reverse': collapsed}">
 | 
			
		||||
                        <i class="fa fa-caret-down"></i>
 | 
			
		||||
                        <i class="fa-solid fa-caret-down"></i>
 | 
			
		||||
                    </span>
 | 
			
		||||
                </div>
 | 
			
		||||
                <div class="collapse-body" x-show="collapsed" x-transition.scale.origin.top>
 | 
			
		||||
@@ -190,54 +234,20 @@
 | 
			
		||||
                            </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>
 | 
			
		||||
                        <tr>
 | 
			
		||||
                            <td>{{ sub.subscription_start }}</td>
 | 
			
		||||
                            <td>{{ sub.subscription_end }}</td>
 | 
			
		||||
                            <td>{{ sub.subscription_type }}</td>
 | 
			
		||||
                            <td>{{ sub.get_payment_method_display() }}</td>
 | 
			
		||||
                        </tr>
 | 
			
		||||
                        {% endfor %}
 | 
			
		||||
                    </table>
 | 
			
		||||
                </div>
 | 
			
		||||
            </div>
 | 
			
		||||
        {% endif %}
 | 
			
		||||
 | 
			
		||||
        <hr>
 | 
			
		||||
        <div>
 | 
			
		||||
        {% if user.is_root or user.is_board_member %}
 | 
			
		||||
            <form class="form-gifts" action="{{ url('core:user_gift_create', user_id=profile.id) }}" method="post">
 | 
			
		||||
                {% csrf_token %}
 | 
			
		||||
                {{ gift_form.label }}
 | 
			
		||||
                {{ gift_form.user }}
 | 
			
		||||
                <input type="submit" value="{% trans %}Give gift{% endtrans %}">
 | 
			
		||||
            </form>
 | 
			
		||||
            {% 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 fa-caret-down"></i>
 | 
			
		||||
                        </span>
 | 
			
		||||
                    </div>
 | 
			
		||||
                    <div class="collapse-body" x-show="collapsed" x-transition.scale.origin.top>
 | 
			
		||||
                        <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 %}
 | 
			
		||||
                    </ul>
 | 
			
		||||
                </div>
 | 
			
		||||
        {% else %}
 | 
			
		||||
            <em>{% trans %}No gift given yet{% endtrans %}</em>
 | 
			
		||||
        {% endif %}
 | 
			
		||||
        </div>
 | 
			
		||||
    </div>
 | 
			
		||||
    {% endif %}
 | 
			
		||||
</div>
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -15,18 +15,17 @@
 | 
			
		||||
#
 | 
			
		||||
 | 
			
		||||
import os
 | 
			
		||||
from datetime import date, timedelta
 | 
			
		||||
from datetime import 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
 | 
			
		||||
 | 
			
		||||
"""
 | 
			
		||||
@@ -598,95 +597,15 @@ 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)
 | 
			
		||||
 
 | 
			
		||||
@@ -15,19 +15,20 @@
 | 
			
		||||
#
 | 
			
		||||
 | 
			
		||||
import os
 | 
			
		||||
import re
 | 
			
		||||
import subprocess
 | 
			
		||||
from datetime import date
 | 
			
		||||
import re
 | 
			
		||||
 | 
			
		||||
# Image utils
 | 
			
		||||
 | 
			
		||||
from io import BytesIO
 | 
			
		||||
from typing import Optional
 | 
			
		||||
from datetime import date
 | 
			
		||||
 | 
			
		||||
from PIL import ExifTags
 | 
			
		||||
 | 
			
		||||
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:
 | 
			
		||||
@@ -43,54 +44,34 @@ def get_git_revision_short_hash() -> str:
 | 
			
		||||
        return ""
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def get_start_of_semester(today: Optional[date] = None) -> date:
 | 
			
		||||
def get_start_of_semester(d=date.today()):
 | 
			
		||||
    """
 | 
			
		||||
    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
 | 
			
		||||
    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
 | 
			
		||||
    """
 | 
			
		||||
    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
 | 
			
		||||
    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
 | 
			
		||||
        return autumn
 | 
			
		||||
    if today >= spring:  # between 15/02 (included) and 15/08 -> spring semester
 | 
			
		||||
    if today > spring:  # spring semester
 | 
			
		||||
        return spring
 | 
			
		||||
    # between 01/01 and 15/02 -> autumn semester of the previous year
 | 
			
		||||
    return autumn.replace(year=autumn.year - 1)
 | 
			
		||||
    return autumn.replace(year=year - 1)  # autumn semester of last year
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
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()
 | 
			
		||||
 | 
			
		||||
def get_semester(d=date.today()):
 | 
			
		||||
    start = get_start_of_semester(d)
 | 
			
		||||
 | 
			
		||||
    if (start.month, start.day) == settings.SITH_SEMESTER_START_AUTUMN:
 | 
			
		||||
    if start.month <= 6:
 | 
			
		||||
        return "P" + str(start.year)[-2:]
 | 
			
		||||
    else:
 | 
			
		||||
        return "A" + str(start.year)[-2:]
 | 
			
		||||
    return "P" + str(start.year)[-2:]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def file_exist(path):
 | 
			
		||||
 
 | 
			
		||||
@@ -15,7 +15,7 @@
 | 
			
		||||
#
 | 
			
		||||
from __future__ import annotations
 | 
			
		||||
 | 
			
		||||
from typing import Tuple, Optional
 | 
			
		||||
from typing import Tuple
 | 
			
		||||
 | 
			
		||||
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: Optional[date] = None) -> QuerySet:
 | 
			
		||||
    def get_top_customers(self, since=get_start_of_semester()) -> 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,8 +546,6 @@ 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(
 | 
			
		||||
@@ -559,8 +557,7 @@ class Counter(models.Model):
 | 
			
		||||
            )
 | 
			
		||||
            .annotate(nickname=F("customer__user__nick_name"))
 | 
			
		||||
            .annotate(promo=F("customer__user__promo"))
 | 
			
		||||
            .annotate(user=F("customer__user"))
 | 
			
		||||
            .values("user", "promo", "name", "nickname")
 | 
			
		||||
            .values("customer__user", "name", "nickname")
 | 
			
		||||
            .annotate(
 | 
			
		||||
                selling_sum=Sum(
 | 
			
		||||
                    F("unit_price") * F("quantity"), output_field=CurrencyField()
 | 
			
		||||
@@ -570,17 +567,15 @@ class Counter(models.Model):
 | 
			
		||||
            .order_by("-selling_sum")
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def get_total_sales(self, since=None) -> CurrencyField:
 | 
			
		||||
    def get_total_sales(self, since=get_start_of_semester()) -> 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 | None
 | 
			
		||||
        :type since: datetime | date
 | 
			
		||||
        :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(
 | 
			
		||||
 
 | 
			
		||||
@@ -28,15 +28,15 @@
 | 
			
		||||
    <h5>{% trans %}Legend{% endtrans %}</h5>
 | 
			
		||||
    <div class="activity-description">
 | 
			
		||||
        <div>
 | 
			
		||||
            <i class="fa fa-check" style="color: #2ecc71"></i>
 | 
			
		||||
            <i class="fa-solid 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 fa-question" style="color: #f39c12"></i>
 | 
			
		||||
            <i class="fa-solid 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 fa-times" style="color: #eb2f06"></i>
 | 
			
		||||
            <i class="fa-solid fa-xmark" style="color: #eb2f06"></i>
 | 
			
		||||
            <span>{% trans %}counter is not open : no one is connected{% endtrans %}</span>
 | 
			
		||||
        </div>
 | 
			
		||||
    </div>
 | 
			
		||||
 
 | 
			
		||||
@@ -14,7 +14,7 @@
 | 
			
		||||
    {{ form }}
 | 
			
		||||
    <p><input type="submit" value="{% trans %}Show{% endtrans %}" /></p>
 | 
			
		||||
</form>
 | 
			
		||||
<h6>{% trans %}Reloads{% endtrans %}</h6>
 | 
			
		||||
<h6>{% trans %}Refillings{% endtrans %}</h6>
 | 
			
		||||
<p>
 | 
			
		||||
{% for b,s in refilling_sums.items() %}
 | 
			
		||||
    {{ b }}: {{ s }} €<br/>
 | 
			
		||||
 
 | 
			
		||||
@@ -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 %}Reloads list{% endtrans %}</a>
 | 
			
		||||
    <a href="{{ url('counter:refilling_list', counter_id=c.id) }}">{% trans %}Refillings 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 %}Reloads list{% endtrans %}</a>
 | 
			
		||||
    <a href="{{ url('counter:refilling_list', counter_id=c.id) }}">{% trans %}Refillings list{% endtrans %}</a>
 | 
			
		||||
    {% endif %}
 | 
			
		||||
    </li>
 | 
			
		||||
    {% endfor %}
 | 
			
		||||
 
 | 
			
		||||
@@ -21,7 +21,7 @@
 | 
			
		||||
{% block content %}
 | 
			
		||||
<h3>{% trans counter_name=counter %}{{ counter_name }} counter{% endtrans %}</h3>
 | 
			
		||||
    <div>
 | 
			
		||||
        <h3>{% trans %}Sales{% endtrans %}</h3>
 | 
			
		||||
        <h3>{% trans %}Sellings{% 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>
 | 
			
		||||
 
 | 
			
		||||
@@ -13,7 +13,7 @@
 | 
			
		||||
 | 
			
		||||
{% block content %}
 | 
			
		||||
<h3>{% trans counter_name=counter %}{{ counter_name }} last operations{% endtrans %}</h3>
 | 
			
		||||
<h4>{% trans %}Reloads{% endtrans %}</h4>
 | 
			
		||||
<h4>{% trans %}Refillings{% endtrans %}</h4>
 | 
			
		||||
<table>
 | 
			
		||||
    <thead>
 | 
			
		||||
        <tr>
 | 
			
		||||
@@ -38,7 +38,7 @@
 | 
			
		||||
    </tbody>
 | 
			
		||||
</table>
 | 
			
		||||
 | 
			
		||||
<h4>{% trans %}Sales{% endtrans %}</h4>
 | 
			
		||||
<h4>{% trans %}Sellings{% endtrans %}</h4>
 | 
			
		||||
<table>
 | 
			
		||||
    <thead>
 | 
			
		||||
        <tr>
 | 
			
		||||
 
 | 
			
		||||
@@ -2,7 +2,7 @@
 | 
			
		||||
{% from 'core/macros.jinja' import paginate %}
 | 
			
		||||
 | 
			
		||||
{% block title %}
 | 
			
		||||
{%- trans %}Reloads list{% endtrans %} -- {{ counter.name }}
 | 
			
		||||
{%- trans %}Refillings list{% endtrans %} -- {{ counter.name }}
 | 
			
		||||
{% endblock %}
 | 
			
		||||
 | 
			
		||||
{% block content %}
 | 
			
		||||
 
 | 
			
		||||
@@ -11,9 +11,7 @@
 | 
			
		||||
 | 
			
		||||
{% block content %}
 | 
			
		||||
    <h3>{% trans counter_name=counter %}{{ counter_name }} stats{% endtrans %}</h3>
 | 
			
		||||
    <h4>
 | 
			
		||||
        {% trans counter_name=counter.name %}Top 100 {{ counter_name }}{% endtrans %} ({{ current_semester }})
 | 
			
		||||
    </h4>
 | 
			
		||||
    <h4>{% trans counter_name=counter.name %}Top 100 {{ counter_name }}{% endtrans %}</h4>
 | 
			
		||||
    <table>
 | 
			
		||||
        <thead>
 | 
			
		||||
        <tr>
 | 
			
		||||
@@ -37,9 +35,7 @@
 | 
			
		||||
        </tbody>
 | 
			
		||||
    </table>
 | 
			
		||||
 | 
			
		||||
    <h4>
 | 
			
		||||
        {% trans counter_name=counter.name %}Top 100 barman {{ counter_name }}{% endtrans %} ({{ current_semester }})
 | 
			
		||||
    </h4>
 | 
			
		||||
    <h4>{% trans counter_name=counter.name %}Top 100 barman {{ counter_name }}{% endtrans %}</h4>
 | 
			
		||||
    <table>
 | 
			
		||||
        <thead>
 | 
			
		||||
        <tr>
 | 
			
		||||
 
 | 
			
		||||
@@ -13,7 +13,6 @@
 | 
			
		||||
# OR WITHIN THE LOCAL FILE "LICENSE"
 | 
			
		||||
#
 | 
			
		||||
#
 | 
			
		||||
from datetime import date, timedelta
 | 
			
		||||
import json
 | 
			
		||||
import re
 | 
			
		||||
import string
 | 
			
		||||
@@ -323,49 +322,42 @@ class CounterStatsTest(TestCase):
 | 
			
		||||
        Test the result of Counter.get_top_customers() is correct
 | 
			
		||||
        """
 | 
			
		||||
        top = iter(self.counter.get_top_customers())
 | 
			
		||||
        expected_results = [
 | 
			
		||||
        self.assertEqual(
 | 
			
		||||
            next(top),
 | 
			
		||||
            {
 | 
			
		||||
                "user": self.sli.id,
 | 
			
		||||
                "customer__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),
 | 
			
		||||
            {
 | 
			
		||||
                "user": self.skia.id,
 | 
			
		||||
                "customer__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),
 | 
			
		||||
            {
 | 
			
		||||
                "user": self.krophil.id,
 | 
			
		||||
                "customer__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),
 | 
			
		||||
            {
 | 
			
		||||
                "user": self.root.id,
 | 
			
		||||
                "customer__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))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -48,7 +48,7 @@ import pytz
 | 
			
		||||
from datetime import timedelta, datetime
 | 
			
		||||
from http import HTTPStatus
 | 
			
		||||
 | 
			
		||||
from core.utils import get_start_of_semester, get_semester_code
 | 
			
		||||
from core.utils import get_start_of_semester
 | 
			
		||||
from core.views import CanViewMixin, TabedViewMixin, CanEditMixin
 | 
			
		||||
from core.views.forms import LoginForm
 | 
			
		||||
from core.models import User
 | 
			
		||||
@@ -1354,14 +1354,13 @@ class CounterStatView(DetailView, CounterAdminMixin):
 | 
			
		||||
 | 
			
		||||
    def get_context_data(self, **kwargs):
 | 
			
		||||
        """Add stats to the context"""
 | 
			
		||||
        counter: Counter = self.object
 | 
			
		||||
        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],
 | 
			
		||||
 
 | 
			
		||||
@@ -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 fa-minus fa-xs" @click="remove(item.id)"></i>
 | 
			
		||||
                            <i class="fa-solid fa-minus"></i>
 | 
			
		||||
                            <span x-text="item.quantity"></span>
 | 
			
		||||
                            <i class="fa fa-plus" @click="add(item)"></i>
 | 
			
		||||
                            <i class="fa-solid fa-plus"></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 fa-trash"></i>
 | 
			
		||||
                    <i class="fa-solid fa-trash"></i>
 | 
			
		||||
                    {% trans %}Clear{% endtrans %}
 | 
			
		||||
                </button>
 | 
			
		||||
                <form method="get" action="{{ url('eboutic:command') }}">
 | 
			
		||||
                    {% csrf_token %}
 | 
			
		||||
                    <button class="validate">
 | 
			
		||||
                        <i class="fa fa-check"></i>
 | 
			
		||||
                        <i class="fa-solid 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 fa-close"></i>
 | 
			
		||||
                        <i class="fa-solid fa-xmark"></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 fa-2x fa-picture-o product-image" ></i>
 | 
			
		||||
                                            <i class="fa-solid fa-image product-image"></i>
 | 
			
		||||
                                        {% endif %}
 | 
			
		||||
                                        <div class="product-description">
 | 
			
		||||
                                            <h4>{{ p.name }}</h4>
 | 
			
		||||
@@ -123,6 +123,19 @@
 | 
			
		||||
            {% 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 %}
 | 
			
		||||
 
 | 
			
		||||
@@ -60,7 +60,7 @@
 | 
			
		||||
                        {% trans %}Edit billing information{% endtrans %}
 | 
			
		||||
                    </span>
 | 
			
		||||
                    <span class="collapse-header-icon" :class="{'reverse': collapsed}">
 | 
			
		||||
                        <i class="fa fa-caret-down"></i>
 | 
			
		||||
                        <i class="fa-sharp fa-solid 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 fa-close"></i>
 | 
			
		||||
                            <i class="fa-solid fa-xmark"></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 fa-close"></i>
 | 
			
		||||
                            <i class="fa-solid fa-xmark"></i>
 | 
			
		||||
                        </div>
 | 
			
		||||
                    </div>
 | 
			
		||||
                    <input type="submit" class="btn btn-blue clickable"
 | 
			
		||||
 
 | 
			
		||||
@@ -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 fa-arrow-down"></i></button>
 | 
			
		||||
                                    <button disabled><i class="fa fa-caret-down"></i></button>
 | 
			
		||||
                                    <button disabled><i class="fa-solid fa-arrow-down"></i></button>
 | 
			
		||||
                                    <button disabled><i class="fa-sharp fa-solid fa-caret-down"></i></button>
 | 
			
		||||
                                    {%- else %}
 | 
			
		||||
                                    <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>
 | 
			
		||||
                                    <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>
 | 
			
		||||
                                    {%- endif %}
 | 
			
		||||
                                    {% if role == role_list.first() %}
 | 
			
		||||
                                    <button disabled><i class="fa fa-caret-up"></i></button>
 | 
			
		||||
                                    <button disabled><i class="fa fa-arrow-up"></i></button>
 | 
			
		||||
                                    <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>
 | 
			
		||||
                                    {% else %}
 | 
			
		||||
                                    <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>
 | 
			
		||||
                                    <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>
 | 
			
		||||
                                    {% endif %}
 | 
			
		||||
                                </div>
 | 
			
		||||
                            {%- endif -%}
 | 
			
		||||
 
 | 
			
		||||
@@ -63,7 +63,7 @@ msgstr "IBAN"
 | 
			
		||||
 | 
			
		||||
#: accounting/models.py:112
 | 
			
		||||
msgid "account number"
 | 
			
		||||
msgstr "numéro de compte"
 | 
			
		||||
msgstr "numero 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 "Sales"
 | 
			
		||||
msgid "Sellings"
 | 
			
		||||
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 "Accounting: "
 | 
			
		||||
msgid "Accouting: "
 | 
			
		||||
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éée n'est pas affichée, attendez jusqu'à ce qu'un modérateur "
 | 
			
		||||
"prenne une décision"
 | 
			
		||||
"nouvellement créee n'est pas affichée, attendez jusqu'à qu'un modérateur "
 | 
			
		||||
"entre en action"
 | 
			
		||||
 | 
			
		||||
#: 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 moderated again)"
 | 
			
		||||
msgstr "Éditer (sera soumise de nouveau à la modération)"
 | 
			
		||||
msgid "Edit (will be remoderated)"
 | 
			
		||||
msgstr "Éditer (sera resoumise à 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, résultat d'élection - sans date"
 | 
			
		||||
msgstr "Information, resultat 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 régénérer"
 | 
			
		||||
msgstr "Supprimer et sauver pour regé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ésélectionnez au "
 | 
			
		||||
"Est-ce que l'utilisateur doit être traité comme actif. Déselectionnez 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 t-shirt"
 | 
			
		||||
msgstr "taille de tshirt"
 | 
			
		||||
 | 
			
		||||
#: 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 comme lu"
 | 
			
		||||
msgstr "Marquer tout commme lu"
 | 
			
		||||
 | 
			
		||||
#: core/templates/core/base.jinja:132
 | 
			
		||||
msgid "Logout"
 | 
			
		||||
@@ -2440,17 +2440,33 @@ msgstr "Accueil"
 | 
			
		||||
msgid "Associations & Clubs"
 | 
			
		||||
msgstr "Associations & Clubs"
 | 
			
		||||
 | 
			
		||||
#: core/templates/core/base.jinja:204
 | 
			
		||||
#: core/templates/core/base.jinja:173
 | 
			
		||||
msgid "AE"
 | 
			
		||||
msgstr "L'AE"
 | 
			
		||||
 | 
			
		||||
#: core/templates/core/base.jinja:205
 | 
			
		||||
#: core/templates/core/base.jinja:174
 | 
			
		||||
msgid "AE's clubs"
 | 
			
		||||
msgstr "Les clubs de L'AE"
 | 
			
		||||
 | 
			
		||||
#: core/templates/core/base.jinja:206
 | 
			
		||||
msgid "Others UTBM's Associations"
 | 
			
		||||
msgstr "Les autres associations de l'UTBM"
 | 
			
		||||
#: 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:187 core/templates/core/user_tools.jinja:118
 | 
			
		||||
msgid "Elections"
 | 
			
		||||
@@ -2764,7 +2780,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 "Numéro de compte : "
 | 
			
		||||
msgstr "Numero de compte : "
 | 
			
		||||
 | 
			
		||||
#: core/templates/core/macros.jinja:91 launderette/models.py:217
 | 
			
		||||
msgid "Slot"
 | 
			
		||||
@@ -2988,7 +3004,7 @@ msgstr "Bienvenue, %(user_name)s!"
 | 
			
		||||
 | 
			
		||||
#: core/templates/core/register.jinja:10
 | 
			
		||||
msgid ""
 | 
			
		||||
"You successfully registered and you will soon receive a confirmation mail."
 | 
			
		||||
"You successfully registred and you will soon receive a confirmation mail."
 | 
			
		||||
msgstr ""
 | 
			
		||||
"Vous vous êtes correctement enregistré, et vous devriez recevoir rapidement "
 | 
			
		||||
"un email de confirmation."
 | 
			
		||||
@@ -3047,14 +3063,14 @@ msgstr "Compte utilisateur"
 | 
			
		||||
 | 
			
		||||
#: core/templates/core/user_account.jinja:44
 | 
			
		||||
#: core/templates/core/user_account_detail.jinja:13
 | 
			
		||||
msgid "Account purchases"
 | 
			
		||||
msgstr "Achats du compte"
 | 
			
		||||
msgid "Account buyings"
 | 
			
		||||
msgstr "Achat sur compte utilisateur"
 | 
			
		||||
 | 
			
		||||
#: 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 "Reloads"
 | 
			
		||||
msgid "Refillings"
 | 
			
		||||
msgstr "Rechargements"
 | 
			
		||||
 | 
			
		||||
#: core/templates/core/user_account.jinja:53
 | 
			
		||||
@@ -4098,7 +4114,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 "Reloads list"
 | 
			
		||||
msgid "Refillings list"
 | 
			
		||||
msgstr "Liste de rechargements"
 | 
			
		||||
 | 
			
		||||
#: counter/templates/counter/counter_list.jinja:27
 | 
			
		||||
@@ -5017,7 +5033,7 @@ msgid ""
 | 
			
		||||
"The code of an UV must only contains uppercase characters without accent and "
 | 
			
		||||
"numbers"
 | 
			
		||||
msgstr ""
 | 
			
		||||
"Le code d'une UV doit seulement contenir des caractères majuscule sans "
 | 
			
		||||
"Le code d'une UV doit seulement contenire des caractères majuscule sans "
 | 
			
		||||
"accents et nombres"
 | 
			
		||||
 | 
			
		||||
#: pedagogy/models.py:67
 | 
			
		||||
@@ -5042,7 +5058,7 @@ msgstr "département"
 | 
			
		||||
 | 
			
		||||
#: pedagogy/models.py:103
 | 
			
		||||
msgid "objectives"
 | 
			
		||||
msgstr "objectifs"
 | 
			
		||||
msgstr "objecifs"
 | 
			
		||||
 | 
			
		||||
#: pedagogy/models.py:104
 | 
			
		||||
msgid "program"
 | 
			
		||||
@@ -5078,7 +5094,7 @@ msgstr "heures TE"
 | 
			
		||||
 | 
			
		||||
#: pedagogy/models.py:217 pedagogy/models.py:291
 | 
			
		||||
msgid "uv"
 | 
			
		||||
msgstr "UE"
 | 
			
		||||
msgstr "uv"
 | 
			
		||||
 | 
			
		||||
#: pedagogy/models.py:221
 | 
			
		||||
msgid "global grade"
 | 
			
		||||
@@ -5137,7 +5153,7 @@ msgstr "%(credit_type)s"
 | 
			
		||||
#: pedagogy/templates/pedagogy/guide.jinja:59
 | 
			
		||||
#: pedagogy/templates/pedagogy/moderation.jinja:12
 | 
			
		||||
msgid "UV"
 | 
			
		||||
msgstr "UE"
 | 
			
		||||
msgstr "UV"
 | 
			
		||||
 | 
			
		||||
#: pedagogy/templates/pedagogy/guide.jinja:61
 | 
			
		||||
msgid "Department"
 | 
			
		||||
@@ -5237,8 +5253,8 @@ msgid "Key concepts"
 | 
			
		||||
msgstr "Concepts clefs"
 | 
			
		||||
 | 
			
		||||
#: pedagogy/templates/pedagogy/uv_detail.jinja:79
 | 
			
		||||
msgid "UE manager: "
 | 
			
		||||
msgstr "Gestionnaire d'UE : "
 | 
			
		||||
msgid "UV manager: "
 | 
			
		||||
msgstr "Gestionnaire d'UV : "
 | 
			
		||||
 | 
			
		||||
#: pedagogy/templates/pedagogy/uv_detail.jinja:86 pedagogy/tests.py:453
 | 
			
		||||
msgid ""
 | 
			
		||||
@@ -5268,23 +5284,23 @@ msgstr "Signaler ce commentaire"
 | 
			
		||||
 | 
			
		||||
#: pedagogy/templates/pedagogy/uv_edit.jinja:4
 | 
			
		||||
#: pedagogy/templates/pedagogy/uv_edit.jinja:8
 | 
			
		||||
msgid "Edit UE"
 | 
			
		||||
msgstr "Éditer l'UE"
 | 
			
		||||
msgid "Edit UV"
 | 
			
		||||
msgstr "Éditer"
 | 
			
		||||
 | 
			
		||||
#: 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 UE code"
 | 
			
		||||
msgstr "Code d'UE inconnu"
 | 
			
		||||
msgid "Unknown UV code"
 | 
			
		||||
msgstr "Code d'UV 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 occurred: "
 | 
			
		||||
msgid "An error occured: "
 | 
			
		||||
msgstr "Une erreur est survenue : "
 | 
			
		||||
 | 
			
		||||
#: rootplace/templates/rootplace/delete_user_messages.jinja:8
 | 
			
		||||
@@ -5550,7 +5566,7 @@ msgstr "Automne et printemps"
 | 
			
		||||
 | 
			
		||||
#: sith/settings.py:448
 | 
			
		||||
msgid "German"
 | 
			
		||||
msgstr "Allemand"
 | 
			
		||||
msgstr "Allemant"
 | 
			
		||||
 | 
			
		||||
#: sith/settings.py:449
 | 
			
		||||
msgid "Spanish"
 | 
			
		||||
@@ -6346,3 +6362,11 @@ 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."
 | 
			
		||||
 
 | 
			
		||||
@@ -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 fa-leaf"></i></label>
 | 
			
		||||
					<input type="checkbox" name="semester" id="radioSPRING" value="SPRING"><label for="radioSPRING"><i class="fa fa-sun-o"></i></label>
 | 
			
		||||
					<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>
 | 
			
		||||
					<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 fa-leaf"></i></td>
 | 
			
		||||
				<td><i class="fa fa-sun-o"></i></td>
 | 
			
		||||
				<td><i class="fa-solid fa-leaf"></i></td>
 | 
			
		||||
				<td><i class="fa-solid fa-sun"></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 fa-leaf"></i>
 | 
			
		||||
						<i class="fa-solid fa-leaf"></i>
 | 
			
		||||
					{% endif %}
 | 
			
		||||
				</td>
 | 
			
		||||
				<td>
 | 
			
		||||
					{% if uv.semester in ["SPRING", "AUTUMN_AND_SPRING"] %}
 | 
			
		||||
						<i class="fa fa-sun-o"></i>
 | 
			
		||||
						<i class="fa-solid fa-sun"></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 fa-leaf'></i>";
 | 
			
		||||
			autumn = "<i class='fa-solid fa-leaf'></i>";
 | 
			
		||||
		if (uv.semester == "SPRING" || uv.semester == "AUTUMN_AND_SPRING")
 | 
			
		||||
			spring = "<i class='fa fa-sun-o'></i>";
 | 
			
		||||
			spring = "<i class='fa-solid fa-sun'></i>";
 | 
			
		||||
 | 
			
		||||
		var html = `
 | 
			
		||||
			<tr onclick="window.location.href = '${uv.absolute_url}';">
 | 
			
		||||
 
 | 
			
		||||
@@ -3,9 +3,9 @@
 | 
			
		||||
    {% if grade >= 0 %}
 | 
			
		||||
        {% for i in range(5) %}
 | 
			
		||||
            {% if i <= grade %}
 | 
			
		||||
                <span class="fa fa-star pedagogy star-checked grade-with-star"></span>
 | 
			
		||||
                <span class="fa-solid fa-star pedagogy star-checked grade-with-star"></span>
 | 
			
		||||
            {% else %}
 | 
			
		||||
                <span class="fa fa-star pedagogy star-not-checked grade-with-star"></span>
 | 
			
		||||
                <span class="fa-solid fa-star pedagogy star-not-checked grade-with-star"></span>
 | 
			
		||||
            {% endif %}
 | 
			
		||||
        {% endfor %}
 | 
			
		||||
        <span class="pedagogy grade-without-star">{{ grade }}/5</span>
 | 
			
		||||
 
 | 
			
		||||
@@ -26,7 +26,7 @@
 | 
			
		||||
                stars[i].setAttribute("class", attrs + " unchecked");
 | 
			
		||||
            }
 | 
			
		||||
        ' checked>
 | 
			
		||||
        <span class="fa fa-times-circle"> {{ translations.do_not_vote }}</span>
 | 
			
		||||
        <span class="fa-solid fa-xmark-circle"> {{ translations.do_not_vote }}</span>
 | 
			
		||||
    </label>
 | 
			
		||||
 | 
			
		||||
    {# Star widget #}
 | 
			
		||||
@@ -46,7 +46,7 @@
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        '>
 | 
			
		||||
        <i class="{{ widget.name }} fa fa-star unchecked"></i>
 | 
			
		||||
        <i class="{{ widget.name }} fa-solid fa-star unchecked"></i>
 | 
			
		||||
    </label>
 | 
			
		||||
    {% endfor %}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -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 %}UE manager: {% endtrans %}</b>{{ object.manager }}</p>
 | 
			
		||||
				<p><b>{% trans %}UV 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 fa-paragraph"></i></a>
 | 
			
		||||
							<a href="{{ url('pedagogy:uv_detail', uv_id=uv.id) }}#{{ comment.id }}"><i class="fa-solid fa-paragraph"></i></a>
 | 
			
		||||
						</div>
 | 
			
		||||
						{{ comment.comment|markdown }}
 | 
			
		||||
					</div>
 | 
			
		||||
@@ -206,8 +206,8 @@
 | 
			
		||||
	$("#return_noscript").hide();
 | 
			
		||||
	$("#return_js").show();
 | 
			
		||||
	var icons = {
 | 
			
		||||
		header: "fa fa-toggle-right",
 | 
			
		||||
		activeHeader: "fa fa-toggle-down"
 | 
			
		||||
		header: "fa-solid fa-toggle-on",
 | 
			
		||||
		activeHeader: "fa-solid fa-toggle-off"
 | 
			
		||||
	};
 | 
			
		||||
	$(function(){
 | 
			
		||||
		$("#leave_comment").accordion({
 | 
			
		||||
 
 | 
			
		||||
@@ -1,11 +1,11 @@
 | 
			
		||||
{% extends "core/base.jinja" %}
 | 
			
		||||
 | 
			
		||||
{% block title %}
 | 
			
		||||
{% trans %}Edit UE{% endtrans %}
 | 
			
		||||
{% trans %}Edit UV{% endtrans %}
 | 
			
		||||
{% endblock %}
 | 
			
		||||
 | 
			
		||||
{% block content %}
 | 
			
		||||
<h2>{% trans %}Edit UE{% endtrans %}</h2>
 | 
			
		||||
<h2>{% trans %}Edit UV{% 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 UE code{% endtrans %}")
 | 
			
		||||
                        createQuickNotif("{% trans %}Unknown UV code{% endtrans %}")
 | 
			
		||||
                        return
 | 
			
		||||
                    }
 | 
			
		||||
                    for (let key in data) {
 | 
			
		||||
@@ -77,7 +77,7 @@
 | 
			
		||||
                    createQuickNotif('{% trans %}Successful autocomplete{% endtrans %}')
 | 
			
		||||
                },
 | 
			
		||||
                error: function(_, _, statusMessage) {
 | 
			
		||||
                    createQuickNotif('{% trans %}An error occurred: {% endtrans %}' + statusMessage)
 | 
			
		||||
                    createQuickNotif('{% trans %}An error occured: {% endtrans %}' + statusMessage)
 | 
			
		||||
                },
 | 
			
		||||
            })
 | 
			
		||||
        })
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										140
									
								
								poetry.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										140
									
								
								poetry.lock
									
									
									
										generated
									
									
									
								
							@@ -1,9 +1,10 @@
 | 
			
		||||
# This file is automatically @generated by Poetry 1.6.1 and should not be changed by hand.
 | 
			
		||||
# This file is automatically @generated by Poetry 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 = [
 | 
			
		||||
@@ -15,6 +16,7 @@ files = [
 | 
			
		||||
name = "appnope"
 | 
			
		||||
version = "0.1.3"
 | 
			
		||||
description = "Disable App Nap on macOS >= 10.9"
 | 
			
		||||
category = "dev"
 | 
			
		||||
optional = false
 | 
			
		||||
python-versions = "*"
 | 
			
		||||
files = [
 | 
			
		||||
@@ -26,6 +28,7 @@ files = [
 | 
			
		||||
name = "asgiref"
 | 
			
		||||
version = "3.6.0"
 | 
			
		||||
description = "ASGI specs, helper code, and adapters"
 | 
			
		||||
category = "main"
 | 
			
		||||
optional = false
 | 
			
		||||
python-versions = ">=3.7"
 | 
			
		||||
files = [
 | 
			
		||||
@@ -40,6 +43,7 @@ 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 = [
 | 
			
		||||
@@ -54,6 +58,7 @@ 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 = [
 | 
			
		||||
@@ -65,6 +70,7 @@ files = [
 | 
			
		||||
name = "black"
 | 
			
		||||
version = "23.3.0"
 | 
			
		||||
description = "The uncompromising code formatter."
 | 
			
		||||
category = "dev"
 | 
			
		||||
optional = false
 | 
			
		||||
python-versions = ">=3.7"
 | 
			
		||||
files = [
 | 
			
		||||
@@ -114,6 +120,7 @@ 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 = [
 | 
			
		||||
@@ -125,6 +132,7 @@ files = [
 | 
			
		||||
name = "cffi"
 | 
			
		||||
version = "1.15.1"
 | 
			
		||||
description = "Foreign Function Interface for Python calling C code."
 | 
			
		||||
category = "main"
 | 
			
		||||
optional = false
 | 
			
		||||
python-versions = "*"
 | 
			
		||||
files = [
 | 
			
		||||
@@ -201,6 +209,7 @@ 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 = [
 | 
			
		||||
@@ -215,6 +224,7 @@ unicode-backport = ["unicodedata2"]
 | 
			
		||||
name = "click"
 | 
			
		||||
version = "8.1.3"
 | 
			
		||||
description = "Composable command line interface toolkit"
 | 
			
		||||
category = "dev"
 | 
			
		||||
optional = false
 | 
			
		||||
python-versions = ">=3.7"
 | 
			
		||||
files = [
 | 
			
		||||
@@ -229,6 +239,7 @@ 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 = [
 | 
			
		||||
@@ -240,6 +251,7 @@ 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 = [
 | 
			
		||||
@@ -304,6 +316,7 @@ 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 = [
 | 
			
		||||
@@ -345,6 +358,7 @@ tox = ["tox"]
 | 
			
		||||
name = "decorator"
 | 
			
		||||
version = "5.1.1"
 | 
			
		||||
description = "Decorators for Humans"
 | 
			
		||||
category = "dev"
 | 
			
		||||
optional = false
 | 
			
		||||
python-versions = ">=3.5"
 | 
			
		||||
files = [
 | 
			
		||||
@@ -356,6 +370,7 @@ 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 = [
 | 
			
		||||
@@ -370,6 +385,7 @@ 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 = [
 | 
			
		||||
@@ -390,6 +406,7 @@ 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 = [
 | 
			
		||||
@@ -400,6 +417,7 @@ files = [
 | 
			
		||||
name = "django-countries"
 | 
			
		||||
version = "7.5.1"
 | 
			
		||||
description = "Provides a country field for Django models."
 | 
			
		||||
category = "main"
 | 
			
		||||
optional = false
 | 
			
		||||
python-versions = "*"
 | 
			
		||||
files = [
 | 
			
		||||
@@ -421,6 +439,7 @@ 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 = [
 | 
			
		||||
@@ -436,6 +455,7 @@ sqlparse = ">=0.2"
 | 
			
		||||
name = "django-haystack"
 | 
			
		||||
version = "3.2.1"
 | 
			
		||||
description = "Pluggable search for Django."
 | 
			
		||||
category = "main"
 | 
			
		||||
optional = false
 | 
			
		||||
python-versions = "*"
 | 
			
		||||
files = [
 | 
			
		||||
@@ -452,6 +472,7 @@ 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 = [
 | 
			
		||||
@@ -465,19 +486,21 @@ jinja2 = ">=3"
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "django-ordered-model"
 | 
			
		||||
version = "3.7.4"
 | 
			
		||||
version = "3.6"
 | 
			
		||||
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.7.4.tar.gz", hash = "sha256:f258b9762525c00a53009e82f8b8bf2a3aa315e8b453e281e8fdbbfe2b8cb3ba"},
 | 
			
		||||
    {file = "django_ordered_model-3.7.4-py3-none-any.whl", hash = "sha256:dfcd3183fe0749dad1c9971cba1d6240ce7328742a30ddc92feca41107bb241d"},
 | 
			
		||||
    {file = "django-ordered-model-3.6.tar.gz", hash = "sha256:62161a6bc51d8b402644854b257605d7b5183d01fd349826682a87e9227c05b5"},
 | 
			
		||||
    {file = "django_ordered_model-3.6-py3-none-any.whl", hash = "sha256:0006b111f472a2348f75554a4e77bee2b1f379a0f96726af6b1a3ebf3a950789"},
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[[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 = [
 | 
			
		||||
@@ -496,6 +519,7 @@ 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 = [
 | 
			
		||||
@@ -509,6 +533,7 @@ 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 = [
 | 
			
		||||
@@ -528,6 +553,7 @@ test = ["testfixtures"]
 | 
			
		||||
name = "djangorestframework"
 | 
			
		||||
version = "3.14.0"
 | 
			
		||||
description = "Web APIs for Django, made easy."
 | 
			
		||||
category = "main"
 | 
			
		||||
optional = false
 | 
			
		||||
python-versions = ">=3.6"
 | 
			
		||||
files = [
 | 
			
		||||
@@ -543,6 +569,7 @@ 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 = [
 | 
			
		||||
@@ -550,24 +577,11 @@ 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 = [
 | 
			
		||||
@@ -579,6 +593,7 @@ 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 = [
 | 
			
		||||
@@ -590,6 +605,7 @@ files = [
 | 
			
		||||
name = "importlib-metadata"
 | 
			
		||||
version = "6.0.0"
 | 
			
		||||
description = "Read metadata from Python packages"
 | 
			
		||||
category = "main"
 | 
			
		||||
optional = true
 | 
			
		||||
python-versions = ">=3.7"
 | 
			
		||||
files = [
 | 
			
		||||
@@ -609,6 +625,7 @@ 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 = [
 | 
			
		||||
@@ -645,6 +662,7 @@ 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 = [
 | 
			
		||||
@@ -664,6 +682,7 @@ 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 = [
 | 
			
		||||
@@ -681,6 +700,7 @@ 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 = [
 | 
			
		||||
@@ -695,6 +715,7 @@ 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 = [
 | 
			
		||||
@@ -744,6 +765,7 @@ files = [
 | 
			
		||||
name = "matplotlib-inline"
 | 
			
		||||
version = "0.1.6"
 | 
			
		||||
description = "Inline Matplotlib backend for Jupyter"
 | 
			
		||||
category = "dev"
 | 
			
		||||
optional = false
 | 
			
		||||
python-versions = ">=3.5"
 | 
			
		||||
files = [
 | 
			
		||||
@@ -758,6 +780,7 @@ traitlets = "*"
 | 
			
		||||
name = "mistune"
 | 
			
		||||
version = "0.8.4"
 | 
			
		||||
description = "The fastest markdown parser in pure Python"
 | 
			
		||||
category = "main"
 | 
			
		||||
optional = false
 | 
			
		||||
python-versions = "*"
 | 
			
		||||
files = [
 | 
			
		||||
@@ -769,6 +792,7 @@ 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 = [
 | 
			
		||||
@@ -780,6 +804,7 @@ files = [
 | 
			
		||||
name = "packaging"
 | 
			
		||||
version = "23.0"
 | 
			
		||||
description = "Core utilities for Python packages"
 | 
			
		||||
category = "main"
 | 
			
		||||
optional = false
 | 
			
		||||
python-versions = ">=3.7"
 | 
			
		||||
files = [
 | 
			
		||||
@@ -791,6 +816,7 @@ files = [
 | 
			
		||||
name = "parso"
 | 
			
		||||
version = "0.8.3"
 | 
			
		||||
description = "A Python Parser"
 | 
			
		||||
category = "dev"
 | 
			
		||||
optional = false
 | 
			
		||||
python-versions = ">=3.6"
 | 
			
		||||
files = [
 | 
			
		||||
@@ -806,6 +832,7 @@ 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 = [
 | 
			
		||||
@@ -817,6 +844,7 @@ files = [
 | 
			
		||||
name = "pexpect"
 | 
			
		||||
version = "4.8.0"
 | 
			
		||||
description = "Pexpect allows easy control of interactive console applications."
 | 
			
		||||
category = "dev"
 | 
			
		||||
optional = false
 | 
			
		||||
python-versions = "*"
 | 
			
		||||
files = [
 | 
			
		||||
@@ -831,6 +859,7 @@ 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 = [
 | 
			
		||||
@@ -842,6 +871,7 @@ files = [
 | 
			
		||||
name = "pickleshare"
 | 
			
		||||
version = "0.7.5"
 | 
			
		||||
description = "Tiny 'shelve'-like database with concurrency support"
 | 
			
		||||
category = "dev"
 | 
			
		||||
optional = false
 | 
			
		||||
python-versions = "*"
 | 
			
		||||
files = [
 | 
			
		||||
@@ -853,6 +883,7 @@ files = [
 | 
			
		||||
name = "pillow"
 | 
			
		||||
version = "9.4.0"
 | 
			
		||||
description = "Python Imaging Library (Fork)"
 | 
			
		||||
category = "main"
 | 
			
		||||
optional = false
 | 
			
		||||
python-versions = ">=3.7"
 | 
			
		||||
files = [
 | 
			
		||||
@@ -943,6 +974,7 @@ 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 = [
 | 
			
		||||
@@ -958,6 +990,7 @@ 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 = [
 | 
			
		||||
@@ -972,6 +1005,7 @@ wcwidth = "*"
 | 
			
		||||
name = "psycopg2-binary"
 | 
			
		||||
version = "2.9.3"
 | 
			
		||||
description = "psycopg2 - Python-PostgreSQL Database Adapter"
 | 
			
		||||
category = "main"
 | 
			
		||||
optional = false
 | 
			
		||||
python-versions = ">=3.6"
 | 
			
		||||
files = [
 | 
			
		||||
@@ -1040,6 +1074,7 @@ files = [
 | 
			
		||||
name = "ptyprocess"
 | 
			
		||||
version = "0.7.0"
 | 
			
		||||
description = "Run a subprocess in a pseudo terminal"
 | 
			
		||||
category = "dev"
 | 
			
		||||
optional = false
 | 
			
		||||
python-versions = "*"
 | 
			
		||||
files = [
 | 
			
		||||
@@ -1051,6 +1086,7 @@ 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 = [
 | 
			
		||||
@@ -1062,6 +1098,7 @@ 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 = [
 | 
			
		||||
@@ -1076,6 +1113,7 @@ plugins = ["importlib-metadata"]
 | 
			
		||||
name = "pygraphviz"
 | 
			
		||||
version = "1.10"
 | 
			
		||||
description = "Python interface to Graphviz"
 | 
			
		||||
category = "main"
 | 
			
		||||
optional = false
 | 
			
		||||
python-versions = ">=3.8"
 | 
			
		||||
files = [
 | 
			
		||||
@@ -1086,6 +1124,7 @@ files = [
 | 
			
		||||
name = "pyopenssl"
 | 
			
		||||
version = "23.1.1"
 | 
			
		||||
description = "Python wrapper module around the OpenSSL library"
 | 
			
		||||
category = "main"
 | 
			
		||||
optional = false
 | 
			
		||||
python-versions = ">=3.6"
 | 
			
		||||
files = [
 | 
			
		||||
@@ -1104,6 +1143,7 @@ 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 = [
 | 
			
		||||
@@ -1118,6 +1158,7 @@ six = ">=1.5"
 | 
			
		||||
name = "pytz"
 | 
			
		||||
version = "2021.3"
 | 
			
		||||
description = "World timezone definitions, modern and historical"
 | 
			
		||||
category = "main"
 | 
			
		||||
optional = false
 | 
			
		||||
python-versions = "*"
 | 
			
		||||
files = [
 | 
			
		||||
@@ -1129,6 +1170,7 @@ files = [
 | 
			
		||||
name = "reportlab"
 | 
			
		||||
version = "3.6.12"
 | 
			
		||||
description = "The Reportlab Toolkit"
 | 
			
		||||
category = "main"
 | 
			
		||||
optional = false
 | 
			
		||||
python-versions = ">=3.7,<4"
 | 
			
		||||
files = [
 | 
			
		||||
@@ -1190,6 +1232,7 @@ 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 = [
 | 
			
		||||
@@ -1211,6 +1254,7 @@ 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 = [
 | 
			
		||||
@@ -1252,6 +1296,7 @@ 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 = [
 | 
			
		||||
@@ -1268,6 +1313,7 @@ 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 = [
 | 
			
		||||
@@ -1279,6 +1325,7 @@ 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 = [
 | 
			
		||||
@@ -1290,6 +1337,7 @@ files = [
 | 
			
		||||
name = "sphinx"
 | 
			
		||||
version = "4.5.0"
 | 
			
		||||
description = "Python documentation generator"
 | 
			
		||||
category = "main"
 | 
			
		||||
optional = true
 | 
			
		||||
python-versions = ">=3.6"
 | 
			
		||||
files = [
 | 
			
		||||
@@ -1325,6 +1373,7 @@ 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 = [
 | 
			
		||||
@@ -1343,6 +1392,7 @@ 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 = [
 | 
			
		||||
@@ -1361,6 +1411,7 @@ 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 = [
 | 
			
		||||
@@ -1376,6 +1427,7 @@ 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 = [
 | 
			
		||||
@@ -1391,6 +1443,7 @@ 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 = [
 | 
			
		||||
@@ -1406,6 +1459,7 @@ 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 = [
 | 
			
		||||
@@ -1420,6 +1474,7 @@ 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 = [
 | 
			
		||||
@@ -1435,6 +1490,7 @@ 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 = [
 | 
			
		||||
@@ -1448,24 +1504,21 @@ test = ["pytest"]
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "sqlparse"
 | 
			
		||||
version = "0.4.4"
 | 
			
		||||
version = "0.4.3"
 | 
			
		||||
description = "A non-validating SQL parser."
 | 
			
		||||
category = "main"
 | 
			
		||||
optional = false
 | 
			
		||||
python-versions = ">=3.5"
 | 
			
		||||
files = [
 | 
			
		||||
    {file = "sqlparse-0.4.4-py3-none-any.whl", hash = "sha256:5430a4fe2ac7d0f93e66f1efc6e1338a41884b7ddf2a350cedd20ccc4d9d28f3"},
 | 
			
		||||
    {file = "sqlparse-0.4.4.tar.gz", hash = "sha256:d446183e84b8349fa3061f0fe7f06ca94ba65b426946ffebe6e3e8295332420c"},
 | 
			
		||||
    {file = "sqlparse-0.4.3-py3-none-any.whl", hash = "sha256:0323c0ec29cd52bceabc1b4d9d579e311f3e4961b98d174201d5622a23b85e34"},
 | 
			
		||||
    {file = "sqlparse-0.4.3.tar.gz", hash = "sha256:69ca804846bb114d2ec380e4360a8a340db83f0ccf3afceeb1404df028f57268"},
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
[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 = [
 | 
			
		||||
@@ -1477,6 +1530,7 @@ files = [
 | 
			
		||||
name = "traitlets"
 | 
			
		||||
version = "5.8.1"
 | 
			
		||||
description = "Traitlets Python configuration system"
 | 
			
		||||
category = "dev"
 | 
			
		||||
optional = false
 | 
			
		||||
python-versions = ">=3.7"
 | 
			
		||||
files = [
 | 
			
		||||
@@ -1492,6 +1546,7 @@ 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 = [
 | 
			
		||||
@@ -1503,6 +1558,7 @@ 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 = [
 | 
			
		||||
@@ -1519,6 +1575,7 @@ 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 = [
 | 
			
		||||
@@ -1526,10 +1583,37 @@ 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 = [
 | 
			
		||||
@@ -1542,10 +1626,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-copybutton", "sphinx-rtd-theme"]
 | 
			
		||||
docs = ["Sphinx", "sphinx-rtd-theme", "sphinx-copybutton"]
 | 
			
		||||
testing = ["coverage"]
 | 
			
		||||
 | 
			
		||||
[metadata]
 | 
			
		||||
lock-version = "2.0"
 | 
			
		||||
python-versions = "^3.8"
 | 
			
		||||
content-hash = "32bf0229e7ac812ea278a36587c184cd4ae507f87816f573447e85cd5312e52c"
 | 
			
		||||
content-hash = "95b9a07660bd8f95a5a90cc273071a675d884424479cd1ed922438dc767d230a"
 | 
			
		||||
 
 | 
			
		||||
@@ -33,8 +33,10 @@ 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.7"
 | 
			
		||||
django-ordered-model = "^3.6"
 | 
			
		||||
django-simple-captcha = "^0.5.17"
 | 
			
		||||
python-dateutil = "^2.8.2"
 | 
			
		||||
psycopg2-binary = "2.9.3"
 | 
			
		||||
@@ -57,7 +59,6 @@ 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"
 | 
			
		||||
 
 | 
			
		||||
@@ -191,9 +191,9 @@ TEMPLATES = [
 | 
			
		||||
 | 
			
		||||
HAYSTACK_CONNECTIONS = {
 | 
			
		||||
    "default": {
 | 
			
		||||
        "ENGINE": "haystack.backends.elasticsearch7_backend.Elasticsearch7SearchEngine",
 | 
			
		||||
        'URL': 'http://localhost:9200/',
 | 
			
		||||
        'INDEX_NAME': 'haystack',
 | 
			
		||||
        "ENGINE": "xapian_backend.XapianEngine",
 | 
			
		||||
        "PATH": os.path.join(os.path.dirname(__file__), "search_indexes", "xapian"),
 | 
			
		||||
        "INCLUDE_SPELLING": True,
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -327,8 +327,7 @@ SITH_CLUB_ROOT_PAGE = "clubs"
 | 
			
		||||
 | 
			
		||||
# Define the date in the year serving as reference for the subscriptions calendar
 | 
			
		||||
# (month, day)
 | 
			
		||||
SITH_SEMESTER_START_AUTUMN = (8, 15)  # 15 August
 | 
			
		||||
SITH_SEMESTER_START_SPRING = (2, 15)  # 15 February
 | 
			
		||||
SITH_START_DATE = (8, 15)  # 15th August
 | 
			
		||||
 | 
			
		||||
# Used to determine the valid promos
 | 
			
		||||
SITH_SCHOOL_START_YEAR = 1999
 | 
			
		||||
@@ -715,7 +714,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/": "4.7.0",
 | 
			
		||||
    "https://github.com/FortAwesome/Font-Awesome/": "6.4.0",
 | 
			
		||||
    "https://github.com/jquery/jquery/": "3.6.2",
 | 
			
		||||
    "https://github.com/sethmcl/jquery-ui/": "1.11.1",
 | 
			
		||||
    "https://github.com/viralpatel/jquery.shorten/": "",
 | 
			
		||||
 
 | 
			
		||||
@@ -114,12 +114,12 @@ class Subscription(models.Model):
 | 
			
		||||
            return "No user - " + str(self.pk)
 | 
			
		||||
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    def compute_start(d: date = None, duration: int = 1, user: User = None) -> date:
 | 
			
		||||
    def compute_start(d=None, duration=1, user=None):
 | 
			
		||||
        """
 | 
			
		||||
        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_SEMESTER_START_AUTUMN.
 | 
			
		||||
        and the start date given in settings.SITH_START_DATE.
 | 
			
		||||
        It takes the nearest past start date.
 | 
			
		||||
        Exemples: with SITH_SEMESTER_START_AUTUMN = (8, 15)
 | 
			
		||||
        Exemples: with SITH_START_DATE = (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: int, start: date = None, user: User = None) -> date:
 | 
			
		||||
    def compute_end(duration, start=None, user=None):
 | 
			
		||||
        """
 | 
			
		||||
        This function compute the end date of the subscription given a start date and a duration in number of semester
 | 
			
		||||
        This function compute the end date of the subscription given a start date and a duration in number of semestre
 | 
			
		||||
        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: User):
 | 
			
		||||
    def can_be_edited_by(self, user):
 | 
			
		||||
        return user.is_board_member or user.is_root
 | 
			
		||||
 | 
			
		||||
    def is_valid_now(self):
 | 
			
		||||
 
 | 
			
		||||
@@ -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_code
 | 
			
		||||
from core.utils import get_start_of_semester, get_semester
 | 
			
		||||
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_code(m.end_date)
 | 
			
		||||
                end_date = get_semester(m.end_date)
 | 
			
		||||
            else:
 | 
			
		||||
                end_date = ""
 | 
			
		||||
            TrombiClubMembership(
 | 
			
		||||
                user=self,
 | 
			
		||||
                club=str(m.club),
 | 
			
		||||
                role=role[:64],
 | 
			
		||||
                start=get_semester_code(m.start_date),
 | 
			
		||||
                start=get_semester(m.start_date),
 | 
			
		||||
                end=end_date,
 | 
			
		||||
            ).save()
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user